NEDAS.models.vort3d.vort3d_model module
- class NEDAS.models.vort3d.vort3d_model.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 = {}
- read_grid(**kwargs)[source]
Read the grid information from the model output.
- Parameters:
**kwargs – Keyword arguments for reading the grid.
- 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.