Source code for MDMC.trajectory_analysis.observables.obs

"""
Module defining a class for processing observables from MD trajectories.
"""

from abc import ABC, abstractmethod
from typing import Literal

from MDMC.common.decorators import repr_decorator
from MDMC.readers.observables.obs_reader_factory import ObservableReaderFactory
from MDMC.trajectory_analysis.compact_trajectory import CompactTrajectory


[docs] @repr_decorator('origin', 'data') class Observable(ABC): """ Abstract class that defines methods common to all observable data containers. Observable data can either be from a file or calculated from MD and stored in the data property, along with the associated uncertainty. The `bool` property ``from_MD`` states the source of the information. Attributes ---------- reader : ObservableReader The file reader for reading experimental data. """ def __init__(self): self.reader = None self._errors = None self._dependent_variables = None self._independent_variables = None self._origin = None self.trajectory = None self.universe_dimensions = None @property def name(self) -> str: """ Get or set the module name that used for factory instantiation. Returns ------- str The name of the module in which the ``Observable`` is located. """ return self._name @name.setter def name(self, name: str) -> None: self._name = name @property def origin(self) -> Literal['experiment', 'MD']: """ Get or set the origin of the observable. Returns ------- {'experiment', 'MD'} The origin of the ``Observable``, either ``'experiment'`` or ``'MD'``. """ return self._origin @origin.setter def origin(self, origin: str) -> None: self._origin = origin @property def data(self) -> dict: """ Get the independent, dependent and error data. Returns ------- dict The independent, dependent and error data. """ return {'independent': self.independent_variables, 'dependent': self.dependent_variables, 'errors': self.errors} @property @abstractmethod def independent_variables(self) -> dict: """ The independent variables. Returns ------- dict The independent variables. """ raise NotImplementedError @property @abstractmethod def dependent_variables(self) -> dict: """ The dependent variables. Returns ------- dict The dependent variables. """ raise NotImplementedError @property @abstractmethod def errors(self) -> dict: """ The errors on the dependent variables. Returns ------- dict The errors on the ``dependent_variables``. """ raise NotImplementedError
[docs] @abstractmethod def minimum_frames(self, dt: float = None) -> int: """ The no. of ``CompactTrajectory`` frames needed to calculate the ``dependent_variables``. Parameters ---------- dt : float, optional The time separation of frames in ``fs``, default is `None`. Returns ------- int The minimum number of frames. """ raise NotImplementedError
[docs] @abstractmethod def maximum_frames(self) -> int: """ The max no. of ``CompactTrajectory`` frames able to calculate the ``dependent_variables``. Returns ------- int The maximum number of frames. """ raise NotImplementedError
@property def use_FFT(self) -> bool: """ Get or set whether to use FFT when calculating from MD. Returns ------- bool Whether to use FFT. """ return self._use_FFT @use_FFT.setter def use_FFT(self, use_FFT: bool) -> None: self._use_FFT = use_FFT
[docs] def read_from_file(self, reader: str, file_name: str) -> None: """ Read in experimental data from a file using a specified reader. Parameters ---------- reader : str The name of the required file reader. file_name : str The name of the file. """ self._origin = 'experiment' self.reader = ObservableReaderFactory.create_reader(reader, file_name) with self.reader: self.reader.parse() self.reader.assign(observable=self)
[docs] @abstractmethod def calculate_from_MD(self, MD_input: CompactTrajectory | list[CompactTrajectory], verbose: int = 0, **parameters: dict) -> None: """ Calculate the observable using input from an MD simulation. Parameters ---------- MD_input : CompactTrajectory | list[CompactTrajectory] Some input from an MD simulation, commonly a ``CompactTrajectory``. verbose : int Enables verbose printing of the calculation. **parameters Additional parameters required for calculation specific ``Observable`` objects. """ raise NotImplementedError
@property @abstractmethod def dependent_variables_structure(self) -> dict: """ Structure of the dependent variables with respect to the independent variables. Specifically, the order in which the dependent variables are indexed with regards to the independent variables. The purpose of this method is to ensure that all ``Observable`` s of a particular type are created with dependent_variables that are consistent regardless of how they were created (e.g. by different ``Reader`` s). Returns ------- dict The np.shape of the dependent variables. Examples -------- If ``dep_var1[indep_var1_index, indep_var2_index, ...] == data point`` for values of the independent_variables with the stated indices, then the relevant entry in the returned dict should be: ``{'dependent_variable1': [independent_variable1, independent_variable2, ...]}`` .. note:: This would also correspond to numpy.shape of the dependent variable being: .. code-block:: python np.shape(dependent_variable1)=(np.size(independent_variable1), np.size(independent_variable2), ...) """ raise NotImplementedError @property @abstractmethod def uniformity_requirements(self) -> dict[str, dict[str, bool]]: """ Get the current limitations on ``independent_variables`` of the ``Observable``. It captures if the ``independent_variables`` are required to be uniform or to start at zero The keys of the returned dictionary should be the variables that have such a restriction, with the associated values being a dictionary with booleans if the variables are 'uniform' or 'zeroed'. Variables without any requirements do not need to be included, but can be included. .. note:: If there are no uniformity requirements it is okay to return 'None'. Returns ------- dict[str, dict[str, bool]] Dictionary of independent variables with their uniformity restrictions represented as booleans. """ raise NotImplementedError