Skip to content

Releases: MNiMORPH/MNiShed

MNiShed v3.1.0

24 Jun 16:35

Choose a tag to compare

MNiShed v3.1.0 — a backward-compatible minor release (single-cascade behaviour is unchanged from v3.0.0).

Added

  • Parallel sub-catchments: a basin can be partitioned into spatially distinct
    zones that drain to the same channel in parallel, each with its own
    reservoir cascade and snowpack/frozen-ground state. Basin discharge and
    storage are the area-weighted means over sub-catchments. Configure with a
    sub_catchments: YAML block (each entry has a name, area_fraction, its
    own reservoirs block, and optional initial_conditions); calibrate by
    passing a sub_catchments=[...] argument to run_and_score. Supported on
    both the pure-Python and Numba JIT time loops. A single sub-catchment of
    area 1.0 reproduces the previous single-cascade behaviour exactly, so
    existing configurations and calls are unchanged. New SubCatchment class
    and Buckets.sub_catchments / Buckets.n_sub_catchments.
    run_and_score chains per-sub-catchment storage state (reservoir depths,
    snowpack, frozen-ground index, carried deficit) across decade windows:
    final_states / initial_states are nested per sub-catchment when there
    are several, and stay flat/scalar for a single sub-catchment.
  • The Numba JIT now covers PDM saturation-excess (pdm_H0) and
    et_water_stress; those configurations previously fell back to the
    pure-Python loop. The JIT and pure-Python loops remain verified-identical, so
    the JIT is now used for every supported configuration whenever Numba is
    importable.

Changed

  • The pure-Python time-loop fallback is no longer silent. Buckets.run() emits
    a one-time UserWarning when Numba is installed but fails to import (usually
    a NumPy/Numba version mismatch), so an unexpected ~100× slowdown is visible.
    A plain "Numba not installed" stays quiet, since pure Python is the expected
    default without the jit extra.

Deprecated

  • The flat single-sub-catchment state shape ({'reservoirs': [...], 'snowpack': ..., 'fgi': ...}) for run_and_score's initial_states /
    post_spinup_states. Passing it now emits a DeprecationWarning; it will be
    removed in v4.0 in favour of the uniform per-sub-catchment form
    ({'sub_catchments': [...]}). See
    #18. The nested form is
    accepted at any number of sub-catchments (including one) and does not warn.

