NEDAS.models.vort3d package

class NEDAS.models.vort3d.Vort3DModel(**kwargs)[source]

Bases: Model[RegularGrid]

Zhu, Smith & Ulrich (2001) minimal 3D tropical cyclone model: sigma- coordinate primitive equations on an f/beta-plane, nz free- atmosphere layers (top-to-bottom) plus one boundary layer at the bottom, + surface fluxes, radiative cooling, explicit condensation, and a convective closure. See ~/Google_Drive/papers/ 2024.NEDAS.Introduction/vort3d/ for the standalone prototype and validation this was ported from (dev log: techNotes/models/vort3d.md).

State is (nz+1) layers x 4 fields (u,v,theta,q) + one 2D field (p*, column mass) – represented as 3 multi-level NEDAS variables (‘wind’, ‘theta’, ‘q’, each with levels spanning all nz+1 layers, level index k=0..nz-1 = free-atmosphere layers top-to-bottom, k=nz = boundary layer) plus one single-level ‘pstar’, following the same VarDesc(levels=…) + per-level read_var(k=…) pattern NEDAS’s qg model uses – NOT the earlier (pre-2026-07-21) design of one separate NEDAS variable per layer (‘wind_0’, ‘wind_1’, …, ‘wind_b’, …), which was simpler to read/write but made state_def/variable-list length scale with nz (3*(nz+1)+1 entries), an increasingly bad tradeoff as nz grows. Unlike qg’s own per-level file I/O (which reloads and rewrites the ENTIRE multi-level array on every single-level write, since it stores each level in a plain .npy file), this uses a second “unlimited” netCDF dimension (z, alongside t) so a single-level write only touches that level’s slice – verified directly that netCDF4/HDF5 supports multiple unlimited dimensions per variable and handles out-of-order/sparse writes correctly (unwritten slices read back as NaN, not garbage).

Parameters:
  • nx (int) – grid dimensions (paper: 200x200)

  • ny (int) – grid dimensions (paper: 200x200)

  • dx (float) – grid spacing, m (paper: 20000)

  • nz (int) – number of free-atmosphere layers (paper: 2 – upper troposphere + lower/mid troposphere). Total prognostic layers = nz+1 (always +1 boundary layer at the bottom). For nz=2, the vertical sigma levels match the paper’s own Fig. 1/Table A1 exactly; other nz use equal-sigma-thickness free-tropospheric layers (see core.make_sigma_levels).

  • dt (float) – internal model integration time step, s (paper: 15)

  • restart_dt (float) – restart/output interval, hours

  • beta (float) – df/dy, Coriolis beta parameter, /m/s (0 = pure f-plane, matching the paper’s own experiments; >0 enables beta-drift)

  • moist (bool) – if False, runs the dry dynamical core only (no surface fluxes, radiative cooling, condensation, or convection)

  • convection_scheme (str) – ‘ooyama’ (the paper’s own closure, only valid for nz=2) or ‘betts_miller’ (a simplified Betts/ Betts-Miller-style column relaxation, valid for any nz – see core.py’s module docstring and the dev log for why these are the two supported options)

  • Vbg (float) – random background-flow wind speed amplitude, m/s (0 = calm, matching the paper’s own experiments)

  • Vslope (float) – background-flow kinetic-energy spectrum power law

  • bg_seed (int|None) – RNG seed for the background flow; if None, generate_init_ensemble uses the member index as the seed (so each ensemble member gets an independent background-flow realization – the only source of initial-condition spread currently implemented; the vortex itself is not yet randomized in position/intensity, unlike vort2d’s loc_sprd).

  • Vmax (float) – initial vortex peak tangential wind speed, m/s (smith_vortex’s own default: 15.0)

  • Rmw (float) – initial vortex radius of maximum wind, m (smith_vortex’s own default: 120e3)

  • vortex_x0 (float) – initial vortex center, m, relative to the domain center (Core’s own default: displaced toward the southern boundary, vortex_y0=-700e3, to give the vortex room to drift over a long integration – see vort3d.md dev log, 2026-07-22). This is the truth/reference center; ensemble members perturb around it (see pos_sprd).

  • vortex_y0 (float) – initial vortex center, m, relative to the domain center (Core’s own default: displaced toward the southern boundary, vortex_y0=-700e3, to give the vortex room to drift over a long integration – see vort3d.md dev log, 2026-07-22). This is the truth/reference center; ensemble members perturb around it (see pos_sprd).

  • pos_sprd (float) – initial vortex position spread, m – each ensemble member’s vortex center is drawn from a Gaussian with std=pos_sprd around (vortex_x0, vortex_y0), matching vort2d’s loc_sprd exactly (np.random.normal(0, loc_sprd), NOT a uniform draw), seeded per member the same way as the background flow (vort2d itself reseeds from system entropy every call instead, which vort3d deliberately does not do – reproducibility across runs is needed for the model-comparison work this was built for). The truth run always uses the exact configured center (pos_sprd=0), matching vort2d’s convention.

  • u_bkg (float) – uniform (spatially-constant) steering flow, m/s, added on top of Vbg’s turbulent background flow – see Core’s own docstring for why this is a distinct mechanism from Vbg (persists/steers rather than getting sheared apart by the vortex).

  • v_bkg (float) – uniform (spatially-constant) steering flow, m/s, added on top of Vbg’s turbulent background flow – see Core’s own docstring for why this is a distinct mechanism from Vbg (persists/steers rather than getting sheared apart by the vortex).

  • f0 (float) – reference Coriolis parameter, /s, at beta=0/y=0 (default: 20N, matching the paper’s own fixed-latitude assumption).

  • theta_sprd (float) – ensemble spread (Gaussian std, K and kg/kg) for a domain-uniform boundary-layer-only theta/q perturbation per member – same per-member-seeded Gaussian mechanism as pos_sprd, applied to Core’s theta_offset/q_offset (see Core’s own docstring for why only the boundary layer, and why no dynamical adjustment is needed).

  • q_sprd (float) – ensemble spread (Gaussian std, K and kg/kg) for a domain-uniform boundary-layer-only theta/q perturbation per member – same per-member-seeded Gaussian mechanism as pos_sprd, applied to Core’s theta_offset/q_offset (see Core’s own docstring for why only the boundary layer, and why no dynamical adjustment is needed).

  • Vmax_sprd (float) – ensemble spread (Gaussian std, m/s and m) for the initial vortex’s own peak wind / radius of maximum wind, drawn the same per-member-seeded way as pos_sprd; floored (Vmax >= 1 m/s, Rmw >= 10 km) to avoid a degenerate/negative vortex from an unlucky large negative draw.

  • Rmw_sprd (float) – ensemble spread (Gaussian std, m/s and m) for the initial vortex’s own peak wind / radius of maximum wind, drawn the same per-member-seeded way as pos_sprd; floored (Vmax >= 1 m/s, Rmw >= 10 km) to avoid a degenerate/negative vortex from an unlucky large negative draw.

  • (pos_sprd (All spread parameters)

  • theta_sprd

  • q_sprd

  • Vmax_sprd

:param : :param Rmw_sprd) draw from ONE shared per-member RNG stream (see: :param generate_init_ensemble/_perturb_ic): :param not independently re-seeded: :param streams – re-seeding fresh for each quantity would make them draw: :param the same underlying random sample: :type the same underlying random sample: just rescaled :param correlating position/thermodynamic/intensity perturbations across: :param the ensemble instead of sampling them independently.:

