From 6f12522c6b5d547d81acf23959a0cf6f326425b3 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Mon, 7 May 2018 12:21:51 +0300 Subject: [PATCH 1/5] Begin 0.0.6 development cycle --- .travis.yml | 8 +++++--- CMakeLists.txt | 2 +- setup.py | 4 ++-- src/gmx/core/CMakeLists.txt | 2 +- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0ccea9935f..1a0184b19a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,8 @@ matrix: - GMX_THREAD_MPI=ON - gmxapi_DIR=$HOME/gromacs - GROMACS_DIR=$HOME/gromacs + - GROMACS_TAG=devel +# Todo: GROMACS_TAG=devel needs to be fixed before merging to a release branch. # It is clearly a bad idea to have the development branch of a project requiring the development branch # of another project in the primary build recipe. We either need to build and test development and master @@ -40,11 +42,11 @@ before_install: - apt list --installed - test -n $CC && unset CC - test -n $CXX && unset CXX - - wget https://github.com/kassonlab/gromacs-gmxapi/archive/master.zip - - unzip master.zip + - wget https://github.com/kassonlab/gromacs-gmxapi/archive/$GROMACS_TAG.zip + - unzip $GROMACS_TAG.zip install: - - pushd gromacs-gmxapi-master + - pushd gromacs-gmxapi-$GROMACS_TAG - mkdir build - pushd build - cmake -DCMAKE_INSTALL_PREFIX=$HOME/gromacs -DGMX_FFT_LIBRARY=fftpack -DGMX_GPU=OFF -DGMX_OPENMP=OFF -DGMX_SIMD=None -DGMX_USE_RDTSCP=OFF -DGMX_MPI=$GMX_MPI -DGMX_THREAD_MPI=$GMX_THREAD_MPI .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bd06ce860..9e278d6add 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.4.3) #list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) # Sets the PROJECT_VERSION variable, as well... -project(gmxpy VERSION 0.0.5) +project(gmxpy VERSION 0.0.6) add_subdirectory(pybind11) diff --git a/setup.py b/setup.py index 7801d001cd..21b42db93d 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ from setuptools.command.test import test as TestCommand #import gmx.version -__version__ = '0.0.5' +__version__ = '0.0.6' extra_link_args=[] @@ -210,7 +210,7 @@ def build_extension(self, ext): if build_gromacs: # TODO! We need to distinguish dev branch builds from master branch builds or always build with the # master branch of the dependency. For one thing, as is, this line needs to be toggled for every release. - gromacs_url = "https://github.com/kassonlab/gromacs-gmxapi/archive/dev_5.zip" + gromacs_url = "https://github.com/kassonlab/gromacs-gmxapi/archive/devel.zip" gmxapi_DIR = os.path.join(extdir, 'data/gromacs') if build_for_readthedocs: extra_cmake_args = ['-DCMAKE_INSTALL_PREFIX=' + gmxapi_DIR, diff --git a/src/gmx/core/CMakeLists.txt b/src/gmx/core/CMakeLists.txt index 49ab1956f0..35cd8b417e 100644 --- a/src/gmx/core/CMakeLists.txt +++ b/src/gmx/core/CMakeLists.txt @@ -7,7 +7,7 @@ # cmake was invoked with `-DCMAKE_PREFIX_PATH=...` pointing to the GROMACS # installation directory. We can also check now for a GROMACS_DIR environment # variable and provide it with the HINTS option. -find_package(gmxapi 0.0.5 REQUIRED +find_package(gmxapi 0.0.6 REQUIRED HINTS "$ENV{GROMACS_DIR}" ) From 72e3c1d151bead9eeb978a3ce0469569b516ff9a Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Sat, 5 May 2018 16:17:53 +0300 Subject: [PATCH 2/5] allow full CMake-driven build and install. --- CMakeLists.txt | 36 +++++++++++++++++++++++++++++++++++- setup.py | 2 +- src/gmx/CMakeLists.txt | 27 +++++++++++++++++++++++++++ src/gmx/core/CMakeLists.txt | 9 +++++---- src/gmx/version.in | 11 +++++++++++ 5 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 src/gmx/CMakeLists.txt create mode 100644 src/gmx/version.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e278d6add..abf6764329 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,40 @@ cmake_minimum_required(VERSION 3.4.3) # Sets the PROJECT_VERSION variable, as well... project(gmxpy VERSION 0.0.6) +# If the user is not in a virtual environment and is not a privileged user and has not specified an install locatoin +# for the Python module (GMXAPI_INSTALL_PATH), this option causes the automatic install location to query the user +# site-packages directory instead of using the default site-packages directory for the interpreter. +option(GMXAPI_USER_INSTALL "Override the default site-packages directory with the user-specific Python packages directory.\ +(Do not use with virtual environments.)") +# Since a user may have multiple virtual environments with different Python interpreters, it is generally confusing to +# have a package for a virtual environment installed in the user's default user site-packages directory. + +unset(PYTHONINTERP_FOUND) +unset(PYTHONLIBS_FOUND) +find_package(PythonInterp) +if (PYTHONINTERP_FOUND) + message(STATUS "Found Python interpreter: ${PYTHON_EXECUTABLE}") + find_package(PythonLibs) + if (PYTHONLIBS_FOUND) + if (GMXAPI_USER_INSTALL) + execute_process(COMMAND ${PYTHON_EXECUTABLE} -m site --user OUTPUT_VARIABLE GMXAPI_DEFAULT_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Python user site-packages directory is ${GMXAPI_DEFAULT_SITE_PACKAGES}") + else() + execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import sys; import os; print(os.path.abspath(os.path.join(sys.prefix, 'lib', 'python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}', 'site-packages')))" OUTPUT_VARIABLE GMXAPI_DEFAULT_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Python site-packages directory is ${GMXAPI_DEFAULT_SITE_PACKAGES}") + endif() + else() + message(FATAL "Found Python interpreter ${PYTHON_EXECUTABLE} but this Python installation does not have developer tools." + "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.") +endif() + + +set(GMXAPI_INSTALL_PATH ${GMXAPI_DEFAULT_SITE_PACKAGES}/gmx CACHE PATH "Path to Python module install location (site-packages).") + + add_subdirectory(pybind11) -add_subdirectory(src/gmx/core) +add_subdirectory(src/gmx) diff --git a/setup.py b/setup.py index 21b42db93d..0c567fd23d 100644 --- a/setup.py +++ b/setup.py @@ -248,7 +248,7 @@ def build_extension(self, ext): os.makedirs(self.build_temp) # cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir] - cmake_args += ['-DCMAKE_INSTALL_PREFIX=' + extdir] + cmake_args += ['-DGMXAPI_INSTALL_PATH=' + extdir] # if platform.system() == "Windows": # cmake_args += ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}'.format(cfg.upper(), extdir)] try: diff --git a/src/gmx/CMakeLists.txt b/src/gmx/CMakeLists.txt new file mode 100644 index 0000000000..42cc2dc26a --- /dev/null +++ b/src/gmx/CMakeLists.txt @@ -0,0 +1,27 @@ +file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/gmx/data) + +configure_file(__init__.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(context.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(data.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(exceptions.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(fileio.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(status.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(system.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(util.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) +configure_file(workflow.py ${CMAKE_BINARY_DIR}/gmx/ COPYONLY) + +configure_file(data/topol.tpr ${CMAKE_BINARY_DIR}/gmx/data/ COPYONLY) + +configure_file(version.in ${CMAKE_BINARY_DIR}/gmx/version.py @ONLY) + +file(COPY test DESTINATION ${CMAKE_BINARY_DIR}/gmx/) + +install(DIRECTORY ${CMAKE_BINARY_DIR}/gmx/ + DESTINATION ${GMXAPI_INSTALL_PATH} + FILES_MATCHING PATTERN "*.py" PATTERN "*.ini" + ) + +install(DIRECTORY ${CMAKE_BINARY_DIR}/gmx/data + DESTINATION ${GMXAPI_INSTALL_PATH}) + +add_subdirectory(core) diff --git a/src/gmx/core/CMakeLists.txt b/src/gmx/core/CMakeLists.txt index 35cd8b417e..4e50b9b450 100644 --- a/src/gmx/core/CMakeLists.txt +++ b/src/gmx/core/CMakeLists.txt @@ -39,6 +39,8 @@ set_target_properties(pygmx_core PROPERTIES OUTPUT_NAME core) # pygmx_core # PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${PYGMX_DIRECTORY}") +set_target_properties(pygmx_core PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/gmx) + target_include_directories(pygmx_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} @@ -71,7 +73,6 @@ endif() set_target_properties(pygmx_core PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) install(TARGETS pygmx_core - LIBRARY DESTINATION - ${CMAKE_INSTALL_PREFIX} - ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX} - RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + LIBRARY DESTINATION ${GMXAPI_INSTALL_PATH} + ARCHIVE DESTINATION ${GMXAPI_INSTALL_PATH} + RUNTIME DESTINATION ${GMXAPI_INSTALL_PATH}) diff --git a/src/gmx/version.in b/src/gmx/version.in new file mode 100644 index 0000000000..251b6b9f7a --- /dev/null +++ b/src/gmx/version.in @@ -0,0 +1,11 @@ +# Version file generated by CMake +__version__ = "@PROJECT_VERSION@" +major = @PROJECT_VERSION_MAJOR@ +minor = @PROJECT_VERSION_MINOR@ +patch = @PROJECT_VERSION_PATCH@ + +def api_is_at_least(a, b, c): + return (major >= a) and (minor >= b) and (patch >= c) + +# Todo: Release status needs to be automatically maintained. +release = False From e831bad02bae6b78869ebdf38a746484d4e8bcd1 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Sat, 5 May 2018 19:22:53 +0300 Subject: [PATCH 3/5] clean-up tasks... --- src/gmx/core/export_system.cpp | 4 +++- src/gmx/data.py | 2 +- src/gmx/test/test_pymd.py | 13 ------------- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/gmx/core/export_system.cpp b/src/gmx/core/export_system.cpp index fa443a4201..e1e05e464e 100644 --- a/src/gmx/core/export_system.cpp +++ b/src/gmx/core/export_system.cpp @@ -32,7 +32,9 @@ void export_system(py::module &m) py::class_ > system(m, "MDSystem"); system.def(py::init(), "A blank system object is possible, but not useful. Use a helper function."); system.def("launch", - [](System* system){ return system->launch(); }, + [](System* system){ + return system->launch(); + }, "Launch the configured workflow in the default context."); system.def("launch", [](System* system, std::shared_ptr context) diff --git a/src/gmx/data.py b/src/gmx/data.py index 502f19105a..e4752863c3 100644 --- a/src/gmx/data.py +++ b/src/gmx/data.py @@ -18,6 +18,6 @@ raise exceptions.OptionalFeatureNotAvailableWarning("Need pkg_resources from setuptools package to access gmx package data.") if os.path.exists(_tpr_filename) and os.path.isfile(_tpr_filename): - tpr_filename = _tpr_filename + tpr_filename = os.path.abspath(_tpr_filename) else: raise exceptions.OptionalFeatureNotAvailableError('Package data file data/topol.tpr not accessible at {}'.format(_tpr_filename)) diff --git a/src/gmx/test/test_pymd.py b/src/gmx/test/test_pymd.py index b116b74614..468fc578c6 100644 --- a/src/gmx/test/test_pymd.py +++ b/src/gmx/test/test_pymd.py @@ -62,19 +62,6 @@ def test_APIObjectsFromTpr(self): apisystem = gmx.core.from_tpr(tpr_filename) assert isinstance(apisystem, gmx.core.MDSystem) assert hasattr(apisystem, 'launch') - session = apisystem.launch() - assert hasattr(session, 'run') - session.run() - # Test rerunability - # system = gmx.System() - # runner = gmx.runner.SimpleRunner() - # runner._runner = apirunner - # system.runner = runner - # assert isinstance(system, gmx.System) - # assert isinstance(system.runner, gmx.runner.Runner) - # assert isinstance(system.runner._runner, gmx.core.SimpleRunner) - # with gmx.context.DefaultContext(system.runner) as session: - # session.run() def test_SystemFromTpr(self): system = gmx.System._from_file(tpr_filename) system.run() From b93cd4de3375b35fe14fe317d14a81cae5a68f07 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Sun, 6 May 2018 21:19:25 +0300 Subject: [PATCH 4/5] manage working directories a bit better. --- src/gmx/CMakeLists.txt | 3 +++ src/gmx/context.py | 32 ++++++++++++++++++++----- src/gmx/core/export_context.cpp | 36 ++++++++++++++++++++++++---- src/gmx/test/test_mpiarraycontext.py | 4 ++-- 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/src/gmx/CMakeLists.txt b/src/gmx/CMakeLists.txt index 42cc2dc26a..a349b6087a 100644 --- a/src/gmx/CMakeLists.txt +++ b/src/gmx/CMakeLists.txt @@ -16,6 +16,9 @@ configure_file(version.in ${CMAKE_BINARY_DIR}/gmx/version.py @ONLY) file(COPY test DESTINATION ${CMAKE_BINARY_DIR}/gmx/) +# Todo: we need a target dependent on these files to force fresh configure / copy and to do python -m compileall +# for the install target. + install(DIRECTORY ${CMAKE_BINARY_DIR}/gmx/ DESTINATION ${GMXAPI_INSTALL_PATH} FILES_MATCHING PATTERN "*.py" PATTERN "*.ini" diff --git a/src/gmx/context.py b/src/gmx/context.py index 23e5926a74..0c1a5d045b 100644 --- a/src/gmx/context.py +++ b/src/gmx/context.py @@ -9,6 +9,8 @@ __all__ = ['Context', 'DefaultContext'] + +import contextlib import os import warnings import networkx as nx @@ -24,6 +26,21 @@ logger = logging.getLogger(__name__) logger.info('Importing gmx.context') +# ref http://code.activestate.com/recipes/576620-changedirectory-context-manager/#c3 +# Does this really not already exist in os.path or something? +@contextlib.contextmanager +def working_directory(path): + """A context manager which changes the working directory to the given + path, and then changes it back to its previous value on exit. + + """ + prev_cwd = os.getcwd() + os.chdir(path) + try: + yield + finally: + os.chdir(prev_cwd) + class Context(object): """ Proxy to API Context provides Python context manager. @@ -588,6 +605,7 @@ def __enter__(self): instantiate objects to perform the work. In the first implementation, we kind of muddle things into a single pass. """ + self.__cwd = os.getcwd() import numpy try: from mpi4py import MPI @@ -675,12 +693,6 @@ def update(send, recv, tag=None): if workdir_list is None: workdir_list = [os.path.join('.', str(i)) for i in range(self.size)] self.__workdir_list = list([os.path.abspath(dir) for dir in workdir_list]) - for dir in self.__workdir_list: - if os.path.exists(dir): - if not os.path.isdir(dir): - raise exceptions.FileError('{} is not a valid working directory.'.format(dir)) - else: - os.mkdir(dir) # Check the session "width" against the available parallelism if (self.size > comm_size): @@ -704,6 +716,13 @@ def update(send, recv, tag=None): logger.info("Launching work on rank {}.".format(self.rank)) # Launch the work for this rank self.workdir = self.__workdir_list[self.rank] + if os.path.exists(self.workdir): + if not os.path.isdir(self.workdir): + raise exceptions.FileError('{} is not a valid working directory.'.format(self.workdir)) + else: + os.mkdir(self.workdir) + + # This session will live in a subdirectory of the working directory os.chdir(self.workdir) logger.info('rank {} changed directory to {}'.format(self.rank, self.workdir)) sorted_nodes = nx.topological_sort(graph) @@ -762,6 +781,7 @@ def __exit__(self, exception_type, exception_value, traceback): # \todo Make sure session has ended on all ranks before continuing and handle final errors. self._session = None + os.chdir(self.__cwd) return False def get_context(work=None): diff --git a/src/gmx/core/export_context.cpp b/src/gmx/core/export_context.cpp index 8f0aa3c502..9dc3b102f8 100644 --- a/src/gmx/core/export_context.cpp +++ b/src/gmx/core/export_context.cpp @@ -6,6 +6,7 @@ #include "gmxapi/context.h" +#include "gromacs/utility/init.h" namespace gmxpy { @@ -97,15 +98,42 @@ void setMDArgs(std::vector* mdargs, py::dict params) void export_context(py::module &m) { using ::gmxapi::Context; - // Export execution context class - py::class_> context(m, "Context"); - context.def(py::init(), "Create a default execution context."); - context.def("setMDArgs", &Context::setMDArgs, "Set MD runtime parameters."); using MDArgs = std::vector; py::class_> mdargs(m, "MDArgs"); mdargs.def(py::init(), "Create an empty MDArgs object."); mdargs.def("set", &setMDArgs, "Assign parameters in MDArgs from Python dict."); + + // Export execution context class + py::class_> context(m, "Context"); + context.def(py::init(), "Create a default execution context."); + context.def("setMDArgs", &Context::setMDArgs, "Set MD runtime parameters."); + + // During the registration of the gmx.core.Context Python type, perform appropriate environment initialization + // and deinitialize when the type object is destroyed at module destruction. + + // Register a callback function that is invoked when the gmx.core.Context type object is collected + // ref http://pybind11.readthedocs.io/en/stable/advanced/misc.html?highlight=weakref#module-destructors + py::cpp_function cleanup_callback( + [](py::handle weakref) { + // perform cleanup here -- this function is called with the GIL held + // This may not be good enough. What if MPI_Init is called before gmx::init, but MPI_Finalize is called + // before the Context type object is destructed? + gmx::finalize(); + + weakref.dec_ref(); // release weak reference + } + ); + + if (Context::hasMPI()) + { + gmx::init(nullptr, + nullptr); + // Create a weak reference with a cleanup callback and initially leak it + (void) py::weakref(m.attr("Context"), cleanup_callback).release(); + } + // Should we add an IProgramContext implementation? If so, how should we find the libgromacs install location? + } } // end namespace gmxpy::detail diff --git a/src/gmx/test/test_mpiarraycontext.py b/src/gmx/test/test_mpiarraycontext.py index dd8e2acab9..452fa170bd 100644 --- a/src/gmx/test/test_mpiarraycontext.py +++ b/src/gmx/test/test_mpiarraycontext.py @@ -148,8 +148,8 @@ def test_basic(self): with context as session: session.run() # This is a sloppy way to see if the current rank had work to do. - if hasattr(context, "workdir"): - rank = context.rank + rank = context.rank + if rank == 0: output_path = os.path.join(context.workdir, 'traj.trr') assert(os.path.exists(output_path)) print("Worker {} produced {}".format(rank, output_path)) From 7fedeb36c997f90a5906af1f963db3c00a6b56a7 Mon Sep 17 00:00:00 2001 From: "M. Eric Irrgang" Date: Mon, 7 May 2018 21:54:37 +0300 Subject: [PATCH 5/5] Initialize and deinitialize GROMACS at module scope. We call gmx::initialize() in the import code and gmx::finalize() on the destruction of an object stored as a module variable. This means that if we want mpi4py to manage MPI initialization and deinitialization, we need to import it before importing gmx. To do: we need to pass a communicator to GROMACS without initializing or finalizing MPI. We should probably do that when sessions are launched in a context. We should probably store a "current context" as a module member variable, and we could tie MPI initialization and deinitialization to that with the caveat that, once created, the first context to initialize MPI needs to be the last one to use it. To support all of this, we need to make substantial updates to GROMACS to use a received subcommunicator instead of MPI_COMM_WORLD. For the moment, we should be able to use MPI_COMM_WORLD if we prohibit running jobs whose workflow width does not match the MPI width. We can't use ensembles, though, until we manage the communicator. Mark's suggestion the quickest band-aid is to override the definition of MPI_COMM_WORLD and manage a global variable in basenetwork.h. --- src/gmx/core/export_context.cpp | 29 ++++------------------- src/gmx/test/test_mpiarraycontext.py | 35 +++++++++++++++++----------- 2 files changed, 27 insertions(+), 37 deletions(-) diff --git a/src/gmx/core/export_context.cpp b/src/gmx/core/export_context.cpp index 9dc3b102f8..edbd388b34 100644 --- a/src/gmx/core/export_context.cpp +++ b/src/gmx/core/export_context.cpp @@ -2,6 +2,8 @@ // Created by Eric Irrgang on 3/18/18. // +#include +#include #include "core.h" #include "gmxapi/context.h" @@ -110,30 +112,9 @@ void export_context(py::module &m) context.def("setMDArgs", &Context::setMDArgs, "Set MD runtime parameters."); // During the registration of the gmx.core.Context Python type, perform appropriate environment initialization - // and deinitialize when the type object is destroyed at module destruction. - - // Register a callback function that is invoked when the gmx.core.Context type object is collected - // ref http://pybind11.readthedocs.io/en/stable/advanced/misc.html?highlight=weakref#module-destructors - py::cpp_function cleanup_callback( - [](py::handle weakref) { - // perform cleanup here -- this function is called with the GIL held - // This may not be good enough. What if MPI_Init is called before gmx::init, but MPI_Finalize is called - // before the Context type object is destructed? - gmx::finalize(); - - weakref.dec_ref(); // release weak reference - } - ); - - if (Context::hasMPI()) - { - gmx::init(nullptr, - nullptr); - // Create a weak reference with a cleanup callback and initially leak it - (void) py::weakref(m.attr("Context"), cleanup_callback).release(); - } - // Should we add an IProgramContext implementation? If so, how should we find the libgromacs install location? - + // and deinitialize at module destruction. + gmx::init(nullptr, nullptr); + m.add_object("_current_context", py::capsule([](){ gmx::finalize(); std::cout << "Shutting down GROMACS" << std::endl; })); } } // end namespace gmxpy::detail diff --git a/src/gmx/test/test_mpiarraycontext.py b/src/gmx/test/test_mpiarraycontext.py index 452fa170bd..51bb439a44 100644 --- a/src/gmx/test/test_mpiarraycontext.py +++ b/src/gmx/test/test_mpiarraycontext.py @@ -15,12 +15,6 @@ # add the handlers to the logger logging.getLogger().addHandler(ch) -import gmx -import gmx.core -import os -# Get a test tpr filename -from gmx.data import tpr_filename - try: from mpi4py import MPI withmpi_only = pytest.mark.skipif(not MPI.Is_initialized() or MPI.COMM_WORLD.Get_size() < 2, @@ -28,6 +22,12 @@ except ImportError: withmpi_only = pytest.mark.skip(reason="Test requires at least 2 MPI ranks, but mpi4py is not available.") +import gmx +import gmx.core +import os +# Get a test tpr filename +from gmx.data import tpr_filename + class ConsumerElement(gmx.workflow.WorkElement): """Simple workflow element to test the shared data resource.""" def __init__(self, name): @@ -138,10 +138,11 @@ def launch_test_consumer(rank=None): @pytest.mark.usefixtures("cleandir") class ArrayContextTestCase(unittest.TestCase): def test_basic(self): - md = gmx.workflow.from_tpr(tpr_filename) - context = gmx.context.ParallelArrayContext(md) - with context as session: - session.run() + # Todo: let Context run work that will fit, even if it is narrower. + # md = gmx.workflow.from_tpr(tpr_filename) + # context = gmx.context.ParallelArrayContext(md) + # with context as session: + # session.run() md = gmx.workflow.from_tpr([tpr_filename, tpr_filename]) context = gmx.context.ParallelArrayContext(md) @@ -178,11 +179,18 @@ def test_shared_data(self): context.work = workspec # Confirm that oversized width is caught - import mpi4py - size = mpi4py.MPI.COMM_WORLD.Get_size() + from mpi4py import MPI + size = MPI.COMM_WORLD.Get_size() + rank = MPI.COMM_WORLD.Get_rank() + logging.debug("Attempting to launch work with width 3 on rank {}".format(rank)) if size < width: with pytest.raises(gmx.exceptions.UsageError): - context.__enter__() + with context: + pass + # We need to make sure that all ranks in the communicator enter and exit the context. We can probably handle this better. + else: + with context: + pass # Create a workspec that we expect to be runnable. consumer.workspec = None @@ -196,6 +204,7 @@ def test_shared_data(self): context.add_operation(consumer.namespace, consumer.operation, translate_test_consumer) context.work = workspec + logging.debug("Attempting to run session with width {} on rank {}".format(size, rank)) with context as session: session.run() assert session.graph.nodes[consumer.name]['check'] == True