[calibration] CalibrationConfig#

TOML section: [calibration]

Pydantic model: CalibrationConfig defined in hydromodpy.calibration.config.

Source on GitHub

Top-level [calibration] section.

The config selects the optimizer, iteration budget, candidate persistence policy, parameter declarations, observable outputs, and objective blocks. It is the stable user-facing schema used by CLI calibration and Project.calibrate.

When no explicit objective block is declared, HydroModPy can synthesize one from objective and variable if the matching output exists.

Show fields:

Fields#

protocol in TOML: [calibration.protocol]

MatchingHydrographicNetworkOptions | None default = None user source

Published calibration method this file runs, named instead of retyped. A protocol writes the stages, their criteria and the model regimes they need, so the file states only what belongs to the site. Write the name alone, or a table carrying it plus the names this file uses for the parameters and outputs the method moves. Registered: ‘matching_hydrographic_network’ (Abherve et al., 2023, doi:10.5194/hess-27-3221-2023). A file that declares its own phases or objective blocks cannot also name a protocol.

Fields of MatchingHydrographicNetworkOptions
name

Literal[‘matching_hydrographic_network’] required user source

Protocol identifier.

version

str | None default = None user source

Recipe version this file was written against. Unset runs the version this installation carries; pinned, a mismatch is refused rather than approximated, so a result that informed a decision stays replayable.

conductivity

str default = “K” user source

Name of the calibration parameter stage one moves, as the file declares it under [calibration.parameters].

storage

str | None default = “Sy” user source

Name of the calibration parameter stage two moves. Null runs the network stage alone, which is a method in its own right: it identifies the conductivity without any discharge record.

network_output

str | None default = None user source

Name of the network output stage one is scored on. Unset picks the single output declared with support=’network’.

steady_metric

str default = “distance_gap” user source

Criterion of stage one. ‘distance_gap’ is the signed difference of Eq. 1, whose zero is the balance the paper solves for. ‘distance_mean’ is the mean offset: a diagnostic, and the estimator of the reference script, whose interior minimum sits nowhere in particular.

One of: "distance_gap" "distance_mean"

steady_method

str default = “bisection” user source

Engine of stage one. The signed criterion crosses zero once over several decades, which is what a root search wants; any registered engine is accepted.

steady_max_iter

int default = 20 user source

Evaluation budget of stage one.

steady_tolerance

Optional[float] default = None user source

How precisely stage one has to pin the conductivity before it stops, as a relative precision on the conductivity: 0.01 is the paper’s one per cent. Unset takes the engine’s default, which for the bisection is that same one per cent.

steady_optimizer_kwargs in TOML: [calibration.protocol.steady_optimizer_kwargs.<id>]

dict[str, Any] factory dev source

Extra arguments forwarded to the stage-one engine, in that engine’s own units. The escape hatch for reproducing a published call; a precision is said once, in steady_tolerance.

steady_window in TOML: [calibration.protocol.steady_window.<id>]

dict[str, str] | None default = None user source

Dates the steady stage averages, as {start, end}. Unset takes the whole [simulation.time] window.

transient_metric

str default = “nse_log” user source

Criterion of stage two. The default weights recessions as heavily as peaks, which is where storage shows.

transient_method

str default = “scipy_nelder_mead” user source

Engine of stage two.

transient_max_iter

int default = 120 user source

Evaluation budget of stage two.

transient_tolerance

Optional[float] default = None user source

How precisely stage two has to pin the storage before it stops, as a relative precision on the storage coefficient.

transient_optimizer_kwargs in TOML: [calibration.protocol.transient_optimizer_kwargs.<id>]

dict[str, Any] factory dev source

Extra arguments forwarded to the stage-two engine, in that engine’s own units.

discharge_variable

str default = “discharge” user source

Observed variable stage two is scored on.

observed_station_id

str | None default = None user source

Gauge whose cost drives stage two. Required when several stations are loaded.

scoring_window in TOML: [calibration.protocol.scoring_window.<id>]

dict[str, str] | None default = None user source

Dates bounding the samples stage two scores on, as {start, end}. Use it to drop the spin-up year the transient stage still has to simulate.

method

str default = “grid” user source

