From 39b3b826ef34e252bd78310ed3222791a9370f75 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 7 Jul 2023 09:58:44 +0100 Subject: [PATCH 1/5] Allow user to specify backend in process.getTrajectory() [ref OpenBioSim/BioSimSpaceTutorials#32] --- demo/interactive_md.ipynb | 6 +++--- python/BioSimSpace/Process/_amber.py | 15 +++++++++++++-- python/BioSimSpace/Process/_gromacs.py | 15 +++++++++++++-- python/BioSimSpace/Process/_namd.py | 16 ++++++++++++++-- python/BioSimSpace/Process/_openmm.py | 16 ++++++++++++++-- python/BioSimSpace/Process/_somd.py | 15 +++++++++++++-- .../Sandpit/Exscientia/Process/_amber.py | 15 +++++++++++++-- .../Sandpit/Exscientia/Process/_gromacs.py | 15 +++++++++++++-- .../Sandpit/Exscientia/Process/_namd.py | 16 ++++++++++++++-- .../Sandpit/Exscientia/Process/_openmm.py | 15 +++++++++++++-- .../Sandpit/Exscientia/Process/_somd.py | 15 +++++++++++++-- 11 files changed, 136 insertions(+), 23 deletions(-) diff --git a/demo/interactive_md.ipynb b/demo/interactive_md.ipynb index 0b826f4bc..74f5f7054 100644 --- a/demo/interactive_md.ipynb +++ b/demo/interactive_md.ipynb @@ -343,7 +343,7 @@ "\n", "# Compute the RMSD for each frame and plot the result.\n", "BSS.Notebook.plot(\n", - " y=process.getTrajectory().rmsd(frame=0, atoms=indices),\n", + " y=process.getTrajectory(backend=\"mdtraj\").rmsd(frame=0, atoms=indices),\n", " xlabel=\"Frame\",\n", " ylabel=\"RMSD\",\n", ")" @@ -352,7 +352,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -366,7 +366,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.10.11" } }, "nbformat": 4, diff --git a/python/BioSimSpace/Process/_amber.py b/python/BioSimSpace/Process/_amber.py index 8533d325d..e755cf16c 100644 --- a/python/BioSimSpace/Process/_amber.py +++ b/python/BioSimSpace/Process/_amber.py @@ -461,13 +461,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -478,6 +483,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -489,7 +500,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Process/_gromacs.py b/python/BioSimSpace/Process/_gromacs.py index 5994f4e2e..98626325a 100644 --- a/python/BioSimSpace/Process/_gromacs.py +++ b/python/BioSimSpace/Process/_gromacs.py @@ -659,13 +659,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -676,6 +681,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -695,7 +706,7 @@ def getTrajectory(self, block="AUTO"): else: self._traj_file = traj_file - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Process/_namd.py b/python/BioSimSpace/Process/_namd.py index bb2b76d6c..571051e5c 100644 --- a/python/BioSimSpace/Process/_namd.py +++ b/python/BioSimSpace/Process/_namd.py @@ -813,13 +813,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -829,6 +834,13 @@ def getTrajectory(self, block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -840,7 +852,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Process/_openmm.py b/python/BioSimSpace/Process/_openmm.py index 54af93ab6..f8f06d6b4 100644 --- a/python/BioSimSpace/Process/_openmm.py +++ b/python/BioSimSpace/Process/_openmm.py @@ -1350,13 +1350,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -1367,6 +1372,13 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + + # Wait for the process to finish. if block is True: self.wait() @@ -1380,7 +1392,7 @@ def getTrajectory(self, block="AUTO"): if not _os.path.isfile(self._traj_file): return None else: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) def getFrame(self, index): """ diff --git a/python/BioSimSpace/Process/_somd.py b/python/BioSimSpace/Process/_somd.py index 4d9d5e5e8..27582b2e5 100644 --- a/python/BioSimSpace/Process/_somd.py +++ b/python/BioSimSpace/Process/_somd.py @@ -585,13 +585,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -602,6 +607,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -613,7 +624,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py index 10ea8de6f..7595ff33f 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py @@ -708,13 +708,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -725,6 +730,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -736,7 +747,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py index 2a53e58a9..4695d7af1 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py @@ -704,13 +704,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -721,6 +726,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -740,7 +751,7 @@ def getTrajectory(self, block="AUTO"): else: self._traj_file = traj_file - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py index ac6dfaa7a..db585adaf 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py @@ -811,13 +811,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -827,6 +832,13 @@ def getTrajectory(self, block="AUTO"): trajectory : :class:`Trajectory ` The latest trajectory object. """ + + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -838,7 +850,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py index dcea9a2b5..3ccac07bc 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py @@ -1350,13 +1350,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -1367,6 +1372,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -1380,7 +1391,7 @@ def getTrajectory(self, block="AUTO"): if not _os.path.isfile(self._traj_file): return None else: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) def getFrame(self, index): """ diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py index fbc1351c1..97882c198 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py @@ -600,13 +600,18 @@ def getCurrentSystem(self): """ return self.getSystem(block=False) - def getTrajectory(self, block="AUTO"): + def getTrajectory(self, backend="AUTO", block="AUTO"): """ Return a trajectory object. Parameters ---------- + backend : str + The backend to use for trajectory parsing. To see supported backends, + run BioSimSpace.Trajectory.backends(). Using "AUTO" will try each in + sequence. + block : bool Whether to block until the process has finished running. @@ -617,6 +622,12 @@ def getTrajectory(self, block="AUTO"): The latest trajectory object. """ + if not isinstance(backend, str): + raise TypeError("'backend' must be of type 'str'") + + if not isinstance(block, (bool, str)): + raise TypeError("'block' must be of type 'bool' or 'str'") + # Wait for the process to finish. if block is True: self.wait() @@ -628,7 +639,7 @@ def getTrajectory(self, block="AUTO"): _warnings.warn("The process exited with an error!") try: - return _Trajectory.Trajectory(process=self) + return _Trajectory.Trajectory(process=self, backend=backend) except: return None From 009bb8f74fe4fd8c26312bd120d9606d7e42d5c5 Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 7 Jul 2023 10:06:44 +0100 Subject: [PATCH 2/5] Update search. --- demo/interactive_md.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/interactive_md.ipynb b/demo/interactive_md.ipynb index 74f5f7054..34f2293ff 100644 --- a/demo/interactive_md.ipynb +++ b/demo/interactive_md.ipynb @@ -336,10 +336,10 @@ "outputs": [], "source": [ "# Search the system for a residue named ALA. Since there is a single match, we take the first result.\n", - "residue = system.search(\"resname ALA\")[0]\n", + "atoms = system.search(\"resname ALA\")\n", "\n", "# Get the indices of the atoms in the molecule, relative to the original system.\n", - "indices = [system.getIndex(x) for x in residue.getAtoms()]\n", + "indices = [system.getIndex(atom) for atom in atoms]\n", "\n", "# Compute the RMSD for each frame and plot the result.\n", "BSS.Notebook.plot(\n", From 0eb2c7b47770910b38e62f5fa4dd8b1d5aaf636c Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 7 Jul 2023 10:17:11 +0100 Subject: [PATCH 3/5] Blacken. --- python/BioSimSpace/Process/_openmm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/BioSimSpace/Process/_openmm.py b/python/BioSimSpace/Process/_openmm.py index f8f06d6b4..b5a4cc7e5 100644 --- a/python/BioSimSpace/Process/_openmm.py +++ b/python/BioSimSpace/Process/_openmm.py @@ -1378,7 +1378,6 @@ def getTrajectory(self, backend="AUTO", block="AUTO"): if not isinstance(block, (bool, str)): raise TypeError("'block' must be of type 'bool' or 'str'") - # Wait for the process to finish. if block is True: self.wait() From 46de8302c061671704f0b72e8c139daa0129d57a Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 7 Jul 2023 10:17:21 +0100 Subject: [PATCH 4/5] Fix PLUMED version check. [closes OpenBioSim/BioSimSpaceTutorials#35] --- python/BioSimSpace/Process/_plumed.py | 8 +++++++- python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/python/BioSimSpace/Process/_plumed.py b/python/BioSimSpace/Process/_plumed.py index d113f1c22..9f3dfd52e 100644 --- a/python/BioSimSpace/Process/_plumed.py +++ b/python/BioSimSpace/Process/_plumed.py @@ -106,6 +106,10 @@ def __init__(self, work_dir): "PLUMED version >= 2.5 is required." ) + # Store the major and minor versions. + self._plumed_major = major + self._plumed_minor = minor + else: raise _Exceptions.IncompatibleError("Could not determine PLUMED version!") @@ -377,7 +381,9 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): # The funnel collective variable requires an auxiliary file for # PLUMED versions < 2.7. - if self._plumed_version < 2.7: + if self._plumed_major < 2 or ( + self._plumed_major < 3 and self._plumed_minor < 7 + ): aux_file = "ProjectionOnAxis.cpp" self._config.append(f"LOAD FILE={aux_file}") aux_file = ( diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py index d113f1c22..9f3dfd52e 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_plumed.py @@ -106,6 +106,10 @@ def __init__(self, work_dir): "PLUMED version >= 2.5 is required." ) + # Store the major and minor versions. + self._plumed_major = major + self._plumed_minor = minor + else: raise _Exceptions.IncompatibleError("Could not determine PLUMED version!") @@ -377,7 +381,9 @@ def _createMetadynamicsConfig(self, system, protocol, property_map={}): # The funnel collective variable requires an auxiliary file for # PLUMED versions < 2.7. - if self._plumed_version < 2.7: + if self._plumed_major < 2 or ( + self._plumed_major < 3 and self._plumed_minor < 7 + ): aux_file = "ProjectionOnAxis.cpp" self._config.append(f"LOAD FILE={aux_file}") aux_file = ( From 8aee0e82bdbc87093a0e1fd7ffa3cd633d14565f Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 7 Jul 2023 10:20:27 +0100 Subject: [PATCH 5/5] Pass work_dir to Plumed constructor as a string. [closes OpenBioSim/BioSimSpaceTutorials#36] --- python/BioSimSpace/Process/_openmm.py | 2 +- python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/BioSimSpace/Process/_openmm.py b/python/BioSimSpace/Process/_openmm.py index b5a4cc7e5..7a3f80a4d 100644 --- a/python/BioSimSpace/Process/_openmm.py +++ b/python/BioSimSpace/Process/_openmm.py @@ -1109,7 +1109,7 @@ def _generate_config(self): # Create a dummy PLUMED input file so that we can bind PLUMED # analysis functions to this process. - self._plumed = _Plumed(self._work_dir) + self._plumed = _Plumed(str(self._work_dir)) plumed_config, auxillary_files = self._plumed.createConfig( self._system, self._protocol, self._property_map ) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py index 3ccac07bc..d6a1d42fd 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py @@ -1109,7 +1109,7 @@ def _generate_config(self): # Create a dummy PLUMED input file so that we can bind PLUMED # analysis functions to this process. - self._plumed = _Plumed(self._work_dir) + self._plumed = _Plumed(str(self._work_dir)) plumed_config, auxillary_files = self._plumed.createConfig( self._system, self._protocol, self._property_map )