diff --git a/opencadd/structure/superposition/engines/base.py b/opencadd/structure/superposition/engines/base.py index c3dc70e9..1aefd196 100644 --- a/opencadd/structure/superposition/engines/base.py +++ b/opencadd/structure/superposition/engines/base.py @@ -27,8 +27,15 @@ def calculate(self, structures, *args, **kwargs): - ``metadata``: Contextual information provided by the method """ - assert len(structures) == 2 + self._safety_checks() + assert ( + len(structures) == 2, + "This method can only be used for two structures at the same time, for now", + ) return self._calculate(structures, *args, **kwargs) def _calculate(self, structures, *args, **kwargs): raise NotImplementedError("Reimplement in your subclass") + + def _safety_checks(self): + raise NotImplementedError("Reimplement in your subclass") diff --git a/opencadd/structure/superposition/engines/mda.py b/opencadd/structure/superposition/engines/mda.py index 5e4d6106..a89fa023 100644 --- a/opencadd/structure/superposition/engines/mda.py +++ b/opencadd/structure/superposition/engines/mda.py @@ -94,6 +94,13 @@ def __init__( self.superposition_weights = superposition_weights self.superposition_delta_mass_tolerance = superposition_delta_mass_tolerance + def _safety_checks(self): + """ + Check for `mda` installation passes; added here only for consistency across the engines. + """ + + pass + def _calculate(self, structures, *args, **kwargs): """ @@ -109,10 +116,7 @@ def _calculate(self, structures, *args, **kwargs): rmsd metadata """ - if len(structures) > 2: - raise NotImplementedError( - "This method can only be used for two structures at the same time, for now" - ) + ref_universe, mob_universe = structures # Get matching atoms diff --git a/opencadd/structure/superposition/engines/mmligner.py b/opencadd/structure/superposition/engines/mmligner.py index f2525eea..114b334e 100644 --- a/opencadd/structure/superposition/engines/mmligner.py +++ b/opencadd/structure/superposition/engines/mmligner.py @@ -25,6 +25,7 @@ import sys import subprocess import logging +from distutils.spawn import find_executable import numpy as np import biotite.sequence.io.fasta as fasta @@ -57,6 +58,21 @@ def __init__(self, executable=None, protein_selector="protein"): "Current MMLigner wrappers produces accurate RMSD values but slightly shifted structures!" ) + def _safety_checks(self): + """ + Check if `mmligner` is installed (executable found?). + + Raises + ------ + OSError + Raises error if executable `mmligner` cannot be found. + """ + + mmligner = find_executable("mmligner") + if mmligner is None: + raise OSError("mmligner cannot be located. Is it installed?") + # proceed normally + def _calculate(self, structures, *args, **kwargs): """ Calculates the superposition of two protein structures. diff --git a/opencadd/structure/superposition/engines/theseus.py b/opencadd/structure/superposition/engines/theseus.py index 2ffa0808..07c5759c 100644 --- a/opencadd/structure/superposition/engines/theseus.py +++ b/opencadd/structure/superposition/engines/theseus.py @@ -21,6 +21,7 @@ import os import subprocess import logging +from distutils.spawn import find_executable import numpy as np @@ -56,6 +57,21 @@ def __init__(self, alignment_max_iterations: int = 32, statistics_only: bool = F self._alignment_executable = "muscle" self._theseus_transformation_file = "theseus_transf.txt" + def _safety_checks(self): + """ + Check if `theseus` is installed (executable found?). + + Raises + ------ + OSError + Raises error if executable `theseus` cannot be found. + """ + + theseus = find_executable("theseus") + if theseus is None: + raise OSError("theseus cannot be located. Is it installed?") + # proceed normally + def _calculate(self, structures, *args, **kwargs) -> dict: """ Align the sequences with an alignment tool (``muscle``)