Exemple #1
0
class SubtractValues(Protocol):
    """A protocol to subtract one value from another such that:

    `result = value_b - value_a`
    """

    value_a = InputAttribute(
        docstring="`value_a` in the formula `result` = `value_b` - `value_a`.",
        type_hint=typing.Union[
            int, float, pint.Quantity, pint.Measurement, ParameterGradient
        ],
        default_value=UNDEFINED,
    )
    value_b = InputAttribute(
        docstring="`value_b` in the formula `result` = `value_b` - `value_a`.",
        type_hint=typing.Union[
            int, float, pint.Quantity, pint.Measurement, ParameterGradient
        ],
        default_value=UNDEFINED,
    )

    result = OutputAttribute(
        docstring="The results of `value_b` - `value_a`.",
        type_hint=typing.Union[
            int, float, pint.Measurement, pint.Quantity, ParameterGradient
        ],
    )

    def _execute(self, directory, available_resources):
        self.result = self.value_b - self.value_a
Exemple #2
0
class DivideValue(Protocol):
    """A protocol which divides a value by a specified scalar"""

    value = InputAttribute(
        docstring="The value to divide.",
        type_hint=typing.Union[
            int, float, pint.Quantity, pint.Measurement, ParameterGradient
        ],
        default_value=UNDEFINED,
    )
    divisor = InputAttribute(
        docstring="The scalar to divide by.",
        type_hint=typing.Union[int, float, pint.Quantity],
        default_value=UNDEFINED,
    )

    result = OutputAttribute(
        docstring="The result of the division.",
        type_hint=typing.Union[
            int, float, pint.Measurement, pint.Quantity, ParameterGradient
        ],
    )

    def _execute(self, directory, available_resources):
        self.result = self.value / self.divisor
Exemple #3
0
class MultiplyValue(Protocol):
    """A protocol which multiplies a value by a specified scalar"""

    value = InputAttribute(
        docstring="The value to multiply.",
        type_hint=typing.Union[
            int, float, pint.Quantity, pint.Measurement, ParameterGradient
        ],
        default_value=UNDEFINED,
    )
    multiplier = InputAttribute(
        docstring="The scalar to multiply by.",
        type_hint=typing.Union[int, float, pint.Quantity],
        default_value=UNDEFINED,
    )

    result = OutputAttribute(
        docstring="The result of the multiplication.",
        type_hint=typing.Union[
            int, float, pint.Measurement, pint.Quantity, ParameterGradient
        ],
    )

    def _execute(self, directory, available_resources):
        self.result = self.value * self.multiplier
Exemple #4
0
class AverageDielectricConstant(BaseAverageObservable):
    """Computes the average value of the dielectric constant from a set of dipole
    moments (M) and volumes (V) sampled over the course of a molecular simulation such
    that ``eps = 1 + (<M^2> - <M>^2) / (3.0 * eps_0 * <V> * kb * T)`` [1]_.

    References
    ----------
    [1] A. Glattli, X. Daura and W. F. van Gunsteren. Derivation of an improved simple
        point charge model for liquid water: SPC/A and SPC/L. J. Chem. Phys. 116(22):
        9811-9828, 2002
    """

    dipole_moments = InputAttribute(
        docstring="The dipole moments of each sampled configuration.",
        type_hint=ObservableArray,
        default_value=UNDEFINED,
    )
    volumes = InputAttribute(
        docstring="The volume of each sampled configuration.",
        type_hint=ObservableArray,
        default_value=UNDEFINED,
    )

    def _observables(self):
        return {"dipole_moments": self.dipole_moments, "volumes": self.volumes}

    def _bootstrap_function(self, **kwargs: ObservableArray):

        return compute_dielectric_constant(
            kwargs.pop("dipole_moments"),
            kwargs.pop("volumes"),
            self.thermodynamic_state.temperature,
            super(AverageDielectricConstant, self)._bootstrap_function,
        )
