Skip to content
12 changes: 7 additions & 5 deletions benches/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,15 @@ fn calculate_seed_prices(
candidates: &[AssetRef],
writer: &mut DataWriter,
) -> Prices {
let solution_existing = DispatchRun::new(model, base_year_assets, BASE_YEAR)
let market_demands = collect_preset_demands_for_year(&model.commodities, BASE_YEAR);
let solution_existing = DispatchRun::new(model, base_year_assets, BASE_YEAR, &market_demands)
.run("bench setup: without candidates", writer)
.expect("Dispatch without candidates failed");
let solution_with_candidates = DispatchRun::new(model, base_year_assets, BASE_YEAR)
.with_candidates(candidates)
.run("bench setup: with candidates", writer)
.expect("Dispatch with candidates failed");
let solution_with_candidates =
DispatchRun::new(model, base_year_assets, BASE_YEAR, &market_demands)
.with_candidates(candidates)
.run("bench setup: with candidates", writer)
.expect("Dispatch with candidates failed");

calculate_prices(
model,
Expand Down
4 changes: 0 additions & 4 deletions schemas/input/model.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,6 @@ properties:
type: integer
description: Number of iterations to perform when calculating prices for cyclically-dependent markets
default: 1
capacity_margin:
type: number
description: Slack proportion for assets selected during cycle balancing to absorb small demand shifts
default: 0.2
mothball_years:
type: integer
default: 0
Expand Down
180 changes: 146 additions & 34 deletions src/graph/investment.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,8 @@ pub fn load_model<P: AsRef<Path>>(model_dir: P) -> Result<Model> {
)?;

// Solve investment order for each region/year
let investment_order = solve_investment_order_for_model(&commodity_graphs, &commodities, years);
let investment_order =
solve_investment_order_for_model(&commodity_graphs, &commodities, years)?;

let model_path = model_dir
.as_ref()
Expand Down
10 changes: 0 additions & 10 deletions src/model/parameters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,6 @@ pub struct ModelParameters {
pub price_tolerance: Dimensionless,
/// Number of iterations to perform when calculating prices for cyclically-dependent markets.
pub price_cycle_iterations: u32,
/// Slack applied during cycle balancing, allowing newly selected assets to flex their capacity
/// by this proportion.
///
/// Existing assets remain fixed; this gives newly selected assets the wiggle-room to absorb
/// small demand changes before we would otherwise need to break for re-investment.
#[serde(deserialize_with = "deserialise_finite_non_negative")]
pub capacity_margin: Dimensionless,
/// Number of years an asset can remain unused before being decommissioned
pub mothball_years: u32,
/// Absolute tolerance when checking if remaining demand is close enough to zero
Expand Down Expand Up @@ -155,7 +148,6 @@ impl Default for ModelParameters {
max_ironing_out_iterations: 1,
price_tolerance: Dimensionless(1e-6),
price_cycle_iterations: 1,
capacity_margin: Dimensionless(0.2),
mothball_years: 0,
remaining_demand_absolute_tolerance: DEFAULT_REMAINING_DEMAND_ABSOLUTE_TOLERANCE,
highs: HighsOptions::default(),
Expand Down Expand Up @@ -368,8 +360,6 @@ impl ModelParameters {
// price_cycle_iterations
check_price_cycle_iterations(self.price_cycle_iterations)?;

// capacity_margin already validated with deserialise_finite_non_negative

// remaining_demand_absolute_tolerance already validated with
// deserialise_finite_non_negative; check remaining constraints here
check_remaining_demand_absolute_tolerance(
Expand Down
10 changes: 6 additions & 4 deletions src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::sync::Arc;
pub mod optimisation;
use optimisation::{DispatchRun, FlowMap};
pub mod investment;
use investment::perform_agent_investment;
use investment::{collect_preset_demands_for_year, perform_agent_investment};
pub mod market;
pub mod prices;
pub use prices::PriceMap;
Expand Down Expand Up @@ -178,13 +178,15 @@ fn run_dispatch_for_year(
debug_assert!(assets.iter().all(|asset| !asset.is_candidate()));
debug_assert!(candidates.iter().all(|asset| asset.is_candidate()));

let market_demands = collect_preset_demands_for_year(&model.commodities, year);

// Run dispatch optimisation with existing assets only, if there are any. If not, then assume no
// flows (i.e. all are zero)
let (solution_existing, flow_map) = if assets.is_empty() {
(None, FlowMap::default())
} else {
let solution =
DispatchRun::new(model, assets, year).run("final without candidates", writer)?;
let solution = DispatchRun::new(model, assets, year, &market_demands)
.run("final without candidates", writer)?;
let flow_map = solution.create_flow_map();
(Some(solution), flow_map)
};
Expand All @@ -195,7 +197,7 @@ fn run_dispatch_for_year(
None
} else {
Some(
DispatchRun::new(model, assets, year)
DispatchRun::new(model, assets, year, &market_demands)
.with_candidates(candidates)
.run("final with candidates", writer)?,
)
Expand Down
11 changes: 8 additions & 3 deletions src/simulation/investment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ pub fn perform_agent_investment(
writer: &mut DataWriter,
) -> Result<Vec<AssetRef>> {
// Initialise net demand map
let mut net_demand = collect_preset_demands_for_year(&model.commodities, year);
let preset_demands = collect_preset_demands_for_year(&model.commodities, year);
let mut net_demand = preset_demands.clone();

// Keep a list of all the assets selected
// This includes Commissioned assets that are selected for retention, and new Ready assets
Expand Down Expand Up @@ -110,7 +111,7 @@ pub fn perform_agent_investment(

// As upstream markets by definition will not yet have producers, we explicitly set
// their prices using external values so that they don't appear free
let solution = DispatchRun::new(model, &all_selected_assets, year)
let solution = DispatchRun::new(model, &all_selected_assets, year, &preset_demands)
.without_commodity_constraints()
.with_market_balance_subset(&seen_markets)
.with_input_prices(&prices.shadow)
Expand Down Expand Up @@ -179,10 +180,14 @@ pub fn update_net_demand_map(demand: &mut AllDemandMap, flows: &FlowMap, assets:
let selection = level.containing_selection(time_slice);
let key = (commodity_id.clone(), asset.region_id().clone(), selection);
// Note: we use the negative of the flow as input flows are negative in the flow map.
demand
let value = demand
.entry(key)
.and_modify(|value| *value -= *flow)
.or_insert(-*flow);

if *value < Flow(0.0) {
*value = Flow(0.0);
}
}
}
}
Expand Down
Loading
Loading