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
Exemple #4
0
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
Exemple #5
0
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'))
Exemple #7
0
    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