Exemple #5
0
class ComputeSymmetryCorrection(Protocol):
    """Computes the symmetry correction for an APR calculation which involves
    a guest with symmetry.
    """

    n_microstates = InputAttribute(
        docstring="The number of symmetry microstates of the guest molecule.",
        type_hint=int,
        default_value=UNDEFINED,
    )
    thermodynamic_state = InputAttribute(
        docstring=
        "The thermodynamic state that the calculation was performed at.",
        type_hint=ThermodynamicState,
        default_value=UNDEFINED,
    )

    result = OutputAttribute(docstring="The symmetry correction.",
                             type_hint=Observable)

    def _execute(self, directory, available_resources):

        from paprika.evaluator import Analyze

        self.result = Observable(
            unit.Measurement(
                Analyze.symmetry_correction(
                    self.n_microstates,
                    self.thermodynamic_state.temperature.to(
                        unit.kelvin).magnitude,
                ) * unit.kilocalorie / unit.mole,
                0 * unit.kilocalorie / unit.mole,
            ))
Exemple #6
0
class ConcatenateTrajectories(Protocol):
    """A protocol which concatenates multiple trajectories into
    a single one.
    """

    input_coordinate_paths = InputAttribute(
        docstring=
        "A list of paths to the starting PDB coordinates for each of the "
        "trajectories.",
        type_hint=list,
        default_value=UNDEFINED,
    )
    input_trajectory_paths = InputAttribute(
        docstring="A list of paths to the trajectories to concatenate.",
        type_hint=list,
        default_value=UNDEFINED,
    )

    output_coordinate_path = OutputAttribute(
        docstring="The path the PDB coordinate file which contains the topology "
        "of the concatenated trajectory.",
        type_hint=str,
    )

    output_trajectory_path = OutputAttribute(
        docstring="The path to the concatenated trajectory.", type_hint=str)

    def _execute(self, directory, available_resources):

        import mdtraj

        if len(self.input_coordinate_paths) != len(
                self.input_trajectory_paths):

            raise ValueError(
                "There should be the same number of coordinate and trajectory paths."
            )

        if len(self.input_trajectory_paths) == 0:
            raise ValueError("No trajectories were given to concatenate.")

        trajectories = []

        output_coordinate_path = None

        for coordinate_path, trajectory_path in zip(
                self.input_coordinate_paths, self.input_trajectory_paths):

            output_coordinate_path = output_coordinate_path or coordinate_path
            trajectories.append(
                mdtraj.load_dcd(trajectory_path, coordinate_path))

        self.output_coordinate_path = output_coordinate_path
        output_trajectory = (trajectories[0] if len(trajectories) == 1 else
                             mdtraj.join(trajectories, False, False))

        self.output_trajectory_path = path.join(directory,
                                                "output_trajectory.dcd")
        output_trajectory.save_dcd(self.output_trajectory_path)
class PreparePullCoordinates(_PrepareAPRCoordinates):
    """A protocol which will align a host-guest complex to the z-axis and position
    the guest molecule at a specified point along the pull axis.
    """

    guest_orientation_mask = InputAttribute(
        docstring="The string mask which describes which guest atoms will be "
        "restrained relative to the dummy atoms to keep the molecule aligned to the "
        "z-axis. This should be of the form 'X Y' where X Y are ParmEd selectors for "
        "the first and second guest atoms.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    pull_distance = InputAttribute(
        docstring="The total distance that the guest will be pulled along the z-axis "
        "during the pull phase.",
        type_hint=unit.Quantity,
        default_value=UNDEFINED,
    )
    pull_window_index = InputAttribute(
        docstring="The index of the pull window to generate coordinates for.",
        type_hint=int,
        default_value=UNDEFINED,
    )
    n_pull_windows = InputAttribute(
        docstring="The total number of the pull windows in the calculation.",
        type_hint=int,
        default_value=UNDEFINED,
    )

    def _execute(self, directory, available_resources):

        from paprika.evaluator import Setup
        from simtk.openmm import app

        atom_indices_by_role = _atom_indices_by_role(
            self.substance, self.complex_file_path
        )

        guest_atom_indices = atom_indices_by_role[Component.Role.Ligand]

        host_structure = Setup.prepare_complex_structure(
            self.complex_file_path,
            guest_atom_indices,
            self.guest_orientation_mask,
            self.pull_distance.to(unit.angstrom).magnitude,
            self.pull_window_index,
            self.n_pull_windows,
        )

        self.output_coordinate_path = os.path.join(directory, "output.pdb")

        with open(self.output_coordinate_path, "w") as file:
            app.PDBFile.writeFile(
                host_structure.topology, host_structure.positions, file, True
            )
