ET-Demands vs SWIM-RS: Comprehensive Comparison
This document compares SWIM-RS with ET-Demands,
the original FAO-56 codebase from which SWIM-RS evolved. While SWIM-RS has been completely
rewritten, understanding the differences helps contextualize design decisions.
Software Paradigmatic Differences
| Aspect |
ET-Demands |
SWIM-RS |
| Execution Model |
Sequential per-crop, per-cell loops with optional multiprocessing |
Vectorized NumPy operations with Numba JIT compilation |
| Data Flow |
Pandas DataFrames, scalar computations inside iterrows() |
NumPy arrays with shape (n_fields,) or (n_days, n_fields) |
| Configuration |
Single INI file parsed at runtime |
TOML config + HDF5 binary container |
| State Management |
Mutable class attributes (InitializeCropCycle) |
Immutable-style dataclasses with explicit .copy() |
| Modularity |
Functions in .py files, no clear kernel separation |
Explicit kernel layer (kernels/*.py) with pure physics functions |
| Type Safety |
No type hints, relies on runtime duck typing |
Full type annotations, NDArray[np.float64] throughout |
| Dependencies |
Pandas-heavy, optional xlrd/shapefile support |
NumPy/Numba core, HDF5 for I/O, minimal pandas |
| Parallelism |
multiprocessing.Pool by cell or crop |
Numba prange within kernels (thread-level) |
Overall Software Approach
ET-Demands: Crop-Centric Lookup Model
- Philosophy: Pre-defined crop coefficient curves (35-point tables) interpolated based on phenological progress
- Kcb source: Static crop parameter tables, selected by crop type ID
- Calibration: Manual parameter adjustment via INI/crop files
- Target use: Planning and regulatory compliance for specific crop types
- Data paradigm: Text files (CSV), one file per cell/crop combination
SWIM-RS: Observation-Driven Inverse Model
- Philosophy: Remote sensing observations (NDVI→Kcb) drive coefficients; parameters calibrated via PEST++
- Kcb source: Daily NDVI time series, sigmoid transform with calibrated parameters
- Calibration: Automated parameter estimation using pyemu/PEST++
- Target use: Spatially distributed ET estimation without crop-type assumptions
- Data paradigm: HDF5 containers with all fields/days in single binary file
Algorithmic Differences
Crop Coefficient (Kcb)
| ET-Demands |
SWIM-RS |
| 35-point lookup table per crop type |
Sigmoid function: Kcb = Kc_max / (1 + exp(-k*(NDVI - ndvi_0))) |
| Interpolated by GDD, %PL-EC, or DOY |
Computed directly from daily NDVI |
| Season detection via T30/GDD thresholds |
No explicit season detection (NDVI handles it) |
| Crop-specific curves (alfalfa, corn, etc.) |
Generic sigmoid, calibrated per field |
Evaporation Model
| ET-Demands |
SWIM-RS |
| Two-area model: irrigation-wetted vs precip-wetted |
Single exposed area: few = 1 - fc |
| Three-stage drying (REW→TEW→TEW3) |
Two-stage drying (REW→TEW) |
Separate ke_irr and ke_ppt |
Single Ke coefficient |
Kr2 parameter for cracking soils |
No stage-3 drying |
Stress Coefficient (Ks)
| ET-Demands |
SWIM-RS |
Binary: Ks = max((TAW - Dr) / (TAW - RAW), 0) |
Same formula with damping |
invoke_stress flag can disable |
Always active |
| Unrecoverable stress flag (perennials) |
No unrecoverable stress |
| No damping (instantaneous response) |
ks_damp parameter smooths response |
Runoff
| ET-Demands |
SWIM-RS |
| SCS Curve Number only |
CN method (default) or Infiltration-Excess (IER) |
| ANT moisture: dry (I), avg (II), wet (III) |
CN adjusted by surface layer depletion |
| 4-day S averaging for irrigated fields |
Same 4-day averaging for irrigated |
| No hourly precipitation support |
Hourly precip for IER mode |
Irrigation Logic
| ET-Demands |
SWIM-RS |
Trigger: depl > MAD * TAW |
Same |
Requires Kcb > 0.22 |
No Kcb threshold |
| Multiple types: auto, real, manual, special |
Single simulated irrigation |
days_after_planting_irrigation delay |
Irrigation flag schedule from observations |
Irrigation fraction (fw) for partial wetting |
No partial wetting model |
Deep Percolation
| ET-Demands |
SWIM-RS |
| Two-phase: Ze layer + root zone |
Single-phase from root zone |
| 20mm storage above FC when recently wet |
Immediate percolation when Dr < 0 |
| Conditional on 2-day dry window |
No dry window requirement |
Feeds lower layer (aw3) |
Same (daw3) |
| 10% irrigation bypass to aw3 |
Same 10% bypass |
Snow Dynamics
| ET-Demands |
SWIM-RS |
| Albedo-based Kc_mult reduction |
Degree-day + radiation melt model |
Kc_mult = f(albedo, DOY) |
Full SWE tracking with decay |
| No SWE state variable |
swe, albedo state variables |
| No melt calculation |
Melt computed and added to infiltration |
Root Growth
| ET-Demands |
SWIM-RS |
| Sigmoidal function of days-after-planting |
Linear function of Kcb |
| Borg & Grimes (1986) formula |
zr = zr_min + (zr_max - zr_min) * f(Kcb) |
| GDD-based progress option |
Kcb-based progress |
| Perennial: constant at max |
Perennial: constant at max (same) |
Features Unique to ET-Demands
- Cutting cycles for alfalfa/forage crops
- Frost-kill termination for winter crops
- Aridity temperature adjustments
- CO2 correction factors
- Historical phenology anchoring (±40 day constraint on season start)
Features Unique to SWIM-RS
- Irrigation fraction tracking (
et_irr, dperc_irr) for water rights accounting
- Damped stress coefficients (
kr_damp, ks_damp) for smooth temporal response
- Year-specific groundwater subsidy (
f_sub by year)
- PEST++ integration for automated parameter estimation
- Numba-compiled kernels for performance
Testing Comparison
ET-Demands
Does not have automated tests.
Testing relies on:
- Integration testing via example runs
- Visual inspection of output files
- Comparison with legacy VB6 code output
SWIM-RS
Comprehensive test suite:
- 60+ kernel unit tests
- State initialization and copy tests
- Conservation law verification
- Property-based bounds testing
- pytest framework with CI integration