예제 #1
0
    def default_reweighting_schema(
        absolute_tolerance=UNDEFINED,
        relative_tolerance=UNDEFINED,
        n_effective_samples=50,
    ) -> ReweightingSchema:
        """Returns the default calculation schema to use when estimating
        this property by reweighting existing data.

        Parameters
        ----------
        absolute_tolerance: openff.evaluator.unit.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_effective_samples: int
            The minimum number of effective samples to require when
            reweighting the cached simulation data.

        Returns
        -------
        ReweightingSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = ReweightingSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        protocols, data_replicator = generate_reweighting_protocols(
            ObservableType.Density)
        protocols.reweight_observable.required_effective_samples = n_effective_samples

        schema = WorkflowSchema()
        schema.protocol_schemas = [x.schema for x in protocols]
        schema.protocol_replicators = [data_replicator]

        schema.final_value_source = ProtocolPath(
            "value", protocols.reweight_observable.id)

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #2
0
    def default_simulation_schema(
        cls,
        absolute_tolerance=UNDEFINED,
        relative_tolerance=UNDEFINED,
        n_molecules=1000,
    ) -> SimulationSchema:
        """Returns the default calculation schema to use when estimating
        this class of property from direct simulations.

        Parameters
        ----------
        absolute_tolerance: openff.evaluator.unit.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_molecules: int
            The number of molecules to use in the simulation.

        Returns
        -------
        SimulationSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = SimulationSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        use_target_uncertainty = (absolute_tolerance != UNDEFINED
                                  or relative_tolerance != UNDEFINED)

        # Define the protocols to use for the fully mixed system.
        (
            mixture_protocols,
            mixture_value,
            mixture_stored_data,
        ) = generate_simulation_protocols(
            analysis.AverageObservable("extract_observable_mixture"),
            use_target_uncertainty,
            id_suffix="_mixture",
            n_molecules=n_molecules,
        )
        # Specify the average observable which should be estimated.
        mixture_protocols.analysis_protocol.observable = ProtocolPath(
            f"observables[{cls._observable_type().value}]",
            mixture_protocols.production_simulation.id,
        )
        (
            mixture_protocols.analysis_protocol.divisor,
            mixture_n_molar_molecules,
        ) = cls._n_molecules_divisor(
            ProtocolPath("output_number_of_molecules",
                         mixture_protocols.build_coordinates.id),
            "_mixture",
        )

        # Define the protocols to use for each component, creating a replicator that
        # will copy these for each component in the mixture substance.
        component_replicator = ProtocolReplicator("component_replicator")
        component_replicator.template_values = ProtocolPath(
            "components", "global")
        component_substance = ReplicatorValue(component_replicator.id)

        component_protocols, _, component_stored_data = generate_simulation_protocols(
            analysis.AverageObservable(
                f"extract_observable_component_{component_replicator.placeholder_id}"
            ),
            use_target_uncertainty,
            id_suffix=f"_component_{component_replicator.placeholder_id}",
            n_molecules=n_molecules,
        )
        # Make sure the protocols point to the correct substance.
        component_protocols.build_coordinates.substance = component_substance
        # Specify the average observable which should be estimated.
        component_protocols.analysis_protocol.observable = ProtocolPath(
            f"observables[{cls._observable_type().value}]",
            component_protocols.production_simulation.id,
        )
        (
            component_protocols.analysis_protocol.divisor,
            component_n_molar_molecules,
        ) = cls._n_molecules_divisor(
            ProtocolPath("output_number_of_molecules",
                         component_protocols.build_coordinates.id),
            f"_component_{component_replicator.placeholder_id}",
        )

        # Weight the component value by the mole fraction.
        weight_by_mole_fraction = miscellaneous.WeightByMoleFraction(
            f"weight_by_mole_fraction_{component_replicator.placeholder_id}")
        weight_by_mole_fraction.value = ProtocolPath(
            "value", component_protocols.analysis_protocol.id)
        weight_by_mole_fraction.full_substance = ProtocolPath(
            "substance", "global")
        weight_by_mole_fraction.component = component_substance

        component_protocols.converge_uncertainty.add_protocols(
            weight_by_mole_fraction)

        # Make sure the convergence criteria is set to use the per component
        # uncertainty target.
        if use_target_uncertainty:
            component_protocols.converge_uncertainty.conditions[
                0].right_hand_value = ProtocolPath("per_component_uncertainty",
                                                   "global")

        # Finally, set up the protocols which will be responsible for adding together
        # the component observables, and subtracting these from the mixture system value.
        add_component_observables = miscellaneous.AddValues(
            "add_component_observables")
        add_component_observables.values = ProtocolPath(
            "weighted_value",
            component_protocols.converge_uncertainty.id,
            weight_by_mole_fraction.id,
        )

        calculate_excess_observable = miscellaneous.SubtractValues(
            "calculate_excess_observable")
        calculate_excess_observable.value_b = mixture_value
        calculate_excess_observable.value_a = ProtocolPath(
            "result", add_component_observables.id)

        # Build the final workflow schema
        schema = WorkflowSchema()

        schema.protocol_schemas = [
            component_protocols.build_coordinates.schema,
            component_protocols.assign_parameters.schema,
            component_protocols.energy_minimisation.schema,
            component_protocols.equilibration_simulation.schema,
            component_protocols.converge_uncertainty.schema,
            component_protocols.decorrelate_trajectory.schema,
            component_protocols.decorrelate_observables.schema,
            mixture_protocols.build_coordinates.schema,
            mixture_protocols.assign_parameters.schema,
            mixture_protocols.energy_minimisation.schema,
            mixture_protocols.equilibration_simulation.schema,
            mixture_protocols.converge_uncertainty.schema,
            mixture_protocols.decorrelate_trajectory.schema,
            mixture_protocols.decorrelate_observables.schema,
            add_component_observables.schema,
            calculate_excess_observable.schema,
        ]

        if component_n_molar_molecules is not None:
            schema.protocol_schemas.append(component_n_molar_molecules.schema)
        if mixture_n_molar_molecules is not None:
            schema.protocol_schemas.append(mixture_n_molar_molecules.schema)

        schema.protocol_replicators = [component_replicator]

        schema.final_value_source = ProtocolPath(
            "result", calculate_excess_observable.id)

        schema.outputs_to_store = {
            "full_system":
            mixture_stored_data,
            f"component_{component_replicator.placeholder_id}":
            component_stored_data,
        }

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #3
0
    def _default_reweighting_schema(
        cls,
        observable_type: ObservableType,
        absolute_tolerance: unit.Quantity = UNDEFINED,
        relative_tolerance: float = UNDEFINED,
        n_effective_samples: int = 50,
    ) -> ReweightingSchema:
        """Returns the default calculation schema to use when estimating this class of
        property by re-weighting cached simulation data.

        This internal implementation allows re-weighting a different observable than
        may be specified by the `_observable_type` class property.

        Parameters
        ----------
        absolute_tolerance
            The absolute tolerance to estimate the property to within.
        relative_tolerance
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_effective_samples
            The minimum number of effective samples to require when
            reweighting the cached simulation data.

        Returns
        -------
            The default re-weighting calculation schema.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = ReweightingSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        # Set up the storage queries
        calculation_schema.storage_queries = cls._default_reweighting_storage_query(
        )

        # Define the protocols which will re-weight the observable computed for the
        # fully mixed system.
        mixture_protocols, mixture_data_replicator = generate_reweighting_protocols(
            observable_type,
            "mixture_data_replicator",
            "_mixture",
        )
        mixture_protocols.reweight_observable.required_effective_samples = (
            n_effective_samples)

        divide_by_mixture_molecules = miscellaneous.DivideValue(
            "divide_by_mixture_molecules")
        divide_by_mixture_molecules.value = ProtocolPath(
            "value", mixture_protocols.reweight_observable.id)
        (
            divide_by_mixture_molecules.divisor,
            mixture_n_molar_molecules,
        ) = cls._n_molecules_divisor(
            ProtocolPath(
                "total_number_of_molecules",
                mixture_protocols.unpack_stored_data.id.replace(
                    mixture_data_replicator.placeholder_id, "0"),
            ),
            "_mixture",
        )

        # Define the protocols to use for each component, creating a replicator that
        # will copy these for each component in the full substance.
        component_replicator = ProtocolReplicator("component_replicator")
        component_replicator.template_values = ProtocolPath(
            "components", "global")

        component_protocols, component_data_replicator = generate_reweighting_protocols(
            observable_type,
            f"component_{component_replicator.placeholder_id}_data_replicator",
            f"_component_{component_replicator.placeholder_id}",
        )
        component_protocols.reweight_observable.required_effective_samples = (
            n_effective_samples)
        component_data_replicator.template_values = ProtocolPath(
            f"component_data[$({component_replicator.id})]", "global")

        divide_by_component_molecules = miscellaneous.DivideValue(
            f"divide_by_component_{component_replicator.placeholder_id}_molecules"
        )
        divide_by_component_molecules.value = ProtocolPath(
            "value", component_protocols.reweight_observable.id)
        (
            divide_by_component_molecules.divisor,
            component_n_molar_molecules,
        ) = cls._n_molecules_divisor(
            ProtocolPath(
                "total_number_of_molecules",
                component_protocols.unpack_stored_data.id.replace(
                    component_data_replicator.placeholder_id, "0"),
            ),
            f"_component_{component_replicator.placeholder_id}",
        )

        # Make sure the protocols point to the correct substance.
        component_substance = ReplicatorValue(component_replicator.id)

        component_protocols.build_reference_system.substance = component_substance
        component_protocols.build_target_system.substance = component_substance

        # Weight the component value by the mole fraction.
        weight_by_mole_fraction = miscellaneous.WeightByMoleFraction(
            f"weight_by_mole_fraction_{component_replicator.placeholder_id}")
        weight_by_mole_fraction.value = ProtocolPath(
            "result", divide_by_component_molecules.id)
        weight_by_mole_fraction.full_substance = ProtocolPath(
            "substance", "global")
        weight_by_mole_fraction.component = component_substance

        # Finally, set up the protocols which will be responsible for adding together
        # the component observables, and subtracting these from the full system value.
        add_component_observables = miscellaneous.AddValues(
            "add_component_observables")
        add_component_observables.values = ProtocolPath(
            "weighted_value",
            weight_by_mole_fraction.id,
        )

        calculate_excess_observable = miscellaneous.SubtractValues(
            "calculate_excess_observable")
        calculate_excess_observable.value_b = ProtocolPath(
            "result", divide_by_mixture_molecules.id)
        calculate_excess_observable.value_a = ProtocolPath(
            "result", add_component_observables.id)

        # Build the final workflow schema
        schema = WorkflowSchema()

        schema.protocol_schemas = [
            *[x.schema for x in mixture_protocols if x is not None],
            divide_by_mixture_molecules.schema,
            *[x.schema for x in component_protocols if x is not None],
            divide_by_component_molecules.schema,
            weight_by_mole_fraction.schema,
            add_component_observables.schema,
            calculate_excess_observable.schema,
        ]

        if component_n_molar_molecules is not None:
            schema.protocol_schemas.append(component_n_molar_molecules.schema)
        if mixture_n_molar_molecules is not None:
            schema.protocol_schemas.append(mixture_n_molar_molecules.schema)

        schema.protocol_replicators = [
            mixture_data_replicator,
            component_replicator,
            component_data_replicator,
        ]

        schema.final_value_source = ProtocolPath(
            "result", calculate_excess_observable.id)

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #4
0
    def default_reweighting_schema(
        cls,
        absolute_tolerance=UNDEFINED,
        relative_tolerance=UNDEFINED,
        n_effective_samples=50,
    ):
        """Returns the default calculation schema to use when estimating
        this property by reweighting existing data.

        Parameters
        ----------
        absolute_tolerance: openff.evaluator.unit.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_effective_samples: int
            The minimum number of effective samples to require when
            reweighting the cached simulation data.

        Returns
        -------
        ReweightingSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = ReweightingSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        # Set up the storage queries
        calculation_schema.storage_queries = cls._default_reweighting_storage_query()

        # Set up a protocol to extract the liquid phase energy from the existing data.
        liquid_protocols, liquid_replicator = generate_reweighting_protocols(
            ObservableType.PotentialEnergy,
            id_suffix="_liquid",
            replicator_id="liquid_data_replicator",
        )
        liquid_replicator.template_values = ProtocolPath("liquid_data", "global")
        liquid_protocols.reweight_observable.required_effective_samples = (
            n_effective_samples
        )

        # Dive the potential by the number of liquid phase molecules from the first
        # piece of cached data.
        divide_by_liquid_molecules = miscellaneous.DivideValue(
            "divide_by_liquid_molecules"
        )
        divide_by_liquid_molecules.value = ProtocolPath(
            "value", liquid_protocols.reweight_observable.id
        )
        divide_by_liquid_molecules.divisor = ProtocolPath(
            "total_number_of_molecules",
            liquid_protocols.unpack_stored_data.id.replace(
                liquid_replicator.placeholder_id, "0"
            ),
        )

        # Set up a protocol to extract the gas phase energy from the existing data.
        gas_protocols, gas_replicator = generate_reweighting_protocols(
            ObservableType.PotentialEnergy,
            id_suffix="_gas",
            replicator_id="gas_data_replicator",
        )
        gas_replicator.template_values = ProtocolPath("gas_data", "global")
        gas_protocols.reweight_observable.required_effective_samples = (
            n_effective_samples
        )

        # Turn of PBC for the gas phase.
        gas_protocols.evaluate_reference_potential.enable_pbc = False
        gas_protocols.evaluate_target_potential.enable_pbc = False

        # Combine the values to estimate the final enthalpy of vaporization
        energy_of_vaporization = miscellaneous.SubtractValues("energy_of_vaporization")
        energy_of_vaporization.value_b = ProtocolPath(
            "value", gas_protocols.reweight_observable.id
        )
        energy_of_vaporization.value_a = ProtocolPath(
            "result", divide_by_liquid_molecules.id
        )

        ideal_volume = miscellaneous.MultiplyValue("ideal_volume")
        ideal_volume.value = 1.0 * unit.molar_gas_constant
        ideal_volume.multiplier = ProtocolPath(
            "thermodynamic_state.temperature", "global"
        )

        enthalpy_of_vaporization = miscellaneous.AddValues("enthalpy_of_vaporization")
        enthalpy_of_vaporization.values = [
            ProtocolPath("result", energy_of_vaporization.id),
            ProtocolPath("result", ideal_volume.id),
        ]

        # Build the workflow schema.
        schema = WorkflowSchema()
        schema.protocol_schemas = [
            *(x.schema for x in liquid_protocols if x is not None),
            *(x.schema for x in gas_protocols if x is not None),
            divide_by_liquid_molecules.schema,
            energy_of_vaporization.schema,
            ideal_volume.schema,
            enthalpy_of_vaporization.schema,
        ]
        schema.protocol_replicators = [liquid_replicator, gas_replicator]
        schema.final_value_source = ProtocolPath("result", enthalpy_of_vaporization.id)

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #5
0
    def default_reweighting_schema(
        absolute_tolerance=UNDEFINED,
        relative_tolerance=UNDEFINED,
        n_effective_samples=50,
    ):
        """Returns the default calculation schema to use when estimating
        this property by reweighting existing data.

        Parameters
        ----------
        absolute_tolerance: pint.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_effective_samples: int
            The minimum number of effective samples to require when
            reweighting the cached simulation data.

        Returns
        -------
        ReweightingSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = ReweightingSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        # Set up the storage queries
        calculation_schema.storage_queries = (
            ExcessMolarVolume._default_reweighting_storage_query()
        )

        # Set up a replicator that will re-run the component reweighting workflow for each
        # component in the system.
        component_replicator = ProtocolReplicator(replicator_id="component_replicator")
        component_replicator.template_values = ProtocolPath("components", "global")

        gradient_replicator = ProtocolReplicator("gradient")
        gradient_replicator.template_values = ProtocolPath(
            "parameter_gradient_keys", "global"
        )

        # Set up the protocols which will reweight data for the full system.
        full_data_replicator_id = "full_data_replicator"

        (
            full_protocols,
            full_volume,
            full_data_replicator,
            full_gradient_group,
            full_gradient_source,
        ) = ExcessMolarVolume._get_reweighting_protocols(
            "_full",
            gradient_replicator.id,
            full_data_replicator_id,
            n_effective_samples=n_effective_samples,
        )

        # Set up the protocols which will reweight data for each component.
        component_data_replicator_id = (
            f"component_{component_replicator.placeholder_id}_data_replicator"
        )

        (
            component_protocols,
            component_volumes,
            component_data_replicator,
            component_gradient_group,
            component_gradient_source,
        ) = ExcessMolarVolume._get_reweighting_protocols(
            "_component",
            gradient_replicator.id,
            component_data_replicator_id,
            replicator_id=component_replicator.id,
            weight_by_mole_fraction=True,
            substance_reference=ReplicatorValue(component_replicator.id),
            n_effective_samples=n_effective_samples,
        )

        # Make sure the replicator is only replicating over component data.
        component_data_replicator.template_values = ProtocolPath(
            f"component_data[$({component_replicator.id})]", "global"
        )

        add_component_molar_volumes = miscellaneous.AddValues(
            "add_component_molar_volumes"
        )
        add_component_molar_volumes.values = component_volumes

        calculate_excess_volume = miscellaneous.SubtractValues(
            "calculate_excess_potential"
        )
        calculate_excess_volume.value_b = full_volume
        calculate_excess_volume.value_a = ProtocolPath(
            "result", add_component_molar_volumes.id
        )

        # Combine the gradients.
        add_component_gradients = miscellaneous.AddValues(
            f"add_component_gradients" f"_{gradient_replicator.placeholder_id}"
        )
        add_component_gradients.values = component_gradient_source

        combine_gradients = miscellaneous.SubtractValues(
            f"combine_gradients_{gradient_replicator.placeholder_id}"
        )
        combine_gradients.value_b = full_gradient_source
        combine_gradients.value_a = ProtocolPath("result", add_component_gradients.id)

        # Build the final workflow schema.
        schema = WorkflowSchema()

        schema.protocol_schemas = [
            *(x.schema for x in full_protocols),
            *(x.schema for x in component_protocols),
            add_component_molar_volumes.schema,
            calculate_excess_volume.schema,
            full_gradient_group.schema,
            component_gradient_group.schema,
            add_component_gradients.schema,
            combine_gradients.schema,
        ]

        schema.protocol_replicators = [
            full_data_replicator,
            component_replicator,
            component_data_replicator,
            gradient_replicator,
        ]

        schema.gradients_sources = [ProtocolPath("result", combine_gradients.id)]
        schema.final_value_source = ProtocolPath("result", calculate_excess_volume.id)

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #6
0
    def default_simulation_schema(
        absolute_tolerance=UNDEFINED, relative_tolerance=UNDEFINED, n_molecules=1000
    ):
        """Returns the default calculation schema to use when estimating
        this class of property from direct simulations.

        Parameters
        ----------
        absolute_tolerance: pint.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_molecules: int
            The number of molecules to use in the simulation.

        Returns
        -------
        SimulationSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = SimulationSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        use_target_uncertainty = (
            absolute_tolerance != UNDEFINED or relative_tolerance != UNDEFINED
        )

        # Define the id of the replicator which will clone the gradient protocols
        # for each gradient key to be estimated.
        gradient_replicator_id = "gradient_replicator"

        # Set up a workflow to calculate the molar volume of the full, mixed system.
        (
            full_system_protocols,
            full_system_molar_molecules,
            full_system_volume,
            full_output,
            full_system_gradient_group,
            full_system_gradient_replicator,
            full_system_gradient,
        ) = ExcessMolarVolume._get_simulation_protocols(
            "_full",
            gradient_replicator_id,
            use_target_uncertainty=use_target_uncertainty,
            n_molecules=n_molecules,
        )

        # Set up a general workflow for calculating the molar volume of one of the system components.
        component_replicator_id = "component_replicator"
        component_substance = ReplicatorValue(component_replicator_id)

        # Make sure to weight by the mole fractions of the actual full system as these may be slightly
        # different to the mole fractions of the measure property due to rounding.
        full_substance = ProtocolPath(
            "output_substance", full_system_protocols.build_coordinates.id
        )

        (
            component_protocols,
            component_molar_molecules,
            component_volumes,
            component_output,
            component_gradient_group,
            component_gradient_replicator,
            component_gradient,
        ) = ExcessMolarVolume._get_simulation_protocols(
            "_component",
            gradient_replicator_id,
            replicator_id=component_replicator_id,
            weight_by_mole_fraction=True,
            component_substance_reference=component_substance,
            full_substance_reference=full_substance,
            use_target_uncertainty=use_target_uncertainty,
            n_molecules=n_molecules,
        )

        # Finally, set up the protocols which will be responsible for adding together
        # the component molar volumes, and subtracting these from the mixed system molar volume.
        add_component_molar_volumes = miscellaneous.AddValues(
            "add_component_molar_volumes"
        )
        add_component_molar_volumes.values = component_volumes

        calculate_excess_volume = miscellaneous.SubtractValues(
            "calculate_excess_volume"
        )
        calculate_excess_volume.value_b = full_system_volume
        calculate_excess_volume.value_a = ProtocolPath(
            "result", add_component_molar_volumes.id
        )

        # Create the replicator object which defines how the pure component
        # molar volume estimation protocols will be replicated for each component.
        component_replicator = ProtocolReplicator(replicator_id=component_replicator_id)
        component_replicator.template_values = ProtocolPath("components", "global")

        # Combine the gradients.
        add_component_gradients = miscellaneous.AddValues(
            f"add_component_gradients" f"_$({gradient_replicator_id})"
        )
        add_component_gradients.values = component_gradient

        combine_gradients = miscellaneous.SubtractValues(
            f"combine_gradients_$({gradient_replicator_id})"
        )
        combine_gradients.value_b = full_system_gradient
        combine_gradients.value_a = ProtocolPath("result", add_component_gradients.id)

        # Combine the gradient replicators.
        gradient_replicator = ProtocolReplicator(replicator_id=gradient_replicator_id)
        gradient_replicator.template_values = ProtocolPath(
            "parameter_gradient_keys", "global"
        )

        # Build the final workflow schema
        schema = WorkflowSchema()

        schema.protocol_schemas = [
            component_protocols.build_coordinates.schema,
            component_protocols.assign_parameters.schema,
            component_protocols.energy_minimisation.schema,
            component_protocols.equilibration_simulation.schema,
            component_protocols.converge_uncertainty.schema,
            component_molar_molecules.schema,
            full_system_protocols.build_coordinates.schema,
            full_system_protocols.assign_parameters.schema,
            full_system_protocols.energy_minimisation.schema,
            full_system_protocols.equilibration_simulation.schema,
            full_system_protocols.converge_uncertainty.schema,
            full_system_molar_molecules.schema,
            component_protocols.extract_uncorrelated_trajectory.schema,
            component_protocols.extract_uncorrelated_statistics.schema,
            full_system_protocols.extract_uncorrelated_trajectory.schema,
            full_system_protocols.extract_uncorrelated_statistics.schema,
            add_component_molar_volumes.schema,
            calculate_excess_volume.schema,
            component_gradient_group.schema,
            full_system_gradient_group.schema,
            add_component_gradients.schema,
            combine_gradients.schema,
        ]

        schema.protocol_replicators = [gradient_replicator, component_replicator]

        # Finally, tell the schemas where to look for its final values.
        schema.gradients_sources = [ProtocolPath("result", combine_gradients.id)]
        schema.final_value_source = ProtocolPath("result", calculate_excess_volume.id)

        schema.outputs_to_store = {
            "full_system": full_output,
            f"component_$({component_replicator_id})": component_output,
        }

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #7
0
    def default_simulation_schema(
        absolute_tolerance=UNDEFINED, relative_tolerance=UNDEFINED, n_molecules=1000
    ):
        """Returns the default calculation schema to use when estimating
        this class of property from direct simulations.

        Parameters
        ----------
        absolute_tolerance: pint.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_molecules: int
            The number of molecules to use in the simulation.

        Returns
        -------
        SimulationSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = SimulationSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        use_target_uncertainty = (
            absolute_tolerance != UNDEFINED or relative_tolerance != UNDEFINED
        )

        # Define the protocol which will extract the average density from
        # the results of a simulation.
        extract_density = analysis.ExtractAverageStatistic("extract_density")
        extract_density.statistics_type = ObservableType.Density

        # Define the protocols which will run the simulation itself.
        protocols, value_source, output_to_store = generate_base_simulation_protocols(
            extract_density,
            use_target_uncertainty,
            n_molecules=n_molecules,
        )

        # Set up the gradient calculations
        coordinate_source = ProtocolPath(
            "output_coordinate_file", protocols.equilibration_simulation.id
        )
        trajectory_source = ProtocolPath(
            "trajectory_file_path",
            protocols.converge_uncertainty.id,
            protocols.production_simulation.id,
        )
        statistics_source = ProtocolPath(
            "statistics_file_path",
            protocols.converge_uncertainty.id,
            protocols.production_simulation.id,
        )

        reweight_density_template = reweighting.ReweightStatistics("")
        reweight_density_template.statistics_type = ObservableType.Density
        reweight_density_template.statistics_paths = statistics_source
        reweight_density_template.reference_reduced_potentials = statistics_source

        (
            gradient_group,
            gradient_replicator,
            gradient_source,
        ) = generate_gradient_protocol_group(
            reweight_density_template,
            ProtocolPath("force_field_path", "global"),
            coordinate_source,
            trajectory_source,
            statistics_source,
        )

        # Build the workflow schema.
        schema = WorkflowSchema()

        schema.protocol_schemas = [
            protocols.build_coordinates.schema,
            protocols.assign_parameters.schema,
            protocols.energy_minimisation.schema,
            protocols.equilibration_simulation.schema,
            protocols.converge_uncertainty.schema,
            protocols.extract_uncorrelated_trajectory.schema,
            protocols.extract_uncorrelated_statistics.schema,
            gradient_group.schema,
        ]

        schema.protocol_replicators = [gradient_replicator]

        schema.outputs_to_store = {"full_system": output_to_store}

        schema.gradients_sources = [gradient_source]
        schema.final_value_source = value_source

        calculation_schema.workflow_schema = schema
        return calculation_schema
예제 #8
0
    def default_reweighting_schema(
        absolute_tolerance=UNDEFINED,
        relative_tolerance=UNDEFINED,
        n_effective_samples=50,
    ):
        """Returns the default calculation schema to use when estimating
        this property by reweighting existing data.

        Parameters
        ----------
        absolute_tolerance: pint.Quantity, optional
            The absolute tolerance to estimate the property to within.
        relative_tolerance: float
            The tolerance (as a fraction of the properties reported
            uncertainty) to estimate the property to within.
        n_effective_samples: int
            The minimum number of effective samples to require when
            reweighting the cached simulation data.

        Returns
        -------
        ReweightingSchema
            The schema to follow when estimating this property.
        """
        assert absolute_tolerance == UNDEFINED or relative_tolerance == UNDEFINED

        calculation_schema = ReweightingSchema()
        calculation_schema.absolute_tolerance = absolute_tolerance
        calculation_schema.relative_tolerance = relative_tolerance

        data_replicator_id = "data_replicator"

        # The protocol which will be used to calculate the densities from
        # the existing data.
        density_calculation = analysis.ExtractAverageStatistic(
            f"calc_density_$({data_replicator_id})"
        )
        density_calculation.statistics_type = ObservableType.Density

        reweight_density = reweighting.ReweightStatistics("reweight_density")
        reweight_density.statistics_type = ObservableType.Density
        reweight_density.required_effective_samples = n_effective_samples

        protocols, data_replicator = generate_base_reweighting_protocols(
            density_calculation, reweight_density, data_replicator_id
        )

        # Set up the gradient calculations
        coordinate_path = ProtocolPath(
            "output_coordinate_path", protocols.concatenate_trajectories.id
        )
        trajectory_path = ProtocolPath(
            "output_trajectory_path", protocols.concatenate_trajectories.id
        )
        statistics_path = ProtocolPath(
            "statistics_file_path", protocols.reduced_target_potential.id
        )

        reweight_density_template = copy.deepcopy(reweight_density)

        (
            gradient_group,
            gradient_replicator,
            gradient_source,
        ) = generate_gradient_protocol_group(
            reweight_density_template,
            ProtocolPath("force_field_path", "global"),
            coordinate_path,
            trajectory_path,
            statistics_path,
            replicator_id="grad",
            effective_sample_indices=ProtocolPath(
                "effective_sample_indices", protocols.mbar_protocol.id
            ),
        )

        schema = WorkflowSchema()
        schema.protocol_schemas = [
            *(x.schema for x in protocols),
            gradient_group.schema,
        ]
        schema.protocol_replicators = [data_replicator, gradient_replicator]
        schema.gradients_sources = [gradient_source]
        schema.final_value_source = ProtocolPath("value", protocols.mbar_protocol.id)

        calculation_schema.workflow_schema = schema
        return calculation_schema