def _execute(self, directory, available_resources): yaml_filename = os.path.join(directory, "yank.yaml") # Create the yank yaml input file from a dictionary of options. with open(yaml_filename, "w") as file: yaml.dump( self._get_full_input_dictionary(available_resources), file, sort_keys=False, ) setup_only = self.setup_only # Yank is not safe to be called from anything other than the main thread. # If the current thread is not detected as the main one, then yank should # be spun up in a new process which should itself be safe to run yank in. if threading.current_thread() is threading.main_thread(): logger.info("Launching YANK in the main thread.") free_energy, free_energy_uncertainty = self._run_yank( directory, available_resources, setup_only) else: from multiprocessing import Process, Queue logger.info("Launching YANK in a new process.") # Create a queue to pass the results back to the main process. queue = Queue() # Create the process within which yank will run. process = Process( target=BaseYankProtocol._run_yank_as_process, args=[queue, directory, available_resources, setup_only], ) # Start the process and gather back the output. process.start() free_energy, free_energy_uncertainty, exception = queue.get() process.join() if exception is not None: raise exception self.estimated_free_energy = openmm_quantity_to_pint( free_energy).plus_minus( openmm_quantity_to_pint(free_energy_uncertainty))
def test_openmm_unit_to_pint(openmm_unit, value): openmm_quantity = value * openmm_unit openmm_raw_value = openmm_quantity.value_in_unit(openmm_unit) pint_quantity = openmm_quantity_to_pint(openmm_quantity) pint_raw_value = pint_quantity.magnitude assert np.allclose(openmm_raw_value, pint_raw_value)
def test_daltons(): openmm_quantity = random() * simtk_unit.dalton openmm_raw_value = openmm_quantity.value_in_unit(simtk_unit.gram / simtk_unit.mole) pint_quantity = openmm_quantity_to_pint(openmm_quantity) pint_raw_value = pint_quantity.to(unit.gram / unit.mole).magnitude assert np.allclose(openmm_raw_value, pint_raw_value)
def get_molecular_weight(smiles): from simtk import unit as simtk_unit from openforcefield.topology import Molecule molecule = Molecule.from_smiles(smiles, allow_undefined_stereo=True) molecular_weight = 0.0 * simtk_unit.dalton for atom in molecule.atoms: molecular_weight += atom.mass return openmm_quantity_to_pint(molecular_weight)
def test_combinatorial_openmm_to_pint(): all_openmm_units = list(_get_all_openmm_units()) for i in range(len(all_openmm_units)): for j in range(i, len(all_openmm_units)): openmm_unit = all_openmm_units[i] * all_openmm_units[j] openmm_quantity = random() * openmm_unit openmm_raw_value = openmm_quantity.value_in_unit(openmm_unit) pint_quantity = openmm_quantity_to_pint(openmm_quantity) pint_raw_value = pint_quantity.magnitude assert np.isclose(openmm_raw_value, pint_raw_value)
def test_openmm_unit_conversions(openmm_unit, value): openmm_quantity = value * openmm_unit openmm_base_quantity = openmm_quantity.in_unit_system( simtk_unit.md_unit_system) if not isinstance(openmm_base_quantity, simtk_unit.Quantity): openmm_base_quantity *= simtk_unit.dimensionless pint_base_quantity = openmm_quantity_to_pint(openmm_base_quantity) pint_unit = openmm_unit_to_pint(openmm_unit) pint_quantity = pint_base_quantity.to(pint_unit) pint_raw_value = pint_quantity.magnitude openmm_raw_value = openmm_quantity.value_in_unit(openmm_unit) assert np.allclose(openmm_raw_value, pint_raw_value)
def _execute(self, directory, available_resources): import mdtraj from openforcefield.topology import Molecule, Topology with open(self.force_field_path) as file: force_field_source = ForceFieldSource.parse_json(file.read()) if not isinstance(force_field_source, SmirnoffForceFieldSource): raise ValueError( "Only SMIRNOFF force fields are supported by this protocol.", ) # Load in the inputs force_field = force_field_source.to_force_field() trajectory = mdtraj.load_dcd(self.trajectory_file_path, self.coordinate_file_path) unique_molecules = [] for component in self.substance.components: molecule = Molecule.from_smiles(smiles=component.smiles) unique_molecules.append(molecule) pdb_file = app.PDBFile(self.coordinate_file_path) topology = Topology.from_openmm(pdb_file.topology, unique_molecules=unique_molecules) # Compute the difference between the energies using the reduced force field, # and the full force field. energy_corrections = None if self.use_subset_of_force_field: target_system, _ = self._build_reduced_system( force_field, topology) subset_potentials_path = os.path.join(directory, f"subset.csv") subset_potentials = self._evaluate_reduced_potential( target_system, trajectory, subset_potentials_path, available_resources) full_statistics = StatisticsArray.from_pandas_csv( self.statistics_path) energy_corrections = ( full_statistics[ObservableType.PotentialEnergy] - subset_potentials[ObservableType.PotentialEnergy]) # Build the slightly perturbed system. reverse_system, reverse_parameter_value = self._build_reduced_system( force_field, topology, -self.perturbation_scale) forward_system, forward_parameter_value = self._build_reduced_system( force_field, topology, self.perturbation_scale) self.reverse_parameter_value = openmm_quantity_to_pint( reverse_parameter_value) self.forward_parameter_value = openmm_quantity_to_pint( forward_parameter_value) # Calculate the reduced potentials. self.reverse_potentials_path = os.path.join(directory, "reverse.csv") self.forward_potentials_path = os.path.join(directory, "forward.csv") self._evaluate_reduced_potential( reverse_system, trajectory, self.reverse_potentials_path, available_resources, energy_corrections, ) self._evaluate_reduced_potential( forward_system, trajectory, self.forward_potentials_path, available_resources, energy_corrections, )
def _approximate_box_size_by_density( molecules, n_copies, mass_density, box_aspect_ratio, box_scaleup_factor=1.1, ): """Generate an approximate box size based on the number and molecular weight of the molecules present, and a target density for the final solvated mixture. Parameters ---------- molecules : list of openforcefield.topology.Molecule The molecules in the system. n_copies : list of int The number of copies of each molecule. mass_density : pint.Quantity The target mass density for final system. It should have units compatible with g / mL. box_aspect_ratio: List of float The aspect ratio of the simulation box, used in conjunction with the `mass_density` parameter. box_scaleup_factor : float The factor by which the estimated box size should be increased. Returns ------- pint.Quantity A list of the three box lengths in units compatible with angstroms. """ volume = 0.0 * unit.angstrom**3 for (molecule, number) in zip(molecules, n_copies): molecule_mass = reduce((lambda x, y: x + y), [atom.mass for atom in molecule.atoms]) molecule_mass = openmm_quantity_to_pint( molecule_mass) / unit.avogadro_constant molecule_volume = molecule_mass / mass_density volume += molecule_volume * number box_length = volume**(1.0 / 3.0) * box_scaleup_factor box_length_angstrom = box_length.to(unit.angstrom).magnitude aspect_ratio_normalizer = (box_aspect_ratio[0] * box_aspect_ratio[1] * box_aspect_ratio[2])**(1.0 / 3.0) box_size = [ box_length_angstrom * box_aspect_ratio[0], box_length_angstrom * box_aspect_ratio[1], box_length_angstrom * box_aspect_ratio[2], ] * unit.angstrom box_size /= aspect_ratio_normalizer return box_size