def _initialise_runner(self, system0, system1): """Internal helper function to initialise the process runner. Parameters ---------- system0 : :class:`System <BioSimSpace._SireWrappers.System>` The system for the first free energy leg. system1 : :class:`System <BioSimSpace._SireWrappers.System>` The system for the second free energy leg. """ if type(system0) is not _System: raise TypeError( "'system0' must be of type 'BioSimSpace._SireWrappers.System'") if type(system1) is not _System: raise TypeError( "'system1' must be of type 'BioSimSpace._SireWrappers.System'") # Initialise lists to store the processes for each leg. leg0 = [] leg1 = [] # Get the simulation type. sim_type = self.__class__.__name__ # Store the working directories for the legs. if sim_type == "Solvation": self._dir0 = "%s/free" % self._work_dir if self._is_dual: self._dir1 = "%s/vacuum" % self._work_dir elif sim_type == "Binding": self._dir0 = "%s/bound" % self._work_dir if self._is_dual: self._dir1 = "%s/free" % self._work_dir else: raise TypeError("Unsupported FreeEnergy simulation: '%s'" % sim_type) # Convert to an appropriate AMBER topology. (Required by SOMD.) if self._engine == "SOMD": # Try to get the water model used to solvate the system. try: water_model = system0._sire_object.property( "water_model").toString() waters0 = _SireIO.setAmberWater( system0._sire_object.search("water"), water_model) if self._is_dual: waters1 = _SireIO.setAmberWater( system1._sire_object.search("water"), water_model) # If the system wasn't solvated by BioSimSpace, e.g. read from file, then try # to guess the water model from the topology. except: num_point = system0.getWaterMolecules()[0].nAtoms() if num_point == 3: # TODO: Assume TIP3P. Not sure how to detect SPC/E. waters0 = _SireIO.setAmberWater( system0._sire_object.search("water"), "TIP3P") if self._is_dual: waters1 = _SireIO.setAmberWater( system1._sire_object.search("water"), "TIP3P") water_model = "tip3p" elif num_point == 4: waters0 = _SireIO.setAmberWater( system0._sire_object.search("water"), "TIP4P") if self._is_dual: waters1 = _SireIO.setAmberWater( system1._sire_object.search("water"), "TIP4P") water_model = "tip4p" elif num_point == 5: waters0 = _SireIO.setAmberWater( system0._sire_object.search("water"), "TIP5P") if self._is_dual: waters1 = _SireIO.setAmberWater( system1._sire_object.search("water"), "TIP5P") water_model = "tip5p" else: raise RuntimeError("Unsupported %d-point water model!" % num_point) # Warn the user that we've guessed the water topology. _warnings.warn("Guessed water topology: %r" % water_model) # Remove the existing water molecules from the systems. system0.removeWaterMolecules() if self._is_dual: system1.removeWaterMolecules() # Convert the waters to BioSimSpace molecule containers. waters0 = _Molecules(waters0.toMolecules()) if self._is_dual: waters1 = _Molecules(waters1.toMolecules()) # Add the updated water topology back into the systems. system0.addMolecules(waters0) if self._is_dual: system1.addMolecules(waters1) # Get the lambda values from the protocol. lam_vals = self._protocol.getLambdaValues() # Loop over all of the lambda values. for lam in lam_vals: # Update the protocol lambda values. self._protocol.setLambdaValues(lam=lam, lam_vals=lam_vals) # Create and append the required processes for each leg. # Nest the working directories inside self._work_dir. # SOMD. if self._engine == "SOMD": # Check for GPU support. if "CUDA_VISIBLE_DEVICES" in _os.environ: platform = "CUDA" else: platform = "CPU" leg0.append( _Process.Somd(system0, self._protocol, platform=platform, work_dir="%s/lambda_%5.4f" % (self._dir0, lam))) if self._is_dual: leg1.append( _Process.Somd(system1, self._protocol, platform=platform, work_dir="%s/lambda_%5.4f" % (self._dir1, lam))) # GROMACS. elif self._engine == "GROMACS": leg0.append( _Process.Gromacs(system0, self._protocol, work_dir="%s/lambda_%5.4f" % (self._dir0, lam))) if self._is_dual: leg1.append( _Process.Gromacs(system1, self._protocol, work_dir="%s/lambda_%5.4f" % (self._dir1, lam))) # Initialise the process runner. All processes have already been nested # inside the working directory so no need to re-nest. self._runner = _Process.ProcessRunner(leg0 + leg1, work_dir=self._work_dir, nest_dirs=False)
def _initialise_runner(self, system0, system1): """Internral helper function to initialise the process runner. Parameters ---------- system0 : :class:`System <BioSimSpace._SireWrappers.System>` The system for the first free energy leg. system1 : :class:`System <BioSimSpace._SireWrappers.System>` The system for the second free energy leg. """ if type(system0) is not _System: raise TypeError( "'system0' must be of type 'BioSimSpace._SireWrappers.System'") if type(system1) is not _System: raise TypeError( "'system1' must be of type 'BioSimSpace._SireWrappers.System'") # Initialise lists to store the processes for each leg. leg0 = [] leg1 = [] # Get the simulation type. sim_type = self.__class__.__name__ # Store the working directories for the legs. if sim_type == "Solvation": self._dir0 = "%s/free" % self._work_dir self._dir1 = "%s/vacuum" % self._work_dir elif sim_type == "Binding": self._dir0 = "%s/bound" % self._work_dir self._dir1 = "%s/free" % self._work_dir else: raise TypeError("Unsupported FreeEnergy simulation: '%s'" % sim_type) # Try to get the water model property of the system. try: water_model = system0._sire_system.property( "water_model").toString() # Default to TIP3P. except: water_model = "tip3p" if self._engine == "SOMD": # Reformat all of the water molecules so that they match the expected # AMBER topology template. (Required by SOMD.) waters0 = _SireIO.setAmberWater( system0._sire_system.search("water"), water_model) waters1 = _SireIO.setAmberWater( system1._sire_system.search("water"), water_model) # Loop over all of the renamed water molecules, delete the old one # from the system, then add the renamed one back in. # TODO: This is a hack since the "update" method of Sire.System # doesn't work properly at present. system0.removeWaterMolecules() system1.removeWaterMolecules() for wat in waters0: system0._sire_system.add(wat, _SireMol.MGName("all")) for wat in waters1: system1._sire_system.add(wat, _SireMol.MGName("all")) # Get the lambda values from the protocol. lam_vals = self._protocol.getLambdaValues() # Loop over all of the lambda values. for lam in lam_vals: # Update the protocol lambda values. self._protocol.setLambdaValues(lam=lam, lam_vals=lam_vals) # Create and append the required processes for each leg. # Nest the working directories inside self._work_dir. # SOMD. if self._engine == "SOMD": # TODO: This is currently hard-coded to use SOMD with the CUDA platform. leg0.append( _Process.Somd(system0, self._protocol, platform="CUDA", work_dir="%s/lambda_%5.4f" % (self._dir0, lam))) leg1.append( _Process.Somd(system1, self._protocol, platform="CUDA", work_dir="%s/lambda_%5.4f" % (self._dir1, lam))) # GROMACS. elif self._engine == "GROMACS": # TODO: This is currently hard-coded to use SOMD with the CUDA platform. leg0.append( _Process.Gromacs(system0, self._protocol, work_dir="%s/lambda_%5.4f" % (self._dir0, lam))) leg1.append( _Process.Gromacs(system1, self._protocol, work_dir="%s/lambda_%5.4f" % (self._dir1, lam))) # Initialise the process runner. All processes have already been nested # inside the working directory so no need to re-nest. self._runner = _Process.ProcessRunner(leg0 + leg1, work_dir=self._work_dir, nest_dirs=False)