use caseformat::{Bus, Gen};
use num_complex::Complex64;
use sparsetools::csr::{CCSR, CSR};
pub trait SBus {
fn s_bus(&self, v_m: &[f64]) -> Vec<Complex64>;
fn d_sbus_d_vm(&self, v_m: &[f64]) -> CSR<usize, Complex64>;
}
pub struct MakeSBus<'a> {
pub base_mva: f64,
pub bus: &'a [Bus],
pub gen: &'a [Gen],
pub pw: Option<[f64; 3]>,
pub qw: Option<[f64; 3]>,
}
impl<'a> SBus for MakeSBus<'a> {
fn s_bus(&self, v_m: &[f64]) -> Vec<Complex64> {
make_sbus(
self.base_mva,
self.bus,
self.gen,
self.pw,
self.qw,
Some(v_m),
None,
)
}
fn d_sbus_d_vm(&self, v_m: &[f64]) -> CSR<usize, Complex64> {
make_d_sbus_d_vm(
self.base_mva,
self.bus,
self.gen,
self.pw,
self.qw,
Some(v_m),
None,
)
}
}
pub fn make_sbus(
base_mva: f64,
bus: &[Bus],
gen: &[Gen],
pw: Option<[f64; 3]>,
qw: Option<[f64; 3]>,
vm: Option<&[f64]>,
sg: Option<&[Complex64]>,
) -> Vec<Complex64> {
let nb = bus.len();
let base_mva = Complex64::new(base_mva, 0.0);
let mut s_bus = vec![Complex64::default(); nb];
if let Some(sg) = sg {
gen.iter()
.zip(sg)
.filter(|(g, _)| g.is_on())
.for_each(|(g, s_pu)| {
s_bus[g.gen_bus] += s_pu;
});
} else {
gen.iter().filter(|g| g.is_on()).for_each(|g| {
s_bus[g.gen_bus] += Complex64::new(g.pg, g.qg) / base_mva;
});
}
let pw = pw.unwrap_or([1.0, 0.0, 0.0]);
let qw = qw.unwrap_or(pw);
bus.iter()
.filter(|b| b.pd != 0.0 || b.qd != 0.0)
.for_each(|b| {
let sd_z = Complex64::new(b.pd * pw[2], b.qd * qw[2]) / base_mva;
let sd_i = Complex64::new(b.pd * pw[1], b.qd * qw[1]) / base_mva;
let sd_p = Complex64::new(b.pd * pw[0], b.qd * qw[0]) / base_mva;
let vm_i = match vm {
Some(vm) => Complex64::new(vm[b.bus_i], 0.0),
None => Complex64::new(1.0, 0.0),
};
let sd = sd_p + (sd_i * vm_i) + (sd_z * (vm_i * vm_i));
s_bus[b.bus_i] -= sd;
});
s_bus
}
pub fn d_sbus_d_v(
y_bus: &CSR<usize, Complex64>,
v: &[Complex64],
cartesian: bool,
) -> (CSR<usize, Complex64>, CSR<usize, Complex64>) {
let i_bus = y_bus * v;
let diag_v = CSR::<usize, Complex64>::with_diagonal(v.to_vec());
let diag_i_bus = CSR::<usize, Complex64>::with_diagonal(i_bus);
if cartesian {
let d_sbus_d_vr = diag_i_bus.conj() + &diag_v * y_bus.conj();
let d_sbus_d_vi = (diag_i_bus.conj() - &diag_v * y_bus.conj()) * Complex64::i();
(d_sbus_d_vr, d_sbus_d_vi)
} else {
let v_norm = v
.iter()
.map(|v| v / Complex64::new(v.norm(), 0.0))
.collect();
let diag_v_norm = CSR::<usize, Complex64>::with_diagonal(v_norm);
let mut d_sbus_d_va = &diag_v * (&diag_i_bus - y_bus * &diag_v).conj() * Complex64::i();
let d_sbus_d_vm =
&diag_v * (y_bus * &diag_v_norm).conj() + diag_i_bus.conj() * &diag_v_norm;
d_sbus_d_va.sort_indexes();
(d_sbus_d_va, d_sbus_d_vm)
}
}
pub fn make_d_sbus_d_vm(
base_mva: f64,
bus: &[Bus],
_gen: &[Gen],
pw: Option<[f64; 3]>,
qw: Option<[f64; 3]>,
vm: Option<&[f64]>,
_sg: Option<&[Complex64]>,
) -> CSR<usize, Complex64> {
let nb = bus.len();
let base_mva = Complex64::new(base_mva, 0.0);
let pw = pw.unwrap_or([1.0, 0.0, 0.0]);
let qw = qw.unwrap_or(pw);
if let Some(vm) = vm {
const TWO: Complex64 = Complex64 { re: 2.0, im: 0.0 };
let diag: Vec<Complex64> = bus
.iter()
.map(|b| {
if b.pd != 0.0 || b.qd != 0.0 {
let sd_z = Complex64::new(b.pd * pw[2], b.qd * qw[2]) / base_mva;
let sd_i = Complex64::new(b.pd * pw[1], b.qd * qw[1]) / base_mva;
let vm_i = Complex64::new(vm[b.bus_i], 0.0);
-(sd_i + TWO * vm_i * sd_z)
} else {
Complex64::default()
}
})
.collect();
CSR::with_diagonal(diag)
} else {
CSR::with_size(nb, nb)
}
}