Optimization method. Built-ins: ‘grid’ (regular sweep, sized by optimizer_kwargs.points_per_dim), ‘random_search’, ‘bisection’ (root of a signed criterion on one parameter, the stream-network stage), ‘optuna’ (TPE), ‘cma_es’, ‘scipy_de’, ‘scipy_nelder_mead’, ‘gp_mapping’, ‘da_mh_gp’. An unknown name is refused when the optimizer is built, with the list installed here.Optuna is installed by default; install the calibration extra for cma_es and Optuna’s cmaes sampler.

max_iter

int default = 100 user source

Maximum number of calibration iterations.

tolerance

Optional[float] default = None user source

How precisely the search has to pin a parameter before it stops, as a relative precision on the parameter itself: 0.01 asks for one per cent, 0.1 for ten. On a log-transformed parameter that is a ratio, which is how a conductivity is known in the first place, and it holds wherever the value sits; on any other transform there is no scale on the value to be relative to before the search has one, so it reads as a fraction of the declared interval. Each engine’s own stopping option is written from it, so the same number survives a change of engine, and the engine’s own option stays available for reproducing a published call verbatim. Unset, the engine’s default applies. An engine that stops on its budget rather than on a precision refuses this rather than ignore it.

batch_size

int default = 1 dev source

Number of suggestions drawn per ask (for parallel optimizers).

parallel

int default = 1 dev source

Number of trials evaluated concurrently inside one batch via a thread pool. parallel=1 keeps the legacy sequential loop.

reject_water_budget_above

float | None default = None user source

Percent water-balance discrepancy past which a trial is rejected instead of scored. The solver reports the figure on every run; unset, it is recorded and nothing acts on it, so a run at twelve per cent is ranked beside one that closed even though part of the water it routed came from nowhere. There is no default because there is no universal value: a steady solve on a coarse mesh closes to a fraction of a per cent, a transient one with a lake and a routed network legitimately sits higher.

warmup_periods

int default = 0 user source

Spin-up (burn-in) periods excluded from every objective block. The first warmup_periods of each observed/simulated series are dropped before the metric, so the window where the state still depends on the initial condition does not bias the calibration. Default 0 (no exclusion). Size it by increasing it until the objective stops changing (initial-condition insensitivity), not a fixed guess.

scoring_window in TOML: [calibration.scoring_window]

CalibScoringWindow | None default = None user source

Dates bounding the samples every metric is computed on. Mutually exclusive with warmup_periods, which counts samples instead of dates.

Fields of CalibScoringWindow
start

str | None default = None user source

First date scored, ISO 8601. Unset means from the first sample.

end

str | None default = None user source

Last date scored, ISO 8601. Unset means up to the last sample.

phases in TOML: [[calibration.phases]]

list[CalibPhaseDecl] | None default = None user source

Stages run one after the other, each calibrating its own parameters and freezing them for the next. Declaring this table is what switches the runner to staged mode; without it nothing changes for an existing configuration. The default is None and not an empty list on purpose: the resume lock hashes the configuration with exclude_none, so an absent table leaves that hash untouched and checkpoints stay resumable.

Fields of CalibPhaseDecl
name

str required user source

Phase identifier, unique in the calibration and used in the session directory and in the report.

description

str default = “” user source

What this phase calibrates and against what, in one sentence.

method

str default = “grid” user source

Optimization method for this phase only. Built-ins: ‘grid’ (regular sweep, sized by optimizer_kwargs.points_per_dim), ‘random_search’, ‘bisection’ (root of a signed criterion on one parameter, the stream-network stage), ‘optuna’ (TPE), ‘cma_es’, ‘scipy_de’, ‘scipy_nelder_mead’, ‘gp_mapping’, ‘da_mh_gp’. An unknown name is refused when the optimizer is built, with the list installed here.

max_iter

int default = 100 user source

Maximum number of evaluations for this phase.

tolerance

Optional[float] default = None user source

How precisely the search has to pin a parameter before it stops, as a relative precision on the parameter itself: 0.01 asks for one per cent, 0.1 for ten. On a log-transformed parameter that is a ratio, which is how a conductivity is known in the first place, and it holds wherever the value sits; on any other transform there is no scale on the value to be relative to before the search has one, so it reads as a fraction of the declared interval. Each engine’s own stopping option is written from it, so the same number survives a change of engine, and the engine’s own option stays available for reproducing a published call verbatim. Unset, the engine’s default applies. An engine that stops on its budget rather than on a precision refuses this rather than ignore it.

