Skip to content

v2.0.0 - #5

Open
aaTman wants to merge 116 commits into
ai2es:masterfrom
aaTman:feat/2.0.0
Open

v2.0.0#5
aaTman wants to merge 116 commits into
ai2es:masterfrom
aaTman:feat/2.0.0

Conversation

@aaTman

@aaTman aaTman commented Jun 30, 2026

Copy link
Copy Markdown

This is a comprehensive refactor that transforms the fronts project into a well-structured, tested Python package. It introduces proper project organization, CI/CD pipelines, a complete test suite, and modernized dependencies via pixi.

Key Structural Changes:

  1. Project Organization
  • New package structure: All source code moved to src/fronts/ with organized modules:
    • src/fronts/data/ — data generation, sources, targets, datasets, and derived variables
    • src/fronts/layers/ — custom Keras layers, losses, metrics, activations, and modules
    • src/fronts/plot/ — visualization and plotting utilities
    • src/fronts/ — core modules: train.py, model.py, evaluate.py, calibrate.py, pipeline.py, utils.py
  • Removed legacy files: All old root-level scripts deleted (train_model.py, models.py, file_manager.py, custom loss/activation files, etc.)
  • Test suite added: Comprehensive tests under tests/ mirroring the source structure with fixtures and config files
  1. Dependency Management
  • Moved from requirements.txtpyproject.toml: Modern Python packaging with pixi as the lock/environment manager
  • Generated pixi.lock: 10,617 lines of locked dependencies for reproducible environments
  • Pre-commit hooks: Added .pre-commit-config.yaml for code quality checks
  1. CI/CD & Automation
  • GitHub Actions workflows:
    • .github/workflows/ci.yml — continuous integration pipeline
    • .github/workflows/tests.yml — automated test suite execution
    • .github/workflows/update-locks.yml — dependency updates and lock file maintenance
  1. Configuration & Scripts
  • Centralized YAML configs:
    • configs/schooner_train.yaml — training configuration
    • configs/schooner_eval.yaml — evaluation configuration
    • configs/schooner_pipeline.yaml — full pipeline orchestration
    • configs/generate_icechunk.yaml — data generation for icechunk storage
  • Utility scripts in scripts/:
    • benchmark_dataloaders.py — performance testing
    • compare_era5_sources.py — data source validation
    • rechunk_era5_store.py — storage optimization
    • scan_nan_channels.py — data quality checks

Core Module Highlights

Data Pipeline (src/fronts/data/)

  • Config-driven data generation with DataGenerationConfig
  • Modular data sources (ERA5, fronts) and targets
  • Derived variable computation (equivalent potential temperature, etc.)
  • PyDataset integration for efficient TensorFlow training (more info here)

Model Architecture (src/fronts/layers/)

  • Custom activations (including Gelu, Swish)
  • Advanced loss functions (physics-informed losses)
  • Evaluation metrics
  • Keras layer builders and custom modules (attention, etc.)

Training & Evaluation (src/fronts/train.py / src/fronts/evaluate.py)

  • Comprehensive training loop with early stopping, model checkpointing
  • Memory logging and garbage collection management
  • Evaluation pipeline with performance statistics
  • Integration with Weights & Biases for experiment tracking

Visualization (src/fronts/plot/)

  • Plotting utilities for model predictions and performance diagnostics
  • Customizable colormaps and geographic projections

Test Coverage

  • 1000+ lines of tests covering:
    • Data generation and validation (test_generate.py)
    • Input/output handling (test_inputs.py, test_targets.py)
    • Derived variables and transformations (test_derived.py)
    • Loss functions and metrics (test_losses.py, test_metrics.py)
    • Training workflows (test_train.py)
    • Calibration (test_calibrate.py)

Documentation & Quality

  • Updated README.md with setup and usage instructions
  • pyproject.toml with full project metadata and build configuration
  • .gitignore enhanced for Python, data, and logs
  • Pre-commit integration for automated code quality

Notable Commits

  • Initial reorganization (May 27–28): Project structure and CI setup
  • Data pipeline refactor (May–June): Modularized data generation with improved variable handling
  • Model training improvements (June 1–9): Checkpointing, memory logging, vertical velocity channel
  • PyDataset adoption (June 19): Modern TensorFlow data pipeline integration
  • Evaluation refactor (June 26–29): Comprehensive evaluation and visualization pipeline
  • Testing setup (Ongoing): Test suite expansion and validation

