ADD --chown=testing:testing sample_restraint /home/testing/sample_restraint
-RUN . $HOME/testing/bin/activate && \
+# TODO: (#3027) Get googletest sources locally.
+RUN . $VENV/bin/activate && \
. /usr/local/gromacs/bin/GMXRC && \
(cd $HOME/sample_restraint && \
mkdir build && \
cd build && \
- cmake .. && \
- make -j4 install \
+ cmake .. \
+ -DDOWNLOAD_GOOGLETEST=ON \
+ -DGMXAPI_EXTENSION_DOWNLOAD_PYBIND=ON && \
+ make -j4 && \
+ make test && \
+ make install \
)
# TODO: this can be in the root user section above once it is stable
--- /dev/null
+cmake_minimum_required(VERSION 3.9)
+project(pybind-download NONE)
+
+include(ExternalProject)
+ExternalProject_Add(pybind11
+ GIT_REPOSITORY https://github.com/pybind/pybind11.git
+ GIT_TAG v2.2
+ SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind-src"
+ BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/pybind-build"
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ TEST_COMMAND ""
+ )
-cmake_minimum_required(VERSION 3.4.3)
+cmake_minimum_required(VERSION 3.9.6)
# 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.7)
+find_package(PythonInterp)
+
+# Check if Python package is being built directly or via add_subdirectory.
+# I.e. is this being built as a standalone project or as part of the GROMACS
+# build tree (for testing)?
+set(GMXAPI_EXTENSION_MASTER_PROJECT OFF)
+if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
+ set(GMXAPI_EXTENSION_MASTER_PROJECT ON)
+endif()
+
+# TODO: (Issue #3027) Handle pybind sources both for forked projects and
+# for GROMACS-project-internal testing.
+# Projects based on this subtree should bundle pybind11 sources.
+# The GROMACS project already bundles one copy of pybind sources for the gmxapi
+# Python package, and should package them with source distribution archives of
+# 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
+set(GMXAPI_USE_BUNDLED_PYBIND OFF CACHE BOOL
+ "Use pybind11 headers bundled with this repository. If OFF, CMake does `find_package(pybind11)`.")
+if(GMXAPI_USE_BUNDLED_PYBIND)
+ add_subdirectory(external/pybind)
+else()
+ # If configuring as part of the GROMACS project, pybind11 2.2 has already been found.
+ if(NOT GMXAPI_EXTENSION_MASTER_PROJECT)
+ # TODO: (Issue #3027) Handle locally available sources.
+ else()
+ # TODO: Find a proper way to reference a local directory, once we figure out where that is.
+ option(GMXAPI_EXTENSION_DOWNLOAD_PYBIND ON)
+ if(GMXAPI_EXTENSION_DOWNLOAD_PYBIND)
+ configure_file(CMakeLists.pybind.in pybind-download/CMakeLists.txt)
+ execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pybind-download)
+ if(result)
+ message(FATAL_ERROR "CMake step for pybind download failed: ${result}")
+ endif()
+ execute_process(COMMAND ${CMAKE_COMMAND} --build .
+ RESULT_VARIABLE result
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pybind-download)
+ if(result)
+ message(FATAL_ERROR "Build step for pybind failed: ${result}")
+ endif()
+ add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/pybind-src)
+ else()
+ find_package(pybind11 2.2 REQUIRED)
+ endif()
+ endif()
+endif()
+
# This project requires a GROMACS supporting gmxapi 0.0.7 or higher. It should
# be sufficient to source the GMXRC, but you can also set the GROMACS_DIR or
# gmxapi_DIR environment variable to help CMake find the GROMACS installation.
# not use any Python virtual environments, we recommend you _do_ perform "user" installs exclusively. Overall, we
# we recommend you use Python virtual environments and activate one before performing a regular (non-"user") install.
-unset(PYTHONINTERP_FOUND)
-unset(PYTHONLIBS_FOUND)
-find_package(PythonInterp)
if (PYTHONINTERP_FOUND)
message(STATUS "Found Python interpreter: ${PYTHON_EXECUTABLE}")
- add_subdirectory(src/external/pybind11)
if (PYTHON_LIBRARIES)
if (GMXPLUGIN_USER_INSTALL)
execute_process(COMMAND ${PYTHON_EXECUTABLE} "-m" "site" "--user-site"
"Set PYTHON_EXECUTABLE to the Python interpreter that was installed with a working Python.h header file.")
endif()
else()
- message(FATAL "Could not find Python interpreter. Set CMake flag -DPYTHON_EXECUTABLE=/path/to/python to hint.")
+ message(FATAL_ERROR "Could not find Python interpreter. Set CMake flag -DPYTHON_EXECUTABLE=/path/to/python to hint.")
endif()
# At some point this may be part of a CMake package with several components for which a single CMAKE_INSTALL_PREFIX does
# Set up testing options.
include(CTest)
-# Either use the git subtree in tests/googletest or download a fresh copy of
-# googletest master branch.
-option(DOWNLOAD_GOOGLETEST OFF "Download the latest master branch of googletest.")
+# TODO: (Issue #3027) Handle Googletest sources both for forked projects and
+# for GROMACS-project-internal testing.
+# Projects based on this subtree should bundle googletest sources.
+# The GROMACS project already bundles googletest sources for internal use, but
+# they will only be available to this project with some additional management by
+# the parent project CMake configuration.
+option(DOWNLOAD_GOOGLETEST "Download the latest master branch of googletest." OFF)
mark_as_advanced(DOWNLOAD_GOOGLETEST)
# Prevent overriding the parent project's compiler/linker
# Process CMake configuration for Python and C++ tests.
if(BUILD_TESTING)
+ include(GoogleTest)
add_subdirectory(tests)
endif()
# framework or the Googletest framework.
add_subdirectory(cpp)
-
-# Import the Googletest targets, unless the user has requested that the repository
-# be downloaded instead (see tests/CMakeLists.txt)
-if(NOT ${DOWNLOAD_GOOGLETEST})
- if(${BUILD_TESTING})
- add_subdirectory(external/googletest EXCLUDE_FROM_ALL)
- endif()
-endif()
-
-
# Build a Python extension package from our new library.
add_subdirectory(pythonmodule)
* potentials.
*/
-#include <vector>
#include <array>
+#include <memory>
#include <mutex>
+#include <vector>
#include "gmxapi/gromacsfwd.h"
#include "gmxapi/session.h"
+++ /dev/null
-/*
- * 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.
- */
-#ifndef GMX_COMPAT_MAKE_UNIQUE_H
-#define GMX_COMPAT_MAKE_UNIQUE_H
-/*! \libinternal
- * \file
- * \brief Provides template gmx::compat::make_unique
- *
- * The implementation of gmx::compat::make_unique is copied with little
- * modification from C++ standardization doc N3656
- * at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3656.htm
- * though additional wrapping has been added for use in \Gromacs.
- *
- * \author M. Eric Irrgang <ericirrgang@gmail.com>
- * \ingroup group_compatibility
- */
-/*! \addtogroup group_compatibility
- * ### gmx::compat::make_unique
- *
- * Provide a trivially adapted implementation of the C++ standard library `make_unique` function template.
- * When All supported \Gromacs build platforms provide `std::make_unique`, this should be removed.
- *
- */
-#include <cstddef>
-
-#include <memory>
-#include <type_traits>
-#include <utility>
-
-namespace gmx
-{
-namespace compat
-{
-
-///\cond
-
-// All gmx::compat code should use std::unique_ptr
-using ::std::unique_ptr;
-
-template<class T> struct Unique_if {
- typedef unique_ptr<T> Single_object;
-};
-
-template<class T> struct Unique_if<T[]> {
- typedef unique_ptr<T[]> Unknown_bound;
-};
-
-template<class T, size_t N> struct Unique_if<T[N]> {
- typedef void Known_bound;
-};
-
-template<class T, class ... Args>
-typename Unique_if<T>::Single_object
-make_unique(Args && ... args)
-{
- return unique_ptr<T>(new T(::std::forward<Args>(args) ...));
-}
-
-template<class T>
-typename Unique_if<T>::Unknown_bound
-make_unique(size_t n)
-{
- typedef typename ::std::remove_extent<T>::type U;
- return unique_ptr<T>(new U[n]());
-}
-
-template<class T, class ... Args>
-typename Unique_if<T>::Known_bound
-make_unique(Args && ...) = delete;
-
-///\endcond
-
-} // namespace gmx::compat
-} // namespace gmx
-
-#endif // header guard
add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src
${CMAKE_CURRENT_BINARY_DIR}/googletest-build
EXCLUDE_FROM_ALL)
-
+ add_library(GTest::Main ALIAS gtest_main)
else()
- # use googletest git subtree from the current repository
- # subdirectory already included in src/external/CMakeLists.txt
+ find_package(GTest REQUIRED)
endif() # DOWNLOAD_GOOGLETEST
#
#
-# Copy test files to test directory
-configure_file(data/topol.tpr topol.tpr COPYONLY)
-configure_file(data/water.gro water.gro COPYONLY)
+# Copy test files
+# TODO: solve test data file requirement in a follow up change
+file(DOWNLOAD
+ https://github.com/kassonlab/sample_restraint/raw/master/tests/data/topol.tpr
+ ${CMAKE_CURRENT_SOURCE_DIR}/topol.tpr
+ STATUS _download_status
+ LOG _download_log)
+list(GET _download_status 0 _status)
+if(${_status} GREATER 0)
+ message(WARNING "Could not download test data: ${_download_log}")
+else()
+ configure_file(topol.tpr ${CMAKE_CURRENT_BINARY_DIR}/topol.tpr COPYONLY)
+endif()
+unset(_status)
+# Note: Expects topol.tpr to be in CURRENT_BINARY_DUR
configure_file(testingconfiguration.in.h testingconfiguration.h)
# Test that the library can access its dependencies and build.
add_executable(library-test test_binding.cpp)
-add_test(BasicPlugin library-test)
+gtest_add_tests(TARGET library-test
+ TEST_LIST BasicPlugin)
target_include_directories(library-test PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(library-test Gromacs::gmxapi gtest_main)
-set_tests_properties(BasicPlugin PROPERTIES
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+set_tests_properties(${BasicPlugin} PROPERTIES
COMPILE_DEFINITIONS "${GMOCK_COMPILE_DEFINITIONS}"
COMPILE_FLAGS "${GMOCK_COMPILE_FLAGS}")
# Test the C++ code implementing the basic harmonic potential.
add_executable(harmonic-test test_harmonic.cpp)
-add_test(HarmonicPotentialPlugin harmonic-test)
+gtest_add_tests(TARGET harmonic-test
+ TEST_LIST HarmonicPotentialPlugin)
target_include_directories(harmonic-test PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(harmonic-test harmonicpotential Gromacs::gmxapi
gtest_main)
-set_tests_properties(HarmonicPotentialPlugin PROPERTIES
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+set_tests_properties(${HarmonicPotentialPlugin} PROPERTIES
COMPILE_DEFINITIONS "${GMOCK_COMPILE_DEFINITIONS}"
COMPILE_FLAGS "${GMOCK_COMPILE_FLAGS}")
# Test the C++ force evaluation for the restrained-ensemble biasing potential.
add_executable(histogram-test test_histogram.cpp)
-add_test(EnsembleHistogramPotentialPlugin histogram-test)
+gtest_add_tests(TARGET histogram-test
+ TEST_LIST EnsembleHistogramPotentialPlugin)
target_include_directories(histogram-test PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(histogram-test ensemblepotential Gromacs::gmxapi
gtest_main)
-set_tests_properties(EnsembleHistogramPotentialPlugin PROPERTIES
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+set_tests_properties(${EnsembleHistogramPotentialPlugin} PROPERTIES
COMPILE_DEFINITIONS "${GMOCK_COMPILE_DEFINITIONS}"
COMPILE_FLAGS "${GMOCK_COMPILE_FLAGS}")
# Test the flat-bottom bounding potential built in to the ensemble restraint.
add_executable(bounding-test test_bounding_restraint.cpp)
-add_test(EnsembleBoundingPotentialPlugin bounding-test)
+gtest_add_tests(TARGET bounding-test
+ TEST_LIST EnsembleBoundingPotentialPlugin)
target_include_directories(bounding-test PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties(bounding-test PROPERTIES SKIP_BUILD_RPATH FALSE)
target_link_libraries(bounding-test ensemblepotential Gromacs::gmxapi
- gtest_main)
+ GTest::Main)
-set_tests_properties(EnsembleBoundingPotentialPlugin PROPERTIES
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+set_tests_properties(${EnsembleBoundingPotentialPlugin} PROPERTIES
COMPILE_DEFINITIONS "${GMOCK_COMPILE_DEFINITIONS}"
COMPILE_FLAGS "${GMOCK_COMPILE_FLAGS}")
def tpr_filename():
"""Provide a sample TPR file by filename."""
current_dir = os.path.dirname(__file__)
- file_path = os.path.join(current_dir, 'data', 'topol.tpr')
+ file_path = os.path.join(current_dir, 'topol.tpr')
return os.path.abspath(file_path)
}
};
-// This should be in a validation test suite, not a unit test...
-//TEST(ApiRestraint, MdAndPlugin)
-//{
-//
-// {
-// // Run a lot of steps of a large system.
-// std::string waterfile = "water.tpr";
-// auto system = gmxapi::fromTprFile(waterfile);
-// std::shared_ptr<gmxapi::Context> context = gmxapi::defaultContext();
-//
-// auto module = std::make_shared<SimpleApiModule>();
-// system->setRestraint(module);
-//
-// auto session = system->launch(context);
-//
-// gmxapi::Status status;
-// ASSERT_NO_THROW(status = session->run());
-//// ASSERT_TRUE(module->force_called() > 0);
-//// ASSERT_NO_THROW(session->run(1000));
-// ASSERT_TRUE(status.success());
-// }
-//
-//}
-
} // end anonymous namespace
import logging
import os
-import gmx
+import gmxapi as gmx
+from gmxapi.simulation.context import ParallelArrayContext
+from gmxapi.simulation.workflow import WorkElement, from_tpr
+from gmxapi import version as gmx_version
import pytest
from tests.conftest import withmpi_only
def test_harmonic_potential(tpr_filename):
print("Testing plugin potential with input file {}".format(os.path.abspath(tpr_filename)))
- md = gmx.workflow.from_tpr(tpr_filename, append_output=False)
+ md = from_tpr(tpr_filename, append_output=False)
+
+ context = ParallelArrayContext(md)
+ with context as session:
+ session.run()
# Create a WorkElement for the potential
- #potential = gmx.core.TestModule()
params = {'sites': [1, 4],
'R0': 2.0,
'k': 10000.0}
- potential_element = gmx.workflow.WorkElement(namespace="myplugin",
- operation="create_restraint",
- params=params)
+ potential_element = WorkElement(namespace="myplugin",
+ operation="create_restraint",
+ params=params)
# Note that we could flexibly capture accessor methods as workflow elements, too. Maybe we can
# hide the extra Python bindings by letting myplugin.HarmonicRestraint automatically convert
# to a WorkElement when add_dependency is called on it.
# potential = myplugin.HarmonicRestraint()
# potential.set_params(1, 4, 2.0, 10000.0)
- with gmx.get_context(md) as session:
+ context = ParallelArrayContext(md)
+ with context as session:
session.run()
def test_ensemble_potential_nompi(tpr_filename):
"""Test ensemble potential without an ensemble.
"""
-
print("Testing plugin potential with input file {}".format(os.path.abspath(tpr_filename)))
- assert gmx.version.api_is_at_least(0,0,5)
- assert not gmx.version.api_is_at_least(0,0,8)
- md = gmx.workflow.from_tpr([tpr_filename], append_output=False)
+ assert gmx.version.api_is_at_least(0, 0, 5)
+ md = from_tpr([tpr_filename], append_output=False)
# Create a WorkElement for the potential
- #potential = gmx.core.TestModule()
params = {'sites': [1, 4],
'nbins': 10,
'binWidth': 0.1,
'min_dist': 0.,
'max_dist': 10.,
- 'experimental': [1.]*10,
+ 'experimental': [1.] * 10,
'nsamples': 1,
'sample_period': 0.001,
'nwindows': 4,
'k': 10000.,
'sigma': 1.}
- potential = gmx.workflow.WorkElement(namespace="myplugin",
- operation="ensemble_restraint",
- params=params)
+ potential = WorkElement(namespace="myplugin",
+ operation="ensemble_restraint",
+ params=params)
# Note that we could flexibly capture accessor methods as workflow elements, too. Maybe we can
# hide the extra Python bindings by letting myplugin.HarmonicRestraint automatically convert
# to a WorkElement when add_dependency is called on it.
potential.name = "ensemble_restraint"
md.add_dependency(potential)
- with gmx.get_context(md) as session:
+ context = ParallelArrayContext(md)
+
+ with context as session:
session.run()
logger.info("Testing plugin potential with input file {}".format(os.path.abspath(tpr_filename)))
- assert gmx.version.api_is_at_least(0,0,5)
- assert not gmx.version.api_is_at_least(0,0,8)
- md = gmx.workflow.from_tpr([tpr_filename, tpr_filename], append_output=False)
+ assert gmx_version.api_is_at_least(0, 0, 5)
+ md = from_tpr([tpr_filename, tpr_filename], append_output=False)
# Create a WorkElement for the potential
- #potential = gmx.core.TestModule()
params = {'sites': [1, 4],
'nbins': 10,
'binWidth': 0.1,
'min_dist': 0.,
'max_dist': 10.,
- 'experimental': [0.5]*10,
+ 'experimental': [0.5] * 10,
'nsamples': 1,
'sample_period': 0.001,
'nwindows': 4,
'k': 10000.,
'sigma': 1.}
- potential = gmx.workflow.WorkElement(namespace="myplugin",
- operation="ensemble_restraint",
- params=params)
+ potential = WorkElement(namespace="myplugin",
+ operation="ensemble_restraint",
+ params=params)
# Note that we could flexibly capture accessor methods as workflow elements, too. Maybe we can
# hide the extra Python bindings by letting myplugin.HarmonicRestraint automatically convert
# to a WorkElement when add_dependency is called on it.
potential.name = "ensemble_restraint"
md.add_dependency(potential)
- with gmx.get_context(md) as session:
+ context = ParallelArrayContext(md)
+ with context as session:
session.run()
EXPECT_FLOAT_EQ(real(0.5*k*4*R0*R0), energy) << " where energy is " << energy << "\n";
}
-// This should be part of a validation test, not a unit test.
-//TEST(HarmonicPotentialPlugin, Bind)
-//{
-//
-// {
-// std::string waterfile = "water.tpr";
-// auto system = gmxapi::fromTprFile(waterfile);
-// std::shared_ptr<gmxapi::Context> context = gmxapi::defaultContext();
-//
-// auto module = std::make_shared<plugin::HarmonicModule>();
-// module->setParams(1, 4, 2.0, 100.0);
-// system->setRestraint(module);
-//
-// auto session = system->launch(context);
-//
-// gmxapi::Status status;
-// ASSERT_NO_THROW(status = session->run());
-//// ASSERT_TRUE(module->force_called() > 0);
-//// ASSERT_NO_THROW(session->run(1000));
-// ASSERT_TRUE(status.success());
-// }
-//
-//}
-
} // end anonymous namespace
int main(int argc, char* argv[])
*/
}
-// This should be part of a validation test, not a unit test.
-//TEST(HarmonicPotentialPlugin, Bind)
-//{
-//
-// {
-// std::string waterfile = "water.tpr";
-// auto system = gmxapi::fromTprFile(waterfile);
-// std::shared_ptr<gmxapi::Context> context = gmxapi::defaultContext();
-//
-// auto module = std::make_shared<plugin::HarmonicModule>();
-// module->setParams(1, 4, 2.0, 100.0);
-// system->setRestraint(module);
-//
-// auto session = system->launch(context);
-//
-// gmxapi::Status status;
-// ASSERT_NO_THROW(status = session->run());
-//// ASSERT_TRUE(module->force_called() > 0);
-//// ASSERT_NO_THROW(session->run(1000));
-// ASSERT_TRUE(status.success());
-// }
-//
-//}
-
} // end anonymous namespace
int main(int argc, char* argv[])