Config-driven builds
One YAML file per model: which test case and window, the family (rom.kind: qlgalerkin,
the intrusive family this package carries), 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.builder.KS_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.builder.KS_CASES only maps a
readable name onto one of those entries (read live at build time), 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.
There is no key for the inner product: Mw comes from the case (none for the uniform KS grids,
the FEM velocity mass matrix for the pinball, read without dolfinx from the file written next
to the mesh), and the clustering, the chart affiliation, the transitions and the reported
errors all use it.
From Python the same pipeline is qlroms.utils.builder.load_config → build_case →
run_build; see the qlroms.utils reference.