Parameterizations

In this notebook, we’ll review tools for defining, running, and comparing subgrid parameterizations.

[1]:
import numpy as np
import pandas as pd
pd.set_option('display.max_columns', 10)
import pyqg
import pyqg.diagnostic_tools
import matplotlib.pyplot as plt
%matplotlib inline

Run baseline high- and low-resolution models

To illustrate the effect of parameterizations, we’ll run two baseline models:

  • a low-resolution model without parameterizations at nx=64 resolution (where \(\Delta x\) is larger than the deformation radius \(r_d\), preventing the model from fully resolving eddies),

  • a high-resolution model at nx=256 resolution (where \(\Delta x\) is ~4x finer than the deformation radius, so eddies can be almost fully resolved).

[2]:
%%time
year = 24*60*60*360.
base_kwargs = dict(dt=3600., tmax=5*year, tavestart=2.5*year, twrite=25000)

low_res = pyqg.QGModel(nx=64, **base_kwargs)
low_res.run()
INFO:  Logger initialized
INFO: Step: 25000, Time: 9.00e+07, KE: 4.14e-04, CFL: 0.042
CPU times: user 14.3 s, sys: 23.6 s, total: 38 s
Wall time: 10.8 s
[3]:
%%time
high_res = pyqg.QGModel(nx=256, **base_kwargs)
high_res.run()
INFO:  Logger initialized
INFO: Step: 25000, Time: 9.00e+07, KE: 4.62e-04, CFL: 0.217
CPU times: user 3min 47s, sys: 5min 43s, total: 9min 30s
Wall time: 2min 55s

Run Smagorinsky and backscatter parameterizations

Now we’ll run two types of parameterization: one from Smagorinsky 1963 which models an effective eddy viscosity from subgrid stress, and one adapted from Jansen and Held 2014 and Jansen et al. 2015, which reinjects a fraction of the energy dissipated by Smagorinsky back into larger scales:

[4]:
def run_parameterized_model(p):
    model = pyqg.QGModel(nx=64, parameterization=p, **base_kwargs)
    model.run()
    return model
[5]:
%%time
smagorinsky = run_parameterized_model(
    pyqg.parameterizations.Smagorinsky(constant=0.08))
INFO:  Logger initialized
INFO: Step: 25000, Time: 9.00e+07, KE: 3.37e-04, CFL: 0.043
CPU times: user 39.3 s, sys: 1min, total: 1min 39s
Wall time: 30.3 s
[8]:
%%time
backscatter = run_parameterized_model(
    pyqg.parameterizations.BackscatterBiharmonic(smag_constant=0.08, back_constant=1.1))
INFO:  Logger initialized
INFO: Step: 25000, Time: 9.00e+07, KE: 5.35e-04, CFL: 0.048
CPU times: user 35.9 s, sys: 57.8 s, total: 1min 33s
Wall time: 27.5 s

Note how these are slightly slower than the baseline low-resolution model, but much faster than the high-resolution model.

See the parameterizations API section and code for examples of how these parameterizations are defined!

Compute similarity metrics between parameterized and high-resolution simulations

To assist with evaluating the effects of parameterizations, we include helpers for computing similarity metrics between model diagnostics. Similarity metrics quantify the percentage closer a diagnostic is to high resolution than low resolution; values greater than 0 indicate improvement over low resolution (with 1 being the maximum), while values below 0 indicate worsening. We can compute these for all diagnostics for all four simulations:

[9]:
def label_for(sim):
    return f"nx={sim.nx}, {sim.parameterization or 'unparameterized'}"

sims = [high_res, backscatter, low_res, smagorinsky]

pd.DataFrame.from_dict([
    dict(Simulation=label_for(sim),
        **pyqg.diagnostic_tools.diagnostic_similarities(sim, high_res, low_res))
    for sim in sims])
[9]:
Simulation Ensspec1 Ensspec2 KEspec1 KEspec2 ... ENSfrictionspec APEgenspec APEflux KEflux APEgen
0 nx=256, unparameterized 1.000000 1.000000 1.000000 1.000000 ... 1.000000 1.000000 1.000000 1.000000 1.000000
1 nx=64, BackscatterBiharmonic(Cs=0.08, Cb=1.1) 0.617871 0.595677 0.719966 0.776835 ... 0.444769 0.222930 0.571082 0.448418 0.015557
2 nx=64, unparameterized 0.000000 0.000000 0.000000 0.000000 ... 0.000000 0.000000 0.000000 0.000000 0.000000
3 nx=64, Smagorinsky(Cs=0.08) -0.049632 0.476905 -0.242329 -0.269631 ... -0.161524 0.069705 -0.171567 -0.296460 0.206204

4 rows × 19 columns

Note that the high-resolution and low-resolution models themselves have similarity scores of 1 and 0 by definition. In this case, the backscatter parameterization is consistently closer to high-resolution than low-resolution, while the Smagorinsky is consistently further.

Let’s plot some of the actual curves underlying these metrics to get a better sense:

[19]:
def plot_kwargs_for(sim):
    kw = dict(label=label_for(sim).replace('Biharmonic',''))
    kw['ls'] = (':' if sim.uv_parameterization else ('--' if sim.q_parameterization else '-'))
    kw['lw'] = (4 if sim.nx==256 else 3)
    return kw

plt.figure(figsize=(16,6))
plt.rcParams.update({'font.size': 16})

plt.subplot(121, title="KE spectrum")
for sim in sims:
    plt.loglog(
        *pyqg.diagnostic_tools.calc_ispec(sim, sim.get_diagnostic('KEspec').sum(0)),
        **plot_kwargs_for(sim))
plt.ylabel("[$m^2 s^{-2}$]")
plt.xlabel("[$m^{-1}$]")
plt.ylim(1e-2,2e2)
plt.xlim(1e-5, 2e-4)
plt.legend(loc='lower left')

plt.subplot(122, title="Enstrophy spectrum")
for sim in sims:
    plt.loglog(
        *pyqg.diagnostic_tools.calc_ispec(sim, sim.get_diagnostic('Ensspec').sum(0)),
        **plot_kwargs_for(sim))
plt.ylabel("[$s^{-2}$]")
plt.xlabel("[$m^{-1}$]")
plt.ylim(1e-8,2e-6)
plt.xlim(1e-5, 2e-4)
plt.tight_layout()
../_images/examples_parameterizations_13_0.png

The backscatter model, though low-resolution, has energy and enstrophy spectra that more closely resemble those of the high-resolution model.