powerio_matrix/dcopf/
mod.rs1mod bundle;
5pub(crate) mod limits;
6pub(crate) mod nodal;
7mod prep;
8#[cfg(test)]
9mod tests;
10
11use crate::matrix::calc_solver_branch_flow_matrix;
12use crate::matrix::triplet::CooBuilder;
13use crate::{
14 IndexedNetwork, SparseMatrix, calc_diagonal, calc_reference_indicator, calc_weighted_laplacian,
15 ground_at_each,
16};
17
18use crate::Result;
19use powerio_prob::DcOpfInstance;
20use prep::{DcOpfOptions, apply_instance_semantics, preparation_from_view};
21
22pub use bundle::{DcOpfBundleMetadata, DcOpfBundleOptions, DcOpfOutputs, emit_dcopf_bundle};
23pub use prep::{
24 DcBranchParameters, DcGeneratorParameters, DcOpfPreparation, NodalGeneratorParameters, Units,
25};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[non_exhaustive]
31pub struct DcOpfAssemblyOptions {
32 pub units: Units,
34 pub skip_zero_impedance: bool,
39 pub synthesize_unrated_limits: bool,
43 pub correct_angle_difference_bounds: bool,
46}
47
48impl Default for DcOpfAssemblyOptions {
49 fn default() -> Self {
50 Self {
51 units: Units::default(),
52 skip_zero_impedance: false,
53 synthesize_unrated_limits: false,
54 correct_angle_difference_bounds: true,
55 }
56 }
57}
58
59impl DcOpfAssemblyOptions {
60 #[must_use]
61 pub const fn with_units(mut self, units: Units) -> Self {
62 self.units = units;
63 self
64 }
65
66 #[must_use]
67 pub const fn with_skip_zero_impedance(mut self, skip: bool) -> Self {
68 self.skip_zero_impedance = skip;
69 self
70 }
71
72 #[must_use]
73 pub const fn with_synthesize_unrated_limits(mut self, synthesize: bool) -> Self {
74 self.synthesize_unrated_limits = synthesize;
75 self
76 }
77
78 #[must_use]
79 pub const fn with_correct_angle_difference_bounds(mut self, correct: bool) -> Self {
80 self.correct_angle_difference_bounds = correct;
81 self
82 }
83}
84
85#[derive(Debug, Clone)]
87#[non_exhaustive]
88pub struct DcOpfMatrices {
89 pub bus_branch_incidence: SparseMatrix,
92 pub laplacian: SparseMatrix,
93 pub grounded_laplacian: SparseMatrix,
94 pub branch_flow_matrix: SparseMatrix,
97 pub generator_bus: SparseMatrix,
98 pub generator_cost: SparseMatrix,
100 pub reference_selector: Vec<f64>,
101}
102
103pub fn calc_dc_opf_matrices(
111 instance: &DcOpfInstance,
112 options: &DcOpfAssemblyOptions,
113) -> Result<DcOpfMatrices> {
114 Ok(matrices_from_preparation(&build_dc_opf_preparation(
115 instance, options,
116 )?))
117}
118
119pub fn build_dc_opf_preparation(
131 instance: &DcOpfInstance,
132 options: &DcOpfAssemblyOptions,
133) -> Result<DcOpfPreparation> {
134 let view = IndexedNetwork::new(instance.network());
135 let objective = crate::opf::compile_objective(instance.objective())?;
136 let mut preparation = preparation_from_view(
137 &view,
138 DcOpfOptions {
139 formula: instance.branch_susceptance_formula(),
140 units: options.units,
141 skip_zero_impedance: options.skip_zero_impedance,
142 synthesize_unrated_limits: options.synthesize_unrated_limits,
143 correct_angle_difference_bounds: options.correct_angle_difference_bounds,
144 objective,
145 },
146 )?;
147 apply_instance_semantics(&mut preparation, instance.network(), instance.constraints())?;
148 Ok(preparation)
149}
150
151pub(crate) fn matrices_from_preparation(instance: &DcOpfPreparation) -> DcOpfMatrices {
152 let n = instance.n_buses;
153 let m = instance.n_branches();
154 let mut incidence = CooBuilder::with_capacity_rect(n, m, 2 * m);
155 for column in 0..m {
156 incidence.add(instance.branches.from_bus[column], column, 1.0);
157 incidence.add(instance.branches.to_bus[column], column, -1.0);
158 }
159 let incidence = incidence.finish_csr();
160 let laplacian = calc_weighted_laplacian(&incidence, &instance.branches.susceptance_magnitude);
161 let grounded_laplacian = ground_at_each(&laplacian, instance.reference_buses.as_ref());
162 let branch_flow_matrix =
163 calc_solver_branch_flow_matrix(&incidence, &instance.branches.susceptance_magnitude);
164
165 let n_gen = instance.n_generators();
166 let mut generator_bus = CooBuilder::with_capacity_rect(n, n_gen, n_gen);
167 for (column, &bus) in instance.generators.bus_of_gen.iter().enumerate() {
168 generator_bus.add(bus, column, 1.0);
169 }
170
171 DcOpfMatrices {
172 bus_branch_incidence: incidence,
173 laplacian,
174 grounded_laplacian,
175 branch_flow_matrix,
176 generator_bus: generator_bus.finish_csr(),
177 generator_cost: calc_diagonal(&instance.generators.q),
178 reference_selector: calc_reference_indicator(n, instance.reference_buses.as_ref()),
179 }
180}