use crate::types::Bus;
use super::jacobian::{Row, StateLayout};
use super::SeNetwork;
pub struct Constraints {
pub buses: Vec<usize>,
}
impl Constraints {
pub fn new(net: &SeNetwork) -> Self {
Self::from_flags(&net.constrained_buses())
}
pub fn from_flags(zero_injection: &[bool]) -> Self {
let buses = zero_injection
.iter()
.enumerate()
.filter(|&(_, &z)| z)
.map(|(i, _)| i)
.collect();
Self { buses }
}
pub fn len(&self) -> usize {
2 * self.buses.len()
}
pub fn is_empty(&self) -> bool {
self.buses.is_empty()
}
pub fn evaluate(
&self,
buses: &[Bus],
net: &SeNetwork,
layout: &StateLayout,
) -> (Vec<f64>, Vec<Row>) {
let (p_inj, q_inj) = crate::network::power_injections(buses, &net.ybus);
let v: Vec<num_complex::Complex<f64>> = buses
.iter()
.map(|b| num_complex::Complex::from_polar(b.voltage_mag, b.voltage_ang))
.collect();
let mut values = Vec::with_capacity(self.len());
let mut rows = Vec::with_capacity(self.len());
let mut scratch = Vec::new();
for &bus in &self.buses {
for active in [true, false] {
values.push(if active { p_inj[bus] } else { q_inj[bus] });
rows.push(super::jacobian::injection_row(
layout, net, &v, bus, active, &mut scratch,
));
}
}
(values, rows)
}
}
pub fn augment(
mut triplets: Vec<(usize, usize, f64)>,
mut rhs: Vec<f64>,
n: usize,
constraint_values: &[f64],
constraint_rows: &[Row],
) -> (Vec<(usize, usize, f64)>, Vec<f64>) {
rhs.resize(n + constraint_rows.len(), 0.0);
for (k, (row, &c)) in constraint_rows.iter().zip(constraint_values).enumerate() {
let lambda = n + k;
for &(col, value) in row {
triplets.push((lambda, col, value));
triplets.push((col, lambda, value));
}
rhs[lambda] = -c;
}
(triplets, rhs)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_flags_means_no_constraints() {
let c = Constraints::from_flags(&[false, false]);
assert!(c.is_empty());
assert_eq!(c.len(), 0);
}
#[test]
fn each_bus_contributes_two_rows() {
let c = Constraints::from_flags(&[true, false, true]);
assert_eq!(c.buses, vec![0, 2]);
assert_eq!(c.len(), 4);
}
#[test]
fn augmented_system_is_symmetric() {
let n = 3;
let g = vec![(0, 0, 2.0), (1, 1, 3.0), (2, 2, 4.0)];
let rows = vec![vec![(0, 1.0), (2, -0.5)]];
let (triplets, rhs) = augment(g, vec![1.0, 2.0, 3.0], n, &[0.25], &rows);
let size = n + 1;
let mut dense = vec![vec![0.0; size]; size];
for (i, j, v) in triplets {
dense[i][j] += v;
}
for i in 0..size {
for j in 0..size {
assert!(
(dense[i][j] - dense[j][i]).abs() < 1e-12,
"asymmetric at ({i},{j})"
);
}
}
assert_eq!(dense[n][n], 0.0, "the multiplier block must stay zero");
assert_eq!(rhs[n], -0.25, "the constraint row drives the violation to zero");
}
}