class AttributeObject(AttributeClass):

    required_input = InputAttribute("", str, UNDEFINED, optional=False)
    optional_input = InputAttribute("", int, UNDEFINED, optional=True)

    some_output = OutputAttribute("", int)

    def __init__(self):
        self.some_output = 5
Exemple #9
0
class AveragePropertyProtocol(Protocol, abc.ABC):
    """An abstract base class for protocols which will calculate the
    average of a property and its uncertainty via bootstrapping.
    """

    bootstrap_iterations = InputAttribute(
        docstring="The number of bootstrap iterations to perform.",
        type_hint=int,
        default_value=250,
        merge_behavior=InequalityMergeBehaviour.LargestValue,
    )
    bootstrap_sample_size = InputAttribute(
        docstring="The relative sample size to use for bootstrapping.",
        type_hint=float,
        default_value=1.0,
        merge_behavior=InequalityMergeBehaviour.LargestValue,
    )

    equilibration_index = OutputAttribute(
        docstring=
        "The index in the data set after which the data is stationary.",
        type_hint=int,
    )
    statistical_inefficiency = OutputAttribute(
        docstring="The statistical inefficiency in the data set.",
        type_hint=float)

    value = OutputAttribute(docstring="The average value and its uncertainty.",
                            type_hint=pint.Measurement)
    uncorrelated_values = OutputAttribute(
        docstring=
        "The uncorrelated values which the average was calculated from.",
        type_hint=pint.Quantity,
    )

    def _bootstrap_function(self, **sample_kwargs):
        """The function to perform on the data set being sampled by
        bootstrapping.

        Parameters
        ----------
        sample_kwargs: dict of str and np.ndarray
            A key words dictionary of the bootstrap sample data, where the
            sample data is a numpy array of shape=(num_frames, num_dimensions)
            with dtype=float.

        Returns
        -------
        float
            The result of evaluating the data.
        """

        assert len(sample_kwargs) == 1
        sample_data = next(iter(sample_kwargs.values()))

        return sample_data.mean()
Exemple #10
0
class DummyReplicableProtocol(Protocol):

    replicated_value_a = InputAttribute(docstring="",
                                        type_hint=Union[str, int, float],
                                        default_value=UNDEFINED)
    replicated_value_b = InputAttribute(docstring="",
                                        type_hint=Union[str, int, float],
                                        default_value=UNDEFINED)
    final_value = OutputAttribute(docstring="", type_hint=unit.Measurement)

    def _execute(self, directory, available_resources):
        pass
Exemple #11
0
class AverageTrajectoryProperty(AveragePropertyProtocol, abc.ABC):
    """An abstract base class for protocols which will calculate the
    average of a property from a simulation trajectory.
    """

    input_coordinate_file = InputAttribute(
        docstring="The file path to the starting coordinates of a trajectory.",
        type_hint=str,
        default_value=UNDEFINED,
    )
    trajectory_path = InputAttribute(
        docstring="The file path to the trajectory to average over.",
        type_hint=str,
        default_value=UNDEFINED,
    )
class ConcatenateStatistics(Protocol):
    """A protocol which concatenates multiple trajectories into
    a single one.
    """

    input_statistics_paths = InputAttribute(
        docstring="A list of paths to statistics arrays to concatenate.",
        type_hint=list,
        default_value=UNDEFINED,
    )
    output_statistics_path = OutputAttribute(
        docstring="The path the csv file which contains the concatenated statistics.",
        type_hint=str,
    )

    def _execute(self, directory, available_resources):

        if len(self.input_statistics_paths) == 0:
            raise ValueError("No statistics arrays were given to concatenate.")

        arrays = [
            StatisticsArray.from_pandas_csv(file_path)
            for file_path in self.input_statistics_paths
        ]

        if len(arrays) > 1:
            output_array = StatisticsArray.join(*arrays)
        else:
            output_array = arrays[0]

        self.output_statistics_path = path.join(directory, "output_statistics.csv")
        output_array.to_pandas_csv(self.output_statistics_path)