batch_size

int default = 1 dev source

Suggestions drawn per ask. A root search returns one point at a time during its refinement, whatever this asks for.

parallel

int default = 1 dev source

Trials evaluated concurrently inside one batch.

parameters

list[str] required user source

Names of the calibration parameters this phase may move. Every other parameter keeps the value it entered the phase with.

outputs

list[str] factory user source

Names of the calibration outputs this phase scores on. Empty means every declared output.

objective_blocks

list[str] factory user source

Names of the objective blocks this phase evaluates. Empty means every declared block.

variable

str | None default = None user source

Single-metric variable, when this phase does not use blocks.

objective

Optional[str] default = None user source

Metric scoring this phase’s single simulated series, when the phase does not use objective blocks. Same vocabulary as a block’s ‘metric’.

One of: "rmse" "nse" "kge" "mae" "nse_log" "nse_delta" "nse_seasonal" "reservoir" "distance_gap" "distance_mean"

observed_station_id

str | None default = None user source

Observed station whose cost the optimizer minimises. Every loaded gauge is already scored at its own mesh cell, on the discharge routed to that cell, and every cost is reported; naming one says which of them drives the search. Required when several stations are loaded, optional with one. Overrides the calibration-level value for this phase.

optimizer_kwargs in TOML: [calibration.phases.optimizer_kwargs.<id>]

dict[str, Any] factory dev source

Extra keyword arguments forwarded to this phase’s optimizer.

overrides in TOML: [calibration.phases.overrides.<id>]

dict[str, Any] factory user source

Configuration values this phase runs with, as dotted paths into the project configuration. The two stages of a stream-network calibration are one steady and one transient, which is a property of the model and not of the search, so a phase has to be able to say it.

scoring_window in TOML: [calibration.phases.scoring_window]

CalibScoringWindow | None default = None user source

Dates bounding the samples this phase scores on.

Fields of CalibScoringWindow
start

str | None default = None user source

First date scored, ISO 8601. Unset means from the first sample.

end

str | None default = None user source

Last date scored, ISO 8601. Unset means up to the last sample.

depends_on

str | None default = None user source

Name of the phase that must run first. Its frozen parameters enter this one as fixed values.

freeze_on_success

bool default = True user source

Hold the parameters this phase calibrated fixed for the phases that depend on it. Success means the phase converged, not that its validity indicator is good.

seed

int | None default = None user source

Random seed for reproducibility.

save_runs

str default = “none” user source

How much to persist per iteration: - ‘none’: 1 DuckDB row per iteration, no Zarr. - ‘best_n’: same + promote top N to full simulations after the loop. - ‘all’: every iteration becomes a full simulation (Zarr included).

One of: "none" "best_n" "all"

save_best_n

int default = 10 user source

Number of top iterations to promote when save_runs=’best_n’.

use_cache

bool default = True dev source

Enable params_hash content-addressable cache.

lightweight_extraction

bool default = True dev source

Skip Parquet/Zarr writes for lumped models (GR4J, …) and read simulated series from the per-trial RAM cache instead. Only the promoted runs go through the catalog write path.

objective

str default = “nse” user source

Metric scoring the single simulated series, when no objective block is declared. Same vocabulary as a block’s ‘metric’; typed here so a bad value is reported against the key that was written.

One of: "rmse" "nse" "kge" "mae" "nse_log" "nse_delta" "nse_seasonal" "reservoir" "distance_gap" "distance_mean"

variable

str default = “head” user source

Observed variable (for ObservationSet).

observed_station_id

str | None default = None user source

Observed station whose cost the optimizer minimises. Every loaded gauge is already scored at its own mesh cell, on the discharge routed to that cell, and every cost is reported; naming one says which of them drives the search. Required when several stations are loaded, optional with one.

optimizer_kwargs in TOML: [calibration.optimizer_kwargs.<id>]

dict[str, Any] factory dev source

Extra keyword arguments forwarded to the optimizer adapter.

