MPAS unstructured grid data

MPAS unstructured grid data#

In this example, we demonstrate CE identification with unstructured-grid data from MPAS and compare to the results using the regridded dataset.

import warnings

import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import xarray as xr
from matplotlib.tri import Triangulation

import tams

warnings.filterwarnings("ignore", category=cartopy.io.DownloadWarning)

%matplotlib inline

xr.set_options(display_expand_data=False)
Hide code cell output
ERROR 1: PROJ: proj_create_from_database: Open of /home/docs/checkouts/readthedocs.org/user_builds/tams/conda/latest/share/proj failed
<xarray.core.options.set_options at 0x7f4d0191d0f0>

Load data#

ds = tams.load_example_mpas_ug()
ds
<xarray.Dataset>
Dimensions:  (time: 127, cell: 133447)
Coordinates:
  * time     (time) datetime64[ns] 2006-09-08T12:00:00 ... 2006-09-13T18:00:00
    lat      (cell) float64 ...
    lon      (cell) float64 ...
Dimensions without coordinates: cell
Data variables:
    tb       (time, cell) float32 nan nan nan nan ... 264.8 264.6 266.4 263.8
    precip   (time, cell) float32 0.0323 0.03974 0.0 0.1706 ... nan nan nan nan
fig, ax = plt.subplots(figsize=(6, 3))

sel = ds.isel(cell=slice(None, None, 20))
ax.scatter(sel.lon, sel.lat, marker=".", s=10, alpha=0.5, edgecolors="none")
ax.set(xlabel="lon", ylabel="lat")
ax.autoscale(tight=True)
../_images/683ae7c4f05f5c7e7a1a5df0954910abc88f11005ba59de1deee80c4973673eb.png
%%time

fig, ax = plt.subplots(figsize=(7.5, 3))

im = ax.scatter(ds.lon, ds.lat, c=ds.tb.isel(time=10), marker=".", s=3, edgecolors="none")
fig.colorbar(im, ax=ax, label="Tb")
ax.autoscale(tight=True)
ax.set(xlabel="lon", ylabel="lat")
CPU times: user 91.6 ms, sys: 0 ns, total: 91.6 ms
Wall time: 90.6 ms
[Text(0.5, 0, 'lon'), Text(0, 0.5, 'lat')]
../_images/772f2757cdef289380ade2bcf9dfff647988e9a0ee253e5cc3cf84f761dc313e.png
ts = ds.mean("cell", keep_attrs=True)

fig, ax = plt.subplots(figsize=(6, 3))
ax2 = ax.twinx()

ts.tb.plot(ax=ax, c="orangered")
ts.precip.plot(ax=ax2, c="cornflowerblue")
ax.autoscale(axis="x", tight=True)
ax.grid(True)
../_images/4694072a9988cafafa0371c87d346c898140a6cbcad9bd936fee26009234b6fe.png

Identify CEs#

%%time

itime = 10

stime = pd.Timestamp(ds.time.values[itime]).strftime(r"%Y-%m-%d_%H")
print(stime)

x = ds.lon
y = ds.lat
tri = Triangulation(x, y)
2006-09-08_22
CPU times: user 1.01 s, sys: 36 ms, total: 1.04 s
Wall time: 1.04 s
%%time

# Passing the triangulation in is not required but makes it faster
shapes = tams.core._contours_to_gdf(tams.contours(ds.tb.isel(time=itime), value=235, triangulation=tri));
CPU times: user 295 ms, sys: 3.62 ms, total: 299 ms
Wall time: 867 ms
%%time

# `tams.identify` does the above but also for the core threshold and does size filtering by default
cs_ug, cs_ug_core = list(zip(*tams.identify(ds.tb.isel(time=10))))[0]
CPU times: user 1.93 s, sys: 83.6 ms, total: 2.01 s
Wall time: 2.01 s
%%time

tran = ccrs.PlateCarree()
proj = ccrs.PlateCarree()  # near equator

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), subplot_kw=dict(projection=proj), constrained_layout=True)

