Earth2Studio general release 0.18.0
🚨 Breaking Changes 🚨
- No notable breaks this release 🎉
📕 Changelog
🌎 Added
- Added model scorecards to the documentation produced by the new
recipes/eval/scorecardcampaigns (FCN3 and Aurora to start) - Added IEM parsed ASOS/AWOS station observation data source (
IEM_ASOS) - Added
IceChunkBackendIO backend for writing versioned, transactional output to an Icechunk repository, with non-blocking writes by default (blocking=Trueto opt back into synchronous writes) ZarrBackend(andIceChunkBackend) write/read now use basic slice indexing for contiguous coordinate subsets, ~7x faster than the previous fancy-indexing path on step-by-step forecast writes- Added SamudrACE coupled atmosphere-ocean prognostic model (
SamudrACE) with its initial-condition and forcing data sources (SamudrACEData,SamudrACEForcingData) - Added
CorrDiffCosmoEra5SDA, score-based data assimilation (DPS) for the CorrDiff-COSMO downscaler - Added Zarr v3 sharding support to
AsyncZarrBackend - Added obstore support to
AsyncZarrBackendvia a newstoreparameter (store URL, obstore store, or zarr store) - Added a working
add_arrayplus__contains__,__getitem__,__iter__,__len__,storeandcoordstoAsyncZarrBackend, matchingZarrBackend, andoutput.io_backendto the eval recipe to select between them (async_zarris the new default) - Added hyperspectral IR sounder variables (
airs,iasi,cris) toNNJAObsSat, returned as brightness temperature (K) with per-channel wavenumbers alongside the existing microwave sensors - Added Atlas CRPS ensemble prognostic model (
AtlasCRPS), which generates ensemble members from noise conditioned transformer blocks and shares the Atlas autoencoder - Added
earth2studio.data.utils.table_to_dataframe, a shared Arrow-to-pandas conversion producing fully Arrow-backed (pd.ArrowDtype) DataFrames with optional dictionary encoding of low-cardinality string columns - Added
StormScopeMeteoSatEUEuropean domain satellite nowcasting model
⚙️ Changed
- GFS, HRRR, and GEFS GRIB sources now fill unresolved output regions with
NaNinstead of zeros or uninitialized memory, making missing index records detectable UFSObsConvandUFSObsSatnow decode diag files in parallel across a persistent spawn-based process pool (newdecode_workersparameter, default"auto").- Updated StormCast SDA example to use the
GHCNHourlydata source. - Vectorized the
NNJAObsSatIR sounder decode (one NumPy pass per footprint instead of per-channel operations; IASI ~7.8x, CrIS ~5.8x faster) and moved decode workers to per-batch Arrow tables instead of pickled row dicts (~6.2x less accumulation memory, near-zero cross-process serialization cost) NNJAObsSatoutput DataFrames now use Arrow-backed dtypes for every column across both microwave and IR sensors: floats asfloat[pyarrow]/double[pyarrow], times astimestamp[ns][pyarrow], and thesatellite,variable, andclassstring columns dictionary-encodedAsyncZarrBackendnow throttles on in flight writes rather than submitted writes, and waits for whichever write completes first rather than the oldest.- Migrated GOES data source from s3fs to obstore; hour-directory listings are now async and memoized, so same-hour timestamps share one LIST request (~30% faster)
- Migrated GOES GLM data source from s3fs to obstore; listings of complete hours are memoized per instance while the current hour is always re-listed
- Migrated Himawari AHI data source from s3fs to obstore with memoized minute-directory listings (scans older than an hour)
- Migrated Planetary Computer data sources from
planetary_computer.sign()+ httpx streaming to obstoreAzureStorewith the
PlanetaryComputerCredentialProvider, which fetches and renews SAS tokens automatically; theplanetary-computerandhttpxpackages are no longer required by the data extra PlanetaryComputerECMWFOpenDataIFSnow downloads only the GRIB messages for the requested variables via byte ranges resolved from the item's GRIB index asset (~6x faster and ~25-180x less transfer than the previous whole-file download, depending on variable count)obstore_fetch_to_cachegained an opt-inatomicmode that publishes cache files via temp file + rename, so interrupted or concurrent downloads cannot leave a partial file as a poisoned cache entry; used by the Planetary Computer sources' fetches. Off by default, leaving the other obstore-migrated data sources' cache writes unchanged.- Migrated GHCNDaily and GHCNHourly data sources to obstore; GHCNDaily station-scale requests now fetch per-station parquet files instead of global by_year partitions (~25x faster), and their default
async_workersis raised from 16 to 32 since fetches are small, latency-bound requests where throughput scales with concurrency (roughly halves wall time again for large station lists) - Migrated JPSS VIIRS, ATMS, and CrIS data sources from s3fs to obstore; day-directory listings of completed days are memoized per instance while in-progress days are always re-listed, and the CrIS SDR/GEO dual listings remain concurrent
- Vectorized the JPSS ATMS BUFR decode (numpy column assembly + Arrow table accumulation instead of per-row dicts), roughly halving end-to-end fetch time; decoded output is bit-identical to the previous implementation
- JPSS VIIRS HDF5 decode now runs in worker threads (serialized by an HDF5 lock) so decoding no longer blocks concurrent granule downloads
- JPSS ATMS now decodes each BUFR file as soon as its download completes (pipelined with in-flight downloads, one decode per unique file) instead of decoding after all downloads finish
- JPSS CrIS granule downloads now fetch large objects as concurrent byte ranges (1 MiB x 8 streams per file) instead of one single-stream GET, roughly halving fetch time for typical requests
- Migrated ISD, IBTrACS, CFS reforecast, and OPERA data sources from fsspec/s3fs to obstore
- CFS reforecast grib decoding now resolves all requested variables in a single pass over the file's messages instead of one
pygrib.selectscan per variable (~20x faster for full-lexicon requests) - Consolidated the four identical per-source grib decode helpers (
_decode_gfs_grib,_decode_hrrr_grib,_decode_gefs_grib,_decode_cfs_grib) into a shareddecode_grib_messagehelper - Migrated MRMS data source from s3fs to obstore, with memoized day-directory listings and threaded, header-based grid decoding
- Migrated NClimGridDaily data source from s3fs to obstore; monthly NetCDF files are now downloaded once into the cache and shared across all (day, variable) slices instead of being streamed per slice over fsspec
- Updated
DLESyMandDLESyMLatLondefault package to provide newer CRPS-trained checkpoints used in the AI Weather Quest competition
❌ Removed
- Deprecated the
fs_factoryparameter ofAsyncZarrBackendin favor ofstore; the default local write path no longer uses fsspec
🔨 Fixed
- Fixed intermittent repeated FengWu forecast timesteps by synchronizing ONNX Runtime I/O buffers with PyTorch.
- Fixed HRRR GRIB index lookups for total precipitation at lead zero and for snow depth, snow cover, and total cloud cover at positive forecast lead times.
- Fixed the March 22, 2021 GFS archive cutoff and historical
GFS_FXtotal precipitation lookups. - Fixed
lat_weightreturning a small negative weight at the poles in float32, which could give NaN undersqrt. Weights are now clamped to be non-negative. - Fixed
SamudrACEinference being non-reproducible run-to-run: togglingtorch.backends.cudnn.benchmarkon and off around each coupled cycle re-triggered cuDNN's GPU-timing-based algorithm search every cycle - Fixed
Aurora.create_iteratorfirst yield pairing a lead-sliced tensor with unsliced coords:lead_timekept[-6h, 0h]while the tensor held one step. Coords are now sliced the same way as FuXi, DLWP and FengWu, so the initial condition is yielded atlead_time=[0h] - Fixed
CFS_Reforecast_FXandCFS_Reforecast_FX_Fluxpointing at the retired NCEI archive path; the reforecast archive moved tohttps://www.ncei.noaa.gov/oa/prod-cfs-reforecastwith renamed product subdirs - Fixed
CorrDiffCosmoEra5loading files from the wrong resolution when cache names collided. Cache names now include the resolution. - Fixed
OPERAdata source returning negative precipitation values (-99.0 mm/hfortprate,-0.099 mfortp01) for pixels where the radar detected no rain. Undetect pixels for RATE and ACRR quantities are now filled with0.0instead of the reflectivity sentinel-99.0 dBZ. - Fixed
GHCNHourlystation discovery to use the published GHCNh station list (ghcnh-station-list.csv) instead of the GHCN-Daily station list. - Fixed
to_time_arrayandfetch_datasilently wrapping timestamps outside thedatetime64[ns]range (roughly 1678-2262) to unrelated dates, which prevented workflows such asrun.deterministicfrom using the model-year calendars of climate emulators (for example the CM4 initial conditions ofSamudrACE). - Fixed
AIFS2andAIFS2ENSassigning time-dependent forcing values to the wrong samples when processing multiple batches and initialization times. - Fixed
AsyncZarrBackenddiscarding exceptions raised by non-blocking writes. A write future that had already completed was never resulted, so its error was swallowed - Fixed
AsyncZarrBackendbugs covering non-blocking write safety, tensor aliasing, metadata visibility, coordinate parsing, and shard buffer allocation. - Fixed Atlas models using incorrect total precipitation accumulation, now models correctly use
tp06.
Security
- Added
zizmorstatic security auditing of GitHub Actions workflows (pre-commit hook,make zizmorlint step, and a code scanning workflow)
📦 Dependencies
- Added
obspec>=0.1core dependency; the shared obstore helpers are typed against its vendor-neutral store protocols - Updated GraphCast and GenCast optional dependencies to use WeatherNext.
Special thank you to our external contributors: