Parameter validation (custom models)¶
OpenPFC encourages fail-fast configuration: invalid or missing parameters should be caught at startup, not hours into a run. The machinery lives in the frontend UI layer and is optional—physics and custom drivers can ignore it or adopt it fully.
Building blocks¶
Piece |
Header |
Role |
|---|---|---|
|
|
Describes one parameter: name, range, units, required, typical value. |
|
|
Aggregates metadata, |
|
Your physics header |
Used by |
Value-returning helpers (ParameterValidator::validate, ValidationResult::{is_valid,format_errors,format_summary}, ParameterMetadata::validate / format_info, and ParameterMetadata::Builder::build) are marked [[nodiscard]] so ignoring validation output is typically a compile-time error.
Validation is typically invoked from application main right after load_settings_file, before the session is constructed, or integrated inside your app’s settings loader if you have one.
Validation vs. session parsing order¶
ParameterValidator and the default spectral pipeline both read the model.params JSON object, but they are separate layers:
Layer |
When |
Responsibility |
|---|---|---|
Optional validation |
In your code, after loading the config and before constructing the session |
Fail fast on missing keys, bad types, or out-of-range values using metadata you register on |
Library |
Inside |
Copies JSON fields into your physics via |
Wiring |
After physics construction |
Catalog ICs/BCs/writers and |
The framework never calls ParameterValidator for you. If you validate in main and then construct the session, validation runs first; the library still applies Physics::from_json so the physics receives the parsed values. Keep validator metadata and from_json field names in sync to avoid rejecting configs that would parse, or accepting configs that from_json would mis-handle.
Pattern¶
Declare metadata for each scalar (or structured) parameter your physics reads from
model.params.Call
validator.validate(config["model"]["params"])(or thejsonsubtree you store parameters in).If
!result.is_valid(), printresult.format_errors()and exit.Optionally print
result.format_summary()for reproducibility (see rootREADME.md— Configuration Validation).
Minimal sketch (matches the root README.md snippet; headers live under openpfc/frontend/ui/):
#include <cstdlib>
#include <iostream>
#include <openpfc/frontend/ui/parameter_metadata.hpp>
#include <openpfc/frontend/ui/parameter_validator.hpp>
void validate_my_params(const pfc::ui::json &root) {
pfc::ui::ParameterValidator validator;
validator.add_metadata(
pfc::ui::ParameterMetadata<double>::builder()
.name("temperature")
.description("Effective temperature")
.required(true)
.range(0.0, 10000.0)
.typical(3300.0)
.units("K")
.build());
const pfc::ui::json ¶ms = root["model"]["params"];
auto result = validator.validate(params);
if (!result.is_valid()) {
std::cerr << result.format_errors() << '\n';
std::exit(1);
}
if (/* rank 0 */) {
std::cout << result.format_summary() << '\n';
}
}
Call this from main after loading the config file and before constructing the session if you want validation outside the library; many apps instead fold validation into Physics::from_json.
Reference implementation¶
apps/tungsten/include/tungsten/common/tungsten_input.hpp (and related) registers many parameters with ranges and descriptions—use it as the full example.
Smaller programs may only validate 3–5 critical scalars; you can still use the same ParameterMetadata<double>::builder() pattern as in the root README.md snippet.
Documentation elsewhere¶
Root
README.md— user-facing description of validation output and benefits.app_pipeline.md— whenmodel.paramsis applied in the session pipeline.
See also¶
tutorials/custom_app_minimal.md— minimal config-driven applicationstyleguide.md— API and header conventions
Domain periodicity¶
The current domain block uses size, lower and upper. Set one optional
periodic array of exactly three booleans, in x/y/z order:
"domain": {
"size": [128, 128, 64],
"lower": [0.0, 0.0, 0.0],
"upper": [128.0, 128.0, 63.0],
"periodic": [true, true, false]
}
The same array is supported in TOML. Omitting it preserves fully periodic
behavior. A periodic axis has spacing (upper-lower)/size; a bounded axis
includes both endpoints, uses (upper-lower)/(size-1) and requires at least two
grid points. Decomposition and halo exchange use these flags to identify physical
faces; boundary-stage operators must still provide the desired boundary values.
Fourier/HeFFTe operators retain their periodic basis assumptions. Setting domain
metadata does not make a Fourier operator a bounded spectral method.
Checkpoint schema version 2 records periodicity and restart rejects mismatches before restoring fields. Legacy version-1 bundles without the field are interpreted as fully periodic, so they cannot silently restart a mixed-periodic configuration. Older readers reject version 2; a topology-aware checkpoint must not be labeled version 1. See checkpoint publication.
All ranks must agree on source periodicity before a bundle is published. On restart, every destination field is checked on every rank before collective field I/O; one rank’s topology mismatch rejects the complete request. The checkpoint service also leaves its clock, result counter and boundary records unchanged when that metadata validation fails.