Rossby Wave Packet Diagnostics (Ghinassi et al. 2018)

A reproduction of the results of section 4, Large-amplitude Rossby wave packets in an idealized barotropic model simulation, by Ghinassi et al. (2018).

[1]:
import numpy as np
import matplotlib.pyplot as plt
import barotropic as bt

Model setup and integration

[2]:
# 2° resolution of longitude-latitude grid
grid = bt.Grid(resolution=2.)
# No forcing
rhs = None
# Model with hyperdiffusion
model = bt.BarotropicModel(rhs, diffusion_order=2, diffusion_coeff=1.0e15)
[3]:
# Start from Held and Phillips (1987) zonal profile
background_u = bt.init.held_1985(grid)

# Add perturbation according to eqs. (24) to (26)
# λ_2 = 3π/2 here instead of 2π/3 as in the paper, which seems to be a typo
def perturbation_vorticity(grid, s1, s2, zeta0, phi0, lam1, lam2, sigma_phi, sigma_lam):
    L1 = np.exp(-((grid.lam2 - lam1)/sigma_lam)**2) * np.sin(s1 * grid.lam2)
    L2 = np.exp(-((grid.lam2 - lam2)/sigma_lam)**2) * np.cos(s2 * grid.lam2)
    return zeta0 * np.cos(grid.phi2) * np.exp(-((grid.phi2 - phi0)/sigma_phi)**2) * (L1 + L2)

perturbation = perturbation_vorticity(grid, s1=6, s2=9, zeta0=8.0e-5, phi0=np.pi/4,
        lam1=np.pi/3, lam2=3*np.pi/2, sigma_phi=np.deg2rad(10), sigma_lam=np.deg2rad(30))

# Add vorticity perturbation to background state to obtain initial state
init = background_u.add_vorticity(perturbation)

# Run model for 5 days with a timestep of 900 seconds and save state every 24 hours
last, states = model.run(init, 900, 5*bt.DAY, save_every=1*bt.DAY)

Figure 5

Absolute vorticity in the barotropic model simulation (a) at initial time and (b) after 5 days of integration.

[4]:
def style_lonlat(ax):
    ax.set_xticks([0, 60, 120, 180, 240, 300])
    ax.set_xticklabels(["0°", "60°E", "120°E", "180°", "120°W", "60°W"])
    ax.set_ylim([0, 90])
    ax.set_yticks([0, 10, 20, 30, 40, 50, 60, 70, 80])
    ax.set_yticklabels(["0°", "10°N", "20°N", "30°N", "40°N", "50°N", "60°N", "70°N", "80°N"])
    ax.grid(linestyle="dotted", color="k")
[5]:
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(6, 5), gridspec_kw=dict(height_ratios=(8, 8, 1)))

pv_levels = [0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6]
pv_cmap = "viridis"

ax1.set_title("(a)", loc="left")
ax1.set_title("Absolute vort (t = {:.0f} h)".format(init.time / bt.HOUR), loc="center")
ax1.contourf(grid.lon2, grid.lat2, init.pv * 10000,
             levels=pv_levels, cmap=pv_cmap, extend="both")
style_lonlat(ax1)

ax2.set_title("(b)", loc="left")
ax2.set_title("Absolute vort (t = {:.0f} h)".format(last.time / bt.HOUR), loc="center")
pv2 = ax2.contourf(grid.lon2, grid.lat2, last.pv * 10000,
                   levels=pv_levels, cmap=pv_cmap, extend="both")
plt.colorbar(pv2, cax=ax3, orientation="horizontal")
ax3.set_title("$10^{-4}\;\mathrm{s}^{-1}$", loc="right")
style_lonlat(ax2)

fig.tight_layout()
../_images/examples_rwp-diagnostics-ghinassi-et-al-2018_7_0.png

Figure 6

Evolution of (left) filtered LWA and (right) the envelope of the meridional wind in the barotropic model simulation, shown as snapshots every 24h.

[6]:
fig, axs = plt.subplots(7, 2, figsize=(10, 12), gridspec_kw=dict(height_ratios=(7, 7, 7, 7, 7, 7, 1)))

panels = list("lfkejdichbga")

for state, (axl, axr) in zip(states, axs):
    # State.falwa_filtered uses Fourier instead of wavelet transform to
    # obtain the dominant wavenumber, so compute manually here
    dominant_wavenumber = bt.diagnostics.dominant_wavenumber_wavelet(state.v, grid)
    falwa_filtered = bt.diagnostics.filter_by_wavenumber(state.falwa, 2*dominant_wavenumber)

    axl.set_title("(" + panels.pop() + ")", loc="left")
    axl.set_title("Filtered LWA (t = {:.0f}h)".format(state.time / bt.HOUR), loc="center")
    ml = axl.contourf(grid.lon2, grid.lat2, falwa_filtered,
                      levels=[0, 4, 8, 12, 16, 20], extend="max", cmap="YlOrRd")
    axr.set_title("(" + panels.pop() + ")", loc="left")
    axr.set_title("Envelope of v (t = {:.0f}h)".format(state.time / bt.HOUR), loc="center")
    mr = axr.contourf(grid.lon2, grid.lat2, state.v_envelope_hilbert,
                      levels=[4, 8, 12, 16, 20], extend="both", cmap="Purples")

plt.colorbar(ml, cax=axs[-1,0], orientation="horizontal")
plt.colorbar(mr, cax=axs[-1,1], orientation="horizontal")

axs[-1,0].set_title("$\mathrm{m}\;\mathrm{s}^{-1}$", loc="right")
axs[-1,1].set_title("$\mathrm{m}\;\mathrm{s}^{-1}$", loc="right")

for ax in axs[:-1,:].flatten():
    style_lonlat(ax)

fig.tight_layout()
../_images/examples_rwp-diagnostics-ghinassi-et-al-2018_9_0.png

Figure A1

Initial state of the barotropic model simulation: (a) meridional wind \(v\) and (b) local dominant wavenumber from wavelet analysis.

[7]:
fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(6, 6), gridspec_kw=dict(height_ratios=(8, 1, 8, 1)))

ax1.set_title("(a)", loc="left")
ax1.set_title("Meridional wind (t = {:.0f} h)".format(init.time / bt.HOUR), loc="center")
m1 = ax1.contourf(grid.lon2, grid.lat2, init.v,
                  levels=[-30, -20, -10, -5, 5, 10, 20, 30], cmap="RdBu_r", extend="both")
plt.colorbar(m1, cax=ax2, orientation="horizontal")
ax2.set_title("$\mathrm{m}\;\mathrm{s}^{-1}$", loc="right")
style_lonlat(ax1)

ax3.set_title("(b)", loc="left")
ax3.set_title("Local dominant wavenumber (t = {:.0f} h)".format(init.time / bt.HOUR), loc="center")
m3 = ax3.contourf(grid.lon2, grid.lat2, bt.diagnostics.dominant_wavenumber_wavelet(init.v, grid),
                  levels=[1, 2, 3, 4, 5, 6, 7, 8, 9], cmap="Spectral_r", extend="both")
plt.colorbar(m3, cax=ax4, orientation="horizontal")
ax4.set_title("dimensionless", loc="right")
style_lonlat(ax3)

fig.tight_layout()
../_images/examples_rwp-diagnostics-ghinassi-et-al-2018_11_0.png