Skip to content

Commit 6baeb0b

Browse files
authored
Merge pull request #16 from stephenworsley/general_tidy
Address additional regridding section comments
2 parents e54ebd3 + 89b7887 commit 6baeb0b

File tree

2 files changed

+428
-80
lines changed

2 files changed

+428
-80
lines changed

notebooks/.notebook_shadow_copies/Sec_05_Regridding.md

Lines changed: 201 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ regridder = MeshToGridESMFRegridder(mesh_cube, grid_cube)
5353

5454
```python
5555
# Regrid the mesh cube.
56-
result = regridder(mesh_cube)
57-
result
56+
regridded_orography = regridder(mesh_cube)
57+
regridded_orography
5858
```
5959

6060
The reason this is done in two steps is because initialising a regridder is potentially quite expensive if the grids or meshes involved are large. Once initialised, a regridder can regrid many source cubes (defined on the same source grid/mesh) onto the same target. We can demonstrate this by regridding a different cube using the same regridder.
@@ -73,8 +73,8 @@ mesh_temp
7373
```python
7474
# Regrid the new mesh cube using the same regridder.
7575
# Note how the time coordinate is also transposed in the result.
76-
result_2 = regridder(mesh_temp)
77-
result_2
76+
regridded_temperature = regridder(mesh_temp)
77+
regridded_temperature
7878
```
7979

8080
We can save time in future runs by saving and loading a regridder with `save_regridder` and `load_regridder`.
@@ -104,15 +104,16 @@ iqplt.pcolormesh(grid_temp[0, 0])
104104
plt.gca().coastlines()
105105
plt.show()
106106

107-
iqplt.pcolormesh(result_2[0, 0])
107+
iqplt.pcolormesh(regridded_temperature[0, 0])
108108
plt.gca().coastlines()
109109
plt.show()
110110
```
111111

112112
We can then plot the difference between the UM data and the data regridded from LFRic. Since all our data is now on a latlon grid we can subtract to find the difference between the regridded LFRic data and equivalent UM data and plot this with matplotlib as normal.
113113

114114
```python
115-
temp_diff = result_2 - grid_temp
115+
temp_diff = regridded_temperature - grid_temp
116+
temp_diff.long_name = "Difference in temperature"
116117

117118
# We choose a colormap that makes it clear where the differences are.
118119
iqplt.pcolormesh(temp_diff[0, 0], vmin=-4,vmax=4, cmap="seismic")
@@ -126,47 +127,93 @@ We can also regrid from latlon grids to LFRic style meshes using `GridToMeshESMF
126127
# Initialise the regridder.
127128
g2m_regridder = GridToMeshESMFRegridder(grid_cube, mesh_cube)
128129
# Regrid the grid cube.
129-
result_3 = g2m_regridder(grid_cube)
130-
result_3
130+
orography_on_mesh = g2m_regridder(grid_cube)
131+
orography_on_mesh
131132
```
132133

133134
```python
134135
# Bonus task:
135136
# Use %%timeit to investigate how much time it takes to initialise a regridder vs applying the regridder.
136137
```
137138

139+
<!-- #region -->
138140
## Exercise 1: Comparing regridding methods
139141

140142
By default, regridding uses the area weighted `conservative` method. We can also use the bilinear regridding method.
141143

142144
**Step 1:** Use the `method="bilinear"` keyword to initialise a bilinear `MeshToGridESMFRegridder` with arguments `mesh_cube` and `grid_cube`.
143145

146+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
147+
148+
```python
149+
bilinear_regridder = MeshToGridESMFRegridder(mesh_cube, grid_cube, method="bilinear")
150+
```
151+
</details>
152+
<!-- #endregion -->
153+
144154
```python
145155
bilinear_regridder = MeshToGridESMFRegridder(mesh_cube, grid_cube, method="bilinear")
146156
```
147157

158+
<!-- #region -->
148159
**Step 2:** Use this regridder to regrid `mesh_cube`.
149160

161+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
162+
163+
```python
164+
bilinear_regridded_orography = bilinear_regridder(mesh_cube)
165+
```
166+
</details>
167+
<!-- #endregion -->
168+
150169
```python
151-
bilinear_result = bilinear_regridder(mesh_cube)
170+
bilinear_regridded_orography = bilinear_regridder(mesh_cube)
152171
```
153172

173+
<!-- #region -->
154174
**Step 3:** Compare this result with the result from the default area weighted conservative regridder.
155175

176+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
177+
178+
```python
179+
bilinear_diff = bilinear_regridded_orography - regridded_orography
180+
```
181+
</details>
182+
<!-- #endregion -->
183+
156184
```python
157-
bilinear_diff = bilinear_result - result
185+
bilinear_diff = bilinear_regridded_orography - regridded_orography
158186
```
159187

