Skip to content

Commit 0e3fe5a

Browse files
authored
Merge pull request #2395 from jsiirola/units-checking-roundoff
Allow relative tolerance when comparing unit dimentionality
2 parents c650d28 + f3c2389 commit 0e3fe5a

File tree

2 files changed

+39
-3
lines changed

2 files changed

+39
-3
lines changed

pyomo/core/base/units_container.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -1082,22 +1082,32 @@ def __getattr__(self, item):
10821082
# float(conv_offset))
10831083
# self._pint_registry.define(defn_str)
10841084

1085+
def _rel_diff(self, a, b):
1086+
scale = min(abs(a), abs(b))
1087+
if scale < 1.:
1088+
scale = 1.
1089+
return abs(a - b) / scale
1090+
10851091
def _equivalent_pint_units(self, a, b, TOL=1e-12):
10861092
if a is b or a == b:
10871093
return True
10881094
base_a = self._pint_registry.get_base_units(a)
10891095
base_b = self._pint_registry.get_base_units(b)
10901096
if base_a[1] != base_b[1]:
1091-
return False
1092-
return abs(base_a[0] - base_b[0]) / min(base_a[0], base_b[0]) <= TOL
1097+
uc_a = base_a[1].dimensionality
1098+
uc_b = base_b[1].dimensionality
1099+
for key in uc_a.keys() | uc_b.keys():
1100+
if self._rel_diff(uc_a.get(key, 0), uc_b.get(key, 0)) >= TOL:
1101+
return False
1102+
return self._rel_diff(base_a[0], base_b[0]) <= TOL
10931103

10941104
def _equivalent_to_dimensionless(self, a, TOL=1e-12):
10951105
if a is self._pint_dimensionless or a == self._pint_dimensionless:
10961106
return True
10971107
base_a = self._pint_registry.get_base_units(a)
10981108
if not base_a[1].dimensionless:
10991109
return False
1100-
return abs(base_a[0] - 1.) / min(base_a[0], 1.) <= TOL
1110+
return self._rel_diff(base_a[0], 1.) <= TOL
11011111

11021112
def _get_pint_units(self, expr):
11031113
"""

pyomo/util/tests/test_check_units.py

+26
Original file line numberDiff line numberDiff line change
@@ -198,5 +198,31 @@ def arcrule(m):
198198

199199
assert_units_consistent(m)
200200

201+
def test_units_roundoff_error(self):
202+
# Issue 2393: this example resulted in roundoff error where the
203+
# computed units for var_1 were
204+
#(0.9999999999999986,
205+
# <Unit('kilogram ** 1 / kelvin / meter ** 1.66533e-16 / second ** 3')>
206+
#)
207+
m = ConcreteModel()
208+
m.var_1 = Var(
209+
initialize=400,
210+
units=((units.J**0.4) *
211+
(units.kg**0.2) *
212+
(units.W**0.6) /
213+
units.K /
214+
(units.m**2.2) /
215+
(units.Pa**0.2) /
216+
(units.s**0.8))
217+
)
218+
m.var_1.fix()
219+
m.var_2 = Var(
220+
initialize=400,
221+
units=units.kg/units.s**3/units.K
222+
)
223+
assert_units_equivalent(m.var_1, m.var_2)
224+
225+
226+
201227
if __name__ == "__main__":
202228
unittest.main()

0 commit comments

Comments
 (0)