Skip to content

Commit f4026b4

Browse files
committed
add more information when no feasible investment options left
force all non-default remaining_demand_absolute_tolerance parameter changes to require borken results enabled
1 parent 5135d29 commit f4026b4

3 files changed

Lines changed: 28 additions & 15 deletions

File tree

schemas/input/model.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ properties:
5454
type: number
5555
description: |
5656
Absolute tolerance when checking if remaining demand is close enough to zero in the
57-
investment cycle.
57+
investment cycle. Changing the value of this parameter is potentially dangerous,
58+
so it requires setting `please_give_me_broken_results` to true.
5859
default: 1e-12
5960

6061
required: [milestone_years]

src/model/parameters.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,11 @@ fn check_remaining_demand_absolute_tolerance(
177177
"remaining_demand_absolute_tolerance must be a finite number greater than or equal to zero"
178178
);
179179

180-
// we already checked value is positive, but if they are
181-
// increasing it above the default this is potentially dangerous.
182180
let default_value = default_remaining_demand_absolute_tolerance();
183181
if !allow_broken_options {
184182
ensure!(
185-
value <= default_value,
186-
"Setting a remaining_demand_absolute_tolerance higher than the default value of {:e} \
183+
value == default_value,
184+
"Setting a remaining_demand_absolute_tolerance different from the default value of {:e} \
187185
is potentially dangerous, set please_give_me_broken_results to true \
188186
if you want to allow this.",
189187
default_value.0
@@ -392,10 +390,10 @@ mod tests {
392390
}
393391

394392
#[rstest]
395-
#[case(false, 0.0, true)] // Valid minimum value (exactly zero)
393+
#[case(true, 0.0, true)] // Valid minimum value broken options allowed
396394
#[case(true, 1e-10, true)] // Valid value with broken options allowed
397395
#[case(true, 1e-15, true)] // Valid value with broken options allowed
398-
#[case(false, 1e-15, true)] // Valid value below default, no broken options needed
396+
#[case(false, 1e-12, true)] // Valid value same as default, no broken options needed
399397
#[case(true, 1.0, true)] // Valid larger value with broken options allowed
400398
#[case(true, f64::MAX, true)] // Valid maximum finite value with broken options allowed
401399
#[case(true, -1e-10, false)] // Invalid: negative value
@@ -423,10 +421,11 @@ mod tests {
423421
}
424422

425423
#[rstest]
424+
#[case(0.0)] // smaller than default
426425
#[case(1e-10)] // Larger than default (1e-12)
427426
#[case(1.0)] // Well above default
428427
#[case(f64::MAX)] // Maximum finite value
429-
fn check_remaining_demand_absolute_tolerance_requires_broken_options_above_default(
428+
fn check_remaining_demand_absolute_tolerance_requires_broken_options_if_non_default(
430429
#[case] value: f64,
431430
) {
432431
let flow = Flow::new(value);
@@ -435,7 +434,7 @@ mod tests {
435434
result,
436435
false,
437436
value,
438-
"Setting a remaining_demand_absolute_tolerance higher than the default value \
437+
"Setting a remaining_demand_absolute_tolerance different from the default value \
439438
of 1e-12 is potentially dangerous, set \
440439
please_give_me_broken_results to true if you want to allow this.",
441440
);

src/simulation/investment.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::region::RegionID;
99
use crate::simulation::CommodityPrices;
1010
use crate::time_slice::{TimeSliceID, TimeSliceInfo};
1111
use crate::units::{Capacity, Dimensionless, Flow, FlowPerCapacity};
12-
use anyhow::{Context, Result, ensure};
12+
use anyhow::{Context, Result, bail, ensure};
1313
use indexmap::IndexMap;
1414
use itertools::{Itertools, chain};
1515
use log::debug;
@@ -808,11 +808,24 @@ fn select_best_assets(
808808
// demand.
809809
// - known issue with the NPV objective
810810
// (see https://github.com/EnergySystemsModellingLab/MUSE2/issues/716).
811-
ensure!(
812-
!outputs_for_opts.is_empty(),
813-
"No feasible investment options for commodity '{}' after appraisal",
814-
&commodity.id
815-
);
811+
if outputs_for_opts.is_empty() {
812+
let remaining_demands: Vec<_> = demand
813+
.iter()
814+
.filter(|(_, flow)| **flow > Flow(0.0))
815+
.map(|(time_slice, flow)| format!("{} : {:e}", time_slice, flow.value()))
816+
.collect();
817+
818+
bail!(
819+
"No feasible investment options left for \
820+
commodity '{}', region '{}', year '{}', agent '{}' after appraisal.\n\
821+
Remaining unmet demand (time_slice : flow):\n{}",
822+
&commodity.id,
823+
region_id,
824+
year,
825+
agent.id,
826+
remaining_demands.join("\n")
827+
);
828+
}
816829

817830
// Warn if there are multiple equally good assets
818831
warn_on_equal_appraisal_outputs(&outputs_for_opts, &agent.id, &commodity.id, region_id);

0 commit comments

Comments
 (0)