Dongdong Kong
Parameter management and soil model infrastructure for physically-based models in Julia.
ModelParams.jl provides three integrated layers:
- Parameter framework — annotate model structs with calibration bounds and units; recursively collect, filter, and update parameters via a unified DataFrame API.
- Soil column model — type-stable N-layer hydraulic and thermal profiles with SoA/AoS dual representation, multiple K_sat profile types, and a calibration-aware update pipeline.
- Optimization — SCE-UA global optimizer and goodness-of-fit metrics (NSE, KGE, R², RMSE, …).
@bounds @units @with_kw mutable struct Campbell{T<:Real} <: AbstractRetention{T}
θ_sat::T = 0.287 | (0.25, 0.50) | "m3 m-3"
ψ_sat::T = -10.0 | (-100.0, -5.0) | "cm"
K_sat::T = 34.0 | nothing | "cm h-1" # nothing → excluded from calibration
b::T = 4.0 | (2.0, 15.0) | "-"
endThe | bound | unit pipe syntax is handled at macro-expansion time — zero runtime cost.
model = ParamBEPS3{Float64,5}()
# Collect all calibratable parameters (bound ≠ nothing) as a DataFrame
df = parameters(model) # columns: path, name, value, type, bound, unit
# Initial values and box constraints for an optimizer
x0, lb, ub, paths = get_opt_info(model)
# Write new values back into the model
update!(model, paths, x_new)# Hydraulic parameters only, ψ_sat shared across all layers, b fixed
p = filter_params(model, :hydraulic;
list_sameLayer = [:ψ_sat], # one representative row; update_params! broadcasts
list_fix = [:b]) # excluded from calibration
# Then update + sync all caches in one call
update_params!(model, p.path, x_new; params=p, list_sameLayer=[:ψ_sat])filter_params is generic — it works on any struct whose parameters() returns a standard DataFrame. update_params! is defined once on AbstractSoilModel and inherited by all subtypes.
AbstractSoilModel{FT,N}
SoilColumn{FT,N}
hydraulic::HydraulicProfile
profile::MultiLayer{FT,N,P} ← SoA — parameter source of truth
layers::Vector{P} ← AoS cache — rebuilt after each update_params!
kv::K ← KvExp | KvExpConst | KvExpPiecewise | KvLayers
dz_cm::Vector{FT}
thermal::ThermalProfile
profile::MultiLayer{FT,N,P}
layers::Vector{P}
MultiLayer{FT,N,S} is a generated Struct-of-Arrays container that automatically extracts all AbstractFloat fields from scalar type S into Vector{FT} arrays. Index with profile[i] to get back an S instance (AoS slice).
| Type | Parameters with bounds |
|---|---|
Campbell |
θ_sat, ψ_sat, b |
VanGenuchten |
θ_sat, θ_res, α, n (m derived) |
Two complementary ways to obtain retention parameters before calibration:
Look-up table (get_soilpar) — Bonan (2019) Table 8.3, 12 USDA texture classes:
par = get_soilpar(:Campbell, 7) # Loam, Campbell parameters
par = get_soilpar(:VanGenuchten, 9) # Sandy loam, van Genuchten parametersPedotransfer functions (campbell_from_ptf) — derive parameters from measured soil
texture attributes (clay %, silt %, sand %, bulk density, pH):
par = campbell_from_ptf(30.0, 30.0, 40.0, 1.35, 6.5) # Brakensiek K_sat
par = campbell_from_ptf(30.0, 30.0, 40.0, 1.35, 6.5; ksat_method=:cosby)Sources: θ_sat — Tóth et al. (2015); b, θ_res — Rawls & Brakensiek (1989); ψ_sat, K_sat — Cosby et al. (1984); K_sat (alt) — Brakensiek et al. (1984).
| Type | Description |
|---|---|
KvLayers |
Per-layer constant |
KvExp |
Exponential decay: kv·exp(−f·z) |
KvExpConst |
Exponential above z_exp, constant below |
KvExpPiecewise |
Piecewise exponential segments |
kv_layer_ksat computes the thickness-weighted average K_sat for a layer, used by _sync_ksat! to pre-compute profile.K_sat[i] once before the solver hot path.
update!(model, paths, theta) # write values to SoA profile
fill!(profile.field, value) # broadcast list_sameLayer to all layers
update_hydraulic!(profile) # VanGenuchten: m = 1 − 1/n
_sync_ksat!(kv, profile, dz_cm) # K_sat ← layer-integrated kv profile
layers[i] = profile[i] # rebuild AoS cache
@bounds @with_kw mutable struct MyModel{FT,N,H,T} <: AbstractSoilModel{FT,N}
hydraulic::H
thermal::T
extra_param::FT = 1.0 | (0.1, 10.0)
endInherits filter_params and update_params! automatically.
GOF(obs, sim)
# → (NSE=…, R2=…, KGE=…, R=…, RMSE=…, MAE=…, bias=…, bias_perc=…, n_valid=…)result = sceua(cost_fn, x0, lb, ub; maxn=10000, kstop=5)Shuffled Complex Evolution — global optimizer well-suited for hydrological model calibration.