use std::collections::HashSet;
use super::System;
use super::validate::{
CrossRefEntities, build_index, build_stage_index, check_duplicate_stages, check_duplicates,
validate_cross_references, validate_filling_configs,
};
use crate::{
Bus, CascadeTopology, CorrelationModel, EnergyContract, EntityId, ExternalLoadRow,
ExternalNcsRow, ExternalScenarioRow, GenericConstraint, Hydro, InflowHistoryRow, InflowModel,
InitialConditions, Line, LoadModel, NcsModel, NetworkTopology, NonControllableSource,
PolicyGraph, PumpingStation, ResolvedBounds, ResolvedExchangeFactors,
ResolvedGenericConstraintBounds, ResolvedLoadFactors, ResolvedNcsBounds, ResolvedNcsFactors,
ResolvedPenalties, Stage, Thermal, ValidationError,
};
pub struct SystemBuilder {
buses: Vec<Bus>,
lines: Vec<Line>,
hydros: Vec<Hydro>,
thermals: Vec<Thermal>,
pumping_stations: Vec<PumpingStation>,
contracts: Vec<EnergyContract>,
non_controllable_sources: Vec<NonControllableSource>,
stages: Vec<Stage>,
policy_graph: PolicyGraph,
penalties: ResolvedPenalties,
bounds: ResolvedBounds,
resolved_generic_bounds: ResolvedGenericConstraintBounds,
resolved_load_factors: ResolvedLoadFactors,
resolved_exchange_factors: ResolvedExchangeFactors,
resolved_ncs_bounds: ResolvedNcsBounds,
resolved_ncs_factors: ResolvedNcsFactors,
inflow_models: Vec<InflowModel>,
load_models: Vec<LoadModel>,
ncs_models: Vec<NcsModel>,
correlation: CorrelationModel,
initial_conditions: InitialConditions,
generic_constraints: Vec<GenericConstraint>,
inflow_history: Vec<InflowHistoryRow>,
external_scenarios: Vec<ExternalScenarioRow>,
external_load_scenarios: Vec<ExternalLoadRow>,
external_ncs_scenarios: Vec<ExternalNcsRow>,
}
impl Default for SystemBuilder {
fn default() -> Self {
Self::new()
}
}
impl SystemBuilder {
#[must_use]
pub fn new() -> Self {
Self {
buses: Vec::new(),
lines: Vec::new(),
hydros: Vec::new(),
thermals: Vec::new(),
pumping_stations: Vec::new(),
contracts: Vec::new(),
non_controllable_sources: Vec::new(),
stages: Vec::new(),
policy_graph: PolicyGraph::default(),
penalties: ResolvedPenalties::empty(),
bounds: ResolvedBounds::empty(),
resolved_generic_bounds: ResolvedGenericConstraintBounds::empty(),
resolved_load_factors: ResolvedLoadFactors::empty(),
resolved_exchange_factors: ResolvedExchangeFactors::empty(),
resolved_ncs_bounds: ResolvedNcsBounds::empty(),
resolved_ncs_factors: ResolvedNcsFactors::empty(),
inflow_models: Vec::new(),
load_models: Vec::new(),
ncs_models: Vec::new(),
correlation: CorrelationModel::default(),
initial_conditions: InitialConditions::default(),
generic_constraints: Vec::new(),
inflow_history: Vec::new(),
external_scenarios: Vec::new(),
external_load_scenarios: Vec::new(),
external_ncs_scenarios: Vec::new(),
}
}
#[must_use]
pub fn buses(mut self, buses: Vec<Bus>) -> Self {
self.buses = buses;
self
}
#[must_use]
pub fn lines(mut self, lines: Vec<Line>) -> Self {
self.lines = lines;
self
}
#[must_use]
pub fn hydros(mut self, hydros: Vec<Hydro>) -> Self {
self.hydros = hydros;
self
}
#[must_use]
pub fn thermals(mut self, thermals: Vec<Thermal>) -> Self {
self.thermals = thermals;
self
}
#[must_use]
pub fn pumping_stations(mut self, stations: Vec<PumpingStation>) -> Self {
self.pumping_stations = stations;
self
}
#[must_use]
pub fn contracts(mut self, contracts: Vec<EnergyContract>) -> Self {
self.contracts = contracts;
self
}
#[must_use]
pub fn non_controllable_sources(mut self, sources: Vec<NonControllableSource>) -> Self {
self.non_controllable_sources = sources;
self
}
#[must_use]
pub fn stages(mut self, stages: Vec<Stage>) -> Self {
self.stages = stages;
self
}
#[must_use]
pub fn policy_graph(mut self, policy_graph: PolicyGraph) -> Self {
self.policy_graph = policy_graph;
self
}
#[must_use]
pub fn penalties(mut self, penalties: ResolvedPenalties) -> Self {
self.penalties = penalties;
self
}
#[must_use]
pub fn bounds(mut self, bounds: ResolvedBounds) -> Self {
self.bounds = bounds;
self
}
#[must_use]
pub fn resolved_generic_bounds(
mut self,
resolved_generic_bounds: ResolvedGenericConstraintBounds,
) -> Self {
self.resolved_generic_bounds = resolved_generic_bounds;
self
}
#[must_use]
pub fn resolved_load_factors(mut self, resolved_load_factors: ResolvedLoadFactors) -> Self {
self.resolved_load_factors = resolved_load_factors;
self
}
#[must_use]
pub fn resolved_exchange_factors(
mut self,
resolved_exchange_factors: ResolvedExchangeFactors,
) -> Self {
self.resolved_exchange_factors = resolved_exchange_factors;
self
}
#[must_use]
pub fn resolved_ncs_bounds(mut self, resolved_ncs_bounds: ResolvedNcsBounds) -> Self {
self.resolved_ncs_bounds = resolved_ncs_bounds;
self
}
#[must_use]
pub fn resolved_ncs_factors(mut self, resolved_ncs_factors: ResolvedNcsFactors) -> Self {
self.resolved_ncs_factors = resolved_ncs_factors;
self
}
#[must_use]
pub fn inflow_models(mut self, inflow_models: Vec<InflowModel>) -> Self {
self.inflow_models = inflow_models;
self
}
#[must_use]
pub fn load_models(mut self, load_models: Vec<LoadModel>) -> Self {
self.load_models = load_models;
self
}
#[must_use]
pub fn ncs_models(mut self, ncs_models: Vec<NcsModel>) -> Self {
self.ncs_models = ncs_models;
self
}
#[must_use]
pub fn correlation(mut self, correlation: CorrelationModel) -> Self {
self.correlation = correlation;
self
}
#[must_use]
pub fn initial_conditions(mut self, initial_conditions: InitialConditions) -> Self {
self.initial_conditions = initial_conditions;
self
}
#[must_use]
pub fn generic_constraints(mut self, generic_constraints: Vec<GenericConstraint>) -> Self {
self.generic_constraints = generic_constraints;
self
}
#[must_use]
pub fn inflow_history(mut self, rows: Vec<InflowHistoryRow>) -> Self {
self.inflow_history = rows;
self
}
#[must_use]
pub fn external_scenarios(mut self, rows: Vec<ExternalScenarioRow>) -> Self {
self.external_scenarios = rows;
self
}
#[must_use]
pub fn external_load_scenarios(mut self, rows: Vec<ExternalLoadRow>) -> Self {
self.external_load_scenarios = rows;
self
}
#[must_use]
pub fn external_ncs_scenarios(mut self, rows: Vec<ExternalNcsRow>) -> Self {
self.external_ncs_scenarios = rows;
self
}
#[allow(clippy::too_many_lines)]
pub fn build(mut self) -> Result<System, Vec<ValidationError>> {
self.buses.sort_by_key(|e| e.id.0);
self.lines.sort_by_key(|e| e.id.0);
self.hydros.sort_by_key(|e| e.id.0);
self.thermals.sort_by_key(|e| e.id.0);
self.pumping_stations.sort_by_key(|e| e.id.0);
self.contracts.sort_by_key(|e| e.id.0);
self.non_controllable_sources.sort_by_key(|e| e.id.0);
self.stages.sort_by_key(|s| s.id);
self.generic_constraints.sort_by_key(|c| c.id.0);
let mut errors: Vec<ValidationError> = Vec::new();
check_duplicates(&self.buses, "Bus", &mut errors);
check_duplicates(&self.lines, "Line", &mut errors);
check_duplicates(&self.hydros, "Hydro", &mut errors);
check_duplicates(&self.thermals, "Thermal", &mut errors);
check_duplicates(&self.pumping_stations, "PumpingStation", &mut errors);
check_duplicates(&self.contracts, "EnergyContract", &mut errors);
check_duplicates(
&self.non_controllable_sources,
"NonControllableSource",
&mut errors,
);
check_duplicate_stages(&self.stages, &mut errors);
if !errors.is_empty() {
return Err(errors);
}
let bus_index = build_index(&self.buses);
let line_index = build_index(&self.lines);
let hydro_index = build_index(&self.hydros);
let thermal_index = build_index(&self.thermals);
let pumping_station_index = build_index(&self.pumping_stations);
let contract_index = build_index(&self.contracts);
let non_controllable_source_index = build_index(&self.non_controllable_sources);
validate_cross_references(
&CrossRefEntities {
lines: &self.lines,
hydros: &self.hydros,
thermals: &self.thermals,
pumping_stations: &self.pumping_stations,
contracts: &self.contracts,
non_controllable_sources: &self.non_controllable_sources,
},
&bus_index,
&hydro_index,
&mut errors,
);
if !errors.is_empty() {
return Err(errors);
}
let cascade = CascadeTopology::build(&self.hydros);
if cascade.topological_order().len() < self.hydros.len() {
let in_topo: HashSet<EntityId> = cascade.topological_order().iter().copied().collect();
let mut cycle_ids: Vec<EntityId> = self
.hydros
.iter()
.map(|h| h.id)
.filter(|id| !in_topo.contains(id))
.collect();
cycle_ids.sort_by_key(|id| id.0);
errors.push(ValidationError::CascadeCycle { cycle_ids });
}
validate_filling_configs(&self.hydros, &mut errors);
if !errors.is_empty() {
return Err(errors);
}
let network = NetworkTopology::build(
&self.buses,
&self.lines,
&self.hydros,
&self.thermals,
&self.non_controllable_sources,
&self.contracts,
&self.pumping_stations,
);
let stage_index = build_stage_index(&self.stages);
Ok(System {
buses: self.buses,
lines: self.lines,
hydros: self.hydros,
thermals: self.thermals,
pumping_stations: self.pumping_stations,
contracts: self.contracts,
non_controllable_sources: self.non_controllable_sources,
bus_index,
line_index,
hydro_index,
thermal_index,
pumping_station_index,
contract_index,
non_controllable_source_index,
cascade,
network,
stages: self.stages,
policy_graph: self.policy_graph,
stage_index,
penalties: self.penalties,
bounds: self.bounds,
resolved_generic_bounds: self.resolved_generic_bounds,
resolved_load_factors: self.resolved_load_factors,
resolved_exchange_factors: self.resolved_exchange_factors,
resolved_ncs_bounds: self.resolved_ncs_bounds,
resolved_ncs_factors: self.resolved_ncs_factors,
inflow_models: self.inflow_models,
load_models: self.load_models,
ncs_models: self.ncs_models,
correlation: self.correlation,
initial_conditions: self.initial_conditions,
generic_constraints: self.generic_constraints,
inflow_history: self.inflow_history,
external_scenarios: self.external_scenarios,
external_load_scenarios: self.external_load_scenarios,
external_ncs_scenarios: self.external_ncs_scenarios,
})
}
}