diff --git a/.github/workflows/install-dev.yml b/.github/workflows/install-dev.yml deleted file mode 100644 index 80cfb30..0000000 --- a/.github/workflows/install-dev.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: install-dev - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - build: - runs-on: ${{ matrix.platform }} - strategy: - matrix: - platform: [ubuntu-latest] - python-version: [3.6, 3.7, 3.8, 3.9] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: taurenmddev - environment-file: requirements-dev.yml - python-version: ${{ matrix.python-version }} - channels: conda-forge - auto-activate-base: false - - - shell: bash -l {0} - run: | - conda info - conda list - -# - name: Install dependencies -# run: | -# conda env update -n taurenmddev --file requirements-dev.yml - - - name: Test versions - shell: bash -l {0} - run: | - python -c "from mdtraj import version; print('mdtraj version: ', version.version)" - python -c "from openmm import version; print('openmm version: ', version.version)" - python -c "from MDAnalysis import version; print('MDAnalysis version: ', version.__version__)" diff --git a/.github/workflows/install.yml b/.github/workflows/install.yml deleted file mode 100644 index b3db239..0000000 --- a/.github/workflows/install.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: install - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - build: - runs-on: ${{ matrix.platform }} - strategy: - matrix: - platform: [ubuntu-latest] - python-version: [3.6, 3.7, 3.8, 3.9] - - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: taurenmd - environment-file: requirements.yml - python-version: ${{ matrix.python-version }} - channels: conda-forge - auto-activate-base: false - - - shell: bash -l {0} - run: | - conda info - conda list - - - name: Test versions - shell: bash -l {0} - run: | - python -c "from mdtraj import version; print('mdtraj version: ', version.version)" - python -c "from openmm import version; print('openmm version: ', version.version)" - python -c "from MDAnalysis import version; print('MDAnalysis version: ', version.__version__)" diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3f0f9b9..172fca7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -10,6 +10,9 @@ Upon version 0.8, and before version 1, SV2 major version increments are reflect Changelog ========= +* Add support for CIF topologies in libmda using openmm.app.pdbxfile +* removes the install*.yml gitactions + v0.10.0 (2021-11-24) ------------------------------------------------------------ diff --git a/README.rst b/README.rst index cfaf3af..14f49dc 100644 --- a/README.rst +++ b/README.rst @@ -50,7 +50,7 @@ taurenmd :target: https://pypistats.org/packages/taurenmd **A command-line interface for analysis routines of Molecular Dynamics data.** - + **Taurenmd** provides an easy, flexible and extensible, **command-line** interface for the most common *(and not so common)* routines of analysis and representation of Molecular Dynamics (MD) data. It bridges the gap between the highly complex (and powerful) Python libraries available for analysis of MD data and the *non-developer* users that lack the programming skills to perform a thorough and proficient use those libraries. *But not only*, **taurenmd** also facilitates high throughput operations, even to those proficient *devs*, because complex executions are reduced to single argument-rich command-lines that can be concatenated or aliased. diff --git a/src/taurenmd/libs/libmda.py b/src/taurenmd/libs/libmda.py index a15b502..fb815b9 100644 --- a/src/taurenmd/libs/libmda.py +++ b/src/taurenmd/libs/libmda.py @@ -17,7 +17,7 @@ from taurenmd import Path from taurenmd import core as tcore from taurenmd import log -from taurenmd.libs import libcli, libio +from taurenmd.libs import libcli, libio, libopenmm from taurenmd.logger import S, T @@ -73,11 +73,17 @@ def load_universe( trajectories = libio.sort_numbered_input(*trajectories) libio.report_input(topology, trajectories) - universe = mda.Universe( - Path(topology).str(), - [Path(i).str() for i in trajectories], - **universe_args, - ) + + topo_path = Path(topology).str() + traj_path = [Path(i).str() for i in trajectories], + + try: + universe = mda.Universe(topo_path, traj_path, **universe_args) + + except ValueError: + pdbx = libopenmm.attempt_to_load_top_from_simtk(topo_path) + universe = mda.Universe(pdbx, traj_path, **universe_args) + report(universe) return universe diff --git a/src/taurenmd/libs/libmdt.py b/src/taurenmd/libs/libmdt.py index 547671d..c1bf71f 100644 --- a/src/taurenmd/libs/libmdt.py +++ b/src/taurenmd/libs/libmdt.py @@ -15,36 +15,16 @@ .. _Simtk OpenMM: http://openmm.org/ """ import os -import sys import mdtraj from taurenmd import Path from taurenmd import core as tcore from taurenmd import log -from taurenmd.libs import libcli, libio +from taurenmd.libs import libcli, libio, libopenmm from taurenmd.logger import S, T -try: - from simtk.openmm.app import PDBxFile - SIMTK = True -except ImportError: - SIMTK = False - - -def _log_simtkimport_error(): - msg = ( - "To use .cif files as Topologies taurenmd requires OpenMM, " - "which is currently not installed. " - "Please visit our Installation instruction at " - "https://taurenmd.readthedocs.io/" - ) - log.error(T('Dependency Error')) - log.error(S(msg)) - sys.exit(0) - - @libcli.add_reference(tcore.ref_mdt) def load_traj(topology, trajectories, insort=False): """ @@ -65,7 +45,8 @@ def load_traj(topology, trajectories, insort=False): trajectory : str or Path Path to the trajectory file. Accepts MDTraj compatible `files `_ - Returns ------- + Returns + ------- MDTraj trajectory `Trajectory object `_. """ # noqa: E501 @@ -79,41 +60,16 @@ def load_traj(topology, trajectories, insort=False): trajs = os.fspath(trajectories) libio.report_input(topology, trajs) - top = attempt_to_load_top_from_simtk(topology) - mdtrajectory = mdtraj.load(trajs, top=top) - - return mdtrajectory - - -@libcli.add_reference(tcore.ref_openmm) -def attempt_to_load_top_from_simtk(topology): - """ - Load topology from SIMTK. - Parameters - ---------- - topology : str or Path - - Returns - ------- - topology from mdtraj.Topology.from_openmm` - - Raises - ------ - Dependency error from :func:`_log_simtkimport_error`, program - halts. - """ - topp = Path(topology) - - if topp.suffix == '.cif' and SIMTK: - mol = PDBxFile(topp.str()) - return mdtraj.Topology.from_openmm(mol.topology) + if Path(topology).suffix == '.cif': + _top = libopenmm.attempt_to_load_top_from_simtk(topology) + top = mdtraj.Topology.from_openmm(_top.topology) + else: + top = Path(topology).str() - elif topp.suffix == '.cif' and not SIMTK: - _log_simtkimport_error() + mdtrajectory = mdtraj.load(trajs, top=top) - else: - return topp.str() + return mdtrajectory @libcli.add_reference(tcore.ref_mdt) diff --git a/src/taurenmd/libs/libopenmm.py b/src/taurenmd/libs/libopenmm.py new file mode 100644 index 0000000..493656e --- /dev/null +++ b/src/taurenmd/libs/libopenmm.py @@ -0,0 +1,67 @@ +""" +Functions that wrap around `OpenMM library`_. + +Read our `citing documentation`_ to understand how to cite multiple +libraries. + +.. _citing documentation: https://taurenmd.readthedocs.io/en/latest/citing.html +.. _OpenMM library: http://openmm.org/ +""" +import sys + + +try: + from openmm.app.pdbxfile import PDBxFile + SIMTK = True +except ImportError: + SIMTK = False + + +from taurenmd import Path +from taurenmd import core as tcore +from taurenmd import log +from taurenmd.libs import libcli +from taurenmd.logger import S, T + + +@libcli.add_reference(tcore.ref_openmm) +def attempt_to_load_top_from_simtk(topology): + """ + Load topology from SIMTK. + + Parameters + ---------- + topology : str or Path + + Returns + ------- + topology from mdtraj.Topology.from_openmm` + + Raises + ------ + Dependency error from :func:`_log_simtkimport_error`, program + halts. + """ + topp = Path(topology) + + if topp.suffix == '.cif' and SIMTK: + mol = PDBxFile(topp.str()) + return mol + + elif topp.suffix == '.cif' and not SIMTK: + _log_simtkimport_error() + + else: + raise ValueError(f'`topology` suffix is not CIF: {topp.suffix!r}') + + +def _log_simtkimport_error(): + msg = ( + "To use .cif files as Topologies taurenmd requires OpenMM, " + "which is currently not installed. " + "Please visit our Installation instruction at " + "https://taurenmd.readthedocs.io/" + ) + log.error(T('Dependency Error')) + log.error(S(msg)) + sys.exit(0) diff --git a/tests/test_libmda.py b/tests/test_libmda.py index 33d529c..c37f684 100644 --- a/tests/test_libmda.py +++ b/tests/test_libmda.py @@ -7,7 +7,7 @@ from taurenmd.libs import libmda as la -from . import toptest, trajtest +from . import toptest, toptest_cif, trajtest def test_load_universe_1(): @@ -20,6 +20,16 @@ def test_load_universe_1(): assert len(universe.trajectory) == 10 +def test_load_universe_1_cif(): + """Test load MDA Universe.""" + universe = la.load_universe( + toptest_cif, + trajtest, + ) + assert isinstance(universe, mda.Universe) + assert len(universe.trajectory) == 10 + + def test_load_universe_2(): """Test load MDA Universe multiple trajs.""" universe = la.load_universe( diff --git a/tests/test_libmdt.py b/tests/test_libmdt.py index 108b196..4bb4485 100644 --- a/tests/test_libmdt.py +++ b/tests/test_libmdt.py @@ -1,9 +1,5 @@ """Test libmdt.""" -import importlib -import sys - import mdtraj as md -import pytest from taurenmd.libs import libmdt @@ -33,36 +29,7 @@ def test_load_traj_pdb_3(): def test_load_traj_cif(): """Test loading traj.""" + assert toptest_cif.suffix == '.cif' traj = libmdt.load_traj(toptest_cif, trajtest) assert isinstance(traj, md.Trajectory) assert len(traj) == 10 - - -def test_attempt_load_cif_SIMTK_1(): - """Test attempt load CIF.""" - libmdt.attempt_to_load_top_from_simtk(toptest_cif) - - -def test_attempt_load_cif_SIMTK_2(): - """Test attempt load CIF.""" - assert toptest.str() == libmdt.attempt_to_load_top_from_simtk(toptest) - - -def test_load_traj_cif_import_error(): - """Test loadtraj_cif import error as if simtk was not installed.""" - sys.modules['simtk.openmm.app'] = None - importlib.reload(libmdt) - """Test loading traj.""" - with pytest.raises(SystemExit) as err: - libmdt.load_traj(toptest_cif, trajtest) - assert err.type == SystemExit - assert err.value.code == 0 - - -def test_simtk_import_error(): - """Test import error message.""" - with pytest.raises(SystemExit) as err: - libmdt._log_simtkimport_error() - - assert err.type == SystemExit - assert err.value.code == 0 diff --git a/tests/test_libopenmm.py b/tests/test_libopenmm.py new file mode 100644 index 0000000..a31a6cc --- /dev/null +++ b/tests/test_libopenmm.py @@ -0,0 +1,56 @@ +"""Test libopenmm.""" +import importlib +import sys + +import pytest +from openmm.app import pdbxfile + +from taurenmd.libs import libopenmm + +from . import toptest, toptest_cif, trajtest + + +def test_simtk(): + """Test simtk is installed.""" + assert libopenmm.SIMTK + + +def test_attempt_load_cif_SIMTK_1(): + """Test attempt load CIF.""" + mol = libopenmm.attempt_to_load_top_from_simtk(toptest_cif) + assert isinstance(mol, pdbxfile.PDBxFile) + + +def test_attempt_load_cif_SIMTK_2(): + """Test attempt load CIF.""" + with pytest.raises(ValueError): + libopenmm.attempt_to_load_top_from_simtk(toptest) + + +def test_simtk_import_error(): + """Test import error message.""" + with pytest.raises(SystemExit) as err: + libopenmm._log_simtkimport_error() + assert err.type == SystemExit + assert err.value.code == 0 + + +def test_without_simtk(): + """Test without simtk installed.""" + sys.modules['openmm.app.pdbxfile'] = None + importlib.reload(libopenmm) + with pytest.raises(SystemExit) as err: + libopenmm.attempt_to_load_top_from_simtk(toptest_cif) + assert err.type == SystemExit + assert err.value.code == 0 + + +def test_load_traj_cif_import_error(): + """Test loadtraj_cif import error as if simtk was not installed.""" + sys.modules['openmm.app.pdbxfile'] = None + from taurenmd.libs import libmdt + importlib.reload(libmdt) + with pytest.raises(SystemExit) as err: + libmdt.load_traj(toptest_cif, trajtest) + assert err.type == SystemExit + assert err.value.code == 0