Statistics

  • Files: 100+ new/modified files
  • Commits: 50+ commits driving incremental improvements
  • Code: ~4,000 lines of new source code, ~3,000+ lines of tests
  • Deletions: ~6,500 lines of deprecated scripts and modules

Purpose

v2.0.0 establishes a production-ready foundation for the fronts deep learning project with proper software engineering practices: modularity, testability, reproducibility via lock files, and continuous integration.

aaTman added 30 commits May 27, 2026 17:35
* add gc callback

* fix wandb logging and add memory logging

* add model checkpointing

* add model checkpoint path to config
* init commit; convert old stats generation for new approach

* remove default coordinates from evalconfig

* add eval config

* add imports

* update imports and eval domain

* turn compile off for loading model

* place tf imports at top of files where possible

* use periodicboundaryindex

* reorder the selection and subsetting for front targets

* handle dask weirdness

* fixing zerodivision weirdness

* latitude sel flip

* more fixes, typing, add drop duplicate func

* trying to resolve the lat/lon slicing

* remove dask refs

* add iso8601 config date option

* improve throughput, load data into memory

* logging to figure out speed up opportunities

* logging

* move bool mask outside of loop

* attempt 1 at speedup

* ruff

* update cmap function

* change central lon calc

* modify colormaps

* add logs and compute stats prealloc

* fix contourf longitude coords and have predict_config use coordinates instead of domain. Add tests

* update dependences

* comment

* remove constants module and put relevant vars into plot and compute_stats
* change run name to vertical_velocity

* change model checkpoint dir

* dedupe era5

* regenerate icechunk

* change path of icechunk data, create variable addition check

* swap to to_icechunk and defer compute until that method

* update icechunk dir

* update num channels to account for new w variable (30 -> 36)

* correct num channels in default yaml

* Merge branch 'feat/2.0.0' into train/vertical-velocity
@aaTman
aaTman marked this pull request as ready for review July 9, 2026 19:29
aaTman and others added 12 commits July 13, 2026 11:05
* Add latitude-aware neighborhood Brier loss; remove label dilation

Replace fractions_skill_score as the training objective with a proper
scoring rule: the MSE between observed and forecast neighborhood
fractions (FSS's numerator without the hedgeable skill-score
normalization), summed over 25/100/250 km tolerance scales. Zonal
windows widen by 1/cos(lat) so tolerance stays isotropic in km, and
domain edges use valid-cell normalization (no reflect or wrap; the
longitude strip ends are not adjacent). No sigmoid discretization,
which would break propriety.

The 25 km scale reproduces the old 1-pixel label dilation, so
front_dilation is removed from the dataset, evaluation, and configs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* run name update

* bump learning rate based on slow training

* update run names

* use min-max instead of standardization for compute_norm_stats

* brier loss but 3d

* set gradient clipping max, fix callbacks to end run with lr decay

* add brier conus

* more precise lat dependent pool

* test bucketing for lat dependency

* remove lat dependent pooling

* drop batch size and multiple neighborhoods

* add spatial subset internals

* merge conus and brier branches

* swap loss to fractions for test

* bugfix conv3dbackpropfilterv2

* swap to brier loss

* Revert "bugfix conv3dbackpropfilterv2"

This reverts commit bdcb14e.

* fix remainder issue

* swap to fractions for comparison test

* swap back to brier

* remove comment

* typo

* add 0.5 threshold hss metric

* use keras meanmetricwrapper for hss threshold

* try 32 for batch size

* generalize consolidation callback

* swap to fractions loss (again, for updated metrics and batch size)

* brier

