use crate::pfopt::MPOpt;
use powers::SBus;
use anyhow::Result;
use full::slice::{abs, norm_inf};
use itertools::izip;
use num_complex::Complex64;
use powers::debug::format_rect_vec;
use sparsetools::csr::CSR;
use spsolve::Solver;
pub trait ProgressMonitor {
fn update(&self, i: usize, norm_f: f64);
}
pub struct PrintProgress {}
impl ProgressMonitor for PrintProgress {
fn update(&self, i: usize, norm_f: f64) {
if i == 0 {
println!(" it max P & Q mismatch (p.u.)");
println!("---- ---------------------------");
println!("{} {}", i, norm_f);
} else {
println!("{} {}", i, norm_f);
}
}
}
#[allow(non_snake_case)]
pub(crate) fn gausspf(
Ybus: &CSR<usize, Complex64>,
SbusFn: &dyn SBus,
V0: &[Complex64],
_ref: &[usize],
pv: &[usize],
pq: &[usize],
_solver: &dyn Solver<usize, f64>,
mpopt: &MPOpt,
progress: Option<&dyn ProgressMonitor>,
) -> Result<(Vec<Complex64>, bool, usize)> {
let pv_pq = [pv, pq].concat();
let tol = mpopt.pf.tolerance;
let max_it = mpopt.pf.max_it_gs;
let mut converged = false;
let mut i = 0;
let mut V = V0.to_vec();
let Vm = abs(&V);
let npv = pv.len();
let Ibus: Vec<Complex64> = Ybus * &V;
let mut Sbus = SbusFn.s_bus(&Vm);
let mis: Vec<Complex64> = izip!(&V, &Ibus, &Sbus)
.map(|(V, Ibus, Sbus)| V * Ibus.conj() - Sbus)
.collect();
let F: Vec<f64> = [
pv_pq.iter().map(|&i| mis[i].re).collect::<Vec<_>>(),
pq.iter().map(|&i| mis[i].im).collect::<Vec<_>>(),
]
.concat();
log::trace!("Sbus0: {}", format_rect_vec(&Sbus));
let normF = norm_inf(&F);
if let Some(pm) = progress {
pm.update(i, normF);
}
if normF < tol {
converged = true;
log::info!("Converged!");
}
log::debug!("normF0: {}", normF);
let diagYbus = Ybus.diagonal();
while !converged && i < max_it {
i = i + 1;
let Ibus: Vec<Complex64> = Ybus * &V;
for &k in pq {
V[k] = V[k] + ((Sbus[k] / V[k]).conj() - Ibus[k]) / diagYbus[k];
}
if npv != 0 {
for &k in pv {
Sbus[k] = Complex64::new(Sbus[k].re, (V[k] * Ibus[k].conj()).im);
V[k] = V[k] + ((Sbus[k] / V[k]).conj() - Ibus[k]) / diagYbus[k];
}
for &k in pv {
V[k] = Vm[k] * V[k] / Complex64::new(V[k].norm(), 0.0);
}
}
let F = {
let Ibus: Vec<Complex64> = Ybus * &V;
let Sbus = SbusFn.s_bus(&Vm);
log::trace!("Sbus_{}: {}", i, format_rect_vec(&Sbus));
let mis: Vec<Complex64> = izip!(&V, &Ibus, &Sbus)
.map(|(v, i_bus, s_bus)| v * i_bus.conj() - s_bus)
.collect();
[
pv_pq.iter().map(|&i| mis[i].re).collect::<Vec<_>>(),
pq.iter().map(|&i| mis[i].im).collect::<Vec<_>>(),
]
.concat()
};
let norm_f = norm_inf(&F);
if let Some(pm) = progress {
pm.update(i, norm_f);
}
if norm_f < tol {
converged = true;
log::info!("Gauss-Seidel power flow converged in {} iterations.", i);
}
log::debug!("norm_f{}: {}", i, norm_f);
}
if !converged {
log::info!(
"Gauss-Seidel power flow did not converge in {} iterations.",
i
);
}
Ok((V, converged, i))
}