muse2/
commodity.rs

1#![allow(missing_docs)]
2use crate::id::{define_id_getter, define_id_type};
3use crate::region::RegionID;
4use crate::time_slice::{TimeSliceID, TimeSliceLevel};
5use indexmap::IndexMap;
6use serde::Deserialize;
7use serde_string_enum::DeserializeLabeledStringEnum;
8use std::collections::HashMap;
9use std::rc::Rc;
10
11define_id_type! {CommodityID}
12
13/// A map of [`Commodity`]s, keyed by commodity ID
14pub type CommodityMap = IndexMap<CommodityID, Rc<Commodity>>;
15
16/// A map of [`CommodityCost`]s, keyed by region ID, year and time slice ID
17pub type CommodityCostMap = HashMap<(RegionID, u32, TimeSliceID), CommodityCost>;
18
19/// A map of demand values, keyed by region ID, year and time slice ID
20pub type DemandMap = HashMap<(RegionID, u32, TimeSliceID), f64>;
21
22/// A commodity within the simulation. Represents a substance (e.g. CO2) or form of energy (e.g.
23/// electricity) that can be produced and/or consumed by technologies in the model.
24#[derive(PartialEq, Debug, Deserialize)]
25pub struct Commodity {
26    /// Unique identifier for the commodity (e.g. "ELC")
27    pub id: CommodityID,
28    /// Text description of commodity (e.g. "electricity")
29    pub description: String,
30    #[serde(rename = "type")] // NB: we can't name a field type as it's a reserved keyword
31    /// Commodity balance type. Can be supply = demand (SED), service demand (SVD), non-balance commodity (NBC).
32    pub kind: CommodityType,
33    /// The time slice level for commodity balance. Can be annual, seasonal or at time slice level.
34    pub time_slice_level: TimeSliceLevel,
35
36    #[serde(skip)]
37    pub costs: CommodityCostMap,
38    #[serde(skip)]
39    pub demand: DemandMap,
40}
41define_id_getter! {Commodity, CommodityID}
42
43/// Type of balance for application of cost
44#[derive(PartialEq, Clone, Debug, DeserializeLabeledStringEnum)]
45pub enum BalanceType {
46    #[string = "net"]
47    Net,
48    #[string = "cons"]
49    Consumption,
50    #[string = "prod"]
51    Production,
52}
53
54/// Represents a tax or other external cost on a commodity
55#[derive(PartialEq, Clone, Debug)]
56pub struct CommodityCost {
57    /// Type of balance for application of cost.
58    pub balance_type: BalanceType,
59    /// Cost per unit commodity. For example, if a CO2 price is specified in input data, it can be applied to net CO2 via this value.
60    pub value: f64,
61}
62
63/// Commodity balance type
64#[derive(PartialEq, Debug, DeserializeLabeledStringEnum)]
65pub enum CommodityType {
66    #[string = "sed"]
67    SupplyEqualsDemand,
68    #[string = "svd"]
69    ServiceDemand,
70    #[string = "inc"]
71    InputCommodity,
72    #[string = "ouc"]
73    OutputCommodity,
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_demand_map() {
82        let time_slice = TimeSliceID {
83            season: "all-year".into(),
84            time_of_day: "all-day".into(),
85        };
86        let value = 0.25;
87        let mut map = DemandMap::new();
88        map.insert(("North".into(), 2020, time_slice.clone()), value);
89
90        assert_eq!(
91            map.get(&("North".into(), 2020, time_slice)).unwrap(),
92            &value
93        )
94    }
95
96    #[test]
97    fn test_commodity_cost_map() {
98        let ts = TimeSliceID {
99            season: "winter".into(),
100            time_of_day: "day".into(),
101        };
102        let value = CommodityCost {
103            balance_type: BalanceType::Consumption,
104            value: 0.5,
105        };
106        let mut map = CommodityCostMap::new();
107        assert!(map
108            .insert(("GBR".into(), 2010, ts.clone()), value.clone())
109            .is_none());
110        assert_eq!(map.get(&("GBR".into(), 2010, ts)).unwrap(), &value);
111    }
112}