Quasi-Geostrophic Models
The quasi-geostrophic (QG) equations describe large-scale ocean and atmospheric flow where the Rossby number is small.
Barotropic QG¶
The barotropic QG equation for the streamfunction :
where is the potential vorticity and is the Jacobian operator.
Multi-Layer QG¶
The multi-layer extension couples multiple fluid layers through the stretching term, representing baroclinic instability — the primary energy source for mesoscale ocean eddies.
Implementation in somax¶
somax uses a DST-based (Discrete Sine Transform) Poisson solver for the elliptic inversion and the Arakawa Jacobian for the advection term.
Non-dimensional form¶
BarotropicQG.from_nondimensional builds the model from dimensionless
numbers instead of SI coefficients. It uses the advective scale set
(, so and ), and in those units
the equation reads
| Input | Definition | Sets |
|---|---|---|
rossby | f0 = 1/Ro | |
beta_hat | beta | |
delta_M | lateral_viscosity | |
delta_S | bottom_drag | |
delta_I | wind_amplitude |
model, scales = BarotropicQG.from_nondimensional(
nx=128,
ny=128,
rossby=0.02,
beta_hat=50.0,
delta_M=0.03,
delta_S=0.01,
)The point of specifying and rather than and
is that the western-boundary-layer widths are what has to be
resolved. Picking them directly replaces tuning two coefficients until a
run is both stable and resolved, and it lets the factory check the grid:
the munk_width and stommel_width preflight assertions fail when
or , and warn just above
those thresholds. Both guards compare two lengths taken from the same
model, so they apply to dimensional runs too:
assertions:
munk_width: {n_cells_min: 2.0}
stommel_width: {n_cells_min: 1.0}assertions is a flat {name: params} mapping — there is no
preflight: level. Which phase a check runs in comes from the registry
it is in, not from the config, and run_preflight treats every
top-level key as an assertion name.
The wind amplitude follows the Sverdrup balance. Left unspecified it is
, the amplitude whose Sverdrup interior velocity is
exactly the velocity scale ; passing delta_I instead sets
.
The returned Scales is the unit scale set the model runs in — — so it is not by itself enough to convert a dimensional
state: with the vorticity scale is 1 and a
StateAffine.from_scales built from it would leave a dimensional
untouched. Keep the physical scale set alongside it, the
Scales.advective(L=..., U=...) describing the run being reproduced,
and build the transform from that one. Times passed to the
nondimensional model are in units of .
Layered QG¶
BaroclinicQG.from_nondimensional and
ReparameterizedQG.from_nondimensional use the same advective set and
add stratification, given as one Burger number per interface:
model, scales = BaroclinicQG.from_nondimensional(
nx=128,
ny=128,
rossby=0.02,
beta_hat=20.0,
burger=[1.0, 0.02],
thickness_ratio=[1.0, 4.0],
delta_M=0.06,
)The interface convention is not the per-mode one. The deformation
radius of vertical mode comes from the eigenproblem and combines
the interface values: for two layers the rigid-lid result is
, and the free surface shifts it
a few percent below that. Prescribing modal radii directly would mean
inverting the eigenproblem, so the factory takes the interface numbers
and the built model reports the resulting radii as
model.modal.rossby_radii — already in units of , so directly
comparable with . That is exactly what the deformation_radius
guard reads, and from_nondimensional runs it alongside the Munk and
Stommel guards.
The wind default is again Sverdrup-balanced, . The right-hand side applies the wind as , so the dimensionless group is and the factory multiplies by the top-layer thickness on the way in.