Use gmxconfig.json to support some additional testing support.
Configure thread allocation for gmxapi CI jobs more carefully.
Fixes #3710
popd
# Run Python unit tests.
-python -m pytest python_packaging/src/test --junitxml=$PY_UNIT_TEST_XML
+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.
# 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)
+ $PWD/python_packaging/src/test \
+ --threads=1)
# shellcheck disable=SC2068
if [ -x `which mpiexec` ]; then
PYTHONDONTWRITEBYTECODE=1 \
# 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)
+ $PWD/python_packaging/test \
+ --threads=1)
# shellcheck disable=SC2068
if [ -x `which mpiexec` ]; then
PYTHONDONTWRITEBYTECODE=1 \
make install
popd
- python -m pytest $PWD/tests --junitxml=$PLUGIN_TEST_XML
+ 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.
# https://www.open-mpi.org/doc/v3.0/man1/mpiexec.1.php
PROGRAM=(`which python` -m mpi4py -m pytest \
-p no:cacheprovider \
- $PWD/tests)
+ $PWD/tests \
+ --threads=1)
# shellcheck disable=SC2068
if [ -x `which mpiexec` ]; then
PYTHONDONTWRITEBYTECODE=1 \
@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)
- # Note that *threads* argument causes errors for MPI-enabled GROMACS.
- # Ref #3563 and #3573
- md = from_tpr([tpr_filename], append_output=False, threads=2)
+ md = from_tpr([tpr_filename], append_output=False, **mdrun_kwargs)
# Create a WorkElement for the potential
params = {'sites': [1, 4],
@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)
- # Note that *threads* argument causes errors for MPI-enabled GROMACS.
- # Ref #3563 and #3573
- md = from_tpr([tpr_filename, tpr_filename], append_output=False, threads=2)
+ md = from_tpr([tpr_filename, tpr_filename], append_output=False, **mdrun_kwargs)
# Create a WorkElement for the potential
params = {'sites': [1, 4],
ensemble_rank,
self.workdir
))
- # TODO: Normalize the way we pass run time parameters to mdrun.
- # See also #3573
+ # 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)))
def pytest_addoption(parser):
- """Add a command-line user option for the pytest invocation."""
+ """Add command-line user options for the pytest invocation."""
parser.addoption(
'--rm',
action='store',
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):
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):
class GmxBin:
"""Represent the detected GROMACS installation."""
- def __init__(self):
+ def __init__(self, gmxconfig):
# Try to use package resources to locate the "gmx" binary wrapper.
- try:
- from importlib.resources import open_text
- with open_text('gmxapi', 'gmxconfig.json') as textfile:
- config = json.load(textfile)
- gmxbindir = config.get('gmx_bindir', None)
- command = config.get('gmx_executable', None)
- except ImportError:
- 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)
- gmxbindir = config.get('gmx_bindir', None)
- command = config.get('gmx_executable', None)
- except ImportError:
- gmxbindir = None
- command = None
+ 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']
return self._bindir
-_gmx = GmxBin()
-
-
@pytest.fixture(scope='session')
-def gmxcli():
- command = _gmx.command()
+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)
import gmxapi as gmx
-# TODO: (#3573) Normalize the handling of run-time arguments.
-from gmxapi.simulation.mdrun import ResourceManager as _ResourceManager
-# Note that *threads* argument causes errors for MPI-enabled GROMACS.
-# Ref #3563 and #3573
-_ResourceManager.mdrun_kwargs = {'threads': 2}
# Configure the `logging` module before proceeding any further.
gmx.logger.setLevel(logging.WARNING)
@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)
@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):
@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'):
@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)
import pytest
-# TODO: (#3573) Normalize the handling of run-time arguments.
+# TODO: (#3718) Normalize the handling of run-time arguments.
from gmxapi.simulation.mdrun import ResourceManager as _ResourceManager
-# Note that *threads* argument causes errors for MPI-enabled GROMACS.
-# Ref #3563 and #3573
_ResourceManager.mdrun_kwargs = {'threads': 2}
withmpi_only = None