From 6d6acc940419ccd7ceaa856b29184434a9eb24bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jules=20Ch=C3=A9ron?= Date: Thu, 18 Nov 2021 22:57:05 +0100 Subject: [PATCH] Fix #1184 to_reduced with ndarray of int Do not use ito with to_reduced function --- CHANGES | 3 +++ pint/quantity.py | 46 ++++++++++++++++++++------------- pint/testsuite/test_quantity.py | 7 +++++ 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/CHANGES b/CHANGES index 0c221417b..64d523394 100644 --- a/CHANGES +++ b/CHANGES @@ -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) ----------------- diff --git a/pint/quantity.py b/pint/quantity.py index f11c79040..138e17aa6 100644 --- a/pint/quantity.py +++ b/pint/quantity.py @@ -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 @@ -775,21 +790,10 @@ 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 @@ -797,10 +801,16 @@ def to_reduced_units(self) -> Quantity[_MagnitudeType]: 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. diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py index 8f68262b4..b5413f4c1 100644 --- a/pint/testsuite/test_quantity.py +++ b/pint/testsuite/test_quantity.py @@ -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):