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
6 changes: 4 additions & 2 deletions src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! assets.
use crate::commodity::CommodityID;
use crate::id::define_id_type;
use crate::process::Process;
use crate::process::{FlowDirection, Process};
use crate::region::RegionID;
use crate::units::{Dimensionless, Money};
use indexmap::{IndexMap, IndexSet};
Expand Down Expand Up @@ -63,7 +63,9 @@ impl Agent {
let flows_key = (region_id.clone(), year);
self.search_space[&(commodity_id.clone(), year)]
.iter()
.filter(move |process| process.flows[&flows_key][commodity_id].is_output())
.filter(move |process| {
process.flows[&flows_key][commodity_id].direction() == FlowDirection::Output
})
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/asset.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Assets are instances of a process which are owned and invested in by agents.
use crate::agent::AgentID;
use crate::commodity::CommodityID;
use crate::process::{Process, ProcessFlow, ProcessID, ProcessParameter};
use crate::process::{FlowDirection, Process, ProcessFlow, ProcessID, ProcessParameter};
use crate::region::RegionID;
use crate::simulation::CommodityPrices;
use crate::time_slice::TimeSliceID;
Expand Down Expand Up @@ -338,7 +338,9 @@ impl Asset {
input_prices: &CommodityPrices,
time_slice: &TimeSliceID,
) -> MoneyPerActivity {
-self.get_revenue_from_flows_with_filter(input_prices, time_slice, ProcessFlow::is_input)
-self.get_revenue_from_flows_with_filter(input_prices, time_slice, |x| {
x.direction() == FlowDirection::Input
})
}

/// Get the total revenue from a subset of flows.
Expand Down
6 changes: 3 additions & 3 deletions src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Module for creating and analysing commodity graphs
use crate::commodity::{CommodityID, CommodityMap, CommodityType};
use crate::process::{ProcessID, ProcessMap};
use crate::process::{FlowDirection, ProcessID, ProcessMap};
use crate::region::RegionID;
use crate::simulation::investment::InvestmentSet;
use crate::time_slice::{TimeSliceInfo, TimeSliceLevel, TimeSliceSelection};
Expand Down Expand Up @@ -97,14 +97,14 @@ fn create_commodities_graph_for_region_year(
// Get output nodes for the process
let mut outputs: Vec<_> = flows
.values()
.filter(|flow| flow.is_output())
.filter(|flow| flow.direction() == FlowDirection::Output)
.map(|flow| GraphNode::Commodity(flow.commodity.id.clone()))
.collect();

// Get input nodes for the process
let mut inputs: Vec<_> = flows
.values()
.filter(|flow| flow.is_input())
.filter(|flow| flow.direction() == FlowDirection::Input)
.map(|flow| GraphNode::Commodity(flow.commodity.id.clone()))
.collect();

Expand Down
33 changes: 22 additions & 11 deletions src/input/process/flow.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//! Code for reading process flows file
use super::super::{input_err_msg, read_csv};
use crate::commodity::{CommodityID, CommodityMap};
use crate::process::{FlowType, ProcessFlow, ProcessFlowsMap, ProcessID, ProcessMap};
use crate::process::{
FlowDirection, FlowType, ProcessFlow, ProcessFlowsMap, ProcessID, ProcessMap,
};
use crate::region::{RegionID, parse_region_str};
use crate::units::{FlowPerActivity, MoneyPerFlow};
use crate::year::parse_year_str;
Expand Down Expand Up @@ -30,9 +32,9 @@ struct ProcessFlowRaw {

impl ProcessFlowRaw {
fn validate(&self) -> Result<()> {
// Check that flow is not infinity, nan, 0 etc.
// Check that flow is not infinity or nan.
ensure!(
self.coeff.is_normal(),
self.coeff.is_finite(),
"Invalid value for coeff ({})",
self.coeff
);
Expand Down Expand Up @@ -189,9 +191,9 @@ fn validate_flows_and_update_primary_output(
///
/// This is only possible if there is only one output flow for the process.
fn infer_primary_output(map: &IndexMap<CommodityID, ProcessFlow>) -> Result<Option<CommodityID>> {
let mut iter = map
.iter()
.filter_map(|(commodity_id, flow)| flow.is_output().then_some(commodity_id));
let mut iter = map.iter().filter_map(|(commodity_id, flow)| {
(flow.direction() == FlowDirection::Output).then_some(commodity_id)
});

let Some(first_output) = iter.next() else {
// If there are only input flows, then the primary output should be None
Expand All @@ -217,12 +219,15 @@ fn check_flows_primary_output(
})?;

ensure!(
flow.is_output(),
flow.direction() == FlowDirection::Output,
"Primary output commodity '{primary_output}' isn't an output flow",
);
} else {
ensure!(
flows_map.values().all(ProcessFlow::is_input),
flows_map
.values()
.all(|x| x.direction() == FlowDirection::Input
|| x.direction() == FlowDirection::Zero),
"First year is only inputs, but subsequent years have outputs, although no primary \
output is specified"
);
Expand All @@ -245,13 +250,13 @@ fn validate_secondary_flows(

// Get the non-primary io flows for all years, if any, arranged by (commodity, region)
let iter = iproduct!(process.years.iter(), process.regions.iter());
let mut flows: HashMap<(CommodityID, RegionID), Vec<bool>> = HashMap::new();
let mut flows: HashMap<(CommodityID, RegionID), Vec<&ProcessFlow>> = HashMap::new();
for (&year, region_id) in iter {
let flow = map[&(region_id.clone(), year)]
.iter()
.filter_map(|(commodity_id, flow)| {
(Some(commodity_id) != process.primary_output.as_ref())
.then_some(((commodity_id.clone(), region_id.clone()), flow.is_input()))
.then_some(((commodity_id.clone(), region_id.clone()), flow))
});

for (key, value) in flow {
Expand All @@ -267,8 +272,14 @@ fn validate_secondary_flows(
"Flow of commodity {commodity_id} in region {region_id} for process {process_id} \
does not cover all years"
);
let input_or_zero = value
.iter()
.all(|&x| [FlowDirection::Input, FlowDirection::Zero].contains(&x.direction()));
let output_or_zero = value
.iter()
.all(|&x| [FlowDirection::Output, FlowDirection::Zero].contains(&x.direction()));
ensure!(
value.iter().all(|&x| x == value[0]),
input_or_zero || output_or_zero,
"Flow of commodity {commodity_id} in region {region_id} for process {process_id} \
behaves as input or output in different years."
);
Expand Down
50 changes: 29 additions & 21 deletions src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,28 @@ impl ProcessFlow {

/// Get the levy/incentive for this process flow with the given parameters, if any
fn get_levy(&self, region_id: &RegionID, year: u32, time_slice: &TimeSliceID) -> MoneyPerFlow {
if let Some(levy) = if self.is_input() {
self.commodity
match self.direction() {
FlowDirection::Input => *self
.commodity
.levies_cons
.get(&(region_id.clone(), year, time_slice.clone()))
} else {
self.commodity
.unwrap_or(&MoneyPerFlow(0.0)),
FlowDirection::Output => *self
.commodity
.levies_prod
.get(&(region_id.clone(), year, time_slice.clone()))
} {
return *levy;
.unwrap_or(&MoneyPerFlow(0.0)),
FlowDirection::Zero => MoneyPerFlow(0.0),
}
MoneyPerFlow(0.0)
}

/// Returns true if this flow is an input (i.e., coeff < 0)
pub fn is_input(&self) -> bool {
self.coeff < FlowPerActivity(0.0)
}

/// Returns true if this flow is an output (i.e., coeff > 0)
pub fn is_output(&self) -> bool {
self.coeff > FlowPerActivity(0.0)
/// Direction of the flow
pub fn direction(&self) -> FlowDirection {
match self.coeff {
x if x < FlowPerActivity(0.0) => FlowDirection::Input,
x if x > FlowPerActivity(0.0) => FlowDirection::Output,
_ => FlowDirection::Zero,
}
}
}

Expand All @@ -143,6 +143,17 @@ pub enum FlowType {
Flexible,
}

/// Direction of the flow (see [`ProcessFlow`])
#[derive(PartialEq, Debug)]
pub enum FlowDirection {
/// The flow is an input (i.e., coeff < 0)
Input,
/// The flow is an output (i.e., coeff > 0)
Output,
/// The flow is zero, neither input nor output (i.e., coeff == 0)
Zero,
}

/// Additional parameters for a process
#[derive(PartialEq, Clone, Debug)]
pub struct ProcessParameter {
Expand Down Expand Up @@ -640,11 +651,8 @@ mod tests {
cost: MoneyPerFlow(0.0),
};

assert!(flow_in.is_input());
assert!(!flow_in.is_output());
assert!(flow_out.is_output());
assert!(!flow_out.is_input());
assert!(!flow_zero.is_input());
assert!(!flow_zero.is_output());
assert!(flow_in.direction() == FlowDirection::Input);
assert!(flow_out.direction() == FlowDirection::Output);
assert!(flow_zero.direction() == FlowDirection::Zero);
}
}
6 changes: 5 additions & 1 deletion src/simulation/prices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use crate::asset::AssetRef;
use crate::commodity::CommodityID;
use crate::model::{Model, PricingStrategy};
use crate::process::FlowDirection;
use crate::region::RegionID;
use crate::simulation::optimisation::Solution;
use crate::time_slice::{TimeSliceID, TimeSliceInfo};
Expand Down Expand Up @@ -218,7 +219,10 @@ where
let mut highest_duals = HashMap::new();
for (asset, time_slice, dual) in activity_duals {
// Iterate over all output flows
for flow in asset.iter_flows().filter(|flow| flow.is_output()) {
for flow in asset
.iter_flows()
.filter(|flow| flow.direction() == FlowDirection::Output)
{
// Update the highest dual for this commodity/time slice
highest_duals
.entry((
Expand Down