Skip to content

Commit

Permalink
copy fix for cf_units from xarray-contrib/cf-xarray#523
Browse files Browse the repository at this point in the history
  • Loading branch information
MuellerSeb committed Jul 1, 2024
1 parent 87fb450 commit b2c9d3d
Showing 1 changed file with 56 additions and 47 deletions.
103 changes: 56 additions & 47 deletions src/finam/data/cf_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,60 +3,60 @@
Source: cf-xarray (https://github.com/xarray-contrib/cf-xarray/blob/main/cf_xarray/units.py)
License: Apache License 2.0 (https://github.com/xarray-contrib/cf-xarray/blob/main/LICENSE)
"""

import functools
import re
import warnings

import pint
from packaging.version import Version


@pint.register_unit_format("cf")
def short_formatter(unit, registry, **options):
"""Return a CF-compliant unit string from a `pint` unit.
Parameters
----------
unit : pint.UnitContainer
Input unit.
registry : pint.UnitRegistry
The associated registry
**options
Additional options (may be ignored)
Returns
-------
out : str
Units following CF-Convention, using symbols.
"""
# pint 0.24.1 gives this for dimensionless units
if unit == {"dimensionless": 1}:
return ""

# If u is a name, get its symbol (same as pint's "~" pre-formatter)
# otherwise, assume a symbol (pint should have already raised on invalid units before this)
unit = pint.util.UnitsContainer(
{
registry._get_symbol(u) if u in registry._units else u: exp
for u, exp in unit.items()
}
)

# pylint: disable-next=unused-import
from pint import DimensionalityError, UndefinedUnitError, UnitStrippedWarning

# from `xclim`'s unit support module with permission of the maintainers
try:
# Change in formatter signature in pint 0.24
if Version(pint.__version__) < Version("0.24"):
args = (unit.items(),)
else:
# Numerators splitted from denominators
args = (
((u, e) for u, e in unit.items() if e >= 0),
((u, e) for u, e in unit.items() if e < 0),
)

@pint.register_unit_format("cf") # pylint: disable-next=unused-argument
def short_formatter(unit, registry, **options):
"""Return a CF-compliant unit string from a :mod:`pint` unit.
Parameters
----------
unit : pint.UnitContainer
Input unit.
registry : pint.UnitRegistry
the associated registry
**options
Additional options (may be ignored)
Returns
-------
out : str
Units following CF-Convention, using symbols.
"""

# convert UnitContainer back to Unit
unit = registry.Unit(unit)
# Print units using abbreviations (millimeter -> mm)
s = f"{unit:~D}"

# Search and replace patterns
pat = r"(?P<inverse>(?:1 )?/ )?(?P<unit>\w+)(?: \*\* (?P<pow>\d))?"

def repl(m):
i, u, p = m.groups()
p = p or (1 if i else "")
neg = "-" if i else ""

return f"{u}{neg}{p}"

out, _n = re.subn(pat, repl, s)

# Remove multiplications
out = out.replace(" * ", " ")
# Delta degrees:
out = out.replace("Δ°", "delta_deg")
return out.replace("percent", "%")
out = pint.formatter(*args, as_ratio=False, product_fmt=" ", power_fmt="{}{}")
# To avoid potentiel unicode problems in netCDF. In both case, this unit is not recognized by udunits
return out.replace("Δ°", "delta_deg")

except ImportError:
pass

# ------
# Reused with modification from MetPy under the terms of the BSD 3-Clause License.
Expand All @@ -75,7 +75,13 @@ def repl(m):
],
force_ndarray_like=True,
)
# ----- end block copied from metpy

# need to insert to make sure this is the first preprocessor
# This ensures we convert integer `1` to string `"1"`, as needed by pint.
units.preprocessors.insert(0, str)

# -----
units.define("percent = 0.01 = %")

# Define commonly encountered units (both CF and non-CF) not defined by pint
Expand All @@ -98,6 +104,8 @@ def repl(m):
units.define(
"degrees_east = degree = degrees_east = degrees_E = degreesE = degree_east = degree_E = degreeE"
)
# degrees for grid_longitude / grid_latitude for grid_mappings
units.define("degrees = degree = degrees")
units.define("[speed] = [length] / [time]")
# ----- end block copied from xclim

Expand All @@ -110,7 +118,8 @@ def repl(m):
except ImportError:
warnings.warn(
"Import(s) unavailable to set up matplotlib support...skipping this portion "
"of the setup."
"of the setup.",
UserWarning,
)
# end of vendored code from MetPy

Expand Down

0 comments on commit b2c9d3d

Please sign in to comment.