Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix to_reduced_units #1417

Merged
merged 1 commit into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Pint Changelog
- Fix setting options of the application registry (Issue #1403).
- Fix Quantity & Unit `is_compatible_with` with registry active contexts (Issue #1424).
- Allow Quantity to parse 'NaN' and 'inf(inity)', case insensitive
- Fix casting error when using to_reduced_units with array of int.
(Issue #1184)


0.18 (2021-10-26)
-----------------
Expand Down
46 changes: 28 additions & 18 deletions pint/quantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,21 @@ def to_base_units(self) -> Quantity[_MagnitudeType]:

return self.__class__(magnitude, other)

def _get_reduced_units(self, units):
# loop through individual units and compare to each other unit
# can we do better than a nested loop here?
for unit1, exp in units.items():
# make sure it wasn't already reduced to zero exponent on prior pass
if unit1 not in units:
continue
for unit2 in units:
if unit1 != unit2:
power = self._REGISTRY._get_dimensionality_ratio(unit1, unit2)
if power:
units = units.add(unit2, exp / power).remove([unit1])
break
return units

def ito_reduced_units(self) -> None:
"""Return Quantity scaled in place to reduced units, i.e. one unit per
dimension. This will not reduce compound units (e.g., 'J/kg' will not
Expand All @@ -775,32 +790,27 @@ def ito_reduced_units(self) -> None:
if len(self._units) == 1:
return None

newunits = self._units.copy()
# loop through individual units and compare to each other unit
# can we do better than a nested loop here?
for unit1, exp in self._units.items():
# make sure it wasn't already reduced to zero exponent on prior pass
if unit1 not in newunits:
continue
for unit2 in newunits:
if unit1 != unit2:
power = self._REGISTRY._get_dimensionality_ratio(unit1, unit2)
if power:
newunits = newunits.add(unit2, exp / power).remove([unit1])
break
units = self._units.copy()
new_units = self._get_reduced_units(units)

return self.ito(newunits)
return self.ito(new_units)

def to_reduced_units(self) -> Quantity[_MagnitudeType]:
"""Return Quantity scaled in place to reduced units, i.e. one unit per
dimension. This will not reduce compound units (intentionally), nor
can it make use of contexts at this time.
"""

# can we make this more efficient?
newq = copy.copy(self)
newq.ito_reduced_units()
return newq
# shortcuts in case we're dimensionless or only a single unit
if self.dimensionless:
return self.ito({})
if len(self._units) == 1:
return None

units = self._units.copy()
new_units = self._get_reduced_units(units)

return self.to(new_units)

def to_compact(self, unit=None) -> Quantity[_MagnitudeType]:
""" "Return Quantity rescaled to compact, human-readable units.
Expand Down
7 changes: 7 additions & 0 deletions pint/testsuite/test_quantity.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,13 @@ def test_nan(self, nan_str, has_unit):
assert math.isnan(test.magnitude)
assert ref != test

@helpers.requires_numpy
def test_to_reduced_units(self):
q = self.Q_([3, 4], "s * ms")
helpers.assert_quantity_equal(
q.to_reduced_units(), self.Q_([3000.0, 4000.0], "ms**2")
)


class TestQuantityToCompact(QuantityTestCase):
def assertQuantityAlmostIdentical(self, q1, q2):
Expand Down