Description
Refactor all common hydrological routines (precipitation, snow, soil, response, routing) into shared mixins. Update BaseHBVModel to inherit from these mixins. Update HBV variant implementations to reuse shared routines unless overriding behavior.
Current state
- Each HBV variant implements its own version of snow, soil, routing, and response functions.
- These implementations are nearly identical, with small differences (e.g., extra lake handling, different parameters).
- Any bug fix or enhancement must be made in multiple places, risking inconsistency.
Objective
- Centralize shared routines (like snow(), soil(), etc.).
- Let each model extend only the parts it needs (via mixins or method overrides).
- Reduce maintenance effort and enforce consistency.
Routine | Description | Common Across | Differences |
---|---|---|---|
precipitation() | Splits precip into rain/snow based on thresholds | All models | Some include pcorr, others don’t |
snow() | Handles snowpack melt and refreeze | All models | Minor naming differences; some pass tfac, some don’t |
soil() | Computes soil moisture, recharge, evapotranspiration | All models | Some include capillary flux, some skip it |
response() | Routes water through upper/lower zones | All models | Some use two/three outflows; lumped/distributed logic varies |
routing() | Applies MAXBAS weighting | Most models | Same logic in all |
lake() | Only in lake variant | Unique | Keep as-is in HBVLakeModel |
Tasks
- Create Shared Mixins
These mixins contain shared routines. Each can be inherited by any model variant.
# src/Hapi/core/mixins.py
class PrecipitationMixin:
def precipitation(self, temp, ltt, utt, prec, rfcf, sfcf, pcorr=1.0):
...
class SnowMixin:
def snow(self, temp, ttm, cfmax, cfr, cwh, rf, sf, wc_old, sp_old):
...
class SoilMixin:
def soil(self, fc, beta, etf, temp, tm, e_corr, lp, tfac, c_flux,
infiltration, ep, sm_old, uz_old):
...
class ResponseMixin:
def response(self, tfac, perc, alpha, k, k1, area, lz_old, uz_int_1):
...
class RoutingMixin:
def routing(self, q, maxbas=1):
...
- Update Base Model to Use Mixins
class BaseHBVModel(
PrecipitationMixin,
SnowMixin,
SoilMixin,
ResponseMixin,
RoutingMixin
):
pass
- Update All Model Variants
Refactor all variants to:
- Inherit from BaseHBVModel instead of defining their own versions of these routines.
- Only override routines that actually differ.
class HBVLakeModel(BaseHBVModel):
def lake(...):
...
- Unify Interfaces Across Implementations
Some current discrepancies:
- Parameter names like cfmax vs CFMAX
- Missing default values
- Use of positional args instead of keyword args
Fix: Normalize function signatures and standardize argument names across all mixin routines.
Summary tasks:
- Create PrecipitationMixin, SnowMixin, SoilMixin, ResponseMixin, RoutingMixin.
- Move shared logic from HBV variants into mixins
- Standardize function signatures and parameter naming
- Update BaseHBVModel to inherit from mixins
- Refactor HBV variant classes to use shared methods
- Write unit tests for mixins
- Write integration tests for refactored models
File Structure
src/Hapi/
├── core/
│ ├── mixins.py ← 🔹 New
│ └── simulator.py
├── rrm/
│ ├── base_model.py ← updated to inherit mixins
│ ├── hbv.py
│ ├── hbv_lake.py
│ └── ...