Source code for aviary.utils.named_values

'''
Define utilities for using named values with associated units.

Utilities
---------
Units : type alias
    define a type hint for associated units

ValueAndUnits : type alias
    define a type hint for a single value paired with its associated units

OptionalValueAndUnits : type alias
    define a type hint for an optional single value paired with its associated units

class NamedValues
    define a collection of named values with associated units
'''
import copy
from collections.abc import Collection
from typing import Any, Tuple, Union

from openmdao.core.constants import _UNDEFINED
from openmdao.utils.units import convert_units as _convert_units

Units = str
ValueAndUnits = Tuple[Any, Units]
OptionalValueAndUnits = Union[ValueAndUnits, Any]


[docs] class NamedValues(Collection): ''' Define a collection of named values with associated units. '''
[docs] def __init__(self, other=None, **kwargs): ''' Initialize this collection. Notes ----- When initializing from another collection, the following types of collections are supported: * `NamedValues` * `Dict[str, ValueAndUnits]` * `Iterable[Tuple[str, ValueAndUnits]]` When initializing from keyword arguments, the mapped item must be of a type of `ValueAndUnits`. ''' self._mapping = {} self.update(other, **kwargs)
[docs] def get_item(self, key, default=(None, None)) -> OptionalValueAndUnits: ''' Return the named value and its associated units. Note, this method never raises `KeyError` or `TypeError`. Parameters ---------- key : str the name of the item default : OptionalValueAndUnits (None, None) if the item does not exist, return this object Returns ------- OptionalValueAndUnits See Also -------- get_val set_val ''' item = self._mapping.get(key, _UNDEFINED) if item is _UNDEFINED: return default return item
[docs] def copy(self): ''' Return a copy of the instance of this class. Parameters --------- None Returns ------- NamedValues() ''' return copy.copy(self)
[docs] def deepcopy(self): ''' Return a deep copy of the instance of this class. Parameters --------- None Returns ------- NamedValues() ''' return copy.deepcopy(self)
[docs] def get_val(self, key, units='unitless') -> Any: ''' Return the named value in the specified units. Note, requesting a named value that does not exist will raise `KeyError`. Note, specifying units of `None` or units of any type other than `str` will raise `TypeError`. Parameters ---------- key : str the name of the item units : str ('unitless') the units of the returned value Returns ------- val Raises ------ KeyError if the named value does not exist TypeError if units of `None` were specified or units of any type other than `str` ''' self._check_units('get_val', key, units) item = self._mapping.get(key, _UNDEFINED) if item is _UNDEFINED: raise KeyError(f'KeyError: key not found: {key}') val, old_units = item if isinstance(val, tuple): val = tuple(_convert_units(v, old_units, units) for v in val) elif old_units != units: val = _convert_units(val, old_units, units) return val
[docs] def set_val(self, key, val, units='unitless'): ''' Update the named value and its associated units. Note, specifying units of `None` or units of any type other than `str` will raise `Typerror`. Parameters ---------- key : str the name of the item val : Any the new value of the item units : str ('unitless') the units associated with the new value, if any Raises ------ TypeError if units of `None` were specified or units of any type other than `str` ''' self._check_units('set_val', key, units) self._mapping[key] = (val, units)
def __repr__(self): ''' Return a string containing a printable representation of the collection. ''' return repr(self._mapping)
[docs] def clear(self): ''' Remove all items from the collection. ''' self._mapping.clear()
[docs] def update(self, other=None, **kwargs): ''' Assign named values and their associated units found in another collection to this collection, overwriting existing items. If keyword arguments are specified, the collection is then assigned those named values and their associated units, overwriting existing items. Parameters ---------- other (None) a collection of named values and their associated units **kwargs (optional) individual named values and their associated units Notes ----- The following types of collections are supported * `NamedValues` * `Dict[str, ValueAndUnits]` * `Iterable[Tuple[str, ValueAndUnits]]` When assigning from keyword arguments, the mapped item must be of a type of `ValueAndUnits`. ''' if not (other or kwargs): return set_val = self.set_val if isinstance(other, type(self)): # NamedValues other = other._mapping if other is not None: # check for dictionary keys = getattr(other, 'keys', None) if keys is None: # iterable, but not dictionary for key, (val, units) in other: set_val(key, val, units) else: # dictionary for key in keys(): val, units = other[key] set_val(key, val, units) for key, (val, units) in kwargs.items(): set_val(key, val, units)
[docs] def delete(self, key): ''' Remove the named value and its associated units. Raises ------ KeyError if the named value does not exist ''' try: del self._mapping[key] except KeyError: raise KeyError(f'KeyError: key not found: {key}')
def __eq__(self, other): ''' Return whether or not this collection is equivalent to another. ''' collection = self._mapping if isinstance(other, type(self)): return collection == other._mapping return collection == other
[docs] def __contains__(self, key): ''' Return whether or not the named value exists. ''' return key in self._mapping
[docs] def __iter__(self): ''' Return an iterator over the `(key, (val, units))` data stored in this collection. ''' items = self._mapping.items() yield from items
def __len__(self): ''' Return the number of items in this collection. ''' return len(self._mapping) def _check_units(self, funcname, key, units): ''' If units of `None` were specified or units of any type other than `str`, raise `TypeError`. Otherwise, do nothing. Parameters ---------- funcname : str the name of a method/function key : str the name of the item units : Any the units to check ''' if ((units is None) or not isinstance(units, str)): raise TypeError( f'{self.__class__.__name__}: {funcname}({key}):' f' unsupported units: {units}' ) __slots__ = ('_mapping',)
[docs] def get_keys(named_values: NamedValues): ''' Return a new view of the collection's names. ''' return named_values._mapping.keys()
[docs] def get_items(named_values: NamedValues): ''' Return a new view of the collection's `(key, (val, units))`. ''' return named_values._mapping.items()
[docs] def get_values(named_values: NamedValues): ''' Return a new view of the collection's `(val, units)`. ''' return named_values._mapping.values()