diff --git a/bapsflib/_hdf/maps/controls/map_controls.py b/bapsflib/_hdf/maps/controls/map_controls.py index cd2cd199..b1896694 100644 --- a/bapsflib/_hdf/maps/controls/map_controls.py +++ b/bapsflib/_hdf/maps/controls/map_controls.py @@ -35,17 +35,6 @@ class HDFMapControls(dict): A dictionary that contains mapping objects for all the discovered control devices in the HDF5 data group. The dictionary keys are the names of the discovered control devices. - - :Example: - - >>> from bapsflib import lapd - >>> from bapsflib._hdf.maps import HDFMapControls - >>> f = lapd.File('sample.hdf5') - >>> # 'Raw data + config' is the LaPD HDF5 group name for the - ... # group housing digitizer and control devices - ... control_map = HDFMapControls(f['Raw data + config']) - >>> control_map['6K Compumotor'] - """ _defined_mapping_classes = { @@ -62,7 +51,22 @@ class HDFMapControls(dict): def __init__(self, data_group: h5py.Group): """ - :param data_group: HDF5 group object + Parameters + ---------- + data_group : `h5py.Group` + HDF5 group object to be mapped + + Examples + -------- + + >>> from bapsflib import lapd + >>> from bapsflib._hdf.maps import HDFMapControls + >>> f = lapd.File('sample.hdf5') + >>> # 'Raw data + config' is the LaPD HDF5 group name for the + ... # group housing digitizer and control devices + ... control_map = HDFMapControls(f['Raw data + config']) + >>> control_map['6K Compumotor'] + """ # condition data_group arg if not isinstance(data_group, h5py.Group): @@ -100,10 +104,12 @@ def __build_dict(self) -> Dict[str, ControlMap]: """ Discovers the HDF5 control devices and builds the dictionary containing the control device mapping objects. This is the - dictionary used to initialize :code:`self`. + dictionary used to initialize ``self``. - :return: control device mapping dictionary - :rtype: dict + Returns + ------- + dict + control device mapping dictionary """ control_dict = {} for name in self.data_group_subgnames: diff --git a/bapsflib/_hdf/maps/controls/n5700ps.py b/bapsflib/_hdf/maps/controls/n5700ps.py index 2bea114c..34f8696d 100644 --- a/bapsflib/_hdf/maps/controls/n5700ps.py +++ b/bapsflib/_hdf/maps/controls/n5700ps.py @@ -43,7 +43,10 @@ class HDFMapControlN5700PS(HDFMapControlCLTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 control device group + Parameters + ---------- + group : `h5py.Group` + the HDF5 control device group """ # initialize HDFMapControlCLTemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/controls/nixyz.py b/bapsflib/_hdf/maps/controls/nixyz.py index f62ae58b..5fd703e9 100644 --- a/bapsflib/_hdf/maps/controls/nixyz.py +++ b/bapsflib/_hdf/maps/controls/nixyz.py @@ -48,7 +48,10 @@ class HDFMapControlNIXYZ(HDFMapControlTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 control device group + Parameters + ---------- + group : `h5py.Group` + the HDF5 control device group """ HDFMapControlTemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/controls/nixz.py b/bapsflib/_hdf/maps/controls/nixz.py index 4f257721..a3868e1d 100644 --- a/bapsflib/_hdf/maps/controls/nixz.py +++ b/bapsflib/_hdf/maps/controls/nixz.py @@ -47,7 +47,10 @@ class HDFMapControlNIXZ(HDFMapControlTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 control device group + Parameters + ---------- + group : `h5py.Group` + the HDF5 control device group """ HDFMapControlTemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/controls/parsers.py b/bapsflib/_hdf/maps/controls/parsers.py index b887b769..229bfdee 100644 --- a/bapsflib/_hdf/maps/controls/parsers.py +++ b/bapsflib/_hdf/maps/controls/parsers.py @@ -34,8 +34,10 @@ class CLParse(object): def __init__(self, command_list: Union[str, Iterable[str]]): """ - :param command_list: the command list for a control device - :type command_list: list of strings + Parameters + ---------- + command_list : Union[str, Iterable[str]] + the command list for a control device """ super().__init__() @@ -56,15 +58,26 @@ def __init__(self, command_list: Union[str, Iterable[str]]): def apply_patterns(self, patterns: Union[str, Iterable[str]]): """ - Applies a the REs defined in `patterns` to parse the command - list. + Applies the regular expressions defined in `patterns` to parse + the command list. - :param patterns: list or raw strings defining REs for parsing + Parameters + ---------- + patterns : Union[str, Iterable[str]] + list of raw strings defining regular expressions for parsing the command list - :type patterns: str or list of strings - :return: (bool, dict) - :Example: + Returns + ------- + bool + `True` if the command list is parsed successfully, `False` + otherwise. + + dict + results from the command list parsing + + Examples + -------- >>> # define a command list >>> cl = ['VOLT 20.0', 'VOLT 25.0', 'VOLT 30.0'] @@ -275,9 +288,11 @@ def try_patterns(self, patterns: Union[str, Iterable[str]]): Prints to the results of applying the REs in patterns to the command list. Pretty print of :meth:`apply_patterns`. - :param patterns: list or raw strings defining REs for parsing + Parameters + ---------- + patterns : Union[str, Iterable[str]] + list of raw strings defining regular expressions for parsing the command list - :type patterns: str or list of strings """ # TODO: clean method and format print better # diff --git a/bapsflib/_hdf/maps/controls/sixk.py b/bapsflib/_hdf/maps/controls/sixk.py index 8f1fde71..2ce207fb 100644 --- a/bapsflib/_hdf/maps/controls/sixk.py +++ b/bapsflib/_hdf/maps/controls/sixk.py @@ -49,7 +49,10 @@ class HDFMapControl6K(HDFMapControlTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 control device group + Parameters + ---------- + group : `h5py.Group` + the HDF5 control device group """ HDFMapControlTemplate.__init__(self, group) @@ -212,8 +215,15 @@ def _analyze_motionlist(self, gname: str) -> dict: Determines if `gname` matches the RE for a motion list group name. It yes, then it gathers the motion list info. - :param str gname: name of potential motion list group - :return: dictionary with `'name'` and `'config'` keys + Parameters + ---------- + gname : `str` + name of potential motion list group + + Returns + ------- + dict + dictionary with ``'name'`` and ``'config'`` keys """ # Define RE pattern # - A motion list group follows the naming scheme of: @@ -340,8 +350,15 @@ def _analyze_probelist(self, gname: str) -> dict: Determines if `gname` matches the RE for a probe list group name. If yes, then it gathers the probe info. - :param str gname: name of potential probe list group - :return: dictionary with `'probe-id'` and `'config'` keys + Parameters + ---------- + gname : `str` + name of potential probe list group + + Returns + ------- + dict + dictionary with ``'probe-id'`` and ``'config'`` keys """ # Define RE pattern # - A probe list group follows the naming scheme of: diff --git a/bapsflib/_hdf/maps/controls/templates.py b/bapsflib/_hdf/maps/controls/templates.py index 785eea83..2b3627a6 100644 --- a/bapsflib/_hdf/maps/controls/templates.py +++ b/bapsflib/_hdf/maps/controls/templates.py @@ -26,40 +26,41 @@ class HDFMapControlTemplate(ABC): # noinspection PySingleQuotedDocstring - ''' + """ Template class for all control mapping classes to inherit from. - Any inheriting class should define :code:`__init__` as:: + .. note:: - def __init__(self, group: h5py.Group): - """ - :param group: HDF5 group object - """ - # initialize - HDFMapControlTemplate.__init__(self, group) + If a control device is structured around a :ibf:`command list`, + then its mapping class should subclass + :class:`~.templates.HDFMapControlCLTemplate` instead. + """ - # define control type - self.info['contype'] = ConType.motion + def __init__(self, group: h5py.Group): + """ + Parameters + ---------- + group : `h5py.Group` + the control device HDF5 group object - # populate self.configs - self._build_configs() + Notes + ----- - .. note:: + Any inheriting class should define ``__init__`` as:: - * Any method that raises a :exc:`NotImplementedError` is - intended to be overwritten by the inheriting class. - * :code:`from bapsflib._hdf.maps.controls import ConType` - * If a control device is structured around a - :ibf:`command list`, then its mapping class should subclass - :class:`~.templates.HDFMapControlCLTemplate`. - Which is a subclass of - :class:`~.templates.HDFMapControlTemplate`, - but adds methods for parsing/handling a command list. - ''' + def __init__(self, group: h5py.Group): + ''' + :param group: HDF5 group object + ''' + # initialize + HDFMapControlTemplate.__init__(self, group) + + # define control type + self.info['contype'] = ConType.motion + + # populate self.configs + self._build_configs() - def __init__(self, group: h5py.Group): - """ - :param group: the control device HDF5 group object """ # condition group arg if isinstance(group, h5py.Group): @@ -84,9 +85,9 @@ def configs(self) -> dict: translate the HDF5 data into a numpy array by :class:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls`. - **-- Constructing** :code:`configs` **--** + **-- Constructing** ``configs`` **--** - The :code:`configs` dict is a nested dictionary where the first + The ``configs`` dict is a nested dictionary where the first level of keys represents the control device configuration names. Each configuration corresponds to one dataset in the HDF5 control group and represents a grouping of state values @@ -94,8 +95,8 @@ def configs(self) -> dict: experiment. Each configuration is a dictionary consisting of a set of - required keys (:code:`'dset paths'`, :code:`'shotnum'`, and - :code:`'state values'`) and optional keys. Any optional key is + required keys (``'dset paths'``, ``'shotnum'``, and + ``'state values'``) and optional keys. Any optional key is considered as meta-info for the device and is added to the :attr:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls.info` dictionary when the numpy array is constructed. The required @@ -103,7 +104,7 @@ def configs(self) -> dict: and are explained in the table below. .. csv-table:: Dictionary breakdown for - :code:`config = configs['config name']` + ``config = configs['config name']`` :header: "Key", "Description" :widths: 20, 60 @@ -122,7 +123,7 @@ def configs(self) -> dict: config['shotnum'] ", " Defines how the run shot numbers are stored in the HDF5 - file, which are mapped to the :code:`'shotnum'` field of the + file, which are mapped to the ``'shotnum'`` field of the constructed numpy array. Should look like, :: config['shotnum'] = { @@ -132,12 +133,12 @@ def configs(self) -> dict: 'dtype': numpy.int32, } - where :code:`'dset paths'` is the internal HDF5 path to the - dataset, :code:`'dset field'` is the field name of the - dataset containing shot numbers, :code:`'shape'` is the - numpy shape of the shot number data, and :code:`'dtype'` - is the numpy :code:`dtype` of the data. This all defines - the numpy :code:`dtype` of the :code:`'shotnum'` field in + where ``'dset paths'`` is the internal HDF5 path to the + dataset, ``'dset field'`` is the field name of the + dataset containing shot numbers, ``'shape'`` is the + numpy shape of the shot number data, and ``'dtype'`` + is the numpy `~numpy.dtype` of the data. This all defines + the numpy `~numpy.dtype` of the ``'shotnum'`` field in the :class:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls` constructed numpy array. @@ -146,7 +147,7 @@ def configs(self) -> dict: config['state values'] ", " - This is another dictionary defining :code:`'state values`. + This is another dictionary defining ``'state values'``. For example, :: config['state values'] = { @@ -159,31 +160,31 @@ def configs(self) -> dict: will tell :class:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls` - to construct a numpy array with a the :code:`'xyz'` field. + to construct a numpy array with a the ``'xyz'`` field. This field would be a 3-element array of - :code:`numpy.float32`, where the :code:`'x'` field of the - HDF5 dataset is mapped to the 1st index, :code:`'y'` is - mapped to the 2nd index, and :code:`'z'` is mapped to the + `numpy.float32`, where the ``'x'`` field of the + HDF5 dataset is mapped to the 1st index, ``'y'`` is + mapped to the 2nd index, and ``'z'`` is mapped to the 3rd index. **Note:** * A state value field (key) can not be defined as - :code:`'signal'` since this field is reserved for - digitizer data constructed by + ``'signal'`` since this field is reserved for digitizer + data constructed by :class:`~bapsflib._hdf.utils.hdfreaddata.HDFReadData`. * If state value data represents probe position data, then - it should be given the field name (key) :code:`'xyz'` + it should be given the field name (key) ``'xyz'`` (like in the example above). " If a control device saves data around the concept of a - :ibf:`command list`, then :code:`configs` has a few additional + :ibf:`command list`, then ``configs`` has a few additional required keys, see table below. .. csv-table:: Additional required keys for - :code:`config = configs['config name']` when + ``config = configs['config name']`` when the control device saves data around the concept of a :ibf:`command list`. :header: "Key", "Description" @@ -206,8 +207,7 @@ def configs(self) -> dict: config['state values'] ", " Has all the same keys as before, plus the addition of - :code:`'command list'`, :code:`'cl str`, - and :code:`'re pattern'`. + ``'command list'``, ``'cl str'``, and ``re pattern``. For example, :: config['state values'] = { @@ -222,10 +222,10 @@ def configs(self) -> dict: 're pattern': re.compile(r'some RE pattern')} } - where :code:`'re pattern'` is the compiled RE pattern used - to parse the original **command list**, :code:`'cl str'` is + where ``'re pattern'`` is the compiled RE pattern used + to parse the original **command list**, ``'cl str'`` is the matched string segment of the **command list**, and - :code:`'command list'` is the set of values that will + ``'command list'`` is the set of values that will populate the constructed numpy array. " @@ -256,7 +256,7 @@ def group(self) -> h5py.Group: @property def has_command_list(self) -> bool: """ - :return: :code:`True` if dataset utilizes a command list + ``True`` if the control device utilizes a command list """ has_cl = False for config_name in self._configs: @@ -281,7 +281,7 @@ def info(self) -> dict: @property def one_config_per_dset(self) -> bool: """ - :code:`'True'` if each control configuration has its own dataset + `True` if each control configuration has its own dataset """ n_dset = len(self.dataset_names) n_configs = len(self._configs) @@ -289,7 +289,7 @@ def one_config_per_dset(self) -> bool: @property def subgroup_names(self) -> List[str]: - """list of names of the HDF5 sub-groups in the control group""" + """list of names of the HDF5 subgroups in the control group""" sgroup_names = [ name for name in self.group if isinstance(self.group[name], h5py.Group) ] @@ -306,58 +306,58 @@ def construct_dataset_name(self, *args) -> str: Constructs the dataset name corresponding to the input arguments. - :return: name of dataset - :raise: :exc:`NotImplementedError` + Returns + ------- + str + name of dataset """ - raise NotImplementedError + ... @abstractmethod def _build_configs(self): """ Gathers the necessary metadata and fills :data:`configs`. - - :raise: :exc:`NotImplementedError` """ - raise NotImplementedError + ... class HDFMapControlCLTemplate(HDFMapControlTemplate): # noinspection PySingleQuotedDocstring - ''' + """ A modified :class:`HDFMapControlTemplate` template class for mapping control devices that record around the concept of a :ibf:`command list`. + """ - Any inheriting class should define :code:`__init__` as:: - - def __init__(self, group: h5py.Group): - """ - :param group: HDF5 group object - """ - # initialize - HDFMapControlCLTemplate.__init__(self, control_group) + def __init__(self, group: h5py.Group): + """ + Parameters + ---------- + group: `h5py.Group` + the control device HDF5 group object - # define control type - self.info['contype'] = ConType.waveform + Notes + ----- - # define known command list RE patterns - self._default_re_patterns = ( - r'(?P(\bFREQ\s)(?P(\d+\.\d*|\.\d+|\d+\b)))', - ) + Any inheriting class should define ``__init__`` as:: - # populate self.configs - self._build_configs() + def __init__(self, group: h5py.Group): + ''' + :param group: HDF5 group object + ''' + # initialize + HDFMapControlCLTemplate.__init__(self, control_group) - .. note:: + # define control type + self.info['contype'] = ConType.waveform - * Any method that raises a :exc:`NotImplementedError` is - intended to be overwritten by the inheriting class. - * :code:`from bapsflib._hdf.maps.controls import ConType` - ''' + # define known command list RE patterns + self._default_re_patterns = ( + r'(?P(\bFREQ\s)(?P(\d+\.\d*|\.\d+|\d+\b)))', + ) - def __init__(self, group: h5py.Group): - """ - :param group: the control device HDF5 group object + # populate self.configs + self._build_configs() """ HDFMapControlTemplate.__init__(self, group) @@ -369,50 +369,58 @@ def __init__(self, group: h5py.Group): @abstractmethod def _default_state_values_dict(self, config_name: str) -> dict: """ - Returns the default :code:`'state values'` dictionary for + Returns the default ``'state values'`` dictionary for configuration *config_name*. - :param str config_name: configuration name - :raise: :exc:`NotImplementedError` - - :Example: - - .. code-block:: python - - # define default dict - default_dict = { - 'command': { - 'dset paths': - self._configs[config_name]['dese paths'], - 'dset field': ('Command index', ), - 're pattern': None, - 'command list': - self._configs[config_name]['command list'], - 'cl str': - self._configs[config_name]['command list'], - 'shape': (), - } + Parameters + ---------- + config_name: `str` + configuration name + + Examples + -------- + + .. code-block:: python + + # define default dict + default_dict = { + 'command': { + 'dset paths': + self._configs[config_name]['dese paths'], + 'dset field': ('Command index', ), + 're pattern': None, + 'command list': + self._configs[config_name]['command list'], + 'cl str': + self._configs[config_name]['command list'], + 'shape': (), } - default_dict['command']['dtype'] = \\ - default_dict['command']['command list'].dtype + } + default_dict['command']['dtype'] = \\ + default_dict['command']['command list'].dtype - # return - return default_dict + # return + return default_dict """ - raise NotImplementedError + ... def _construct_state_values_dict( self, config_name: str, patterns: Union[str, Iterable[str]] ) -> dict: """ Returns a dictionary for - :code:`configs[config_name]['state values']` based on the - supplied RE patterns. :code:`None` is returned if the + ``configs[config_name]['state values']`` based on the + supplied RE patterns. `None` is returned if the construction failed. - :param config_name: configuration name - :param patterns: list of RE pattern strings + Parameters + ---------- + config_name : `str` + configuration name + + patterns : `Union[str, Iterable[str]] + list of RE pattern strings """ # -- check requirements exist before continuing ---- # get dataset @@ -465,9 +473,12 @@ def clparse(self, config_name: str) -> CLParse: """ Return instance of :class:`~bapsflib.lapd.controls.parsers.CLParse` - for `config_name`. + for ``config_name``. - :param str config_name: configuration name + Parameters + ---------- + config_name : `str` + configuration name """ # retrieve command list cl = self._configs[config_name]["command list"] @@ -477,14 +488,17 @@ def clparse(self, config_name: str) -> CLParse: def reset_state_values_config(self, config_name: str, apply_patterns=False): """ - Reset the :code:`configs[config_name]['state values']` - dictionary. - - :param config_name: configuration name - :param bool apply_patterns: Set :code:`False` (DEFAULT) to - reset to :code:`_default_state_values_dict(config_name)`. - Set :code:`True` to rebuild dict using - :attr:`_default_re_patterns`. + Reset the ``configs[config_name]['state values']`` dictionary. + + Parameters + ---------- + config_name : `str` + configuration name + + apply_patterns : `bool` + Set `False` (DEFAULT) to reset to + ``_default_state_values_dict(config_name)``. Set `True` to + rebuild dict using :attr:`_default_re_patterns`. """ if apply_patterns: # get sv_dict dict @@ -504,12 +518,16 @@ def set_state_values_config( self, config_name: str, patterns: Union[str, Iterable[str]] ): """ - Rebuild and set - :code:`configs[config_name]['state values']` based on the - supplied RE *patterns*. + Rebuild and set ``configs[config_name]['state values']`` based + on the supplied RE *patterns*. + + Parameters + ---------- + config_name : `str` + configuration name - :param config_name: configuration name - :param patterns: list of RE strings + patterns : `Union[str, Iterable[str]]` + list of RE strings """ # construct dict for 'state values' dict sv_dict = self._construct_state_values_dict(config_name, patterns) diff --git a/bapsflib/_hdf/maps/controls/tests/fauxsixk.py b/bapsflib/_hdf/maps/controls/tests/fauxsixk.py index 6c94fdcb..53da5678 100644 --- a/bapsflib/_hdf/maps/controls/tests/fauxsixk.py +++ b/bapsflib/_hdf/maps/controls/tests/fauxsixk.py @@ -64,7 +64,7 @@ def n_configs(self, val: int): def n_motionlists(self): """ Number of motion lists used. Will always be one unless - :code:`n_configs == 1` and then :code:`n_motionlists >= 1` + ``n_configs == 1`` and then ``n_motionlists >= 1`` """ return self._faux._n_motionlists @@ -339,7 +339,7 @@ def _add_motionlist_groups(self): self._configs[config_name]["motion lists"] = self._motionlist_names def _add_datasets(self): - """Create datasets for each configurations""" + """Create datasets for each configuration""" # TODO: fill data fields 'x', 'y', 'z', 'theta', 'phi' shape = (self._sn_size,) dtype = np.dtype( diff --git a/bapsflib/_hdf/maps/controls/waveform.py b/bapsflib/_hdf/maps/controls/waveform.py index 082cb9a0..d2d40e9d 100644 --- a/bapsflib/_hdf/maps/controls/waveform.py +++ b/bapsflib/_hdf/maps/controls/waveform.py @@ -44,7 +44,10 @@ class HDFMapControlWaveform(HDFMapControlCLTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 control device group + Parameters + ---------- + group : `h5py.Group` + the HDF5 control device group """ # initialize HDFMapControlCLTemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/core.py b/bapsflib/_hdf/maps/core.py index 1b7cc766..89d5ae32 100644 --- a/bapsflib/_hdf/maps/core.py +++ b/bapsflib/_hdf/maps/core.py @@ -45,26 +45,33 @@ class HDFMap(object): utilized by the HDF5 utility classes (in module :mod:`bapsflib._hdf.utils`) to manipulate and read data out of the HDF5 file. - - The following classes are leveraged to construct the mappings: - - * :class:`~.controls.map_controls.HDFMapControls`. - * :class:`~.digitizers.map_digis.HDFMapDigitizers`. - * :class:`~.msi.map_msi.HDFMapMSI`. """ def __init__( self, hdf_obj: h5py.File, control_path: str, digitizer_path: str, msi_path: str ): """ - :param hdf_obj: the HDF5 file object - :type hdf_obj: :class:`h5py.File` - :param control_path: internal HDF5 path to group containing - control devices - :param digitizer_path: internal HDF5 path to group containing - digitizers - :param msi_path: internal HDF5 path to group containing - MSI diagnostics + Parameters + ---------- + hdf_obj : `h5py.File` + the HDF5 file object + + control_path : `str` + internal HDF5 path to group containing control devices + + digitizer_path : `str` + internal HDF5 path to group containing digitizers + + msi_path : `str` + internal HDF5 path to group containing MSI diagnostics + + Notes + ----- + The following classes are leveraged to construct the mappings: + + * :class:`~.controls.map_controls.HDFMapControls`. + * :class:`~.digitizers.map_digis.HDFMapDigitizers`. + * :class:`~.msi.map_msi.HDFMapMSI`. """ # store an instance of the HDF5 object for HDFMap if isinstance(hdf_obj, h5py.File): @@ -180,13 +187,14 @@ def controls(self) -> Union[dict, HDFMapControls]: """ Dictionary of all the control device mapping objects. - :Example: + Examples + -------- - How to retrieve the mapping object of the control device - :code:`'6K Compumotor'`:: + How to retrieve the mapping object of the control device + ``'6K Compumotor'``:: - fmap = HDFMap(file_obj) - dmap = fmap.controls['6K Compumotor'] + fmap = HDFMap(file_obj) + dmap = fmap.controls['6K Compumotor'] """ return self.__controls @@ -195,34 +203,38 @@ def digitizers(self) -> Union[dict, HDFMapDigitizers]: """ Dictionary of all the digitizer device mapping objects. - :Example: + Examples + -------- - How to retrieve the mapping object of the digitizer - :code:`'SIS 3301'`:: + How to retrieve the mapping object of the digitizer + ``'SIS 3301'``:: - fmap = HDFMap(file_obj) - dmap = fmap.digitizers['SIS 3301'] + fmap = HDFMap(file_obj) + dmap = fmap.digitizers['SIS 3301'] """ return self.__digitizers def get(self, name: str): """ - Get an device mapping instance. - - :param name: name of desired device - :returns: If the specified device is mapped, then an instance - of the mapping is returned. Otherwise, :code:`None` is - returned. - :Example: - - How to retrieve the mapping object for the - :code:`'SIS 3301'` digitizer:: - - >>> fmap = HDFMap(file_obj) - >>> dmap = fmap.get('SIS 3301') - >>> - >>> # which is equivalent to - >>> dmap = fmap.digitizers['SIS 3301'] + Get a device mapping instance. Returns `None` if no mapping + instance for ``name`` is found. + + Parameters + ---------- + name : `str` + name of desired device + + Examples + -------- + + How to retrieve the mapping object for the ``'SIS 3301'`` + digitizer:: + + >>> fmap = HDFMap(file_obj) + >>> dmap = fmap.get('SIS 3301') + >>> + >>> # which is equivalent to + >>> dmap = fmap.digitizers['SIS 3301'] """ if name in self.controls: _map = self.controls[name] @@ -238,15 +250,16 @@ def get(self, name: str): @property def main_digitizer(self) -> Union[None, DigiMap]: """ - :return: the mapping object for the digitizer that is assumed - to be the :ibf:`main digitizer` in :attr:`digitizers` + Mapping object for the digitizer that is assumed to be the + :ibf:`main digitizer` in :attr:`digitizers`. + + Notes + ----- - The main digitizer is determine by scanning through the local + The main digitizer is determined by scanning through the local tuple :const:`possible_candidates` that contains a hierarchical list of digitizers. The first digitizer found is - assumed to be the :ibf:`main digitizer`. :: - - possible_candidates = ('SIS 3301', 'SIS crate') + assumed to be the :ibf:`main digitizer`. """ # possible_candidates is a hierarchical tuple of all digitizers # such that the first found digitizer is assumed to be the main @@ -268,13 +281,14 @@ def msi(self) -> Union[dict, HDFMapMSI]: """ Dictionary of all the MSI diagnostic mapping objects. - :Example: + Examples + -------- - How to retrieve the mapping object of the - :code:`'Magnetic field'` MSI diagnostic:: + How to retrieve the mapping object of the ``'Magnetic field'`` + MSI diagnostic:: - fmap = HDFMap(file_obj) - dmap = fmap.msi['Magnetic field'] + fmap = HDFMap(file_obj) + dmap = fmap.msi['Magnetic field'] """ return self.__msi diff --git a/bapsflib/_hdf/maps/digitizers/map_digis.py b/bapsflib/_hdf/maps/digitizers/map_digis.py index 5f48e17f..6faef0ed 100644 --- a/bapsflib/_hdf/maps/digitizers/map_digis.py +++ b/bapsflib/_hdf/maps/digitizers/map_digis.py @@ -25,18 +25,7 @@ class HDFMapDigitizers(dict): """ A dictionary that contains mapping objects for all the discovered digitizers in the HDF5 data group. The dictionary keys are the - names of he discovered digitizers. - - :Example: - - >>> from bapsflib import lapd - >>> from bapsflib._hdf.maps import HDFMapDigitizers - >>> f = lapd.File('sample.hdf5') - >>> # 'Raw data + config' is the LaPD HDF5 group name for the - ... # group housing digitizer and control devices - ... digi_map = HDFMapDigitizers(f['Raw data + config']) - >>> digi_map['SIS 3301'] - + names of the discovered digitizers. """ _defined_mapping_classes = { @@ -50,7 +39,22 @@ class HDFMapDigitizers(dict): def __init__(self, data_group: h5py.Group): """ - :param data_group: HDF5 group object + Parameters + ---------- + data_group : `h5py.Group` + HDF5 group object + + Examples + -------- + + >>> from bapsflib import lapd + >>> from bapsflib._hdf.maps import HDFMapDigitizers + >>> f = lapd.File('sample.hdf5') + >>> # 'Raw data + config' is the LaPD HDF5 group name for the + ... # group housing digitizer and control devices + ... digi_map = HDFMapDigitizers(f['Raw data + config']) + >>> digi_map['SIS 3301'] + """ # condition data_group arg if not isinstance(data_group, h5py.Group): @@ -74,10 +78,12 @@ def __build_dict(self) -> Dict[str, HDFMapDigiTemplate]: """ Discovers the HDF5 digitizers and builds the dictionary containing the digitizer mapping objects. This is the - dictionary used to initialize :code:`self`. + dictionary used to initialize ``self``. - :return: digitizer mapping dictionary - :rtype: dict + Returns + ------- + dict + digitizer mapping dictionary """ # all data_group subgroups # - each of these subgroups can fall into one of four 'LaPD diff --git a/bapsflib/_hdf/maps/digitizers/sis3301.py b/bapsflib/_hdf/maps/digitizers/sis3301.py index 54477fb2..c8f98276 100644 --- a/bapsflib/_hdf/maps/digitizers/sis3301.py +++ b/bapsflib/_hdf/maps/digitizers/sis3301.py @@ -54,7 +54,10 @@ class HDFMapDigiSIS3301(HDFMapDigiTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 digitizer group + Parameters + ---------- + group : `h5py.Group` + the HDF5 digitizer group """ # initialize HDFMapDigiTemplate.__init__(self, group) @@ -73,18 +76,25 @@ def _adc_info_first_pass( channel numbers, as well as, the associated setup configuration for each connected board. - :param adc_name: name of analog-digital-converter - :param config_group: HDF5 group object of the configuration - group + Parameters + ---------- + adc_name : `str` + name of analog-digital-converter - :returns: + config_group : `h5py.Group` + HDF5 group object of the configuration group + Returns + ------- + tuple Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the *board*, and the 3rd element is a dictionary of adc setup values (*bit*, *clock rate*, etc.). + Notes + ----- On the first pass, the meta-info dict will contain: .. csv-table:: @@ -198,17 +208,26 @@ def _adc_info_second_pass( Reviews and updates the `adc_info` originally generated by :meth:`_adc_info_first_pass`. - :param config_name: digitizer configuration name - :param adc_name: name of analog-digital-converter + Parameters + ---------- + config_name : `str` + digitizer configuration name - :return: + adc_name : `str` + name of analog-digital-converter + Returns + ------- + tuple Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the *board*, and the 3rd element is a dictionary of adc setup values (*bit*, *clock rate*, etc.). + Notes + ----- + On the second pass, the main and header dataset associated with each board number and channel number is reviewed. The meta-info dict is then updated with: @@ -223,7 +242,7 @@ def _adc_info_second_pass( ", " the number of shot numbers contained in the datasets associated with a **board**, equivalent to - :code:`dset.shape[0]` + ``dset.shape[0]`` " ":: @@ -231,7 +250,7 @@ def _adc_info_second_pass( ", " the number of time samples recorded in the datasets associated with a **board**, equivalent to - :code:`dset.shape[1]` + ``dset.shape[1]`` " """ # ensure dataset exists for each (brd, ch) combo @@ -525,10 +544,15 @@ def _find_active_adcs(config_group: h5py.Group) -> Tuple[str, ...]: """ Determines active adc's used in the digitizer configuration. - :param config_group: HDF5 group object of the configuration - group + Parameters + ---------- + config_group : `h5py.Group` + HDF5 group object of the configuration group - :returns: tuple of active (used) analog-digital-converter names + Returns + ------- + tuple + tuple of active (used) analog-digital-converter names """ # noinspection PyRedundantParentheses return ("SIS 3301",) @@ -539,12 +563,17 @@ def _find_adc_connections( """ Determines active connections on the adc. - :param adc_name: name of the analog-digital-converter - :param config_group: HDF5 group object of the configuration - group + Parameters + ---------- + adc_name : `str` + name of the analog-digital-converter - :return: + config_group : `h5py.Group` + HDF5 group object of the configuration group + Returns + ------- + tuple Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the @@ -675,14 +704,21 @@ def _find_adc_connections( @staticmethod def _parse_config_name(name: str) -> Union[None, str]: """ - Parses :code:`name` to determine the digitizer configuration + Parses ``name`` to determine the digitizer configuration name. A configuration group name follows the format:: 'Configuration: ' - :param name: name of potential configuration group - :returns: digitizer configuration name, or :code:`None` if - `name` does not represent a configuration group + Parameters + ---------- + name : `str` + name of potential configuration group + + Returns + ------- + Union[None, str] + digitizer configuration name, or `None` if ``name`` does + not represent a configuration group """ # Define RE pattern # - A configuration group follows the naming scheme of: @@ -721,17 +757,32 @@ def construct_dataset_name( `` is the requested board number, and `` is the requested channel number. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :param bool return_info: :code:`True` will return a dictionary - of meta-info associated with the digitizer data - (DEFAULT :code:`False`) - :returns: digitizer dataset name. If :code:`return_info=True`, - then returns a tuple of (dataset name, dictionary of - meta-info) + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `str`, optional + analog-digital-converter name + + return_info : `bool`, optional + `True` will return a dictionary of meta-info associated + with the digitizer data (DEFAULT `False`) + Returns + ------- + Union[str, Tuple[str, Dict[str, Any]]] + digitizer dataset name. If ``return_info=True``, then + returns a tuple of (dataset name, dictionary of meta-info) + + Notes + ----- The returned adc information dictionary looks like:: adc_dict = { @@ -824,12 +875,24 @@ def construct_header_dataset_name( by the input arguments and constructed by :meth:`construct_dataset_name`. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :returns: header dataset name associated with the digitizer - dataset + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `str`, optional + analog-digital-converter name + + Returns + ------- + str + header dataset name associated with the digitizer dataset """ # ensure return_info kwarg is always False kwargs["return_info"] = False diff --git a/bapsflib/_hdf/maps/digitizers/siscrate.py b/bapsflib/_hdf/maps/digitizers/siscrate.py index 802c1a36..f979b59a 100644 --- a/bapsflib/_hdf/maps/digitizers/siscrate.py +++ b/bapsflib/_hdf/maps/digitizers/siscrate.py @@ -69,7 +69,10 @@ class HDFMapDigiSISCrate(HDFMapDigiTemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 digitizer group + Parameters + ---------- + group : `h5py.Group` + the HDF5 digitizer group """ # initialize HDFMapDigiTemplate.__init__(self, group) @@ -91,17 +94,26 @@ def _adc_info_first_pass( channel numbers, as well as, the associated setup configuration for each connected board. - :param config_name: digitizer configuration name - :param adc_name: name of analog-digital-converter + Parameters + ---------- + config_name : `str` + digitizer configuration name - :returns: + adc_name : `str` + name of analog-digital-converter + Returns + ------- + Tuple[Tuple[int, Tuple[int, ...], Dict[str, Any]], ...] Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the *board*, and the 3rd element is a dictionary of adc setup values (*bit*, *clock rate*, etc.). + Notes + ----- + On the first pass, the meta-info dict will contain: .. csv-table:: @@ -181,17 +193,26 @@ def _adc_info_second_pass( Reviews and updates the `adc_info` originally generated by :meth:`_adc_info_first_pass`. - :param config_name: digitizer configuration name - :param adc_name: name of analog-digital-converter + Parameters + ---------- + config_name : `str` + digitizer configuration name - :return: + adc_name : `str` + name of analog-digital-converter + Returns + ------- + Tuple[Tuple[int, Tuple[int, ...], Dict[str, Any]], ...] Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the *board*, and the 3rd element is a dictionary of adc setup values (*bit*, *clock rate*, etc.). + Notes + ----- + On the second pass, the main and header dataset associated with each board number and channel number is reviewed. The meta-info dict is then updated with: @@ -205,16 +226,14 @@ def _adc_info_second_pass( 'nshotnum' ", " the number of shot numbers contained in the datasets - associated with a **board**, equivalent to - :code:`dset.shape[0]` + associated with a **board**, equivalent to ``dset.shape[0]`` " ":: 'nt' ", " the number of time samples recorded in the datasets - associated with a **board**, equivalent to - :code:`dset.shape[1]` + associated with a **board**, equivalent to ``dset.shape[1]`` " """ # ensure dataset exists for each (brd, ch) combo @@ -528,10 +547,15 @@ def _find_active_adcs(config_group: h5py.Group) -> Tuple[str, ...]: """ Determines active adc's used in the digitizer configuration. - :param config_group: HDF5 group object of the configuration - group + Parameters + ---------- + config_group : `h5py.Group` + HDF5 group object of the configuration group - :returns: tuple of active (used) analog-digital-converter names + Returns + ------- + Tuple[str, ...] + tuple of active (used) analog-digital-converter names """ active_adcs = [] adc_types = config_group.attrs["SIS crate board types"] @@ -548,17 +572,26 @@ def _find_adc_connections( """ Determines active connections on the adc. - :param adc_name: name of the analog-digital-converter - :param config_name: digitizer configuration name + Parameters + ---------- + adc_name : `str` + name of the analog-digital-converter - :return: + config_name : `str` + digitizer configuration name + Returns + ------- + Tuple[Tuple[int, Tuple[int, ...], Dict[str, Any]], ...] Tuple of 3-element tuples where the 1st element of the nested tuple represents a connected *board* number, the 2nd element is a tuple of connected *channel* numbers for the *board*, and the 3rd element is a dictionary of adc setup values (*bit*, *clock rate*, etc.). + Notes + ----- + On determination of adc connections, the meta-info dict will also be populated with: @@ -834,14 +867,21 @@ def _get_dset_shape(self, config_name, adc, conn_tuple): def _parse_config_name(self, name: str) -> Union[None, str]: """ - Parses :code:`name` to determine the digitizer configuration + Parses ``name`` to determine the digitizer configuration name. A configuration group name follows the format:: '' - :param name: name of potential configuration group - :returns: digitizer configuration name, or :code:`None` if - `name` does not represent a configuration group + Parameters + ---------- + name : `str` + name of potential configuration group + + Returns + ------- + Union[None, str] + digitizer configuration name, or `None` if ``name`` does + not represent a configuration group .. note:: @@ -885,17 +925,33 @@ def construct_dataset_name( 'SIS 3305' utilizes the FPGA nomenclature, where channels 1-4 reside on 'FPGA 1' and channels 5-8 reside on 'FPGA 2'. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :param bool return_info: :code:`True` will return a dictionary - of meta-info associated with the digitizer data - (DEFAULT :code:`False`) - :returns: digitizer dataset name. If :code:`return_info=True`, + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `int`, optional + analog-digital-converter name + return_info : `bool`, optional + `True` will return a dictionary of meta-info associated + with the digitizer data (DEFAULT `False`) + + Returns + ------- + Union[str, Tuple[str, Dict[str, Any]]] + digitizer dataset name. If ``return_info=True``, then returns a tuple of (dataset name, dictionary of meta-info) + Notes + ----- + The returned adc information dictionary looks like:: adc_dict = { @@ -1004,7 +1060,12 @@ def construct_dataset_name( return dataset_name def construct_header_dataset_name( - self, board: int, channel: int, config_name=None, adc=None, **kwargs + self, + board: int, + channel: int, + config_name: str = None, + adc: str = None, + **kwargs, ) -> str: """ Construct the name of the HDF5 header dataset associated with @@ -1019,12 +1080,24 @@ def construct_header_dataset_name( by the input arguments and constructed by :meth:`construct_dataset_name`. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :returns: header dataset name associated with the digitizer - dataset + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `str1, optional + analog-digital-converter name + + Returns + ------- + str + header dataset name associated with the digitizer dataset """ # ensure return_info kwarg is always False kwargs["return_info"] = False @@ -1042,10 +1115,17 @@ def get_slot(self, brd: int, adc: str) -> Union[None, int]: """ Get slot number for given board number and adc. - :param brd: board number - :param adc: digitizer analog-digital-converter name - :returns: slot number, or :code:`None` if there is no associated - slot number + Parameters + ---------- + brd : `int` + board number + adc : `str` + digitizer analog-digital-converter name + + Returns + ------- + Union[None, int] + slot number, or `None` if there is no associated slot number """ slot = None for s, info in self.slot_info.items(): diff --git a/bapsflib/_hdf/maps/digitizers/templates.py b/bapsflib/_hdf/maps/digitizers/templates.py index 6b788e66..fd08080e 100644 --- a/bapsflib/_hdf/maps/digitizers/templates.py +++ b/bapsflib/_hdf/maps/digitizers/templates.py @@ -24,36 +24,38 @@ class HDFMapDigiTemplate(ABC): # noinspection PySingleQuotedDocstring - ''' + """ Template class for all digitizer mapping classes to inherit from. + """ - Any inheriting class should define :code:`__init__` as:: + def __init__(self, group: h5py.Group): + """ + Parameters + ---------- + group : `h5py.Group` + the digitizer HDF5 group - def __init__(self, group: h5py.Group) - """ - :param group: HDF5 group object - """ - # initialize - HDFMapDigiTemplate.__init__(self, group) + Examples + -------- - # define device adc's - self._device_adcs = () # type: Tuple[str, ...] + Any inheriting class should define ``__init__`` as:: - # populate self.configs - self._build_configs() + def __init__(self, group: h5py.Group) + ''' + :param group: HDF5 group object + ''' + # initialize + HDFMapDigiTemplate.__init__(self, group) - .. note:: + # define device adc's + self._device_adcs = () # type: Tuple[str, ...] - * In the code sample above, :code:`self._device_adcs` is - digitizer specific and should be defined as a tuple of - strings of analog-digital-converter (adc) names. - * Any method that raises a :exc:`NotImplementedError` is - intended to be overwritten by the inheriting class. - ''' + # populate self.configs + self._build_configs() - def __init__(self, group: h5py.Group): - """ - :param group: the digitizer HDF5 group + where ``self._device_adcs`` is digitizer specific and should be + defined as a tuple of strings of analog-digital-converter (adc) + names. """ # condition group arg if isinstance(group, h5py.Group): @@ -78,11 +80,9 @@ def _build_configs(self): """ Examines the HDF5 group to build the :data:`configs` dictionary. - :raise: :exc:`NotImplementedError` - .. note:: - It is recommended to define additioal helper methods to + It is recommended to define additional helper methods to construct and populate the :attr:`configs` dictionary. .. csv-table:: Possible helper methods. @@ -115,7 +115,7 @@ def _build_configs(self): " """ - raise NotImplementedError + ... @property def active_configs(self) -> List[str]: @@ -134,16 +134,16 @@ def configs(self) -> dict: translate the HDF5 data into a numpy array by :class:`~bapsflib._hdf.utils.hdfreaddata.HDFReadData`. - **-- Constructing** :code:`configs` **--** + **-- Constructing** ``configs`` **--** - The :code:`configs` dict is a nested dictionary where the first + The ``configs`` dict is a nested dictionary where the first level of keys represents the digitizer configuration names. Each configuration dictionary then consists of a set of non-polymorphic and polymorphic keys. Any additional keys are currently ignored. The non-polymorphic keys are as follows: .. csv-table:: Required Non-Polymorphic keys for - :code:`config=configs['config name']` + ``config=configs['config name']`` :header: "Key", "Description" :widths: 20, 60 @@ -151,8 +151,8 @@ def configs(self) -> dict: config['active'] ", " - :code:`True` or :code:`False` indicating if this - configuration was used in recording the digitizer datasets + `True` or `False` indicating if this configuration was used + in recording the digitizer datasets " ":: @@ -187,18 +187,18 @@ def configs(self) -> dict: 'dtype': numpy.uint32, } - where :code:`'dset field'` is the field name of the - header dataset containing shot numbers, :code:`'shape'` is - the numpy shape of the shot number data, and :code:`'dtype'` - is the numpy :code:`dtype` of the data. This all defines - the numpy :code:`dtype` of the :code:`'shotnum'` field in + where ``'dset field'`` is the field name of the + header dataset containing shot numbers, ``'shape'`` is + the numpy shape of the shot number data, and ``'dtype'`` + is the numpy `~numpy.dtype` of the data. This all defines + the numpy `~numpy.dtype` of the ``'shotnum'`` field in the :class:`~bapsflib._hdf.utils.hdfreaddata.HDFReadData` constructed numpy array. " The required polymorphic keys are the names of each adc listed - in :code:`configs['config name']['adc']`. These entries contain + in ``configs['config name']['adc']``. These entries contain the adc board and channel hookup configuration, as well as, the adc setup configuration for each connected board. Continuing with the example above, this key would look something like :: @@ -215,7 +215,7 @@ def configs(self) -> dict: follows: .. csv-table:: Breakdown of Polymorphic Key. - (:code:`config=configs['config name']`) + (``config=configs['config name']``) :header: "Key", "Description" :widths: 20, 60 @@ -223,20 +223,20 @@ def configs(self) -> dict: config['SIS 3301'][0][0] ", " - :code:`int` representing the connected board number + `int` representing the connected board number " ":: config['SIS 3301'][0][1] ", " - :code:`Tuple[int, ...]` representing the connected channel + ``Tuple[int, ...]`` representing the connected channel numbers associated with the board number " ":: config['SIS 3301'][0][2] ", " - :code:`Dict[str, Any]` representing the setup configuration + ``Dict[str, Any]`` representing the setup configuration of the adc. The dictionary should look like:: config['SIS 3301'][0][2] = { @@ -249,14 +249,14 @@ def configs(self) -> dict: 'shot average (software)': None, } - where :code:`'bit'` represents the bit resolution of the - adc, :code:`'clock rate'` represents the base clock rate of - the adc, :code:`'nshotnum'` is the number of shot numbers - recorded, :code:`'nt'` is the number of time samples - recorded, :code:`'sample average (hardware)'` is the number + where ``'bit'`` represents the bit resolution of the + adc, ``'clock rate'`` represents the base clock rate of + the adc, ``'nshotnum'`` is the number of shot numbers + recorded, ``'nt'`` is the number of time samples + recorded, ``'sample average (hardware)'`` is the number of time samples averaged together (this and the - :code:`'clock rate'` make up the :code:`'sample rate'`), - and :code:`'shot average (software)'` is the number of shots + ``'clock rate'`` make up the ``'sample rate'``), + and ``'shot average (software)'`` is the number of shots intended to be average together. " @@ -265,22 +265,43 @@ def configs(self) -> dict: @abstractmethod def construct_dataset_name( - self, board: int, channel: int, config_name=None, adc=None, return_info=False + self, + board: int, + channel: int, + config_name: str = None, + adc: str = None, + return_info: bool = False, ) -> Union[str, Tuple[str, Dict[str, Any]]]: """ Constructs the name of the HDF5 dataset containing digitizer data. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :param bool return_info: :code:`True` will return a dictionary - of meta-info associated with the digitizer data - (DEFAULT :code:`False`) - :return: digitizer dataset name. If :code:`return_info=True`, - then returns a tuple of (dataset name, dictionary of - meta-info) + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `str`, optional + analog-digital-converter name + + return_info : `bool`, optional + `True` will return a dictionary of meta-info associated + with the digitizer data (DEFAULT `False`) + + Returns + ------- + Union[str, Tuple[str, Dict[str, Any]]] + digitizer dataset name. If ``return_info=True``, then + returns a tuple of (dataset name, dictionary of meta-info) + + Notes + ----- The returned adc information dictionary should look like:: @@ -296,13 +317,17 @@ def construct_dataset_name( 'shot average (software)': int, } - :raise: :exc:`NotImplementedError` """ - raise NotImplementedError + ... @abstractmethod def construct_header_dataset_name( - self, board: int, channel: int, config_name=None, adc="", **kwargs + self, + board: int, + channel: int, + config_name: str = None, + adc: str = "", + **kwargs, ) -> str: """ Construct the name of the HDF5 header dataset associated with @@ -310,29 +335,48 @@ def construct_header_dataset_name( and other shot number specific meta-data. It also has a one- to-one row correspondence with the digitizer dataset. - :param board: board number - :param channel: channel number - :param str config_name: digitizer configuration name - :param str adc: analog-digital-converter name - :returns: header dataset name associated with the digitizer - dataset + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + config_name : `str`, optional + digitizer configuration name + + adc : `str`, optional + analog-digital-converter name + + Returns + ------- + str + header dataset name associated with the digitizer dataset """ - raise NotImplementedError + ... def deduce_config_active_status(self, config_name: str) -> bool: """ Determines if data was recorded using the configuration - specified by :code:`config_name`. This is done by comparing + specified by ``config_name``. This is done by comparing the configuration name against the dataset names. - :param config_name: digitizer configuration name - :returns: :code:`True` if the configuration was used in - recording the group datasets; otherwise, :code:`False` + Parameters + ---------- + config_name : `str` + digitizer configuration name + + Returns + ------- + bool + `True` if the configuration was used in recording the group + datasets; otherwise, `False` .. note:: If the digitizer does not use the configuration name in the - name of the created datasets, then the subclassing digitzier + name of the created datasets, then the subclassing digitizer mapping class should override this method with a rule that is appropriate for the digitizer the class is being designed for. @@ -368,18 +412,35 @@ def device_name(self) -> str: return self._info["group name"] def get_adc_info( - self, board: int, channel: int, adc=None, config_name=None + self, + board: int, + channel: int, + adc: str = None, + config_name: str = None, ) -> Dict[str, Any]: """ Get adc setup info dictionary associated with **board** and **channel**. - :param board: board number - :param channel: channel number - :param str adc: analog-digital-converter name - :param config_name: digitizer configuration name - :returns: dictionary of adc setup info (bit, clock rate, - averaging, etc.) associated with **board* and **channel** + Parameters + ---------- + board : `int` + board number + + channel : `int` + channel number + + adc : `str`, optional + analog-digital-converter name + + config_name : `str`, optional + digitizer configuration name + + Returns + ------- + Dict[str, Any] + dictionary of adc setup info (bit, clock rate, averaging, + etc.) associated with **board** and **channel** """ # look for `config_name` if config_name is None: diff --git a/bapsflib/_hdf/maps/digitizers/tests/fauxsis3301.py b/bapsflib/_hdf/maps/digitizers/tests/fauxsis3301.py index 3781a235..819cc10f 100644 --- a/bapsflib/_hdf/maps/digitizers/tests/fauxsis3301.py +++ b/bapsflib/_hdf/maps/digitizers/tests/fauxsis3301.py @@ -230,7 +230,10 @@ def _build_config_group(self, config_name: str): """ Creates and populates the digitizer configuration group. - :param config_name: name of digitizer configuration + Parameters + ---------- + config_name : `str` + name of digitizer configuration """ # create configuration group gname = f"Configuration: {config_name}" diff --git a/bapsflib/_hdf/maps/digitizers/tests/fauxsiscrate.py b/bapsflib/_hdf/maps/digitizers/tests/fauxsiscrate.py index d50728ad..0f1d89e2 100644 --- a/bapsflib/_hdf/maps/digitizers/tests/fauxsiscrate.py +++ b/bapsflib/_hdf/maps/digitizers/tests/fauxsiscrate.py @@ -244,7 +244,10 @@ def _build_config_group(self, config_name: str): """ Creates and populates the digitizer configuration group. - :param config_name: name of digitizer configuration + Parameters + ---------- + config_name : `str` + name of digitizer configuration """ # TODO: consider adding calibration sub-groups # create configuration group diff --git a/bapsflib/_hdf/maps/msi/discharge.py b/bapsflib/_hdf/maps/msi/discharge.py index c49da05c..7dff3f83 100644 --- a/bapsflib/_hdf/maps/msi/discharge.py +++ b/bapsflib/_hdf/maps/msi/discharge.py @@ -41,8 +41,10 @@ class HDFMapMSIDischarge(HDFMapMSITemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 MSI diagnostic group - :type group: :class:`h5py.Group` + Parameters + ---------- + group : `h5py.Group` + the HDF5 MSI diagnostic group """ # initialize HDFMapMSITemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/msi/gaspressure.py b/bapsflib/_hdf/maps/msi/gaspressure.py index 60fe788d..56355539 100644 --- a/bapsflib/_hdf/maps/msi/gaspressure.py +++ b/bapsflib/_hdf/maps/msi/gaspressure.py @@ -40,8 +40,10 @@ class HDFMapMSIGasPressure(HDFMapMSITemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 MSI diagnostic group - :type group: :class:`h5py.Group` + Parameters + ---------- + group : `h5py.Group` + the HDF5 MSI diagnostic group """ # initialize HDFMapMSITemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/msi/heater.py b/bapsflib/_hdf/maps/msi/heater.py index 923b4dab..bfc28aac 100644 --- a/bapsflib/_hdf/maps/msi/heater.py +++ b/bapsflib/_hdf/maps/msi/heater.py @@ -39,8 +39,10 @@ class HDFMapMSIHeater(HDFMapMSITemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 MSI diagnostic group - :type group: :class:`h5py.Group` + Parameters + ---------- + group : `h5py.Group` + the HDF5 MSI diagnostic group """ # initialize HDFMapMSITemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/msi/interferometerarray.py b/bapsflib/_hdf/maps/msi/interferometerarray.py index 2fb3a407..50b53e6e 100644 --- a/bapsflib/_hdf/maps/msi/interferometerarray.py +++ b/bapsflib/_hdf/maps/msi/interferometerarray.py @@ -49,8 +49,10 @@ class HDFMapMSIInterferometerArray(HDFMapMSITemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 MSI diagnostic group - :type group: :class:`h5py.Group` + Parameters + ---------- + group : `h5py.Group` + the HDF5 MSI diagnostic group """ # initialize HDFMapMSITemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/msi/magneticfield.py b/bapsflib/_hdf/maps/msi/magneticfield.py index abbdfc76..53de7181 100644 --- a/bapsflib/_hdf/maps/msi/magneticfield.py +++ b/bapsflib/_hdf/maps/msi/magneticfield.py @@ -40,8 +40,10 @@ class HDFMapMSIMagneticField(HDFMapMSITemplate): def __init__(self, group: h5py.Group): """ - :param group: the HDF5 MSI diagnostic group - :type group: :class:`h5py.Group` + Parameters + ---------- + group : `h5py.Group` + the HDF5 MSI diagnostic group """ # initialize HDFMapMSITemplate.__init__(self, group) diff --git a/bapsflib/_hdf/maps/msi/map_msi.py b/bapsflib/_hdf/maps/msi/map_msi.py index e91db169..6948afe7 100644 --- a/bapsflib/_hdf/maps/msi/map_msi.py +++ b/bapsflib/_hdf/maps/msi/map_msi.py @@ -29,15 +29,6 @@ class HDFMapMSI(dict): A dictionary containing mapping objects for all the discovered MSI diagnostic HDF5 groups. The dictionary keys are the MSI diagnostic names. - - :Example: - - >>> from bapsflib import lapd - >>> from bapsflib._hdf.maps import HDFMapMSI - >>> f = lapd.File('sample.hdf5') - >>> # 'MSI' is the LaPD HDF5 group name for the group housing - ... # MSI diagnostic groups - ... msi_map = HDFMapMSI(f['MSI']) """ _defined_mapping_classes = { @@ -54,7 +45,20 @@ class HDFMapMSI(dict): def __init__(self, msi_group: h5py.Group): """ - :param msi_group: HDF5 group object + Parameters + ---------- + msi_group : `h5py.Group` + HDF5 group object + + Examples + -------- + + >>> from bapsflib import lapd + >>> from bapsflib._hdf.maps import HDFMapMSI + >>> f = lapd.File('sample.hdf5') + >>> # 'MSI' is the LaPD HDF5 group name for the group housing + ... # MSI diagnostic groups + ... msi_map = HDFMapMSI(f['MSI']) """ # condition msi_group arg if not isinstance(msi_group, h5py.Group): @@ -87,10 +91,12 @@ def __build_dict(self) -> Dict[str, HDFMapMSITemplate]: """ Discovers the HDF5 MSI diagnostics and builds the dictionary containing the diagnostic mapping objects. This is the - dictionary used to initialize :code:`self`. + dictionary used to initialize ``self``. - :return: MSI diagnostic mapping dictionary - :rtype: dict + Returns + ------- + dict + MSI diagnostic mapping dictionary """ # do not attach item if mapping is not known msi_dict = {} diff --git a/bapsflib/_hdf/maps/msi/templates.py b/bapsflib/_hdf/maps/msi/templates.py index f02e2553..5eba3164 100644 --- a/bapsflib/_hdf/maps/msi/templates.py +++ b/bapsflib/_hdf/maps/msi/templates.py @@ -19,31 +19,32 @@ class HDFMapMSITemplate(ABC): # noinspection PySingleQuotedDocstring - ''' + """ Template class for all MSI diagnostic mapping classes to inherit from. + """ - Any inheriting class should define :code:`__init__` as:: - - def __init__(self, group: h5py.Group): - """ - :param group: HDF5 group object - """ - # initialize - HDFMapMSITemplate.__init__(self, group) + def __init__(self, group: h5py.Group): + """ + Parameters + ---------- + group : `h5py.Group` + the MSI diagnostic HDF5 group - # populate self.configs - self._build_configs() + Notes + ----- - .. note:: + Any inheriting class should define ``__init__`` as:: - Any method that raises a :exc:`NotImplementedError` is intended - to be overwritten by the inheriting class. - ''' + def __init__(self, group: h5py.Group): + ''' + :param group: HDF5 group object + ''' + # initialize + HDFMapMSITemplate.__init__(self, group) - def __init__(self, group: h5py.Group): - """ - :param group: the MSI diagnostic HDF5 group + # populate self.configs + self._build_configs() """ # condition group arg if isinstance(group, h5py.Group): @@ -68,12 +69,12 @@ def configs(self) -> dict: array construction is done by :class:`~bapsflib._hdf.utils.hdfreadmsi.HDFReadMSI`. - **-- Constructing** :code:`configs` **--** + **-- Constructing** ``configs`` **--** - The :code:`configs` dict is broken into a set of required keys - (:code:`'shape'`, :code:`'shotnum'`, :code:`'signals'`, and - :code:`'meta'`) and optional keys. Any option key is considered - as meta-info for the device and is added to the + The ``configs`` dict is broken into a set of required keys + (``'shape'``, ``'shotnum'``, ``'signals'``, and ``'meta'``) and + optional keys. Any option key is considered as meta-info for + the device and is added to the :attr:`~bapsflib._hdf.utils.hdfreadmsi.HDFReadMSI.info` dictionary when the numpy array is constructed. The required keys constitute the mapping for constructing the numpy array. @@ -92,16 +93,16 @@ def configs(self) -> dict: configs['shape'] = (nsn, ) - where :code:`nsn` is the number of shot numbers saved to - the diagnostic's datasets. + where ``nsn`` is the number of shot numbers saved to the + diagnostic's datasets. " ":: configs['shotnum'] ", " Specifies the dataset(s) containing the recorded HDF5 shot - numbers. This maps to the :code:`'shotnum'` field of the - numpy array constructed by + numbers. This maps to the ``'shotnum'`` field of the + `numpy` array constructed by :class:`~bapsflib._hdf.utils.hdfreadmsi.HDFReadMSI`. Should look like:: @@ -112,11 +113,11 @@ def configs(self) -> dict: 'dtype': numpy.int32, } - where :code:`'dset paths'` is the internal HDF5 path to the - dataset(s), :code:`'dset field'` is the field name of the - dataset containing shot numbers, :code:`'shape'` of the shot - number data, and :code:`'dtype'` is the numpy :code:`dtype` - that the :code:`'shotnum'` field of the constructed numpy + where ``'dset paths'`` is the internal HDF5 path to the + dataset(s), ``'dset field'`` is the field name of the + dataset containing shot numbers, ``'shape'`` of the shot + number data, and ``'dtype'`` is the numpy `~numpy.dtype` + that the ``'shotnum'`` field of the constructed numpy array will be. " ":: @@ -136,7 +137,7 @@ def configs(self) -> dict: 'dtype': numpy.float32}, } - would created a :code:`'current'` field in the constructed + would create a ``'current'`` field in the constructed numpy array. Any field specified in this key is considered to be your plot-able, or ""primary"", diagnostic data. " @@ -156,8 +157,8 @@ def configs(self) -> dict: 'dtype': numpy.float32}, } - would create a :code:`'max current'` field in the - :code:`'meta'` field of the constructed numpy array. + would create a ``'max current'`` field in the ``'meta'`` + field of the constructed numpy array. " .. note:: @@ -194,12 +195,10 @@ def _build_configs(self): Gathers the necessary mapping data and constructs the :attr:`configs` dictionary. - :raises: :exc:`NotImplementedError` - .. note:: Examine :meth:`_build_configs` in existing modules for ideas on how to override this method. Also read :ref:`add_msi_mod`. """ - raise NotImplementedError + ... diff --git a/bapsflib/_hdf/maps/msi/tests/fauxinterarr.py b/bapsflib/_hdf/maps/msi/tests/fauxinterarr.py index a23b0268..de3af440 100644 --- a/bapsflib/_hdf/maps/msi/tests/fauxinterarr.py +++ b/bapsflib/_hdf/maps/msi/tests/fauxinterarr.py @@ -118,7 +118,10 @@ def _build_interferometer_group(self, inter_num): Builds interferometer group for interferometer :data:`inter_num`. - :param int inter_num: interferometer number (0 to 6) + Parameters + ---------- + inter_num : `int` + interferometer number (0 to 6) """ # Order of operations # 1. create interferometer group @@ -151,7 +154,10 @@ def _build_interferometer_datasets(self, inter_gname): Builds the datasets for interferometer associated with :data:`inter_gname`. - :param str inter_gname: name of interferometer group + Parameters + ---------- + inter_gname : `str` + name of interferometer group """ # ------ Build trace dataset ------ dname = "Interferometer trace" diff --git a/bapsflib/_hdf/maps/msi/tests/test_map_msi.py b/bapsflib/_hdf/maps/msi/tests/test_map_msi.py index 42335c51..b55de575 100644 --- a/bapsflib/_hdf/maps/msi/tests/test_map_msi.py +++ b/bapsflib/_hdf/maps/msi/tests/test_map_msi.py @@ -62,7 +62,7 @@ def map_msi(group): return HDFMapMSI(group) def test_not_h5py_group(self): - """Test error if object to map is not h5py.Group""" + """Test error if object to map is not `h5py.Group`""" with self.assertRaises(TypeError): self.map_msi(None) diff --git a/bapsflib/_hdf/maps/tests/fauxhdfbuilder.py b/bapsflib/_hdf/maps/tests/fauxhdfbuilder.py index 52a5fe37..f12bd80b 100644 --- a/bapsflib/_hdf/maps/tests/fauxhdfbuilder.py +++ b/bapsflib/_hdf/maps/tests/fauxhdfbuilder.py @@ -62,9 +62,12 @@ class FauxHDFBuilder(h5py.File): def __init__(self, name=None, add_modules=None, **kwargs): """ - :param str name: name of HDF5 file - :param add_modules: - :param kwargs: + Parameters + ---------- + name : `str` + name of HDF5 file + add_modules : optional + kwargs : optional """ # define file name, directory, and path if name is None: @@ -135,16 +138,16 @@ def modules(self): @property def tempdir(self): """ - Temporary directory containing :attr:`tempfile`. :code:`None` - if a real directory is specified upon creation. + Temporary directory containing :attr:`tempfile`. `None` if a + real directory is specified upon creation. """ return self._tempdir @property def tempfile(self): """ - Temporary HDF5 file. :code:`None` if a real file is specified - upon creation. + Temporary HDF5 file. `None` if a real file is specified upon + creation. """ return self._tempfile @@ -156,7 +159,7 @@ def path(self): def close(self): """ Close the HDF5 file and remove temporary files/directories if - the exist. + they exist. """ _path = os.path.abspath(self.filename) super().close() @@ -210,9 +213,13 @@ def add_module(self, mod_name, mod_args=None): Adds all the groups and datasets to the HDF5 file for the requested module. - :param str mod_name: name of module (e.g. :code:`'Waveform'`) - :param dict mod_args: dictionary of input arguments for the - module adder + Parameters + ---------- + mod_name : `str` + name of module (e.g. ``'Waveform'``) + + mod_args : `dict`, optional + dictionary of input arguments for the module adder """ # TODO: behavior when adding a module that already exists ?? # diff --git a/bapsflib/_hdf/utils/file.py b/bapsflib/_hdf/utils/file.py index a393bc50..e5fe170b 100644 --- a/bapsflib/_hdf/utils/file.py +++ b/bapsflib/_hdf/utils/file.py @@ -25,7 +25,7 @@ class File(h5py.File): """ Open a HDF5 file created at the Basic Plasma Science Facility. - All functionality of :class:`h5py.File` is preserved (for details + All functionality of `h5py.File` is preserved (for details see http://docs.h5py.org/en/latest/) """ @@ -40,21 +40,29 @@ def __init__( **kwargs ): """ - :param name: name (and path) of file on disk - :param mode: readonly :code:`'r'` (DEFAULT) and read/write - :code:`'r+'` - :param control_path: internal HDF5 path to group containing - control devices - :param digitizer_path: internal HDF5 path to group containing - digitizer devices - :param msi_path: internal HDF5 path to group containing MSI - devices - :param silent: set :code:`True` to suppress warnings - (:code:`False` DEFAULT) - :param kwargs: additional keywords passed on to - :class:`h5py.File` - - :Example: + Parameters + ---------- + name : `str` + name (and path) of file on disk + + mode : `str`, optional + readonly ``'r'`` (DEFAULT) and read/write ``'r+'`` + + control_path : `str`, optional + internal HDF5 path to group containing control devices + + digitizer_path : `str`, optional + internal HDF5 path to group containing digitizer devices + msi_path : `str`, optional + internal HDF5 path to group containing MSI devices + + silent : `bool`, optional + set `True` to suppress warnings (`False` DEFAULT) + kwargs : `dict`, optional + additional keywords passed on to `h5py.File` + + Examples + -------- >>> # open HDF5 file >>> f = File('sample.hdf5', @@ -73,14 +81,14 @@ def __init__( h5py.File.__init__(self, name, **kwargs) # -- define device paths -- - #: Internal HDF5 path for control devices. (DEFAULT :code:`'/'`) + #: Internal HDF5 path for control devices. (DEFAULT ``'/'``) self.CONTROL_PATH = control_path #: Internal HDF5 path for digitizer devices. - #: (DEFAULT :code:`'/'`) + #: (DEFAULT ``'/'``) self.DIGITIZER_PATH = digitizer_path - #: Internal HDF5 path for MSI devices. (DEFAULT :code:`'/'`) + #: Internal HDF5 path for MSI devices. (DEFAULT ``'/'``) self.MSI_PATH = msi_path # -- map and build info -- @@ -161,73 +169,74 @@ def read_controls( Reads data from control device datasets. See :class:`~.hdfreadcontrols.HDFReadControls` for more detail. - :param controls: - - A list of strings and/or 2-element tuples - indicating the control device(s). If a control device has - only one configuration then only the device name - :code:`'control'` needs to be passed in the list. If a - control device has multiple configurations, then the device - name and its configuration "name" needs to be passed as a - tuple element :code:`('control', 'config')` in the list. - (see :func:`~.helpers.condition_controls` for details) - - :type controls: List[Union[str, Tuple[str, Any]]] - :param shotnum: - + Parameters + ---------- + controls : List[Union[str, Tuple[str, Any]]] + A list of strings and/or 2-element tuples indicating the + control device(s). If a control device has only one + configuration then only the device name ``'control'`` needs + to be passed in the list. If a control device has multiple + configurations, then the device name and its configuration + "name" needs to be passed as a tuple element + ``('control', 'config')`` in the list. (see + :func:`~.helpers.condition_controls` for details) + + shotnum : Union[int, list(int), slice(), numpy.array], optional HDF5 file shot number(s) indicating data entries to be extracted - :type shotnum: Union[int, list(int), slice(), numpy.array] - :param bool intersection_set: - - :code:`True` (DEFAULT) will force the returned shot numbers - to be the intersection of :data:`shotnum` and the shot - numbers contained in each control device dataset. - :code:`False` will return the union instead of the - intersection, minus :math:`shotnum \\le 0`. (see - :class:`~.hdfreadcontrols.HDFReadControls` - for details) - - :param bool silent: - - :code:`False` (DEFAULT). Set :code:`True` to ignore any - `BaPSFWarning` (soft-warnings) - - :rtype: :class:`~.hdfreadcontrols.HDFReadControls` - - :Example: - - >>> # open HDF5 file - >>> f = File('sample.hdf5') - >>> - >>> # list control devices - >>> list(f.controls) - ['6K Compumotor', 'Waveform'] - >>> - >>> # list '6K Compumotor' configurations - >>> list(f.controls['6K Compumotor'].configs) - [2, 3] - >>> - >>> # extract all '6k Compumotor' data for configuration 3 - >>> cdata = f.read_controls([('6K Compumotor', 3)]) - >>> type(cdata) - bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls - >>> - >>> # list 'Waveform' configurations - >>> list(f.file_map.controls['Waveform'].configs) - ['config01'] - >>> - >>> # extract 'Waveform' data - >>> cdata = f.read_controls(['Waveform']) - >>> list(cdata.info['controls']) - ['Waveform'] - >>> - >>> # extract both 'Waveform' and '6K Compumotor' - >>> controls = ['Waveform', ('6K Compumotor', 2)] - >>> cdata = f.read_controls(controls) - >>> list(cdata.info['controls']) - ['6K Compumotor', 'Waveform'] + intersection_set : `bool`, optional + `True` (DEFAULT) will force the returned shot numbers to be + the intersection of :data:`shotnum` and the shot numbers + contained in each control device dataset. `False` will + return the union instead of the intersection, minus + :math:`shotnum \\le 0`. (see + :class:`~.hdfreadcontrols.HDFReadControls` for details) + + silent : bool, optional + `False` (DEFAULT). Set `True` to ignore any `BaPSFWarning` + (soft-warnings) + + Returns + ------- + `~.hdfreadcontrols.HDFReadControls` + `structured numpy array + `_ of + control device data + + Examples + -------- + + >>> # open HDF5 file + >>> f = File('sample.hdf5') + >>> + >>> # list control devices + >>> list(f.controls) + ['6K Compumotor', 'Waveform'] + >>> + >>> # list '6K Compumotor' configurations + >>> list(f.controls['6K Compumotor'].configs) + [2, 3] + >>> + >>> # extract all '6k Compumotor' data for configuration 3 + >>> cdata = f.read_controls([('6K Compumotor', 3)]) + >>> type(cdata) + bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls + >>> + >>> # list 'Waveform' configurations + >>> list(f.file_map.controls['Waveform'].configs) + ['config01'] + >>> + >>> # extract 'Waveform' data + >>> cdata = f.read_controls(['Waveform']) + >>> list(cdata.info['controls']) + ['Waveform'] + >>> + >>> # extract both 'Waveform' and '6K Compumotor' + >>> controls = ['Waveform', ('6K Compumotor', 2)] + >>> cdata = f.read_controls(controls) + >>> list(cdata.info['controls']) + ['6K Compumotor', 'Waveform'] """ # to avoid cyclical imports @@ -266,90 +275,104 @@ def read_data( data when requested. (see :class:`.hdfreaddata.HDFReadData` for details) - :param board: digitizer board number - :param channel: digitizer channel number - :param index: dataset row index - :type index: Union[int, list(int), slice(), numpy.array] - :param shotnum: HDF5 global shot number - :type shotnum: Union[int, list(int), slice(), numpy.array] - :param str digitizer: name of digitizer - :param str adc: name of the digitizer's analog-digital converter - :param str config_name: name of digitizer configuration - :param bool keep_bits: - - :code:`True` to keep digitizer signal in bits, - :code:`False` (default) to convert digitizer signal to - voltage - - :param add_controls: - - A list of strings and/or 2-element tuples - indicating the control device(s). If a control device has - only one configuration then only the device name - :code:`'control'` needs to be passed in the list. If a - control device has multiple configurations, then the device - name and its configuration "name" needs to be passed as a - tuple element :code:`('control', 'config')` in the list. - (see :func:`~.helpers.condition_controls` for details) - - :type add_controls: List[Union[str, Tuple[str, Any]]] - :param bool intersection_set: - - :code:`True` (DEFAULT) will force the returned shot numbers - to be the intersection of :data:`shotnum`, the digitizer - dataset shot numbers, and, if requested, the shot numbers - contained in each control device dataset. :code:`False` - will return the union instead of the intersection, minus + Parameters + ---------- + board : `int` + digitizer board number + + channel : `int` + digitizer channel number + + index : Union[int, list(int), slice(), numpy.array], optional + dataset row index + + shotnum : Union[int, list(int), slice(), numpy.array], optional + HDF5 global shot number + + digitizer : `str`, optional + name of digitizer + + adc : `str`, optional + name of the digitizer's analog-digital converter + + config_name : `str`, optional + name of digitizer configuration + + keep_bits : `bool`, optional + `True` to keep digitizer signal in bits, `False` (default) + to convert digitizer signal to voltage + + add_controls : List[Union[str, Tuple[str, Any]]], optional + A list of strings and/or 2-element tuples indicating the + control device(s). If a control device has only one + configuration then only the device name ``'control'`` needs + to be passed in the list. If a control device has multiple + configurations, then the device name and its configuration + "name" needs to be passed as a tuple element + ``('control', 'config')`` in the list. (see + :func:`~.helpers.condition_controls` for details) + + intersection_set : `bool`, optional + `True` (DEFAULT) will force the returned shot numbers to be + the intersection of :data:`shotnum`, the digitizer dataset + shot numbers, and, if requested, the shot numbers contained + in each control device dataset. `False` will return the + union instead of the intersection, minus :math:`shotnum \\le 0`. (see :class:`~.hdfreaddata.HDFReadData` for details) - :param bool silent: - - :code:`False` (DEFAULT). Set :code:`True` to ignore any - `BaPSFWarning` (soft-warnings) - - :rtype: :class:`~.hdfreaddata.HDFReadData` - - :Example: - - >>> # open HDF5 file - >>> f = File('sample.hdf5') - >>> - >>> # list control devices - >>> list(f.digitizers) - ['SIS crate'] - >>> - >>> # get active configurations - >>> f.digitizers['SIS crate'].configs - ['config01', 'config02'] - >>> - >>> # get active adc's for config - >>> f.digitizers['SIS crate'].configs['config01']['adc'] - ('SIS 3302,) - >>> - >>> # get first connected brd and channels to adc - >>> brd, chs = f.digitizers['SIS crate'].configs[ - ... 'config01']['SIS 3302'][0][0:2] - >>> brd - 1 - >>> chs - (1, 2, 3) - >>> - >>> # get data for brd = 1, ch = 1 - >>> data = f.read_data(brd, chs[0], - ... digitizer='SIS crate', - ... adc='SIS 3302', - ... config_name='config01') - >>> type(data) - bapsflib._hdf.utils.hdfreaddata.HDFReadData - >>> - >>> # Note: a quicker way to see how the digitizers are - >>> # configured is to use - >>> # - >>> # f.overview.report_digitizers() - >>> # - >>> # which prints to screen a report of the - >>> # digitizer hookup + silent : `bool`, optional + `False` (DEFAULT). Set `True` to ignore any `BaPSFWarning` + (soft-warnings) + + Returns + ------- + `~.hdfreaddata.HDFReadData` + `structured numpy array + `_ of + digitized data + + Examples + -------- + + >>> # open HDF5 file + >>> f = File('sample.hdf5') + >>> + >>> # list control devices + >>> list(f.digitizers) + ['SIS crate'] + >>> + >>> # get active configurations + >>> f.digitizers['SIS crate'].configs + ['config01', 'config02'] + >>> + >>> # get active adc's for config + >>> f.digitizers['SIS crate'].configs['config01']['adc'] + ('SIS 3302,) + >>> + >>> # get first connected brd and channels to adc + >>> brd, chs = f.digitizers['SIS crate'].configs[ + ... 'config01']['SIS 3302'][0][0:2] + >>> brd + 1 + >>> chs + (1, 2, 3) + >>> + >>> # get data for brd = 1, ch = 1 + >>> data = f.read_data(brd, chs[0], + ... digitizer='SIS crate', + ... adc='SIS 3302', + ... config_name='config01') + >>> type(data) + bapsflib._hdf.utils.hdfreaddata.HDFReadData + >>> + >>> # Note: a quicker way to see how the digitizers are + >>> # configured is to use + >>> # + >>> # f.overview.report_digitizers() + >>> # + >>> # which prints to screen a report of the + >>> # digitizer hookup """ # to avoid cyclical imports from bapsflib._hdf.utils.hdfreaddata import HDFReadData @@ -379,27 +402,36 @@ def read_msi(self, msi_diag: str, silent=False, **kwargs): Reads data from MSI Diagnostic datasets. See :class:`~.hdfreadmsi.HDFReadMSI` for more detail. - :param msi_diag: name of MSI diagnostic - :param bool silent: - - :code:`False` (DEFAULT). Set :code:`True` to ignore any - `BaPSFWarning` (soft-warnings) - - :rtype: :class:`~.hdfreadmsi.HDFReadMSI` - - :Example: - - >>> # open HDF5 file - >>> f = File('sample.hdf5') - >>> - >>> # list msi diagnostics - >>> list(f.msi) - ['Interferometer array', 'Magnetic field'] - >>> - >>> # read 'Interferometer array' - >>> mdata = f.read_msi('Interferometer array') - >>> type(mdata) - bapsflib._hdf.utils.hdfreadmsi.HDFReadMSI + Parameters + ---------- + msi_diag : `str` + name of MSI diagnostic + + silent : `bool`, optional + `False` (DEFAULT). Set `True` to ignore any `BaPSFWarning` + (soft-warnings) + + Returns + ------- + `~.hdfreadmsi.HDFReadMSI` + `structured numpy array + `_ of + MSI diagnostic data + + Examples + -------- + + >>> # open HDF5 file + >>> f = File('sample.hdf5') + >>> + >>> # list msi diagnostics + >>> list(f.msi) + ['Interferometer array', 'Magnetic field'] + >>> + >>> # read 'Interferometer array' + >>> mdata = f.read_msi('Interferometer array') + >>> type(mdata) + bapsflib._hdf.utils.hdfreadmsi.HDFReadMSI """ from bapsflib._hdf.utils.hdfreadmsi import HDFReadMSI diff --git a/bapsflib/_hdf/utils/hdfoverview.py b/bapsflib/_hdf/utils/hdfoverview.py index 3d0cdd73..c610a765 100644 --- a/bapsflib/_hdf/utils/hdfoverview.py +++ b/bapsflib/_hdf/utils/hdfoverview.py @@ -33,8 +33,10 @@ class HDFOverview(object): def __init__(self, hdf_obj: File): """ - :param hdf_obj: HDF5 file object - :type hdf_obj: :class:`~bapsflib._hdf.utils.file.File` + Parameters + ---------- + hdf_obj : `~bapsflib._hdf.utils.file.File` + HDF5 file object """ super().__init__() @@ -77,8 +79,13 @@ def save(self, filename=""): """ Saves the HDF5 overview to a text file. - :param str filename: name of text file to save the overview - report generated by :meth:`print`. + Parameters + ---------- + filename : `str`, optional + name of text file to save the overview report generated by + :meth:`print`. If no ``filename`` is given, then a text + file ``.txt`` with the same name as the HDF5 file will be + generated. """ if filename == "": # use the same name as the HDF5 file @@ -225,9 +232,11 @@ def report_msi(self, name=None): Prints to screen a report of detected MSI diagnostics and their configurations. - :param str name: name of MSI diagnostic. If :code:`None` or - `name` is not among MSI diagnostics, then all MSI - diagnostics are printed. + Parameters + ---------- + name : `str` + name of MSI diagnostic. If `None` or ``name`` is not among + MSI diagnostics, then all MSI diagnostics are printed. """ # gather configs to print if name in self._fmap.msi: @@ -258,9 +267,12 @@ def report_msi(self, name=None): @staticmethod def report_msi_configs(msi: HDFMapMSITemplate): """ - Print to screan information about the passed MSI configuration. + Print to screen information about the passed MSI configuration. - :param msi: a MSI mapping object + Parameters + ---------- + msi : `~.maps.msi.templates.HDFMapMSITemplate` + an MSI mapping object """ # print configs title status_print("configs", "", "", indent=1) @@ -275,9 +287,11 @@ def report_digitizers(self, name=None): Prints to screen a report of detected digitizers and their configurations. - :param str name: name of digitizer. If :code:`None` or - `name` is not among digitizers, then all digitizers are - printed. + Parameters + ---------- + name : `str`, optional + name of digitizer. If `None` or ``name`` is not among + digitizers, then all digitizers are printed. """ # gather configs to print if name in self._fmap.digitizers: @@ -314,7 +328,10 @@ def report_digitizer_configs(digi: HDFMapDigiTemplate): Prints to screen information about the passed digitizer configuration(s). - :param digi: a digitizer mapping object + Parameters + ---------- + digi : `~.maps.digitizers.templates.HDFMapDigiTemplate` + a digitizer mapping object """ nconfigs = len(digi.configs) if nconfigs != 0: @@ -381,9 +398,11 @@ def report_controls(self, name=None): Prints to screen a detailed report of detected control devices and their configuration(s). - :param str name: name of control device. If :code:`None` or - `name` is not among controls, then all control devices are - printed. + Parameters + ---------- + name : `str`, optional + name of control device. If `None` or ``name`` is not among + controls, then all control devices are printed. """ # gather configs to print if name in self._fmap.controls: @@ -421,7 +440,10 @@ def report_control_configs(control: HDFMapControlTemplate): Prints to screen information about the passed control device configuration(s). - :param control: a control device mapping object + Parameters + ---------- + control : `~.maps.controls.templates.HDFMapControlTemplate` + a control device mapping object """ nconfigs = len(control.configs) if nconfigs != 0: @@ -450,23 +472,37 @@ def status_print( """ Stylistic status printing for :class:`HDFOverview`. - :param first: string for 1st column - :param second: string for 2nd column - :param third: string for 3rd column - :param int indent: num. of indentations for 1st column display - :param str onetwo_pad: one character string for pad style - between 1st and 2nd column - :param int second_tab: number of characters between start of string - and start of 2nd column - - :Example: - .. code-block:: python - - >>> status_print('one', 'two', 'three', second_tab=15) - one two three - >>> status_print('one', 'two', 'three', second_tab=15, - ... indent=2, onetwo_pad='~') - | +-- one two three + Parameters + ---------- + first : `str` + string for 1st column + + second : `str` + string for 2nd column + third : `str` + string for 3rd column + + indent : `int`, optional + number of indentations for 1st column display (DEFAULT ``0``) + + onetwo_pad : `str`, optional + one character string for pad style between 1st and 2nd column + (DEFAULT ``' '``) + + second_tab : `int`, optional + number of characters between start of string and start of 2nd + column (DEFAULT ``55``) + + Examples + -------- + + .. code-block:: python + + >>> status_print('one', 'two', 'three', second_tab=15) + one two three + >>> status_print('one', 'two', 'three', second_tab=15, + ... indent=2, onetwo_pad='~') + | +-- one two three """ note_tab = 7 diff --git a/bapsflib/_hdf/utils/hdfreadcontrols.py b/bapsflib/_hdf/utils/hdfreadcontrols.py index b1de36ab..7289d97b 100644 --- a/bapsflib/_hdf/utils/hdfreadcontrols.py +++ b/bapsflib/_hdf/utils/hdfreadcontrols.py @@ -50,7 +50,7 @@ class HDFReadControls(np.ndarray): This class constructs and returns a structured numpy array. The data in the array is grouped into two categories: - #. shot numbers which are contained in the :code:`'shotnum'` field + #. shot numbers which are contained in the ``'shotnum'`` field #. control device data which is represented by the remaining fields in the numpy array. These field names are polymorphic and are defined by the control device mapping class. @@ -77,27 +77,30 @@ class HDFReadControls(np.ndarray): """ __example_doc__ = """ - :Example: Here the control device :code:`'Waveform'` is used as a - basic example: + Examples + -------- + + Here the control device ``'Waveform'`` is used as a basic + example: - >>> # open HDF5 file - >>> f = bapsflib.lapd.File('test.hdf5') - >>> - >>> # read control data - >>> # - this is equivalent to - >>> # f.read_control(['Waveform', 'config01']) - >>> data = HDFReadControls(f, ['Waveform', 'config01']) - >>> data.dtype - dtype([('shotnum', '>> - >>> # display shot numbers - >>> data['shotnum'] - array([ 1, 2, 3, ..., 6158, 6159, 6160], dtype=uint32) - >>> - >>> # show 'command' values for shot numbers 1 to 2 - >>> data['command'][0:2:] - array(['FREQ 50000.000000', 'FREQ 50000.000000'], - dtype='>> # open HDF5 file + >>> f = bapsflib.lapd.File('test.hdf5') + >>> + >>> # read control data + >>> # - this is equivalent to + >>> # f.read_control(['Waveform', 'config01']) + >>> data = HDFReadControls(f, ['Waveform', 'config01']) + >>> data.dtype + dtype([('shotnum', '>> + >>> # display shot numbers + >>> data['shotnum'] + array([ 1, 2, 3, ..., 6158, 6159, 6160], dtype=uint32) + >>> + >>> # show 'command' values for shot numbers 1 to 2 + >>> data['command'][0:2:] + array(['FREQ 50000.000000', 'FREQ 50000.000000'], + dtype='>> # open HDF5 file - >>> f = bapsflib.lapd.File('test.hdf5') - >>> - >>> # read digitizer data from board 1, channel 1, - >>> # - this is equivalent to - >>> # f.read_data(1, 1) - >>> data = HDFReadData(f, 1, 1) - >>> data.dtype - dtype([('shotnum', '>> - >>> # display shot numbers - >>> data['shotnum'] - array([ 1, 2, ..., 98, 99], dtype=uint32) - >>> - >>> # show 'signal' values for shot number 1 - >>> data['signal'][0] - array([-0.41381955, -0.4134333 , -0.4118886 , ..., -0.41127062, - -0.4105754 , -0.41119337], dtype=float32) - >>> - >>> # show 'xyz' values for shot number 1 - >>> data['xyz'][0] - array([nan, nan, nan], dtype=float32) - >>> - >>> # read digitizer data while adding '6K Compumotor' data - >>> # from receptacle (configuration) 3 - >>> data = HDFReadData(f, 1, 1, - add_controls=[('6K Compumotor', 3)]) - >>> data.dtype - dtype([('shotnum', '>> - >>> # show 'xyz' values for shot number 1 - >>> data['xyz'][0] - array([ -32. , 15. , 1022.4], dtype=float32) + >>> # open HDF5 file + >>> f = bapsflib.lapd.File('test.hdf5') + >>> + >>> # read digitizer data from board 1, channel 1, + >>> # - this is equivalent to + >>> # f.read_data(1, 1) + >>> data = HDFReadData(f, 1, 1) + >>> data.dtype + dtype([('shotnum', '>> + >>> # display shot numbers + >>> data['shotnum'] + array([ 1, 2, ..., 98, 99], dtype=uint32) + >>> + >>> # show 'signal' values for shot number 1 + >>> data['signal'][0] + array([-0.41381955, -0.4134333 , -0.4118886 , ..., -0.41127062, + -0.4105754 , -0.41119337], dtype=float32) + >>> + >>> # show 'xyz' values for shot number 1 + >>> data['xyz'][0] + array([nan, nan, nan], dtype=float32) + >>> + >>> # read digitizer data while adding '6K Compumotor' data + >>> # from receptacle (configuration) 3 + >>> data = HDFReadData(f, 1, 1, + add_controls=[('6K Compumotor', 3)]) + >>> data.dtype + dtype([('shotnum', '>> + >>> # show 'xyz' values for shot number 1 + >>> data['xyz'][0] + array([ -32. , 15. , 1022.4], dtype=float32) """ @@ -125,32 +128,53 @@ def __new__( **kwargs, ): """ - :param hdf_file: HDF5 file object - :param board: analog-digital-converter board number - :param channel: analog-digital-converter channel number - :param index: dataset row indices to be sliced (overridden - by :code:`shotnum`) - :type index: Union[int, List[int], slice, numpy.ndarray] - :param shotnum: HDF5 file shot number(s) indicating data - entries to be extracted (overrides :code:`index`) - :type shotnum: Union[int, List[int], slice, numpy.ndarray] - :param str digitizer: digitizer name - :param str adc: name of analog-digital-converter - :param str config_name: name of the digitizer configuration - :param bool keep_bits: set :code:`True` to keep data in bits, - :code:`False` (DEFAULT) to convert data to voltage - :param add_controls: a list indicating the desired control - device names and their configuration name (if more than one - configuration exists) - :type controls: Union[str, Iterable[str, Tuple[str, Any]]] - :param bool intersection_set: :code:`True` (DEFAULT) will force - the returned shot numbers to be the intersection of - :data:`shotnum` and the shot numbers contained in each - control device and digitizer dataset. :code:`False` will - return the union of shot numbers. - - Behavior of :data:`index`, :data:`shotnum` and - :data:`intersection_set`: + Parameters + ---------- + hdf_file : `~bapsflib._hdf.utils.file.File` + HDF5 file object + + board : `int` + analog-digital-converter board number + + channel : `int` + analog-digital-converter channel number + + index : Union[int, List[int], slice, numpy.ndarray], optional + dataset row indices to be sliced. Overridden by argument + ``shotnum``. (DEFAULT ``slice(None)``) + + shotnum : Union[int, List[int], slice, numpy.ndarray], optional + HDF5 file shot number(s) indicating data entries to be + extracted. Overrides argument ``index``. (DEFAULT + ``slice(None)``) + + digitizer : `str`, optional + name of the digitizer + + adc : `str`, optional + name of the analog-digital-converter + + config_name : `str`, optional + name of the digitizer configuration + + keep_bits : `bool`, optional + set `True` to keep data in bits, `False` (DEFAULT) to + convert data to voltage + + add_controls : Union[str, Iterable[str, Tuple[str, Any]]], optional + a list indicating the desired control device names and their + configuration name (if more than one configuration exists) + + intersection_set : `bool`, optional + `True` (DEFAULT) will force the returned shot numbers to be + the intersection of ``shotnum`` and the shot numbers + contained in each control device and digitizer dataset. + `False` will return the union of shot numbers. + + Notes + ----- + + Behavior of ``index``, ``shotnum`` and ``intersection_set``: .. note:: @@ -778,10 +802,9 @@ def info(self): @property def dt(self) -> Union[u.Quantity, None]: r""" - Temporal step size (in sec) calculated from the - :code:`'clock rate'` and :code:`'sample average'` items in - :attr:`info`. Returns :code:`None` if step size can not be - calculated. + Temporal step size (in sec) calculated from the ``'clock rate'`` + and ``'sample average'`` items in :attr:`info`. Returns `None` + if step size can not be calculated. .. math:: @@ -803,9 +826,9 @@ def dt(self) -> Union[u.Quantity, None]: @property def dv(self) -> Union[u.Quantity, None]: """ - Voltage step size (in volts) calculated from the :code:`'bit'` - and :code:`'voltage offset'` items in :attr:`info`. Returns - :code:`None` if step size can not be calculated. + Voltage step size (in volts) calculated from the ``'bit'`` and + ``'voltage offset'`` items in :attr:`info`. Returns `None` if + step size can not be calculated. """ if self.info["voltage offset"] is None: return @@ -886,13 +909,28 @@ def set_plasma( Set :attr:`plasma` and add key frequency, length, and velocity parameters. (all quantities in cgs except temperature is in eV) - :param float Bo: magnetic field (in Gauss) - :param float kTe: electron temperature (in eV) - :param float kTi: ion temperature (in eV) - :param float m_i: ion mass (in g) - :param float n_e: electron number density (in cm^-3) - :param int Z: ion charge number - :param float gamma: adiabatic index (arb.) + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + kTe : `float` + electron temperature (in eV) + + kTi : `float` + ion temperature (in eV) + + m_i : `float` + ion mass (in g) + + n_e : `float` + electron number density (in cm^-3) + + Z : `int` + ion charge number + + gamma : `float` + adiabatic index (arb.) """ # define base values self._plasma["Bo"] = core.FloatUnit(Bo, "G") @@ -935,8 +973,13 @@ def set_plasma_value(self, key, value): # pragma: no cover Re-define one of the base plasma values (Bo, gamma, kT, kTe, kTi, m_i, n, n_e, or Z) in the :attr:`plasma` dictionary. - :param str key: one of the base plasma values - :param value: value for key + Parameters + ---------- + key : str + one of the base plasma values + + value : + value for key """ # set plasma value if key == "Bo": @@ -1017,8 +1060,8 @@ def condition_shotnum(shotnum, dheader, shotnumkey, :param dheader: :class:`h5py.Dataset` :param str shotnumkey: field name in **dheader** that contains the shot numbers - :param bool intersection_set: Set :code:`True` to intersect - **shotnum** with the shot numbers in :code:`dheader[shotnumkey]` + :param bool intersection_set: Set `True` to intersect **shotnum** + with the shot numbers in ``dheader[shotnumkey]`` :return: index, shotnum, sni .. note:: diff --git a/bapsflib/_hdf/utils/hdfreadmsi.py b/bapsflib/_hdf/utils/hdfreadmsi.py index 3e3d7955..80d382cf 100644 --- a/bapsflib/_hdf/utils/hdfreadmsi.py +++ b/bapsflib/_hdf/utils/hdfreadmsi.py @@ -28,9 +28,9 @@ class HDFReadMSI(np.ndarray): This class constructs and returns a structured numpy array. The data in the array is grouped in three categories: - #. shot numbers which are contained in the :code:`'shotnum'` field + #. shot numbers which are contained in the ``'shotnum'`` field #. metadata data that is both shot number and diagnostic specific - which is stored in the sub-fields of the :code:`'meta'` field + which is stored in the sub-fields of the ``'meta'`` field #. recorded data arrays which get unique fields based on their mapping :attr:`~bapsflib._hdf.maps.msi.templates.HDFMapMSITemplate.configs` @@ -41,57 +41,63 @@ class HDFReadMSI(np.ndarray): """ __example_doc__ = """ - :Example: Here the :code:`'Discharge'` MSI diagnostic is used - as an example: - - >>> # open HDF5 file - >>> f = bapsflib.lapd.File('test.hdf5') - >>> - >>> # read MSI data - >>> # - this is equivalent to f.read_msi('Discharge') - >>> mdata = HDFReadMSI(f, 'Discharge') - >>> mdata.dtype - dtype([('shotnum', '>> # display shot numbers - >>> mdata['shotnum'] - array([ 0, 19251], dtype=int32) - >>> - >>> # fields 'voltage' and 'current' belong to data arrays - >>> # - show first 3 elements of 'voltage' for shot number 0 - >>> mdata['voltage'][0,0:3:] - array([-46.99707 , -46.844482, -46.99707], dtype=float32) - >>> - >>> # display peak current for shot number 0 - >>> mdata['meta']['peak current'][0] - 6127.1323 - >>> - >>> # the `info` attribute has diagnostic specific data - >>> mdata.info - {'current conversion factor': [0.0], - 'device name': 'Discharge', - 'device group path': '/MSI/Discharge', - 'dt': [4.88e-05], - 'source file': '/foo/bar/test.hdf5', - 't0': [-0.0249856], - 'voltage conversion factor': [0.0]} - >>> - >>> # get time step for the data arrays - >>> mdata.info['dt'][0] - 4.88e-05 + Examples + -------- + + Here the ``'Discharge'`` MSI diagnostic is used as an example: + + >>> # open HDF5 file + >>> f = bapsflib.lapd.File('test.hdf5') + >>> + >>> # read MSI data + >>> # - this is equivalent to f.read_msi('Discharge') + >>> mdata = HDFReadMSI(f, 'Discharge') + >>> mdata.dtype + dtype([('shotnum', '>> # display shot numbers + >>> mdata['shotnum'] + array([ 0, 19251], dtype=int32) + >>> + >>> # fields 'voltage' and 'current' belong to data arrays + >>> # - show first 3 elements of 'voltage' for shot number 0 + >>> mdata['voltage'][0,0:3:] + array([-46.99707 , -46.844482, -46.99707], dtype=float32) + >>> + >>> # display peak current for shot number 0 + >>> mdata['meta']['peak current'][0] + 6127.1323 + >>> + >>> # the `info` attribute has diagnostic specific data + >>> mdata.info + {'current conversion factor': [0.0], + 'device name': 'Discharge', + 'device group path': '/MSI/Discharge', + 'dt': [4.88e-05], + 'source file': '/foo/bar/test.hdf5', + 't0': [-0.0249856], + 'voltage conversion factor': [0.0]} + >>> + >>> # get time step for the data arrays + >>> mdata.info['dt'][0] + 4.88e-05 """ def __new__(cls, hdf_file: File, dname: str, **kwargs): """ - :param hdf_file: HDF5 file object - :type hdf_file: :class:`~bapsflib.lapd.File` - :param str dname: name of desired MSI diagnostic + Parameters + ---------- + hdf_file : `~bapsflib._hdf.utils.file.File` + HDF5 file object + + dname : `str` + name of desired MSI diagnostic """ # ---- Condition `hdf_file` ---- # - `hdf_file` is a lapd.File object diff --git a/bapsflib/_hdf/utils/helpers.py b/bapsflib/_hdf/utils/helpers.py index 0b8f5b59..e58ab0fb 100644 --- a/bapsflib/_hdf/utils/helpers.py +++ b/bapsflib/_hdf/utils/helpers.py @@ -9,8 +9,8 @@ # license terms and contributor agreement. # """ -Helper functions that are utilized by the the HDF5 utility classes -defined in module :mod:`bapsflib._hdf.utils`. +Helper functions that are utilized by the HDF5 utility classes defined +in module :mod:`bapsflib._hdf.utils`. """ __all__ = [ "build_shotnum_dset_relation", @@ -45,7 +45,7 @@ def build_shotnum_dset_relation( cconfn: Any, ) -> Tuple[np.ndarray, np.ndarray]: """ - Compares the **shotnum** numpy array to the specified dataset, + Compares the **shotnum** `numpy` array to the specified dataset, **dset**, to determine which indices contain the desired shot number(s) [for :class:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls`]. @@ -58,14 +58,32 @@ def build_shotnum_dset_relation( be in the dataset, and **index** is an array of indices corresponding to the desired shot number(s). - :param shotnum: desired HDF5 shot number(s) - :param dset: control device dataset - :type dset: :class:`h5py.Dataset` - :param str shotnumkey: field name in the control device dataset that - contains shot numbers - :param cmap: mapping object for control device - :param cconfn: configuration name for the control device - :return: :code:`index` and :code:`sni` numpy arrays + Parameters + ---------- + shotnum : :term:`array_like` + desired HDF5 shot number(s) + + dset: `h5py.Dataset` + control device dataset + + shotnumkey : `str` + field name in the control device dataset that contains shot + numbers + + cmap : `ControlMap` + mapping object for control device + + cconfn : + configuration name for the control device + + Returns + ------- + index : `numpy.ndarray` + array of indices to index ``dset`` + + sni : `numpy.ndarray` + boolean array that masks the ``shotnum`` array + .. note:: @@ -120,12 +138,25 @@ def build_sndr_for_simple_dset( A "simple" dataset is a dataset in which the data for only ONE configuration is recorded. - :param shotnum: desired HDF5 shot number - :param dset: dataset containing shot numbers - :type dset: :class:`h5py.Dataset` - :param str shotnumkey: field name in the dataset that contains + Parameters + ---------- + shotnum : :term:`array_like` + desired HDF5 shot number + + dset : `h5py.Dataset` + dataset containing shot numbers + + shotnumkey : `str` + field name in the dataset that contains the shot numbers - :return: :code:`index` and :code:`sni` numpy arrays + + Returns + ------- + index : `numpy.ndarray` + array of indices to index ``dset`` + + sni : `numpy.ndarray` + boolean array that masks the ``shotnum`` array """ # this is for a dataset that only records data for one configuration # @@ -223,14 +254,30 @@ def build_sndr_for_complex_dset( are recorded in that order, then each following grouping of three rows will maintain that order. - :param shotnum: desired HDF5 shot number - :param dset: dataset containing shot numbers - :type dset: :class:`h5py.Dataset` - :param str shotnumkey: field name in the dataset that contains - the shot numbers - :param cmap: mapping object for control device - :param cconfn: configuration name for the control device - :return: :code:`index` and :code:`sni` numpy arrays + Parameters + ---------- + shotnum : :term:`array_like` + desired HDF5 shot number + + dset : `h5py.Dataset` + dataset containing shot numbers + + shotnumkey : `str` + field name in the dataset that contains the shot numbers + + cmap : ControlMap + mapping object for control device + + cconfn : + configuration name for the control device + + Returns + ------- + index : `numpy.ndarray` + array of indices to index ``dset`` + + sni : `numpy.ndarray` + boolean array that masks the ``shotnum`` array """ # this is for a dataset that records data for multiple # configurations @@ -368,24 +415,34 @@ def condition_controls(hdf_file: File, controls: Any) -> List[Tuple[str, Any]]: :class:`~.hdfreadcontrols.HDFReadControls` and :class:`~.hdfreaddata.HDFReadData`. - :param hdf_file: HDF5 object instance - :param controls: `controls` argument to be conditioned - :return: list containing tuple pairs of control device name and - desired configuration name + Parameters + ---------- + hdf_file : `~bapsflib._hdf.utils.file.File` + HDF5 object instance + + controls : + ``controls`` argument to be conditioned + + Returns + ------- + `list` + A `list` containing tuple pairs of control device name and desired + configuration name - :Example: + Examples + -------- - >>> from bapsflib import lapd - >>> f = lapd.File('sample.hdf5') - >>> controls = ['Wavefrom', ('6K Compumotor', 3)] - >>> conditioned_controls = condition_controls(f, controls) - >>> conditioned_controls - [('Waveform', 'config01'), ('6K Compumotor', 3)] + >>> from bapsflib import lapd + >>> f = lapd.File('sample.hdf5') + >>> controls = ['Wavefrom', ('6K Compumotor', 3)] + >>> conditioned_controls = condition_controls(f, controls) + >>> conditioned_controls + [('Waveform', 'config01'), ('6K Compumotor', 3)] .. admonition:: Condition Criteria #. Input **controls** should be - :code:`Union[str, Iterable[Union[str, Tuple[str, Any]]]]` + ``Union[str, Iterable[Union[str, Tuple[str, Any]]]]`` #. There can only be one control for each :class:`~bapsflib._hdf.maps.controls.types.ConType`. #. If a control has multiple configurations, then one must be @@ -481,24 +538,36 @@ def condition_controls(hdf_file: File, controls: Any) -> List[Tuple[str, Any]]: def condition_shotnum( shotnum: Any, dset_dict: Dict[str, h5py.Dataset], shotnumkey_dict: Dict[str, str] ) -> np.ndarray: - """ + r""" Conditions the **shotnum** argument for :class:`~bapsflib._hdf.utils.hdfreadcontrols.HDFReadControls` and :class:`~bapsflib._hdf.utils.hdfreaddata.HDFReadData`. - :param shotnum: desired HDF5 shot numbers - :param dset_dict: dictionary of all control dataset instances - :param shotnumkey_dict: dictionary of the shot number field name - for each control dataset in dset_dict - :return: conditioned **shotnum** numpy array + Parameters + ---------- + shotnum: :term:`array_like` + desired HDF5 shot numbers + + dset_dict : Dict[str, h5py.Dataset] + dictionary of all control dataset instances + + shotnumkey_dict : Dict[str, str] + dictionary of the shot number field name for each control + dataset in dset_dict + + Returns + ------- + `numpy.ndarray` + conditioned ``shotnum`` numpy array + .. admonition:: Condition Criteria #. Input **shotnum** should be - :code:`Union[int, List[int,...], slice, np.ndarray]` + ``Union[int, List[int,...], slice, np.ndarray]`` #. Any :math:`\mathbf{shotnum} \le 0` will be removed. - #. A :code:`ValueError` will be thrown if the conditioned array - is NULL. + #. A `ValueError` will be thrown if the conditioned array is + NULL. """ # Acceptable `shotnum` types # 1. int @@ -593,17 +662,35 @@ def do_shotnum_intersection( Calculates intersection of **shotnum** and all existing dataset shot numbers, **shotnum[sni]**. - :param shotnum: desired HDF5 shot numbers - :param sni_dict: dictionary of all dataset **sni** arrays - :param index_dict: dictionary of all dataset **index** arrays - :return: intersected and re-calculated versions of :code:`index` - and :code:`sni` numpy arrays - .. admonition:: Recall Array Relationship .. code-block:: python shotnum[sni] = dset[index, shotnumkey] + + Parameters + ---------- + shotnum : :term:`array_like` + desired HDF5 shot numbers + + sni_dict : `IndexDict` + dictionary of all dataset **sni** arrays + + index_dict : `IndexDict` + dictionary of all dataset **index** arrays + + Returns + ------- + shotnum : `numpy.ndarray` + intersected and re-calculated array of shot numbers + + sni_dict : `IndexDict` + intersected and re-calculated arrays of ``sni`` indexing values + for each dataset + + index_dict : `IndexDict` + intersected and re-calculated arrays of ``index`` indexing + values for each dataset """ # intersect shot numbers shotnum_intersect = shotnum diff --git a/bapsflib/_hdf/utils/tests/test_hdfreadcontrol.py b/bapsflib/_hdf/utils/tests/test_hdfreadcontrol.py index 56d44aa3..ad86e5df 100644 --- a/bapsflib/_hdf/utils/tests/test_hdfreadcontrol.py +++ b/bapsflib/_hdf/utils/tests/test_hdfreadcontrol.py @@ -571,7 +571,7 @@ def test_single_control(self, _bf: File): @with_bf def test_two_controls(self, _bf: File): """ - Testing HDF5 with two control devices. Each control is setup + Testing HDF5 with two control devices. Each control is set up with one configuration each. """ # Test Outline: diff --git a/bapsflib/_hdf/utils/tests/test_helpers.py b/bapsflib/_hdf/utils/tests/test_helpers.py index 4017187b..6c1b9bae 100644 --- a/bapsflib/_hdf/utils/tests/test_helpers.py +++ b/bapsflib/_hdf/utils/tests/test_helpers.py @@ -122,7 +122,11 @@ def test_complex_dataset(self): cdset = self.cgroup["Run time list"] with self.assertRaises(ValueError): build_shotnum_dset_relation( - np.empty(5, dtype=np.uint32), cdset, "Shot number", self.map, "config01" + np.empty(5, dtype=np.uint32), + cdset, + "Shot number", + self.map, + "config01", ) def assertInRangeSN(self): diff --git a/bapsflib/lapd/_hdf/__init__.py b/bapsflib/lapd/_hdf/__init__.py index e486e27f..2a06d17e 100644 --- a/bapsflib/lapd/_hdf/__init__.py +++ b/bapsflib/lapd/_hdf/__init__.py @@ -9,8 +9,8 @@ # license terms and contributor agreement. # """ -Sub-package defining the high-level interface to access and read data written -in the HDF5 files generated by the Large Plasma Device (LaPD). +Sub-package defining the high-level interface to access and read data +written in the HDF5 files generated by the Large Plasma Device (LaPD). """ __all__ = [] diff --git a/bapsflib/lapd/_hdf/file.py b/bapsflib/lapd/_hdf/file.py index f874cf5a..d78167fd 100644 --- a/bapsflib/lapd/_hdf/file.py +++ b/bapsflib/lapd/_hdf/file.py @@ -22,24 +22,31 @@ class File(BaseFile): def __init__(self, name: str, mode="r", silent=False, **kwargs): """ - :param name: name (and path) of file on disk - :param mode: readonly :code:`'r'` (DEFAULT) and read/write - :code:`'r+'` - :param silent: set :code:`True` to suppress warnings - (:code:`False` DEFAULT) - :param kwargs: additional keywords passed on to - :class:`h5py.File` - - :Example: - - >>> # open HDF5 file - >>> f = File('sample.hdf5') - >>> type(f) - bapsflib.lapd._hdf.file.File - >>> isinstance(f, bapsflib._hdf.utils.file.File) - True - >>> isinstance(f, h5py.File) - True + Parameters + ---------- + name : `str` + name (and path) of file on disk + + mode : `str`, optional + readonly ``'r'`` (DEFAULT) and read/write ``'r+'`` + + silent : `bool`, optional + set `True` to suppress warnings (`False` DEFAULT) + + kwargs : `dict`, optional + additional keywords passed on to `h5py.File` + + Examples + -------- + + >>> # open HDF5 file + >>> f = File('sample.hdf5') + >>> type(f) + bapsflib.lapd._hdf.file.File + >>> isinstance(f, bapsflib._hdf.utils.file.File) + True + >>> isinstance(f, h5py.File) + True """ super().__init__( name, diff --git a/bapsflib/lapd/_hdf/lapdmap.py b/bapsflib/lapd/_hdf/lapdmap.py index 659f8665..766b83c5 100644 --- a/bapsflib/lapd/_hdf/lapdmap.py +++ b/bapsflib/lapd/_hdf/lapdmap.py @@ -40,22 +40,22 @@ def __init__( msi_path="MSI", ): """ - :param hdf_obj: HDF5 file object - :type hdf_obj: :class:`h5py.File` - :param str control_path: + Parameters + ---------- + hdf_obj : `h5py.File` + HDF5 file object + control_path : `str`, optional internal HDF5 path to group containing control devices - (DEFAULT :code:`'Raw data + config'`) - - :param str digitizer_path: + (DEFAULT ``'Raw data + config'``) + digitizer_path : `str`, optional internal HDF5 path to group containing digitizers - (DEFAULT :code:`'Raw data + config'`) - - :param str msi_path: + (DEFAULT ``'Raw data + config'``) + msi_path : `str`, optional internal HDF5 path to group containing MSI diagnostics - (DEFAULT :code:`'MSI'`) + (DEFAULT ``'MSI'``) """ super().__init__( hdf_obj, @@ -73,7 +73,7 @@ def __init__( @property def is_lapd(self) -> bool: - """:code:`True` if HDF5 file was generated by the LaPD""" + """`True` if HDF5 file was generated by the LaPD""" is_lapd = True if "LaPD HDF5 software version" in self._hdf_obj.attrs else False return is_lapd diff --git a/bapsflib/lapd/_hdf/lapdoverview.py b/bapsflib/lapd/_hdf/lapdoverview.py index fd872a4a..b71f30cd 100644 --- a/bapsflib/lapd/_hdf/lapdoverview.py +++ b/bapsflib/lapd/_hdf/lapdoverview.py @@ -24,8 +24,10 @@ class LaPDOverview(HDFOverview): def __init__(self, hdf_obj: File): """ - :param hdf_obj: HDF5 file object - :type hdf_obj: :class:`~bapsflib.lapd._hdf.file.File` + Parameters + ---------- + hdf_obj : `~bapsflib.lapd._hdf.file.File` + HDF5 file object """ super().__init__(hdf_obj) diff --git a/bapsflib/lapd/_hdf/tests/__init__.py b/bapsflib/lapd/_hdf/tests/__init__.py index 6dd64276..eb379b18 100644 --- a/bapsflib/lapd/_hdf/tests/__init__.py +++ b/bapsflib/lapd/_hdf/tests/__init__.py @@ -49,9 +49,16 @@ def assertMethodOverride(self, base_class, obj, method): Assert the class that instantiated `obj` over-road `base_class` `method`. - :param base_class: the class the was sub-classes - :param obj: the instantiated object - :param str method: method that should have been over-written + Parameters + ---------- + base_class + the class the was sub-classes + + obj + the instantiated object + + method : `str` + method that should have been over-written """ self.assertTrue(method_overridden(base_class, obj, method)) @@ -60,9 +67,16 @@ def assertNotMethodOverride(self, base_class, obj, method): Assert the class that instantiated `obj` did NOT override `base_class` `method`. - :param base_class: the class the was sub-classes - :param obj: the instantiated object - :param str method: method that should have NOT been over-written + Parameters + ---------- + base_class + the class the was subclasses + + obj + the instantiated object + + method : `str` + method that should have NOT been over-written """ self.assertTrue(method_overridden(base_class, obj, method)) diff --git a/bapsflib/lapd/constants/constants.py b/bapsflib/lapd/constants/constants.py index a0f116ac..c8ce3bd3 100644 --- a/bapsflib/lapd/constants/constants.py +++ b/bapsflib/lapd/constants/constants.py @@ -59,14 +59,17 @@ class SouthCathode(object): def __init__(self, operation_date=datetime.datetime.now()): """ - :param operation_date: Date the south 'main' cathode was - operated (i.e. date of the experiment) - :type operation_date: :class:`datetime.datetime` + Parameters + ---------- + operation_data `datatime.datetime` + Date the south 'main' cathode was operated (i.e. date of + the experiment) - :Example: + Examples + -------- - >>> import datetime - >>> MC = SouthCathode(datetime.date(2018, 1, 1)) + >>> import datetime + >>> MC = SouthCathode(datetime.date(2018, 1, 1)) """ super().__init__() diff --git a/bapsflib/lapd/tools/tools.py b/bapsflib/lapd/tools/tools.py index 3b044d1b..d38fc38b 100644 --- a/bapsflib/lapd/tools/tools.py +++ b/bapsflib/lapd/tools/tools.py @@ -38,10 +38,17 @@ def z_to_portnum( """ Converts LaPD axial z location to port number. - :param z: axial z location - :param unit: string or :class:`astropy.units` specifying unit type - :param bool round_to_nearest: :code:`False` (DEFAULT), :code:`True` - will round the port number to the nearest full integer + Parameters + ---------- + z : Union[int, float, u.Quantity] + axial z location + + unit : `str`, optional + string or :class:`astropy.units` specifying unit type + + round_to_nearest : `bool`, optional + `False` (DEFAULT), `True` will round the port number to the + nearest full integer .. note:: diff --git a/bapsflib/plasma/core.py b/bapsflib/plasma/core.py index 9cabf050..40b5a171 100644 --- a/bapsflib/plasma/core.py +++ b/bapsflib/plasma/core.py @@ -58,10 +58,17 @@ class FloatUnit(float): def __new__(cls, value, cgs_unit): """ - :param float value: value of constant - :param str cgs_unit: string representation of of cgs unit - :return: value of constant - :rtype: float + Parameters + ---------- + value : `float` + value of constant + cgs_unit : `str` + string representation of the cgs unit + + Returns + ------- + float + value of constant """ obj = super().__new__(cls, value) obj._unit = cgs_unit @@ -81,10 +88,17 @@ class IntUnit(int): def __new__(cls, value, cgs_unit): """ - :param int value: value of constant - :param str cgs_unit: string representation of of cgs unit - :return: value of constant - :rtype: int + Parameters + ---------- + value : `int` + value of constant + cgs_unit : `str` + string representation of the cgs unit + + Returns + ------- + int + value of constant """ obj = super().__new__(cls, value) obj._unit = cgs_unit @@ -120,15 +134,18 @@ def unit(self): # ---- frequency constants ---- def fce(Bo, **kwargs): - """ + r""" electron-cyclotron frequency (Hz) .. math:: - f_{ce} = \\frac{\Omega_{ce}}{2 \pi} - = -\\frac{|e| B_{o}}{2 \pi m_{e} c} + f_{ce} = \frac{\Omega_{ce}}{2 \pi} + = -\frac{|e| B_{o}}{2 \pi m_{e} c} - :param float Bo: magnetic field (in Gauss) + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) .. note:: see function :func:`oce` """ @@ -137,17 +154,24 @@ def fce(Bo, **kwargs): def fci(Bo, m_i, Z, **kwargs): - """ + r""" ion-cyclotron frequency (Hz) .. math:: - f_{ci} = \\frac{\Omega_{ci}}{2 \pi} - = \\frac{Z |e| B_{o}}{2 \pi m_{i} c} + f_{ci} = \frac{\Omega_{ci}}{2 \pi} + = \frac{Z |e| B_{o}}{2 \pi m_{i} c} + + Parameters + ---------- + Bo : `float` + magnetic-field (in Gauss) + + m_i : `float` + ion-mass (in g) - :param float Bo: magnetic-field (in Gauss) - :param float m_i: ion-mass (in g) - :param int Z: charge number + Z : `int` + charge number .. note:: see function :func:`oci` """ @@ -156,16 +180,25 @@ def fci(Bo, m_i, Z, **kwargs): def fLH(Bo, m_i, n_i, Z, **kwargs): - """ + r""" Lower-Hybrid Resonance frequency (Hz) .. math:: - f_{LH} = \\frac{\omega_{LH}}{2 \pi} + f_{LH} = \frac{\omega_{LH}}{2 \pi} + + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + m_i : `float` + ion mass (in g) - :param float Bo: magnetic field (in Gauss) - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) - :param int Z: ion charge number + n_i : `float` + ion number density (in :math:`cm^{-3}`) + + Z : `int` + ion charge number .. note:: for details see function :func:`oLH` """ @@ -174,15 +207,18 @@ def fLH(Bo, m_i, n_i, Z, **kwargs): def fpe(n_e, **kwargs): - """ + r""" electron-plasma frequency (Hz) .. math:: - f_{pe} = \\frac{\omega_{pe}}{2 \pi} - = \sqrt{\\frac{n_{e} e^{2}}{\pi m_{e}}} + f_{pe} = \frac{\omega_{pe}}{2 \pi} + = \sqrt{\frac{n_{e} e^{2}}{\pi m_{e}}} - :param float n_e: electron number density (in :math:`cm^{-3}`) + Parameters + ---------- + n_e : `float` + electron number density (in :math:`cm^{-3}`) .. note:: see function :func:`ope` """ @@ -191,17 +227,24 @@ def fpe(n_e, **kwargs): def fpi(m_i, n_i, Z, **kwargs): - """ + r""" ion-plasma frequency (Hz) .. math:: - f_{pi} = \\frac{\omega_{pi}}{2 \pi} - = \sqrt{\\frac{n_{i} (Z e)^{2}}{\pi m_{i}}} + f_{pi} = \frac{\omega_{pi}}{2 \pi} + = \sqrt{\frac{n_{i} (Z e)^{2}}{\pi m_{i}}} - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) - :param int Z: ion charge number + Parameters + ---------- + m_i : `float` + ion mass (in g) + + n_i : `float` + ion number density (in :math:`cm^{-3}`) + + Z : `int` + ion charge number .. note:: see function :func:`opi` """ @@ -210,15 +253,20 @@ def fpi(m_i, n_i, Z, **kwargs): def fUH(Bo, n_e, **kwargs): - """ + r""" Upper-Hybrid Resonance frequency (Hz) .. math:: - f_{UH} = \\frac{\omega_{UH}}{2 \pi} + f_{UH} = \frac{\omega_{UH}}{2 \pi} - :param float Bo: magnetic field (in Gauss) - :param float n_e: electron number density (in :math:`cm^{-3}`) + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + n_e : `float` + electron number density (in :math:`cm^{-3}`) .. note:: see function :func:`oUH` """ @@ -227,56 +275,75 @@ def fUH(Bo, n_e, **kwargs): def oce(Bo, **kwargs): - """ + r""" electron-cyclotron frequency (rad/s) .. math:: - \Omega_{ce} = -\\frac{|e| B_{o}}{m_{e} c} + \Omega_{ce} = -\frac{|e| B_{o}}{m_{e} c} - :param float Bo: magnetic-field (in Gauss) + Parameters + ---------- + Bo : `float` + magnetic-field (in Gauss) """ _oce = (-E * Bo) / (ME * C) return FloatUnit(_oce, "rad s^-1") def oci(Bo, m_i, Z, **kwargs): - """ + r""" ion-cyclotron frequency (rads / s) .. math:: - \Omega_{ci} = \\frac{Z |e| B_{o}}{m_{i} c} + \Omega_{ci} = \frac{Z |e| B_{o}}{m_{i} c} - :param float Bo: magnetic-field (in Gauss) - :param float m_i: ion-mass (in g) - :param int Z: charge number + Parameters + ---------- + Bo : `float` + magnetic-field (in Gauss) + + m_i : `float` + ion-mass (in g) + + Z : `int` + charge number """ _oci = (Z * E * Bo) / (m_i * C) return FloatUnit(_oci, "rad s^-1") def oLH(Bo, m_i, n_i, Z, **kwargs): - """ + r""" Lower-Hybrid Resonance frequency (rad/s) .. math:: - \\frac{1}{\omega_{LH}^{2}}= - \\frac{1}{\Omega_{i}^{2}+\omega_{pi}^{2}} - + \\frac{1}{\\lvert \Omega_{e}\Omega_{i} \\rvert} + \frac{1}{\omega_{LH}^{2}}= + \frac{1}{\Omega_{i}^{2}+\omega_{pi}^{2}} + + \frac{1}{\lvert \Omega_{e}\Omega_{i} \rvert} .. note:: This form is for a quasi-neutral plasma that satisfies .. math:: - \\frac{Z m_{e}}{m_{i}} \ll - 1 - \\left(\\frac{V_{A}}{c}\\right)^{2} + \frac{Z m_{e}}{m_{i}} \ll + 1 - \left(\frac{V_{A}}{c}\right)^{2} - :param float Bo: magnetic field (in Gauss) - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) - :param int Z: ion charge number + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + m_i : `float` + ion mass (in g) + + n_i : `float` + ion number density (in :math:`cm^{-3}`) + + Z : `int` + ion charge number """ _args = {"Bo": Bo, "m_i": m_i, "n_i": n_i, "Z": Z} _opi = opi(**_args) @@ -289,45 +356,60 @@ def oLH(Bo, m_i, n_i, Z, **kwargs): def ope(n_e, **kwargs): - """ + r""" electron-plasma frequency (in rad/s) .. math:: - \omega_{pe}^{2} = \\frac{4 \pi n_{e} e^2}{m_{e}} + \omega_{pe}^{2} = \frac{4 \pi n_{e} e^2}{m_{e}} - :param float n_e: electron number density (in :math:`cm^{-3}`) + Parameters + ---------- + n_e : `float` + electron number density (in :math:`cm^{-3}`) """ _ope = math.sqrt(4 * math.pi * n_e * E * E / ME) return FloatUnit(_ope, "rad s^-1") def opi(m_i, n_i, Z, **kwargs): - """ + r""" ion-plasma frequency (in rad/s) .. math:: - \omega_{pi}^{2} = \\frac{4 \pi n_{i} (Z e)^{2}}{m_{i}} + \omega_{pi}^{2} = \frac{4 \pi n_{i} (Z e)^{2}}{m_{i}} + + Parameters + ---------- + m_i : `float` + ion mass (in g) - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) - :param int Z: ion charge number + n_i : `float` + ion number density (in :math:`cm^{-3}`) + + Z : `int` + ion charge number """ _opi = math.sqrt(4 * math.pi * n_i * (Z * E) * (Z * E) / m_i) return FloatUnit(_opi, "rad s^-1") def oUH(Bo, n_e, **kwargs): - """ + r""" Upper-Hybrid Resonance frequency (rad/s) .. math:: \omega_{UH}^{2} =\omega_{pe}^{2} + \Omega_{ce}^{2} - :param float Bo: magnetic field (in Gauss) - :param float n_e: electron number density (in :math:`cm^{-3}`) + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + n_e : `float` + electron number density (in :math:`cm^{-3}`) """ _ope = ope(n_e) _oce = oce(Bo) @@ -337,15 +419,20 @@ def oUH(Bo, n_e, **kwargs): # ---- length constants ---- def lD(kT, n, **kwargs): - """ + r""" Debye Length (in cm) .. math:: - \lambda_{D} = \sqrt{\\frac{k_{B} T}{4 \pi n e^{2}}} + \lambda_{D} = \sqrt{\frac{k_{B} T}{4 \pi n e^{2}}} - :param float kT: temperature (in eV) - :param float n: number density (in :math:`cm^{-3}`) + Parameters + ---------- + kT : `float` + temperature (in eV) + + n : `float` + number density (in :math:`cm^{-3}`) """ kT = kT * constants.e * 1.0e7 # eV to ergs _lD = math.sqrt(kT / (4.0 * math.pi * n)) / E @@ -353,14 +440,17 @@ def lD(kT, n, **kwargs): def lpe(n_e, **kwargs): - """ + r""" electron-inertial length (cm) .. math:: - l_{pe} = \\frac{c}{\omega_{pe}} + l_{pe} = frac{c}{\omega_{pe}} - :param float n_e: electron number density (in :math:`cm^{-3}`) + Parameters + ---------- + n_e : `float` + electron number density (in :math:`cm^{-3}`) .. note:: see function :func:`ope` """ @@ -369,16 +459,23 @@ def lpe(n_e, **kwargs): def lpi(m_i, n_i, Z, **kwargs): - """ + r""" ion-inertial length (cm) .. math:: - l_{pi} = \\frac{c}{\omega_{pi}} + l_{pi} = \frac{c}{\omega_{pi}} - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) - :param int Z: ion charge number + Parameters + ---------- + m_i : `float` + ion mass (in g) + + n_i : `float` + ion number density (in :math:`cm^{-3}`) + + Z : `int` + ion charge number .. note:: see function :func:`opi` """ @@ -387,15 +484,20 @@ def lpi(m_i, n_i, Z, **kwargs): def rce(Bo, kTe, **kwargs): - """ + r""" electron gyroradius (cm) .. math:: - r_{ce} = \\frac{v_{T_{e}}}{\Omega_{ce}} + r_{ce} = \frac{v_{T_{e}}}{\Omega_{ce}} - :param float Bo: magnetic field (in Gauss) - :param float kTe: electron temperature (in eV) + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + kTe: `float` + electron temperature (in eV) .. note:: see functions :func:`vTe` and :func:`oce` """ @@ -404,17 +506,26 @@ def rce(Bo, kTe, **kwargs): def rci(Bo, kTi, m_i, Z, **kwargs): - """ + r""" ion gyroradius (cm) .. math:: - r_{ci} = \\frac{v_{T_{i}}}{\Omega_{ci}} + r_{ci} = \frac{v_{T_{i}}}{\Omega_{ci}} + + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) - :param float Bo: magnetic field (in Gauss) - :param float kTi: ion temperature (in eV) - :param float m_i: ion mass (in g) - :param int Z: ion charge number + kTi : `float` + ion temperature (in eV) + + m_i : `float` + ion mass (in g) + + Z : `int` + ion charge number .. note:: see functions :func:`vTi` and :func:`oci` """ @@ -424,22 +535,31 @@ def rci(Bo, kTi, m_i, Z, **kwargs): # ---- velocity constants ---- def cs(kTe, m_i, Z, gamma=1.0, **kwargs): - """ + r""" ion sound speed (cm/s) .. math:: - C_{s} = \sqrt{\\frac{\gamma Z k T_{e}}{m_{i}}} + C_{s} = \sqrt{frac{\gamma Z k T_{e}}{m_{i}}} .. note:: :math:`\gamma=1` for the case of :math:`T_{i}\ll T_{e}` (DEFAULT) - :param float kTe: electron temperature (in eV) - :param float m_i: ion mass (in g) - :param int Z: charge number - :param float gamma: adiabatic index + Parameters + ---------- + kTe: `float` + electron temperature (in eV) + + m_i : `float` + ion mass (in g) + + Z : `int` + charge number + + gamma : `float` + adiabatic index """ # TODO: double check adiabatic index default value kTe = kTe * constants.e * 1.0e7 # eV to ergs @@ -448,30 +568,40 @@ def cs(kTe, m_i, Z, gamma=1.0, **kwargs): def VA(Bo, m_i, n_i, **kwargs): - """ + r""" Alfvén Velocity (in cm/s) .. math:: - V_{A} = \\frac{B_{o}}{\sqrt{4 \pi n_{i} m_{i}}} + V_{A} = \frac{B_{o}}{\sqrt{4 \pi n_{i} m_{i}}} + + Parameters + ---------- + Bo : `float` + magnetic field (in Gauss) + + m_i : `float` + ion mass (in g) - :param float Bo: magnetic field (in Gauss) - :param float m_i: ion mass (in g) - :param float n_i: ion number density (in :math:`cm^{-3}`) + n_i : `float` + ion number density (in :math:`cm^{-3}`) """ _VA = Bo / math.sqrt(4.0 * math.pi * n_i * m_i) return FloatUnit(_VA, "cm s^-1") def vTe(kTe, **kwargs): - """ + r""" electron thermal velocity (in cm/s) .. math:: - v_{T_{e}} = \sqrt{\\frac{k T_{e}}{m_{e}}} + v_{T_{e}} = \sqrt{\frac{k T_{e}}{m_{e}}} - :param float kTe: electron temperature (in eV) + Parameters + ---------- + kTe: `float` + electron temperature (in eV) """ kTe = kTe * constants.e * 1.0e7 # eV to erg _vTe = math.sqrt(kTe / ME) @@ -479,15 +609,20 @@ def vTe(kTe, **kwargs): def vTi(kTi, m_i, **kwargs): - """ + r""" ion thermal velocity (in cm/s) .. math:: - v_{T_{i}} = \sqrt{\\frac{k T_{i}}{m_{i}}} + v_{T_{i}} = \sqrt{\frac{k T_{i}}{m_{i}}} + + Parameters + ---------- + kTi : `float` + ion temperature (in eV) - :param float kTi: ion temperature (in eV) - :param float m_i: ion mass (in g) + m_i : `float` + ion mass (in g) """ kTi = kTi * constants.e * 1.0e7 # eV to erg _vTi = math.sqrt(kTi / m_i) diff --git a/bapsflib/utils/decorators.py b/bapsflib/utils/decorators.py index ca174d8e..39c58de4 100644 --- a/bapsflib/utils/decorators.py +++ b/bapsflib/utils/decorators.py @@ -34,80 +34,92 @@ def with_bf( the positional arguments. The decorator is primarily designed for use on test methods, but can also be used as a function decorator. - :param wfunc: function or method to be wrapped - :param filename: name of the BaPSF HDF5 file - :param control_path: internal HDF5 path for control devices - :param digitizer_path: internal HDF5 path for digitizers - :param msi_path: internal HDF5 path for MSI devices - - :example: - The HDF5 file parameters (:data:`filename`, :data:`control_path`, - :data:`digitizer_path`, and :data:`msi_path`) can be passed to the - decorator in three ways (listed by predominance): - - #. The wrapped function arguments. - #. If the wrapped function is a method, then through appropriately - named :data:`self` attributes. - #. The decorator keywords. - - **Defined with wrapped function arguments**:: - - >>> # as function keywords - >>> @with_bf - ... def foo(bf, **kwargs): - ... # * bf will be the HDF5 file object - ... # * do whatever is needed with bf and @with_bf will close - ... # the file at the end - ... return bf.filename - >>> foo(filename='test.hdf5', control_path='Raw data + config', - ... digitizer_path='Raw data + config', msi_path='MSI') - 'test.hdf5' - >>> - >>> # as a function argument - >>> @with_bf - ... def foo(filename, bf, **kwargs): - ... # use bf - ... return bf.filename - ... foo('test.hdf5') - 'test.hdf5' - - **Defined with wrapped method attributes**:: - - >>> # use `self` to pass file settings - >>> class BehaveOnFile: - ... def __init__(self): - ... super().__init__() - ... self.filename = 'test.hdf5' - ... self.control_path = 'Raw data + config' - ... self.digitizer_path = 'Raw data + config' - ... self.msi_path = 'MSI' - ... - ... @with_bf - ... def foo(self, bf, **kwargs): - ... return bf.filename - >>> a = BehaveOnFile() - >>> a.foo() - 'test.hdf5' - >>> - >>> # but keywords will still take precedence - >>> a.foo(filename='test_2.hdf5') - 'test_2.hdf5' - - **Defined with decorator keywords**: - - >>> # as function keywords - >>> @with_bf(filename='test.hdf5', - ... control_path='Raw data + config', - ... digitizer_path='Raw data +config', - ... msi_path='MSI') - ... def foo(bf, **kwargs): - ... return bf.filename - >>> foo() - 'test.hdf5' - >>> - >>> # function keywords will still take precedence - >>> foo(filename='test_2.hdf5') - 'test_2.hdf5' + Parameters + ---------- + wfunc : callable + function or method to be wrapped + + filename : `str` or `None` + name of the BaPSF HDF5 file + + control_path : `str` or `None` + internal HDF5 path for control devices + + digitizer_path : `str` or `None` + internal HDF5 path for digitizers + + msi_path : `str` or `None` + internal HDF5 path for MSI devices + + Examples + -------- + The HDF5 file parameters (:data:`filename`, :data:`control_path`, + :data:`digitizer_path`, and :data:`msi_path`) can be passed to the + decorator in three ways (listed by predominance): + + #. The wrapped function arguments. + #. If the wrapped function is a method, then through appropriately + named :data:`self` attributes. + #. The decorator keywords. + + **Defined with wrapped function arguments**:: + + >>> # as function keywords + >>> @with_bf + ... def foo(bf, **kwargs): + ... # * bf will be the HDF5 file object + ... # * do whatever is needed with bf and @with_bf will close + ... # the file at the end + ... return bf.filename + >>> foo(filename='test.hdf5', control_path='Raw data + config', + ... digitizer_path='Raw data + config', msi_path='MSI') + 'test.hdf5' + >>> + >>> # as a function argument + >>> @with_bf + ... def foo(filename, bf, **kwargs): + ... # use bf + ... return bf.filename + ... foo('test.hdf5') + 'test.hdf5' + + **Defined with wrapped method attributes**:: + + >>> # use `self` to pass file settings + >>> class BehaveOnFile: + ... def __init__(self): + ... super().__init__() + ... self.filename = 'test.hdf5' + ... self.control_path = 'Raw data + config' + ... self.digitizer_path = 'Raw data + config' + ... self.msi_path = 'MSI' + ... + ... @with_bf + ... def foo(self, bf, **kwargs): + ... return bf.filename + >>> a = BehaveOnFile() + >>> a.foo() + 'test.hdf5' + >>> + >>> # but keywords will still take precedence + >>> a.foo(filename='test_2.hdf5') + 'test_2.hdf5' + + **Defined with decorator keywords**: + + >>> # as function keywords + >>> @with_bf(filename='test.hdf5', + ... control_path='Raw data + config', + ... digitizer_path='Raw data +config', + ... msi_path='MSI') + ... def foo(bf, **kwargs): + ... return bf.filename + >>> foo() + 'test.hdf5' + >>> + >>> # function keywords will still take precedence + >>> foo(filename='test_2.hdf5') + 'test_2.hdf5' """ # How to pass in file settings (listed in priority): # 1. function keywords @@ -200,69 +212,75 @@ def with_lapdf(wfunc=None, *, filename: Union[str, None] = None): the positional arguments. The decorator is primarily designed for use on test methods, but can also be used as a function decorator. - :param wfunc: function or method to be wrapped - :param filename: name of the BaPSF HDF5 file - - :example: - The HDF5 :data:`filename` can be passed to the decorator in three - ways (listed by predominance): - - #. The wrapped function arguments. - #. If the wrapped function is a method, then through the - appropriately named :data:`self` attributes. - #. The decorator keywords. - - **Defined with wrapped function arguments**:: - - >>> # as function keywords - >>> @with_lapdf - ... def foo(lapdf, **kwargs): - ... # * bf will be the HDF5 file object - ... # * do whatever is needed with bf and @with_bf will close - ... # the file at the end - ... return lapdf.filename - >>> foo(filename='test.hdf5') - 'test.hdf5' - >>> - >>> # as a function argument - >>> @with_lapdf - ... def foo(filename, lapdf, **kwargs): - ... # use bf - ... return lapdf.filename - ... foo('test.hdf5') - 'test.hdf5' - - **Defined with wrapped method attributes**:: - - >>> # use `self` to pass file settings - >>> class BehaveOnFile: - ... def __init__(self): - ... super().__init__() - ... self.filename = 'test.hdf5' - ... - ... @with_bf - ... def foo(self, lapdf, **kwargs): - ... return lapdf.filename - >>> a = BehaveOnFile() - >>> a.foo() - 'test.hdf5' - >>> - >>> # but keywords will still take precedence - >>> a.foo(filename='test_2.hdf5') - 'test_2.hdf5' - - **Defined with decorator keywords**: - - >>> # as function keywords - >>> @with_bf(filename='test.hdf5') - ... def foo(lapdf, **kwargs): - ... return lapdf.filename - >>> foo() - 'test.hdf5' - >>> - >>> # function keywords will still take precedence - >>> foo(filename='test_2.hdf5') - 'test_2.hdf5' + Parameters + ---------- + wfunc : callable + function or method to be wrapped + + filename : `str` or `None` + name of the BaPSF HDF5 file + + Examples + -------- + The HDF5 :data:`filename` can be passed to the decorator in three + ways (listed by predominance): + + #. The wrapped function arguments. + #. If the wrapped function is a method, then through the + appropriately named :data:`self` attributes. + #. The decorator keywords. + + **Defined with wrapped function arguments**:: + + >>> # as function keywords + >>> @with_lapdf + ... def foo(lapdf, **kwargs): + ... # * bf will be the HDF5 file object + ... # * do whatever is needed with bf and @with_bf will close + ... # the file at the end + ... return lapdf.filename + >>> foo(filename='test.hdf5') + 'test.hdf5' + >>> + >>> # as a function argument + >>> @with_lapdf + ... def foo(filename, lapdf, **kwargs): + ... # use bf + ... return lapdf.filename + ... foo('test.hdf5') + 'test.hdf5' + + **Defined with wrapped method attributes**:: + + >>> # use `self` to pass file settings + >>> class BehaveOnFile: + ... def __init__(self): + ... super().__init__() + ... self.filename = 'test.hdf5' + ... + ... @with_bf + ... def foo(self, lapdf, **kwargs): + ... return lapdf.filename + >>> a = BehaveOnFile() + >>> a.foo() + 'test.hdf5' + >>> + >>> # but keywords will still take precedence + >>> a.foo(filename='test_2.hdf5') + 'test_2.hdf5' + + **Defined with decorator keywords**: + + >>> # as function keywords + >>> @with_bf(filename='test.hdf5') + ... def foo(lapdf, **kwargs): + ... return lapdf.filename + >>> foo() + 'test.hdf5' + >>> + >>> # function keywords will still take precedence + >>> foo(filename='test_2.hdf5') + 'test_2.hdf5' """ # How to pass in file settings (listed in priority): # 1. function keywords diff --git a/changelog/140.doc.rst b/changelog/140.doc.rst new file mode 100644 index 00000000..57093651 --- /dev/null +++ b/changelog/140.doc.rst @@ -0,0 +1,2 @@ +Converted all docstrings to the `numpydoc style +`_. diff --git a/changelog/140.pkg_management.rst b/changelog/140.pkg_management.rst new file mode 100644 index 00000000..d77570ca --- /dev/null +++ b/changelog/140.pkg_management.rst @@ -0,0 +1 @@ +Added package dependency `numpydoc`. diff --git a/docs/conf.py b/docs/conf.py index 3e9f4f2a..52d8ba1e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,9 +41,10 @@ extensions = [ "hoverxref.extension", "sphinx.ext.autodoc", + "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.mathjax", - "sphinx.ext.autosummary", + "sphinx.ext.napoleon", "sphinx_automodapi.automodapi", "sphinx_automodapi.smart_resolver", "sphinx_changelog", @@ -54,6 +55,7 @@ "astropy": ("http://docs.astropy.org/en/stable/", None), "h5py": ("https://docs.h5py.org/en/latest/", None), "numpy": ("https://numpy.org/doc/stable/", None), + "numpydoc": ("https://numpydoc.readthedocs.io/en/latest/", None), "plasmapy": ("https://docs.plasmapy.org/en/latest/", None), "python": ("https://docs.python.org/3", None), "readthedocs": ("https://docs.readthedocs.io/en/stable/", None), diff --git a/requirements/docs.txt b/requirements/docs.txt index a1b966ab..73d8ae63 100644 --- a/requirements/docs.txt +++ b/requirements/docs.txt @@ -1,6 +1,7 @@ # these are dependencies required to build package documentation # ought to mirror 'docs' under options.extras_require in config.cfg -r install.txt +numpydoc >= 1.6 packaging sphinx >= 3.2.0 sphinx-automodapi >= 0.13 diff --git a/setup.cfg b/setup.cfg index abe912ee..9cd9c2cb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,6 +59,7 @@ tests = coverage[toml] >= 4.5.1 docs = # ought to mirror requirements/docs.txt + numpydoc >= 1.6 packaging sphinx >= 3.2.0 sphinx-automodapi >= 0.13