|
2 | 2 | # Distributed under the terms of the BSD 3-Clause License. |
3 | 3 | # SPDX-License-Identifier: BSD-3-Clause |
4 | 4 | """ |
5 | | -============== |
6 | | -Units Tutorial |
7 | | -============== |
| 5 | +================== |
| 6 | +Working With Units |
| 7 | +================== |
8 | 8 |
|
9 | 9 | Early in our scientific careers we all learn about the importance of paying |
10 | 10 | attention to units in our calculations. Unit conversions can still get the best |
11 | 11 | of us and have caused more than one major technical disaster, including the |
12 | 12 | crash and complete loss of the $327 million Mars Climate Orbiter. |
13 | 13 |
|
14 | | -In MetPy, we use the pint library and a custom unit registry to help prevent |
| 14 | +In MetPy, we use the :mod:`pint` library and a custom unit registry to help prevent |
15 | 15 | unit mistakes in calculations. That means that every quantity you pass to MetPy |
16 | 16 | should have units attached, just like if you were doing the calculation on |
17 | | -paper! |
18 | | -
|
19 | | -In MetPy units are attached by multiplying them with the integer, float, array, |
20 | | -etc. In this tutorial we'll show some examples of working with units and get |
21 | | -you on your way to utilizing the computation functions in MetPy. |
| 17 | +paper! This simplifies the MetPy API by eliminating the need to specify units |
| 18 | +various functions. Instead, only the final results need to be converted to desired units. For |
| 19 | +more information on unit support, see the documentation for |
| 20 | +`Pint <http://pint.readthedocs.io>`_. Particular attention should be paid to the support |
| 21 | +for `temperature units <https://pint.readthedocs.io/en/latest/nonmult.html>`_. |
| 22 | +
|
| 23 | +In this tutorial we'll show some examples of working with units and get you on your way to |
| 24 | +utilizing the computation functions in MetPy. |
22 | 25 | """ |
23 | 26 |
|
| 27 | +######################################################################### |
| 28 | +# Getting Started |
| 29 | +# --------------- |
| 30 | +# To use units, the first step is to import the default MetPy units registry from the |
| 31 | +# :mod:`~metpy.units` module: |
| 32 | + |
24 | 33 | import numpy as np |
25 | 34 |
|
26 | 35 | from metpy.units import units |
27 | 36 |
|
28 | 37 | ######################################################################### |
29 | | -# Simple Calculation |
30 | | -# ------------------ |
| 38 | +# The unit registry encapsulates all of the available units, as well as any pertinent settings. |
| 39 | +# The registry also understands unit prefixes and suffixes; this allows the registry to |
| 40 | +# understand ``'kilometer'`` and ``'meters'`` in addition to the base ``'meter'`` unit. |
| 41 | +# |
| 42 | +# In general, using units is only a small step on top of using the :class:`numpy.ndarray` |
| 43 | +# object. |
| 44 | +# |
| 45 | +# Adding Units to Data |
| 46 | +# -------------------- |
| 47 | +# The easiest way to attach units to an array (or integer, float, etc.) is to multiply by the |
| 48 | +# units: |
| 49 | + |
| 50 | +distance = np.arange(1, 5) * units.meters |
| 51 | + |
| 52 | +######################################################################### |
| 53 | +# It is also possible to directly construct a :class:`pint.Quantity`, with a full units string: |
| 54 | + |
| 55 | +time = units.Quantity(np.arange(2, 10, 2), 'sec') |
| 56 | + |
| 57 | +######################################################################### |
| 58 | +# Compound units can be constructed by the direct mathematical operations necessary: |
| 59 | + |
| 60 | +g = 9.81 * units.meter / (units.second * units.second) |
| 61 | + |
| 62 | +######################################################################### |
| 63 | +# This verbose syntax can be reduced by using the unit registry's support for parsing units: |
| 64 | + |
| 65 | +g = 9.81 * units('m/s^2') |
| 66 | + |
| 67 | +######################################################################### |
| 68 | +# Operations With Units |
| 69 | +# --------------------- |
| 70 | +# With units attached, it is possible to perform mathematical operations, resulting in the |
| 71 | +# proper units: |
| 72 | + |
| 73 | +print(distance / time) |
| 74 | + |
| 75 | +######################################################################### |
| 76 | +# For multiplication and division, units can combine and cancel. For addition and subtraction, |
| 77 | +# instead the operands must have compatible units. For instance, this works: |
| 78 | + |
| 79 | +print(distance + distance) |
| 80 | + |
| 81 | +######################################################################### |
| 82 | +# But for instance, `distance + time` would not work; instead it gives an error: |
| 83 | +# |
| 84 | +# `DimensionalityError: Cannot convert from 'meter' ([length]) to 'second' ([time])` |
31 | 85 | # |
32 | | -# Let's say we want to calculate the area of a rectangle. It so happens that |
33 | | -# one of our colleagues measures their side of the rectangle in imperial units |
34 | | -# and the other in metric units. No problem! First we need to attach units to |
35 | | -# our measurements. For many units the easiest way is by find the unit as an |
36 | | -# attribute of the unit registry: |
| 86 | +# Even if the units are not identical, as long as they are dimensionally equivalent, the |
| 87 | +# operation can be performed: |
37 | 88 |
|
38 | | -length = 10.4 * units.inches |
39 | | -width = 20 * units.meters |
40 | | -print(length, width) |
| 89 | +print(3 * units.inch + 5 * units.cm) |
41 | 90 |
|
42 | 91 | ######################################################################### |
43 | | -# Don't forget that you can use tab completion to see what units are available! |
44 | | -# Just about every imaginable quantity is there, but if you find one that isn't, |
45 | | -# we're happy to talk about adding it. |
| 92 | +# Converting Units |
| 93 | +# ---------------- |
46 | 94 | # |
47 | | -# While it may seem like a lot of trouble, let's compute the area of a rectangle |
48 | | -# defined by our length and width variables above. Without units attached, you'd |
49 | | -# need to remember to perform a unit conversion before multiplying or you would |
50 | | -# end up with an area in inch-meters and likely forget about it. With units |
51 | | -# attached, the units are tracked for you. |
| 95 | +# Converting a :class:`~pint.Quantity` between units can be accomplished by using the |
| 96 | +# :meth:`~pint.Quantity.to` method call, which constructs a new :class:`~pint.Quantity` in the |
| 97 | +# desired units: |
52 | 98 |
|
53 | | -area = length * width |
54 | | -print(area) |
| 99 | +print((1 * units.inch).to(units.mm)) |
55 | 100 |
|
56 | 101 | ######################################################################### |
57 | | -# That's great, now we have an area, but it is not in a very useful unit still. |
58 | | -# Units can be converted using the `to()` method. While you won't see square meters in |
59 | | -# the units list, we can parse complex/compound units as strings: |
| 102 | +# There is also the :meth:`~pint.Quantity.ito` method which performs the same operation |
| 103 | +# in-place: |
60 | 104 |
|
| 105 | +a = np.arange(5.) * units.meter |
| 106 | +a.ito('feet') |
| 107 | +print(a) |
| 108 | + |
| 109 | +######################################################################### |
| 110 | +# To simplify units, there is also the :meth:`~pint.Quantity.to_base_units` method, |
| 111 | +# which converts a quantity to SI units, performing any needed cancellation: |
| 112 | + |
| 113 | +Lf = 3.34e6 * units('J/kg') |
| 114 | +print(Lf, Lf.to_base_units(), sep='\n') |
| 115 | + |
| 116 | +######################################################################### |
| 117 | +# :meth:`~pint.Quantity.to_base_units` can also be done in-place via the |
| 118 | +# :meth:`~pint.Quantity.ito_base_units` method. |
| 119 | +# |
| 120 | +# By default Pint does not do any more than simple unit simplification, so when you perform |
| 121 | +# operations you could get some seemingly odd results: |
| 122 | + |
| 123 | +l = 10.4 * units.inch |
| 124 | +w = 5 * units.cm |
| 125 | +area = l * w |
| 126 | +print(area) |
| 127 | + |
| 128 | +######################################################################### |
| 129 | +# This is another place where :meth:`~pint.Quantity.to` comes in handy: |
61 | 130 | print(area.to('m^2')) |
62 | 131 |
|
63 | 132 | ######################################################################### |
|
103 | 172 | print(273 * units.kelvin - 10 * units.kelvin) |
104 | 173 |
|
105 | 174 | ######################################################################### |
| 175 | +# MetPy Calculations |
| 176 | +# ------------------ |
| 177 | +# All MetPy calculations are unit-aware and rely on this information to ensure |
| 178 | +# that the calculations operate correctly. For example, we can use units to take |
| 179 | +# an observation in whatever units are most convenient and let MetPy handle everything |
| 180 | +# under the hood. Below we calculate dewpoint from the temperature and relative humidity: |
106 | 181 |
|
107 | | -######################################################################### |
108 | | -# Compound Units |
109 | | -# -------------- |
110 | | -# We can create compound units for things like speed by parsing a string of |
111 | | -# units. Abbreviations or full unit names are acceptable. |
| 182 | +import metpy.calc as mpcalc |
| 183 | + |
| 184 | +temperature = 73.2 * units.degF |
| 185 | +rh = 64 * units.percent |
| 186 | +dewpoint = mpcalc.dewpoint_from_relative_humidity(temperature, rh) |
112 | 187 |
|
113 | | -u = np.random.randint(0, 15, 10) * units('m/s') |
114 | | -v = np.random.randint(0, 15, 10) * units('meters/second') |
| 188 | +print(dewpoint) |
| 189 | + |
| 190 | +######################################################################### |
| 191 | +# or back to Fahrenheit: |
115 | 192 |
|
116 | | -print(u) |
117 | | -print(v) |
| 193 | +print(dewpoint.to('degF')) |
118 | 194 |
|
119 | 195 | ######################################################################### |
120 | 196 | # Common Mistakes |
121 | 197 | # --------------- |
122 | 198 | # There are a few common mistakes the new users often make. Be sure to check |
123 | | -# these when you're having issues |
| 199 | +# these when you're having issues. |
124 | 200 | # |
125 | 201 | # * Pressure units are `mbar` or `hPa` for common atmospheric measurements. The |
126 | | -# unit `mb` is actually millibarns. |
| 202 | +# unit `mb` is actually millibarns--a unit used in particle physics. |
127 | 203 | # * When using masked arrays, units must be multiplied on the left side. This |
128 | 204 | # will be addressed in the future, but is a current limitation in the |
129 | 205 | # ecosystem. The expected error will be |
130 | | -# `AttributeError: 'MaskedArray' object has no attribute 'units'` |
| 206 | +# `AttributeError: 'MaskedArray' object has no attribute 'units'` or calculation |
| 207 | +# functions complaining about expecting a units and getting "dimensionless". |
0 commit comments