Exemple #13
0
class AddValues(Protocol):
    """A protocol to add together a list of values.

    Notes
    -----
    The `values` input must either be a list of pint.Quantity, a ProtocolPath to a list
    of pint.Quantity, or a list of ProtocolPath which each point to a pint.Quantity.
    """

    values = InputAttribute(
        docstring="The values to add together.", type_hint=list, default_value=UNDEFINED
    )

    result = OutputAttribute(
        docstring="The sum of the values.",
        type_hint=typing.Union[
            int, float, pint.Measurement, pint.Quantity, ParameterGradient
        ],
    )

    def _execute(self, directory, available_resources):

        if len(self.values) < 1:
            raise ValueError("There were no values to add together")

        self.result = self.values[0]

        for value in self.values[1:]:
            self.result += value
Exemple #14
0
class DecorrelateObservables(BaseDecorrelateProtocol):
    """A protocol which will subsample a trajectory of observables, yielding only
    uncorrelated entries as determined from a provided statistical inefficiency and
    equilibration time.
    """

    input_observables = InputAttribute(
        docstring="The observables to decorrelate.",
        type_hint=typing.Union[ObservableArray, ObservableFrame],
        default_value=UNDEFINED,
    )

    output_observables = OutputAttribute(
        docstring="The decorrelated observables.",
        type_hint=typing.Union[ObservableArray, ObservableFrame],
    )

    def _execute(self, directory, available_resources):

        assert len(self.input_observables) == self._n_expected()

        uncorrelated_indices = self._uncorrelated_indices()
        uncorrelated_observable = self.input_observables.subset(uncorrelated_indices)

        self.output_observables = uncorrelated_observable
Exemple #15
0
class ExtractUncorrelatedStatisticsData(ExtractUncorrelatedData):
    """A protocol which will subsample entries from a statistics array, yielding only uncorrelated
    entries as determined from a provided statistical inefficiency and equilibration time.
    """

    input_statistics_path = InputAttribute(
        docstring="The file path to the statistics to subsample.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    output_statistics_path = OutputAttribute(
        docstring="The file path to the subsampled statistics.", type_hint=str)

    def _execute(self, directory, available_resources):

        statistics_array = StatisticsArray.from_pandas_csv(
            self.input_statistics_path)

        uncorrelated_indices = timeseries.get_uncorrelated_indices(
            len(statistics_array) - self.equilibration_index,
            self.statistical_inefficiency,
        )

        uncorrelated_indices = [
            index + self.equilibration_index for index in uncorrelated_indices
        ]
        uncorrelated_statistics = StatisticsArray.from_existing(
            statistics_array, uncorrelated_indices)

        self.output_statistics_path = path.join(directory,
                                                "uncorrelated_statistics.csv")
        uncorrelated_statistics.to_pandas_csv(self.output_statistics_path)

        self.number_of_uncorrelated_samples = len(uncorrelated_statistics)
Exemple #16
0
class ConcatenateObservables(Protocol):
    """A protocol which concatenates multiple ``ObservableFrame`` objects into
    a single ``ObservableFrame`` object.
    """

    input_observables = InputAttribute(
        docstring="A list of observable arrays to concatenate.",
        type_hint=list,
        default_value=UNDEFINED,
    )
    output_observables = OutputAttribute(
        docstring="The concatenated observable array.",
        type_hint=typing.Union[ObservableArray, ObservableFrame],
    )

    def _execute(self, directory, available_resources):

        if len(self.input_observables) == 0:
            raise ValueError("No arrays were given to concatenate.")

        if not all(
                isinstance(observables, type(self.input_observables[0]))
                for observables in self.input_observables):
            raise ValueError(
                "The observables to concatenate must be the same type.")

        object_type = type(self.input_observables[0])

        if len(self.input_observables) > 1:
            self.output_observables = object_type.join(*self.input_observables)
        else:
            self.output_observables = copy.deepcopy(self.input_observables[0])
