Open-source code from the lab, each package documented in a peer-reviewed paper. If you use it, please cite the paper: every page below gives the reference.
The web interface of TEDA. Configure an ensemble data assimilation experiment in the browser, launch it, watch the error curves arrive in real time, and keep every run in a database so methods can be compared later or exported as CSV.

A reproducible Python port of the 1.5-layer quasi-geostrophic model QG-C, with a direct sparse Helmholtz solver, RK4, Dormand-Prince and Leapfrog (RA and RAW filters) integrators under one API, and diagnostics for the linearized operator, its eigenspectrum and the maximum Lyapunov exponent. Comes with a published stability and filter sensitivity dataset.

two_jets regime of swesphere: zonal wind, meridional wind, and the departure of the fluid depth from its zonal mean, drawn as relief.Shallow-water equations on the rotating sphere: a small geophysical flow for numerical experiments, verified against the standard test cases and reproducible from a container. Three documented regimes, a state-vector interface any external program can drive, and a pluggable time integrator. About two seconds per model day on one core.
A lightweight, object-oriented Python toolbox for ensemble-based data assimilation. TEDA implements the filters and the Lorenz-96 model; larger models come from separate packages, QG-Python and swesphere, through the same state-vector interface. pip install pyteda
from analysis.analysis_enkf_modified_cholesky \
import AnalysisEnKFModifiedCholesky
model = Lorenz96()
background = Background(model, ensemble_size=20)
analysis = AnalysisEnKFModifiedCholesky(model, r=2)
observation = Observation(m=32, std_obs=0.01)
params = {'obs_freq': 0.1,
'obs_times': 10,
'inf_fact': 1.04}
simulation = Simulation(model, background,
analysis, observation,
params=params)
simulation.run()
# background and analysis errors per step
errb, erra = simulation.get_errors()
A model is anything with a state vector and a propagate call, so the same filters run on Lorenz-96, on the quasi-geostrophic model of QG-Python and on the shallow-water sphere of swesphere without changing a line of the assimilation code.
GitHub Paper, SoftwareX 2025 ICCS 2022
If you use TEDA, please cite
Nino-Ruiz, E. D. (2025). TEDA: A lightweight Python framework for educational data assimilation. SoftwareX, 31, 102297. doi:10.1016/j.softx.2025.102297
Nino-Ruiz, E. D., & Valbuena, S. R. (2022). TEDA: A computational toolbox for teaching ensemble based data assimilation. In Computational Science, ICCS 2022, LNCS 13353. Springer. doi:10.1007/978-3-031-08760-8_60
| Class | Method | Reference |
|---|---|---|
AnalysisEnKF | EnKF with the full covariance matrix | Evensen (2009) |
AnalysisEnKFNaive | EnKF via an iterative Sherman-Morrison formula | Nino-Ruiz, Sandu, Anderson (2015) |
AnalysisEnKFCholesky | EnKF via Cholesky decomposition | Mandel (2006) |
AnalysisEnKFModifiedCholesky | EnKF via modified Cholesky decomposition | Nino-Ruiz, Sandu, Deng (2018) |
AnalysisEnKFShrinkagePrecision | EnKF with shrinkage precision matrix | Nino-Ruiz, Sandu (2015) |
AnalysisEnKFBLoc | EnKF with B-localization | Greybush et al. (2011) |
AnalysisEnSRF | Ensemble square root filter | Tippett et al. (2003) |
AnalysisETKF | Ensemble transform Kalman filter | Bishop, Etherton, Majumdar (2001) |
AnalysisLETKF | Local ensemble transform Kalman filter | Hunt, Kostelich, Szunyogh (2007) |
AnalysisLEnKF | Local ensemble Kalman filter | Ott et al. (2004) |
Code we use in our own experiments, packaged for reuse.
Data assimilation for atmospheric general circulation models. This is the package the lab uses for experiments with the SPEEDY model at near-operational resolutions, including ensemble-based methods for its leapfrog integration scheme.
The aml_pred_assim package estimates precision (inverse covariance) matrices from small samples in high dimensions via a modified Cholesky decomposition. It downloads climate fields from the Copernicus Climate Data Store, builds the predecessor structure of each grid point and fits the sparse factors with ridge regression, saving everything to NetCDF.