parameters in TOML: [calibration.parameters.<id>]

dict[str, CalibParameterDecl] factory user source

Per-parameter declarations (bounds, transform, prior, path).

Fields of CalibParameterDecl
bounds

list[float] | None default = None user source

[low, high] physical bounds. Inherits from Pydantic annotation when omitted.

transform

str default = “identity” user source

Transform applied before sampling. ‘log’ for strictly-positive quantities spanning orders of magnitude.

One of: "identity" "log" "logit"

prior

str default = “uniform” user source

Prior distribution used by Bayesian samplers.

One of: "uniform" "log_uniform" "normal"

path

str | None default = None user source

Dotted path into HydroModPyConfig. Optional: when omitted, the caller is responsible for injection.

target

str | None default = None user source

Readable alias for ‘path’. When both are set, ‘target’ wins.

mode

str default = “replace” user source

‘replace’ writes the sampled value as-is; ‘scale’ multiplies the base TOML value at the target path by the sample.

One of: "replace" "scale"

units

str | None default = None user source

Parameter units label.

outputs in TOML: [calibration.outputs.<id>]

support = “point” | “boundary” | “cell” | “lake” | “network” factory user source

Named observables extracted from each candidate run.

Pick a tab below: setting support selects the matching schema.

TOML: [calibration.outputs.<id>] with support = "point" – model CalibOutputPoint.

observes

str | None default = None user source

Station whose loaded record this output is scored against. The record is aligned on the simulated timestamps, so a weighted block scores dated observations rather than a vector typed into the file. The data family follows ‘variable’: discharge from hydrometry, head from piezometry, stage from lake_levels. Mutually exclusive with ‘observed_values’. The station is located by its own record and not by coordinates written beside it, so this output and the single-metric route read the same cell and their costs are comparable; a station the project cannot locate is refused by name rather than scored on another quantity.

variable

str required user source

Simulated variable to extract (e.g. ‘head’, ‘outlet_discharge’).

geometry in TOML: [calibration.outputs.<id>.geometry.<id>]

dict[str, Any] | None default = None user source

GeoJSON point geometry. Coordinates are in metres.

x

Optional[Any] default = None user source

X coordinate. Accepts a bare number (metres) or a pint string like ‘100 m’.

y

Optional[Any] default = None user source

Y coordinate. Accepts a bare number (metres) or a pint string like ‘100 m’.

time

Union[str, list[str]] default = “all” user source

‘all’ keeps every time step; ‘last’ / ‘first’ selects one; a list of ISO timestamps selects specific steps.

One of: "all" "last" "first"

reducer

str default = “none” user source

Aggregation over the retained time slice.

One of: "mean" "sum" "last" "none"

observed_values

list[float] | None default = None user source

Hard-coded observed values, positional and dateless. Name a station in ‘observes’ to score a record the project loaded instead.

TOML: [calibration.outputs.<id>] with support = "boundary" – model CalibOutputBoundary.

observes

str | None default = None user source

Station whose loaded record this output is scored against. The record is aligned on the simulated timestamps, so a weighted block scores dated observations rather than a vector typed into the file. The data family follows ‘variable’: discharge from hydrometry, head from piezometry, stage from lake_levels. Mutually exclusive with ‘observed_values’. The station is located by its own record and not by coordinates written beside it, so this output and the single-metric route read the same cell and their costs are comparable; a station the project cannot locate is refused by name rather than scored on another quantity.

variable

str required user source

Simulated variable to extract (e.g. ‘discharge’).

boundary_id

str required user source

Boundary package identifier.

time

Union[str, list[str]] default = “all” user source

‘all’ keeps every time step; ‘last’ / ‘first’ selects one; a list of ISO timestamps selects specific steps.

One of: "all" "last" "first"

reducer

str default = “none” user source

Aggregation over the retained time slice.

One of: "mean" "sum" "last" "none"

observed_values

list[float] | None default = None user source

Hard-coded observed values, positional and dateless. Name a station in ‘observes’ to score a record the project loaded instead.

TOML: [calibration.outputs.<id>] with support = "cell" – model CalibOutputCell.

observes

str | None default = None user source

