"""
Unit handling
"""
import warnings
from typing import Optional, Sequence, Union
import numpy as np
from openscm_units import ScmUnitRegistry
_unit_registry = ScmUnitRegistry()
"""
SCMData standard unit registry
The unit registry contains all of the recognised units.
"""
_unit_registry.add_standards()
[docs]class UnitConverter:
"""
Converts numbers between two units.
"""
def __init__(self, source: str, target: str, context: Optional[str] = None):
"""
Initialize.
Parameters
----------
source
Unit to convert **from**
target
Unit to convert **to**
context
Context to use for the conversion i.e. which metric to apply when performing
CO2-equivalent calculations. If ``None``, no metric will be applied and
CO2-equivalent calculations will raise :class:`DimensionalityError`.
Raises
------
pint.errors.DimensionalityError
Units cannot be converted into each other.
pint.errors.UndefinedUnitError
Unit undefined.
"""
self._source = source
self._target = target
source_unit = _unit_registry.Unit(source)
target_unit = _unit_registry.Unit(target)
s1 = _unit_registry.Quantity(1, source_unit)
s2 = _unit_registry.Quantity(-1, source_unit)
if context is None:
t1 = s1.to(target_unit)
t2 = s2.to(target_unit)
else:
with _unit_registry.context(context):
t1 = s1.to(target_unit)
t2 = s2.to(target_unit)
if np.isnan(t1) or np.isnan(t2):
warn_msg = (
"No conversion from {} to {} available, nan will be returned "
"upon conversion".format(source, target)
)
warnings.warn(warn_msg)
self._scaling = float(t2.m - t1.m) / float(s2.m - s1.m)
self._offset = t1.m - self._scaling * s1.m
[docs] def convert_from(self, v: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""
Convert value **from** source unit to target unit.
Parameters
----------
value
Value in source unit
Returns
-------
Union[float, np.ndarray]
Value in target unit
"""
return self._offset + v * self._scaling
[docs] def convert_to(self, v: Union[float, np.ndarray]) -> Union[float, np.ndarray]:
"""
Convert value from target unit **to** source unit.
Parameters
----------
value
Value in target unit
Returns
-------
Union[float, np.ndarray]
Value in source unit
"""
return (v - self._offset) / self._scaling
@property
def contexts(self) -> Sequence[str]:
"""
Available contexts for unit conversions
"""
return list(_unit_registry._contexts.keys()) # pylint: disable=protected-access
@property
def unit_registry(self) -> ScmUnitRegistry:
"""
Underlying unit registry
"""
return _unit_registry
@property
def source(self) -> str:
"""
Source unit
"""
return self._source
@property
def target(self) -> str:
"""
Target unit
"""
return self._target