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
14 changes: 9 additions & 5 deletions examples/simple/agent_objectives.csv
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
agent_id,objective_type,decision_weight,decision_lexico_tolerance
A0_GEX,lcox,,
A0_GPR,lcox,,
A0_ELC,lcox,,
A0_RES,eac,,
agent_id,year,objective_type,decision_weight,decision_lexico_tolerance
A0_GEX,2020,lcox,,
A0_GEX,2030,lcox,,
A0_GPR,2020,lcox,,
A0_GPR,2030,lcox,,
A0_ELC,2020,lcox,,
A0_ELC,2030,lcox,,
A0_RES,2020,eac,,
A0_RES,2030,eac,,
2 changes: 2 additions & 0 deletions src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ pub enum DecisionRule {
pub struct AgentObjective {
/// Unique agent id identifying the agent this objective belongs to
pub agent_id: String,
/// The year the objective is relevant for
pub year: u32,
/// Acronym identifying the objective (e.g. LCOX)
pub objective_type: ObjectiveType,
/// For the weighted sum objective, the set of weights to apply to each objective.
Expand Down
8 changes: 7 additions & 1 deletion src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,13 @@ pub fn load_model<P: AsRef<Path>>(model_dir: P) -> Result<(Model, AssetPool)> {
&time_slice_info,
years,
)?;
let agents = read_agents(model_dir.as_ref(), &commodities, &processes, &region_ids)?;
let agents = read_agents(
model_dir.as_ref(),
&commodities,
&processes,
&region_ids,
years,
)?;
let agent_ids = agents.keys().cloned().collect();
let assets = read_assets(model_dir.as_ref(), &agent_ids, &processes, &region_ids)?;

Expand Down
3 changes: 2 additions & 1 deletion src/input/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ pub fn read_agents(
commodities: &CommodityMap,
processes: &ProcessMap,
region_ids: &HashSet<Rc<str>>,
milestone_years: &[u32],
) -> Result<AgentMap> {
let process_ids = processes.keys().cloned().collect();
let mut agents = read_agents_file(model_dir, commodities, &process_ids)?;
let agent_ids = agents.keys().cloned().collect();

let mut agent_regions = read_agent_regions(model_dir, &agent_ids, region_ids)?;
let mut objectives = read_agent_objectives(model_dir, &agents)?;
let mut objectives = read_agent_objectives(model_dir, &agents, milestone_years)?;

for (id, agent) in agents.iter_mut() {
agent.regions = agent_regions.remove(id).unwrap();
Expand Down
61 changes: 51 additions & 10 deletions src/input/agent/objective.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@ define_id_getter! {Agent}
pub fn read_agent_objectives(
model_dir: &Path,
agents: &AgentMap,
milestone_years: &[u32],
) -> Result<HashMap<Rc<str>, Vec<AgentObjective>>> {
let file_path = model_dir.join(AGENT_OBJECTIVES_FILE_NAME);
let agent_objectives_csv = read_csv(&file_path)?;
read_agent_objectives_from_iter(agent_objectives_csv, agents)
read_agent_objectives_from_iter(agent_objectives_csv, agents, milestone_years)
.with_context(|| input_err_msg(&file_path))
}

fn read_agent_objectives_from_iter<I>(
iter: I,
agents: &AgentMap,
milestone_years: &[u32],
) -> Result<HashMap<Rc<str>, Vec<AgentObjective>>>
where
I: Iterator<Item = AgentObjective>,
Expand All @@ -45,17 +47,34 @@ where
// Check that required parameters are present and others are absent
check_objective_parameter(&objective, &agent.decision_rule)?;

// Check that the year is a valid milestone year
ensure!(
milestone_years.binary_search(&objective.year).is_ok(),
"Invalid milestone year {}",
objective.year
);

// Append to Vec with the corresponding key or create
objectives
.entry(Rc::clone(id))
.or_insert_with(|| Vec::with_capacity(1))
.push(objective);
}

ensure!(
objectives.len() >= agents.len(),
"All agents must have at least one objective"
);
// Validate that each agent has at least one objective for each milestone year
for (agent_id, _agent) in agents {
let agent_objectives = objectives
.get(agent_id)
.with_context(|| format!("Agent {} has no objectives", agent_id))?;
for &year in milestone_years {
ensure!(
agent_objectives.iter().any(|obj| obj.year == year),
"Agent {} is missing objectives for milestone year {}",
agent_id,
year
);
}
}

Ok(objectives)
}
Expand Down Expand Up @@ -119,6 +138,7 @@ mod tests {
($decision_weight:expr, $decision_lexico_tolerance:expr) => {
AgentObjective {
agent_id: "agent".into(),
year: 2020,
objective_type: ObjectiveType::EquivalentAnnualCost,
decision_weight: $decision_weight,
decision_lexico_tolerance: $decision_lexico_tolerance,
Expand Down Expand Up @@ -181,30 +201,51 @@ mod tests {
)]
.into_iter()
.collect();
let milestone_years = [2020];

// Valid
let objective = AgentObjective {
agent_id: "agent".into(),
year: 2020,
objective_type: ObjectiveType::EquivalentAnnualCost,
decision_weight: None,
decision_lexico_tolerance: None,
};
let expected = [("agent".into(), vec![objective.clone()])]
.into_iter()
.collect();
let actual = read_agent_objectives_from_iter([objective].into_iter(), &agents).unwrap();
let actual = read_agent_objectives_from_iter(
[objective.clone()].into_iter(),
&agents,
&milestone_years,
)
.unwrap();
assert_eq!(actual, expected);

// Missing objective for agent
assert!(read_agent_objectives_from_iter([].into_iter(), &agents).is_err());
assert!(
read_agent_objectives_from_iter([].into_iter(), &agents, &milestone_years).is_err()
);

// Missing objective for milestone year
assert!(
read_agent_objectives_from_iter([objective].into_iter(), &agents, &[2020, 2030])
.is_err()
);

// Bad parameter
let objective = AgentObjective {
let bad_objective = AgentObjective {
agent_id: "agent".into(),
year: 2020,
objective_type: ObjectiveType::EquivalentAnnualCost,
decision_weight: Some(1.0),
decision_weight: Some(1.0), // Should only accept None for DecisionRule::Single
decision_lexico_tolerance: None,
};
assert!(read_agent_objectives_from_iter([objective].into_iter(), &agents).is_err());
assert!(read_agent_objectives_from_iter(
[bad_objective].into_iter(),
&agents,
&milestone_years
)
.is_err());
}
}