188+
<!-- #region -->
160189
**Step 4:** Plot the results and the difference using `iris.quickplot` and `matplotlib`.
161190

191+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
192+
162193
```python
163194
import iris.quickplot as iqplt
164195
import matplotlib.pyplot as plt
165196

166-
iqplt.pcolormesh(result, cmap="terrain")
197+
iqplt.pcolormesh(regridded_orography, cmap="terrain")
167198
plt.show()
168-
iqplt.pcolormesh(bilinear_result, cmap="terrain")
199+
iqplt.pcolormesh(bilinear_regridded_orography, cmap="terrain")
169200
plt.show()
201+
bilinear_diff.long_name = "Difference in altitude"
202+
iqplt.pcolormesh(bilinear_diff, vmin=-400,vmax=400, cmap="seismic")
203+
plt.show()
204+
```
205+
</details>
206+
<!-- #endregion -->
207+
208+
```python
209+
import iris.quickplot as iqplt
210+
import matplotlib.pyplot as plt
211+
212+
iqplt.pcolormesh(regridded_orography, cmap="terrain")
213+
plt.show()
214+
iqplt.pcolormesh(bilinear_regridded_orography, cmap="terrain")
215+
plt.show()
216+
bilinear_diff.long_name = "Difference in altitude"
170217
iqplt.pcolormesh(bilinear_diff, vmin=-400,vmax=400, cmap="seismic")
171218
plt.show()
172219
```
@@ -179,6 +226,7 @@ plt.show()
179226
# - demonstrate that the data in the grid file was probably a result of regridding from the mesh file.
180227
```
181228

229+
<!-- #region -->
182230
## Exercise 2: Zonal means
183231

184232
For a latlon cube, a common operation is to collapse over longitude by taking an average. This is not possible for an LFRic style mesh cube since there is no independent longitude dimension to collapse. While it is possible to regrid to a latlon cube and then collapse, this introduces an additional step to the process. Instead, it is possible to simplify this into a single step by considering this as a regridding operation where the target cube contains multiple latitude bands.
@@ -188,6 +236,8 @@ Calculating zonal means can be done as a regridding operation where the zone is
188236

189237
**Step 1:** Define a latitude coordinate whose bounds are `[[-90, -60], [-60, -30], [-30, 0], [0, 30], [30, 60], [60, 90]]`. Remember to set the standard name to be `"latitude"` and the units to be `"degrees"`
190238

239+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
240+
191241
```python
192242
lat_bands = DimCoord(
193243
[-75, -45, -15, 15, 45, 75],
@@ -196,22 +246,55 @@ lat_bands = DimCoord(
196246
units="degrees"
197247
)
198248
```
249+
</details>
250+
<!-- #endregion -->
199251

252+
```python
253+
lat_bands = DimCoord(
254+
[-75, -45, -15, 15, 45, 75],
255+
bounds=[[-90, -60], [-60, -30], [-30, 0], [0, 30], [30, 60], [60, 90]],
256+
standard_name="latitude",
257+
units="degrees"
258+
)
259+
```
260+
261+
<!-- #region -->
200262
**Step 2:** Define a longitude coordinate whose bounds are `[[-180, 180]]`. Remember to set the standard name to be `"longitude"` and the units to be `"degrees"`
201263

264+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
265+
266+
```python
267+
lon_full = DimCoord(0, bounds=[[-180, 180]], standard_name="longitude", units="degrees")
268+
```
269+
</details>
270+
<!-- #endregion -->
271+
202272
```python
203273
lon_full = DimCoord(0, bounds=[[-180, 180]], standard_name="longitude", units="degrees")
204274
```
205275

276+
<!-- #region -->
206277
**Step 3:** Create a six celled cube (i.e. `Cube([[0, 0, 0, 0, 0, 0]])`) and attach the latitude and longitude coordinates to it.
207278

279+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
280+
281+
```python
282+
lat_band_cube = Cube([[0, 0, 0, 0, 0, 0]])
283+
lat_band_cube.add_dim_coord(lat_bands, 1)
284+
lat_band_cube.add_dim_coord(lon_full, 0)
285+
lat_band_cube
286+
```
287+
</details>
288+
<!-- #endregion -->
289+
208290
```python
209291
lat_band_cube = Cube([[0, 0, 0, 0, 0, 0]])
210292
lat_band_cube.add_dim_coord(lat_bands, 1)
211293
lat_band_cube.add_dim_coord(lon_full, 0)
212294
lat_band_cube
213295
```
214296

297+
<!-- #region -->
215298
**Step 4:** Create a regridder from `mesh_cube` to the single celled cube you created.
216299

217300
*Note:* ESMF represents all lines as sections of great circles rather than lines of constant latitude. This means that `MeshToGridESMFRegridder` would fail to properly handle such a large cell. We can solve this problem by using the `resolution` keyword. By providing a `resolution`, we divide each cell into as many sub-cells each bounded by the same latitude bounds.
@@ -220,12 +303,33 @@ If we initialise a regridder with `MeshToGridESMFRegridder(src_mesh, tgt_grid, r
220303

221304
Initialise a `MeshToGridESMFRegridder` with `mesh_cube` and your single celled cube as its arguments and with a `resolution=10` keyword.
222305

306+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
307+
308+
```python
309+
lat_band_mean_calculator_10 = MeshToGridESMFRegridder(mesh_cube, lat_band_cube, resolution=10)
310+
```
311+
</details>
312+
<!-- #endregion -->
313+
223314
```python
224315
lat_band_mean_calculator_10 = MeshToGridESMFRegridder(mesh_cube, lat_band_cube, resolution=10)
225316
```
226317

318+
<!-- #region -->
227319
**Step 5:** Apply this regridder to `mesh_cube` and print the data from this result (i.e. `print(result_cube.data)`) and plot with `iqplt.pcolormesh`.
228320

321+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
322+
323+
```python
324+
lat_band_mean_10 = lat_band_mean_calculator_10(mesh_cube)
325+
print(lat_band_mean_10.data)
326+
iqplt.pcolormesh(lat_band_mean_10)
327+
plt.gca().coastlines()
328+
plt.show()
329+
```
330+
</details>
331+
<!-- #endregion -->
332+
229333
```python
230334
lat_band_mean_10 = lat_band_mean_calculator_10(mesh_cube)
231335
print(lat_band_mean_10.data)
@@ -234,10 +338,25 @@ plt.gca().coastlines()
234338
plt.show()
235339
```
236340

341+
<!-- #region -->
237342
**Step 6:** Repeat step 4 and 5 for `resolution=100`.
238343

239344
Note the difference in value. Also note that it takes more time to initialise a regridder with higher resolution. Higher resolutions ought to be more accurate but there is a tradeoff between performance and accuracy.
240345

346+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
347+
348+
```python
349+
lat_band_mean_calculator_100 = MeshToGridESMFRegridder(mesh_cube, lat_band_cube, resolution=100)
350+
lat_band_mean_100 = lat_band_mean_calculator_100(mesh_cube)
351+
print(lat_band_mean_100.data)
352+
353+
iqplt.pcolormesh(lat_band_mean_100)
354+
plt.gca().coastlines()
355+
plt.show()
356+
```
357+
</details>
358+
<!-- #endregion -->
359+
241360
```python
242361
lat_band_mean_calculator_100 = MeshToGridESMFRegridder(mesh_cube, lat_band_cube, resolution=100)
243362
lat_band_mean_100 = lat_band_mean_calculator_100(mesh_cube)
@@ -248,10 +367,13 @@ plt.gca().coastlines()
248367
plt.show()
249368
```
250369

370+
<!-- #region -->
251371
**Step 7:** Repeat steps 1 - 6 for latitude bounds `[[-90, 90]]`, longitude bounds `[[-40, 40]]` and resolutions 2 and 10.
252372

253373
*Note:* Unlike lines of constant latitude, lines of constant longitude are already great circle arcs.This might suggest that the `resolution` argument is unnnecessary, however these arcs are 180 degrees which ESMF is unable to represent so we still need a `resolution` of at least 2. In this case, an increase in resolution will not affect the accuracy since a resolution of 2 will already have maximum accuracy. Note how the results are the equal.
254374

375+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
376+
255377
```python
256378
lat_full = DimCoord(0, bounds=[[-90, 90]], standard_name="latitude", units="degrees")
257379
lon_band = DimCoord(0, bounds=[[-40, 40]], standard_name="longitude", units="degrees")
@@ -268,22 +390,65 @@ lon_band_mean_calculator_10 = MeshToGridESMFRegridder(mesh_cube, lon_band_cube,
268390
lon_band_mean_10 = lon_band_mean_calculator_10(mesh_cube)
269391
print(lon_band_mean_10.data)
270392
```
393+
</details>
394+
<!-- #endregion -->
395+
396+
```python
397+
lat_full = DimCoord(0, bounds=[[-90, 90]], standard_name="latitude", units="degrees")
398+
lon_band = DimCoord(0, bounds=[[-40, 40]], standard_name="longitude", units="degrees")
271399

400+
lon_band_cube = Cube([[0]])
401+
lon_band_cube.add_dim_coord(lat_full, 0)
402+
lon_band_cube.add_dim_coord(lon_band, 1)
403+
404+
lon_band_mean_calculator_2 = MeshToGridESMFRegridder(mesh_cube, lon_band_cube, resolution=2)
405+
lon_band_mean_2 = lon_band_mean_calculator_2(mesh_cube)
406+
print(lon_band_mean_2.data)
407+
408+
lon_band_mean_calculator_10 = MeshToGridESMFRegridder(mesh_cube, lon_band_cube, resolution=10)
409+
lon_band_mean_10 = lon_band_mean_calculator_10(mesh_cube)
410+
print(lon_band_mean_10.data)
411+
```
412+
413+
<!-- #region -->
272414
## Exercise 3: Hovmoller plots
273415

274416
If we have data on aditional dimensions, we can use the same approach as exercise 2 to produce a Hovmoller diagram. That is, if we have data that varies along time we can take the area weighted mean over latitude bands and plot the data aginst latitude and time (or similarly, we can plot against longitude and time).
275417

276418
**Step 1:** Load a cube with humidity data using the `testdata_fetching` function `lfric_rh_alltimes_3d`.
277419

420+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
421+
278422
```python
279423
from testdata_fetching import lfric_rh_alltimes_3d
280424

281425
humidity_cube = lfric_rh_alltimes_3d()
282426
humidity_cube
283427
```
428+
</details>
429+
<!-- #endregion -->
284430

431+
```python
432+
from testdata_fetching import lfric_rh_alltimes_3d
433+
434+
humidity_cube = lfric_rh_alltimes_3d()
435+
humidity_cube
436+
```
437+
438+
<!-- #region -->
285439
**Step 2:** Create a target cube whose latitude coordinate is derived from the UM cube loaded from `um_orography` and whose longitude coordinate has bounds `[[-180, 180]]`. This can be done by slicing a cube derived from `um_orography` (using the slice `[:, :1]` so that this dimension isnt collapsed), removing the longitude coordinate and adding a longitude coordinate with bounds `[[-180, 180]]` (you can reuse the coordinate from exercise 2).
286440

441+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
442+
443+
```python
444+
target_cube_lats = grid_cube[:,:1]
445+
target_cube_lats.remove_coord("longitude")
446+
target_cube_lats.add_dim_coord(lon_full, 1)
447+
target_cube_lats
448+
```
449+
</details>
450+
<!-- #endregion -->
451+
287452
```python
288453
target_cube_lats = grid_cube[:,:1]
289454
target_cube_lats.remove_coord("longitude")
@@ -300,8 +465,19 @@ target_cube_lats
300465
# target_cube_lons
301466
```
302467

468+
<!-- #region -->
303469
**Step 3:** Create a `MeshToGridESMFRegridder` regridder from the slice of the humidity cube onto the target cube. Set the resolution keyword to 500 (this should be good balance of accuracy and performance). Use this regridder to create a resulting cube.
304470

471+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
472+
473+
```python
474+
um_lat_band_mean_calculator = MeshToGridESMFRegridder(humidity_cube, target_cube_lats, resolution=500)
475+
um_lat_band_means = um_lat_band_mean_calculator(humidity_cube)
476+
um_lat_band_means
477+
```
478+
</details>
479+
<!-- #endregion -->
480+
305481
```python
306482
um_lat_band_mean_calculator = MeshToGridESMFRegridder(humidity_cube, target_cube_lats, resolution=500)
307483
um_lat_band_means = um_lat_band_mean_calculator(humidity_cube)
@@ -318,8 +494,21 @@ um_lat_band_means
318494
# um_lon_band_means
319495
```
320496

497+
<!-- #region -->
321498
**Step 4:** Plot the data in the resulting cube. This can be done with `iqplt.pcolormesh`. Note that the resulting cube will have an unnecessary dimension which will have to be sliced (using `[:, :, 0]`). Note that extra steps can be taken to format the dates for this plot.
322499

500+
<details><summary>Sample code solution : <b>click to reveal</b></summary>
501+
502+
```python
503+
import matplotlib.dates as mdates
504+
# We use a colormap which highlights fine details.
505+
iqplt.pcolormesh(um_lat_band_means[:, :, 0])
506+
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%D"))
507+
plt.show()
508+
```
509+
</details>
510+
<!-- #endregion -->
511+
323512
```python
324513
import matplotlib.dates as mdates
325514
# We use a colormap which highlights fine details.

0 commit comments

Comments
 (0)