ax = ax1

ax.add_feature(cfeature.LAND)

tcs = ax.tricontour(tri, ds.tb.isel(time=itime), levels=[235], colors="red", linewidths=1, transform=tran)
tcs_core = ax.tricontour(tri, ds.tb.isel(time=itime), levels=[219], colors="blue", linewidths=1, transform=tran)

shapes.plot(fc="none", ec="green", lw=1.5, ls=":", ax=ax, transform=tran)  # not size-filtered
cs_ug.plot(fc="none", ec="lawngreen", lw=2, ax=ax, zorder=3, transform=tran)

ax.gridlines(draw_labels=True)

legend_handles = [
    mpl.patches.Patch(color="red", label="219 K contours"),
    mpl.patches.Patch(color="blue", label="235 K contours"),
    mpl.patches.Patch(color="lawngreen", label="CE polygons"),
]
ax.legend(handles=legend_handles, loc="upper right")

ax = ax2

ds_rg = tams.load_example_mpas().sel(lat=slice(None, 20))  # same lat upper bound as in the ug data

ax.add_feature(cfeature.LAND)

cs_rg, cs_rg_core = list(zip(*tams.identify(ds_rg.tb.isel(time=itime))))[0]

a = cs_rg_core.plot(fc="none", ec="royalblue", lw=1.5, ls="--", transform=tran, ax=ax)
cs_ug_core.plot(fc="none", ec="mediumblue", lw=2, transform=tran, ax=ax)

cs_rg.plot(fc="none", ec="red", lw=1.5, ls="--", transform=tran, ax=ax)
cs_ug.plot(fc="none", ec="firebrick", lw=2, zorder=3, transform=tran, ax=ax)

legend_handles = [
    mpl.lines.Line2D([], [], color="royalblue", ls="--", lw=1.5, label="Regridded | cold-core polygons"),
    mpl.lines.Line2D([], [], color="red", ls="--", lw=1.5, label="Regridded | CE polygons"),
    mpl.lines.Line2D([], [], color="mediumblue", ls="-", lw=2, label="Native | cold-core polygons"),
    mpl.lines.Line2D([], [], color="firebrick", ls="-", lw=2, label="Native | CE polygons"),
]

gl = ax.gridlines(draw_labels=True)
gl.top_labels = False
# ax.autoscale(tight=True)
ax.set_xlim(ax1.get_xlim()); ax.set_ylim(ax1.get_ylim())
fig.legend(handles=legend_handles, ncol=2, loc="lower right", bbox_to_anchor=[0, -0.09, 0.961, 1], frameon=False)

for a, ax in zip("abc", [ax1, ax2]):
    ax.text(0.007, 0.98, a, weight="bold", size=14, va="top", ha="left", transform=ax.transAxes)

fig.savefig(f"mpas-ug-contours-and-vs-rg-ces_{stime}.pdf", bbox_inches="tight", pad_inches=0.05, transparent=False)
CPU times: user 2.65 s, sys: 35.9 ms, total: 2.69 s
Wall time: 3.14 s
../_images/58320d8b73743b17c45b45ec893ce90b8fbdfd4a95aff10bd8075aeb272d5ea1.png

Triangulations#

The above contouring is based on the Delauney triangulation. This connects the cell centers with straight lines to form triangles. But this triangulation doesn’t correspond to the MPAS mesh, per se.

MPAS-Tools, though, provides a method for dividing the mesh grid cells into triangles and interpolating data defined at cell centers (like OLR) to the triangle nodes. The triangle nodes include the cell centers (one per triangle) and the grid cell vertices (two per triangle). A hexagonal grid cell, e.g., is divided into 6 triangles, which all share the cell center as a node.

We compared the contourings from these two methods. For our intents and purposes (identifying CEs with area ≥ 4000 km²), the differences appear to be negligible. The mesh horizontal resolution is 15 km, which seems to be sufficiently high that the Delauney triangulation is a good approximation.

# Code

tricontour comparison zoomed