use crate::jacobian::JacobianPattern;
use crate::network::YBusSparse;
use crate::types::{Bus, BusType};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum Parametrization {
Natural,
#[default]
Local,
PseudoArcLength,
}
pub struct Layout {
pub non_slack_idx: Vec<usize>,
pub pq_idx: Vec<usize>,
pub n_angle: usize,
pub n_unknowns: usize,
pub n_buses: usize,
}
impl Layout {
pub fn analyze(buses: &[Bus]) -> Self {
let non_slack_idx: Vec<usize> = buses
.iter()
.filter(|b| b.bus_type != BusType::Slack)
.map(|b| b.idx)
.collect();
let pq_idx: Vec<usize> = buses
.iter()
.filter(|b| b.bus_type == BusType::PQ)
.map(|b| b.idx)
.collect();
let n_angle = non_slack_idx.len();
let n_unknowns = n_angle + pq_idx.len();
Self {
non_slack_idx,
pq_idx,
n_angle,
n_unknowns,
n_buses: buses.len(),
}
}
pub fn embed(&self, from: &Layout, t: &[f64]) -> Vec<f64> {
let mut ang_of = vec![usize::MAX; self.n_buses];
for (row, &i) in from.non_slack_idx.iter().enumerate() {
ang_of[i] = row;
}
let mut vm_of = vec![usize::MAX; self.n_buses];
for (row, &i) in from.pq_idx.iter().enumerate() {
vm_of[i] = from.n_angle + row;
}
let mut out = vec![0.0; self.n_unknowns + 1];
for (row, &i) in self.non_slack_idx.iter().enumerate() {
if ang_of[i] != usize::MAX {
out[row] = t[ang_of[i]];
}
}
for (row, &i) in self.pq_idx.iter().enumerate() {
if vm_of[i] != usize::MAX {
out[self.n_angle + row] = t[vm_of[i]];
}
}
out[self.n_unknowns] = t[from.n_unknowns];
out
}
pub fn get(&self, buses: &[Bus], lambda: f64, k: usize) -> f64 {
if k < self.n_angle {
buses[self.non_slack_idx[k]].voltage_ang
} else if k < self.n_unknowns {
buses[self.pq_idx[k - self.n_angle]].voltage_mag
} else {
lambda
}
}
pub fn add(&self, buses: &mut [Bus], k: usize, delta: f64) {
if k < self.n_angle {
buses[self.non_slack_idx[k]].voltage_ang += delta;
} else if k < self.n_unknowns {
buses[self.pq_idx[k - self.n_angle]].voltage_mag += delta;
}
}
pub fn direction_rows(&self, d_p: &[f64], d_q: &[f64]) -> Vec<f64> {
let mut ds = vec![0.0; self.n_unknowns];
for (row, &i) in self.non_slack_idx.iter().enumerate() {
ds[row] = d_p[i];
}
for (row, &i) in self.pq_idx.iter().enumerate() {
ds[self.n_angle + row] = d_q[i];
}
ds
}
}
pub fn border_columns(param: Parametrization, k: usize, n: usize) -> Vec<usize> {
match param {
Parametrization::Natural => vec![n],
Parametrization::Local => vec![k],
Parametrization::PseudoArcLength => (0..=n).collect(),
}
}
pub struct AugmentedPattern {
jac: JacobianPattern,
n: usize,
border_cols: Vec<usize>,
}
impl AugmentedPattern {
pub fn analyze(buses: &[Bus], ybus: &YBusSparse, border_cols: Vec<usize>) -> Self {
let jac = JacobianPattern::analyze(buses, ybus);
let n = jac.n_unknowns;
Self {
jac,
n,
border_cols,
}
}
pub fn border_cols(&self) -> &[usize] {
&self.border_cols
}
pub fn dim(&self) -> usize {
self.n + 1
}
pub fn len(&self) -> usize {
self.jac.len() + self.n + self.border_cols.len()
}
pub fn is_empty(&self) -> bool {
self.n == 0
}
pub fn to_triplets(&self, values: &[f64]) -> Vec<(usize, usize, f64)> {
let mut out = Vec::with_capacity(self.len());
for (k, ((&r, &c), &v)) in self
.jac
.rows()
.iter()
.zip(self.jac.cols())
.zip(values)
.enumerate()
{
let _ = k;
out.push((r as usize, c as usize, v));
}
let mut at = self.jac.len();
for r in 0..self.n {
out.push((r, self.n, values[at]));
at += 1;
}
for &c in &self.border_cols {
out.push((self.n, c, values[at]));
at += 1;
}
out
}
pub fn fill(
&self,
buses: &[Bus],
p_calc: &[f64],
q_calc: &[f64],
ds: &[f64],
border: &[f64],
values: &mut Vec<f64>,
) {
values.clear();
self.jac.fill_into(buses, p_calc, q_calc, values);
values.extend(ds.iter().map(|d| -d));
values.extend_from_slice(border);
debug_assert_eq!(values.len(), self.len());
}
}
pub fn dominant_index(tangent: &[f64]) -> usize {
let mut best = 0;
let mut best_abs = 0.0;
for (i, t) in tangent.iter().enumerate() {
if t.abs() > best_abs {
best_abs = t.abs();
best = i;
}
}
best
}