Merged systems into UnitRegistry

This commit is contained in:
Hernan Grecco 2015-07-21 18:34:34 -03:00
parent 5218647d54
commit f9729fa802
4 changed files with 195 additions and 148 deletions

View File

@ -174,13 +174,13 @@ class _Quantity(SharedRegistryObject):
def unitless(self): def unitless(self):
"""Return true if the quantity does not have units. """Return true if the quantity does not have units.
""" """
return not bool(self.to_base_units()._units) return not bool(self.to_root_units()._units)
@property @property
def dimensionless(self): def dimensionless(self):
"""Return true if the quantity is dimensionless. """Return true if the quantity is dimensionless.
""" """
tmp = self.to_base_units() tmp = self.to_root_units()
return not bool(tmp.dimensionality) return not bool(tmp.dimensionality)
@ -243,6 +243,26 @@ class _Quantity(SharedRegistryObject):
return self.__class__(magnitude, other) return self.__class__(magnitude, other)
def ito_root_units(self):
"""Return Quantity rescaled to base units
"""
_, other = self._REGISTRY._get_root_units(self._units)
self._magnitude = self._convert_magnitude(other)
self._units = other
return None
def to_root_units(self):
"""Return Quantity rescaled to base units
"""
_, other = self._REGISTRY._get_root_units(self._units)
magnitude = self._convert_magnitude_not_inplace(other)
return self.__class__(magnitude, other)
def ito_base_units(self): def ito_base_units(self):
"""Return Quantity rescaled to base units """Return Quantity rescaled to base units
""" """
@ -263,6 +283,7 @@ class _Quantity(SharedRegistryObject):
return self.__class__(magnitude, other) return self.__class__(magnitude, other)
def to_compact(self, unit=None): def to_compact(self, unit=None):
"""Return Quantity rescaled to compact, human-readable units. """Return Quantity rescaled to compact, human-readable units.
@ -603,14 +624,14 @@ class _Quantity(SharedRegistryObject):
if not self._ok_for_muldiv(no_offset_units_self): if not self._ok_for_muldiv(no_offset_units_self):
raise OffsetUnitCalculusError(self._units, other._units) raise OffsetUnitCalculusError(self._units, other._units)
elif no_offset_units_self == 1 and len(self._units) == 1: elif no_offset_units_self == 1 and len(self._units) == 1:
self.ito_base_units() self.ito_root_units()
no_offset_units_other = len(other._get_non_multiplicative_units()) no_offset_units_other = len(other._get_non_multiplicative_units())
if not other._ok_for_muldiv(no_offset_units_other): if not other._ok_for_muldiv(no_offset_units_other):
raise OffsetUnitCalculusError(self._units, other._units) raise OffsetUnitCalculusError(self._units, other._units)
elif no_offset_units_other == 1 and len(other._units) == 1: elif no_offset_units_other == 1 and len(other._units) == 1:
other.ito_base_units() other.ito_root_units()
self._magnitude = magnitude_op(self._magnitude, other._magnitude) self._magnitude = magnitude_op(self._magnitude, other._magnitude)
self._units = units_op(self._units, other._units) self._units = units_op(self._units, other._units)
@ -665,14 +686,14 @@ class _Quantity(SharedRegistryObject):
if not self._ok_for_muldiv(no_offset_units_self): if not self._ok_for_muldiv(no_offset_units_self):
raise OffsetUnitCalculusError(self._units, other._units) raise OffsetUnitCalculusError(self._units, other._units)
elif no_offset_units_self == 1 and len(self._units) == 1: elif no_offset_units_self == 1 and len(self._units) == 1:
new_self = self.to_base_units() new_self = self.to_root_units()
no_offset_units_other = len(other._get_non_multiplicative_units()) no_offset_units_other = len(other._get_non_multiplicative_units())
if not other._ok_for_muldiv(no_offset_units_other): if not other._ok_for_muldiv(no_offset_units_other):
raise OffsetUnitCalculusError(self._units, other._units) raise OffsetUnitCalculusError(self._units, other._units)
elif no_offset_units_other == 1 and len(other._units) == 1: elif no_offset_units_other == 1 and len(other._units) == 1:
other = other.to_base_units() other = other.to_root_units()
magnitude = magnitude_op(new_self._magnitude, other._magnitude) magnitude = magnitude_op(new_self._magnitude, other._magnitude)
units = units_op(new_self._units, other._units) units = units_op(new_self._units, other._units)
@ -718,7 +739,7 @@ class _Quantity(SharedRegistryObject):
if not self._ok_for_muldiv(no_offset_units_self): if not self._ok_for_muldiv(no_offset_units_self):
raise OffsetUnitCalculusError(self._units, '') raise OffsetUnitCalculusError(self._units, '')
elif no_offset_units_self == 1 and len(self._units) == 1: elif no_offset_units_self == 1 and len(self._units) == 1:
self = self.to_base_units() self = self.to_root_units()
return self.__class__(other_magnitude / self._magnitude, 1 / self._units) return self.__class__(other_magnitude / self._magnitude, 1 / self._units)
@ -732,7 +753,7 @@ class _Quantity(SharedRegistryObject):
if not self._ok_for_muldiv(no_offset_units_self): if not self._ok_for_muldiv(no_offset_units_self):
raise OffsetUnitCalculusError(self._units, '') raise OffsetUnitCalculusError(self._units, '')
elif no_offset_units_self == 1 and len(self._units) == 1: elif no_offset_units_self == 1 and len(self._units) == 1:
self = self.to_base_units() self = self.to_root_units()
return self.__class__(other_magnitude // self._magnitude, 1 / self._units) return self.__class__(other_magnitude // self._magnitude, 1 / self._units)
@ -803,12 +824,12 @@ class _Quantity(SharedRegistryObject):
else: else:
if not self._is_multiplicative: if not self._is_multiplicative:
if self._REGISTRY.autoconvert_offset_to_baseunit: if self._REGISTRY.autoconvert_offset_to_baseunit:
new_self = self.to_base_units() new_self = self.to_root_units()
else: else:
raise OffsetUnitCalculusError(self._units) raise OffsetUnitCalculusError(self._units)
if getattr(other, 'dimensionless', False): if getattr(other, 'dimensionless', False):
units = new_self._units ** other.to_base_units().magnitude units = new_self._units ** other.to_root_units().magnitude
elif not getattr(other, 'dimensionless', True): elif not getattr(other, 'dimensionless', True):
raise DimensionalityError(self._units, 'dimensionless') raise DimensionalityError(self._units, 'dimensionless')
else: else:
@ -828,7 +849,7 @@ class _Quantity(SharedRegistryObject):
if isinstance(self._magnitude, ndarray): if isinstance(self._magnitude, ndarray):
if np.size(self._magnitude) > 1: if np.size(self._magnitude) > 1:
raise DimensionalityError(self._units, 'dimensionless') raise DimensionalityError(self._units, 'dimensionless')
new_self = self.to_base_units() new_self = self.to_root_units()
return other**new_self._magnitude return other**new_self._magnitude
def __abs__(self): def __abs__(self):
@ -880,8 +901,8 @@ class _Quantity(SharedRegistryObject):
if self.dimensionality != other.dimensionality: if self.dimensionality != other.dimensionality:
raise DimensionalityError(self._units, other._units, raise DimensionalityError(self._units, other._units,
self.dimensionality, other.dimensionality) self.dimensionality, other.dimensionality)
return op(self.to_base_units().magnitude, return op(self.to_root_units().magnitude,
other.to_base_units().magnitude) other.to_root_units().magnitude)
__lt__ = lambda self, other: self.compare(other, op=operator.lt) __lt__ = lambda self, other: self.compare(other, op=operator.lt)
__le__ = lambda self, other: self.compare(other, op=operator.le) __le__ = lambda self, other: self.compare(other, op=operator.le)
@ -1089,9 +1110,9 @@ class _Quantity(SharedRegistryObject):
try: try:
if isinstance(value, self.__class__): if isinstance(value, self.__class__):
factor = self.__class__(value.magnitude, value._units / self._units).to_base_units() factor = self.__class__(value.magnitude, value._units / self._units).to_root_units()
else: else:
factor = self.__class__(value, self._units ** (-1)).to_base_units() factor = self.__class__(value, self._units ** (-1)).to_root_units()
if isinstance(factor, self.__class__): if isinstance(factor, self.__class__):
if not factor.dimensionless: if not factor.dimensionless:
@ -1170,7 +1191,7 @@ class _Quantity(SharedRegistryObject):
if unt == 'radian': if unt == 'radian':
mobjs.append(getattr(other, 'magnitude', other)) mobjs.append(getattr(other, 'magnitude', other))
else: else:
factor, units = self._REGISTRY._get_base_units(unt) factor, units = self._REGISTRY._get_root_units(unt)
if units and units != UnitsContainer({'radian': 1}): if units and units != UnitsContainer({'radian': 1}):
raise DimensionalityError(units, dst_units) raise DimensionalityError(units, dst_units)
mobjs.append(getattr(other, 'magnitude', other) * factor) mobjs.append(getattr(other, 'magnitude', other) * factor)

View File

@ -370,108 +370,48 @@ class System(object):
return system return system
#: These dictionaries will be part of the registry class GSManager(object):
#: :type: dict[str, Group | System]
_groups_systems = dict()
_root_group = Group('root', _groups_systems)
def __init__(self):
#: :type: dict[str, Group | System]
self._groups_systems = dict()
self._root_group = Group('root', self._groups_systems)
# These methods will be included in the registry, upgrading the existing ones. def get_group(self, name, create_if_needed=True):
"""Return a Group.
def get_group(registry, name, create_if_needed=True): :param name: Name of the group.
"""Return a Group. :param create_if_needed: Create a group if not Found. If False, raise an Exception.
:return: Group
"""
try:
return self._groups_systems[name]
except KeyError:
if create_if_needed:
if name == 'root':
raise ValueError('The name root is reserved.')
return Group(name, self._groups_systems)
else:
raise KeyError('No group %s found.' % name)
:param registry: def get_system(self, name, create_if_needed=True):
:param name: Name of the group to be """Return a Group.
:param create_if_needed: Create a group if not Found. If False, raise an Exception.
:return: Group
"""
if name == 'root':
raise ValueError('The name root is reserved.')
try: :param name: Name of the system
return _groups_systems[name] :param create_if_needed: Create a group if not Found. If False, raise an Exception.
except KeyError: :return: System
if create_if_needed: """
return Group(name, _groups_systems)
else:
raise KeyError('No group %s found.' % name)
try:
return self._groups_systems[name]
except KeyError:
if create_if_needed:
return System(name, self._groups_systems)
else:
raise KeyError('No system found named %s.' % name)
def get_system(registry, name, create_if_needed=True): def __getitem__(self, item):
"""Return a Group. if item in self._groups_systems:
return self._groups_systems[item]
:param registry: raise KeyError('No group or system found named %s.' % item)
:param name: Name of the group to be
:param create_if_needed: Create a group if not Found. If False, raise an Exception.
:return: System
"""
try:
return _groups_systems[name]
except KeyError:
if create_if_needed:
return System(name, _groups_systems)
else:
raise KeyError('No system %s found.' % name)
def get_compatible_units(registry, input_units, group_or_system=None):
"""
:param registry:
:param input_units:
:param group_or_system:
:type group_or_system: Group | System
:return:
"""
ret = registry.get_compatible_units(input_units)
if not group_or_system:
return ret
members = _groups_systems[group_or_system].members
# This will not be necessary after integration with the registry as it has a strings intermediate
members = frozenset((getattr(registry, member) for member in members))
return ret.intersection(members)
# Current get_base_units will be renamed to get_root_units
#
# Not sure yet how to deal with the cache.
# Should we cache get_root_units, get_base_units or both?
# - get_base_units will need to be invalidated when the system is changed (How often will this happen?)
# - get_root_units will not need to be invalidated.
def get_base_units(registry, input_units, check_nonmult=True, system=None):
"""
:param registry:
:param input_units:
:param check_nonmult:
:param system: System
:return:
"""
factor, units = registry.get_base_units(input_units, check_nonmult)
if not system:
return factor, units
# This will not be necessary after integration with the registry as it has a UnitsContainer intermediate
units = to_units_container(units, registry)
destination_units = UnitsContainer()
bu = _groups_systems[system].base_units
for unit, value in units.items():
if unit in bu:
new_unit = bu[unit]
new_unit = to_units_container(new_unit, registry)
destination_units *= new_unit ** value
else:
destination_units *= UnitsContainer({unit: value})
base_factor = registry.convert(factor, units, destination_units)
return base_factor, destination_units

View File

@ -4,7 +4,7 @@ from __future__ import division, unicode_literals, print_function, absolute_impo
from pint import UnitRegistry from pint import UnitRegistry
from pint.systems import Group, get_compatible_units, get_group, System, get_base_units from pint.systems import Group, System
from pint.testsuite import QuantityTestCase from pint.testsuite import QuantityTestCase
@ -187,9 +187,9 @@ class TestGroup(QuantityTestCase):
def test_get_compatible_units(self): def test_get_compatible_units(self):
ureg = UnitRegistry() ureg = UnitRegistry()
g = get_group(ureg, 'imperial') g = ureg.get_group('imperial')
g.add_units('inch', 'yard', 'pint') g.add_units('inch', 'yard', 'pint')
c = get_compatible_units(ureg, 'meter', 'imperial') c = ureg.get_compatible_units('meter', 'imperial')
self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) self.assertEqual(c, frozenset([ureg.inch, ureg.yard]))
@ -238,10 +238,10 @@ class TestSystem(QuantityTestCase):
sysname = 'mysys1' sysname = 'mysys1'
ureg = UnitRegistry() ureg = UnitRegistry()
g = get_group(ureg, 'imperial') g = ureg.get_group('imperial')
g.add_units('inch', 'yard', 'pint') g.add_units('inch', 'yard', 'pint')
c = get_compatible_units(ureg, 'meter', 'imperial') c = ureg.get_compatible_units('meter', 'imperial')
self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) self.assertEqual(c, frozenset([ureg.inch, ureg.yard]))
lines = ['@system %s using imperial' % sysname, lines = ['@system %s using imperial' % sysname,
@ -249,7 +249,7 @@ class TestSystem(QuantityTestCase):
] ]
s = System.from_lines(lines, lambda x: x, g._groups_systems) s = System.from_lines(lines, lambda x: x, g._groups_systems)
c = get_compatible_units(ureg, 'meter', sysname) c = ureg.get_compatible_units('meter', sysname)
self.assertEqual(c, frozenset([ureg.inch, ureg.yard])) self.assertEqual(c, frozenset([ureg.inch, ureg.yard]))
def test_get_base_units(self): def test_get_base_units(self):
@ -257,7 +257,7 @@ class TestSystem(QuantityTestCase):
ureg = UnitRegistry() ureg = UnitRegistry()
g = get_group(ureg, 'imperial') g = ureg.get_group('imperial')
g.add_units('inch', 'yard', 'pint') g.add_units('inch', 'yard', 'pint')
lines = ['@system %s using imperial' % sysname, lines = ['@system %s using imperial' % sysname,
@ -267,11 +267,11 @@ class TestSystem(QuantityTestCase):
s = System.from_lines(lines, ureg.get_base_units, g._groups_systems) s = System.from_lines(lines, ureg.get_base_units, g._groups_systems)
# base_factor, destination_units # base_factor, destination_units
c = get_base_units(ureg, 'inch', system=sysname) c = ureg.get_base_units('inch', system=sysname)
self.assertAlmostEqual(c[0], 1) self.assertAlmostEqual(c[0], 1)
self.assertEqual(c[1], {'inch': 1}) self.assertEqual(c[1], {'inch': 1})
c = get_base_units(ureg, 'cm', system=sysname) c = ureg.get_base_units('cm', system=sysname)
self.assertAlmostEqual(c[0], 1./2.54) self.assertAlmostEqual(c[0], 1./2.54)
self.assertEqual(c[1], {'inch': 1}) self.assertEqual(c[1], {'inch': 1})
@ -280,9 +280,9 @@ class TestSystem(QuantityTestCase):
ureg = UnitRegistry() ureg = UnitRegistry()
g = get_group(ureg, 'imperial') g = ureg.get_group('imperial')
g.add_units('inch', 'yard', 'pint') g.add_units('inch', 'yard', 'pint')
c = get_compatible_units(ureg, 'meter', 'imperial') c = ureg.get_compatible_units('meter', 'imperial')
lines = ['@system %s using imperial' % sysname, lines = ['@system %s using imperial' % sysname,
'pint:meter', 'pint:meter',
@ -291,19 +291,19 @@ class TestSystem(QuantityTestCase):
s = System.from_lines(lines, ureg.get_base_units, g._groups_systems) s = System.from_lines(lines, ureg.get_base_units, g._groups_systems)
# base_factor, destination_units # base_factor, destination_units
c = get_base_units(ureg, 'inch', system=sysname) c = ureg.get_base_units('inch', system=sysname)
self.assertAlmostEqual(c[0], 0.326, places=3) self.assertAlmostEqual(c[0], 0.326, places=3)
self.assertEqual(c[1], {'pint': 1./3}) self.assertEqual(c[1], {'pint': 1./3})
c = get_base_units(ureg, 'cm', system=sysname) c = ureg.get_base_units('cm', system=sysname)
self.assertAlmostEqual(c[0], 0.1283, places=3) self.assertAlmostEqual(c[0], 0.1283, places=3)
self.assertEqual(c[1], {'pint': 1./3}) self.assertEqual(c[1], {'pint': 1./3})
c = get_base_units(ureg, 'inch**2', system=sysname) c = ureg.get_base_units('inch**2', system=sysname)
self.assertAlmostEqual(c[0], 0.326**2, places=3) self.assertAlmostEqual(c[0], 0.326**2, places=3)
self.assertEqual(c[1], {'pint': 2./3}) self.assertEqual(c[1], {'pint': 2./3})
c = get_base_units(ureg, 'cm**2', system=sysname) c = ureg.get_base_units('cm**2', system=sysname)
self.assertAlmostEqual(c[0], 0.1283**2, places=3) self.assertAlmostEqual(c[0], 0.1283**2, places=3)
self.assertEqual(c[1], {'pint': 2./3}) self.assertEqual(c[1], {'pint': 2./3})
@ -312,7 +312,7 @@ class TestSystem(QuantityTestCase):
ureg = UnitRegistry() ureg = UnitRegistry()
g = get_group(ureg, 'imperial') g = ureg.get_group('imperial')
g.add_units('inch', 'yard', 'pint') g.add_units('inch', 'yard', 'pint')
lines = ['@system %s using imperial' % sysname, lines = ['@system %s using imperial' % sysname,
@ -322,10 +322,10 @@ class TestSystem(QuantityTestCase):
s = System.from_lines(lines, ureg.get_base_units, g._groups_systems) s = System.from_lines(lines, ureg.get_base_units, g._groups_systems)
# base_factor, destination_units # base_factor, destination_units
c = get_base_units(ureg, 'inch', system=sysname) c = ureg.get_base_units('inch', system=sysname)
self.assertAlmostEqual(c[0], 0.0568, places=3) self.assertAlmostEqual(c[0], 0.0568, places=3)
self.assertEqual(c[1], {'mph': 1, 'second': 1}) self.assertEqual(c[1], {'mph': 1, 'second': 1})
c = get_base_units(ureg, 'kph', system=sysname) c = ureg.get_base_units('kph', system=sysname)
self.assertAlmostEqual(c[0], .6214, places=4) self.assertAlmostEqual(c[0], .6214, places=4)
self.assertEqual(c[1], {'mph': 1}) self.assertEqual(c[1], {'mph': 1})

View File

@ -37,6 +37,7 @@ from .errors import (DimensionalityError, UndefinedUnitError,
DefinitionSyntaxError, RedefinitionError) DefinitionSyntaxError, RedefinitionError)
from .pint_eval import build_eval_tree from .pint_eval import build_eval_tree
from . import systems
class _Unit(SharedRegistryObject): class _Unit(SharedRegistryObject):
"""Implements a class to describe a unit supporting math operations. """Implements a class to describe a unit supporting math operations.
@ -265,6 +266,9 @@ class UnitRegistry(object):
#: Map dimension name (string) to its definition (DimensionDefinition). #: Map dimension name (string) to its definition (DimensionDefinition).
self._dimensions = {} self._dimensions = {}
#: :type: systems.GSManager
self._gsmanager = systems.GSManager()
#: Map unit name (string) to its definition (UnitDefinition). #: Map unit name (string) to its definition (UnitDefinition).
#: Might contain prefixed units. #: Might contain prefixed units.
self._units = {} self._units = {}
@ -289,6 +293,9 @@ class UnitRegistry(object):
#: Maps dimensionality (UnitsContainer) to Units (str) #: Maps dimensionality (UnitsContainer) to Units (str)
self._dimensional_equivalents = dict() self._dimensional_equivalents = dict()
#: Maps dimensionality (UnitsContainer) to Dimensionality (UnitsContainer)
self._root_units_cache = dict()
#: Maps dimensionality (UnitsContainer) to Dimensionality (UnitsContainer) #: Maps dimensionality (UnitsContainer) to Dimensionality (UnitsContainer)
self._base_units_cache = dict() self._base_units_cache = dict()
@ -344,6 +351,25 @@ class UnitRegistry(object):
self.Unit.default_format = value self.Unit.default_format = value
self.Quantity.default_format = value self.Quantity.default_format = value
def get_group(self, name, create_if_needed=True):
"""Return a Group.
:param name: Name of the group to be
:param create_if_needed: Create a group if not Found. If False, raise an Exception.
:return: Group
"""
return self._gsmanager.get_group(name, create_if_needed)
def get_system(self, name, create_if_needed=True):
"""Return a Group.
:param registry:
:param name: Name of the group to be
:param create_if_needed: Create a group if not Found. If False, raise an Exception.
:return: System
"""
return self._gsmanager.get_system(name, create_if_needed)
def add_context(self, context): def add_context(self, context):
"""Add a context object to the registry. """Add a context object to the registry.
@ -611,10 +637,10 @@ class UnitRegistry(object):
try: try:
uc = ParserHelper.from_word(unit_name) uc = ParserHelper.from_word(unit_name)
bu = self._get_base_units(uc) bu = self._get_root_units(uc)
di = self._get_dimensionality(uc) di = self._get_dimensionality(uc)
self._base_units_cache[uc] = bu self._root_units_cache[uc] = bu
self._dimensionality_cache[uc] = di self._dimensionality_cache[uc] = di
if not prefixed: if not prefixed:
@ -726,8 +752,8 @@ class UnitRegistry(object):
if reg.reference is not None: if reg.reference is not None:
self._get_dimensionality_recurse(reg.reference, exp2, accumulator) self._get_dimensionality_recurse(reg.reference, exp2, accumulator)
def get_base_units(self, input_units, check_nonmult=True): def get_root_units(self, input_units, check_nonmult=True):
"""Convert unit or dict of units to the base units. """Convert unit or dict of units to the root units.
If any unit is non multiplicative and check_converter is True, If any unit is non multiplicative and check_converter is True,
then None is returned as the multiplicative factor. then None is returned as the multiplicative factor.
@ -741,12 +767,12 @@ class UnitRegistry(object):
""" """
input_units = to_units_container(input_units) input_units = to_units_container(input_units)
f, units = self._get_base_units(input_units, check_nonmult=True) f, units = self._get_root_units(input_units, check_nonmult)
return f, self.Unit(units) return f, self.Unit(units)
def _get_base_units(self, input_units, check_nonmult=True): def _get_root_units(self, input_units, check_nonmult=True):
"""Convert unit or dict of units to the base units. """Convert unit or dict of units to the root units.
If any unit is non multiplicative and check_converter is True, If any unit is non multiplicative and check_converter is True,
then None is returned as the multiplicative factor. then None is returned as the multiplicative factor.
@ -762,11 +788,11 @@ class UnitRegistry(object):
return 1., UnitsContainer() return 1., UnitsContainer()
# The cache is only done for check_nonmult=True # The cache is only done for check_nonmult=True
if check_nonmult and input_units in self._base_units_cache: if check_nonmult and input_units in self._root_units_cache:
return self._base_units_cache[input_units] return self._root_units_cache[input_units]
accumulators = [1., defaultdict(float)] accumulators = [1., defaultdict(float)]
self._get_base_units_recurse(input_units, 1.0, accumulators) self._get_root_units_recurse(input_units, 1.0, accumulators)
factor = accumulators[0] factor = accumulators[0]
units = UnitsContainer(dict((k, v) for k, v in accumulators[1].items() units = UnitsContainer(dict((k, v) for k, v in accumulators[1].items()
@ -780,7 +806,63 @@ class UnitRegistry(object):
return factor, units return factor, units
def _get_base_units_recurse(self, ref, exp, accumulators): def get_base_units(self, input_units, check_nonmult=True, system=None):
"""Convert unit or dict of units to the base units.
If any unit is non multiplicative and check_converter is True,
then None is returned as the multiplicative factor.
:param input_units: units
:type input_units: UnitsContainer or str
:param check_nonmult: if True, None will be returned as the
multiplicative factor if a non-multiplicative
units is found in the final Units.
:return: multiplicative factor, base units
"""
input_units = to_units_container(input_units)
f, units = self._get_base_units(input_units, check_nonmult, system)
return f, self.Unit(units)
def _get_base_units(self, input_units, check_nonmult=True, system=None):
"""
:param registry:
:param input_units:
:param check_nonmult:
:param system: System
:return:
"""
# The cache is only done for check_nonmult=True
if check_nonmult and input_units in self._base_units_cache:
return self._base_units_cache[input_units]
factor, units = self.get_root_units(input_units, check_nonmult)
if not system:
return factor, units
# This will not be necessary after integration with the registry as it has a UnitsContainer intermediate
units = to_units_container(units, self)
destination_units = UnitsContainer()
bu = self._gsmanager.get_system(system, False).base_units
for unit, value in units.items():
if unit in bu:
new_unit = bu[unit]
new_unit = to_units_container(new_unit, self)
destination_units *= new_unit ** value
else:
destination_units *= UnitsContainer({unit: value})
base_factor = self.convert(factor, units, destination_units)
return base_factor, destination_units
def _get_root_units_recurse(self, ref, exp, accumulators):
for key in ref: for key in ref:
exp2 = exp*ref[key] exp2 = exp*ref[key]
key = self.get_name(key) key = self.get_name(key)
@ -790,19 +872,19 @@ class UnitRegistry(object):
else: else:
accumulators[0] *= reg._converter.scale ** exp2 accumulators[0] *= reg._converter.scale ** exp2
if reg.reference is not None: if reg.reference is not None:
self._get_base_units_recurse(reg.reference, exp2, self._get_root_units_recurse(reg.reference, exp2,
accumulators) accumulators)
def get_compatible_units(self, input_units): def get_compatible_units(self, input_units, group_or_system=None):
""" """
""" """
input_units = to_units_container(input_units) input_units = to_units_container(input_units)
equiv = self._get_compatible_units(input_units) equiv = self._get_compatible_units(input_units, group_or_system)
return frozenset(self.Unit(eq) for eq in equiv) return frozenset(self.Unit(eq) for eq in equiv)
def _get_compatible_units(self, input_units): def _get_compatible_units(self, input_units, group_or_system=None):
""" """
""" """
if not input_units: if not input_units:
@ -819,7 +901,11 @@ class UnitRegistry(object):
for node in nodes: for node in nodes:
ret |= self._dimensional_equivalents[node] ret |= self._dimensional_equivalents[node]
return frozenset(ret) if group_or_system:
members = self._gsmanager[group_or_system].members
return frozenset(ret.intersection(members))
return ret
def convert(self, value, src, dst, inplace=False): def convert(self, value, src, dst, inplace=False):
"""Convert value from some source to destination units. """Convert value from some source to destination units.
@ -927,7 +1013,7 @@ class UnitRegistry(object):
# Here src and dst have only multiplicative units left. Thus we can # Here src and dst have only multiplicative units left. Thus we can
# convert with a factor. # convert with a factor.
factor, units = self._get_base_units(src / dst) factor, units = self._get_root_units(src / dst)
# factor is type float and if our magnitude is type Decimal then # factor is type float and if our magnitude is type Decimal then
# must first convert to Decimal before we can '*' the values # must first convert to Decimal before we can '*' the values