Skip to content

Commit 7009efa

Browse files
authored
Merge pull request #456 from OpenBioSim/fix_445
Fix issue #445
2 parents 1a109a4 + a2d9c55 commit 7009efa

8 files changed

Lines changed: 527 additions & 112 deletions

File tree

python/BioSimSpace/Sandpit/Exscientia/Trajectory/_trajectory.py

Lines changed: 137 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from sire._load import _resolve_path
4848

4949
from .. import _isVerbose
50+
from ..Align._squash import _squash, _unsquash
5051
from .._Exceptions import IncompatibleError as _IncompatibleError
5152
from ..Process._process import Process as _Process
5253
from .._SireWrappers import System as _System
@@ -270,9 +271,22 @@ def getFrame(trajectory, topology, index, system=None, property_map={}):
270271
# The new_system object will contain a single molecule with the
271272
# coordinates of all of the atoms in the reference. As such, we
272273
# will need to split the system into molecules.
273-
new_system = _split_molecules(
274+
new_system, is_squashed = _split_molecules(
274275
frame, pdb, renumbered_system, str(work_dir), property_map
275276
)
277+
278+
# Make sure the system has the correct end-state properties.
279+
if is_squashed:
280+
for mol in new_system:
281+
if "is_perturbable" in mol.propertyKeys():
282+
cursor = mol.cursor()
283+
cursor["coordinates"] = cursor["coordinates0"]
284+
try:
285+
cursor["velocities"] = cursor["velocities0"]
286+
except:
287+
pass
288+
new_system.update(cursor.commit())
289+
276290
try:
277291
sire_system, _ = _SireIO.updateCoordinatesAndVelocities(
278292
system._sire_object, new_system, mapping, False, property_map, {}
@@ -791,7 +805,7 @@ def getFrames(self, indices=None):
791805
self._trajectory.select_atoms("all").write(pdb_file)
792806

793807
# Try to update the coordinates/velocities in the reference system.
794-
if self._system is not None:
808+
if self._system is not None and self._system.nPerturbableMolecules() == 0:
795809
if self._backend == "SIRE" and frame.current().num_molecules() > 1:
796810
try:
797811
new_system = frame.current()._system
@@ -853,13 +867,26 @@ def getFrames(self, indices=None):
853867
# The new_system object will contain a single molecule with the
854868
# coordinates of all of the atoms in the reference. As such, we
855869
# will need to split the system into molecules.
856-
new_system = _split_molecules(
870+
new_system, is_squashed = _split_molecules(
857871
frame,
858872
pdb,
859873
self._renumbered_system,
860874
str(self._work_dir),
861875
self._property_map,
862876
)
877+
878+
# Make sure the system has the correct end-state properties.
879+
if is_squashed:
880+
for mol in new_system:
881+
if "is_perturbable" in mol.propertyKeys():
882+
cursor = mol.cursor()
883+
cursor["coordinates"] = cursor["coordinates0"]
884+
try:
885+
cursor["velocities"] = cursor["velocities0"]
886+
except:
887+
pass
888+
new_system.update(cursor.commit())
889+
863890
try:
864891
sire_system, _ = _SireIO.updateCoordinatesAndVelocities(
865892
self._system._sire_object,
@@ -1088,6 +1115,9 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}):
10881115
system : Sire.System.System
10891116
The passed system, split into the appropriate number of molecules,
10901117
as a Sire System object.
1118+
1119+
is_squashed : bool
1120+
Whether the passed frame was squashed.
10911121
"""
10921122

10931123
if not isinstance(frame, (_SireIO.AmberRst7, _SireIO.Gro87)):
@@ -1119,6 +1149,25 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}):
11191149
# Whether the frame contains velocity information.
11201150
has_vels = frame.hasVelocities()
11211151

1152+
# Whether the reference is a perturbable system.
1153+
is_perturbable = reference.nPerturbableMolecules() > 0
1154+
1155+
if is_perturbable:
1156+
# Cannot go via PDB format for perturbable systems.
1157+
pdb = None
1158+
1159+
# AMBER format perturbable systems are squashed.
1160+
if frame.num_atoms() > reference.nAtoms():
1161+
explicit_dummies = property_map.get("explicit_dummies", False)
1162+
if not isinstance(explicit_dummies, bool):
1163+
explicit_dummies = False
1164+
is_amber = True
1165+
squashed_system, mapping = _squash(
1166+
reference, explicit_dummies=explicit_dummies
1167+
)
1168+
else:
1169+
is_amber = False
1170+
11221171
# Store the formats associated with the reference system.
11231172
formats = reference.fileFormat()
11241173

@@ -1144,73 +1193,105 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}):
11441193
else:
11451194
box = _SireVol.TriclinicBox(frame.box_v1(), frame.box_v2(), frame.box_v3())
11461195

1147-
if "PRM7" in formats:
1148-
try:
1149-
top = _SireIO.AmberPrm(reference._sire_object)
1150-
top.writeToFile(top_file)
1151-
except Exception as e:
1152-
msg = "Unable to write reference system to AmberPrm7 format!"
1153-
if _isVerbose():
1154-
raise IOError(msg) from e
1155-
else:
1156-
raise IOError(msg) from None
1196+
if is_perturbable:
1197+
if is_amber:
1198+
# Write the squashed system to file.
1199+
try:
1200+
top = _SireIO.AmberPrm(squashed_system._sire_object)
1201+
top.writeToFile(top_file)
1202+
except Exception as e:
1203+
msg = "Unable to write squashed reference system to AmberPrm7 format!"
1204+
if _isVerbose():
1205+
raise IOError(msg) from e
1206+
else:
1207+
raise IOError(msg) from None
1208+
else:
1209+
try:
1210+
top = _SireIO.GroTop(reference._sire_object)
1211+
top.writeToFile(top_file)
1212+
except Exception as e:
1213+
msg = "Unable to write perturbable reference system to GroTop format!"
1214+
if _isVerbose():
1215+
raise IOError(msg) from e
1216+
else:
1217+
raise IOError(msg) from None
1218+
else:
1219+
if "PRM7" in formats:
1220+
try:
1221+
top = _SireIO.AmberPrm(reference._sire_object)
1222+
top.writeToFile(top_file)
1223+
except Exception as e:
1224+
msg = "Unable to write reference system to AmberPrm7 format!"
1225+
if _isVerbose():
1226+
raise IOError(msg) from e
1227+
else:
1228+
raise IOError(msg) from None
11571229

1158-
elif "GroTop" in formats or "GROTOP" in formats:
1159-
try:
1160-
top = _SireIO.GroTop(reference._sire_object)
1161-
top.writeToFile(top_file)
1162-
except Exception as e:
1163-
msg = "Unable to write reference system to GroTop format!"
1164-
if _isVerbose():
1165-
raise IOError(msg) from e
1166-
else:
1167-
raise IOError(msg) from None
1230+
elif "GroTop" in formats or "GROTOP" in formats:
1231+
try:
1232+
top = _SireIO.GroTop(reference._sire_object)
1233+
top.writeToFile(top_file)
1234+
except Exception as e:
1235+
msg = "Unable to write reference system to GroTop format!"
1236+
if _isVerbose():
1237+
raise IOError(msg) from e
1238+
else:
1239+
raise IOError(msg) from None
11681240

1169-
elif "PSF" in formats:
1170-
try:
1171-
top = _SireIO.CharmmPSF(reference._sire_object)
1172-
top.writeToFile(top_file)
1173-
except Exception as e:
1174-
msg = "Unable to write reference system to CharmmPSF format!"
1175-
if _isVerbose():
1176-
raise IOError(msg) from e
1177-
else:
1178-
raise IOError(msg) from None
1241+
elif "PSF" in formats:
1242+
try:
1243+
top = _SireIO.CharmmPSF(reference._sire_object)
1244+
top.writeToFile(top_file)
1245+
except Exception as e:
1246+
msg = "Unable to write reference system to CharmmPSF format!"
1247+
if _isVerbose():
1248+
raise IOError(msg) from e
1249+
else:
1250+
raise IOError(msg) from None
11791251

1180-
# Unknown, try using PDB.
1181-
else:
1182-
is_pdb = True
1252+
# Unknown, try using PDB.
1253+
else:
1254+
is_pdb = True
11831255

1184-
# Get the PDB records.
1185-
pdb_lines = pdb.toLines()
1256+
# Get the PDB records.
1257+
pdb_lines = pdb.toLines()
11861258

1187-
# Create a list to hold the new lines.
1188-
new_lines = []
1259+
# Create a list to hold the new lines.
1260+
new_lines = []
11891261

1190-
# Find the first atom record.
1191-
for idx, line in enumerate(pdb_lines):
1192-
if line.startswith("ATOM"):
1193-
break
1262+
# Find the first atom record.
1263+
for idx, line in enumerate(pdb_lines):
1264+
if line.startswith("ATOM"):
1265+
break
11941266

1195-
# Loop over all of the molecules in the reference, adding the records fo
1196-
# each atom. We append a standalone TER record between each molecule,
1197-
# which is used by Sire.IO.PDB2 to split molecules.
1198-
for mol in reference:
1199-
new_lines.extend(pdb_lines[idx : idx + mol.nAtoms()])
1200-
new_lines.append("TER")
1201-
idx += mol.nAtoms()
1267+
# Loop over all of the molecules in the reference, adding the records fo
1268+
# each atom. We append a standalone TER record between each molecule,
1269+
# which is used by Sire.IO.PDB2 to split molecules.
1270+
for mol in reference:
1271+
new_lines.extend(pdb_lines[idx : idx + mol.nAtoms()])
1272+
new_lines.append("TER")
1273+
idx += mol.nAtoms()
12021274

1203-
# Recreate the PDB object using the updated records.
1204-
pdb = _SireIO.PDB2(new_lines)
1275+
# Recreate the PDB object using the updated records.
1276+
pdb = _SireIO.PDB2(new_lines)
12051277

1206-
# Convert to a system.
1207-
split_system = pdb.toSystem()
1278+
# Convert to a system.
1279+
split_system = pdb.toSystem()
12081280

12091281
if not is_pdb:
12101282
# Try to read the system back in, making sure that the numbering is unique.
12111283
try:
12121284
split_system = _SireIO.MoleculeParser.read([coord_file, top_file])
12131285

1286+
# Unsquash if necessary.
1287+
if is_perturbable and is_amber:
1288+
split_system = _unsquash(
1289+
reference,
1290+
_System(split_system),
1291+
mapping,
1292+
explicit_dummies=explicit_dummies,
1293+
)._sire_object
1294+
12141295
except Exception as e:
12151296
msg = "Unable to read trajectory frame!"
12161297
if _isVerbose():
@@ -1221,7 +1302,7 @@ def _split_molecules(frame, pdb, reference, work_dir, property_map={}):
12211302
# Add the space property.
12221303
split_system.setProperty(property_map.get("space", "space"), box)
12231304

1224-
return split_system
1305+
return split_system, is_perturbable and is_amber
12251306

12261307

12271308
def _update_water_topology(system, topology, trajectory, property_map):

0 commit comments

Comments
 (0)