class SolvateExistingStructure(BuildCoordinatesPackmol):
    """Solvates a set of 3D coordinates with a specified solvent
    using the PACKMOL package.
    """

    solute_coordinate_file = InputAttribute(
        docstring="A file path to the solute to solvate.",
        type_hint=str,
        default_value=UNDEFINED,
    )
    center_solute_in_box = InputAttribute(
        docstring="If `True`, the solute to solvate will be centered in the "
        "simulation box.",
        type_hint=bool,
        default_value=True,
    )

    def _execute(self, directory, available_resources):

        molecules, number_of_molecules, exception = self._build_molecule_arrays(
        )

        packmol_directory = path.join(directory, "packmol_files")

        # Create packed box
        trajectory, residue_names = packmol.pack_box(
            molecules=molecules,
            number_of_copies=number_of_molecules,
            structure_to_solvate=self.solute_coordinate_file,
            center_solute=self.center_solute_in_box,
            mass_density=self.mass_density,
            box_aspect_ratio=self.box_aspect_ratio,
            tolerance=self.tolerance,
            verbose=self.verbose_packmol,
            working_directory=packmol_directory,
            retain_working_files=self.retain_packmol_files,
        )

        if trajectory is None:
            raise RuntimeError("Packmol failed to complete.")

        self.assigned_residue_names = dict()

        for component, residue_name in zip(self.substance, residue_names):
            self.assigned_residue_names[component.identifier] = residue_name

        self._save_results(directory, trajectory)
Exemple #18
0
class BaseEvaluateEnergies(Protocol, abc.ABC):
    """A base class for protocols which will re-evaluate the energy of a series
    of configurations for a given set of force field parameters.
    """

    thermodynamic_state = InputAttribute(
        docstring="The state to calculate the reduced potentials at.",
        type_hint=ThermodynamicState,
        default_value=UNDEFINED,
    )

    parameterized_system = InputAttribute(
        docstring=
        "The parameterized system object which encodes the systems potential "
        "energy function.",
        type_hint=ParameterizedSystem,
        default_value=UNDEFINED,
    )
    enable_pbc = InputAttribute(
        docstring="If true, periodic boundary conditions will be enabled.",
        type_hint=bool,
        default_value=True,
    )

    trajectory_file_path = InputAttribute(
        docstring="The path to the trajectory file which contains the "
        "configurations to calculate the energies of.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    gradient_parameters = InputAttribute(
        docstring=
        "An optional list of parameters to differentiate the evaluated "
        "energies with respect to.",
        type_hint=list,
        default_value=lambda: list(),
    )

    output_observables = OutputAttribute(
        docstring=
        "An observable array which stores the reduced potentials potential "
        "energies evaluated at the specified state and using the specified system "
        "object for each configuration in the trajectory.",
        type_hint=ObservableFrame,
    )
class GeneratePullRestraints(GenerateAttachRestraints):
    """Generates the restraint values to apply during the 'pull' phase from a set
    of restraint schema definitions and makes them easily accessible for the protocols
    which will apply them to the parameterized system."""

    n_pull_windows = InputAttribute(
        docstring="The number of lambda to use for the pull phase.",
        type_hint=int,
        default_value=UNDEFINED,
    )

    def _execute(self, directory, available_resources):

        from paprika.evaluator import Setup

        # Construct the restraints to keep the host in place and
        # with an open cavity.
        static_restraints = Setup.build_static_restraints(
            self.complex_coordinate_path,
            len(self.attach_lambdas),
            self.n_pull_windows,
            None,
            self.restraint_schemas.get("static", []),
        )
        conformational_restraints = Setup.build_conformational_restraints(
            self.complex_coordinate_path,
            self.attach_lambdas,
            self.n_pull_windows,
            None,
            self.restraint_schemas.get("conformational", []),
        )

        # Construct the restraints to keep the guest at the correct
        # distance to the host.
        guest_restraints = Setup.build_guest_restraints(
            self.complex_coordinate_path,
            self.attach_lambdas,
            self.n_pull_windows,
            self.restraint_schemas.get("guest", []),
        )

        # Remove the attach phases from the restraints as these restraints are
        # only being used for the pull phase.
        for restraint in (
            static_restraints + conformational_restraints + guest_restraints
        ):

            for key in restraint.phase["attach"]:
                restraint.phase["attach"][key] = None

        self._save_restraints(
            directory,
            static_restraints,
            conformational_restraints,
            None,
            None,
            guest_restraints,
        )
