Cette page a été traduite automatiquement de l’anglais et peut contenir des erreurs. Original en anglais
Code source de axqua.validationcli
"""``axqua validation``: what a case needs for a validation, and what it has.
``axqua validation <case-file>`` lists the validation situations of the case
(``calibration.validation``), the inflows of the built model with the discharge each
carries in the calibrated case, whether a finished calibration exists, and the result of
each validation that was run. The form on the *Calibration & validation* tab of the
plugin is filled from this.
The validation itself is a job: ``axqua submit <case-file> --kind validation``.
"""
from __future__ import annotations
import argparse
import json
import logging
from pathlib import Path
from axqua.jobcli import _common, _setup_logging, emit, fail, parse_args
log = logging.getLogger("axqua")
def _parser() -> argparse.ArgumentParser:
p = argparse.ArgumentParser(
prog="axqua validation",
description="Show the validation situations of a case: measurements of "
"another flow situation with which the calibrated model is "
"compared. Run one with 'axqua submit <case> --kind validation'.")
p.add_argument("case", type=Path, help="the case file")
return _common(p)
[docs]
def describe(cfg) -> dict:
"""Everything about the validation of *cfg* as one document."""
from axqua import validation
from axqua.config import _dump_value
base = Path(cfg.config_dir) if getattr(cfg, "config_dir", None) else Path.cwd()
situations = [_dump_value(situation, base)
for situation in cfg.calibration.validation]
try:
calibrated = validation.calibrated_parameters(cfg)
found = {"values": calibrated.as_dict(), "estimate": calibrated.estimate,
"source": str(calibrated.source)}
except Exception: # noqa: BLE001 - there is none yet
found = None
folder = Path(cfg.calibration_dir) / validation.FOLDER
reports = []
for path in sorted(folder.glob("validation-*.json"),
key=lambda p: p.stat().st_mtime, reverse=True):
try:
report = json.loads(path.read_text(encoding="utf-8"))
except (OSError, ValueError):
continue
figure = path.with_suffix(".png")
report.update(path=str(path), table=str(path.with_suffix(".csv")),
figure=str(figure) if figure.is_file() else "")
reports.append(report)
try:
boundaries = validation.boundaries_of(cfg)
except Exception: # noqa: BLE001 - not built, or unreadable
boundaries = []
return {"situations": situations, "boundaries": boundaries,
"outflow_condition": cfg.boundaries.outflow_condition,
"calibrated": found, "reports": reports, "folder": str(folder)}
[docs]
def run_validation(argv: list[str]) -> int:
args = parse_args(_parser(), argv, "validation")
_setup_logging(args)
command = "validation"
try:
from axqua.config import load_config
data = describe(load_config(args.case.expanduser()))
lines = []
for situation in data["situations"]:
flow = situation.get("inflows") or situation.get("prescribed_flowrate")
lines.append(f"situation {situation['name']}: discharge {flow}, "
f"{len(situation.get('sources') or [])} data source(s)")
if not data["situations"]:
lines.append("the case names no validation data (calibration.validation)")
for boundary in data["boundaries"]:
if boundary["kind"] == "inflow":
lines.append(f"inflow {boundary['index']}: {boundary['discharge']} m3/s "
"in the calibrated case")
lines.append("calibrated values: " + (
", ".join(f"{k} = {v:.4g}" for k, v in data["calibrated"]["values"].items())
if data["calibrated"] else "none yet; run the calibration first"))
for report in data["reports"]:
for quantity, numbers in (report.get("summary") or {}).items():
if numbers.get("n"):
lines.append(
f"{report['name']}, {quantity}: n = {numbers['n']}, bias "
f"{numbers['bias']:+.3f} ({100 * numbers['relative_bias']:+.0f} "
f"%), RMSE {numbers['rmse']:.3f}")
return emit(command, data, as_json=args.as_json, lines=lines)
except BaseException as exc: # noqa: BLE001
return fail(command, exc, as_json=args.as_json, verbose=args.verbose)
__all__ = ["describe", "run_validation"]