Station whose loaded record this output is scored against. The record is aligned on the simulated timestamps, so a weighted block scores dated observations rather than a vector typed into the file. The data family follows ‘variable’: discharge from hydrometry, head from piezometry, stage from lake_levels. Mutually exclusive with ‘observed_values’. The station is located by its own record and not by coordinates written beside it, so this output and the single-metric route read the same cell and their costs are comparable; a station the project cannot locate is refused by name rather than scored on another quantity.

variable

str required user source

Simulated variable to extract (e.g. ‘head’).

cell_id

Optional[int] default = None user source

Flat cell index when the backend exposes one.

row

Optional[int] default = None user source

Structured row index.

col

Optional[int] default = None user source

Structured column index.

layer

int default = 0 user source

Structured layer index.

time

Union[str, list[str]] default = “all” user source

‘all’ keeps every time step; ‘last’ / ‘first’ selects one; a list of ISO timestamps selects specific steps.

One of: "all" "last" "first"

reducer

str default = “none” user source

Aggregation over the retained time slice.

One of: "mean" "sum" "last" "none"

observed_values

list[float] | None default = None user source

Hard-coded observed values, positional and dateless. Name a station in ‘observes’ to score a record the project loaded instead.

TOML: [calibration.outputs.<id>] with support = "lake" – model CalibOutputLake.

observes

str | None default = None user source

Station whose loaded record this output is scored against. The record is aligned on the simulated timestamps, so a weighted block scores dated observations rather than a vector typed into the file. The data family follows ‘variable’: discharge from hydrometry, head from piezometry, stage from lake_levels. Mutually exclusive with ‘observed_values’. The station is located by its own record and not by coordinates written beside it, so this output and the single-metric route read the same cell and their costs are comparable; a station the project cannot locate is refused by name rather than scored on another quantity.

variable

str default = “stage” user source

Simulated lake quantity: ‘stage’ (water level, m), ‘volume’ (m3) or ‘surface_area’ (m2). All three are LAK observation states, read in native units and never time-scaled.

One of: "stage" "volume" "surface_area"

lake_id

str required user source

Lake identifier, matching flow.sinks_sources.lakes.<lake_id>.

time

Union[str, list[str]] default = “all” user source

‘all’ keeps every time step; ‘last’ / ‘first’ selects one; a list of ISO timestamps selects specific steps.

One of: "all" "last" "first"

reducer

str default = “none” user source

Aggregation over the retained time slice.

One of: "mean" "sum" "last" "none"

observed_values

list[float] | None default = None user source

Hard-coded observed values, positional and dateless. Name a station in ‘observes’ to score a record the project loaded instead.

TOML: [calibration.outputs.<id>] with support = "network" – model CalibOutputNetwork.

variable

str default = “release_flux” user source

Per-cell observable read from the solver, in m3/s, positive when the aquifer feeds the surface.

stream_geometry_path

str required user source

Vector file holding the mapped stream network. Required, and read only from here: the criterion resolves no geometry of its own and does not reuse the one the hydrography data family loaded.

tau_specific_ratio

float default = 0.0001 user source

A cell releasing less than this fraction of its own recharge is not a seepage face. Zero reproduces the purely geometric criterion of the paper. Frozen over the whole search: a threshold moving with the trial would cost the criterion its monotonicity.

weighting

str default = “cell” user source

Average one cell one vote (the paper) or weighted by cell area. Both values are always reported; use ‘area’ on a mesh refined along the streams, where cell density is highest exactly where distances are smallest.

One of: "cell" "area"

diagonal_neighbors

bool default = False user source

Route over shared nodes rather than shared edges, which recovers the diagonal descents of a D8 grid. Only meaningful on a structured quad mesh. The default is the literal reading of the paper and it is not a second-decimal choice: on a synthetic valley whose talweg runs along the grid diagonal, the most accumulated cell collects 6.6 per cent of the domain over shared edges and 100 per cent over shared nodes, and the delineation that produced the catchment itself uses a D8 pointer. Set it to true wherever the talwegs are not axis-aligned, which on real topography is most of them.

observed_position_accuracy

Optional[Any] default = None user source

Positional accuracy of the mapped network. The validity ratio is normalised by max(cell size, this), because the error floor is set by the network’s own precision and not by the model resolution. Unset is the literal reading of the paper.