Fixed

  • run_and_score now validates chained initial_states / post_spinup_states
    and raises a clear ValueError if they contain non-finite (NaN/inf) values,
    naming the offending key/index. Previously a NaN state from a partial-data or
    failed decade propagated silently — every modelled flow became NaN and the
    score looked merely poor rather than broken. (None reservoir entries in
    post_spinup_states, meaning "keep the spin-up value", are still allowed.)
  • The BMI snowpack__liquid_equivalent_depth and
    land_surface__frozen_ground_index outputs are now the area-weighted basin
    mean over sub-catchments, instead of reporting only the first sub-catchment.
    Exact for a single sub-catchment, so K=1 couplers are unchanged
    (#16).

MNiShed v3.0.0

23 Jun 20:36

Choose a tag to compare

TL;DR

Major release. The library was renamed from hydroRaVENS to MNiShed.
Several parameter, attribute, and config names changed (see the migration
table below). The numerical core moved to a Numba JIT time loop —
roughly 100×–400× faster than the pure-Python loop depending on record
length — installable via the new jit extra (pip install mnished[jit]). New mechanics: power-law (nonlinear) recession,
leakance + threshold junctions between reservoirs, constant-fraction
tile drainage and threshold-activated multipath parallel drainage,
and exponential PDM saturation-excess overland flow, plus snow
insulation, DTR-based frozen-ground decay, and flexible ET modules. A
CSDMS BMI wrapper (BmiMNiShed) was introduced with CSDMS Standard
Names–aligned variables. GitHub Actions CI added. Substantial
calibration-robustness and numerical-correctness fixes (water-balance
closure, deficit handling, NaN propagation, multi-decade chaining,
JIT↔pure-Python equivalence, and the mean-residence-time gauge).

Breaking changes (you'll need to update existing code/configs)

Old New Where it bites
Package name hydroRaVENS mnished imports, install: pip install mnished, from mnished import ...
BMI class BmiHydroRaVENS BmiMNiShed BMI users update the class name and import (from mnished import BmiMNiShed)
Reservoir(t_efold=...) / t_recession=... Reservoir(recession_coeff=...) constructor arg name
YAML e_folding_residence_times__days: recession_coefficients: all config files
Reservoir.H_infiltrated Reservoir.H_to_next attribute access
Boolean scale_et=True/False string enforce_water_balance='water-year' / 'global' / 'none' run_and_score() argument; YAML enforce_water_balance: key
evapotranspiration_method: ThorntwaiteChang2019 (misspelled) ThornthwaiteChang2019 YAML evapotranspiration_method: value
BMI variable names (atmosphere__temperature, daily min/max temperature, channel_exit_water__volume_flow_rate, ET forcing input) CSDMS-aligned (atmosphere_bottom_air__temperature, …__time_min_of_temperature / …__time_max_of_temperature, channel_exit_water_x-section__volume_flow_rate, …__uncorrected_evapotranspiration_volume_flux) BMI couplers update variable names

Migration: for an existing config + driver pair, do a global
search-and-replace on the old terms (t_efold, t_recession,
e_folding_residence_times__days, H_infiltrated, and the misspelled
ThorntwaiteChang2019ThornthwaiteChang2019), and re-spell
scale_et=True/False as the corresponding enforce_water_balance
string. BMI couplers update the variable names above. Note also that
Buckets.initialize() now raises FileNotFoundError / yaml.YAMLError
on a missing or unparseable config file instead of calling sys.exit(), so
library, BMI, and notebook callers can handle the error (the mnished CLI
still exits cleanly).

New features

Numerical core

  • Numba JIT-compiled time loop (_jit_run). The daily loop is roughly
    100×–400× faster than the pure-Python implementation, scaling with
    record length (~120× on a 3-year run, ~300–400× on multi-decade to
    century-long daily records — see the Performance page in the documentation
    for the full scaling analysis and benchmark scripts). Used automatically
    when Numba is installed; install it with the jit extra (pip install mnished[jit]). The pure-Python loop is retained as a fallback for
    non-Numba environments and for code paths that don't yet fit into the JIT
    (PDM, ET water-stress).

Reservoir mechanics

  • Power-law (nonlinear) recession. recession_exponents per reservoir
    (default 1.0 = linear). Exact integration in the JIT loop;
    mean_residence_time() for physically-comparable timescales at a
    reference discharge.
  • Junction types between reservoirs. New junction_type argument
    on Reservoir and matching junction_types:, leakance_R__days:,
    H_threshold__mm: YAML keys:
    • fraction (default, current behaviour)
    • leakance — head-difference driven flow with leakance resistance R
    • threshold — dead-storage threshold; recession only above H_threshold
  • Tile drain sub-reservoir. f_tile + tau_tile divert a constant
    fraction of subsurface drainage to a fast linear sub-reservoir that
    drains directly to stream. Represents constant-fraction agricultural
    tile bypass.
  • Multipath threshold-activated parallel drain. New
    multipath_threshold + multipath_timescale on Reservoir; per-reservoir
    YAML lists multipath_thresholds__mm / multipath_timescales__days.
    Adds a second linear outflow path direct to stream when storage
    exceeds the threshold. Distinct from f_tile/tau_tile: it is
    state-dependent (activates only above the threshold), not a constant-
    fraction split. Models a tile-drain network at a fixed installation
    depth. Both tile mechanisms can be configured independently.
  • Exponential PDM saturation-excess overland flow. pdm_H0 opens up
    the probability-distributed-model fraction of recharge that is shed
    immediately to discharge from a fraction of catchment with
    exponentially-distributed storage capacities.

Hydrologic processes

  • Snowpack insulation in FGI. snow_insulation_k reduces effective
    air temperature reaching the soil under deep snowpack, with
    documented equifinality caveats.
  • DTR-based FGI decay. When daily Tmax/Tmin are available, FGI decay
    coefficient A_t scales with the fraction of the day above 0°C
    (Molnau & Bissell 1983); falls back to the constant A=0.97 when not.
  • Constant regional baseflow baseflow_Q for cases with sub-basin
    groundwater import.
  • Direct-runoff (Hortonian) bypass. direct_runoff_fraction (γ):
    fraction of positive recharge that bypasses the cascade. Reframed
    from a snow-coupled to a free fast-bypass parameter.
  • ET module flexibility. Three independent module toggles:
    • et_reservoir_draw: post-cascade ET drawn from reservoirs with
      optional wilting-point threshold (recommended for calibrations
      with a soil reservoir)
    • et_water_stress: storage-dependent ET scaling (et_scale,
      wp_soil, wp_soil_sigma)
    • et_scale is also usable as a free universal ET multiplier when
      neither stress module is enabled
  • Water-balance enforcement vocabulary. enforce_water_balance
    accepts 'water-year', 'global', 'none' instead of a single
    boolean. 'global' rescales ET by a single multiplier across the
    entire record (no hidden per-year DoFs that would inflate AIC).
  • Module enable/disable toggles. Top-level modules: block in
    YAML to switch snowpack, frozen_ground, rain_on_snow, direct_runoff,
    dtr_fgi_decay, et_water_stress, et_reservoir_draw on/off.
  • Auto-compute spin-up cycles. When spin_up_cycles=None,
    computed automatically as ceil(tau_max / record_length).
  • Analytical steady-state initial conditions. Used by default
    before spin-up to remove cycle-zero artifacts.
  • post_spinup_states hook. Inject per-decade initial conditions
    after spin-up; supports decade-chaining workflows where each new
    decade starts from the prior decade's end state.

Analysis and diagnostics

  • BrutsaertNieber recession analysis class. Fits −dQ/dt = a·Q^b to
    observed hydrograph recessions for B-N-style architecture diagnosis.
    Power-law goodness-of-fit diagnostics included.
  • HydrographSeparation class. Data-driven model initialization;
    time-domain recession analysis is now the primary timescale estimator,
    with the spectral (PSD) approach retained as a fallback/diagnostic;
    rolling-minimum τ_soil estimation, decoupled from LP separation.
  • Priors and suggest_priors. Suggested prior distributions for
    calibration parameters from observed-data diagnostics.
  • Composite scoring metrics. KGE_logKGE, KGE_logKGE_logFDC,
    KGE_logKGE_logFDC_BFI, logKGE_logFDC_BFI. The mean-of-multiple-
    KGEs framing avoids over-emphasizing high flows.
  • kge_logfdc diagnostic on CalibResult.
  • Reservoir.mean_residence_time() for nonlinear reservoirs:
    physically-comparable timescale at a reference steady-state discharge.

CSDMS BMI wrapper

  • BmiMNiShed class implementing the Community Surface
    Dynamics Modeling System Basic Model Interface.
  • 10-reservoir cap (up from 3) via reservoir_0 through reservoir_9.
  • channel_exit_water_x-section__volume_flow_rate BMI output in m³ s⁻¹.
  • Variable names reviewed against the current CSDMS Standard Names
    conventions: air temperature uses the atmosphere_bottom_air object
    (atmosphere_bottom_air__temperature, and
    …__time_min_of_temperature / …__time_max_of_temperature for the daily
    extremes). The ET forcing input is
    land_surface_water__uncorrected_evapotranspiration_volume_flux — a
    deliberately model-specific name for the ET as supplied (Thornthwaite or
    user series), before the water-balance correction — distinct from the
    corrected ET output land_surface_water__evapotranspiration_volume_flux.
  • Flux-partition, state, and ET outputs: direct-runoff, baseflow,
    tile-drain, and multipath *_volume_flux components, plus
    land_surface__frozen_ground_index and an actual-ET
    land_surface_water__evapotranspiration_volume_flux output. The
    baseflow term (baseflow_Q) is exposed as its own output and is not
    folded into the runoff total, so a coupler adds it explicitly. Note this
    differs from run_and_score, whose scored discharge does include
    baseflow_Q and Nash routing (routing_K) as output-layer
    post-processing; the streaming BMI applies neither (routing is an
    inherently batch operation), so a routed/baseflow-augmented calibration
    is not reproduced through the BMI unless the coupler reapplies them.
  • BMI tests skip gracefully if bmipy not installed; `bmip...
Read more

v2.3.0

11 May 12:38

Choose a tag to compare

What's new in v2.3.0

Physics and correctness

  • Rain-on-snow sensible heat melt (McCabe et al. 2007; Würzer et al. 2016): precipitation arriving at T > 0 °C contributes (c_p/L_f)·T·P mm SWE of melt energy
  • Frozen ground index (Molnau & Bissell 1983): accumulates freezing degree-days; when it exceeds a user-set threshold, shallow-to-deep infiltration is blocked (all drainage becomes direct runoff)
  • Leftover melt energy after snowpack exhaustion is credited to frozen-soil thawing rather than discarded
  • Nash-cascade channel routing in run_and_score() (routing_N, routing_K parameters)

Bug fixes

  • check_mass_balance(): now subtracts initial storage so the discrepancy is ~0 for a mass-conserving run
  • run(): resets internal time counter at the start, so calling it more than once (e.g. spin-up + main run) no longer crashes
  • compute_ET_multiplier(): warns when annual discharge exceeds precipitation (ET multiplier ≤ 0 would make ET water-generating)
  • compute_ET(): uses .to_numpy() for the ET scaling product to prevent silent row misalignment after the water-year merge
  • _update_fgi(): raises a clear ValueError when FGI is enabled but temperature data is absent, instead of an opaque KeyError
  • calibration.run_and_score(): _fgi (frozen ground index) is now included in final_states and restored from initial_states so frozen-ground state is physically continuous when chaining decade runs
  • calibration.run_and_score(): b.melt_factor kept in sync with b.snowpack.melt_factor when overriding

Naming and clarity

  • YAML keys water_reservoir_effective_depths__m, maximum_effective_depths__m, and snowpack__m_SWE renamed to __mm variants to match the actual unit used throughout
  • Buckets.H_deficit renamed to H_deficit_carry to accurately describe it as a timestep carry-over debt, not a cumulative accumulation
  • CalibResult extended with bfi_obs, bfi_mod (Eckhardt baseflow index), fdc_obs, fdc_mod (flow duration curves), and final_states for decade chaining

Breaking changes

  • YAML config files must use the new __mm key names (water_reservoir_effective_depths__mm, maximum_effective_depths__mm, snowpack__mm_SWE)

v2.2.0

05 May 15:15

Choose a tag to compare

Documentation

  • Launched full Sphinx documentation site on ReadTheDocs, including model description, configuration reference, API reference, and bibliography
  • Added NumPy-style docstrings to all public methods and classes
  • Expanded README with installation instructions, quick-start example, data format description, and model overview
  • Added CONTRIBUTING.md with bug-reporting guidelines, code-contribution workflow, and style conventions

Bug fixes

  • Fixed AttributeError when accessing self.snowpack on runs without a temperature column; snowpack operations are now guarded by self.has_snowpack throughout
  • Fixed spin-up restart resetting to index 0 instead of self.hydrodata.index[0], which produced wrong results when the DataFrame index did not start at zero
  • Fixed check_mass_balance() raising TypeError when called with time_step=None; now correctly defaults to the full record
  • Fixed loop-variable leakage in update() where self.H_deficit was set from self.reservoirs[i] using a post-loop i; now uses self.reservoirs[-1]
  • Fixed groupby().mean() FutureWarning in recent Pandas by passing numeric_only=True
  • Fixed t_efold <= 0 validation (previously only caught negative values, not zero, which caused division by zero)

Packaging and infrastructure

  • Replaced setup.py / setup.cfg with pyproject.toml (PEP 517/621)
  • Replaced manual PyPI upload scripts with GitHub Actions using Trusted Publishers (OIDC — no stored API tokens)
  • Added build-check.yml workflow to validate the distribution on every push and pull request
  • Added dependabot.yml for automated dependency and Actions version updates

Code quality

  • Removed dead code: unused instance attributes, unreachable methods, stale commented-out blocks
  • Removed development-era comments and inline to-do notes from public-facing code
  • Applied PEP 8 style fixes throughout (spacing, parentheses, naming)
  • Reorganized Cannon River example into examples/cannon_river/
  • Fixed Chang et al. (2019) citation with verified title and DOI

v2.1.0 — hydroRaVENS

04 May 13:54

Choose a tag to compare

Corrections and refactoring of the Thorntwaite–Chang (2019) evapotranspiration implementation, prompted by a close reading of Chang et al. (2019).

Bug fixes

  • Tmin was incorrectly read from the maximum-temperature column; now correctly reads from minimum temperature
  • Transcription error in Thornthwaite exponent a: coefficient on I² corrected from 7.72×10⁻⁵ to 7.71×10⁻⁵

Improvements

  • _compute_Chang_I() and _compute_Chang_a() extracted as proper methods; Buckets() now accepts T_monthly_normals (array of 12 monthly mean temperatures in °C) to compute the thermal index I from the local climate
  • Hardcoded site-specific fallback value for I removed; initialize() now raises a clear ValueError if ThorntwaiteChang2019 is selected without T_monthly_normals having been passed to Buckets()
  • Calibration coefficient k in the T_ef formula is now an explicit parameter (default 0.69 per Pereira & Pruitt 2004, doi:10.1016/j.agrformet.2004.01.005); use 0.72 for monthly ET per Camargo et al. (1999)
  • Piecewise logic replaced with np.where for clarity and correctness
  • Full NumPy-style docstring added with equation references and DOIs

v2.0.0 — hydroRaVENS

04 May 13:27

Choose a tag to compare

This release marks a complete overhaul of the package, previously distributed as a single-file script called bucketHydrology. The model is now a proper Python package named hydroravens, installable via pip, with a CSDMS-compliant BMI interface and a command-line entry point.

Breaking changes

  • Package renamed from bucketHydrology to hydroravens; all existing import and calling code must be updated
  • Model configuration is now driven entirely by a YAML file; the old script-based interface is gone

New features

  • CSDMS Basic Model Interface: initialize(), update(), run(), finalize()
  • Command-line interface: hydroravens -y config.yml
  • Snowpack model with positive-degree-day melt, fully integrated with mass balance
  • Evapotranspiration: read from data file or computed via the Thorntwaite–Chang (2019) method
  • Water-year ET scaling to enforce P − Q − ET = 0 mass balance
  • Nash–Sutcliffe Efficiency (computeNSE())
  • Configurable spin-up cycles
  • Subsurface storage tracked as model output
  • CITATION.cff added

Infrastructure

  • pip-installable via setup.py
  • Version number introduced (2.0.0)
  • Cannon River included as reference test case and example configuration

Note: version infrastructure and tag created 2026-05-04 and backdated to 2023-11-21 to match the date of the substantive work.

v1.0.0

01 Jul 14:36
95251af

Choose a tag to compare

Stable for 3 years -- so here we go! v1.0.0, overdue.