Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mmcif compatibility to mdanalysis library #57

Merged
merged 16 commits into from
Nov 24, 2021
42 changes: 0 additions & 42 deletions .github/workflows/install-dev.yml

This file was deleted.

38 changes: 0 additions & 38 deletions .github/workflows/install.yml

This file was deleted.

3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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)
------------------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
18 changes: 12 additions & 6 deletions src/taurenmd/libs/libmda.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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

Expand Down
64 changes: 10 additions & 54 deletions src/taurenmd/libs/libmdt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand All @@ -65,7 +45,8 @@ def load_traj(topology, trajectories, insort=False):
trajectory : str or Path
Path to the trajectory file. Accepts MDTraj compatible `files <http://mdtraj.org/1.9.3/load_functions.html#trajectory-reference>`_

Returns -------
Returns
-------
MDTraj trajectory
`Trajectory object <http://mdtraj.org/1.9.3/api/generated/mdtraj.Trajectory.html#mdtraj-trajectory>`_.
""" # noqa: E501
Expand All @@ -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)
Expand Down
67 changes: 67 additions & 0 deletions src/taurenmd/libs/libopenmm.py
Original file line number Diff line number Diff line change
@@ -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)
12 changes: 11 additions & 1 deletion tests/test_libmda.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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(
Expand Down
35 changes: 1 addition & 34 deletions tests/test_libmdt.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
"""Test libmdt."""
import importlib
import sys

import mdtraj as md
import pytest

from taurenmd.libs import libmdt

Expand Down Expand Up @@ -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
Loading