pythonpint

Parse temperature string in pint


I was trying to parse a string with a temperature and pint fails due to the non-multiplicative nature of the temperature. However, it seems the internal conversion should handle this:

from pint import Quantity
Quantity("6degC")

Produces

---------------------------------------------------------------------------
OffsetUnitCalculusError                   Traceback (most recent call last)
Cell In[6], line 1
----> 1 Quantity("6degC")

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/quantity.py:199, in PlainQuantity.__new__(cls, value, units)
    197 if units is None and isinstance(value, str):
    198     ureg = SharedRegistryObject.__new__(cls)._REGISTRY
--> 199     inst = ureg.parse_expression(value)
    200     return cls.__new__(cls, inst)
    202 if units is None and isinstance(value, cls):

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/registry.py:1398, in GenericPlainRegistry.parse_expression(self, input_string, case_sensitive, **values)
   1395 def _define_op(s: str):
   1396     return self._eval_token(s, case_sensitive=case_sensitive, **values)
-> 1398 return build_eval_tree(gen).evaluate(_define_op)

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/pint_eval.py:382, in EvalTreeNode.evaluate(self, define_op, bin_op, un_op)
    379     if op_text not in bin_op:
    380         raise DefinitionSyntaxError(f"missing binary operator '{op_text}'")
--> 382     return bin_op[op_text](
    383         self.left.evaluate(define_op, bin_op, un_op),
    384         self.right.evaluate(define_op, bin_op, un_op),
    385     )
    386 elif self.operator:
    387     assert isinstance(self.left, EvalTreeNode), "self.left not EvalTreeNode (4)"

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/quantity.py:1018, in PlainQuantity.__mul__(self, other)
   1017 def __mul__(self, other):
-> 1018     return self._mul_div(other, operator.mul)

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/quantity.py:101, in check_implemented.<locals>.wrapped(self, *args, **kwargs)
     99 elif isinstance(other, list) and other and isinstance(other[0], type(self)):
    100     return NotImplemented
--> 101 return f(self, *args, **kwargs)

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/quantity.py:75, in ireduce_dimensions.<locals>.wrapped(self, *args, **kwargs)
     74 def wrapped(self, *args, **kwargs):
---> 75     result = f(self, *args, **kwargs)
     76     try:
     77         if result._REGISTRY.autoconvert_to_preferred:

File ~/virtual_enviroments/cfd-2d/lib/python3.10/site-packages/pint/facets/plain/quantity.py:966, in PlainQuantity._mul_div(self, other, magnitude_op, units_op)
    964 if not self._check(other):
    965     if not self._ok_for_muldiv(no_offset_units_self):
--> 966         raise OffsetUnitCalculusError(self._units, getattr(other, "units", ""))
    967     if len(offset_units_self) == 1:
    968         if self._units[offset_units_self[0]] != 1 or magnitude_op not in (
    969             operator.mul,
    970             operator.imul,
    971         ):

OffsetUnitCalculusError: Ambiguous operation with offset unit (degree_Celsius). See https://pint.readthedocs.io/en/stable/user/nonmult.html for guidance.

However this works

Quantity(6, "degC")
<Quantity(6, 'degree_Celsius')>

So this should be the parsing path taken internally. Or am I seeing this wrongly?


Solution

  • Pint since version 0.6 no longer supports that syntax. Units with offset must be explicitly created eg Quantity(6, "degC") instead of Quantity("6degC")

    To get the behaviour you want, do

    from pint import UnitRegistry
    ureg = UnitRegistry(autoconvert_offset_to_baseunit=True)
    
    ureg.Quantity("6degC")
    

    6 degree_Celsius

    I like how it was explained here