use std::f64::consts::PI;
use crate::basic::dsbus_dv::{dSbus_dV, dSbus_dV_old};
use crate::basic::newtonpf::Slice;
use crate::basic::solver::Solve;
use crate::basic::sparse::{
conj::RealImage,
slice::*,
stack::{csc_hstack, csc_vstack},
};
use nalgebra::*;
use nalgebra_sparse::*;
use num_complex::Complex64;
use num_traits::Zero;
#[inline(always)]
pub(crate) fn assemble_f(
f: &mut DVector<f64>,
n_bus: usize,
mis: &DVector<Complex64>,
num_state: usize,
npq: usize,
) {
f.rows_range_mut(0..n_bus)
.zip_apply(&mis.rows_range(0..n_bus), |a, b| *a = b.simd_real());
f.rows_range_mut(n_bus..num_state)
.zip_apply(&mis.rows_range(0..npq), |a, b| *a = b.simd_imaginary());
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
fn update_v(
v_a: &mut DVector<f64>,
dx: &DVector<f64>,
n_bus: usize,
v_m: &mut DVector<f64>,
npq: usize,
num_state: usize,
v_norm: &mut DVector<Complex64>,
v: &mut DVector<Complex64>,
) {
v_a.rows_range_mut(0..n_bus)
.zip_apply(&dx.rows_range(0..n_bus), |a, b| {
*a -= b;
*a = a.rem_euclid(2.0 * PI);
});
let mut vm_pq = v_m.rows_range_mut(0..npq);
vm_pq.zip_apply(&dx.rows_range(n_bus..num_state), |a, b| *a -= b);
v_norm.zip_apply(&*v_a, |a, va| *a = Complex64::from_polar(1.0, va));
v.zip_zip_apply(v_norm, v_m, |a, e, vm| *a = vm * e);
}
trait SliceTo {
type Mat;
fn block_to(&self, start_pos: (usize, usize), shape: (usize, usize), mat: &mut Self::Mat);
fn columns_to(&self, start_col: usize, end_col: usize, mat: &mut Self::Mat);
}
impl<T: Copy + Clone + Zero + Scalar + ClosedAddAssign> SliceTo for CscMatrix<T> {
type Mat = CscMatrix<T>;
#[inline(always)]
fn block_to(&self, start_pos: (usize, usize), shape: (usize, usize), mat: &mut Self::Mat) {
slice_csc_matrix_block_to(self, start_pos, shape, mat)
}
#[inline(always)]
fn columns_to(&self, start_col: usize, end_col: usize, mat: &mut Self::Mat) {
slice_csc_matrix_to(self, start_col, end_col, mat)
}
}
#[allow(non_snake_case, dead_code)]
pub(crate) fn build_jacobian(
ds_dvm: &CscMatrix<Complex64>,
ds_dva: &CscMatrix<Complex64>,
npq: usize,
n_ext: usize,
) -> CscMatrix<f64> {
let (real, imag) = ds_dva
.block((0, 0), (ds_dva.nrows() - n_ext, ds_dva.ncols() - n_ext))
.real_imag();
let (real2, imag2) = ds_dvm
.block((0, 0), (ds_dvm.nrows() - n_ext, ds_dvm.ncols() - n_ext))
.real_imag();
let j11 = real;
let j12 = real2.columns(0, npq);
let j21 = imag.block((0, 0), (npq, imag.ncols()));
let j22 = imag2.block((0, 0), (npq, npq));
csc_vstack(&[&csc_hstack(&[&j11, &j12]), &csc_hstack(&[&j21, &j22])])
}
#[allow(non_snake_case)]
pub(crate) fn build_jacobian_cached(
ds_dvm: &CscMatrix<Complex64>,
ds_dva: &CscMatrix<Complex64>,
cache: &mut Option<JacobianCache>,
npq: usize,
n_ext: usize,
) -> CscMatrix<f64> {
match cache {
Some(cache) => {
ds_dva.block_to(
(0, 0),
(ds_dva.nrows() - n_ext, ds_dva.ncols() - n_ext),
&mut cache.ds_dva,
);
let (real, imag) = cache.ds_dva.real_imag();
cache.j11 = real;
ds_dvm.block_to(
(0, 0),
(ds_dvm.nrows() - n_ext, ds_dvm.ncols() - n_ext),
&mut cache.ds_dvm,
);
let (real2, imag2) = cache.ds_dvm.real_imag();
real2.columns_to(0, npq, &mut cache.j12);
imag.block_to((0, 0), (npq, imag.ncols()), &mut cache.j21);
imag2.block_to((0, 0), (npq, npq), &mut cache.j22);
csc_vstack(&[
&csc_hstack(&[&cache.j11, &cache.j12]),
&csc_hstack(&[&cache.j21, &cache.j22]),
])
}
None => {
let ds_dva =
ds_dva.block((0, 0), (ds_dva.nrows() - n_ext, ds_dva.ncols() - n_ext));
let ds_dvm =
ds_dvm.block((0, 0), (ds_dvm.nrows() - n_ext, ds_dvm.ncols() - n_ext));
let (real, imag) = ds_dva.real_imag();
let (real2, imag2) = ds_dvm.real_imag();
let j11 = real;
let j12 = real2.columns(0, npq);
let j21 = imag.block((0, 0), (npq, imag.ncols()));
let j22 = imag2.block((0, 0), (npq, npq));
let j = csc_vstack(&[&csc_hstack(&[&j11, &j12]), &csc_hstack(&[&j21, &j22])]);
cache.replace(JacobianCache { ds_dva, ds_dvm, j11, j12, j21, j22 });
j
}
}
}
pub(crate) struct JacobianCache {
pub ds_dva: CscMatrix<Complex64>,
pub ds_dvm: CscMatrix<Complex64>,
pub j11: CscMatrix<f64>,
pub j12: CscMatrix<f64>,
pub j21: CscMatrix<f64>,
pub j22: CscMatrix<f64>,
}
#[allow(non_snake_case, dead_code, clippy::too_many_arguments)]
pub fn newton_pf_old<Solver: Solve>(
Ybus: &CscMatrix<Complex64>,
Sbus: &DVector<Complex64>,
v_init: &DVector<Complex64>,
npv: usize,
npq: usize,
tolerance: Option<f64>,
max_iter: Option<usize>,
solver: &mut Solver,
) -> Result<(DVector<Complex64>, usize), (String, DVector<Complex64>)> {
let mut v = v_init.clone();
let mut v_norm = v.map(|e| e.simd_signum());
let max_iter = max_iter.unwrap_or(100);
let tol = tolerance.unwrap_or(1e-6);
let mut mis = &v.component_mul(&(Ybus * &v).conjugate()) - Sbus;
let n_ext = v.len() - npv - npq;
let n_bus = npq + npv;
let num_state = npv + 2 * npq;
let mut F = DVector::zeros(num_state);
assemble_f(&mut F, n_bus, &mis, num_state, npq);
if F.norm() < tol {
return Ok((v, 0));
}
let mut v_m = v.map(|e| e.simd_modulus());
let mut v_a = v.map(|e| e.simd_argument());
let mut cache: Option<JacobianCache> = None;
for iterations in 0..max_iter {
let (dS_dVm, dS_dVa) = dSbus_dV(Ybus, &v, &v_norm);
let jacobian = build_jacobian_cached(&dS_dVm, &dS_dVa, &mut cache, npq, n_ext);
let n = jacobian.nrows();
let (mut Ap, mut Ai, mut Ax) = jacobian.disassemble();
let _ = solver.solve(
Ap.as_mut_slice(),
Ai.as_mut_slice(),
Ax.as_mut_slice(),
F.data.as_mut_slice(),
n,
);
let dx = &F;
update_v(&mut v_a, dx, n_bus, &mut v_m, npq, num_state, &mut v_norm, &mut v);
v.component_mul(&(Ybus * &v).conjugate())
.sub_to(Sbus, &mut mis);
assemble_f(&mut F, n_bus, &mis, num_state, npq);
if F.norm() < tol {
return Ok((v, iterations));
}
}
Err((String::from("Did not converge!"), v))
}
#[allow(non_snake_case, dead_code, clippy::too_many_arguments)]
pub fn newton_pf_v0<Solver: Solve>(
Ybus: &CscMatrix<Complex64>,
Sbus: &DVector<Complex64>,
v_init: &DVector<Complex64>,
npv: usize,
npq: usize,
tolerance: Option<f64>,
max_iter: Option<usize>,
solver: &mut Solver,
) -> Result<(DVector<Complex64>, usize), (String, DVector<Complex64>)> {
let mut v = v_init.clone();
let mut v_norm = v.map(|e| e.simd_signum());
let max_iter = max_iter.unwrap_or(100);
let tol = tolerance.unwrap_or(1e-6);
let mut mis = &v.component_mul(&(Ybus * &v).conjugate()) - Sbus;
let n_ext = v.len() - npv - npq;
let n_bus = npq + npv;
let num_state = npv + 2 * npq;
let mut F = DVector::zeros(num_state);
assemble_f(&mut F, n_bus, &mis, num_state, npq);
if F.norm() < tol {
return Ok((v, 0));
}
let mut v_m = v.map(|e| e.simd_modulus());
let mut v_a = v.map(|e| e.simd_argument());
for iterations in 0..max_iter {
let (dS_dVm, dS_dVa) = dSbus_dV_old(Ybus, &v, &v_norm);
let jacobian = build_jacobian(&dS_dVm, &dS_dVa, npq, n_ext);
let n = jacobian.nrows();
let (mut Ap, mut Ai, mut Ax) = jacobian.disassemble();
let _ = solver.solve(
Ap.as_mut_slice(),
Ai.as_mut_slice(),
Ax.as_mut_slice(),
F.data.as_mut_slice(),
n,
);
let dx = &F;
update_v(&mut v_a, dx, n_bus, &mut v_m, npq, num_state, &mut v_norm, &mut v);
v.component_mul(&(Ybus * &v).conjugate())
.sub_to(Sbus, &mut mis);
assemble_f(&mut F, n_bus, &mis, num_state, npq);
if F.norm() < tol {
return Ok((v, iterations));
}
}
Err((String::from("Did not converge!"), v))
}