From 98ccbb47d6926e65ceb90356934c3e658ea94bd6 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 24 Feb 2025 16:14:36 +0000 Subject: [PATCH 1/5] Rename: `cap2act` => `capacity_to_activity` --- examples/simple/process_parameters.csv | 2 +- src/agent.rs | 6 ++--- src/input/asset.rs | 2 +- src/input/process.rs | 2 +- src/input/process/parameter.rs | 32 +++++++++++++------------- src/process.rs | 9 ++++---- src/simulation/optimisation.rs | 2 +- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/examples/simple/process_parameters.csv b/examples/simple/process_parameters.csv index 8cb04c6a9..3f7564125 100644 --- a/examples/simple/process_parameters.csv +++ b/examples/simple/process_parameters.csv @@ -1,4 +1,4 @@ -process_id,start_year,end_year,capital_cost,fixed_operating_cost,variable_operating_cost,lifetime,discount_rate,cap2act +process_id,start_year,end_year,capital_cost,fixed_operating_cost,variable_operating_cost,lifetime,discount_rate,capacity_to_activity GASDRV,2020,2030,10.0,0.3,2.0,25,0.1,1.0 GASPRC,2020,2030,7.0,0.21,0.5,25,0.1,1.0 WNDFRM,2020,2030,1000.0,30.0,0.4,25,0.1,31.54 diff --git a/src/agent.rs b/src/agent.rs index d38fceec2..4d9eaf4dd 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -144,7 +144,7 @@ impl Asset { /// Get the activity limits for this asset in a particular time slice pub fn get_activity_limits(&self, time_slice: &TimeSliceID) -> RangeInclusive { let limits = self.process.capacity_fractions.get(time_slice).unwrap(); - let capacity_a = self.capacity * self.process.parameter.cap2act; + let capacity_a = self.capacity * self.process.parameter.capacity_to_activity; // Multiply the fractional capacity in self.process by this asset's actual capacity (capacity_a * limits.start())..=(capacity_a * limits.end()) @@ -263,7 +263,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 5, discount_rate: 0.9, - cap2act: 3.0, + capacity_to_activity: 3.0, }; let commodity = Rc::new(Commodity { id: "commodity1".into(), @@ -312,7 +312,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 5, discount_rate: 0.9, - cap2act: 1.0, + capacity_to_activity: 1.0, }; let process = Rc::new(Process { id: "process1".into(), diff --git a/src/input/asset.rs b/src/input/asset.rs index 994b12c31..92fbecdaf 100644 --- a/src/input/asset.rs +++ b/src/input/asset.rs @@ -107,7 +107,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 5, discount_rate: 0.9, - cap2act: 1.0, + capacity_to_activity: 1.0, }; let process = Rc::new(Process { id: "process1".into(), diff --git a/src/input/process.rs b/src/input/process.rs index 55377062d..f3c61ee0f 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -314,7 +314,7 @@ mod tests { variable_operating_cost: 0.0, lifetime: 1, discount_rate: 1.0, - cap2act: 0.0, + capacity_to_activity: 0.0, }; (id.into(), parameter) diff --git a/src/input/process/parameter.rs b/src/input/process/parameter.rs index d4bd0a2d0..fdaa4bc17 100644 --- a/src/input/process/parameter.rs +++ b/src/input/process/parameter.rs @@ -22,7 +22,7 @@ struct ProcessParameterRaw { pub variable_operating_cost: f64, pub lifetime: u32, pub discount_rate: Option, - pub cap2act: Option, + pub capacity_to_activity: Option, } define_process_id_getter! {ProcessParameterRaw} @@ -48,7 +48,7 @@ impl ProcessParameterRaw { variable_operating_cost: self.variable_operating_cost, lifetime: self.lifetime, discount_rate: self.discount_rate.unwrap_or(0.0), - cap2act: self.cap2act.unwrap_or(1.0), + capacity_to_activity: self.capacity_to_activity.unwrap_or(1.0), }) } } @@ -61,7 +61,7 @@ impl ProcessParameterRaw { /// Returns an error if: /// - `lifetime` is 0. /// - `discount_rate` is present and less than 0.0. - /// - `cap2act` is present and less than 0.0. + /// - `capacity_to_activity` is present and less than 0.0. /// /// # Warnings /// @@ -93,7 +93,7 @@ impl ProcessParameterRaw { } } - if let Some(c2a) = self.cap2act { + if let Some(c2a) = self.capacity_to_activity { ensure!( c2a >= 0.0, "Error in parameter for process {}: Cap2act must be positive", @@ -146,7 +146,7 @@ mod tests { end_year: Option, lifetime: u32, discount_rate: Option, - cap2act: Option, + capacity_to_activity: Option, ) -> ProcessParameterRaw { ProcessParameterRaw { process_id: "id".to_string(), @@ -157,14 +157,14 @@ mod tests { variable_operating_cost: 0.0, lifetime, discount_rate, - cap2act, + capacity_to_activity, } } fn create_param( years: RangeInclusive, discount_rate: f64, - cap2act: f64, + capacity_to_activity: f64, ) -> ProcessParameter { ProcessParameter { process_id: "id".to_string(), @@ -174,7 +174,7 @@ mod tests { variable_operating_cost: 0.0, lifetime: 1, discount_rate, - cap2act, + capacity_to_activity, } } @@ -203,7 +203,7 @@ mod tests { create_param(2010..=2020, 0.0, 0.0) ); - // Missing cap2act + // Missing capacity_to_activity let raw = create_param_raw(Some(2010), Some(2020), 1, Some(1.0), None); assert_eq!( raw.into_parameter(&year_range).unwrap(), @@ -295,7 +295,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: Some(1.0), - cap2act: Some(1.0), + capacity_to_activity: Some(1.0), }, ProcessParameterRaw { process_id: "B".into(), @@ -306,7 +306,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: Some(1.0), - cap2act: Some(1.0), + capacity_to_activity: Some(1.0), }, ]; @@ -321,7 +321,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: 1.0, - cap2act: 1.0, + capacity_to_activity: 1.0, }, ), ( @@ -334,7 +334,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: 1.0, - cap2act: 1.0, + capacity_to_activity: 1.0, }, ), ] @@ -361,7 +361,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: Some(1.0), - cap2act: Some(1.0), + capacity_to_activity: Some(1.0), }, ProcessParameterRaw { process_id: "B".into(), @@ -372,7 +372,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: Some(1.0), - cap2act: Some(1.0), + capacity_to_activity: Some(1.0), }, ProcessParameterRaw { process_id: "A".into(), @@ -383,7 +383,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 10, discount_rate: Some(1.0), - cap2act: Some(1.0), + capacity_to_activity: Some(1.0), }, ]; diff --git a/src/process.rs b/src/process.rs index c0db5f82d..f175438b6 100644 --- a/src/process.rs +++ b/src/process.rs @@ -107,10 +107,11 @@ pub struct ProcessParameter { pub lifetime: u32, /// Process-specific discount rate pub discount_rate: f64, - /// Factor for calculating the maximum PAC output over a year ("capacity to activity"). + /// Factor for calculating the maximum PAC output over a year. /// /// Used for converting one unit of capacity to maximum activity of the PAC per year. For - /// example, if capacity is measured in GW and activity is measured in PJ, the cap2act for the - /// process is 31.536 because 1 GW of capacity can produce 31.536 PJ energy output in a year. - pub cap2act: f64, + /// example, if capacity is measured in GW and activity is measured in PJ, the + /// capacity_to_activity for the process is 31.536 because 1 GW of capacity can produce 31.536 + /// PJ energy output in a year. + pub capacity_to_activity: f64, } diff --git a/src/simulation/optimisation.rs b/src/simulation/optimisation.rs index 6e3aa927b..7ca5bc978 100644 --- a/src/simulation/optimisation.rs +++ b/src/simulation/optimisation.rs @@ -465,7 +465,7 @@ mod tests { variable_operating_cost: 1.0, lifetime: 5, discount_rate: 0.9, - cap2act: 1.0, + capacity_to_activity: 1.0, }; let commodity = Rc::new(Commodity { id: "commodity1".into(), From c534c72d2756a0ff8bd013a04967e50ab83b79b1 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 24 Feb 2025 16:24:33 +0000 Subject: [PATCH 2/5] Add helper to calculate maximum activity --- src/agent.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 4d9eaf4dd..9843077a2 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -144,10 +144,17 @@ impl Asset { /// Get the activity limits for this asset in a particular time slice pub fn get_activity_limits(&self, time_slice: &TimeSliceID) -> RangeInclusive { let limits = self.process.capacity_fractions.get(time_slice).unwrap(); - let capacity_a = self.capacity * self.process.parameter.capacity_to_activity; + let max_act = self.maximum_activity(); // Multiply the fractional capacity in self.process by this asset's actual capacity - (capacity_a * limits.start())..=(capacity_a * limits.end()) + (max_act * limits.start())..=(max_act * limits.end()) + } + + /// Maximum activity for this asset in a year. + /// + /// This was referred to as `capacity_a` in MUSE 1.0. + pub fn maximum_activity(&self) -> f64 { + self.capacity * self.process.parameter.capacity_to_activity } } From 6a2be8b3336cdd02c67c41737fbf01b6b8dcd027 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Tue, 11 Mar 2025 16:46:46 +0000 Subject: [PATCH 3/5] Rename: "capacity limits" => "activity limits" This name is more descriptive. --- src/agent.rs | 10 +++++----- src/input/asset.rs | 6 +++--- src/input/process.rs | 14 +++++++------- src/input/process/availability.rs | 16 +++++++--------- src/process.rs | 16 ++++++++-------- src/simulation/optimisation.rs | 4 ++-- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 9843077a2..832c50463 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -143,7 +143,7 @@ impl Asset { /// Get the activity limits for this asset in a particular time slice pub fn get_activity_limits(&self, time_slice: &TimeSliceID) -> RangeInclusive { - let limits = self.process.capacity_fractions.get(time_slice).unwrap(); + let limits = self.process.activity_limits.get(time_slice).unwrap(); let max_act = self.maximum_activity(); // Multiply the fractional capacity in self.process by this asset's actual capacity @@ -251,7 +251,7 @@ impl AssetPool { mod tests { use super::*; use crate::commodity::{CommodityCostMap, CommodityType, DemandMap}; - use crate::process::{FlowType, Process, ProcessCapacityMap, ProcessFlow, ProcessParameter}; + use crate::process::{ActivityLimitsMap, FlowType, Process, ProcessFlow, ProcessParameter}; use crate::time_slice::TimeSliceLevel; use itertools::{assert_equal, Itertools}; use std::iter; @@ -289,11 +289,11 @@ mod tests { is_pac: true, }; let fraction_limits = 1.0..=f64::INFINITY; - let capacity_fractions = iter::once((time_slice.clone(), fraction_limits)).collect(); + let activity_limits = iter::once((time_slice.clone(), fraction_limits)).collect(); let process = Rc::new(Process { id: "process1".into(), description: "Description".into(), - capacity_fractions, + activity_limits, flows: vec![flow.clone()], parameter: process_param.clone(), regions: RegionSelection::All, @@ -324,7 +324,7 @@ mod tests { let process = Rc::new(Process { id: "process1".into(), description: "Description".into(), - capacity_fractions: ProcessCapacityMap::new(), + activity_limits: ActivityLimitsMap::new(), flows: vec![], parameter: process_param.clone(), regions: RegionSelection::All, diff --git a/src/input/asset.rs b/src/input/asset.rs index 92fbecdaf..933ff0244 100644 --- a/src/input/asset.rs +++ b/src/input/asset.rs @@ -92,7 +92,7 @@ where #[cfg(test)] mod tests { use super::*; - use crate::process::{Process, ProcessCapacityMap, ProcessParameter}; + use crate::process::{ActivityLimitsMap, Process, ProcessParameter}; use crate::region::RegionSelection; use itertools::assert_equal; use std::iter; @@ -112,7 +112,7 @@ mod tests { let process = Rc::new(Process { id: "process1".into(), description: "Description".into(), - capacity_fractions: ProcessCapacityMap::new(), + activity_limits: ActivityLimitsMap::new(), flows: vec![], parameter: process_param.clone(), regions: RegionSelection::All, @@ -187,7 +187,7 @@ mod tests { let process = Rc::new(Process { id: "process1".into(), description: "Description".into(), - capacity_fractions: ProcessCapacityMap::new(), + activity_limits: ActivityLimitsMap::new(), flows: vec![], parameter: process_param, regions: RegionSelection::Some(["GBR".into()].into_iter().collect()), diff --git a/src/input/process.rs b/src/input/process.rs index f3c61ee0f..84a60cc1a 100644 --- a/src/input/process.rs +++ b/src/input/process.rs @@ -1,7 +1,7 @@ //! Code for reading process-related information from CSV files. use crate::commodity::{Commodity, CommodityMap, CommodityType}; use crate::input::*; -use crate::process::{Process, ProcessCapacityMap, ProcessFlow, ProcessMap, ProcessParameter}; +use crate::process::{ActivityLimitsMap, Process, ProcessFlow, ProcessMap, ProcessParameter}; use crate::region::RegionSelection; use crate::time_slice::TimeSliceInfo; use anyhow::Result; @@ -95,7 +95,7 @@ struct ValidationParams<'a> { milestone_years: &'a [u32], time_slice_info: &'a TimeSliceInfo, parameters: &'a HashMap, ProcessParameter>, - availabilities: &'a HashMap, ProcessCapacityMap>, + availabilities: &'a HashMap, ActivityLimitsMap>, } /// Perform consistency checks for commodity flows. @@ -106,7 +106,7 @@ fn validate_commodities( milestone_years: &[u32], time_slice_info: &TimeSliceInfo, parameters: &HashMap, ProcessParameter>, - availabilities: &HashMap, ProcessCapacityMap>, + availabilities: &HashMap, ActivityLimitsMap>, ) -> anyhow::Result<()> { let params = ValidationParams { flows, @@ -213,7 +213,7 @@ fn validate_svd_commodity( fn create_process_map( descriptions: I, - mut availabilities: HashMap, ProcessCapacityMap>, + mut availabilities: HashMap, ActivityLimitsMap>, mut flows: HashMap, Vec>, mut parameters: HashMap, ProcessParameter>, mut regions: HashMap, RegionSelection>, @@ -240,7 +240,7 @@ where let process = Process { id: Rc::clone(id), description: description.description, - capacity_fractions: availabilities, + activity_limits: availabilities, flows, parameter, regions, @@ -263,7 +263,7 @@ mod tests { struct ProcessData { descriptions: Vec, - availabilities: HashMap, ProcessCapacityMap>, + availabilities: HashMap, ActivityLimitsMap>, flows: HashMap, Vec>, parameters: HashMap, ProcessParameter>, regions: HashMap, RegionSelection>, @@ -286,7 +286,7 @@ mod tests { let availabilities = ["process1", "process2"] .into_iter() .map(|id| { - let mut map = ProcessCapacityMap::new(); + let mut map = ActivityLimitsMap::new(); map.insert( TimeSliceID { season: "winter".into(), diff --git a/src/input/process/availability.rs b/src/input/process/availability.rs index 40ee122b7..e1ca53dd4 100644 --- a/src/input/process/availability.rs +++ b/src/input/process/availability.rs @@ -1,6 +1,6 @@ //! Code for reading process availabilities CSV file use crate::input::*; -use crate::process::ProcessCapacityMap; +use crate::process::ActivityLimitsMap; use crate::time_slice::TimeSliceInfo; use anyhow::{Context, Result}; use serde::Deserialize; @@ -44,25 +44,25 @@ enum LimitType { /// /// # Returns /// -/// A [`HashMap`] with process IDs as the keys and [`ProcessCapacityMap`]s as the values or an +/// A [`HashMap`] with process IDs as the keys and [`ActivityLimitsMap`]s as the values or an /// error. pub fn read_process_availabilities( model_dir: &Path, process_ids: &HashSet>, time_slice_info: &TimeSliceInfo, -) -> Result, ProcessCapacityMap>> { +) -> Result, ActivityLimitsMap>> { let file_path = model_dir.join(PROCESS_AVAILABILITIES_FILE_NAME); let process_availabilities_csv = read_csv(&file_path)?; read_process_availabilities_from_iter(process_availabilities_csv, process_ids, time_slice_info) .with_context(|| input_err_msg(&file_path)) } -/// Process raw process availabilities input data into [`ProcessCapacityMap`]s +/// Process raw process availabilities input data into [`ActivityLimitsMap`]s fn read_process_availabilities_from_iter( iter: I, process_ids: &HashSet>, time_slice_info: &TimeSliceInfo, -) -> Result, ProcessCapacityMap>> +) -> Result, ActivityLimitsMap>> where I: Iterator, { @@ -78,9 +78,7 @@ where let ts_selection = time_slice_info.get_selection(&record.time_slice)?; - let map = map - .entry(process_id) - .or_insert_with(ProcessCapacityMap::new); + let map = map.entry(process_id).or_insert_with(ActivityLimitsMap::new); for (time_slice, ts_length) in time_slice_info.iter_selection(&ts_selection) { // Calculate fraction of annual capacity as availability multiplied by time slice length @@ -109,7 +107,7 @@ where /// Check that every capacity map has an entry for every time slice fn validate_capacity_maps( - map: &HashMap, ProcessCapacityMap>, + map: &HashMap, ActivityLimitsMap>, time_slice_info: &TimeSliceInfo, ) -> Result<()> { for (process_id, map) in map.iter() { diff --git a/src/process.rs b/src/process.rs index f175438b6..be53ff9b2 100644 --- a/src/process.rs +++ b/src/process.rs @@ -20,8 +20,8 @@ pub struct Process { pub id: Rc, /// A human-readable description for the process (e.g. dry gas extraction) pub description: String, - /// The capacity limits for each time slice (as a fraction of maximum) - pub capacity_fractions: ProcessCapacityMap, + /// The activity limits for each time slice (as a fraction of maximum) + pub activity_limits: ActivityLimitsMap, /// Commodity flows for this process pub flows: Vec, /// Additional parameters for this process @@ -44,15 +44,15 @@ impl Process { } } -/// A map indicating capacity limits for a [`Process`] throughout the year. +/// A map indicating activity limits for a [`Process`] throughout the year. /// -/// The capacity value is calculated as availability multiplied by time slice length. Note that it -/// is a *fraction* of capacity for the year; to calculate *actual* capacity for a given time slice -/// you need to know the maximum capacity for the specific instance of a [`Process`] in use. +/// The value is calculated as availability multiplied by time slice length. Note that it is a +/// **fraction** of activity for the year; to calculate **actual** activity for a given time slice +/// you need to know the maximum activity for the specific instance of a [`Process`] in use. /// -/// The capacity is given as a range, depending on the user-specified limit type and value for +/// The limits are given as ranges, depending on the user-specified limit type and value for /// availability. -pub type ProcessCapacityMap = HashMap>; +pub type ActivityLimitsMap = HashMap>; /// Represents a commodity flow for a given process #[derive(PartialEq, Debug, Deserialize, Clone)] diff --git a/src/simulation/optimisation.rs b/src/simulation/optimisation.rs index 7ca5bc978..3cd988b24 100644 --- a/src/simulation/optimisation.rs +++ b/src/simulation/optimisation.rs @@ -446,7 +446,7 @@ fn add_asset_capacity_constraints( mod tests { use super::*; use crate::commodity::{Commodity, CommodityCost, CommodityCostMap, CommodityType, DemandMap}; - use crate::process::{FlowType, Process, ProcessCapacityMap, ProcessParameter}; + use crate::process::{ActivityLimitsMap, FlowType, Process, ProcessParameter}; use crate::region::RegionSelection; use crate::time_slice::TimeSliceLevel; use float_cmp::assert_approx_eq; @@ -486,7 +486,7 @@ mod tests { let process = Rc::new(Process { id: "process1".into(), description: "Description".into(), - capacity_fractions: ProcessCapacityMap::new(), + activity_limits: ActivityLimitsMap::new(), flows: vec![flow.clone()], parameter: process_param.clone(), regions: RegionSelection::All, From 40c125aff9d449c5ececccaf5a41671dfb72e992 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Wed, 12 Mar 2025 09:18:27 +0000 Subject: [PATCH 4/5] Fix: MUSE1 doesn't use `capacity_a` --- src/agent.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 832c50463..12e67b2a1 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -150,9 +150,7 @@ impl Asset { (max_act * limits.start())..=(max_act * limits.end()) } - /// Maximum activity for this asset in a year. - /// - /// This was referred to as `capacity_a` in MUSE 1.0. + /// Maximum activity for this asset in a year pub fn maximum_activity(&self) -> f64 { self.capacity * self.process.parameter.capacity_to_activity } From 740ae5118b5a514b62f35cad8114751279a10967 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Mon, 24 Mar 2025 17:52:48 +0000 Subject: [PATCH 5/5] Fix up some more field names --- src/output.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/output.rs b/src/output.rs index 26dbe9d84..073db8e01 100644 --- a/src/output.rs +++ b/src/output.rs @@ -196,12 +196,12 @@ mod tests { variable_operating_cost: 1.0, lifetime: 5, discount_rate: 0.9, - cap2act: 3.0, + capacity_to_activity: 3.0, }; let process = Rc::new(Process { id: Rc::clone(&process_id), description: "Description".into(), - capacity_fractions: HashMap::new(), + activity_limits: HashMap::new(), flows: vec![], parameter: process_param.clone(), regions: RegionSelection::All,