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_cloned_schema_merging_simulation(registered_property_name, available_layer): """Tests that two, the exact the same, calculations get merged into one by the `WorkflowGraph`.""" registered_property = registered_properties[registered_property_name] dummy_property = create_dummy_property(registered_property) workflow_schema = dummy_property.get_default_workflow_schema(available_layer, WorkflowOptions()) if workflow_schema is None: return global_metadata = create_dummy_metadata(dummy_property, available_layer) workflow_a = Workflow(dummy_property, global_metadata) workflow_a.schema = workflow_schema workflow_b = Workflow(dummy_property, global_metadata) workflow_b.schema = workflow_schema workflow_graph = WorkflowGraph() workflow_graph.add_workflow(workflow_a) workflow_graph.add_workflow(workflow_b) ordered_dict_a = OrderedDict(sorted(workflow_a.dependants_graph.items())) ordered_dict_b = OrderedDict(sorted(workflow_b.dependants_graph.items())) merge_order_a = graph.topological_sort(ordered_dict_a) merge_order_b = graph.topological_sort(ordered_dict_b) assert len(workflow_graph._protocols_by_id) == len(workflow_a.protocols) for protocol_id in workflow_a.protocols: assert protocol_id in workflow_graph._protocols_by_id for protocol_id_A, protocol_id_B in zip(merge_order_a, merge_order_b): assert protocol_id_A == protocol_id_B assert workflow_a.protocols[protocol_id_A].schema.json() == \ workflow_b.protocols[protocol_id_B].schema.json()
def test_workflow_schema_simulation(registered_property_name, available_layer): """Tests serialisation and deserialization of a calculation schema.""" registered_property = registered_properties[registered_property_name] schema = registered_property.get_default_workflow_schema(available_layer, WorkflowOptions()) if schema is None: return schema.validate_interfaces() json_schema = schema.json() print(json_schema) schema_from_json = WorkflowSchema.parse_json(json_schema) print(schema_from_json) property_recreated_json = schema_from_json.json() assert json_schema == property_recreated_json
def create_debug_density_workflow(max_molecules=128, equilibration_steps=50, equilibration_frequency=5, production_steps=100, production_frequency=5): density_workflow_schema = Density.get_default_simulation_workflow_schema( WorkflowOptions()) build_coordinates = coordinates.BuildCoordinatesPackmol('') build_coordinates.schema = density_workflow_schema.protocols[ 'build_coordinates'] build_coordinates.max_molecules = max_molecules density_workflow_schema.protocols[ 'build_coordinates'] = build_coordinates.schema npt_equilibration = simulation.RunOpenMMSimulation('') npt_equilibration.schema = density_workflow_schema.protocols[ 'npt_equilibration'] npt_equilibration.steps_per_iteration = equilibration_steps npt_equilibration.output_frequency = equilibration_frequency density_workflow_schema.protocols[ 'npt_equilibration'] = npt_equilibration.schema converge_uncertainty = groups.ConditionalGroup('') converge_uncertainty.schema = density_workflow_schema.protocols[ 'converge_uncertainty'] converge_uncertainty.protocols[ 'npt_production'].steps_per_iteration = production_steps converge_uncertainty.protocols[ 'npt_production'].output_frequency = production_frequency density_workflow_schema.protocols[ 'converge_uncertainty'] = converge_uncertainty.schema return density_workflow_schema
def main(): setup_timestamp_logging() # Load in the force field force_field_path = 'smirnoff99Frosst-1.1.0.offxml' force_field_source = SmirnoffForceFieldSource.from_path(force_field_path) # Load in the data set containing a single dielectric # property. with open('pure_data_set.json') as file: data_set = PhysicalPropertyDataSet.parse_json(file.read()) data_set.filter_by_property_types('DielectricConstant') # Set up the server object which run the calculations. setup_server(backend_type=BackendType.LocalGPU, max_number_of_workers=1, port=8001) # Request the estimates. property_estimator = client.PropertyEstimatorClient( client.ConnectionOptions(server_port=8001)) options = PropertyEstimatorOptions() options.allowed_calculation_layers = ['SimulationLayer'] options.workflow_options = { 'DielectricConstant': { 'SimulationLayer': WorkflowOptions(WorkflowOptions.ConvergenceMode.NoChecks), 'ReweightingLayer': WorkflowOptions(WorkflowOptions.ConvergenceMode.NoChecks) } } parameter_gradient_keys = [ ParameterGradientKey(tag='vdW', smirks='[#6X4:1]', attribute='epsilon'), ParameterGradientKey(tag='vdW', smirks='[#6X4:1]', attribute='rmin_half') ] request = property_estimator.request_estimate( property_set=data_set, force_field_source=force_field_source, options=options, parameter_gradient_keys=parameter_gradient_keys) # Wait for the results. results = request.results(True, 5) # Save the result to file. with open('dielectric_simulation.json', 'wb') as file: json_results = json.dumps(results, sort_keys=True, indent=2, separators=(',', ': '), cls=TypedJSONEncoder) file.write(json_results.encode('utf-8')) # Attempt to reweight the cached data. options.allowed_calculation_layers = ['ReweightingLayer'] request = property_estimator.request_estimate( property_set=data_set, force_field_source=force_field_source, options=options, parameter_gradient_keys=parameter_gradient_keys) # Wait for the results. results = request.results(True, 5) # Save the result to file. with open('dielectric_reweight.json', 'wb') as file: json_results = json.dumps(results, sort_keys=True, indent=2, separators=(',', ': '), cls=TypedJSONEncoder) file.write(json_results.encode('utf-8'))
def main(): setup_timestamp_logging() # Load in the force field force_field_path = 'smirnoff99Frosst-1.1.0.offxml' force_field_source = SmirnoffForceFieldSource.from_path(force_field_path) # Create a data set containing three solvation free energies. data_set = _create_data_set() # Set up the compute backend which will run the calculations. working_directory = 'working_directory' storage_directory = 'storage_directory' queue_resources = QueueWorkerResources( number_of_threads=1, number_of_gpus=1, preferred_gpu_toolkit=QueueWorkerResources.GPUToolkit.CUDA, per_thread_memory_limit=5 * unit.gigabyte, wallclock_time_limit="05:59") worker_script_commands = [ 'conda activate propertyestimator', 'module load cuda/10.1' ] calculation_backend = DaskLSFBackend( minimum_number_of_workers=1, maximum_number_of_workers=3, resources_per_worker=queue_resources, queue_name='gpuqueue', setup_script_commands=worker_script_commands, adaptive_interval='1000ms', adaptive_class=CustomAdaptive) # Set up a backend to cache simulation data in. storage_backend = LocalFileStorage(storage_directory) # Spin up the server object. PropertyEstimatorServer(calculation_backend=calculation_backend, storage_backend=storage_backend, port=8005, working_directory=working_directory) # Request the estimates. property_estimator = client.PropertyEstimatorClient( client.ConnectionOptions(server_port=8005)) options = PropertyEstimatorOptions() options.allowed_calculation_layers = ['SimulationLayer'] workflow_options = WorkflowOptions( WorkflowOptions.ConvergenceMode.NoChecks) options.workflow_options = { 'SolvationFreeEnergy': { 'SimulationLayer': workflow_options } } options.workflow_schemas = { 'SolvationFreeEnergy': { 'SimulationLayer': _get_fixed_lambda_schema(workflow_options) } } request = property_estimator.request_estimate( property_set=data_set, force_field_source=force_field_source, options=options) # Wait for the results. results = request.results(True, 5) # Save the result to file. with open('solvation_free_energy_simulation.json', 'wb') as file: json_results = json.dumps(results, sort_keys=True, indent=2, separators=(',', ': '), cls=TypedJSONEncoder) file.write(json_results.encode('utf-8'))
def request_estimate(self, property_set, force_field_source, options=None, parameter_gradient_keys=None): """Requests that a PropertyEstimatorServer attempt to estimate the provided property set using the supplied force field and estimator options. Parameters ---------- property_set : PhysicalPropertyDataSet The set of properties to attempt to estimate. force_field_source : ForceFieldSource or openforcefield.typing.engines.smirnoff.ForceField The source of the force field parameters to use for the calculations. options : PropertyEstimatorOptions, optional A set of estimator options. If None, default options will be used. parameter_gradient_keys: list of ParameterGradientKey, optional A list of references to all of the parameters which all observables should be differentiated with respect to. Returns ------- PropertyEstimatorClient.Request An object which will provide access the the results of the request. """ from openforcefield.typing.engines import smirnoff if property_set is None or force_field_source is None: raise ValueError('Both a data set and force field source must be ' 'present to compute physical properties.') if options is None: options = PropertyEstimatorOptions() if isinstance(force_field_source, smirnoff.ForceField): force_field_source = SmirnoffForceFieldSource.from_object( force_field_source) if len(options.allowed_calculation_layers) == 0: raise ValueError( 'A submission contains no allowed calculation layers.') properties_list = [] property_types = set() # Refactor the properties into a list, and extract the types # of properties to be estimated (e.g 'Denisty', 'DielectricConstant'). for substance_tag in property_set.properties: for physical_property in property_set.properties[substance_tag]: properties_list.append(physical_property) type_name = type(physical_property).__name__ if type_name not in registered_properties: raise ValueError( f'The property estimator does not support {type_name} properties.' ) if type_name in property_types: continue property_types.add(type_name) if options.workflow_options is None: options.workflow_options = {} # Assign default workflows in the cases where the user hasn't # provided one, and validate all of the workflows to be used # in the estimation. for type_name in property_types: if type_name not in options.workflow_schemas: options.workflow_schemas[type_name] = {} if type_name not in options.workflow_options: options.workflow_options[type_name] = {} for calculation_layer in options.allowed_calculation_layers: property_type = registered_properties[type_name]() if (calculation_layer not in options.workflow_options[type_name] or options.workflow_options[type_name][calculation_layer] is None): options.workflow_options[type_name][ calculation_layer] = WorkflowOptions() if (calculation_layer not in options.workflow_schemas[type_name] or options.workflow_schemas[type_name][calculation_layer] is None): default_schema = property_type.get_default_workflow_schema( calculation_layer, options.workflow_options[type_name][calculation_layer]) options.workflow_schemas[type_name][ calculation_layer] = default_schema workflow = options.workflow_schemas[type_name][ calculation_layer] if workflow is None: # Not all properties may support every calculation layer. continue # Handle the cases where some protocol types should be replaced with # others. workflow.replace_protocol_types( options.workflow_options[type_name] [calculation_layer].protocol_replacements) # Will raise the correct exception for non-valid interfaces. workflow.validate_interfaces() # Enforce the global option of whether to allow merging or not. for protocol_schema_name in workflow.protocols: protocol_schema = workflow.protocols[protocol_schema_name] if not options.allow_protocol_merging: protocol_schema.inputs['.allow_merging'] = False submission = PropertyEstimatorSubmission( properties=properties_list, force_field_source=force_field_source, options=options, parameter_gradient_keys=parameter_gradient_keys) request_id = IOLoop.current().run_sync( lambda: self._send_calculations_to_server(submission)) request_object = PropertyEstimatorClient.Request( request_id, self._connection_options, self) return request_object
def get_estimation_options(protocol_replacements): """Returns the estimation options which describe the absolute uncertainties to within which properties should be estimated. Parameters ---------- protocol_replacements: dict of str and str A dictionary with keys of classes protocols to replace, and values of the protocol class to use as a replacement. Returns ------- options: PropertyEstimatorOptions The estimation of options. """ options = PropertyEstimatorOptions() options.allowed_calculation_layers = ['SimulationLayer'] options.workflow_options = { 'Density': { 'SimulationLayer': WorkflowOptions(convergence_mode=WorkflowOptions.ConvergenceMode. AbsoluteUncertainty, absolute_uncertainty=0.45 * unit.kilogram * unit.meter**-3, protocol_replacements=protocol_replacements) }, 'DielectricConstant': { 'SimulationLayer': WorkflowOptions(convergence_mode=WorkflowOptions.ConvergenceMode. AbsoluteUncertainty, absolute_uncertainty=1.5 * unit.dimensionless, protocol_replacements=protocol_replacements) }, 'EnthalpyOfVaporization': { 'SimulationLayer': WorkflowOptions(convergence_mode=WorkflowOptions.ConvergenceMode. AbsoluteUncertainty, absolute_uncertainty=0.65 * unit.kilojoule / unit.mole, protocol_replacements=protocol_replacements) }, 'EnthalpyOfMixing': { 'SimulationLayer': WorkflowOptions(convergence_mode=WorkflowOptions.ConvergenceMode. AbsoluteUncertainty, absolute_uncertainty=0.02 * unit.kilojoule / unit.mole, protocol_replacements=protocol_replacements) }, 'ExcessMolarVolume': { 'SimulationLayer': WorkflowOptions(convergence_mode=WorkflowOptions.ConvergenceMode. AbsoluteUncertainty, absolute_uncertainty=2e-8 * unit.meter**3 / unit.mole, protocol_replacements=protocol_replacements) } } return options