Skip to content

Commit 21b4efa

Browse files
authored
Merge pull request #902 from EnergySystemsModellingLab/gate-broken-model-options
Scare users away from using broken options
2 parents 53f2877 + 6becb60 commit 21b4efa

7 files changed

Lines changed: 65 additions & 7 deletions

File tree

schemas/input/agent_objectives.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ fields:
2323
description: The type of objective
2424
notes: |
2525
Must be `npv` (net present value) or `lcox` (levelised cost of X). Note that support for NPV
26-
is experimental and may give bad results.
26+
is [currently broken](https://github.com/EnergySystemsModellingLab/MUSE2/issues/716), so don't
27+
enable this option unless you know what you're doing.
2728
- name: decision_weight
2829
type: number
2930
description: Weight for weighted sum decision rule

schemas/input/model.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,9 @@ properties:
4747
type: number
4848
description: The relative tolerance for price convergence in the ironing out loop
4949
default: 1e-6
50+
please_give_me_broken_results:
51+
type: boolean
52+
description: |
53+
Allows other options that are known to be broken to be used. Please don't ever enable this.
5054
5155
required: [milestone_years]

src/input/agent/objective.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Code for reading the agent objectives CSV file.
22
use super::super::{input_err_msg, read_csv, try_insert};
3+
use crate::ISSUES_URL;
34
use crate::agent::{AgentID, AgentMap, AgentObjectiveMap, DecisionRule, ObjectiveType};
5+
use crate::model::{ALLOW_BROKEN_OPTION_NAME, broken_model_options_allowed};
46
use crate::units::Dimensionless;
57
use crate::year::parse_year_str;
68
use anyhow::{Context, Result, ensure};
@@ -32,6 +34,8 @@ struct AgentObjectiveRaw {
3234
/// # Arguments
3335
///
3436
/// * `model_dir` - Folder containing model configuration files
37+
/// * `agents` - Map of agents
38+
/// * `milestone_years` - Milestone years for the simulation
3539
///
3640
/// # Returns
3741
///
@@ -96,9 +100,15 @@ where
96100
.filter(|year| agent_objectives[year] == ObjectiveType::NetPresentValue)
97101
.collect_vec();
98102
if !npv_years.is_empty() {
103+
ensure!(
104+
broken_model_options_allowed(),
105+
"The NPV option is BROKEN and should not be used. See: {ISSUES_URL}/716.\n\
106+
If you are sure that you want to enable it anyway, you need to set the \
107+
{ALLOW_BROKEN_OPTION_NAME} option to true."
108+
);
99109
warn!(
100110
"Agent {agent_id} is using NPV in years {npv_years:?}. \
101-
Support for NPV is currently experimental and may give bad results."
111+
The NPV option is BROKEN and should not be used. See: {ISSUES_URL}/716."
102112
);
103113
}
104114
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Common functionality for MUSE2.
22
#![warn(missing_docs)]
33

4+
/// The main GitHub issues page for MUSE2
5+
pub const ISSUES_URL: &str = concat!(env!("CARGO_PKG_REPOSITORY"), "/issues");
6+
47
use dirs::config_dir;
58
use std::path::PathBuf;
69

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use human_panic::{metadata, setup_panic};
22
use log::error;
3+
use muse2::ISSUES_URL;
34
use muse2::cli::run_cli;
45
use muse2::log::is_logger_initialised;
56

67
fn main() {
78
setup_panic!(metadata!().support(format!(
8-
"Open an issue on Github: {}/issues/new?template=bug_report.md",
9-
env!("CARGO_PKG_REPOSITORY")
9+
"Open an issue on Github: {ISSUES_URL}/new?template=bug_report.md"
1010
)));
1111

1212
if let Err(err) = run_cli() {

src/model.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use std::collections::HashMap;
88
use std::path::PathBuf;
99

1010
pub mod parameters;
11-
pub use parameters::{ModelParameters, PricingStrategy};
11+
pub use parameters::{
12+
ALLOW_BROKEN_OPTION_NAME, ModelParameters, PricingStrategy, broken_model_options_allowed,
13+
};
1214

1315
/// Model definition
1416
pub struct Model {

src/model/parameters.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Defines the `ModelParameters` struct, which represents the contents of `model.toml`.
2+
use crate::ISSUES_URL;
23
use crate::asset::check_capacity_valid_for_asset;
34
use crate::input::{
45
deserialise_proportion_nonzero, input_err_msg, is_sorted_and_unique, read_toml,
@@ -9,9 +10,23 @@ use log::warn;
910
use serde::Deserialize;
1011
use serde_string_enum::DeserializeLabeledStringEnum;
1112
use std::path::Path;
13+
use std::sync::OnceLock;
1214

1315
const MODEL_PARAMETERS_FILE_NAME: &str = "model.toml";
1416

17+
/// The name of the option used to gate other, broken options.
18+
pub const ALLOW_BROKEN_OPTION_NAME: &str = "please_give_me_broken_results";
19+
20+
/// Whether broken options have been enabled by an option in the model config file
21+
static BROKEN_OPTIONS_ALLOWED: OnceLock<bool> = OnceLock::new();
22+
23+
/// Whether broken model options have been enabled in the config file or not
24+
pub fn broken_model_options_allowed() -> bool {
25+
*BROKEN_OPTIONS_ALLOWED
26+
.get()
27+
.expect("Broken options flag not set")
28+
}
29+
1530
macro_rules! define_unit_param_default {
1631
($name:ident, $type: ty, $value: expr) => {
1732
fn $name() -> $type {
@@ -42,6 +57,9 @@ define_param_default!(default_max_ironing_out_iterations, u32, 10);
4257
pub struct ModelParameters {
4358
/// Milestone years
4459
pub milestone_years: Vec<u32>,
60+
/// Allow known-broken options to be enabled.
61+
#[serde(default, rename = "please_give_me_broken_results")] // Can't use constant here :-(
62+
pub allow_broken_options: bool,
4563
/// The (small) value of capacity given to candidate assets.
4664
///
4765
/// Don't change unless you know what you're doing.
@@ -134,6 +152,11 @@ impl ModelParameters {
134152
let file_path = model_dir.as_ref().join(MODEL_PARAMETERS_FILE_NAME);
135153
let model_params: ModelParameters = read_toml(&file_path)?;
136154

155+
// Set flag signalling whether broken model options are allowed or not
156+
BROKEN_OPTIONS_ALLOWED
157+
.set(model_params.allow_broken_options)
158+
.unwrap(); // Will only fail if there is a race condition, which shouldn't happen
159+
137160
model_params
138161
.validate()
139162
.with_context(|| input_err_msg(file_path))?;
@@ -143,15 +166,30 @@ impl ModelParameters {
143166

144167
/// Validate parameters after reading in file
145168
fn validate(&self) -> Result<()> {
169+
if self.allow_broken_options {
170+
warn!(
171+
"!!! You've enabled the {ALLOW_BROKEN_OPTION_NAME} option. !!!\n\
172+
I see you like to live dangerously 😈. This option should ONLY be used by \
173+
developers as it can cause peculiar behaviour that breaks things. NEVER enable it \
174+
for results you actually care about or want to publish. You have been warned!"
175+
);
176+
}
177+
146178
// milestone_years
147179
check_milestone_years(&self.milestone_years)?;
148180

149181
// pricing_strategy
150182
if self.pricing_strategy == PricingStrategy::ScarcityAdjusted {
183+
ensure!(
184+
self.allow_broken_options,
185+
"The pricing strategy is set to 'scarcity_adjusted', which is known to be broken. \
186+
If you are sure that you want to enable it anyway, you need to set the \
187+
{ALLOW_BROKEN_OPTION_NAME} option to true."
188+
);
189+
151190
warn!(
152191
"The pricing strategy is set to 'scarcity_adjusted'. Commodity prices may be \
153-
incorrect if assets have more than one output commodity. See: {}/issues/677",
154-
env!("CARGO_PKG_REPOSITORY")
192+
incorrect if assets have more than one output commodity. See: {ISSUES_URL}/677"
155193
);
156194
}
157195

0 commit comments

Comments
 (0)