nx: int
ny: int
dx: float
nz: int
dt: float
restart_dt: float
beta: float
moist: bool
convection_scheme: str
Vbg: float
Vslope: float
bg_seed: int | None
Vmax: float
Rmw: float
vortex_x0: float
vortex_y0: float
pos_sprd: float
u_bkg: float
v_bkg: float
f0: float
theta_sprd: float
q_sprd: float
Vmax_sprd: float
Rmw_sprd: float
output_dt: float | None = None
dt_reduction_factor: float = 0.5
max_dt_retries: int = 3
min_dt: float | None = None
memory: dict = {}
filename(**kwargs)[source]
read_grid(**kwargs)[source]

Read the grid information from the model output.

Parameters:

**kwargs – Keyword arguments for reading the grid.

read_mask(**kwargs)[source]
read_var_from_file(**kwargs)[source]
write_var_to_file(var, **kwargs)[source]
z_coords(**kwargs)[source]

Get the vertical coordinates of the model.

Parameters:

**kwargs – Keyword arguments for getting the vertical coordinates.

Returns:

The vertical coordinates.

Return type:

np.ndarray

generate_initial_condition(pos_sprd=None, theta_sprd=None, q_sprd=None, Vmax_sprd=None, Rmw_sprd=None)[source]
preprocess(**kwargs)[source]

Preprocess the model data.

Parameters:

**kwargs – Keyword arguments for preprocessing.

postprocess(*args, **kwargs)[source]

Postprocess the model data.

Parameters:

**kwargs – Keyword arguments for postprocessing.

run(*args, **kwargs)[source]

Run the model forward in time.

Parameters:
  • *args – Arguments

  • **kwargs – Keyword arguments

Keyword Arguments:
  • time (datetime) – current time when forecast starts

  • restart_dir (str) – directory where restart files are located

  • forecast_period (int) – forecast period in hours

If self.ens_run_strategy == ‘batch’, the method will run all ensemble members in one go, expect additional kwargs[‘nens’] to be the ensemble size. If self.ens_run_strategy == ‘scheduler’, the method runs a single member indexed by kwargs[‘member’], and kwargs[‘worker_id’] is the pid assigned by the scheduler to run this method.

generate_truth(*args, **kwargs) None[source]

Generate truth (nature run) model states. Use for running synthetic observation experiments.

generate_init_ensemble(*args, **kwargs) None[source]

Generate initial perturbed model states for ensemble forecasts.

Parameters:
  • nens (int) – ensemble size

  • **kwargs

Submodules