use sprs::CsMat;
pub use powerio::DcConvention;
use crate::indexed::IndexedNetwork;
use crate::matrix::triplet::CooBuilder;
use crate::{Error, Result};
use super::{BuildOptions, ZeroImpedanceSkips};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct IncidenceParts {
pub a: CsMat<f64>,
pub b: Vec<f64>,
pub p_shift: Vec<f64>,
pub branch_of_col: Vec<usize>,
pub skipped_zero_impedance: ZeroImpedanceSkips,
}
impl IncidenceParts {
#[inline]
pub fn n(&self) -> usize {
self.a.rows()
}
#[inline]
pub fn m(&self) -> usize {
self.a.cols()
}
}
pub fn build_incidence(
case: &IndexedNetwork,
conv: DcConvention,
opts: &BuildOptions,
) -> Result<IncidenceParts> {
let n = case.n();
let mut cols: Vec<Column> = Vec::new();
let mut skipped_zero_impedance = Vec::new();
for (idx, br) in case.in_service_branches() {
let i = case.bus_index(br.from).ok_or(Error::UnknownBus {
bus_id: br.from,
element_index: idx,
})?;
let j = case.bus_index(br.to).ok_or(Error::UnknownBus {
bus_id: br.to,
element_index: idx,
})?;
if i == j || br.x == 0.0 {
if i != j && br.x == 0.0 {
if !opts.skip_zero_impedance {
return Err(Error::ZeroImpedance { row: idx });
}
skipped_zero_impedance.push(idx);
}
continue;
}
let b_e = conv.branch_susceptance(br.x, br.effective_tap());
if !b_e.is_finite() {
return Err(Error::NonFiniteSusceptance { row: idx });
}
let shift_rad = if conv.includes_phase_shifts() {
case.angle_radians(br.shift)
} else {
0.0
};
cols.push(Column {
i,
j,
b_e,
shift_rad,
branch: idx,
});
}
let m = cols.len();
let mut a = CooBuilder::with_capacity_rect(n, m, 2 * m);
let mut b = Vec::with_capacity(m);
let mut p_shift = vec![0.0; n];
let mut branch_of_col = Vec::with_capacity(m);
for (k, col) in cols.iter().enumerate() {
a.add(col.i, k, 1.0);
a.add(col.j, k, -1.0);
b.push(col.b_e);
branch_of_col.push(col.branch);
if col.shift_rad != 0.0 {
p_shift[col.i] -= col.b_e * col.shift_rad;
p_shift[col.j] += col.b_e * col.shift_rad;
}
}
Ok(IncidenceParts {
a: a.finish_csr(),
b,
p_shift,
branch_of_col,
skipped_zero_impedance: ZeroImpedanceSkips::new(skipped_zero_impedance),
})
}
struct Column {
i: usize,
j: usize,
b_e: f64,
shift_rad: f64,
branch: usize,
}
pub fn diagonal(values: &[f64]) -> CsMat<f64> {
let n = values.len();
let mut d = CooBuilder::with_capacity(n, n);
for (k, &v) in values.iter().enumerate() {
d.add(k, k, v);
}
d.finish_csr()
}
pub fn susceptance_diag(b: &[f64]) -> CsMat<f64> {
diagonal(b)
}
pub fn build_flow_map(a: &CsMat<f64>, b: &[f64]) -> CsMat<f64> {
let d = susceptance_diag(b);
let at = a.transpose_view().to_csr();
&d * &at
}