-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathunits.py
More file actions
125 lines (105 loc) · 4.08 KB
/
Copy pathunits.py
File metadata and controls
125 lines (105 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""Module to provide unit support via pint approximating UDUNITS/CF."""
import functools
import re
import pint
from packaging.version import Version
from .utils import emit_user_level_warning
@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 {"dimensionless": 1} for non-shortened dimensionless units
# CF uses "1" to denote fractions and dimensionless quantities
if unit == {"dimensionless": 1} or not unit:
return "1"
# 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()
}
)
# 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),
)
out = pint.formatter(*args, as_ratio=False, product_fmt=" ", power_fmt="{}{}")
# To avoid potentiel unicode problems in netCDF. In both cases, this unit is not recognized by udunits
return out.replace("Δ°", "delta_deg")
# ------
# Reused with modification from MetPy under the terms of the BSD 3-Clause License.
# Copyright (c) 2015,2017,2019 MetPy Developers.
# Create registry, with preprocessors for UDUNITS-style powers (m2 s-2) and percent signs
units: pint.UnitRegistry = pint.UnitRegistry(
autoconvert_offset_to_baseunit=True,
preprocessors=[
functools.partial(
re.compile(
r"(?<=[A-Za-z])(?![A-Za-z])(?<![0-9\-][eE])(?<![0-9\-])(?=[0-9\-])"
).sub,
"**",
),
lambda string: string.replace("%", "percent"),
],
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
units.define("@alias meter = gpm")
# ----- end block copied from metpy
# -----
# The following redefinitions were copied from xclim under the terms of their Apache-2 license
# In pint, the default symbol for year is "a" which is not CF-compliant (stands for "are")
units.define("year = 365.25 * day = yr")
# Define commonly encountered units not defined by pint
units.define("@alias degC = C = deg_C = Celsius = degrees_Celsius")
units.define("@alias degK = deg_K")
units.define("@alias day = d")
units.define("@alias hour = h") # Not the Planck constant...
units.define(
"degrees_north = degree = degrees_north = degrees_N = degreesN = degree_north = degree_N = degreeN"
)
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
# Add other specific aliases (by cf_xarray developers)
units.define("practical_salinity_unit = [] = psu = PSU")
# Enable pint's built-in matplotlib support
try:
units.setup_matplotlib()
except ImportError:
emit_user_level_warning(
"Import(s) unavailable to set up matplotlib support...skipping this portion "
"of the setup.",
UserWarning,
)
# end of vendored code from MetPy
# Set as application registry
pint.set_application_registry(units)