roptim_max

float default = 2.0 user source

Validity bound of Eq. 4. It qualifies the result and never penalises the cost: a bad ratio says the agreement is coarse, not that the calibrated value should be discarded.

on_roptim_violation

str default = “warn” user source

What a violation of the validity bound does. Default warns and returns the value, because a calibration is asked for a number.

One of: "warn" "error"

max_unreachable_fraction

float default = 0.05 user source

Bound on ‘frac_unreachable_so’ alone: the share of the simulated network whose descent never meets the mapped one, whose target does not move between trials. Beyond a few per cent the routing surface is not conditioned and D_so would be a fiction. The reciprocal share, ‘frac_unreachable_os’, is reported and deliberately left unbounded: its target is the simulated network, which the search itself retracts.

alpha_warning_threshold

float default = 0.9 expert source

Below this value of ‘alpha_obs_closure_catchment’ the run warns that its distances carry a top-versus-map disagreement on top of the hydrogeology. alpha is the share of the downstream closure of the mapped network the network itself covers, measured on the MODEL TOP and on the catchment. It changes nothing that is computed: the criterion is scored the same way above and below it.

clipping_warning_share

float default = 0.1 expert source

Share of the mapped stream cells lying outside the delineated catchment above which the whole-mesh alpha is reported as unreadable. Those reaches trace through the buffer, where no cell is required to descend into the network, so they inflate the closure without adding to the numerator. Reported together with clipping_warning_gap, never alone.

clipping_warning_gap

float default = 0.05 expert source

Minimum absolute gap between the whole-mesh and the catchment alpha for the clipping report to fire. A linework spilling out of the catchment over ground that routes the same way leaves the two ratios equal, and reporting it there would be noise on every ordinary project.

time

Union[str, list[str]] default = “last” user source

Which timesteps the release flux is read at. Phase one runs a single steady period, so ‘last’ is the whole run.

One of: "all" "last" "first"

objective_blocks in TOML: [[calibration.objective_blocks]]

list[CalibObjectiveBlockDecl] factory user source

Weighted blocks making up a composite objective. When empty, a single implicit block is built from ‘objective’ and ‘variable’.

Fields of CalibObjectiveBlockDecl
name

str required user source

Unique block identifier used in logs and persistence.

metric

str default = “rmse” user source

Metric key. One of rmse, nse, kge, mae, nse_log.

"rmse"

Root-mean-square error, in the observed unit; penalizes large misfits most.

"nse"

Nash-Sutcliffe efficiency against the observed mean; the standard choice for a level or discharge series.

"kge"

Kling-Gupta efficiency; separates correlation, variability and bias when NSE alone is ambiguous.

"mae"

Mean absolute error, in the observed unit; less sensitive to outliers than RMSE.

"nse_log"

NSE on log-transformed series; weights low flows as heavily as peaks, good for recessions.

"nse_delta"

NSE on the increments of the series rather than its level; a level is an integral and can hide flux errors that compensate, which only its increments show.

"nse_seasonal"

NSE against the seasonal cycle rather than the overall mean; asks whether the model beats climatology.

"reservoir"

Half nse_seasonal plus half nse_delta, built for an impounded level: a plain NSE there is beaten by the seasonal cycle, and the increments are what carry the flux errors.

"distance_gap"

Balances the simulated stream network against the mapped one; zero marks the crossing.

"distance_mean"

Mean spatial offset between simulated and mapped streams; a diagnostic, not a substitute for distance_gap.

weight

float default = 1.0 user source

Relative weight of this block in the composite sum.

uses_outputs

list[str] required user source

Outputs (by name) consumed by this block.

normalize_cost

bool default = False user source

When True, divide the block cost by a reference scale (observed std fallback mean absolute value).

transform

str default = “identity” user source

Per-block cost transform applied before weighting. Note that transform=’log’ takes the logarithm of the cost, which is not the same thing as metric=’nse_log’, an NSE computed on log-transformed series.

"identity"

Uses the block cost as computed, with no transform.

"log"

Takes log10 of the cost plus a small epsilon, compressing large costs before weighting.

"inverse"

