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 a38ae994e..aa649707e 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -158,11 +158,16 @@ 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 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 - (capacity_a * limits.start())..=(capacity_a * limits.end()) + (max_act * limits.start())..=(max_act * limits.end()) + } + + /// Maximum activity for this asset in a year + pub fn maximum_activity(&self) -> f64 { + self.capacity * self.process.parameter.capacity_to_activity } } @@ -276,7 +281,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; @@ -295,7 +300,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(), @@ -314,11 +319,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, @@ -344,12 +349,12 @@ 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(), 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 994b12c31..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; @@ -107,12 +107,12 @@ 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(), 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 55377062d..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(), @@ -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/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/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/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, diff --git a/src/process.rs b/src/process.rs index c0db5f82d..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)] @@ -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 16b548c17..b6a7a758a 100644 --- a/src/simulation/optimisation.rs +++ b/src/simulation/optimisation.rs @@ -497,7 +497,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; @@ -516,7 +516,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(), @@ -537,7 +537,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,