Config-driven builds
One YAML file per model: which test case and window, which family, how many charts and modes, and what to measure once it is fitted.
python -m qlroms.utils.builder configs/ks1d_chaotic.yml # [--set rom.K=20] [--set case.params='{"Ntrain": 5000}']
case: {model: ks1d chaos} # a qlroms.utils.config.CASES name, or ks1d/chaotic
rom: {kind: qlgalerkin, seed: 0, clustering: {kmeans_method: minibatch}}
check: {n_steps: 1000, compare_global: true}
A case is defined in exactly one place — its own module's TEST_CASES entry, which holds
the physics, the windows it is generated with, the \((K, r)\) its ql-ROMs are built at and
the timescales its characterization needs. qlroms.utils.config only maps a readable name
onto one of those entries (get_case("ks1d chaos") reads it back live), and a config
restates none of it: leave rom.K / rom.r or case.params out and the case's own
values stand, set them and this run overrides them. Unknown keys fail loudly, dotted
--set beats the file, and both the trajectory and the fitted ROM come from the case's
cached pipeline. The run prints a JSON summary: representation error, closed-loop mean
error and prediction horizon, against the global (\(K=1\)) ROM of the same family when
compare_global is on.
From Python the same pipeline is qlroms.utils.builder.load_config → build_case →
run_build; see the qlroms.utils reference.