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
50#[allow(clippy::too_many_lines)]
51fn get_all_patches() -> PatchMap {
52    [
53        // The simple example with gas boiler process made divisible
54        (
55            "simple_divisible",
56            PatchInfo::new(
57                "simple",
58                vec![
59                    FilePatch::new("processes.csv")
60                        .with_deletion("RGASBR,Gas boiler,all,RSHEAT,2020,2040,1.0,")
61                        .with_addition("RGASBR,Gas boiler,all,RSHEAT,2020,2040,1.0,1000"),
62                ],
63                None,
64            ),
65        ),
66        // The simple example with objective type set to NPV for one agent
67        (
68            "simple_npv",
69            PatchInfo::new(
70                "simple",
71                vec![
72                    FilePatch::new("agent_objectives.csv")
73                        .with_deletion("A0_RES,all,lcox,,")
74                        .with_addition("A0_RES,all,npv,,"),
75                ],
76                None,
77            ),
78        ),
79        (
80            // The circularity example with Agent A0_ELC's objective type set to NPV
81            "circularity_npv",
82            PatchInfo::new(
83                "circularity",
84                vec![
85                    FilePatch::new("agent_objectives.csv")
86                        .with_deletion("A0_ELC,all,lcox,,")
87                        .with_addition("A0_ELC,all,npv,,"),
88                ],
89                None,
90            ),
91        ),
92        (
93            // The simple example with electricity priced using marginal costs
94            "simple_marginal",
95            PatchInfo::new(
96                "simple",
97                vec![FilePatch::new("commodities.csv").with_replacement(&[
98                    "id,description,type,time_slice_level,pricing_strategy,units",
99                    "GASPRD,Gas produced,sed,season,,PJ",
100                    "GASNAT,Natural gas,sed,season,,PJ",
101                    "ELCTRI,Electricity,sed,daynight,marginal,PJ",
102                    "RSHEAT,Residential heating,svd,daynight,,PJ",
103                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
104                ])],
105                None,
106            ),
107        ),
108        (
109            // The simple example with gas commodities priced using full costs
110            "simple_full",
111            PatchInfo::new(
112                "simple",
113                vec![FilePatch::new("commodities.csv").with_replacement(&[
114                    "id,description,type,time_slice_level,pricing_strategy,units",
115                    "GASPRD,Gas produced,sed,season,full,PJ",
116                    "GASNAT,Natural gas,sed,season,full,PJ",
117                    "ELCTRI,Electricity,sed,daynight,,PJ",
118                    "RSHEAT,Residential heating,svd,daynight,,PJ",
119                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
120                ])],
121                None,
122            ),
123        ),
124        (
125            // The simple example with electricity priced using average marginal costs
126            "simple_marginal_average",
127            PatchInfo::new(
128                "simple",
129                vec![FilePatch::new("commodities.csv").with_replacement(&[
130                    "id,description,type,time_slice_level,pricing_strategy,units",
131                    "GASPRD,Gas produced,sed,season,,PJ",
132                    "GASNAT,Natural gas,sed,season,,PJ",
133                    "ELCTRI,Electricity,sed,daynight,marginal_average,PJ",
134                    "RSHEAT,Residential heating,svd,daynight,,PJ",
135                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
136                ])],
137                None,
138            ),
139        ),
140        (
141            // The simple example with electricity priced using shadow prices
142            "simple_shadow",
143            PatchInfo::new(
144                "simple",
145                vec![FilePatch::new("commodities.csv").with_replacement(&[
146                    "id,description,type,time_slice_level,pricing_strategy,units",
147                    "GASPRD,Gas produced,sed,season,,PJ",
148                    "GASNAT,Natural gas,sed,season,,PJ",
149                    "ELCTRI,Electricity,sed,daynight,shadow,PJ",
150                    "RSHEAT,Residential heating,svd,daynight,,PJ",
151                    "CO2EMT,CO2 emitted,oth,annual,,ktCO2",
152                ])],
153                None,
154            ),
155        ),
156        // The simple example with the ironing-out loop turned on
157        (
158            "simple_ironing_out",
159            PatchInfo::new("simple", vec![], Some("max_ironing_out_iterations = 10")),
160        ),
161        // The simple example with mothball_years set to a value >0
162        (
163            "simple_mothball",
164            PatchInfo::new("simple", vec![], Some("mothball_years = 10")),
165        ),
166    ]
167    .into_iter()
168    .collect()
169}
170
171/// Get the names for all the patches
172pub fn get_patch_names() -> impl Iterator<Item = &'static str> {
173    PATCHES.keys().copied()
174}
175
176/// Get patches for the named patched example
177pub fn get_patches(name: &str) -> Result<&'static PatchInfo> {
178    PATCHES
179        .get(name)
180        .with_context(|| format!("Patched example '{name}' not found"))
181}