def test_solvate_existing_structure_protocol(): """Tests solvating a single methanol molecule in water.""" methanol_substance = Substance() methanol_substance.add_component(Substance.Component('CO'), Substance.ExactAmount(1)) water_substance = Substance() water_substance.add_component(Substance.Component('O'), Substance.MoleFraction(1.0)) with tempfile.TemporaryDirectory() as temporary_directory: build_methanol_coordinates = BuildCoordinatesPackmol('build_methanol') build_methanol_coordinates.max_molecules = 1 build_methanol_coordinates.substance = methanol_substance build_methanol_coordinates.execute(temporary_directory, ComputeResources()) solvate_coordinates = SolvateExistingStructure('solvate_methanol') solvate_coordinates.max_molecules = 9 solvate_coordinates.substance = water_substance solvate_coordinates.solute_coordinate_file = build_methanol_coordinates.coordinate_file_path solvate_coordinates.execute(temporary_directory, ComputeResources()) solvated_pdb = PDBFile(solvate_coordinates.coordinate_file_path) assert solvated_pdb.topology.getNumResidues() == 10
def create_dummy_substance(number_of_components, elements=None): """Creates a substance with a given number of components, each containing the specified elements. Parameters ---------- number_of_components : int The number of components to add to the substance. elements : list of str The elements that each component should containt. Returns ------- Substance The created substance. """ if elements is None: elements = ['C'] substance = Substance() mole_fraction = 1.0 / number_of_components for index in range(number_of_components): smiles_pattern = ''.join(elements * (index + 1)) substance.add_component(Substance.Component(smiles_pattern), Substance.MoleFraction(mole_fraction)) return substance
def test_filter_by_smiles(): """A test to ensure that data sets may be filtered by which smiles their measured properties contain.""" methanol_substance = Substance() methanol_substance.add_component(Substance.Component('CO'), Substance.MoleFraction(1.0)) ethanol_substance = Substance() ethanol_substance.add_component(Substance.Component('CCO'), Substance.MoleFraction(1.0)) property_a = create_dummy_property(Density) property_a.substance = methanol_substance property_b = create_dummy_property(Density) property_b.substance = ethanol_substance data_set = PhysicalPropertyDataSet() data_set.properties[methanol_substance.identifier] = [property_a] data_set.properties[ethanol_substance.identifier] = [property_b] data_set.filter_by_smiles('CO') assert data_set.number_of_properties == 1 assert methanol_substance.identifier in data_set.properties assert ethanol_substance.identifier not in data_set.properties
def test_density_dielectric_merging(): substance = Substance() substance.add_component(Substance.Component(smiles='C'), Substance.MoleFraction()) density = Density(thermodynamic_state=ThermodynamicState(temperature=298*unit.kelvin, pressure=1*unit.atmosphere), phase=PropertyPhase.Liquid, substance=substance, value=10*unit.gram/unit.mole, uncertainty=1*unit.gram/unit.mole) dielectric = DielectricConstant(thermodynamic_state=ThermodynamicState(temperature=298*unit.kelvin, pressure=1*unit.atmosphere), phase=PropertyPhase.Liquid, substance=substance, value=10*unit.gram/unit.mole, uncertainty=1*unit.gram/unit.mole) density_schema = density.get_default_workflow_schema('SimulationLayer', WorkflowOptions()) dielectric_schema = dielectric.get_default_workflow_schema('SimulationLayer', WorkflowOptions()) density_metadata = Workflow.generate_default_metadata(density, 'smirnoff99Frosst-1.1.0.offxml', []) dielectric_metadata = Workflow.generate_default_metadata(density, 'smirnoff99Frosst-1.1.0.offxml', []) density_workflow = Workflow(density, density_metadata) density_workflow.schema = density_schema dielectric_workflow = Workflow(dielectric, dielectric_metadata) dielectric_workflow.schema = dielectric_schema workflow_graph = WorkflowGraph('') workflow_graph.add_workflow(density_workflow) workflow_graph.add_workflow(dielectric_workflow) merge_order_a = graph.topological_sort(density_workflow.dependants_graph) merge_order_b = graph.topological_sort(dielectric_workflow.dependants_graph) for protocol_id_A, protocol_id_B in zip(merge_order_a, merge_order_b): if protocol_id_A.find('extract_traj') < 0 and protocol_id_A.find('extract_stats') < 0: assert density_workflow.protocols[protocol_id_A].schema.json() == \ dielectric_workflow.protocols[protocol_id_B].schema.json() else: assert density_workflow.protocols[protocol_id_A].schema.json() != \ dielectric_workflow.protocols[protocol_id_B].schema.json()
def test_multiple_amounts(): substance = Substance() substance.add_component(Substance.Component('[Na+]'), Substance.MoleFraction(0.75)) substance.add_component(Substance.Component('[Na+]'), Substance.ExactAmount(1)) substance.add_component(Substance.Component('[Cl-]'), Substance.MoleFraction(0.25)) substance.add_component(Substance.Component('[Cl-]'), Substance.ExactAmount(1)) assert substance.number_of_components == 2 sodium_amounts = substance.get_amounts('[Na+]') chlorine_amounts = substance.get_amounts('[Cl-]') assert len(sodium_amounts) == 2 assert len(chlorine_amounts) == 2 molecule_counts = substance.get_molecules_per_component(6) assert len(molecule_counts) == 2 assert molecule_counts['[Na+]'] == 4 assert molecule_counts['[Cl-]'] == 2
def test_add_mole_fractions(): substance = Substance() substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.5)) substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.5)) assert substance.number_of_components == 1 amounts = substance.get_amounts('C') assert len(amounts) == 1 amount = next(iter(amounts)) assert isinstance(amount, Substance.MoleFraction) assert np.isclose(amount.value, 1.0)
def retrieve_simulation_data(self, substance, include_component_data=True, data_class=StoredSimulationData): substance_ids = {substance.identifier} # Find the substance identifiers of the substance components if # we should include component data. if isinstance(substance, Substance) and include_component_data is True: for component in substance.components: component_substance = Substance() component_substance.add_component(component, Substance.MoleFraction()) substance_ids.add(component_substance.identifier) return_data = {} for substance_id in substance_ids: if substance_id not in self._simulation_data_by_substance: continue return_data[substance_id] = [] for data_key in self._simulation_data_by_substance[substance_id]: data_object, data_directory = self.retrieve_simulation_data_by_id( data_key) if data_object is None: continue if not isinstance(data_object, data_class): continue return_data[substance_id].append((data_object, data_directory)) return return_data
def test_data_class_retrieval(): """A simple test to that force fields can be stored and retrieved using the local storage backend.""" substance = Substance() substance.add_component(Substance.Component('C'), Substance.MoleFraction(1.0)) with tempfile.TemporaryDirectory() as base_directory_path: storage_directory = os.path.join(base_directory_path, 'storage') local_storage = LocalFileStorage(storage_directory) for data_class_type in [DummyDataClass1, DummyDataClass2]: data_directory = os.path.join(base_directory_path, data_class_type.__name__) os.makedirs(data_directory, exist_ok=True) data_object = data_class_type() data_object.substance = substance local_storage.store_simulation_data(data_object, data_directory) retrieved_data_directories = local_storage.retrieve_simulation_data( substance, data_class=BaseStoredData) assert len(retrieved_data_directories[substance.identifier]) == 2 retrieved_data_directories = local_storage.retrieve_simulation_data( substance, data_class=DummyDataClass1) assert len(retrieved_data_directories[substance.identifier]) == 1 retrieved_data_directories = local_storage.retrieve_simulation_data( substance, data_class=DummyDataClass2) assert len(retrieved_data_directories[substance.identifier]) == 1 retrieved_data_directories = local_storage.retrieve_simulation_data( substance, data_class=StoredSimulationData) assert len(retrieved_data_directories[substance.identifier]) == 0
def create_substance(): test_substance = Substance() test_substance.add_component(Substance.Component('C', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) test_substance.add_component(Substance.Component('CC', role=Substance.ComponentRole.Ligand), Substance.ExactAmount(1)) test_substance.add_component(Substance.Component('CCC', role=Substance.ComponentRole.Receptor), Substance.ExactAmount(1)) test_substance.add_component(Substance.Component('O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) return test_substance
def test_build_docked_coordinates_protocol(): """Tests docking a methanol molecule into alpha-Cyclodextrin.""" ligand_substance = Substance() ligand_substance.add_component( Substance.Component('CO', role=Substance.ComponentRole.Ligand), Substance.ExactAmount(1)) # TODO: This test could likely be made substantially faster # by storing the binary prepared receptor. Would this # be in breach of any oe license terms? with tempfile.TemporaryDirectory() as temporary_directory: build_docked_coordinates = BuildDockedCoordinates('build_methanol') build_docked_coordinates.ligand_substance = ligand_substance build_docked_coordinates.number_of_ligand_conformers = 5 build_docked_coordinates.receptor_coordinate_file = get_data_filename( 'test/molecules/acd.mol2') build_docked_coordinates.execute(temporary_directory, ComputeResources()) docked_pdb = PDBFile( build_docked_coordinates.docked_complex_coordinate_path) assert docked_pdb.topology.getNumResidues() == 2
def _build_input_output_substances(): """Builds sets if input and expected substances for the `test_build_coordinate_composition` test. Returns ------- list of tuple of Substance and Substance A list of input and expected substances. """ # Start with some easy cases substances = [ (Substance.from_components('O'), Substance.from_components('O')), (Substance.from_components('O', 'C'), Substance.from_components('O', 'C')), (Substance.from_components('O', 'C', 'CO'), Substance.from_components('O', 'C', 'CO')) ] # Handle some cases where rounding will need to occur. input_substance = Substance() input_substance.add_component(Substance.Component('O'), Substance.MoleFraction(0.41)) input_substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.59)) expected_substance = Substance() expected_substance.add_component(Substance.Component('O'), Substance.MoleFraction(0.4)) expected_substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.6)) substances.append((input_substance, expected_substance)) input_substance = Substance() input_substance.add_component(Substance.Component('O'), Substance.MoleFraction(0.59)) input_substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.41)) expected_substance = Substance() expected_substance.add_component(Substance.Component('O'), Substance.MoleFraction(0.6)) expected_substance.add_component(Substance.Component('C'), Substance.MoleFraction(0.4)) substances.append((input_substance, expected_substance)) return substances
def test_ligand_receptor_yank_protocol(): full_substance = Substance() full_substance.add_component( Substance.Component(smiles='c1ccccc1', role=Substance.ComponentRole.Receptor), Substance.ExactAmount(1)) full_substance.add_component( Substance.Component(smiles='C', role=Substance.ComponentRole.Ligand), Substance.ExactAmount(1)) full_substance.add_component( Substance.Component(smiles='O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) solute_substance = Substance() solute_substance.add_component( Substance.Component(smiles='C', role=Substance.ComponentRole.Ligand), Substance.ExactAmount(1)) solute_substance.add_component( Substance.Component(smiles='O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) thermodynamic_state = ThermodynamicState(temperature=298.15 * unit.kelvin, pressure=1.0 * unit.atmosphere) with tempfile.TemporaryDirectory() as directory: with temporarily_change_directory(directory): force_field_path = 'ff.json' with open(force_field_path, 'w') as file: file.write(build_tip3p_smirnoff_force_field().json()) complex_coordinate_path, complex_system_path = _setup_dummy_system( 'full', full_substance, 3, force_field_path) ligand_coordinate_path, ligand_system_path = _setup_dummy_system( 'ligand', solute_substance, 2, force_field_path) run_yank = LigandReceptorYankProtocol('yank') run_yank.substance = full_substance run_yank.thermodynamic_state = thermodynamic_state run_yank.number_of_iterations = 1 run_yank.steps_per_iteration = 1 run_yank.checkpoint_interval = 1 run_yank.verbose = True run_yank.setup_only = True run_yank.ligand_residue_name = 'TMP' run_yank.receptor_residue_name = 'TMP' run_yank.solvated_ligand_coordinates = ligand_coordinate_path run_yank.solvated_ligand_system = ligand_system_path run_yank.solvated_complex_coordinates = complex_coordinate_path run_yank.solvated_complex_system = complex_system_path run_yank.force_field_path = force_field_path result = run_yank.execute('', ComputeResources()) assert not isinstance(result, PropertyEstimatorException)
def test_solvation_yank_protocol(solvent_smiles): full_substance = Substance() full_substance.add_component( Substance.Component(smiles='CO', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) full_substance.add_component( Substance.Component(smiles=solvent_smiles, role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) solvent_substance = Substance() solvent_substance.add_component( Substance.Component(smiles=solvent_smiles, role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) solute_substance = Substance() solute_substance.add_component( Substance.Component(smiles='CO', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) thermodynamic_state = ThermodynamicState(temperature=298.15 * unit.kelvin, pressure=1.0 * unit.atmosphere) with tempfile.TemporaryDirectory() as directory: with temporarily_change_directory(directory): force_field_path = 'ff.json' with open(force_field_path, 'w') as file: file.write(build_tip3p_smirnoff_force_field().json()) solvated_coordinate_path, solvated_system_path = _setup_dummy_system( 'full', full_substance, 2, force_field_path) vacuum_coordinate_path, vacuum_system_path = _setup_dummy_system( 'vacuum', solute_substance, 1, force_field_path) run_yank = SolvationYankProtocol('yank') run_yank.solute = solute_substance run_yank.solvent_1 = solvent_substance run_yank.solvent_2 = Substance() run_yank.thermodynamic_state = thermodynamic_state run_yank.number_of_iterations = 1 run_yank.steps_per_iteration = 1 run_yank.checkpoint_interval = 1 run_yank.verbose = True run_yank.setup_only = True run_yank.solvent_1_coordinates = solvated_coordinate_path run_yank.solvent_1_system = solvated_system_path run_yank.solvent_2_coordinates = vacuum_coordinate_path run_yank.solvent_2_system = vacuum_system_path run_yank.electrostatic_lambdas_1 = [1.00] run_yank.steric_lambdas_1 = [1.00] run_yank.electrostatic_lambdas_2 = [1.00] run_yank.steric_lambdas_2 = [1.00] result = run_yank.execute('', ComputeResources()) assert not isinstance(result, PropertyEstimatorException)
def _rebuild_substance(self, number_of_molecules): """Rebuilds the `Substance` object which the protocol will create coordinates. This may not be the same as the input system due to the finite number of molecules to be added causing rounding of mole fractions. Parameters ---------- number_of_molecules: list of int The number of each component which should be added to the system. Returns ------- Substance The substance which contains the corrected component amounts. """ new_amounts = defaultdict(list) total_number_of_molecules = sum(number_of_molecules) # Handle any exact amounts. for component in self.substance.components: exact_amounts = [amount for amount in self.substance.get_amounts(component) if isinstance(amount, Substance.ExactAmount)] if len(exact_amounts) == 0: continue total_number_of_molecules -= exact_amounts[0].value new_amounts[component].append(exact_amounts[0]) # Recompute the mole fractions. total_mole_fraction = 0.0 number_of_new_mole_fractions = 0 for index, component in enumerate(self.substance.components): mole_fractions = [amount for amount in self.substance.get_amounts(component) if isinstance(amount, Substance.MoleFraction)] if len(mole_fractions) == 0: continue molecule_count = number_of_molecules[index] if component in new_amounts: molecule_count -= new_amounts[component][0].value new_mole_fraction = molecule_count / total_number_of_molecules new_amounts[component].append(Substance.MoleFraction(new_mole_fraction)) total_mole_fraction += new_mole_fraction number_of_new_mole_fractions += 1 if not np.isclose(total_mole_fraction, 1.0) and number_of_new_mole_fractions > 0: raise ValueError('The new mole fraction does not equal 1.0') output_substance = Substance() for component, amounts in new_amounts.items(): for amount in amounts: output_substance.add_component(component, amount) return output_substance
def _create_data_set(): """Create a small data set of three properties taken from the FreeSolv data set: https://github.com/mobleylab/FreeSolv. Returns ------- PhysicalPropertyDataSet The data set of three select FreeSolv properties. """ butan_1_ol = Substance() butan_1_ol.add_component( Substance.Component('CCCCO', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) butan_1_ol.add_component( Substance.Component('O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) butan_1_ol_property = SolvationFreeEnergy( thermodynamic_state=ThermodynamicState(298.15 * unit.kelvin, 1.0 * unit.atmosphere), phase=PropertyPhase.Liquid, substance=butan_1_ol, value=-4.72 * unit.kilocalorie / unit.mole, uncertainty=0.6 * unit.kilocalorie / unit.mole, source=MeasurementSource(doi=' 10.1021/ct050097l')) methyl_propanoate = Substance() methyl_propanoate.add_component( Substance.Component('CCC(=O)OC', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) methyl_propanoate.add_component( Substance.Component('O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) methyl_propanoate_property = SolvationFreeEnergy( thermodynamic_state=ThermodynamicState(298.15 * unit.kelvin, 1.0 * unit.atmosphere), phase=PropertyPhase.Liquid, substance=methyl_propanoate, value=-2.93 * unit.kilocalorie / unit.mole, uncertainty=0.6 * unit.kilocalorie / unit.mole, source=MeasurementSource(doi=' 10.1021/ct050097l')) benzamide = Substance() benzamide.add_component( Substance.Component('c1ccc(cc1)C(=O)N', role=Substance.ComponentRole.Solute), Substance.ExactAmount(1)) benzamide.add_component( Substance.Component('O', role=Substance.ComponentRole.Solvent), Substance.MoleFraction(1.0)) benzamide_property = SolvationFreeEnergy( thermodynamic_state=ThermodynamicState(298.15 * unit.kelvin, 1.0 * unit.atmosphere), phase=PropertyPhase.Liquid, substance=benzamide, value=-11.0 * unit.kilocalorie / unit.mole, uncertainty=0.2 * unit.kilocalorie / unit.mole, source=MeasurementSource(doi=' 10.1021/ct050097l')) data_set = PhysicalPropertyDataSet() data_set.properties[butan_1_ol.identifier] = [butan_1_ol_property] data_set.properties[methyl_propanoate.identifier] = [ methyl_propanoate_property ] data_set.properties[benzamide.identifier] = [benzamide_property] return data_set
def test_base_simulation_protocols(): """Tests that the commonly chain build coordinates, assigned topology, energy minimise and perform simulation are able to work together without raising an exception.""" water_substance = Substance() water_substance.add_component(Substance.Component(smiles='O'), Substance.MoleFraction()) thermodynamic_state = ThermodynamicState(298 * unit.kelvin, 1 * unit.atmosphere) with tempfile.TemporaryDirectory() as temporary_directory: force_field_source = build_tip3p_smirnoff_force_field() force_field_path = path.join(temporary_directory, 'ff.offxml') with open(force_field_path, 'w') as file: file.write(force_field_source.json()) build_coordinates = BuildCoordinatesPackmol('') # Set the maximum number of molecules in the system. build_coordinates.max_molecules = 10 # and the target density (the default 1.0 g/ml is normally fine) build_coordinates.mass_density = 0.05 * unit.grams / unit.milliliters # and finally the system which coordinates should be generated for. build_coordinates.substance = water_substance # Build the coordinates, creating a file called output.pdb result = build_coordinates.execute(temporary_directory, None) assert not isinstance(result, PropertyEstimatorException) # Assign some smirnoff force field parameters to the # coordinates print('Assigning some parameters.') assign_force_field_parameters = BuildSmirnoffSystem('') assign_force_field_parameters.force_field_path = force_field_path assign_force_field_parameters.coordinate_file_path = path.join( temporary_directory, 'output.pdb') assign_force_field_parameters.substance = water_substance result = assign_force_field_parameters.execute(temporary_directory, None) assert not isinstance(result, PropertyEstimatorException) # Do a simple energy minimisation print('Performing energy minimisation.') energy_minimisation = RunEnergyMinimisation('') energy_minimisation.input_coordinate_file = path.join( temporary_directory, 'output.pdb') energy_minimisation.system_path = assign_force_field_parameters.system_path result = energy_minimisation.execute(temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException) npt_equilibration = RunOpenMMSimulation('npt_equilibration') npt_equilibration.ensemble = Ensemble.NPT npt_equilibration.steps_per_iteration = 20 # Debug settings. npt_equilibration.output_frequency = 2 # Debug settings. npt_equilibration.thermodynamic_state = thermodynamic_state npt_equilibration.input_coordinate_file = path.join( temporary_directory, 'minimised.pdb') npt_equilibration.system_path = assign_force_field_parameters.system_path result = npt_equilibration.execute(temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException) extract_density = ExtractAverageStatistic('extract_density') extract_density.statistics_type = ObservableType.Density extract_density.statistics_path = path.join(temporary_directory, 'statistics.csv') result = extract_density.execute(temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException) extract_dielectric = ExtractAverageDielectric('extract_dielectric') extract_dielectric.thermodynamic_state = thermodynamic_state extract_dielectric.input_coordinate_file = path.join( temporary_directory, 'input.pdb') extract_dielectric.trajectory_path = path.join(temporary_directory, 'trajectory.dcd') extract_dielectric.system_path = assign_force_field_parameters.system_path result = extract_dielectric.execute(temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException) extract_uncorrelated_trajectory = ExtractUncorrelatedTrajectoryData( 'extract_traj') extract_uncorrelated_trajectory.statistical_inefficiency = extract_density.statistical_inefficiency extract_uncorrelated_trajectory.equilibration_index = extract_density.equilibration_index extract_uncorrelated_trajectory.input_coordinate_file = path.join( temporary_directory, 'input.pdb') extract_uncorrelated_trajectory.input_trajectory_path = path.join( temporary_directory, 'trajectory.dcd') result = extract_uncorrelated_trajectory.execute( temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException) extract_uncorrelated_statistics = ExtractUncorrelatedStatisticsData( 'extract_stats') extract_uncorrelated_statistics.statistical_inefficiency = extract_density.statistical_inefficiency extract_uncorrelated_statistics.equilibration_index = extract_density.equilibration_index extract_uncorrelated_statistics.input_statistics_path = path.join( temporary_directory, 'statistics.csv') result = extract_uncorrelated_statistics.execute( temporary_directory, ComputeResources()) assert not isinstance(result, PropertyEstimatorException)
class FilterSubstanceByRole(BaseProtocol): """A protocol which takes a substance as input, and returns a substance which only contains components whose role match a given criteria. """ input_substance = protocol_input(docstring='The substance to filter.', type_hint=Substance, default_value=UNDEFINED) component_role = protocol_input( docstring='The role to filter substance components against.', type_hint=Substance.ComponentRole, default_value=UNDEFINED) expected_components = protocol_input( docstring='The number of components expected to remain after filtering. ' 'An exception is raised if this number is not matched.', type_hint=int, default_value=UNDEFINED, optional=True) filtered_substance = protocol_output(docstring='The filtered substance.', type_hint=Substance) def execute(self, directory, available_resources): filtered_components = [] total_mole_fraction = 0.0 for component in self.input_substance.components: if component.role != self.component_role: continue filtered_components.append(component) amounts = self.input_substance.get_amounts(component) for amount in amounts: if not isinstance(amount, Substance.MoleFraction): continue total_mole_fraction += amount.value if (self.expected_components != UNDEFINED and self.expected_components != len(filtered_components)): return PropertyEstimatorException( directory=directory, message=f'The filtered substance does not contain the expected ' f'number of components ({self.expected_components}) - ' f'{filtered_components}') inverse_mole_fraction = 1.0 if np.isclose( total_mole_fraction, 0.0) else 1.0 / total_mole_fraction self.filtered_substance = Substance() for component in filtered_components: amounts = self.input_substance.get_amounts(component) for amount in amounts: if isinstance(amount, Substance.MoleFraction): amount = Substance.MoleFraction(amount.value * inverse_mole_fraction) self.filtered_substance.add_component(component, amount) return self._get_output_dictionary()
def _build_workflow_graph(working_directory, properties, target_force_field_path, stored_data_paths, parameter_gradient_keys, options): """Construct a workflow graph, containing all of the workflows which should be followed to estimate a set of properties by reweighting. Parameters ---------- working_directory: str The local directory in which to store all local, temporary calculation data from this graph. properties : list of PhysicalProperty The properties to attempt to compute. target_force_field_path : str The path to the target force field parameters to use in the workflow. stored_data_paths: dict of str and tuple(str, str) A dictionary partitioned by substance identifiers, whose values are a tuple of a path to a stored simulation data object, and its corresponding force field path. parameter_gradient_keys: list of ParameterGradientKey A list of references to all of the parameters which all observables should be differentiated with respect to. options: PropertyEstimatorOptions The options to run the workflows with. """ workflow_graph = WorkflowGraph(working_directory) for property_to_calculate in properties: if (not isinstance(property_to_calculate, IReweightable) or not isinstance(property_to_calculate, IWorkflowProperty)): # Only properties which implement the IReweightable and # IWorkflowProperty interfaces can be reweighted continue property_type = type(property_to_calculate).__name__ if property_type not in options.workflow_schemas: logging.warning('The reweighting layer does not support {} ' 'workflows.'.format(property_type)) continue if ReweightingLayer.__name__ not in options.workflow_schemas[ property_type]: continue schema = options.workflow_schemas[property_type][ ReweightingLayer.__name__] workflow_options = options.workflow_options[property_type].get( ReweightingLayer.__name__) global_metadata = Workflow.generate_default_metadata( property_to_calculate, target_force_field_path, parameter_gradient_keys, workflow_options) substance_id = property_to_calculate.substance.identifier data_class_type = property_to_calculate.required_data_class if (substance_id not in stored_data_paths or data_class_type not in stored_data_paths[substance_id]): # We haven't found and cached data which is compatible with this property. continue global_metadata['full_system_data'] = stored_data_paths[ substance_id][data_class_type] global_metadata['component_data'] = [] if property_to_calculate.multi_component_property: has_data_for_property = True for component in property_to_calculate.substance.components: temporary_substance = Substance() temporary_substance.add_component( component, amount=Substance.MoleFraction()) if (temporary_substance.identifier not in stored_data_paths or data_class_type not in stored_data_paths[ temporary_substance.identifier]): has_data_for_property = False break global_metadata['component_data'].append(stored_data_paths[ temporary_substance.identifier][data_class_type]) if not has_data_for_property: continue workflow = Workflow(property_to_calculate, global_metadata) workflow.schema = schema from propertyestimator.properties import CalculationSource workflow.physical_property.source = CalculationSource( fidelity=ReweightingLayer.__name__, provenance={}) workflow_graph.add_workflow(workflow) return workflow_graph