scientific_workflow/configuration.rs
1//! Strict study settings, named paths, and phase-scoped parameter expansion.
2//!
3//! [`StudySettings`] loads the process-level replicate policy and preserves one
4//! opaque application-owned object from `study.json`. It remains separate from
5//! the scientific parameter registry:
6//!
7//! ```json
8//! {
9//! "replicate_settings": {
10//! "replicates": 1,
11//! "scheduling": "sequential",
12//! "failure_policy": "fail_fast",
13//! "base_seed": 1101
14//! },
15//! "application": {
16//! "protocol": "my-application-study"
17//! }
18//! }
19//! ```
20//!
21//! A study stores scientific parameters in `config/parameters.json` and named
22//! paths independently in `config/paths.json`. Parameter configuration has one
23//! global scope and one or more string-keyed components. Each component
24//! contains shared parameters and string-keyed workloads:
25//!
26//! ```text
27//! global
28//! components
29//! └── <component key>
30//! ├── shared
31//! └── workloads
32//! └── <workload key>
33//! ```
34//!
35//! [`StudyConfiguration`] validates the complete registry. Calling
36//! [`StudyConfiguration::workload`] returns a [`WorkloadConfiguration`], whose
37//! combinations are the Cartesian composition of global, component-shared,
38//! and workload-local selections. There is deliberately no component-level
39//! combination API: components share values but are not expandable spaces.
40//!
41//! Ordinary JSON values, including arrays, are literal. An object containing
42//! exactly `"$sweep"` declares independent Cartesian choices. A scope-level
43//! `"$cases"` array declares correlated alternatives. One scope cannot mix the
44//! two forms.
45//!
46//! ```json
47//! {
48//! "global": {
49//! "temperature": {"$sweep": [280.0, 300.0]},
50//! "lattice_shape": [64]
51//! },
52//! "components": {
53//! "models": {
54//! "shared": {"seed": {"$sweep": [7, 11]}},
55//! "workloads": {
56//! "glv": {"solver": {"step": 0.01}},
57//! "analysis": {"include_space": true}
58//! }
59//! }
60//! }
61//! }
62//! ```
63//!
64//! ```no_run
65//! use scientific_workflow::configuration::{ProjectPaths, StudyConfiguration};
66//!
67//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
68//! let study = StudyConfiguration::load("scientific-study")?;
69//! let models = study.workload("models", "glv")?;
70//! let paths = ProjectPaths::load("scientific-study")?;
71//!
72//! for configuration in models.combinations() {
73//! let (temperature, seed): (f64, u64) =
74//! configuration.decode_values(("/temperature", "/seed"))?;
75//! println!("temperature={temperature} seed={seed}");
76//! }
77//! println!("recordings={}", paths.resolve_path("recordings")?.display());
78//! # Ok(())
79//! }
80//! ```
81//!
82//! Configuration loading is immutable and side-effect free beyond reading its
83//! source. It validates execution policy but does not enact it, create tasks,
84//! resolve model semantics, create output, or inspect path targets.
85//! Applications pass [`ReplicateSettings`] to the execution module and map
86//! [`ResolvedConfiguration`] values into their own workloads.
87
88mod error;
89mod parameter_key_tuple;
90mod parameter_path;
91mod parameter_tree;
92mod parameters;
93mod paths;
94mod settings;
95pub(crate) mod source;
96mod sweep;
97
98pub use error::ConfigurationError;
99#[doc(hidden)]
100pub use parameter_key_tuple::ParameterKeyTuple;
101pub use parameters::{
102 ConfigurationIter, ResolvedConfiguration, StudyConfiguration, WorkloadConfiguration,
103};
104pub use paths::ProjectPaths;
105pub use settings::{ReplicateFailurePolicy, ReplicateScheduling, ReplicateSettings, StudySettings};