use std::iter::zip;
use anyhow::Result;
use caseformat::{Branch, Bus};
use full::iter::neg;
use full::slice::{abs, angle, norm_inf, polar};
use itertools::izip;
use num_complex::Complex64;
use powers::debug::format_rect_vec;
use powers::{make_ybus, SBus};
use sparsetools::csr::CCSR;
use sparsetools::csr::CSR;
use spsolve::FactorSolver;
use crate::pfopt::{Alg, MPOpt};
#[allow(non_snake_case)]
pub(crate) fn fdpf<F>(
Ybus: &CSR<usize, Complex64>,
Sbus: &dyn SBus,
V0: &[Complex64],
Bp: &CSR<usize, f64>,
Bpp: &CSR<usize, f64>,
_ref: &[usize],
pv: &[usize],
pq: &[usize],
solver: &dyn FactorSolver<usize, f64, F>,
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_fd;
let mut converged = false;
let mut i = 0;
let mut V = V0.to_vec();
let mut Va = angle(&V);
let mut Vm = abs(&V);
let (mut P, mut Q) = {
let Ibus: Vec<Complex64> = Ybus * &V;
let Sbus = Sbus.s_bus(&Vm);
log::trace!("Sbus0: {}", format_rect_vec(&Sbus));
let mis: Vec<Complex64> = izip!(&V, &Ibus, &Sbus, &Vm)
.map(|(V, Ibus, Sbus, Vm)| V * Ibus.conj() - Sbus / Vm)
.collect();
(
pv_pq.iter().map(|&i| mis[i].re).collect::<Vec<_>>(),
pq.iter().map(|&i| mis[i].im).collect::<Vec<_>>(),
)
};
let normP = norm_inf(&P);
let normQ = norm_inf(&Q);
if let Some(pm) = progress {
pm.update(i, i, normP, normQ);
}
if normP < tol && normQ < tol {
converged = true;
log::info!("Converged!");
}
log::debug!("normP = {}, normQ = {}", normP, normQ);
let Bp = Bp.select(Some(&pv_pq), Some(&pv_pq))?;
let Bpp = Bpp.select(Some(&pq), Some(&pq))?;
let LUp = solver.factor(Bp.cols(), Bp.colidx(), Bp.rowptr(), Bp.values())?;
let LUpp = solver.factor(Bpp.cols(), Bpp.colidx(), Bpp.rowptr(), Bpp.values())?;
while !converged && i < max_it {
i = i + 1;
solver.solve(&LUp, &mut P, true)?;
let dVa = neg(P.iter().copied());
for (&i, dVa) in zip(&pv_pq, dVa) {
Va[i] += dVa;
}
V = polar(&Vm, &Va);
(P, Q) = {
let Ibus: Vec<Complex64> = Ybus * &V;
let Sbus = Sbus.s_bus(&Vm);
let mis: Vec<Complex64> = izip!(&V, &Ibus, &Sbus, &Vm)
.map(|(V, Ibus, Sbus, Vm)| V * Ibus.conj() - Sbus / Vm)
.collect();
(
pv_pq.iter().map(|&i| mis[i].re).collect::<Vec<_>>(),
pq.iter().map(|&i| mis[i].im).collect::<Vec<_>>(),
)
};
let normP = norm_inf(&P);
let normQ = norm_inf(&Q);
log::debug!("normP = {}, normQ = {}", normP, normQ);
if let Some(pm) = progress {
pm.update(i, i - 1, normP, normQ);
}
if normP < tol && normQ < tol {
converged = true;
log::info!(
"Fast-decoupled power flow converged in {} P-iterations and {} Q-iterations.",
i,
i - 1
);
}
solver.solve(&LUpp, &mut Q, true)?;
let dVm = neg(Q.iter().copied());
zip(pq, dVm).for_each(|(&i, dVm)| Vm[i] += dVm);
V = polar(&Vm, &Va);
(P, Q) = {
let Ibus: Vec<Complex64> = Ybus * &V;
let Sbus = Sbus.s_bus(&Vm);
let mis: Vec<Complex64> = izip!(&V, &Ibus, &Sbus, &Vm)
.map(|(V, Ibus, Sbus, Vm)| V * Ibus.conj() - Sbus / Vm)
.collect();
(
pv_pq.iter().map(|&i| mis[i].re).collect::<Vec<_>>(),
pq.iter().map(|&i| mis[i].im).collect::<Vec<_>>(),
)
};
let normP = norm_inf(&P);
let normQ = norm_inf(&Q);
log::debug!("normP = {}, normQ = {}", normP, normQ);
if let Some(pm) = progress {
pm.update(i, i, normP, normQ);
}
if normP < tol && normQ < tol {
converged = true;
log::info!(
"Fast-decoupled power flow converged in {} P-iterations and {} Q-iterations.",
i,
i
);
}
}
if !converged {
log::info!(
"Fast-decoupled power flow did not converge in {} iterations.",
i
);
}
Ok((V, converged, i))
}
pub(crate) fn make_b(
base_mva: f64,
bus: &[Bus],
branch: &[Branch],
alg: Alg,
double_prime: bool,
) -> (CSR<usize, f64>, Option<CSR<usize, f64>>) {
let mut bus = bus.to_vec(); for b in bus.iter_mut() {
b.bs = 0.0; }
let b_p = {
let mut branch = branch.to_vec(); for br in branch.iter_mut() {
br.br_b = 0.0; br.tap = 1.0; if alg == Alg::FDXB {
br.br_r = 0.0; }
}
let (y_p, _) = make_ybus(base_mva, &bus, &branch, false);
-y_p.to_csr().imag()
};
let b_pp = if double_prime {
let mut branch = branch.to_vec(); for br in branch.iter_mut() {
br.shift = 0.0; if alg == Alg::FDBX {
br.br_r = 0.0; }
}
let (y_pp, _) = make_ybus(base_mva, &bus, &branch, false);
Some(-y_pp.to_csr().imag())
} else {
None
};
(b_p, b_pp)
}
pub trait ProgressMonitor {
fn update(&self, p: usize, q: usize, norm_p: f64, norm_q: f64);
}
pub struct PrintProgress {}
impl ProgressMonitor for PrintProgress {
fn update(&self, p: usize, q: usize, norm_p: f64, norm_q: f64) {
if p == 0 {
println!("iteration max mismatch (p.u.) ");
println!("type # P Q ");
println!("---- ---- ----------- -----------");
println!(" - {} {} {}", p, norm_p, norm_q);
} else {
if p != q {
println!(" P {} {} {}", p, norm_p, norm_q);
} else {
println!(" Q {} {} {}", p, norm_p, norm_q);
}
}
}
}