Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 6 additions & 43 deletions src/commodity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,9 @@ pub struct CommodityCost {
pub value: f64,
}

/// Used for looking up [`CommodityCost`]s in a [`CommodityCostMap`]
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
struct CommodityCostKey {
region_id: RegionID,
year: u32,
time_slice: TimeSliceID,
}

/// A data structure for easy lookup of [`CommodityCost`]s
#[derive(PartialEq, Debug, Default, Clone)]
pub struct CommodityCostMap(HashMap<CommodityCostKey, CommodityCost>);
pub struct CommodityCostMap(HashMap<(RegionID, u32, TimeSliceID), CommodityCost>);

impl CommodityCostMap {
/// Create a new, empty [`CommodityCostMap`]
Expand All @@ -80,12 +72,7 @@ impl CommodityCostMap {
time_slice: TimeSliceID,
value: CommodityCost,
) -> Option<CommodityCost> {
let key = CommodityCostKey {
region_id,
year,
time_slice,
};
self.0.insert(key, value)
self.0.insert((region_id, year, time_slice), value)
}

/// Retrieve a [`CommodityCost`] from the map
Expand All @@ -95,12 +82,7 @@ impl CommodityCostMap {
year: u32,
time_slice: &TimeSliceID,
) -> Option<&CommodityCost> {
let key = CommodityCostKey {
region_id: region_id.clone(),
year,
time_slice: time_slice.clone(),
};
self.0.get(&key)
self.0.get(&(region_id.clone(), year, time_slice.clone()))
}
}

Expand All @@ -122,15 +104,7 @@ pub enum CommodityType {
/// This data type is exported as this is the way in we want to look up demand outside of this
/// module.
#[derive(PartialEq, Debug, Clone, Default)]
pub struct DemandMap(HashMap<DemandMapKey, f64>);

/// The key for a [`DemandMap`]
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
struct DemandMapKey {
region_id: RegionID,
year: u32,
time_slice: TimeSliceID,
}
pub struct DemandMap(HashMap<(RegionID, u32, TimeSliceID), f64>);

impl DemandMap {
/// Create a new, empty [`DemandMap`]
Expand All @@ -141,25 +115,14 @@ impl DemandMap {
/// Retrieve the demand for the specified region, year and time slice
pub fn get(&self, region_id: &RegionID, year: u32, time_slice: &TimeSliceID) -> f64 {
self.0
.get(&DemandMapKey {
region_id: region_id.clone(),
year,
time_slice: time_slice.clone(),
})
.get(&(region_id.clone(), year, time_slice.clone()))
.copied()
.unwrap_or_else(|| panic!("Missing demand entry: {region_id}, {year}, {time_slice}"))
}

/// Insert a new demand entry for the specified region, year and time slice
pub fn insert(&mut self, region_id: RegionID, year: u32, time_slice: TimeSliceID, demand: f64) {
self.0.insert(
DemandMapKey {
region_id,
year,
time_slice,
},
demand,
);
self.0.insert((region_id, year, time_slice), demand);
}
}

Expand Down
1 change: 0 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use itertools::Itertools;
use serde::de::{Deserialize, DeserializeOwned, Deserializer};
use std::collections::HashSet;
use std::fs;
use std::hash::Hash;
use std::path::Path;

mod agent;
Expand Down
81 changes: 15 additions & 66 deletions src/input/commodity/demand.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Code for working with demand for a given commodity. Demand can vary by region, year and time
//! slice.
use super::super::*;
use super::demand_slicing::{read_demand_slices, DemandSliceMap, DemandSliceMapKey};
use super::demand_slicing::{read_demand_slices, DemandSliceMap};
use crate::commodity::{CommodityID, DemandMap};
use crate::id::IDCollection;
use crate::region::RegionID;
Expand All @@ -27,18 +27,7 @@ struct Demand {
}

/// A map relating commodity, region and year to annual demand
pub type AnnualDemandMap = HashMap<AnnualDemandMapKey, f64>;

/// A key for an [`AnnualDemandMap`]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct AnnualDemandMapKey {
/// The commodity to which this demand applies
commodity_id: CommodityID,
/// The region to which this demand applies
region_id: RegionID,
/// The simulation year to which this demand applies
year: u32,
}
pub type AnnualDemandMap = HashMap<(CommodityID, RegionID, u32), f64>;

/// A set of commodity + region pairs
pub type CommodityRegionPairs = HashSet<(CommodityID, RegionID)>;
Expand Down Expand Up @@ -143,13 +132,12 @@ where
"Demand must be a valid number greater than zero"
);