Takes -1 / (cost + epsilon), sharpening the gradient near a cost of zero.

warmup

Optional[int] default = None user source

Burn-in periods dropped from this block only, overriding [calibration].warmup_periods. Leave unset to inherit it; set it to 0 to switch the burn-in off for this block.

persist_iteration_detail

str default = “summary” dev source

‘none’ skips component metrics; ‘summary’ keeps block totals; ‘full’ also stores per-block raw and normalized costs.

One of: "none" "summary" "full"

persist_model_distribution

bool default = False dev source

Persist the candidate distribution alongside the session.

rerun_best_with_outputs

bool default = False user source

Replay the best candidate with full outputs after the loop.

materialize_candidates

bool default = False dev source

Write a standalone override TOML for each candidate under ‘candidates_root’ so runs can be replayed later.

candidates_root

PurePosixPath | None default = None dev source

Directory for per-candidate overlay TOMLs. Required when materialize_candidates is True.

aggregate in TOML: [calibration.aggregate]

CalibAggregateDecl factory user source

How several scored targets become one cost: what made them comparable, how nested gauges are read, and what one unscorable member does.

Fields of CalibAggregateDecl
weighting

str default = “manual” user source

How the members are made comparable before the weights apply. ‘manual’ takes the declared ‘weight’ of each block as the whole story, which is honest as long as the costs are already commensurable. ‘error’ divides each residual by what its instrument resolves, so the members become pure numbers first; it needs a residual criterion and an observation carrying an error model, and is refused without both.

One of: "manual" "error"

nested_gauges

str default = “total” user source

How two gauges on imbricated catchments are read. ‘total’ scores each against its own full drained area, which is what a gauge measures; the residuals are then statistically dependent, and no standard correction exists for that. ‘incremental’ scores the downstream one on what its own reach adds, downstream minus upstream, which is the only mechanisable way to make the two independent. Neither is inferred: the overlap is measured and reported whichever is chosen.

One of: "total" "incremental"

min_samples

int default = 1 user source

Fewest paired samples a member may be scored on. An alignment that collapses to three days still returns a number, and a weight of 65 per cent resting on three days is not what the file says it is.

on_member_failure

str default = “veto” user source

What one unscorable member does to the total. ‘veto’ makes the whole trial fail, which is the default because a partial cost is not comparable to a full one. ‘drop’ scores the survivors and records which member was left out, which has to be asked for explicitly.

One of: "veto" "drop"

uncertainty in TOML: [calibration.uncertainty]

CalibUncertaintyDecl factory user source

How wide the search reports its own answer to be. The calibrated value is unaffected; this only decides the interval printed beside it.

Fields of CalibUncertaintyDecl
method

str default = “cost_profile” user source

How the interval around each calibrated value is obtained. ‘cost_profile’ reads the range of sampled values whose cost stayed within ‘tolerance’ of the best, off the trace the search already produced, and costs no extra model run. ‘multistart’ runs the whole search ‘restarts’ times from ‘restarts’ different starting points and reports the spread of the optima it reaches, which is the only one of the two that can see a second basin; it costs that many times the runs. The calibrated value never moves either way: with ‘multistart’ it is the best of the restarts.

One of: "cost_profile" "multistart" "linearized"

restarts

int | None default = None user source

How many times the search is repeated by method=’multistart’. Required by it and refused by any other method, because a number of restarts that nothing restarts is a statement about a run that did not happen. Each one is a full search: eight restarts of a hundred-evaluation phase is eight hundred model runs.

perturbation

Optional[float] default = None user source

Relative step the derivatives of method=’linearized’ are taken with, as a fraction of each calibrated value: 0.01 moves it by one per cent. Required by it and refused by any other method. Too small and the difference is solver noise; too large and it is no longer a derivative. One per cent is the usual starting point, and the honest check is to move it and see whether the reported width moves with it. ‘linearized’ costs one model run per parameter, reads derivatives around the answer instead of searching again, and is the only declared method that also reports which parameters trade off against which. It is first-order: exact where the model is linear about the optimum, approximate in proportion to the curvature, and not a posterior.

tolerance

float default = 0.05 user source

Width of the interval. A fraction of the best cost when mode=’relative’ (0.05 = five per cent), and a number in the unit of the cost when mode=’absolute’.