* Add CONUS-to-full-domain pretrained warm-start and layer freezing (#41)

* Add pretrained-weight warm-start and layer-freezing for CONUS-to-full-domain transfer

Adds ModelConfig.pretrained_weights_path/freeze_layer_prefixes and train.py helpers
to load a prior checkpoint's weights, reset input_normalization to the current run's
domain stats, and optionally freeze layers by name prefix before compiling. Includes
a schooner_train_full_pretrained_3d.yaml config wiring this up for a full-domain
fine-tune from a CONUS-trained checkpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Zero-weight the dryline class in the full-domain pretrained config

The full domain has no dryline labels because label coverage doesn't extend
there, not because drylines don't occur, so leaving that class weighted
would actively train the model toward "dryline never happens" and erode
what the CONUS checkpoint already learned for it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* change eval run name

* Fix schooner_eval.yaml to match 3D CONUS model's volume-input config

The eval config was never updated for the brier_conus_3d_comparison
3D U-Net: it was missing volume_inputs (defaulting to the flattened
2D path), listed the wrong variable set/order, and pointed at the
full-domain coordinates instead of the CONUS box the model trained on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* Add latitude-aware neighborhood Brier loss; remove label dilation

Replace fractions_skill_score as the training objective with a proper
scoring rule: the MSE between observed and forecast neighborhood
fractions (FSS's numerator without the hedgeable skill-score
normalization), summed over 25/100/250 km tolerance scales. Zonal
windows widen by 1/cos(lat) so tolerance stays isotropic in km, and
domain edges use valid-cell normalization (no reflect or wrap; the
longitude strip ends are not adjacent). No sigmoid discretization,
which would break propriety.

The 25 km scale reproduces the old 1-pixel label dilation, so
front_dilation is removed from the dataset, evaluation, and configs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* run name update

* bump learning rate based on slow training

* update run names

* use min-max instead of standardization for compute_norm_stats

* brier loss but 3d

* set gradient clipping max, fix callbacks to end run with lr decay

* add brier conus

* more precise lat dependent pool

* test bucketing for lat dependency

* remove lat dependent pooling

* drop batch size and multiple neighborhoods

* add spatial subset internals

* merge conus and brier branches

* swap loss to fractions for test

* bugfix conv3dbackpropfilterv2

* swap to brier loss

* Revert "bugfix conv3dbackpropfilterv2"

This reverts commit bdcb14e.

* fix remainder issue

* swap to fractions for comparison test

* swap back to brier

* remove comment

* typo

* add 0.5 threshold hss metric

* use keras meanmetricwrapper for hss threshold

* try 32 for batch size

* generalize consolidation callback

* swap to fractions loss (again, for updated metrics and batch size)

* brier

* Add pretrained-weight warm-start and layer-freezing for CONUS-to-full-domain transfer

Adds ModelConfig.pretrained_weights_path/freeze_layer_prefixes and train.py helpers
to load a prior checkpoint's weights, reset input_normalization to the current run's
domain stats, and optionally freeze layers by name prefix before compiling. Includes
a schooner_train_full_pretrained_3d.yaml config wiring this up for a full-domain
fine-tune from a CONUS-trained checkpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Zero-weight the dryline class in the full-domain pretrained config

The full domain has no dryline labels because label coverage doesn't extend
there, not because drylines don't occur, so leaving that class weighted
would actively train the model toward "dryline never happens" and erode
what the CONUS checkpoint already learned for it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* change eval run name

* Fix schooner_eval.yaml to match 3D CONUS model's volume-input config

The eval config was never updated for the brier_conus_3d_comparison
3D U-Net: it was missing volume_inputs (defaulting to the flattened
2D path), listed the wrong variable set/order, and pointed at the
full-domain coordinates instead of the CONUS box the model trained on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Expand front target classes to 9: merge forming/dissipating variants, add trough/tropical-trough/instability

Forming (CF-F..OF-F) and dissipating (CF-D..OF-D) front codes now collapse into
their parent front class instead of being dropped as background. Trough (TROF),
tropical trough (TT), and instability axis (INST) become new independent classes
6, 7, and 8, bringing the total to 9 (was 6: background + CF/WF/SF/OF/DL).

n_classes, class_weights, loss_class_weights, and front_types are updated across
all training/eval/pipeline configs. The CONUS->full-domain warm-start config keeps
skip_mismatch=True so the 6-class CONUS checkpoint's output layer is dropped and
the 3 new classes train from scratch alongside the rest of the fine-tuned decoder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* swap to completed full domain pretrain in schooner eval

* Add trough, tropical trough, and instability axis to training-viz callback

callbacks.py had its own local 5-class FRONT_TYPE_CLASS_INDEX that was
missed when the rest of the codebase (targets.py, evaluate.py, plot.py,
configs) was expanded to 9 classes. Training-time visualization images
and lite performance-diagram stats were silently excluding TROF/TT/INST.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* update pretrained run name

* update all run names

* typo in run name

* Fix GPU OOM in TestVisualizationCallback by streaming predict() to CPU per batch

model.predict() batches its forward passes internally but still accumulates every
batch's output into one GPU-resident tensor before returning. On full-CONUS-domain
training runs (large spatial extent x 200-timestep visualization subsample x
9 front classes), that accumulated buffer on top of training's already-resident
GPU memory reliably OOMs (observed crash: OOM in ConcatV2 inside
TestVisualizationCallback._predict at shape[200,320,960,9]).

Loop over predict_on_batch and move each batch to CPU immediately instead, so
only one batch's output is ever GPU-resident at a time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Add self-contained aies1702 harness for model_1702 comparison

Runs the legacy AIES FrontFinder checkpoint (Keras 2.10 h5) through the 2.0
eval/plot machinery for a head-to-head against current models on identical
2019 test data:

- loader: patches the stored model_config (TFOpLambda squeeze -> LevelSqueeze,
  standard inbound-node format), rebuilds under Keras 3, loads weights by name
  with full weight-consumption verification (8,745,968 params, 4 softmax heads)
- legacy_formulas: verbatim numpy port of utils/variables.py@c59ac2b with
  docstring golden-value tests (derived.py diverges: Lv 2.501e6 vs 2.257e6)
- normalization: embedded min-max table, parity-tested against
  model_1702_properties.txt
- adapter: raw (batch, lat, lon, level, var) -> legacy normalization ->
  lon-major transpose -> sup1 head -> 6-to-9-class zero padding, matching
  evaluate.compute_stats' contract; ClassPaddingAdapter for 6-class baselines
- store: side icechunk store generation (surface+1000/950/900/850, legacy
  units) from Arraylake ERA5 or ARCO fallback, resume-safe batched writes
- run_eval: config-driven driver for both model kinds over full/land/ocean and
  the five office regions (with wrap-crossing longitude fix), writing
  evaluate-compatible stats_*.nc
- figures/compare: batch 4-panel rendering and cross-model max-CSI summary
  with out-of-domain footnotes

Zero core-module edits; verified end-to-end on the 2019-03-13 bomb cyclone
(coherent CF/WF/DL structures from a real ARCO ERA5 timestep).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Add config-driven case-study figures mimicking AIES Fig. 14

fronts.aies1702.case_study renders multi-panel model_1702 prediction maps in
the paper's presentation: filled front-probability contours at 10% intervals
(CF/WF/SF/OF cmaps from fronts.plot), state-border backgrounds via
plot_background, yellow a)/b)/c) panel labels, and per-panel UTC captions.
Inputs are derived on the fly from Arraylake or ARCO ERA5 with the legacy
formulas and cached to NetCDF so repeat renders skip the fetch entirely.

