"""Module that reads the md stats output timeseries."""from__future__importannotationsfromcollections.abcimportIteratorfromfunctoolsimportsingledispatchmethodimportrefromtypingimportTypeVarfromnumpyimportfloat64,genfromtxt,zerosfromnumpy.typingimportNDArrayfromjanus_core.helpers.janus_typesimportPathLiketry:# Python >=3.10fromtypesimportEllipsisTypeexceptImportError:EllipsisType=type(...)T=TypeVar("T")
[docs]classStats:""" Configure shared molecular dynamics simulation options. Parameters ---------- source : PathLike File that contains the stats of a molecular dynamics simulation. """
[docs]def__init__(self,source:PathLike)->None:""" Initialise MD stats reader. Parameters ---------- source : PathLike File that contains the stats of a molecular dynamics simulation. """self._data=zeros((0,0))self._labels=()self._units=()self._source=sourceself.read()
[docs]@singledispatchmethoddef_getind(self,lab:T)->T:""" Convert an index label from str to int if present in labels. Otherwise return the input. Parameters ---------- lab : str Label to find. Returns ------- int Index of label in self or input if not string. Raises ------ IndexError Label not found in labels. """returnlab
@_getind.registerdef_(self,lab:str)->int:# numpydoc ignore=GL08# Case-insensitive fuzzy match, only has to be `in` the labelsindex=next((indexforindex,labelinenumerate(self.labels)iflab.lower()inlabel.lower()),None,)ifindexisNone:raiseIndexError(f"{lab} not found in labels")returnindex
[docs]@singledispatchmethoddef__getitem__(self,ind)->NDArray[float64]:""" Get member of stats data by label or index. Parameters ---------- ind : Any Index or label to find. Returns ------- NDArray[float64] Columns of data by label. Raises ------ IndexError Invalid index type or label not found in labels. """raiseIndexError(f"Unknown index {ind}")
@propertydefrows(self)->int:""" Return number of rows. Returns ------- int Number of rows in `data`. """returnself.data.shape[0]@propertydefcolumns(self)->int:""" Return number of columns. Returns ------- int Number of columns in `data`. """returnself.data.shape[1]@propertydefsource(self)->PathLike:""" Return filename which is the source of data. Returns ------- PathLike Filename for the source of `data`. """returnself._source@propertydeflabels(self)->tuple[str,...]:""" Return a list of labels for the columns in `data`. Returns ------- tuple[str, ...] List of labels for the columns in `data`. """returnself._labels@propertydefunits(self)->tuple[str,...]:""" Return a list of units for the columns in `data`. Returns ------- tuple[str, ...] List of units for the columns in `data`. """returnself._units@propertydefdata(self)->NDArray[float64]:""" Return the timeseries `data`. Returns ------- NDArray[float64] Data for timeseries in `data`. """returnself._data@propertydefdata_tags(self)->Iterator[tuple[str,str]]:""" Return the labels and their units together. Returns ------- Iterator[tuple[str, str]] Zipped labels and units. """returnzip(self.labels,self.units)
[docs]defread(self)->None:"""Read MD stats and store them in `data`."""self._data=genfromtxt(self.source,skip_header=1)withopen(self.source,"r+",encoding="utf-8")asfile:head=file.readline().split("|")self._units=tuple(match[1]if(match:=re.search(r"\[(.+?)\]",x))else""forxinhead)self._labels=tuple(re.sub(r"\[.*?\]","",x).strip()forxinhead)
[docs]def__repr__(self)->str:""" Summary of timeseries contained, units, headers. Returns ------- str Summary of the `data`. """header=f"contains {self.columns} timeseries, each with {self.rows} elements"header+="\nindex label units"forindex,(label,unit)inenumerate(self.data_tags):header+=f"\n{index}{label}{unit}"returnheader