1use std::sync::Arc;
13
14use powerio_core::Error;
15use powerio_tx::{BalancedNetwork, BusId};
16
17use crate::diagnostics::codes;
18use crate::instance::{AcOpfInstance, AcPfInstance, DcOpfInstance, DcPfInstance};
19use crate::operating::row_identity;
20use crate::solution::{Producer, Residuals, Termination};
21
22#[derive(Clone, Debug, Default, PartialEq)]
24#[non_exhaustive]
25pub struct GeneratorDispatch {
26 pub p_mw: Vec<f64>,
28 pub q_mvar: Vec<f64>,
30}
31
32#[derive(Clone, Copy, Debug, Default, PartialEq)]
36#[non_exhaustive]
37pub struct ThreeWindingTransformerTerminalPower {
38 pub p_mw: [f64; 3],
39 pub q_mvar: [f64; 3],
40}
41
42impl ThreeWindingTransformerTerminalPower {
43 #[must_use]
44 pub const fn new(p_mw: [f64; 3], q_mvar: [f64; 3]) -> Self {
45 Self { p_mw, q_mvar }
46 }
47}
48
49#[derive(Clone, Copy, Debug, Default, PartialEq)]
53#[non_exhaustive]
54pub struct ThreeWindingTransformerTerminalActivePower {
55 pub p_mw: [f64; 3],
56}
57
58impl ThreeWindingTransformerTerminalActivePower {
59 #[must_use]
60 pub const fn new(p_mw: [f64; 3]) -> Self {
61 Self { p_mw }
62 }
63}
64
65fn check_length(what: &'static str, got: usize, expected: usize) -> Result<(), Error> {
66 if got == expected {
67 Ok(())
68 } else {
69 Err(Error::new(
70 &codes::BUILD_SOLUTION_SHAPE_MISMATCH,
71 format!("{what} carries {got} values; the instance's table has {expected} rows"),
72 ))
73 }
74}
75
76fn check_nonnegative_multipliers(what: &'static str, values: &[f64]) -> Result<(), Error> {
77 if let Some((row, value)) = values
78 .iter()
79 .copied()
80 .enumerate()
81 .find(|(_, value)| !value.is_finite() || *value < 0.0)
82 {
83 return Err(Error::new(
84 &codes::BUILD_SOLUTION_MULTIPLIER_INVALID,
85 format!("{what} row {row} is {value}; multipliers must be finite and nonnegative"),
86 ));
87 }
88 Ok(())
89}
90
91#[derive(Clone, Debug, Default)]
94struct SolutionIndex {
95 bus: std::collections::BTreeMap<BusId, usize>,
96 branch: std::collections::BTreeMap<String, usize>,
97 generator: std::collections::BTreeMap<String, usize>,
98}
99
100impl SolutionIndex {
101 fn build(network: &BalancedNetwork) -> Result<Self, Error> {
102 let mut index = Self::default();
103 for (row, bus) in network.buses().iter().enumerate() {
104 if index.bus.insert(bus.id, row).is_some() {
105 return Err(duplicate_identity("bus", &bus.id.to_string()));
106 }
107 }
108 for (row, branch) in network.branches().iter().enumerate() {
109 let identity = row_identity(branch.uid.as_deref(), "branches", row);
110 if index.branch.insert(identity.clone(), row).is_some() {
111 return Err(duplicate_identity("branch", &identity));
112 }
113 }
114 for (row, generator) in network.generators().iter().enumerate() {
115 let identity = row_identity(generator.uid.as_deref(), "generators", row);
116 if index.generator.insert(identity.clone(), row).is_some() {
117 return Err(duplicate_identity("generator", &identity));
118 }
119 }
120 Ok(index)
121 }
122}
123
124fn solution_index<I>(instance: &std::sync::Arc<I>) -> Result<SolutionIndex, Error>
128where
129 I: NetworkCarrier,
130{
131 SolutionIndex::build(instance.network())
132}
133
134trait NetworkCarrier {
136 fn network(&self) -> &BalancedNetwork;
137}
138
139impl NetworkCarrier for DcPfInstance {
140 fn network(&self) -> &BalancedNetwork {
141 DcPfInstance::network(self)
142 }
143}
144impl NetworkCarrier for AcPfInstance {
145 fn network(&self) -> &BalancedNetwork {
146 AcPfInstance::network(self)
147 }
148}
149impl NetworkCarrier for DcOpfInstance {
150 fn network(&self) -> &BalancedNetwork {
151 DcOpfInstance::network(self)
152 }
153}
154impl NetworkCarrier for AcOpfInstance {
155 fn network(&self) -> &BalancedNetwork {
156 AcOpfInstance::network(self)
157 }
158}
159
160fn duplicate_identity(kind: &str, identity: &str) -> Error {
161 Error::new(
162 &codes::BUILD_OPERATING_POINT_IDENTITY_UNKNOWN,
163 format!("{kind}: duplicate element identity `{identity}`"),
164 )
165}
166
167fn bus_position(index: &SolutionIndex, bus: BusId) -> Option<usize> {
168 index.bus.get(&bus).copied()
169}
170
171fn branch_position(index: &SolutionIndex, identity: &str) -> Option<usize> {
172 index.branch.get(identity).copied()
173}
174
175fn generator_position(index: &SolutionIndex, identity: &str) -> Option<usize> {
176 index.generator.get(identity).copied()
177}
178
179macro_rules! shared_solution_accessors {
180 ($instance_type:ty) => {
181 #[must_use]
184 pub fn instance(&self) -> &$instance_type {
185 &self.instance
186 }
187
188 #[must_use]
191 pub fn shared_instance(&self) -> Arc<$instance_type> {
192 Arc::clone(&self.instance)
193 }
194
195 #[must_use]
197 pub fn network(&self) -> &BalancedNetwork {
198 self.instance.network()
199 }
200
201 fn row_index(&self) -> &SolutionIndex {
202 &self.index
203 }
204
205 #[must_use]
207 pub fn bus_order(&self) -> Vec<BusId> {
208 self.network().buses().iter().map(|bus| bus.id).collect()
209 }
210
211 #[must_use]
213 pub fn branch_order(&self) -> Vec<String> {
214 self.network()
215 .branches()
216 .iter()
217 .enumerate()
218 .map(|(row, branch)| row_identity(branch.uid.as_deref(), "branches", row))
219 .collect()
220 }
221
222 #[must_use]
224 pub fn generator_order(&self) -> Vec<String> {
225 self.network()
226 .generators()
227 .iter()
228 .enumerate()
229 .map(|(row, generator)| row_identity(generator.uid.as_deref(), "generators", row))
230 .collect()
231 }
232
233 #[must_use]
235 pub fn termination(&self) -> &Termination {
236 &self.termination
237 }
238
239 #[must_use]
241 pub fn residuals(&self) -> &Residuals {
242 &self.residuals
243 }
244
245 #[must_use]
247 pub fn producer(&self) -> Option<&str> {
248 self.producer.as_deref()
249 }
250
251 #[must_use]
253 pub fn with_producer(mut self, producer: impl Into<String>) -> Self {
254 self.producer = Some(producer.into());
255 self
256 }
257
258 #[must_use]
260 pub fn with_residuals(mut self, residuals: Residuals) -> Self {
261 self.residuals = residuals;
262 self
263 }
264
265 pub fn branch_identity_order(&self) -> impl Iterator<Item = String> + '_ {
267 self.network()
268 .branches()
269 .iter()
270 .enumerate()
271 .map(|(row, branch)| row_identity(branch.uid.as_deref(), "branches", row))
272 }
273 };
274}
275
276macro_rules! optional_dispatch_accessors {
277 () => {
278 #[must_use]
281 pub fn generator_dispatch(&self) -> Option<&GeneratorDispatch> {
282 self.generator_dispatch.as_ref()
283 }
284
285 pub fn with_generator_dispatch(
290 mut self,
291 dispatch: GeneratorDispatch,
292 ) -> Result<Self, Error> {
293 check_length(
294 "generator dispatch",
295 dispatch.p_mw.len(),
296 self.network().generators().len(),
297 )?;
298 if !dispatch.q_mvar.is_empty() {
299 check_length(
300 "generator reactive dispatch",
301 dispatch.q_mvar.len(),
302 self.network().generators().len(),
303 )?;
304 }
305 self.generator_dispatch = Some(dispatch);
306 Ok(self)
307 }
308 };
309}
310
311#[derive(Clone, Debug)]
314pub struct DcPfSolution {
315 instance: Arc<DcPfInstance>,
316 termination: Termination,
317 residuals: Residuals,
318 producer: Producer,
319 bus_voltage_angle: Vec<f64>,
320 bus_active_injection: Vec<f64>,
321 branch_from_active_flow: Vec<f64>,
322 branch_to_active_flow: Vec<f64>,
323 three_winding_transformer_terminal_active_power:
324 Vec<ThreeWindingTransformerTerminalActivePower>,
325 generator_dispatch: Option<GeneratorDispatch>,
326 index: SolutionIndex,
327}
328
329impl DcPfSolution {
330 pub fn new(
338 instance: Arc<DcPfInstance>,
339 termination: Termination,
340 bus_voltage_angle: Vec<f64>,
341 bus_active_injection: Vec<f64>,
342 branch_from_active_flow: Vec<f64>,
343 branch_to_active_flow: Vec<f64>,
344 three_winding_transformer_terminal_active_power: Vec<
345 ThreeWindingTransformerTerminalActivePower,
346 >,
347 ) -> Result<Self, Error> {
348 let buses = instance.network().buses().len();
349 let branches = instance.network().branches().len();
350 check_length("bus voltage angles", bus_voltage_angle.len(), buses)?;
351 check_length("bus active injections", bus_active_injection.len(), buses)?;
352 check_length(
353 "branch from-side flows",
354 branch_from_active_flow.len(),
355 branches,
356 )?;
357 check_length(
358 "branch to-side flows",
359 branch_to_active_flow.len(),
360 branches,
361 )?;
362 check_length(
363 "three winding transformer terminal active powers",
364 three_winding_transformer_terminal_active_power.len(),
365 instance.network().transformers_3w().len(),
366 )?;
367 let index = solution_index(&instance)?;
368 Ok(Self {
369 instance,
370 termination,
371 residuals: Residuals::default(),
372 producer: None,
373 bus_voltage_angle,
374 bus_active_injection,
375 branch_from_active_flow,
376 branch_to_active_flow,
377 three_winding_transformer_terminal_active_power,
378 generator_dispatch: None,
379 index,
380 })
381 }
382
383 shared_solution_accessors!(DcPfInstance);
384 optional_dispatch_accessors!();
385
386 #[must_use]
388 pub fn bus_voltage_angle(&self, bus: BusId) -> Option<f64> {
389 Some(self.bus_voltage_angle[bus_position(self.row_index(), bus)?])
390 }
391
392 #[must_use]
394 pub fn bus_active_injection(&self, bus: BusId) -> Option<f64> {
395 Some(self.bus_active_injection[bus_position(self.row_index(), bus)?])
396 }
397
398 #[must_use]
401 pub fn branch_from_active_flow(&self, identity: &str) -> Option<f64> {
402 Some(self.branch_from_active_flow[branch_position(self.row_index(), identity)?])
403 }
404
405 #[must_use]
407 pub fn branch_to_active_flow(&self, identity: &str) -> Option<f64> {
408 Some(self.branch_to_active_flow[branch_position(self.row_index(), identity)?])
409 }
410
411 #[must_use]
414 pub fn three_winding_transformer_terminal_active_powers(
415 &self,
416 ) -> &[ThreeWindingTransformerTerminalActivePower] {
417 &self.three_winding_transformer_terminal_active_power
418 }
419
420 #[must_use]
422 pub fn bus_voltage_angles(&self) -> &[f64] {
423 &self.bus_voltage_angle
424 }
425 #[must_use]
427 pub fn bus_active_injections(&self) -> &[f64] {
428 &self.bus_active_injection
429 }
430 #[must_use]
432 pub fn branch_from_active_flows(&self) -> &[f64] {
433 &self.branch_from_active_flow
434 }
435 #[must_use]
437 pub fn branch_to_active_flows(&self) -> &[f64] {
438 &self.branch_to_active_flow
439 }
440}
441
442#[derive(Clone, Debug)]
445pub struct AcPfSolution {
446 instance: Arc<AcPfInstance>,
447 termination: Termination,
448 residuals: Residuals,
449 producer: Producer,
450 bus_voltage_magnitude: Vec<f64>,
451 bus_voltage_angle: Vec<f64>,
452 bus_active_injection: Vec<f64>,
453 bus_reactive_injection: Vec<f64>,
454 branch_from_active_flow: Vec<f64>,
455 branch_from_reactive_flow: Vec<f64>,
456 branch_to_active_flow: Vec<f64>,
457 branch_to_reactive_flow: Vec<f64>,
458 three_winding_transformer_terminal_power: Vec<ThreeWindingTransformerTerminalPower>,
459 generator_dispatch: Option<GeneratorDispatch>,
460 index: SolutionIndex,
461}
462
463impl AcPfSolution {
464 #[allow(clippy::too_many_arguments)] pub fn new(
473 instance: Arc<AcPfInstance>,
474 termination: Termination,
475 bus_voltage_magnitude: Vec<f64>,
476 bus_voltage_angle: Vec<f64>,
477 bus_active_injection: Vec<f64>,
478 bus_reactive_injection: Vec<f64>,
479 branch_from_active_flow: Vec<f64>,
480 branch_from_reactive_flow: Vec<f64>,
481 branch_to_active_flow: Vec<f64>,
482 branch_to_reactive_flow: Vec<f64>,
483 three_winding_transformer_terminal_power: Vec<ThreeWindingTransformerTerminalPower>,
484 ) -> Result<Self, Error> {
485 let buses = instance.network().buses().len();
486 let branches = instance.network().branches().len();
487 check_length("bus voltage magnitudes", bus_voltage_magnitude.len(), buses)?;
488 check_length("bus voltage angles", bus_voltage_angle.len(), buses)?;
489 check_length("bus active injections", bus_active_injection.len(), buses)?;
490 check_length(
491 "bus reactive injections",
492 bus_reactive_injection.len(),
493 buses,
494 )?;
495 check_length(
496 "branch from-side active flows",
497 branch_from_active_flow.len(),
498 branches,
499 )?;
500 check_length(
501 "branch from-side reactive flows",
502 branch_from_reactive_flow.len(),
503 branches,
504 )?;
505 check_length(
506 "branch to-side active flows",
507 branch_to_active_flow.len(),
508 branches,
509 )?;
510 check_length(
511 "branch to-side reactive flows",
512 branch_to_reactive_flow.len(),
513 branches,
514 )?;
515 check_length(
516 "three winding transformer terminal powers",
517 three_winding_transformer_terminal_power.len(),
518 instance.network().transformers_3w().len(),
519 )?;
520 let index = solution_index(&instance)?;
521 Ok(Self {
522 instance,
523 termination,
524 residuals: Residuals::default(),
525 producer: None,
526 bus_voltage_magnitude,
527 bus_voltage_angle,
528 bus_active_injection,
529 bus_reactive_injection,
530 branch_from_active_flow,
531 branch_from_reactive_flow,
532 branch_to_active_flow,
533 branch_to_reactive_flow,
534 three_winding_transformer_terminal_power,
535 generator_dispatch: None,
536 index,
537 })
538 }
539
540 shared_solution_accessors!(AcPfInstance);
541 optional_dispatch_accessors!();
542
543 #[must_use]
545 pub fn bus_voltage_magnitude(&self, bus: BusId) -> Option<f64> {
546 Some(self.bus_voltage_magnitude[bus_position(self.row_index(), bus)?])
547 }
548
549 #[must_use]
551 pub fn bus_voltage_angle(&self, bus: BusId) -> Option<f64> {
552 Some(self.bus_voltage_angle[bus_position(self.row_index(), bus)?])
553 }
554
555 #[must_use]
557 pub fn bus_active_injection(&self, bus: BusId) -> Option<f64> {
558 Some(self.bus_active_injection[bus_position(self.row_index(), bus)?])
559 }
560
561 #[must_use]
563 pub fn bus_reactive_injection(&self, bus: BusId) -> Option<f64> {
564 Some(self.bus_reactive_injection[bus_position(self.row_index(), bus)?])
565 }
566
567 #[must_use]
570 pub fn branch_from_active_flow(&self, identity: &str) -> Option<f64> {
571 Some(self.branch_from_active_flow[branch_position(self.row_index(), identity)?])
572 }
573
574 #[must_use]
576 pub fn branch_from_reactive_flow(&self, identity: &str) -> Option<f64> {
577 Some(self.branch_from_reactive_flow[branch_position(self.row_index(), identity)?])
578 }
579
580 #[must_use]
582 pub fn branch_to_active_flow(&self, identity: &str) -> Option<f64> {
583 Some(self.branch_to_active_flow[branch_position(self.row_index(), identity)?])
584 }
585
586 #[must_use]
588 pub fn branch_to_reactive_flow(&self, identity: &str) -> Option<f64> {
589 Some(self.branch_to_reactive_flow[branch_position(self.row_index(), identity)?])
590 }
591
592 #[must_use]
594 pub fn three_winding_transformer_terminal_powers(
595 &self,
596 ) -> &[ThreeWindingTransformerTerminalPower] {
597 &self.three_winding_transformer_terminal_power
598 }
599}
600
601#[derive(Clone, Debug)]
605pub struct DcOpfSolution {
606 instance: Arc<DcOpfInstance>,
607 termination: Termination,
608 residuals: Residuals,
609 producer: Producer,
610 bus_voltage_angle: Vec<f64>,
611 bus_active_injection: Vec<f64>,
612 branch_from_active_flow: Vec<f64>,
613 branch_to_active_flow: Vec<f64>,
614 generator_active_power: Vec<f64>,
615 three_winding_transformer_terminal_active_power:
616 Vec<ThreeWindingTransformerTerminalActivePower>,
617 objective: f64,
618 bus_active_power_marginal: Option<Vec<f64>>,
619 branch_from_limit_multiplier: Option<Vec<f64>>,
620 branch_to_limit_multiplier: Option<Vec<f64>>,
621 index: SolutionIndex,
622}
623
624impl DcOpfSolution {
625 #[allow(clippy::too_many_arguments)] pub fn new(
633 instance: Arc<DcOpfInstance>,
634 termination: Termination,
635 bus_voltage_angle: Vec<f64>,
636 bus_active_injection: Vec<f64>,
637 branch_from_active_flow: Vec<f64>,
638 branch_to_active_flow: Vec<f64>,
639 generator_active_power: Vec<f64>,
640 objective: f64,
641 three_winding_transformer_terminal_active_power: Vec<
642 ThreeWindingTransformerTerminalActivePower,
643 >,
644 ) -> Result<Self, Error> {
645 let buses = instance.network().buses().len();
646 let branches = instance.network().branches().len();
647 check_length("bus voltage angles", bus_voltage_angle.len(), buses)?;
648 check_length("bus active injections", bus_active_injection.len(), buses)?;
649 check_length(
650 "branch from-side flows",
651 branch_from_active_flow.len(),
652 branches,
653 )?;
654 check_length(
655 "branch to-side flows",
656 branch_to_active_flow.len(),
657 branches,
658 )?;
659 check_length(
660 "generator active dispatch",
661 generator_active_power.len(),
662 instance.network().generators().len(),
663 )?;
664 check_length(
665 "three winding transformer terminal active powers",
666 three_winding_transformer_terminal_active_power.len(),
667 instance.network().transformers_3w().len(),
668 )?;
669 let index = solution_index(&instance)?;
670 Ok(Self {
671 instance,
672 termination,
673 residuals: Residuals::default(),
674 producer: None,
675 bus_voltage_angle,
676 bus_active_injection,
677 branch_from_active_flow,
678 branch_to_active_flow,
679 generator_active_power,
680 three_winding_transformer_terminal_active_power,
681 objective,
682 bus_active_power_marginal: None,
683 branch_from_limit_multiplier: None,
684 branch_to_limit_multiplier: None,
685 index,
686 })
687 }
688
689 shared_solution_accessors!(DcOpfInstance);
690
691 pub fn with_bus_active_power_marginals(mut self, marginals: Vec<f64>) -> Result<Self, Error> {
699 check_length(
700 "bus active power marginals",
701 marginals.len(),
702 self.instance.network().buses().len(),
703 )?;
704 self.bus_active_power_marginal = Some(marginals);
705 Ok(self)
706 }
707
708 pub fn with_branch_thermal_limit_multipliers(
718 mut self,
719 from: Vec<f64>,
720 to: Vec<f64>,
721 ) -> Result<Self, Error> {
722 check_length(
723 "branch from-side thermal limit multipliers",
724 from.len(),
725 self.instance.network().branches().len(),
726 )?;
727 check_length(
728 "branch to-side thermal limit multipliers",
729 to.len(),
730 self.instance.network().branches().len(),
731 )?;
732 check_nonnegative_multipliers("branch from-side thermal limit multipliers", &from)?;
733 check_nonnegative_multipliers("branch to-side thermal limit multipliers", &to)?;
734 self.branch_from_limit_multiplier = Some(from);
735 self.branch_to_limit_multiplier = Some(to);
736 Ok(self)
737 }
738
739 #[must_use]
741 pub const fn objective(&self) -> f64 {
742 self.objective
743 }
744
745 #[must_use]
747 pub fn generator_active_power(&self, identity: &str) -> Option<f64> {
748 Some(self.generator_active_power[generator_position(self.row_index(), identity)?])
749 }
750
751 #[must_use]
753 pub fn bus_active_power_marginal(&self, bus: BusId) -> Option<f64> {
754 Some(self.bus_active_power_marginal.as_ref()?[bus_position(self.row_index(), bus)?])
755 }
756
757 #[must_use]
759 pub fn branch_from_limit_multiplier(&self, identity: &str) -> Option<f64> {
760 Some(
761 self.branch_from_limit_multiplier.as_ref()?
762 [branch_position(self.row_index(), identity)?],
763 )
764 }
765
766 #[must_use]
768 pub fn branch_to_limit_multiplier(&self, identity: &str) -> Option<f64> {
769 Some(
770 self.branch_to_limit_multiplier.as_ref()?[branch_position(self.row_index(), identity)?],
771 )
772 }
773
774 #[must_use]
776 pub fn bus_active_power_marginals(&self) -> Option<&[f64]> {
777 self.bus_active_power_marginal.as_deref()
778 }
779
780 #[must_use]
782 pub fn branch_from_limit_multipliers(&self) -> Option<&[f64]> {
783 self.branch_from_limit_multiplier.as_deref()
784 }
785
786 #[must_use]
788 pub fn branch_to_limit_multipliers(&self) -> Option<&[f64]> {
789 self.branch_to_limit_multiplier.as_deref()
790 }
791
792 #[must_use]
794 pub fn bus_voltage_angle(&self, bus: BusId) -> Option<f64> {
795 Some(self.bus_voltage_angle[bus_position(self.row_index(), bus)?])
796 }
797
798 #[must_use]
800 pub fn bus_active_injection(&self, bus: BusId) -> Option<f64> {
801 Some(self.bus_active_injection[bus_position(self.row_index(), bus)?])
802 }
803
804 #[must_use]
806 pub fn branch_from_active_flow(&self, identity: &str) -> Option<f64> {
807 Some(self.branch_from_active_flow[branch_position(self.row_index(), identity)?])
808 }
809
810 #[must_use]
812 pub fn branch_to_active_flow(&self, identity: &str) -> Option<f64> {
813 Some(self.branch_to_active_flow[branch_position(self.row_index(), identity)?])
814 }
815
816 #[must_use]
819 pub fn three_winding_transformer_terminal_active_powers(
820 &self,
821 ) -> &[ThreeWindingTransformerTerminalActivePower] {
822 &self.three_winding_transformer_terminal_active_power
823 }
824}
825
826#[derive(Clone, Debug)]
829pub struct AcOpfSolution {
830 instance: Arc<AcOpfInstance>,
831 termination: Termination,
832 residuals: Residuals,
833 producer: Producer,
834 bus_voltage_magnitude: Vec<f64>,
835 bus_voltage_angle: Vec<f64>,
836 bus_active_injection: Vec<f64>,
837 bus_reactive_injection: Vec<f64>,
838 branch_from_active_flow: Vec<f64>,
839 branch_from_reactive_flow: Vec<f64>,
840 branch_to_active_flow: Vec<f64>,
841 branch_to_reactive_flow: Vec<f64>,
842 generator_active_power: Vec<f64>,
843 generator_reactive_power: Vec<f64>,
844 three_winding_transformer_terminal_power: Vec<ThreeWindingTransformerTerminalPower>,
845 objective: f64,
846 bus_active_power_marginal: Option<Vec<f64>>,
847 bus_reactive_power_marginal: Option<Vec<f64>>,
848 branch_from_limit_multiplier: Option<Vec<f64>>,
849 branch_to_limit_multiplier: Option<Vec<f64>>,
850 index: SolutionIndex,
851}
852
853impl AcOpfSolution {
854 #[allow(clippy::too_many_arguments)] pub fn new(
862 instance: Arc<AcOpfInstance>,
863 termination: Termination,
864 bus_voltage_magnitude: Vec<f64>,
865 bus_voltage_angle: Vec<f64>,
866 bus_active_injection: Vec<f64>,
867 bus_reactive_injection: Vec<f64>,
868 branch_from_active_flow: Vec<f64>,
869 branch_from_reactive_flow: Vec<f64>,
870 branch_to_active_flow: Vec<f64>,
871 branch_to_reactive_flow: Vec<f64>,
872 generator_active_power: Vec<f64>,
873 generator_reactive_power: Vec<f64>,
874 objective: f64,
875 three_winding_transformer_terminal_power: Vec<ThreeWindingTransformerTerminalPower>,
876 ) -> Result<Self, Error> {
877 let buses = instance.network().buses().len();
878 let branches = instance.network().branches().len();
879 let generators = instance.network().generators().len();
880 check_length("bus voltage magnitudes", bus_voltage_magnitude.len(), buses)?;
881 check_length("bus voltage angles", bus_voltage_angle.len(), buses)?;
882 check_length("bus active injections", bus_active_injection.len(), buses)?;
883 check_length(
884 "bus reactive injections",
885 bus_reactive_injection.len(),
886 buses,
887 )?;
888 check_length(
889 "branch from-side active flows",
890 branch_from_active_flow.len(),
891 branches,
892 )?;
893 check_length(
894 "branch from-side reactive flows",
895 branch_from_reactive_flow.len(),
896 branches,
897 )?;
898 check_length(
899 "branch to-side active flows",
900 branch_to_active_flow.len(),
901 branches,
902 )?;
903 check_length(
904 "branch to-side reactive flows",
905 branch_to_reactive_flow.len(),
906 branches,
907 )?;
908 check_length(
909 "generator active dispatch",
910 generator_active_power.len(),
911 generators,
912 )?;
913 check_length(
914 "generator reactive dispatch",
915 generator_reactive_power.len(),
916 generators,
917 )?;
918 check_length(
919 "three winding transformer terminal powers",
920 three_winding_transformer_terminal_power.len(),
921 instance.network().transformers_3w().len(),
922 )?;
923 let index = solution_index(&instance)?;
924 Ok(Self {
925 instance,
926 termination,
927 residuals: Residuals::default(),
928 producer: None,
929 bus_voltage_magnitude,
930 bus_voltage_angle,
931 bus_active_injection,
932 bus_reactive_injection,
933 branch_from_active_flow,
934 branch_from_reactive_flow,
935 branch_to_active_flow,
936 branch_to_reactive_flow,
937 generator_active_power,
938 generator_reactive_power,
939 three_winding_transformer_terminal_power,
940 objective,
941 bus_active_power_marginal: None,
942 bus_reactive_power_marginal: None,
943 branch_from_limit_multiplier: None,
944 branch_to_limit_multiplier: None,
945 index,
946 })
947 }
948
949 shared_solution_accessors!(AcOpfInstance);
950
951 pub fn with_bus_active_power_marginals(mut self, marginals: Vec<f64>) -> Result<Self, Error> {
957 check_length(
958 "bus active power marginals",
959 marginals.len(),
960 self.instance.network().buses().len(),
961 )?;
962 self.bus_active_power_marginal = Some(marginals);
963 Ok(self)
964 }
965
966 pub fn with_bus_reactive_power_marginals(mut self, marginals: Vec<f64>) -> Result<Self, Error> {
972 check_length(
973 "bus reactive power marginals",
974 marginals.len(),
975 self.instance.network().buses().len(),
976 )?;
977 self.bus_reactive_power_marginal = Some(marginals);
978 Ok(self)
979 }
980
981 pub fn with_branch_thermal_limit_multipliers(
986 mut self,
987 from: Vec<f64>,
988 to: Vec<f64>,
989 ) -> Result<Self, Error> {
990 check_length(
991 "branch from-terminal thermal limit multipliers",
992 from.len(),
993 self.instance.network().branches().len(),
994 )?;
995 check_length(
996 "branch to-terminal thermal limit multipliers",
997 to.len(),
998 self.instance.network().branches().len(),
999 )?;
1000 check_nonnegative_multipliers("branch from-terminal thermal limit multipliers", &from)?;
1001 check_nonnegative_multipliers("branch to-terminal thermal limit multipliers", &to)?;
1002 self.branch_from_limit_multiplier = Some(from);
1003 self.branch_to_limit_multiplier = Some(to);
1004 Ok(self)
1005 }
1006
1007 #[must_use]
1009 pub const fn objective(&self) -> f64 {
1010 self.objective
1011 }
1012
1013 #[must_use]
1015 pub fn bus_active_power_marginal(&self, bus: BusId) -> Option<f64> {
1016 Some(self.bus_active_power_marginal.as_ref()?[bus_position(self.row_index(), bus)?])
1017 }
1018
1019 #[must_use]
1021 pub fn bus_reactive_power_marginal(&self, bus: BusId) -> Option<f64> {
1022 Some(self.bus_reactive_power_marginal.as_ref()?[bus_position(self.row_index(), bus)?])
1023 }
1024
1025 #[must_use]
1027 pub fn branch_from_limit_multiplier(&self, identity: &str) -> Option<f64> {
1028 Some(
1029 self.branch_from_limit_multiplier.as_ref()?
1030 [branch_position(self.row_index(), identity)?],
1031 )
1032 }
1033
1034 #[must_use]
1036 pub fn branch_to_limit_multiplier(&self, identity: &str) -> Option<f64> {
1037 Some(
1038 self.branch_to_limit_multiplier.as_ref()?[branch_position(self.row_index(), identity)?],
1039 )
1040 }
1041
1042 #[must_use]
1044 pub fn bus_active_power_marginals(&self) -> Option<&[f64]> {
1045 self.bus_active_power_marginal.as_deref()
1046 }
1047
1048 #[must_use]
1050 pub fn bus_reactive_power_marginals(&self) -> Option<&[f64]> {
1051 self.bus_reactive_power_marginal.as_deref()
1052 }
1053
1054 #[must_use]
1056 pub fn branch_from_limit_multipliers(&self) -> Option<&[f64]> {
1057 self.branch_from_limit_multiplier.as_deref()
1058 }
1059
1060 #[must_use]
1062 pub fn branch_to_limit_multipliers(&self) -> Option<&[f64]> {
1063 self.branch_to_limit_multiplier.as_deref()
1064 }
1065
1066 #[must_use]
1068 pub fn generator_active_power(&self, identity: &str) -> Option<f64> {
1069 Some(self.generator_active_power[generator_position(self.row_index(), identity)?])
1070 }
1071
1072 #[must_use]
1074 pub fn generator_reactive_power(&self, identity: &str) -> Option<f64> {
1075 Some(self.generator_reactive_power[generator_position(self.row_index(), identity)?])
1076 }
1077
1078 #[must_use]
1080 pub fn bus_voltage_magnitude(&self, bus: BusId) -> Option<f64> {
1081 Some(self.bus_voltage_magnitude[bus_position(self.row_index(), bus)?])
1082 }
1083
1084 #[must_use]
1086 pub fn bus_voltage_angle(&self, bus: BusId) -> Option<f64> {
1087 Some(self.bus_voltage_angle[bus_position(self.row_index(), bus)?])
1088 }
1089
1090 #[must_use]
1092 pub fn bus_active_injection(&self, bus: BusId) -> Option<f64> {
1093 Some(self.bus_active_injection[bus_position(self.row_index(), bus)?])
1094 }
1095
1096 #[must_use]
1098 pub fn bus_reactive_injection(&self, bus: BusId) -> Option<f64> {
1099 Some(self.bus_reactive_injection[bus_position(self.row_index(), bus)?])
1100 }
1101
1102 #[must_use]
1104 pub fn branch_from_active_flow(&self, identity: &str) -> Option<f64> {
1105 Some(self.branch_from_active_flow[branch_position(self.row_index(), identity)?])
1106 }
1107
1108 #[must_use]
1110 pub fn branch_from_reactive_flow(&self, identity: &str) -> Option<f64> {
1111 Some(self.branch_from_reactive_flow[branch_position(self.row_index(), identity)?])
1112 }
1113
1114 #[must_use]
1116 pub fn branch_to_active_flow(&self, identity: &str) -> Option<f64> {
1117 Some(self.branch_to_active_flow[branch_position(self.row_index(), identity)?])
1118 }
1119
1120 #[must_use]
1122 pub fn branch_to_reactive_flow(&self, identity: &str) -> Option<f64> {
1123 Some(self.branch_to_reactive_flow[branch_position(self.row_index(), identity)?])
1124 }
1125
1126 #[must_use]
1128 pub fn three_winding_transformer_terminal_powers(
1129 &self,
1130 ) -> &[ThreeWindingTransformerTerminalPower] {
1131 &self.three_winding_transformer_terminal_power
1132 }
1133
1134 #[must_use]
1136 pub fn bus_voltage_magnitudes(&self) -> &[f64] {
1137 &self.bus_voltage_magnitude
1138 }
1139 #[must_use]
1141 pub fn bus_voltage_angles(&self) -> &[f64] {
1142 &self.bus_voltage_angle
1143 }
1144 #[must_use]
1146 pub fn bus_active_injections(&self) -> &[f64] {
1147 &self.bus_active_injection
1148 }
1149 #[must_use]
1151 pub fn bus_reactive_injections(&self) -> &[f64] {
1152 &self.bus_reactive_injection
1153 }
1154 #[must_use]
1156 pub fn branch_from_active_flows(&self) -> &[f64] {
1157 &self.branch_from_active_flow
1158 }
1159 #[must_use]
1161 pub fn branch_from_reactive_flows(&self) -> &[f64] {
1162 &self.branch_from_reactive_flow
1163 }
1164 #[must_use]
1166 pub fn branch_to_active_flows(&self) -> &[f64] {
1167 &self.branch_to_active_flow
1168 }
1169 #[must_use]
1171 pub fn branch_to_reactive_flows(&self) -> &[f64] {
1172 &self.branch_to_reactive_flow
1173 }
1174 #[must_use]
1176 pub fn generator_active_powers(&self) -> &[f64] {
1177 &self.generator_active_power
1178 }
1179 #[must_use]
1181 pub fn generator_reactive_powers(&self) -> &[f64] {
1182 &self.generator_reactive_power
1183 }
1184}