1pub mod config;
2pub mod errors;
3pub mod integrator;
4pub mod metrics;
5pub mod model;
6pub mod monitor;
7pub mod outputs;
8pub mod plots;
9pub mod scenarios;
10pub mod state;
11pub mod sweep;
12
13use std::fs;
14use std::path::{Path, PathBuf};
15
16use anyhow::Result;
17
18use crate::config::{
19 OutputLayout, ResolvedRunConfig, RunConfig, ScenarioOverrides, ScenarioPreset, SweepPreset,
20};
21use crate::integrator::simulate;
22use crate::outputs::{prepare_run_root, write_run_outputs, write_sweep_outputs};
23use crate::scenarios::{build_scenario_config, build_sweep_cases};
24use crate::sweep::run_sweep;
25
26pub fn run_scenario_preset(
27 preset: ScenarioPreset,
28 overrides: ScenarioOverrides,
29 output_root: impl AsRef<Path>,
30 seed: u64,
31) -> Result<PathBuf> {
32 let output_root = output_root.as_ref();
33 let run_root = prepare_run_root(output_root)?;
34 let config = build_scenario_config(preset, overrides, seed)?;
35 let result = simulate(config)?;
36 write_run_outputs(&run_root, &result)?;
37 Ok(run_root)
38}
39
40pub fn run_sweep_preset(
41 preset: SweepPreset,
42 overrides: ScenarioOverrides,
43 output_root: impl AsRef<Path>,
44 seed: u64,
45) -> Result<PathBuf> {
46 let output_root = output_root.as_ref();
47 let run_root = prepare_run_root(output_root)?;
48 let cases = build_sweep_cases(preset, overrides, seed)?;
49 let aggregate = run_sweep(preset, cases, &run_root)?;
50 write_sweep_outputs(&run_root, &aggregate)?;
51 Ok(run_root)
52}
53
54pub fn run_config_file(path: impl AsRef<Path>) -> Result<PathBuf> {
55 run_config_file_with_overrides(path, None, None)
56}
57
58pub fn run_config_file_with_overrides(
59 path: impl AsRef<Path>,
60 output_root_override: Option<&Path>,
61 seed_override: Option<u64>,
62) -> Result<PathBuf> {
63 let path = path.as_ref();
64 let raw = fs::read_to_string(path)?;
65 let mut run_config: RunConfig = serde_json::from_str(&raw)?;
66 apply_cli_overrides(&mut run_config, output_root_override, seed_override);
67 match ResolvedRunConfig::from_run_config(run_config, path.parent())? {
68 ResolvedRunConfig::Scenario {
69 config,
70 output_layout,
71 } => {
72 let run_root = prepare_run_root(output_layout.output_root())?;
73 let result = simulate(config)?;
74 write_run_outputs(&run_root, &result)?;
75 Ok(run_root)
76 }
77 ResolvedRunConfig::Sweep {
78 preset,
79 cases,
80 output_layout,
81 } => {
82 let run_root = prepare_run_root(output_layout.output_root())?;
83 let aggregate = run_sweep(preset, cases, &run_root)?;
84 write_sweep_outputs(&run_root, &aggregate)?;
85 Ok(run_root)
86 }
87 }
88}
89
90pub fn default_output_layout() -> OutputLayout {
91 OutputLayout::default()
92}
93
94fn apply_cli_overrides(
95 run_config: &mut RunConfig,
96 output_root_override: Option<&Path>,
97 seed_override: Option<u64>,
98) {
99 match run_config {
100 RunConfig::Scenario {
101 seed, output_root, ..
102 }
103 | RunConfig::Sweep {
104 seed, output_root, ..
105 } => {
106 if let Some(value) = output_root_override {
107 *output_root = Some(value.to_path_buf());
108 }
109 if let Some(value) = seed_override {
110 *seed = Some(value);
111 }
112 }
113 }
114}