class GenerateReleaseRestraints(_GenerateRestraints):
    """Generates the restraint values to apply during the 'release' phase from a set
    of restraint schema definitions and makes them easily accessible for the protocols
    which will apply them to the parameterized system."""

    host_coordinate_path = InputAttribute(
        docstring="The file path to a coordinate file which contains the solvated"
        "host molecule and has the anchor dummy atoms added.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    release_lambdas = InputAttribute(
        docstring="The values of lambda to use for the release phase. These must"
        "start from 1.0 and decrease monotonically to and include 0.0.",
        type_hint=list,
        default_value=UNDEFINED,
    )

    def _execute(self, directory, available_resources):

        from paprika.evaluator import Setup

        # Construct the restraints to keep the host in place and
        # with an open cavity.
        static_restraints = Setup.build_static_restraints(
            self.host_coordinate_path,
            None,
            None,
            len(self.release_lambdas),
            self.restraint_schemas.get("static", []),
        )
        conformational_restraints = Setup.build_conformational_restraints(
            self.host_coordinate_path,
            None,
            None,
            self.release_lambdas,
            self.restraint_schemas.get("conformational", []),
        )

        self._save_restraints(
            directory,
            static_restraints,
            conformational_restraints,
        )
Exemple #21
0
class BaseDecorrelateProtocol(Protocol, abc.ABC):
    """An abstract base class for protocols which will subsample
    a set of data, yielding only equilibrated, uncorrelated data.
    """

    time_series_statistics: typing.Union[
        TimeSeriesStatistics, typing.List[TimeSeriesStatistics]
    ] = InputAttribute(
        docstring="Statistics about the data to decorrelate. This should include the "
        "statistical inefficiency and the index after which the observables have "
        "become stationary (i.e. equilibrated). If a list of such statistics are "
        "provided it will be assumed that multiple time series which have been "
        "joined together are being decorrelated and hence will each be decorrelated "
        "separately.",
        type_hint=typing.Union[list, TimeSeriesStatistics],
        default_value=UNDEFINED,
    )

    def _n_expected(self) -> int:
        """Returns the expected number of samples to decorrelate."""

        time_series_statistics = self.time_series_statistics

        if isinstance(time_series_statistics, TimeSeriesStatistics):
            time_series_statistics = [time_series_statistics]

        return sum(statistics.n_total_points for statistics in time_series_statistics)

    def _uncorrelated_indices(self) -> typing.List[int]:
        """Returns the indices of the time series being decorrelated to retain."""

        time_series_statistics = self.time_series_statistics

        if isinstance(time_series_statistics, TimeSeriesStatistics):
            time_series_statistics = [time_series_statistics]

        n_cumulative = [
            0,
            *itertools.accumulate(
                [statistics.n_total_points for statistics in time_series_statistics]
            ),
        ]

        uncorrelated_indices = [
            n_cumulative[statistics_index] + index + statistics.equilibration_index
            for statistics_index, statistics in enumerate(time_series_statistics)
            for index in timeseries.get_uncorrelated_indices(
                statistics.n_total_points - statistics.equilibration_index,
                statistics.statistical_inefficiency,
            )
        ]

        assert len(uncorrelated_indices) == sum(
            statistics.n_uncorrelated_points for statistics in time_series_statistics
        )

        return uncorrelated_indices