Ships configs/aies1702/case_study_xmas2023.yaml reproducing the 2023
Christmas storm panels (00Z/12Z 26-27 Dec 2023); verified end-to-end locally
against the real checkpoint — panel structures match the paper's Fig. 14,
including the d-panel occlusion remnants over the UP of Michigan and the
Midwest called out in the text. Raw (uncalibrated) probabilities from ERA5
analysis vs the paper's calibrated GFS f000, noted in the figure title.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Switch side-store source to ARCO and add single-job comparison SLURM script

The Arraylake earthmover-public ERA5 repo lacks the 950 and 900 hPa levels
model_1702 requires, so both generate configs now point at the anonymous
Google ARCO ERA5 zarr (no auth needed).

scripts/aies1702_comparison.sbatch runs the whole 2019 comparison in one job:
side-store generation (resume-safe), all five evaluations, figures, and the
comparison summary. Resubmit-safe — completed eval stages are skipped via
their final region's stats_derived marker file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Rename harness namespace from aies1702 to model_1702

The artifact under comparison is model_1702, so the package, tests, configs,
SLURM script, model_kind string, keras serialization package, render-test env
var (MODEL_1702_RENDER_TESTS), and all schooner paths now use model_1702.
No functional changes; 83 tests pass unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* add cpu and mem resources to slurm script

* update vars

* simplify evaluate to avoid recomputing and running redundant inference

* numpy optimizations

* figures slurm script

* Remove trough/forming/dissipating classes, restore 6-class scheme

harness/model_1702-comparison was branched from
feat/include-forming-dissipating-and-troughs, so every shared file it
touched carried that branch's 9-class scheme (TROF/TT/INST plus
forming/dissipating collapse) even though the actual model_1702
comparison harness itself never needed more than the original 6
classes. Reverts config.py, model.py, plot.py, targets.py, and the
schooner_*.yaml training configs to their feat/2.0.0 state; keeps the
callbacks.py predict_on_batch OOM fix and the evaluate.py
predict_batches/accumulate_stats split plus numpy optimizations, which
are unrelated to class count; and fixes the new model_1702/ package
(adapter.py, eval configs, tests) to assume 6 classes throughout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant