powerio_matrix/matrix/
incidence.rs1use sprs::CsMat;
10
11use crate::indexed::IndexedNetwork;
12use crate::matrix::triplet::CooBuilder;
13use crate::{Error, Result};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
17#[non_exhaustive]
18pub enum DcConvention {
19 #[default]
22 PaperPure,
23 Matpower,
26}
27
28#[derive(Debug, Clone)]
30#[non_exhaustive]
31pub struct IncidenceParts {
32 pub a: CsMat<f64>,
34 pub b: Vec<f64>,
36 pub p_shift: Vec<f64>,
39 pub branch_of_col: Vec<usize>,
41}
42
43impl IncidenceParts {
44 #[inline]
45 pub fn n(&self) -> usize {
46 self.a.rows()
47 }
48
49 #[inline]
50 pub fn m(&self) -> usize {
51 self.a.cols()
52 }
53}
54
55pub fn build_incidence(case: &IndexedNetwork, conv: DcConvention) -> Result<IncidenceParts> {
60 let n = case.n();
61
62 let mut cols: Vec<Column> = Vec::new();
64 for (idx, br) in case.in_service_branches() {
65 let i = case.bus_index(br.from).ok_or(Error::UnknownBus {
66 bus_id: br.from,
67 element_index: idx,
68 })?;
69 let j = case.bus_index(br.to).ok_or(Error::UnknownBus {
70 bus_id: br.to,
71 element_index: idx,
72 })?;
73 if i == j || br.x == 0.0 {
74 continue;
75 }
76 let b_e = match conv {
77 DcConvention::PaperPure => 1.0 / br.x,
78 DcConvention::Matpower => 1.0 / (br.x * br.effective_tap()),
79 };
80 if !b_e.is_finite() {
83 return Err(Error::NonFiniteSusceptance { row: idx });
84 }
85 let shift_rad = match conv {
86 DcConvention::PaperPure => 0.0,
87 DcConvention::Matpower => case.angle_radians(br.shift),
90 };
91 cols.push(Column {
92 i,
93 j,
94 b_e,
95 shift_rad,
96 branch: idx,
97 });
98 }
99
100 let m = cols.len();
102 let mut a = CooBuilder::with_capacity_rect(n, m, 2 * m);
103 let mut b = Vec::with_capacity(m);
104 let mut p_shift = vec![0.0; n];
105 let mut branch_of_col = Vec::with_capacity(m);
106 for (k, col) in cols.iter().enumerate() {
107 a.add(col.i, k, 1.0);
108 a.add(col.j, k, -1.0);
109 b.push(col.b_e);
110 branch_of_col.push(col.branch);
111 if col.shift_rad != 0.0 {
112 p_shift[col.i] -= col.b_e * col.shift_rad;
115 p_shift[col.j] += col.b_e * col.shift_rad;
116 }
117 }
118
119 Ok(IncidenceParts {
120 a: a.finish_csr(),
121 b,
122 p_shift,
123 branch_of_col,
124 })
125}
126
127struct Column {
128 i: usize,
129 j: usize,
130 b_e: f64,
131 shift_rad: f64,
132 branch: usize,
133}
134
135pub fn diagonal(values: &[f64]) -> CsMat<f64> {
137 let n = values.len();
138 let mut d = CooBuilder::with_capacity(n, n);
139 for (k, &v) in values.iter().enumerate() {
140 d.add(k, k, v);
141 }
142 d.finish_csr()
143}
144
145pub fn susceptance_diag(b: &[f64]) -> CsMat<f64> {
147 diagonal(b)
148}
149
150pub fn build_flow_map(a: &CsMat<f64>, b: &[f64]) -> CsMat<f64> {
152 let d = susceptance_diag(b);
153 let at = a.transpose_view().to_csr();
154 &d * &at
155}