Skip to main content

muse2/example/
patches.rs

1//! Patches to be used in integration tests.
2//!
3//! This is used to test small variations on existing example models.
4use crate::patch::FilePatch;
5use anyhow::{Context, Result};
6use std::{collections::BTreeMap, sync::LazyLock};
7/// Holds patch information for a patched example model
8pub struct PatchInfo {
9    /// The base example to patch
10    pub base_example: &'static str,
11    /// File patches to apply to the base example
12    pub file_patches: Vec<FilePatch>,
13    /// An optional TOML patch to apply to the base example
14    pub toml_patch: Option<&'static str>,
15}
16
17impl PatchInfo {
18    /// Create a new `PatchInfo` with the specified base example, file patches, and optional TOML patch
19    pub fn new(
20        base_example: &'static str,
21        file_patches: Vec<FilePatch>,
22        toml_patch: Option<&'static str>,
23    ) -> Self {
24        Self {
25            base_example,
26            file_patches,
27            toml_patch,
28        }
29    }
30    /// Create a new `PatchInfo` with the specified base example, file patches, and TOML patch
31    pub fn new_with_toml_patch(
32        base_example: &'static str,
33        file_patches: Vec<FilePatch>,
34        toml_patch: &'static str,
35    ) -> Self {
36        Self {
37            base_example,
38            file_patches,
39            toml_patch: Some(toml_patch),
40        }
41    }
42}
43/// Map of patches keyed by name, with the file patches and an optional TOML patch
44type PatchMap = BTreeMap<&'static str, PatchInfo>;
45
46/// The patches, keyed by name
47static PATCHES: LazyLock<PatchMap> = LazyLock::new(get_all_patches);
48
49/// Get all patches
50fn get_all_patches() -> PatchMap {
51    [
52        // The simple example with gas boiler process made divisible
53        (
54            "simple_divisible",
55            PatchInfo::new(
56                "simple",
57                vec![
58                    FilePatch::new("processes.csv")
59                        .with_deletion("RGASBR,Gas boiler,all,RSHEAT,2020,2040,1.0,")
60                        .with_addition("RGASBR,Gas boiler,all,RSHEAT,2020,2040,1.0,1000"),
61                ],
62                None,
63            ),
64        ),
65        // The simple example with objective type set to NPV for one agent
66        (
67            "simple_npv",
68            PatchInfo::new(
69                "simple",
70                vec![
71                    FilePatch::new("agent_objectives.csv")
72                        .with_deletion("A0_RES,all,lcox,,")
73                        .with_addition("A0_RES,all,npv,,"),
74                ],
75                None,
76            ),
77        ),
78        (
79            // The simple example with electricity priced using marginal costs
80            "simple_marginal",
81            PatchInfo::new(
82                "simple",
83                vec![FilePatch::new("commodities.csv").with_replacement(&[
84                    "id,description,type,time_slice_level,pricing_strategy,units",
85                    "GASPRD,Gas produced,sed,season,,PJ",
86                    "GASNAT,Natural gas,sed,season,,PJ",
87                    "ELCTRI,Electricity,sed,daynight,marginal,PJ",
88                    "RSHEAT,Residential heating,svd,daynight,,PJ",
89                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
90                ])],
91                None,
92            ),
93        ),
94        (
95            // The simple example with gas commodities priced using full costs
96            "simple_full",
97            PatchInfo::new(
98                "simple",
99                vec![FilePatch::new("commodities.csv").with_replacement(&[
100                    "id,description,type,time_slice_level,pricing_strategy,units",
101                    "GASPRD,Gas produced,sed,season,full,PJ",
102                    "GASNAT,Natural gas,sed,season,full,PJ",
103                    "ELCTRI,Electricity,sed,daynight,,PJ",
104                    "RSHEAT,Residential heating,svd,daynight,,PJ",
105                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
106                ])],
107                None,
108            ),
109        ),
110        (
111            // The simple example with electricity priced using average marginal costs
112            "simple_marginal_average",
113            PatchInfo::new(
114                "simple",
115                vec![FilePatch::new("commodities.csv").with_replacement(&[
116                    "id,description,type,time_slice_level,pricing_strategy,units",
117                    "GASPRD,Gas produced,sed,season,,PJ",
118                    "GASNAT,Natural gas,sed,season,,PJ",
119                    "ELCTRI,Electricity,sed,daynight,marginal_average,PJ",
120                    "RSHEAT,Residential heating,svd,daynight,,PJ",
121                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
122                ])],
123                None,
124            ),
125        ),
126        (
127            // The simple example with electricity priced using shadow prices
128            "simple_shadow",
129            PatchInfo::new(
130                "simple",
131                vec![FilePatch::new("commodities.csv").with_replacement(&[
132                    "id,description,type,time_slice_level,pricing_strategy,units",
133                    "GASPRD,Gas produced,sed,season,,PJ",
134                    "GASNAT,Natural gas,sed,season,,PJ",
135                    "ELCTRI,Electricity,sed,daynight,shadow,PJ",
136                    "RSHEAT,Residential heating,svd,daynight,,PJ",
137                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
138                ])],
139                None,
140            ),
141        ),
142        // The simple example with the ironing-out loop turned on
143        (
144            "simple_ironing_out",
145            PatchInfo::new("simple", vec![], Some("max_ironing_out_iterations = 10")),
146        ),
147    ]
148    .into_iter()
149    .collect()
150}
151
152/// Get the names for all the patches
153pub fn get_patch_names() -> impl Iterator<Item = &'static str> {
154    PATCHES.keys().copied()
155}
156
157/// Get patches for the named patched example
158pub fn get_patches(name: &str) -> Result<&'static PatchInfo> {
159    PATCHES
160        .get(name)
161        .with_context(|| format!("Patched example '{name}' not found"))
162}