Skip to content
Open
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
5 changes: 3 additions & 2 deletions benches/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use muse2::model::Model;
use muse2::output::DataWriter;
use muse2::process::{Process, ProcessID};
use muse2::simulation::candidate_assets_for_next_year;
use muse2::simulation::investment::{collect_preset_demands_for_year, select_best_assets};
use muse2::simulation::investment::collect_preset_demands_for_year;
use muse2::simulation::investment::competitive::{get_asset_options, select_best_assets};
use muse2::simulation::market::{
collect_agent_limits, get_asset_options, get_demand_portion_for_market, get_responsible_agents,
collect_agent_limits, get_demand_portion_for_market, get_responsible_agents,
};
use muse2::simulation::optimisation::DispatchRun;
use muse2::simulation::prices::{Prices, calculate_prices};
Expand Down
6 changes: 6 additions & 0 deletions schemas/input/agents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ fields:
type: string
description: The region(s) in which the agent operates
notes: One or more region IDs, separated by semicolons or the string `all`
- name: agent_type
type: string
enum: [competitive]
description: The asset selection algorithm used by the agent
notes: |
Optional (defaults to `competitive`). Currently the only supported value is `competitive`.
- name: decision_rule
type: string
description: The decision rule applied to objectives
Expand Down
12 changes: 12 additions & 0 deletions src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub struct Agent {
pub commodity_portions: AgentCommodityPortionsMap,
/// The processes that the agent will consider investing in.
pub search_space: AgentSearchSpaceMap,
/// The algorithm that the agent uses to select assets.
pub agent_type: AgentType,
/// The decision rule that the agent uses to decide investment.
pub decision_rule: DecisionRule,
/// The regions in which this agent operates.
Expand All @@ -64,6 +66,16 @@ impl Agent {
}
}

/// The type of an agent, which determines the asset selection algorithm that it uses
#[derive(Debug, Clone, Copy, Default, PartialEq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AgentType {
/// Existing and candidate assets compete in rounds, with the best option according to the
/// agent's objective selected in each round
#[default]
Competitive,
}