let key = AnnualDemandMapKey {
commodity_id: commodity_id.clone(),
region_id: region_id.clone(),
year: demand.year,
};
ensure!(
map.insert(key, demand.demand).is_none(),
map.insert(
(commodity_id.clone(), region_id.clone(), demand.year),
demand.demand
)
.is_none(),
"Duplicate demand entries (commodity: {}, region: {}, year: {})",
commodity_id,
region_id,
Expand All @@ -163,13 +151,8 @@ where
// milestone year
for (commodity_id, region_id) in commodity_regions.iter() {
for year in milestone_years.iter().copied() {
let key = AnnualDemandMapKey {
commodity_id: commodity_id.clone(),
region_id: region_id.clone(),
year,
};
ensure!(
map.contains_key(&key),
map.contains_key(&(commodity_id.clone(), region_id.clone(), year)),
"Missing milestone year {year} for commodity {commodity_id} in region {region_id}"
);
}
Expand All @@ -196,15 +179,9 @@ fn compute_demand_maps(
time_slice_info: &TimeSliceInfo,
) -> HashMap<CommodityID, DemandMap> {
let mut map = HashMap::new();
for (demand_key, annual_demand) in demand.iter() {
let commodity_id = &demand_key.commodity_id;
let region_id = &demand_key.region_id;
for ((commodity_id, region_id, year), annual_demand) in demand.iter() {
for time_slice in time_slice_info.iter_ids() {
let slice_key = DemandSliceMapKey {
commodity_id: commodity_id.clone(),
region_id: region_id.clone(),
time_slice: time_slice.clone(),
};
let slice_key = (commodity_id.clone(), region_id.clone(), time_slice.clone());

// NB: This has already been checked, so shouldn't fail
let demand_fraction = slices.get(&slice_key).unwrap();
Expand All @@ -217,7 +194,7 @@ fn compute_demand_maps(
// Add a new demand entry
map.insert(
region_id.clone(),
demand_key.year,
*year,
time_slice.clone(),
annual_demand * demand_fraction,
);
Expand Down Expand Up @@ -428,38 +405,10 @@ COM1,West,2020,13"
HashSet::from_iter(["North".into(), "South".into(), "East".into(), "West".into()]);
let milestone_years = [2020];
let expected = AnnualDemandMap::from_iter([
(
AnnualDemandMapKey {
commodity_id: "COM1".into(),
region_id: "North".into(),
year: 2020,
},
10.0,
),
(
AnnualDemandMapKey {
commodity_id: "COM1".into(),
region_id: "South".into(),
year: 2020,
},
11.0,
),
(
AnnualDemandMapKey {
commodity_id: "COM1".into(),
region_id: "East".into(),
year: 2020,
},
12.0,
),
(
AnnualDemandMapKey {
commodity_id: "COM1".into(),
region_id: "West".into(),
year: 2020,
},
13.0,
),
(("COM1".into(), "North".into(), 2020), 10.0),
(("COM1".into(), "South".into(), 2020), 11.0),
(("COM1".into(), "East".into(), 2020), 12.0),
(("COM1".into(), "West".into(), 2020), 13.0),
]);
let (demand, commodity_regions) =
read_demand_file(dir.path(), &commodity_ids, &region_ids, &milestone_years).unwrap();
Expand Down
87 changes: 31 additions & 56 deletions src/input/commodity/demand_slicing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,7 @@ struct DemandSlice {
}

/// A map relating commodity, region and time slice to the fraction of annual demand
pub type DemandSliceMap = HashMap<DemandSliceMapKey, f64>;

/// A key for a [`DemandSliceMap`]
#[derive(PartialEq, Eq, Hash, Debug)]
pub struct DemandSliceMapKey {
/// The commodity to which this demand applies
pub commodity_id: CommodityID,
/// The region to which this demand applies
pub region_id: RegionID,
/// The time slice to which this demand applies
pub time_slice: TimeSliceID,
}
pub type DemandSliceMap = HashMap<(CommodityID, RegionID, TimeSliceID), f64>;

/// Read demand slices from specified model directory.
///
Expand Down Expand Up @@ -92,14 +81,8 @@ where
let ts_selection = time_slice_info.get_selection(&slice.time_slice)?;
for (ts, demand_fraction) in time_slice_info.calculate_share(&ts_selection, slice.fraction)
{
let key = DemandSliceMapKey {
commodity_id: commodity_id.clone(),
region_id: region_id.clone(),
time_slice: ts.clone(),
};

// Share demand between the time slices in proportion to duration
ensure!(demand_slices.insert(key, demand_fraction).is_none(),
ensure!(demand_slices.insert((commodity_id.clone(), region_id.clone(), ts.clone()), demand_fraction).is_none(),
"Duplicate demand slicing entry (or same time slice covered by more than one entry) \
(commodity: {commodity_id}, region: {region_id}, time slice: {ts})"
);
Expand Down Expand Up @@ -128,18 +111,14 @@ fn validate_demand_slices(
time_slice_info
.iter_ids()
.map(|time_slice| {
let key = DemandSliceMapKey {
commodity_id: commodity_id.clone(),
region_id: region_id.clone(),
time_slice: time_slice.clone(),
};

demand_slices.get(&key).with_context(|| {
format!(
"Demand slice missing for time slice {} (commodity: {}, region {})",
time_slice, commodity_id, region_id
)
})
demand_slices
.get(&(commodity_id.clone(), region_id.clone(), time_slice.clone()))
.with_context(|| {
format!(
"Demand slice missing for time slice {} (commodity: {}, region {})",
time_slice, commodity_id, region_id
)
})
})
.process_results(|iter| {
check_fractions_sum_to_one(iter.copied()).context("Invalid demand fractions")
Expand Down Expand Up @@ -186,11 +165,7 @@ mod tests {
let time_slice = time_slice_info
.get_time_slice_id_from_str("winter.day")
.unwrap();
let key = DemandSliceMapKey {
commodity_id: "COM1".into(),
region_id: "GBR".into(),
time_slice,
};
let key = ("COM1".into(), "GBR".into(), time_slice);
let expected = DemandSliceMap::from_iter(iter::once((key, 1.0)));
assert_eq!(
read_demand_slices_from_iter(
Expand Down Expand Up @@ -258,47 +233,47 @@ mod tests {
];
let expected = DemandSliceMap::from_iter([
(
DemandSliceMapKey {
commodity_id: "COM1".into(),
region_id: "GBR".into(),
time_slice: TimeSliceID {
(
"COM1".into(),
"GBR".into(),
TimeSliceID {
season: "summer".into(),
time_of_day: "day".into(),
},
},
),
3.0 / 16.0,
),
(
DemandSliceMapKey {
commodity_id: "COM1".into(),
region_id: "GBR".into(),
time_slice: TimeSliceID {
(
"COM1".into(),
"GBR".into(),
TimeSliceID {
season: "summer".into(),
time_of_day: "night".into(),
},
},
),
5.0 / 16.0,
),
(
DemandSliceMapKey {
commodity_id: "COM1".into(),
region_id: "GBR".into(),
time_slice: TimeSliceID {
(
"COM1".into(),
"GBR".into(),
TimeSliceID {
season: "winter".into(),
time_of_day: "day".into(),
},
},
),
3.0 / 16.0,
),
(
DemandSliceMapKey {
commodity_id: "COM1".into(),
region_id: "GBR".into(),
time_slice: TimeSliceID {
(
"COM1".into(),
"GBR".into(),
TimeSliceID {
season: "winter".into(),
time_of_day: "night".into(),
},
},
),
5.0 / 16.0,
),
]);
Expand Down
Loading