muse2/
agent.rs

1//! Agents drive the economy of the MUSE2 simulation, through relative investment in different
2//! assets.
3use crate::commodity::CommodityID;
4use crate::id::define_id_type;
5use crate::process::{FlowDirection, Process};
6use crate::region::RegionID;
7use crate::units::{Dimensionless, Money};
8use indexmap::{IndexMap, IndexSet};
9use serde_string_enum::DeserializeLabeledStringEnum;
10use std::collections::HashMap;
11use std::rc::Rc;
12
13define_id_type! {AgentID}
14
15/// A map of [`Agent`]s, keyed by agent ID
16pub type AgentMap = IndexMap<AgentID, Agent>;
17
18/// A map of cost limits for an agent, keyed by year
19pub type AgentCostLimitsMap = HashMap<u32, AgentCostLimits>;
20
21/// A map of commodity portions for an agent, keyed by commodity and year
22pub type AgentCommodityPortionsMap = HashMap<(CommodityID, u32), Dimensionless>;
23
24/// A map for the agent's search space, keyed by commodity and year
25pub type AgentSearchSpaceMap = HashMap<(CommodityID, u32), Rc<Vec<Rc<Process>>>>;
26
27/// A map of objectives for an agent, keyed by commodity and year.
28///
29/// NB: As we currently only support the "single" decision rule, the only parameter we need for
30/// objectives is the type.
31pub type AgentObjectiveMap = HashMap<u32, ObjectiveType>;
32
33/// An agent in the simulation
34#[derive(Debug, Clone, PartialEq)]
35pub struct Agent {
36    /// A unique identifier for the agent.
37    pub id: AgentID,
38    /// A text description of the agent.
39    pub description: String,
40    /// The proportion of the commodity production that the agent is responsible for.
41    pub commodity_portions: AgentCommodityPortionsMap,
42    /// The processes that the agent will consider investing in.
43    pub search_space: AgentSearchSpaceMap,
44    /// The decision rule that the agent uses to decide investment.
45    pub decision_rule: DecisionRule,
46    /// Cost limits (e.g. capital cost, annual operating cost)
47    pub cost_limits: AgentCostLimitsMap,
48    /// The regions in which this agent operates.
49    pub regions: IndexSet<RegionID>,
50    /// The agent's objectives.
51    pub objectives: AgentObjectiveMap,
52}
53
54impl Agent {
55    /// Get all the processes in this agent's search space which produce the commodity in the given
56    /// year
57    pub fn iter_possible_producers_of<'a>(
58        &'a self,
59        region_id: &RegionID,
60        commodity_id: &'a CommodityID,
61        year: u32,
62    ) -> impl Iterator<Item = &'a Rc<Process>> + use<'a> {
63        let flows_key = (region_id.clone(), year);
64        self.search_space[&(commodity_id.clone(), year)]
65            .iter()
66            .filter(move |process| {
67                process.flows[&flows_key][commodity_id].direction() == FlowDirection::Output
68            })
69    }
70}
71
72/// The cost limits for an agent in a particular year
73#[derive(Debug, Clone, PartialEq)]
74pub struct AgentCostLimits {
75    /// The maximum capital cost the agent will pay.
76    pub capex_limit: Option<Money>,
77    /// The maximum annual operating cost (fuel etc.) that the agent will pay.
78    pub annual_cost_limit: Option<Money>,
79}
80
81/// The decision rule for a particular objective
82#[derive(Debug, Clone, PartialEq)]
83pub enum DecisionRule {
84    /// Used when there is only a single objective
85    Single,
86    /// A simple weighting of objectives
87    Weighted,
88    /// Objectives are considered in a specific order
89    Lexicographical {
90        /// The tolerance around the main objective to consider secondary objectives. This is an absolute value of maximum deviation in the units of the main objective.
91        tolerance: f64,
92    },
93}
94
95/// The type of objective for the agent
96#[derive(Debug, Clone, Copy, PartialEq, DeserializeLabeledStringEnum)]
97pub enum ObjectiveType {
98    /// Average cost of one unit of output commodity over its lifetime
99    #[string = "lcox"]
100    LevelisedCostOfX,
101    /// Net present value
102    #[string = "npv"]
103    NetPresentValue,
104}