/// The decision rule for a particular objective
#[derive(Debug, Clone, PartialEq)]
pub enum DecisionRule {
Expand Down
33 changes: 6 additions & 27 deletions src/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

use crate::agent::{
Agent, AgentCommodityPortionsMap, AgentID, AgentMap, AgentObjectiveMap, AgentSearchSpaceMap,
DecisionRule,
AgentType, DecisionRule,
};
use crate::asset::{Asset, AssetCapacity, AssetPool, AssetRef};
use crate::asset::{Asset, AssetCapacity, AssetPool};
use crate::commodity::{
Commodity, CommodityConstraintsMap, CommodityID, CommodityLevyMap, CommodityType, DemandMap,
PricingStrategy,
Expand All @@ -15,14 +15,10 @@ use crate::process::{
ProcessInvestmentConstraintsMap, ProcessMap, ProcessParameter, ProcessParameterMap,
};
use crate::region::RegionID;
use crate::simulation::investment::appraisal::LCOXMetric;
use crate::simulation::investment::appraisal::{
AppraisalOutput, coefficients::ObjectiveCoefficients,
};
use crate::time_slice::{TimeSliceID, TimeSliceInfo, TimeSliceLevel, TimeSliceSelection};
use crate::time_slice::{TimeSliceID, TimeSliceInfo, TimeSliceLevel};
use crate::units::{
Activity, ActivityPerCapacity, Capacity, Dimensionless, Flow, MoneyPerActivity,
MoneyPerCapacity, MoneyPerCapacityPerYear, Year,
ActivityPerCapacity, Capacity, Dimensionless, MoneyPerActivity, MoneyPerCapacity,
MoneyPerCapacityPerYear, Year,
};
use anyhow::Result;
use indexmap::indexmap;
Expand Down Expand Up @@ -342,6 +338,7 @@ pub fn agents(region_ids: IndexSet<RegionID>) -> AgentMap {
description: String::new(),
commodity_portions: AgentCommodityPortionsMap::new(),
search_space: AgentSearchSpaceMap::new(),
agent_type: AgentType::default(),
decision_rule: DecisionRule::Single,
regions: region_ids,
objectives: AgentObjectiveMap::new(),
Expand Down Expand Up @@ -401,24 +398,6 @@ pub fn time_slice_info2() -> TimeSliceInfo {
}
}

#[fixture]
pub fn appraisal_output(asset: Asset, time_slice: TimeSliceID) -> AppraisalOutput {
let activity_coefficients = indexmap! { time_slice.clone() => MoneyPerActivity(0.5) };
let market_costs = indexmap! { time_slice.clone() => MoneyPerActivity(0.4) };
let activity = indexmap! { time_slice.clone() => Activity(10.0) };
let unmet_demand = indexmap! { TimeSliceSelection::Single(time_slice.clone()) => Flow(5.0) };
AppraisalOutput {
asset: AssetRef::from(asset),
coefficients: Arc::new(ObjectiveCoefficients {
activity_coefficients,
market_costs,
}),
activity,
unmet_demand,
metric: Some(Box::new(LCOXMetric::new(MoneyPerActivity(4.14)))),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
45 changes: 43 additions & 2 deletions src/input/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use super::{input_err_msg, read_csv};
use crate::agent::{
Agent, AgentCommodityPortionsMap, AgentID, AgentMap, AgentObjectiveMap, AgentSearchSpaceMap,
DecisionRule,
AgentType, DecisionRule,
};
use crate::commodity::CommodityMap;
use crate::process::ProcessMap;
Expand Down Expand Up @@ -30,6 +30,8 @@ struct AgentRaw {
description: String,
/// The region(s) in which the agent operates.
regions: String,
/// The algorithm that the agent uses to select assets (defaults to `competitive`).
agent_type: Option<AgentType>,
/// The decision rule that the agent uses to decide investment.
decision_rule: String,
/// The tolerance around the main objective to consider secondary objectives.
Expand Down Expand Up @@ -143,6 +145,7 @@ where
description: agent_raw.description,
commodity_portions: AgentCommodityPortionsMap::new(),
search_space: AgentSearchSpaceMap::new(),
agent_type: agent_raw.agent_type.unwrap_or_default(),
decision_rule,
regions,
objectives: AgentObjectiveMap::new(),
Expand All @@ -161,7 +164,9 @@ where
mod tests {
use super::*;
use crate::agent::DecisionRule;
use std::iter;
use rstest::rstest;
use std::{fs, iter};
use tempfile::tempdir;

#[test]
fn read_agents_file_from_iter_works() {
Expand All @@ -170,6 +175,7 @@ mod tests {
let agent = AgentRaw {
id: "agent".into(),
description: String::new(),
agent_type: None,
decision_rule: "single".into(),
decision_lexico_tolerance: None,
regions: "GBR".into(),
Expand All @@ -179,6 +185,7 @@ mod tests {
description: String::new(),
commodity_portions: AgentCommodityPortionsMap::new(),
search_space: AgentSearchSpaceMap::new(),
agent_type: AgentType::Competitive,
decision_rule: DecisionRule::Single,
regions: IndexSet::from(["GBR".into()]),
objectives: AgentObjectiveMap::new(),
Expand All @@ -192,13 +199,15 @@ mod tests {
AgentRaw {
id: "agent".into(),
description: String::new(),
agent_type: None,
decision_rule: "single".into(),
decision_lexico_tolerance: None,
regions: "GBR".into(),
},
AgentRaw {
id: "agent".into(),
description: String::new(),
agent_type: None,
decision_rule: "single".into(),
decision_lexico_tolerance: None,
regions: "GBR".into(),
Expand All @@ -210,10 +219,42 @@ mod tests {
let agent = AgentRaw {
id: "agent".into(),
description: String::new(),
agent_type: None,
decision_rule: "lexico".into(),
decision_lexico_tolerance: None,
regions: "GBR".into(),
};
read_agents_file_from_iter(iter::once(agent), &region_ids).unwrap_err();
}

#[rstest]
#[case(
"id,description,regions,decision_rule\nagent,,GBR,single\n",
Some(AgentType::Competitive)
)]
#[case(
"id,description,regions,decision_rule,agent_type\nagent,,GBR,single,\n",
Some(AgentType::Competitive)
)]
#[case(
"id,description,regions,decision_rule,agent_type\nagent,,GBR,single,competitive\n",
Some(AgentType::Competitive)
)]
#[case(
"id,description,regions,decision_rule,agent_type\nagent,,GBR,single,invalid\n",
None
)]
fn read_agents_file_agent_type(#[case] csv: &str, #[case] expected: Option<AgentType>) {
let dir = tempdir().unwrap();
fs::write(dir.path().join(AGENT_FILE_NAME), csv).unwrap();

let region_ids = IndexSet::from(["GBR".into()]);
let result = read_agents_file(dir.path(), &region_ids);
match expected {
Some(agent_type) => assert_eq!(result.unwrap()["agent"].agent_type, agent_type),
None => {
result.unwrap_err();
}
}
}
}
3 changes: 2 additions & 1 deletion src/input/agent/search_space.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ fn get_producers_map(agents: &AgentMap, processes: &ProcessMap) -> ProducersMap
mod tests {
use super::*;
use crate::{
agent::{AgentCommodityPortionsMap, AgentObjectiveMap, DecisionRule},
agent::{AgentCommodityPortionsMap, AgentObjectiveMap, AgentType, DecisionRule},
fixture::{
agent_id, assert_error, assert_patched_runs_ok_simple,
assert_validate_fails_with_simple, commodity_id, process, processes, region_id,
Expand Down Expand Up @@ -291,6 +291,7 @@ mod tests {
description: String::new(),
commodity_portions: AgentCommodityPortionsMap::new(),
search_space: AgentSearchSpaceMap::new(),
agent_type: AgentType::default(),
decision_rule: DecisionRule::Single,
regions: iter::once(region_id).collect(),
objectives: AgentObjectiveMap::new(),
Expand Down
Loading