1use std::collections::HashSet;
11
12use super::System;
13use super::validate::{
14 CrossRefEntities, build_index, build_stage_index, check_duplicate_stages, check_duplicates,
15 validate_cross_references, validate_filling_configs,
16};
17use crate::{
18 Bus, CascadeTopology, CorrelationModel, EnergyContract, EntityId, ExternalLoadRow,
19 ExternalNcsRow, ExternalScenarioRow, GenericConstraint, Hydro, InflowHistoryRow, InflowModel,
20 InitialConditions, Line, LoadModel, NcsModel, NetworkTopology, NonControllableSource,
21 PolicyGraph, PumpingStation, ResolvedBounds, ResolvedExchangeFactors,
22 ResolvedGenericConstraintBounds, ResolvedLoadFactors, ResolvedNcsBounds, ResolvedNcsFactors,
23 ResolvedPenalties, Stage, Thermal, ValidationError,
24};
25
26pub struct SystemBuilder {
50 buses: Vec<Bus>,
51 lines: Vec<Line>,
52 hydros: Vec<Hydro>,
53 thermals: Vec<Thermal>,
54 pumping_stations: Vec<PumpingStation>,
55 contracts: Vec<EnergyContract>,
56 non_controllable_sources: Vec<NonControllableSource>,
57 stages: Vec<Stage>,
58 policy_graph: PolicyGraph,
59 penalties: ResolvedPenalties,
60 bounds: ResolvedBounds,
61 resolved_generic_bounds: ResolvedGenericConstraintBounds,
62 resolved_load_factors: ResolvedLoadFactors,
63 resolved_exchange_factors: ResolvedExchangeFactors,
64 resolved_ncs_bounds: ResolvedNcsBounds,
65 resolved_ncs_factors: ResolvedNcsFactors,
66 inflow_models: Vec<InflowModel>,
67 load_models: Vec<LoadModel>,
68 ncs_models: Vec<NcsModel>,
69 correlation: CorrelationModel,
70 initial_conditions: InitialConditions,
71 generic_constraints: Vec<GenericConstraint>,
72 inflow_history: Vec<InflowHistoryRow>,
73 external_scenarios: Vec<ExternalScenarioRow>,
74 external_load_scenarios: Vec<ExternalLoadRow>,
75 external_ncs_scenarios: Vec<ExternalNcsRow>,
76}
77
78impl Default for SystemBuilder {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl SystemBuilder {
85 #[must_use]
90 pub fn new() -> Self {
91 Self {
92 buses: Vec::new(),
93 lines: Vec::new(),
94 hydros: Vec::new(),
95 thermals: Vec::new(),
96 pumping_stations: Vec::new(),
97 contracts: Vec::new(),
98 non_controllable_sources: Vec::new(),
99 stages: Vec::new(),
100 policy_graph: PolicyGraph::default(),
101 penalties: ResolvedPenalties::empty(),
102 bounds: ResolvedBounds::empty(),
103 resolved_generic_bounds: ResolvedGenericConstraintBounds::empty(),
104 resolved_load_factors: ResolvedLoadFactors::empty(),
105 resolved_exchange_factors: ResolvedExchangeFactors::empty(),
106 resolved_ncs_bounds: ResolvedNcsBounds::empty(),
107 resolved_ncs_factors: ResolvedNcsFactors::empty(),
108 inflow_models: Vec::new(),
109 load_models: Vec::new(),
110 ncs_models: Vec::new(),
111 correlation: CorrelationModel::default(),
112 initial_conditions: InitialConditions::default(),
113 generic_constraints: Vec::new(),
114 inflow_history: Vec::new(),
115 external_scenarios: Vec::new(),
116 external_load_scenarios: Vec::new(),
117 external_ncs_scenarios: Vec::new(),
118 }
119 }
120
121 #[must_use]
123 pub fn buses(mut self, buses: Vec<Bus>) -> Self {
124 self.buses = buses;
125 self
126 }
127
128 #[must_use]
130 pub fn lines(mut self, lines: Vec<Line>) -> Self {
131 self.lines = lines;
132 self
133 }
134
135 #[must_use]
137 pub fn hydros(mut self, hydros: Vec<Hydro>) -> Self {
138 self.hydros = hydros;
139 self
140 }
141
142 #[must_use]
144 pub fn thermals(mut self, thermals: Vec<Thermal>) -> Self {
145 self.thermals = thermals;
146 self
147 }
148
149 #[must_use]
151 pub fn pumping_stations(mut self, stations: Vec<PumpingStation>) -> Self {
152 self.pumping_stations = stations;
153 self
154 }
155
156 #[must_use]
158 pub fn contracts(mut self, contracts: Vec<EnergyContract>) -> Self {
159 self.contracts = contracts;
160 self
161 }
162
163 #[must_use]
165 pub fn non_controllable_sources(mut self, sources: Vec<NonControllableSource>) -> Self {
166 self.non_controllable_sources = sources;
167 self
168 }
169
170 #[must_use]
174 pub fn stages(mut self, stages: Vec<Stage>) -> Self {
175 self.stages = stages;
176 self
177 }
178
179 #[must_use]
181 pub fn policy_graph(mut self, policy_graph: PolicyGraph) -> Self {
182 self.policy_graph = policy_graph;
183 self
184 }
185
186 #[must_use]
190 pub fn penalties(mut self, penalties: ResolvedPenalties) -> Self {
191 self.penalties = penalties;
192 self
193 }
194
195 #[must_use]
199 pub fn bounds(mut self, bounds: ResolvedBounds) -> Self {
200 self.bounds = bounds;
201 self
202 }
203
204 #[must_use]
209 pub fn resolved_generic_bounds(
210 mut self,
211 resolved_generic_bounds: ResolvedGenericConstraintBounds,
212 ) -> Self {
213 self.resolved_generic_bounds = resolved_generic_bounds;
214 self
215 }
216
217 #[must_use]
221 pub fn resolved_load_factors(mut self, resolved_load_factors: ResolvedLoadFactors) -> Self {
222 self.resolved_load_factors = resolved_load_factors;
223 self
224 }
225
226 #[must_use]
230 pub fn resolved_exchange_factors(
231 mut self,
232 resolved_exchange_factors: ResolvedExchangeFactors,
233 ) -> Self {
234 self.resolved_exchange_factors = resolved_exchange_factors;
235 self
236 }
237
238 #[must_use]
242 pub fn resolved_ncs_bounds(mut self, resolved_ncs_bounds: ResolvedNcsBounds) -> Self {
243 self.resolved_ncs_bounds = resolved_ncs_bounds;
244 self
245 }
246
247 #[must_use]
251 pub fn resolved_ncs_factors(mut self, resolved_ncs_factors: ResolvedNcsFactors) -> Self {
252 self.resolved_ncs_factors = resolved_ncs_factors;
253 self
254 }
255
256 #[must_use]
258 pub fn inflow_models(mut self, inflow_models: Vec<InflowModel>) -> Self {
259 self.inflow_models = inflow_models;
260 self
261 }
262
263 #[must_use]
265 pub fn load_models(mut self, load_models: Vec<LoadModel>) -> Self {
266 self.load_models = load_models;
267 self
268 }
269
270 #[must_use]
272 pub fn ncs_models(mut self, ncs_models: Vec<NcsModel>) -> Self {
273 self.ncs_models = ncs_models;
274 self
275 }
276
277 #[must_use]
279 pub fn correlation(mut self, correlation: CorrelationModel) -> Self {
280 self.correlation = correlation;
281 self
282 }
283
284 #[must_use]
286 pub fn initial_conditions(mut self, initial_conditions: InitialConditions) -> Self {
287 self.initial_conditions = initial_conditions;
288 self
289 }
290
291 #[must_use]
295 pub fn generic_constraints(mut self, generic_constraints: Vec<GenericConstraint>) -> Self {
296 self.generic_constraints = generic_constraints;
297 self
298 }
299
300 #[must_use]
306 pub fn inflow_history(mut self, rows: Vec<InflowHistoryRow>) -> Self {
307 self.inflow_history = rows;
308 self
309 }
310
311 #[must_use]
317 pub fn external_scenarios(mut self, rows: Vec<ExternalScenarioRow>) -> Self {
318 self.external_scenarios = rows;
319 self
320 }
321
322 #[must_use]
328 pub fn external_load_scenarios(mut self, rows: Vec<ExternalLoadRow>) -> Self {
329 self.external_load_scenarios = rows;
330 self
331 }
332
333 #[must_use]
339 pub fn external_ncs_scenarios(mut self, rows: Vec<ExternalNcsRow>) -> Self {
340 self.external_ncs_scenarios = rows;
341 self
342 }
343
344 #[allow(clippy::too_many_lines)]
376 pub fn build(mut self) -> Result<System, Vec<ValidationError>> {
377 self.buses.sort_by_key(|e| e.id.0);
378 self.lines.sort_by_key(|e| e.id.0);
379 self.hydros.sort_by_key(|e| e.id.0);
380 self.thermals.sort_by_key(|e| e.id.0);
381 self.pumping_stations.sort_by_key(|e| e.id.0);
382 self.contracts.sort_by_key(|e| e.id.0);
383 self.non_controllable_sources.sort_by_key(|e| e.id.0);
384 self.stages.sort_by_key(|s| s.id);
385 self.generic_constraints.sort_by_key(|c| c.id.0);
386
387 let mut errors: Vec<ValidationError> = Vec::new();
388 check_duplicates(&self.buses, "Bus", &mut errors);
389 check_duplicates(&self.lines, "Line", &mut errors);
390 check_duplicates(&self.hydros, "Hydro", &mut errors);
391 check_duplicates(&self.thermals, "Thermal", &mut errors);
392 check_duplicates(&self.pumping_stations, "PumpingStation", &mut errors);
393 check_duplicates(&self.contracts, "EnergyContract", &mut errors);
394 check_duplicates(
395 &self.non_controllable_sources,
396 "NonControllableSource",
397 &mut errors,
398 );
399 check_duplicate_stages(&self.stages, &mut errors);
400
401 if !errors.is_empty() {
402 return Err(errors);
403 }
404
405 let bus_index = build_index(&self.buses);
406 let line_index = build_index(&self.lines);
407 let hydro_index = build_index(&self.hydros);
408 let thermal_index = build_index(&self.thermals);
409 let pumping_station_index = build_index(&self.pumping_stations);
410 let contract_index = build_index(&self.contracts);
411 let non_controllable_source_index = build_index(&self.non_controllable_sources);
412
413 validate_cross_references(
414 &CrossRefEntities {
415 lines: &self.lines,
416 hydros: &self.hydros,
417 thermals: &self.thermals,
418 pumping_stations: &self.pumping_stations,
419 contracts: &self.contracts,
420 non_controllable_sources: &self.non_controllable_sources,
421 },
422 &bus_index,
423 &hydro_index,
424 &mut errors,
425 );
426
427 if !errors.is_empty() {
428 return Err(errors);
429 }
430
431 let cascade = CascadeTopology::build(&self.hydros);
432
433 if cascade.topological_order().len() < self.hydros.len() {
434 let in_topo: HashSet<EntityId> = cascade.topological_order().iter().copied().collect();
435 let mut cycle_ids: Vec<EntityId> = self
436 .hydros
437 .iter()
438 .map(|h| h.id)
439 .filter(|id| !in_topo.contains(id))
440 .collect();
441 cycle_ids.sort_by_key(|id| id.0);
442 errors.push(ValidationError::CascadeCycle { cycle_ids });
443 }
444
445 validate_filling_configs(&self.hydros, &mut errors);
446
447 if !errors.is_empty() {
448 return Err(errors);
449 }
450
451 let network = NetworkTopology::build(
452 &self.buses,
453 &self.lines,
454 &self.hydros,
455 &self.thermals,
456 &self.non_controllable_sources,
457 &self.contracts,
458 &self.pumping_stations,
459 );
460
461 let stage_index = build_stage_index(&self.stages);
462
463 Ok(System {
464 buses: self.buses,
465 lines: self.lines,
466 hydros: self.hydros,
467 thermals: self.thermals,
468 pumping_stations: self.pumping_stations,
469 contracts: self.contracts,
470 non_controllable_sources: self.non_controllable_sources,
471 bus_index,
472 line_index,
473 hydro_index,
474 thermal_index,
475 pumping_station_index,
476 contract_index,
477 non_controllable_source_index,
478 cascade,
479 network,
480 stages: self.stages,
481 policy_graph: self.policy_graph,
482 stage_index,
483 penalties: self.penalties,
484 bounds: self.bounds,
485 resolved_generic_bounds: self.resolved_generic_bounds,
486 resolved_load_factors: self.resolved_load_factors,
487 resolved_exchange_factors: self.resolved_exchange_factors,
488 resolved_ncs_bounds: self.resolved_ncs_bounds,
489 resolved_ncs_factors: self.resolved_ncs_factors,
490 inflow_models: self.inflow_models,
491 load_models: self.load_models,
492 ncs_models: self.ncs_models,
493 correlation: self.correlation,
494 initial_conditions: self.initial_conditions,
495 generic_constraints: self.generic_constraints,
496 inflow_history: self.inflow_history,
497 external_scenarios: self.external_scenarios,
498 external_load_scenarios: self.external_load_scenarios,
499 external_ncs_scenarios: self.external_ncs_scenarios,
500 })
501 }
502}