3

How do I test for unit equivalence in Pint? For example, nM is equivalent to nmol/L and L is equivalent to dm^3, but they are not equal according to Pint. I don't want compatibility, which Pint provides via the is_compatible_with method. For example, s is compatible with ms, but they are not equivalent.

import pint
ureg = pint.UnitRegistry()

nM = ureg.Unit('nM')
nmol_L = ureg.Unit('nmol/L')
m = ureg.Unit('m')
ft = ureg.Unit('ft')

nM == nmol_L  # False
m == ft  # False

nM.is_compatible_with(nmol_L)  # True
m.is_compatible_with(ft)  # True

# What operation does this?
# nM equivalent to nmol  # Should be True
# m equivalent to ft  # Should be False
drhagen
  • 8,331
  • 8
  • 53
  • 82

2 Answers2

0

One workaround is to convert the units to quantities, take their ratio, drop it to base units, and then test that the quantity is dimensionless and its quantity is equal to 1. Note that due to rounding error, you can't just do a regular equals; you need to do an approximately equals and hope you never encounter any units are almost exactly the same, but not quite.

from math import isclose
import pint
ureg = pint.UnitRegistry()

nM = ureg.Unit('nM')
nmol_L = ureg.Unit('nmol/L')
m = ureg.Unit('m')
ft = ureg.Unit('ft')

def is_equivalent(first: pint.Unit, second: pint.Unit):
    ratio = ((1 * first) / (1 * second)).to_base_units()
    return ratio.dimensionless and isclose(ratio.magnitude, 1)

is_equivalent(nM, nmol_L)  # True
is_equivalent(m, ft)  # False
drhagen
  • 8,331
  • 8
  • 53
  • 82
0

One workaround is to use the UnitRegistry.convert(number, current_unit, new_unit) method. If you try to convert a 1 from one unit to another, the result will still be 1 if the units are equivalent. It will raise a DimensionalityError error if the units are not compatible. Note that due to rounding error, you can't just do a regular equals on the 1s; you need to do an approximately equals and hope you never encounter any units are almost exactly the same, but not quite.

from math import isclose
import pint
ureg = pint.UnitRegistry()

nM = ureg.Unit('nM')
nmol_L = ureg.Unit('nmol/L')
m = ureg.Unit('m')
ft = ureg.Unit('ft')

def is_equivalent(first: pint.Unit, second: pint.Unit):
    try:
        factor = ureg.convert(1, first, second)
    except pint.DimensionalityError:
        return False
    return isclose(factor, 1)

is_equivalent(nM, nmol_L)  # True
is_equivalent(m, ft)  # False

Note that it is not documented that it is legal to supply a pint.Unit to convert.

drhagen
  • 8,331
  • 8
  • 53
  • 82