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
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
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
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, )
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, ))
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
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()
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
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)
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
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
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)
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)
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, )
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
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)
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, )
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)
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}
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, ))
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
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