Exemple #22
0
class AverageObservable(BaseAverageObservable):
    """Computes the average value of an observable as well as bootstrapped
    uncertainties for the average.
    """

    observable = InputAttribute(
        docstring="The file path to the observable which should be averaged.",
        type_hint=ObservableArray,
        default_value=UNDEFINED,
    )
    divisor = InputAttribute(
        docstring="A value to divide the statistic by. This is useful if a statistic "
        "(such as enthalpy) needs to be normalised by the number of molecules.",
        type_hint=typing.Union[int, float, unit.Quantity],
        default_value=1.0,
    )

    def _observables(self) -> typing.Dict[str, ObservableArray]:
        return {"observable": self.observable / self.divisor}
class _GenerateRestraints(Protocol, abc.ABC):
    """The base class which will generate a set of restraint values from their
    respective schemas and for a specific APR phase.
    """

    restraint_schemas = InputAttribute(
        docstring="The full set of restraint schemas.",
        type_hint=dict,
        default_value=UNDEFINED,
    )

    restraints_path = OutputAttribute(
        docstring="The file path to the `paprika` generated restraints JSON file.",
        type_hint=str,
    )

    @classmethod
    def _restraints_to_dict(cls, restraints):
        """Converts a list of ``paprika`` restraint objects to
        a list of JSON compatible dictionary representations
        """
        from paprika.io import NumpyEncoder

        return [
            json.loads(json.dumps(restraint.__dict__, cls=NumpyEncoder))
            for restraint in restraints
        ]

    def _save_restraints(
        self,
        directory: str,
        static_restraints,
        conformational_restraints,
        symmetry_restraints=None,
        wall_restraints=None,
        guest_restraints=None,
    ):
        """Saves the restraints to a convenient JSON file."""

        symmetry_restraints = [] if symmetry_restraints is None else symmetry_restraints
        wall_restraints = [] if wall_restraints is None else wall_restraints
        guest_restraints = [] if guest_restraints is None else guest_restraints

        restraints_dictionary = {
            "static": self._restraints_to_dict(static_restraints),
            "conformational": self._restraints_to_dict(conformational_restraints),
            "symmetry": self._restraints_to_dict(symmetry_restraints),
            "wall": self._restraints_to_dict(wall_restraints),
            "guest": self._restraints_to_dict(guest_restraints),
        }

        self.restraints_path = os.path.join(directory, "restraints.json")

        with open(self.restraints_path, "w") as file:
            json.dump(restraints_dictionary, file)
Exemple #24
0
class ExtractUncorrelatedData(Protocol, abc.ABC):
    """An abstract base class for protocols which will subsample
    a data set, yielding only equilibrated, uncorrelated data.
    """

    equilibration_index = InputAttribute(
        docstring=
        "The index in the data set after which the data is stationary.",
        type_hint=int,
        default_value=UNDEFINED,
        merge_behavior=InequalityMergeBehaviour.LargestValue,
    )
    statistical_inefficiency = InputAttribute(
        docstring="The statistical inefficiency in the data set.",
        type_hint=float,
        default_value=UNDEFINED,
        merge_behavior=InequalityMergeBehaviour.LargestValue,
    )

    number_of_uncorrelated_samples = OutputAttribute(
        docstring="The number of uncorrelated samples.", type_hint=int)
class _PrepareAPRCoordinates(Protocol, abc.ABC):
    """The base class for protocols which will be used to prepare the coordinates
    for an APR calculation.
    """

    substance = InputAttribute(
        docstring="The substance which defines the host, guest and solvent.",
        type_hint=Substance,
        default_value=UNDEFINED,
    )
    complex_file_path = InputAttribute(
        docstring="The path to the file which the coordinates of the guest molecule"
        "bound to the host molecule.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    output_coordinate_path = OutputAttribute(
        docstring="The file path to the system which has been correctly aligned to "
        "the z-axis.",
        type_hint=str,
    )
