4747from sire ._load import _resolve_path
4848
4949from .. import _isVerbose
50+ from ..Align ._squash import _squash , _unsquash
5051from .._Exceptions import IncompatibleError as _IncompatibleError
5152from ..Process ._process import Process as _Process
5253from .._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
12271308def _update_water_topology (system , topology , trajectory , property_map ):
0 commit comments