From 8f088a2059a8bc626ab1731b108ff6b2a321d24d Mon Sep 17 00:00:00 2001 From: Lester Hedges Date: Fri, 22 Sep 2023 11:42:24 +0100 Subject: [PATCH] Backport fix from PR #173. [ci skip] --- python/BioSimSpace/Process/_amber.py | 7 ++- python/BioSimSpace/Process/_gromacs.py | 7 ++- python/BioSimSpace/Process/_namd.py | 7 ++- python/BioSimSpace/Process/_openmm.py | 7 ++- python/BioSimSpace/Process/_somd.py | 10 +++- .../Sandpit/Exscientia/Process/_amber.py | 7 ++- .../Sandpit/Exscientia/Process/_gromacs.py | 7 ++- .../Sandpit/Exscientia/Process/_namd.py | 7 ++- .../Sandpit/Exscientia/Process/_openmm.py | 7 ++- .../Sandpit/Exscientia/Process/_somd.py | 10 +++- .../Exscientia/Trajectory/_trajectory.py | 56 ++++++++++++++++++- python/BioSimSpace/Trajectory/_trajectory.py | 56 ++++++++++++++++++- tests/Process/test_somd.py | 10 +++- tests/Sandpit/Exscientia/Process/test_somd.py | 11 +++- 14 files changed, 173 insertions(+), 36 deletions(-) diff --git a/python/BioSimSpace/Process/_amber.py b/python/BioSimSpace/Process/_amber.py index e755cf16c..afcb6315f 100644 --- a/python/BioSimSpace/Process/_amber.py +++ b/python/BioSimSpace/Process/_amber.py @@ -440,9 +440,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Process/_gromacs.py b/python/BioSimSpace/Process/_gromacs.py index c3c1962cf..9801b5351 100644 --- a/python/BioSimSpace/Process/_gromacs.py +++ b/python/BioSimSpace/Process/_gromacs.py @@ -2533,9 +2533,10 @@ def _getFrame(self, time): and space_prop in new_system._sire_object.propertyKeys() ): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) # If this is a vacuum simulation, then translate the centre of mass # of the system back to the origin. diff --git a/python/BioSimSpace/Process/_namd.py b/python/BioSimSpace/Process/_namd.py index 571051e5c..febef4c1e 100644 --- a/python/BioSimSpace/Process/_namd.py +++ b/python/BioSimSpace/Process/_namd.py @@ -789,9 +789,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Process/_openmm.py b/python/BioSimSpace/Process/_openmm.py index fc0871637..a7e2137fa 100644 --- a/python/BioSimSpace/Process/_openmm.py +++ b/python/BioSimSpace/Process/_openmm.py @@ -1316,9 +1316,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Process/_somd.py b/python/BioSimSpace/Process/_somd.py index 27582b2e5..9039f4831 100644 --- a/python/BioSimSpace/Process/_somd.py +++ b/python/BioSimSpace/Process/_somd.py @@ -538,6 +538,13 @@ def getSystem(self, block="AUTO"): new_system = _IO.readMolecules(self._restart_file) + # Try loading the trajectory file to get the box information. + try: + frame = self.getTrajectory().getFrames(-1) + box = frame._sire_object.property("space") + except: + box = None + # Since SOMD requires specific residue and water naming we copy the # coordinates back into the original system. old_system = self._system.copy() @@ -562,8 +569,7 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): - box = new_system._sire_object.property("space") + if box and box.isPeriodic(): old_system._sire_object.setProperty( self._property_map.get("space", "space"), box ) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py index 7595ff33f..42261a191 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_amber.py @@ -687,9 +687,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py index a772e9090..2b95164a6 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_gromacs.py @@ -2579,9 +2579,10 @@ def _getFrame(self, time): and space_prop in new_system._sire_object.propertyKeys() ): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) # If this is a vacuum simulation, then translate the centre of mass # of the system back to the origin. diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py index db585adaf..eb60dce1a 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_namd.py @@ -787,9 +787,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py index 35b830b65..dcd126e22 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_openmm.py @@ -1316,9 +1316,10 @@ def getSystem(self, block="AUTO"): # Update the box information in the original system. if "space" in new_system._sire_object.propertyKeys(): box = new_system._sire_object.property("space") - old_system._sire_object.setProperty( - self._property_map.get("space", "space"), box - ) + if box.isPeriodic(): + old_system._sire_object.setProperty( + self._property_map.get("space", "space"), box + ) return old_system diff --git a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py index 312e60b6e..e95f8d751 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Process/_somd.py @@ -553,6 +553,13 @@ def getSystem(self, block="AUTO"): new_system = _IO.readMolecules(self._restart_file) + # Try loading the trajectory file to get the box information. + try: + frame = self.getTrajectory().getFrames(-1) + box = frame._sire_object.property("space") + except: + box = None + # Since SOMD requires specific residue and water naming we copy the # coordinates back into the original system. old_system = self._system.copy() @@ -577,8 +584,7 @@ def getSystem(self, block="AUTO"): self._mapping = mapping # Update the box information in the original system. - if "space" in new_system._sire_object.propertyKeys(): - box = new_system._sire_object.property("space") + if box and box.isPeriodic(): old_system._sire_object.setProperty( self._property_map.get("space", "space"), box ) diff --git a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py index b76934c07..d42d38423 100644 --- a/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py +++ b/python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py @@ -40,6 +40,8 @@ from sire.legacy import Base as _SireBase from sire.legacy import IO as _SireIO from sire.legacy import Mol as _SireMol +from sire.legacy import Units as _SireUnits +from sire.legacy import Vol as _SireVol from sire import load as _sire_load from sire._load import _resolve_path @@ -202,15 +204,25 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): if system is not None: if is_sire and frame.current().num_molecules() > 1: try: + new_system = frame.current()._system + sire_system, _ = _SireIO.updateCoordinatesAndVelocities( system._sire_object, - frame.current()._system, + new_system, mapping, False, property_map, {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: @@ -260,6 +272,14 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): system._sire_object, new_system, mapping, False, property_map, {} ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -756,15 +776,25 @@ def getFrames(self, indices=None): if self._system is not None: if self._backend == "SIRE" and frame.current().num_molecules() > 1: try: + new_system = frame.current()._system + sire_system, _ = _SireIO.updateCoordinatesAndVelocities( self._system._sire_object, - frame.current()._system, + new_system, self._mapping, False, self._property_map, {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -822,6 +852,14 @@ def getFrames(self, indices=None): {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -1059,6 +1097,17 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Whether we've parsed as a PDB file. is_pdb = False + # Create a triclinic space from the information in the frame file. + if isinstance(frame, _SireIO.AmberRst7): + # Get the box dimensions and angles. Take the values, since the + # units are wrong. + degree = _SireUnits.degree + dimensions = [x.value() for x in frame.box_dimensions()] + angles = [x.value() * degree for x in frame.box_angles()] + box = _SireVol.TriclinicBox(*dimensions, *angles) + else: + box = _SireVol.TriclinicBox(frame.box_v1(), frame.box_v2(), frame.box_v3()) + if "PRM7" in formats: try: top = _SireIO.AmberPrm(reference._sire_object) @@ -1133,6 +1182,9 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): else: raise IOError(msg) from None + # Add the space property. + split_system.setProperty(property_map.get("space", "space"), box) + return split_system diff --git a/python/BioSimSpace/Trajectory/_trajectory.py b/python/BioSimSpace/Trajectory/_trajectory.py index b76934c07..d42d38423 100644 --- a/python/BioSimSpace/Trajectory/_trajectory.py +++ b/python/BioSimSpace/Trajectory/_trajectory.py @@ -40,6 +40,8 @@ from sire.legacy import Base as _SireBase from sire.legacy import IO as _SireIO from sire.legacy import Mol as _SireMol +from sire.legacy import Units as _SireUnits +from sire.legacy import Vol as _SireVol from sire import load as _sire_load from sire._load import _resolve_path @@ -202,15 +204,25 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): if system is not None: if is_sire and frame.current().num_molecules() > 1: try: + new_system = frame.current()._system + sire_system, _ = _SireIO.updateCoordinatesAndVelocities( system._sire_object, - frame.current()._system, + new_system, mapping, False, property_map, {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: @@ -260,6 +272,14 @@ def getFrame(trajectory, topology, index, system=None, property_map={}): system._sire_object, new_system, mapping, False, property_map, {} ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -756,15 +776,25 @@ def getFrames(self, indices=None): if self._system is not None: if self._backend == "SIRE" and frame.current().num_molecules() > 1: try: + new_system = frame.current()._system + sire_system, _ = _SireIO.updateCoordinatesAndVelocities( self._system._sire_object, - frame.current()._system, + new_system, self._mapping, False, self._property_map, {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -822,6 +852,14 @@ def getFrames(self, indices=None): {}, ) + # Update the box information in the original system. + if "space" in new_system.propertyKeys(): + box = new_system.property("space") + if box.isPeriodic(): + sire_system.setProperty( + self._property_map.get("space", "space"), box + ) + new_system = _System(sire_system) except Exception as e: msg = "Failed to copy trajectory coordinates/velocities into BioSimSpace system!" @@ -1059,6 +1097,17 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): # Whether we've parsed as a PDB file. is_pdb = False + # Create a triclinic space from the information in the frame file. + if isinstance(frame, _SireIO.AmberRst7): + # Get the box dimensions and angles. Take the values, since the + # units are wrong. + degree = _SireUnits.degree + dimensions = [x.value() for x in frame.box_dimensions()] + angles = [x.value() * degree for x in frame.box_angles()] + box = _SireVol.TriclinicBox(*dimensions, *angles) + else: + box = _SireVol.TriclinicBox(frame.box_v1(), frame.box_v2(), frame.box_v3()) + if "PRM7" in formats: try: top = _SireIO.AmberPrm(reference._sire_object) @@ -1133,6 +1182,9 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}): else: raise IOError(msg) from None + # Add the space property. + split_system.setProperty(property_map.get("space", "space"), box) + return split_system diff --git a/tests/Process/test_somd.py b/tests/Process/test_somd.py index 74c2081e1..2220e589e 100644 --- a/tests/Process/test_somd.py +++ b/tests/Process/test_somd.py @@ -175,5 +175,11 @@ def run_process(system, protocol): # Make sure the process didn't error. assert not res - # Make sure that we get a molecular system back. - assert process.getSystem() is not None + # Get the updated system. + new_system = process.getSystem() + + # Make sure that we got a molecular system back. + assert new_system is not None + + # Make sure the space is valid. + assert new_system._sire_object.property("space").isPeriodic() diff --git a/tests/Sandpit/Exscientia/Process/test_somd.py b/tests/Sandpit/Exscientia/Process/test_somd.py index 11ecc6ba0..d077aff65 100644 --- a/tests/Sandpit/Exscientia/Process/test_somd.py +++ b/tests/Sandpit/Exscientia/Process/test_somd.py @@ -220,5 +220,12 @@ def run_process(system, protocol, **kwargs): # Make sure the process didn't error. assert not res - # Make sure that we get a molecular system back. - assert process.getSystem() is not None + # Get the updated system. + new_system = process.getSystem() + + # Make sure that we got a molecular system back. + assert new_system is not None + + # Make sure the space is valid. + if system.getBox()[0] is not None: + assert new_system._sire_object.property("space").isPeriodic()