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 aviary.utils.utils import wrapped_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.""" __slots__ = ('_mapping',)
[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) -> OptionalValueAndUnits: """ Return the named value and its associated units. Parameters ---------- key : str the name of the item Returns ------- OptionalValueAndUnits Raises ------ KeyError if the named value does not exist See Also -------- get_val set_val """ item = self._mapping.get(key, _UNDEFINED) if item is _UNDEFINED: raise KeyError(f'key not found: {key}') 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. Parameters ---------- key : str the name of the item units : str the units of the returned value in OpenMDAO format (e.g. "unitless") Returns ------- val value of `key`, converted to requested `units` 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'key not found: {key}') val, old_units = item if old_units != units: val = wrapped_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)
[docs] def keys(self): """Return a new view of the collection's names.""" return self._mapping.keys()
[docs] def items(self): """Return a new view of the collection's `(key, (val, units))`.""" return self._mapping.items()
[docs] def values(self): """Return a new view of the collection's `(val, units)`.""" return self._mapping.values()
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'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}): unsupported units: {units}' )
[docs] def get_keys(named_values: NamedValues): raise DeprecationWarning( 'The get_keys() function has been replaced with the NamedValues.keys() method to better ' 'mirror dictionary functionality.' )
[docs] def get_items(named_values: NamedValues): raise DeprecationWarning( 'The get_items() function has been replaced with the NamedValues.items() method to better ' 'mirror dictionary functionality.' )
[docs] def get_values(named_values: NamedValues): raise DeprecationWarning( 'The get_values() function has been replaced with the NamedValues.values() method to better ' 'mirror dictionary functionality.' )