Exemple #26
0
class BaseEnergyMinimisation(Protocol, abc.ABC):
    """A base class for protocols which will minimise the potential
    energy of a given system.
    """

    input_coordinate_file = InputAttribute(
        docstring="The coordinates to minimise.",
        type_hint=str,
        default_value=UNDEFINED)
    system_path = InputAttribute(
        docstring=
        "The path to the XML system object which defines the forces present "
        "in the system.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    tolerance = InputAttribute(
        docstring=
        "The energy tolerance to which the system should be minimized.",
        type_hint=pint.Quantity,
        default_value=10 * unit.kilojoules / unit.mole,
    )
    max_iterations = InputAttribute(
        docstring="The maximum number of iterations to perform. If this is 0, "
        "minimization is continued until the results converge without regard to "
        "how many iterations it takes.",
        type_hint=int,
        default_value=0,
    )

    enable_pbc = InputAttribute(
        docstring="If true, periodic boundary conditions will be enabled.",
        type_hint=bool,
        default_value=True,
    )

    output_coordinate_file = OutputAttribute(
        docstring="The file path to the minimised coordinates.", type_hint=str)
Exemple #27
0
class ReweightObservable(BaseMBARProtocol):
    """Reweight an array of observables to a new state using MBAR."""

    observable = InputAttribute(
        docstring=
        "The observables to reweight. The array should contain the values of "
        "the observable evaluated for of each configuration at the target state.",
        type_hint=ObservableArray,
        default_value=UNDEFINED,
    )

    def _observables(self) -> typing.Dict[str, ObservableArray]:
        return {"observable": self.observable}
Exemple #28
0
class ComputeReferenceWork(Protocol):
    """Computes the reference state work."""

    thermodynamic_state = InputAttribute(
        docstring=
        "The thermodynamic state that the calculation was performed at.",
        type_hint=ThermodynamicState,
        default_value=UNDEFINED,
    )

    restraints_path = InputAttribute(
        docstring="The file path to the JSON file which contains the restraint "
        "definitions. This will usually have been generated by a "
        "`GenerateXXXRestraints` protocol.",
        type_hint=str,
        default_value=UNDEFINED,
    )

    result = OutputAttribute(docstring="The reference state work.",
                             type_hint=Observable)

    def _execute(self, directory, available_resources):

        from paprika.evaluator import Analyze

        restraints = ApplyRestraints.load_restraints(self.restraints_path)
        guest_restraints = restraints["guest"]

        self.result = Observable(
            unit.Measurement(
                -Analyze.compute_ref_state_work(
                    self.thermodynamic_state.temperature.to(
                        unit.kelvin).magnitude,
                    guest_restraints,
                ) * unit.kilocalorie / unit.mole,
                0 * unit.kilocalorie / unit.mole,
            ))
Exemple #29
0
class DummyInputOutputProtocol(Protocol):

    input_value = InputAttribute(
        docstring="A dummy input.",
        type_hint=Union[str, int, float, pint.Quantity, pint.Measurement, list,
                        tuple, dict, set, frozenset, ],
        default_value=UNDEFINED,
    )
    output_value = OutputAttribute(
        docstring="A dummy output.",
        type_hint=Union[str, int, float, pint.Quantity, pint.Measurement, list,
                        tuple, dict, set, frozenset, ],
    )

    def _execute(self, directory, available_resources):
        self.output_value = self.input_value
Exemple #30
0
class DummyProtocol(Protocol):
    """A protocol whose only purpose is to return an input value as an output
    value."""

    input_value = InputAttribute(
        docstring="A dummy input.",
        type_hint=typing.Union[str, int, float, unit.Quantity,
                               unit.Measurement, Observable, ObservableArray,
                               ParameterGradient, ParameterGradientKey, list,
                               tuple, dict, set, frozenset, ],
        default_value=UNDEFINED,
    )
    output_value = OutputAttribute(
        docstring="A dummy output.",
        type_hint=typing.Union[str, int, float, unit.Quantity,
                               unit.Measurement, Observable, ObservableArray,
                               ParameterGradient, ParameterGradientKey, list,
                               tuple, dict, set, frozenset, ],
    )

    def _execute(self, directory, available_resources):
        self.output_value = self.input_value