mode

str default = “relative” user source

How ‘tolerance’ is read. ‘relative’ is a fraction of the best cost and is the usual choice for an efficiency score. A criterion solved at zero, such as the stream-network gap, has no fraction of itself to take: state the width in the unit of the cost with ‘absolute’, for example 25 metres.

One of: "relative" "absolute"

persistence in TOML: [calibration.persistence]

PersistenceConfig factory user source

Single switch governing every persistence sink (catalog, Zarr, Parquet, lockfile) for calibration outputs.

Fields of PersistenceConfig
save_catalog

bool default = True user source

Persist DuckDB rows (simulations, parameters, metrics, calibration_iterations). When False, catalog writes are skipped.

save_zarr

bool default = True user source

Persist per-simulation field arrays (head, concentration, derived) into the Zarr store.

save_parquet

bool default = True user source

Persist per-simulation tabular outputs (timeseries, budgets, mass_balance) as Parquet files.

compression

str default = “zstd” dev source

Codec DECLARED for Zarr field arrays and Parquet tables. The writers carry their own codec (zstd) and do not read this field, so changing it changes nothing today; it records the intent and is the field a writer would read once the choice is threaded through.

One of: "none" "zstd" "lz4" "gzip" "snappy"

compression_level

int default = 5 dev source

Compression level DECLARED for those writers. Same as the codec: core/io/parquet.py and core/io/geoparquet.py hold level 5 and do not read this field. The default says 5 rather than 3 so the declaration at least matches the bytes actually written.

Starter TOML snippet#

Click to expand a copy-pasteable [calibration] TOML skeleton

Copy this block into your project.toml and uncomment the lines you want to set. Sub-tables ([parent.subfield]) appear in the order Pydantic expects them.

[calibration]
# method = "grid"
# max_iter = 100
# tolerance = ...  # default = None
# reject_water_budget_above = ...  # default = None
# warmup_periods = 0
# seed = ...  # default = None
# save_runs = "none"
# save_best_n = 10
# objective = "nse"
# variable = "head"
# observed_station_id = ...  # default = None
# [calibration.parameters.<id>]
# [calibration.outputs.<id>]
# rerun_best_with_outputs = false

[calibration.protocol]
# name = ""  # REQUIRED
# version = ...  # default = None
# conductivity = "K"
# storage = "Sy"
# network_output = ...  # default = None
# steady_metric = "distance_gap"
# steady_method = "bisection"
# steady_max_iter = 20
# steady_tolerance = ...  # default = None
# steady_window = ...  # default = None
# transient_metric = "nse_log"
# transient_method = "scipy_nelder_mead"
# transient_max_iter = 120
# transient_tolerance = ...  # default = None
# discharge_variable = "discharge"
# observed_station_id = ...  # default = None
# scoring_window = ...  # default = None

[calibration.scoring_window]
# start = ...  # default = None
# end = ...  # default = None

[[calibration.phases]]
# name = ""  # REQUIRED
# description = ""
# method = "grid"
# max_iter = 100
# tolerance = ...  # default = None
# parameters = []  # REQUIRED
# outputs = ...  # factory default
# objective_blocks = ...  # factory default
# variable = ...  # default = None
# objective = ...  # default = None
# observed_station_id = ...  # default = None
# overrides = ...  # factory default
# scoring_window = ...  # default = None
# depends_on = ...  # default = None
# freeze_on_success = true

[[calibration.objective_blocks]]
# name = ""  # REQUIRED
# metric = "rmse"
# weight = 1.0
# uses_outputs = []  # REQUIRED
# normalize_cost = false
# transform = "identity"
# warmup = ...  # default = None

[calibration.aggregate]
# weighting = "manual"
# nested_gauges = "total"
# min_samples = 1
# on_member_failure = "veto"

[calibration.uncertainty]
# method = "cost_profile"
# restarts = ...  # default = None
# perturbation = ...  # default = None
# tolerance = 0.05
# mode = "relative"

[calibration.persistence]
# save_catalog = true
# save_zarr = true
# save_parquet = true

Entity-relationship diagram#

ER diagram for CalibrationConfig

Click to zoom and pan. Press Esc or click outside to close.