Coverage for src/CSET/operators/wind.py: 100%
17 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-05 21:08 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-05 21:08 +0000
1# © Crown copyright, Met Office (2022-2025) and CSET contributors.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""Operators to calculate various forms or properties of wind."""
17import iris
18import iris.cube
19import numpy as np
21from CSET._common import iter_maybe
24def convert_to_beaufort_scale(
25 cubes: iris.cube.Cube | iris.cube.CubeList,
26) -> iris.cube.Cube | iris.cube.CubeList:
27 r"""Convert windspeed from m/s to the Beaufort Scale.
29 Arguments
30 ---------
31 cubes: iris.cube.Cube | iris.cube.CubeList
32 Cubes of windspeed to be converted.
33 Required: `wind_speed_at_10m`.
35 Returns
36 -------
37 iris.cube.Cube | iris.cube.CubeList
38 Converted windspeed.
40 Notes
41 -----
42 The relationship used to convert the windspeed from m/s to the Beaufort
43 Scale is an empirical relationship (e.g., [Beer96]_):
45 .. math:: F = (\frac{v}{0.836})^{2/3}
47 for F the Beaufort Force, and v the windspeed at 10 m in m/s.
49 The Beaufort Scale was devised in 1805 by Rear Admiral Sir Francis Beaufort.
50 It is a widely used windscale that categorises the winds into forces and provides
51 human-understable names (e.g. gale). The table below shows the Beaufort Scale based
52 on the Handbook of Meteorology ([Berryetal45]_).
54 .. list-table:: Beaufort Scale
55 :widths: 5 20 10 10 10
56 :header-rows: 1
58 * - Force [1]
59 - Descriptor
60 - Windspeed [m/s]
61 - Windspeed [kn]
62 - Windspeed [mph]
63 * - 0
64 - Calm
65 - < 0.4
66 - < 1
67 - < 1
68 * - 1
69 - Light Air
70 - 0.4 - 1.5
71 - 1 - 3
72 - 1 - 3
73 * - 2
74 - Light Breeze
75 - 1.6 - 3.3
76 - 4 - 6
77 - 4 - 7
78 * - 3
79 - Gentle Breeze
80 - 3.4 - 5.4
81 - 7 - 10
82 - 8 - 12
83 * - 4
84 - Moderate Breeze
85 - 5.5 - 7.9
86 - 11 - 16
87 - 13 - 18
88 * - 5
89 - Fresh Breeze
90 - 8.0 - 10.7
91 - 17 - 21
92 - 19 - 24
93 * - 6
94 - Strong Breeze
95 - 10.8 - 13.8
96 - 22 - 27
97 - 25 - 31
98 * - 7
99 - Near Gale
100 - 13.9 - 17.1
101 - 28 - 33
102 - 32 - 38
103 * - 8
104 - Gale
105 - 17.2 - 20.7
106 - 34 - 40
107 - 39 - 46
108 * - 9
109 - Strong Gale
110 - 20.8 - 24.4
111 - 41 - 47
112 - 47 - 54
113 * - 10
114 - Storm
115 - 24.5 - 28.4
116 - 48 - 55
117 - 55 - 63
118 * - 11
119 - Violent Storm
120 - 28.5 - 33.5
121 - 56 - 63
122 - 64 - 73
123 * - 12 (+)
124 - Hurricane
125 - > 33.6
126 - > 64
127 - > 74
129 The modern names have been used in this table. However, it should be noted
130 for historical accuracy that Force 7 was originally "Moderate Gale", Force 8
131 was originally "Fresh Gale", Force 10 was originally "Whole Gale", and
132 Force 11 was originally "Storm". Force 9 can also be referred to as
133 "Severe Gale". Furthermore, it should be noted that there is an extended
134 Beaufort Scale, sometimes used for tropical cyclones. Hence, why values
135 can reach above 12 in this diagnostic. However, these are not referred to
136 in the table as anything above F12 is labelled as Hurricane force.
138 References
139 ----------
140 .. [Beer96] Beer, T. (1996) Environmental Oceanography, CRC Marince Science,
141 Vol. 11, 2nd Edition, CRC Press, 402 pp.
142 .. [Berryetal45] Berry, F. A., Jr., E. Bollay, and N. R. Beers, (1945) Handbook
143 of Meteorology. McGraw Hill, 1068 pp.
145 Examples
146 --------
147 >>> Beaufort_Scale=wind.convert_to_Beaufort_scale(winds)
148 """
149 # Create and empty cubelist.
150 winds = iris.cube.CubeList([])
151 # Loop over cubelist.
152 for cube in iter_maybe(cubes):
153 # Copy cube so we do not overwrite data.
154 wind_cube = cube.copy()
155 # Divide data by 0.836.
156 wind_cube /= 0.836
157 # Raise to power of 2/3 to produce decimal Beaufort Scale.
158 wind_cube.data **= 2.0 / 3.0
159 # Round using even round (i.e. to nearest even number).
160 wind_cube.data = np.round(wind_cube.data)
161 # Convert units.
162 wind_cube.units = "1"
163 # Rename cube.
164 wind_cube.rename(f"{cube.name()}_on_Beaufort_Scale")
165 winds.append(wind_cube)
166 # Output as single cube or cubelist depending on if cube of cubelist given
167 # as input.
168 if len(winds) == 1:
169 return winds[0]
170 else:
171 return winds