use crate::types::{Bus, BusType};
#[derive(Clone, Debug, PartialEq)]
pub struct BaseSpec {
pub p: Vec<f64>,
pub q: Vec<f64>,
}
impl BaseSpec {
pub fn capture(buses: &[Bus]) -> Self {
Self {
p: buses.iter().map(|b| b.p_spec).collect(),
q: buses.iter().map(|b| b.q_spec).collect(),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LoadingDirection {
pub d_p: Vec<f64>,
pub d_q: Vec<f64>,
}
impl LoadingDirection {
pub fn scale_loads(buses: &[Bus]) -> Self {
let consumer = |b: &Bus| b.bus_type != BusType::Slack && b.p_spec < 0.0;
Self {
d_p: buses
.iter()
.map(|b| if consumer(b) { b.p_spec } else { 0.0 })
.collect(),
d_q: buses
.iter()
.map(|b| if consumer(b) { b.q_spec } else { 0.0 })
.collect(),
}
}
pub fn scale_buses(buses: &[Bus], subset: &[usize]) -> Self {
let mut d = Self::zero(buses.len());
for &i in subset {
if i >= buses.len() || buses[i].bus_type == BusType::Slack || buses[i].p_spec >= 0.0 {
continue;
}
d.d_p[i] = buses[i].p_spec;
d.d_q[i] = buses[i].q_spec;
}
d
}
pub fn scale_loads_with_pickup(buses: &[Bus], weights: &[f64]) -> Self {
let mut d = Self::scale_loads(buses);
let increase: f64 = d.d_p.iter().sum(); let eligible = |i: usize| i < buses.len() && buses[i].bus_type != BusType::Slack;
let total: f64 = weights
.iter()
.enumerate()
.filter(|&(i, w)| eligible(i) && *w > 0.0)
.map(|(_, w)| *w)
.sum();
if total <= 0.0 {
return d;
}
for (i, w) in weights.iter().enumerate() {
if eligible(i) && *w > 0.0 {
d.d_p[i] -= increase * (w / total);
}
}
d
}
pub fn explicit(d_p: Vec<f64>, d_q: Vec<f64>) -> Self {
Self { d_p, d_q }
}
pub fn to_target(buses: &[Bus], p_target: &[f64], q_target: &[f64]) -> Self {
Self {
d_p: buses
.iter()
.map(|b| p_target.get(b.idx).copied().unwrap_or(b.p_spec) - b.p_spec)
.collect(),
d_q: buses
.iter()
.map(|b| q_target.get(b.idx).copied().unwrap_or(b.q_spec) - b.q_spec)
.collect(),
}
}
pub fn zero(n: usize) -> Self {
Self {
d_p: vec![0.0; n],
d_q: vec![0.0; n],
}
}
pub fn total_active_load_increase(&self) -> f64 {
self.d_p.iter().filter(|&&dp| dp < 0.0).map(|dp| -dp).sum()
}
pub fn is_empty(&self) -> bool {
self.d_p.iter().chain(&self.d_q).all(|v| *v == 0.0)
}
pub(crate) fn freeze_reactive(&mut self, bus: usize, base: &mut BaseSpec, clamped_q: f64) {
self.d_q[bus] = 0.0;
base.q[bus] = clamped_q;
}
pub(crate) fn apply(&self, base: &BaseSpec, lambda: f64, buses: &mut [Bus]) {
for b in buses.iter_mut() {
b.p_spec = base.p[b.idx] + lambda * self.d_p[b.idx];
b.q_spec = base.q[b.idx] + lambda * self.d_q[b.idx];
}
}
}