Changelog
Changelog
All notable changes to NEDAS are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Patch releases (x.y.Z) are cut on demand whenever bug fixes accumulate on develop,
rather than on a fixed schedule, and contain fixes only — no new features. New models,
DA schemes, or other backward-compatible features land in the next minor release.
Unreleased
Added
ice_conc,ice_drift, andcs2smossea ice datasets: opt-inuse_dataset_uncertainty(per-pixel uncertainty from the source file) anduse_adaptive_err(concentration-/displacement-/thickness-dependent error formulas ported fromenkf-topaz) toggles; both default toFalse, no behavior change unless enabled indataset_defInterpolationAssimilator: Cressman/OI local obs-only analysis (interpassimilator type)vort3dobs: core-biased radial sampling option for targeted obs networksDIS optical flow:
variational_refine_alphanow configurablevort3d: adaptive dt retry on NaN blowup (dt_reduction_factor,max_dt_retries,min_dt)
[1.3.1] - 2026-08-06
Fixed
BatchAssimilatorlocalization prefilter: L1 (Manhattan) distance check for thehroiobs filter now scaled by √2, making the prefilter disk a correct superset of the true L2 Gaspari-Cohn support — fixes hard diamond-shaped edges in the analysis from obs incorrectly excluded along diagonal directionscs2smos_obs: netCDF4 fill-value leak —_FillValuesentinel was read as a literal observation for every no-data pixel (~90% of one test-case grid), silently folding the sentinel value into the analysis; now usesfilled(nan)with an explicit finite-validity check before the obs sequencetopaz5model:hice_impact(renamed toseaice_thick_obs_impact) is now wired throughpostprocess()into the externalfixhycombinary call, which previously hardcoded a literal 0 for that argument — the SIT analysis increment was computed by the EnKF but never redistributed into the restart’s per-category ice volume regardless of the configured value. Default remains 0 (no behavior change unless explicitly raised)TopazDEnKF: align with Fortran referenceenkf-topaz(develop @ 0f4c74b):rfactor1parameter added: global obs error inflation factor matching Fortran’sRFACTOR1(applied before local analysis)kfactormoved from insideensemble_transform_weights()to a one-time adjustment inlocal_analysis()before the field loop, matching Fortran’sobs_QC()flow (applied once, not re-computed per field)nlobs_maxdefault changed from 2848 to 0 (no limit), matching Fortran’s default (nlobs = 0→ use all obs within localization radius)(Inflation formula verified: Fortran’s
infl-based matrixIMproduces standard multiplicative inflation, not relaxation-to-prior — no change required on the NEDAS side)
Vort3DObs.__init__KeyErrorwhen thevort3dmodel is not registered in the context (hit by the generic dataset smoke test)Sphinx
release/versionnow derived fromNEDAS.__version__instead of being hardcoded indocs/conf.pytopaz5model.postprocess:fixhycomiced input now links to the posterior state, not the backgroundalignment_updator: target levelkis now configurable; addedvector_imageoption for vector-to-scalar conversionAdaptive posterior inflation for multiscale (
once_after_outer_looptiming): configurablemax_coefcap, and a file-lock re-initialization bug fixqg/python: bottom/top Ekman drag now uses time-laggedpsi_o(matching Fortranqg_driver.f90::Get_rhs) instead of currentpsi; multi-layer spectral initial condition now matches the Fortran model’s modal-to-layer projection, so results reproduce the Fortran version closelyice_driftobs operator:iced/seaice_velocityrestart lookup now rounds the obs valid time to the start of day before theicedattempt, matching the day-level toleranceicehalready had; previously the exact-hour requirement foriced_variablescould never be met (restarts are only ever written at hour 0), so the lookup silently fell through to theicehfallback, which also fails for a cycle prior state before any forecast has runvort3dobs: fixed cyclic-boundary wrap bias invortex_positioncentroid (tracks no longer pin at the domain wrap point)vort3dobs:vortex_positionsearch window now respectsgrid.cyclic_dimper axis (wrap vs. reflect-pad), generalizing the wrap/wall fixes abovevort3dobs: proximity-taper the vorticity centroid toward the anchored vortex, so tracks stop jumping to unrelated blobs or pinning at wallsSeeded obs network/noise RNG by cycle time (and
obs_rec_id) so synthetic obs are reproducible across schemes at a given cycleSeparated obs error inflation (tempering) from obs generation noise, so inflating R for tempering no longer corrupts the generated obs values
Moved
character_lengthintoscale_bandpass’s owntransform_defscope; iterations without scale decomposition no longer assume it existsvort3d: extended NaN detection to all prognostic fields (previously onlyu/pstar) and clip qsat iterates inside the loop, not just after
1.3.0 - 2026-07-23
Added
vort3dmodel: minimal 3D tropical cyclone model (Zhu, Smith & Ulrich 2001), with configurable vertical levels, vortex position/intensity/size, steering flow, f0, Betts-Miller closure, and boundary-layer thermodynamic ensemble spreadqg/python: pure-Python QG model backendlorenz96/tracer_advection: tracer advection extension for the Lorenz-96 modelQCEFassimilator modulealignment_updator: multiscale alignment update schemeSimultaneous state-parameter estimation (SSPE)
Inflation timing option for once-after-outer-loop application
One-time perturbation support
Ensemble verification metrics (
diag/metrics/spectral.py)TOPAZ5 archm variables write-back
DIS/Farneback optical flow: tunable parameters,
local_weightself-normalizing smoothness-weight alternative
Changed
Breaking:
impact_on_staterenamed toimpact_on_variablein configs, docs, and tests (extended to obs variables); old key silently no-ops rather than erroringPer-iteration parameters (
hroi,err.std, updator/assimilator/inflation/transform_def) redesigned as explicititerNdictsSynthetic obs (network + noise draw) now cached once per analysis cycle instead of per outer-loop iteration
Deferred
xarray/scipy/matplotlibimports to avoid slow I/O-bound loading
Fixed
Context.logger()not restoringpid_showaround wrapped callsProgress.update()ZeroDivisionErrorwhen a rank has zero assigned tasksMultiplicativeInflation.apply_inflationnot restoringc.pid_showVort2DModel/Lorenz96Modelmemory dict shared across all instances (class-level mutable default)ETKF
apply_ensemble_transformrenormalizes weight columns instead of abortingobs.pystate_to_obsignoring tag for custom obs_operatorAlignmentPreconditioner.warp()unit mismatch (meters vs. grid-index)obs_prioroutput in npy filesobs_postrecomputation restricted to batch assimilators, now applies to serial tooAlignment updator re-warping already-finalized scales instead of freezing them
Adaptive posterior inflation formula corrected to match Ying (2019) exactly
DIS/Farneback shared-range uint8 normalization; Horn-Schunck weight now derived per-call from alpha_squared
filter.prepare_init_ensemblebugTyping errors
H(X)using scale-decomposed field instead of full state in multiscale DAtaper_boundarynot available by defaultModelmissingparamsannotation;nextsimwrite_paramrenamed param->valueSlurm submitter:
file_pointerinit moved before job-array branchDiag plot module crashes and design issues
rec.nobsnot synced to all MPI ranks afterprepare_obsbroadcastimpact_on_statecross-variable localization not implemented in EAKFOffline scheduler
nworkercalculation on HPC mode (#19)Removed unused TensorFlow dependency; PyTorch only for ML (#22)
Per-variable
dttime loop in perturb step; MPI-safe file-lock init (#24)@njitremoved from KDE dict/lambda functions; QCEF tests unskippedExplicit cycling config to fix
restart_dirlogic for offline DAMPI ranks agreeing on
obs_postavailability before allreduce (inflation)Lorenz-96 bug fix
prepare_init_ensemble/ensemble_forecastnow use the fullnprocbudget
Removed
Preconditioner (reverted)
1.2.0 - 2026-04-22
Added
New
coremodule consolidating theModel,Dataset, andSchemebase classes (previously scattered across submodules), including a dedicatedContextclass to hold runtime-living objects (previously done byConfig)IOBackendclasses implementing both online and offline I/O modes; online mode adds a memory save/load mechanism and is supported by thelorenz96andvort2d(native Python) modelsGeneralized
Gridclass hierarchy (Grid1D,RegularGrid,IrregularGrid)Progressclass for runtime logging: interactive on/off modes, terminal-size detection, Jupyter notebook supportAMSR2 dataset: SIC retrieval and obs_operator
CS2SMOS sea-ice thickness dataset
New synthetic-obs subclass with prescribed
obs_x/obs_y/obs_zsupport, andsave_obsto keep a copy ofobs_seqindataset.memoryfilterandforecast(forecast-only) analysis schemesTOPAZ5:
write_varforicehvariables,tcwv/tclwmade operational in preprocessing, updated namelist andiagevariable, brightness-temperature (Tb) assimilation support, conc reading from eithericedoricehfilesAutomatic package versioning from git tags (setuptools_scm)
Call-stack management, throttled
Progress.update(), persistent logger parameters, and memory dump-to-file in the runtime logger
Changed
assim_tools, perturbation, and scheme base classes substantially refactoredqgmodel renamed toqg.fortranto make room for future backendsConfig key
analysis_schemerenamed toscheme; emptymodel/datasetconfig entries are now allowed; config objects can be kept read-onlymodel.variableschanged todict[str, VarDesc]Type hints made backward-compatible with Python 3.9
Fixed
Divide-by-zero in
gaspari_cohnobs_priorNaN values not filtered out in the batch assimilatorParallel finalization safety issues; nproc/nproc_util ambiguity resolved
AMSR2 channel-order bug; OSISAF
ice_driftgrid definition and time-window mismatch; OSISAFice_concfilename time-string bugRandom perturbation multiscale bug
pandas/xarraymade required dependencies (fixed missing-dependency crashes)GridType checks; job submitter fixes for SLURM/OAR/gricad, including kill-signal handling and clearer error messages
Large batch of pylint and typing cleanups across the codebase
1.1.0 - 2025-06-24
An interim 1.0.1 milestone (workflow rewritten in Python, never separately
tagged) landed between 1.0-beta and this release — folded in below.
Added
assim_toolsrefactored into composable component classes: Assimilators (ETKF, TopazDEnKF, EAKF), Updators (Additive, Alignment), and transform functions (null, scale_bandpass)New models:
lorenz96,nextsim/v1,nextsim/dg,noresm,qg(+ emulator),topaz/v4,topaz/v5,vort2d,wrfNew datasets:
era5,argo(ifremer),rgps,osisaf(ice_conc, ice_drift),qg,lorenz96,vort2dAdaptive inflation algorithms
readthedocs documentation site
QG-model benchmark example comparing filter/DA algorithm performance
Published to PyPI
Changed
Configuration is now fully YAML-file-based; no more Linux environment variables
Models are now
Modelclasses with user-provided methods (read_var, etc.) instead of shell-script modulesEnsemble forecast runs in either batch mode or via a job scheduler
Workflow control moved to Python (
scripts/run_exp.pyas top-level entry point, withassimilate.pyandensemble_forecast.pyas the two main steps)
1.0-beta - 2024-01-17
Initial public beta release.
Added
Bash-script-driven workflow control, with model code run via
model/<model>/module_forecast.shParallel DA step via
scripts/run_assim.pyConfiguration via environment variables defined in
config/*Demo cases for the
vort2dandqgmodels; aqgbenchmark for comparing DA algorithm efficiency