use bevy_ecs::{prelude::*, system::RunSystemOnce};
use nalgebra::*;
use nalgebra_sparse::{CooMatrix, CscMatrix, CsrMatrix};
use num_complex::Complex64;
use num_traits::One;
use crate::basic::ecs::elements::*;
use super::init::*;
#[derive(Debug, Default, Resource, Clone, serde::Serialize, serde::Deserialize)]
pub struct PowerFlowConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_it: Option<usize>, #[serde(skip_serializing_if = "Option::is_none")]
pub tol: Option<f64>, }
#[derive(Debug, Default, Resource, Clone, serde::Serialize, serde::Deserialize)]
pub struct PowerFlowResult {
pub v: DVector<Complex64>, pub iterations: usize, pub converged: bool, }
#[derive(Debug, Resource, Clone, serde::Serialize, serde::Deserialize)]
pub struct PowerFlowMat {
pub reorder: CsrMatrix<Complex<f64>>, pub y_bus: CscMatrix<Complex<f64>>, pub s_bus: DVector<Complex64>, pub v_bus_init: DVector<Complex64>, pub npv: usize, pub npq: usize, pub to_perm: Vec<usize>, pub from_perm: Vec<usize>, }
impl PowerFlowMat {
pub fn reorder_index(&self, orig: usize) -> usize {
self.to_perm[orig]
}
pub fn inverse_index(&self, perm: usize) -> usize {
self.from_perm[perm]
}
}
pub(crate) fn create_permutation_matrix(
pv: &[i64],
pq: &[i64],
ext: &[i64],
nodes: usize,
) -> CooMatrix<i64> {
let row_indices: Vec<usize> = (0..nodes).collect();
let mut col_indices: Vec<usize> = (0..nodes).collect();
let values = vec![1; nodes];
let n_bus = pv.len() + pq.len();
for i in 0..pv.len() {
col_indices[i] = pv[i] as usize;
}
for i in pv.len()..n_bus {
col_indices[i] = pq[i - pv.len()] as usize;
}
for i in n_bus..nodes {
col_indices[i] = ext[i - n_bus] as usize;
}
CooMatrix::try_from_triplets(nodes, nodes, row_indices, col_indices, values)
.expect("Failed to create permutation matrix")
}
pub(crate) fn create_y_bus(
common: Res<PFCommonData>,
node_lookup: Res<NodeLookup>,
y_br: Query<(&Admittance, &Port2, &VBase)>,
trans: Query<(&Port4MatPatch, &TransformerDevice, &FromBus, &ToBus)>,
) -> (CsrMatrix<Complex64>, CsrMatrix<Complex64>) {
let nodes = node_lookup.len();
let branches = y_br.iter();
let s_base = common.sbase;
let mut diag_admit = CsrMatrix::identity(branches.len());
let admit_br = diag_admit.values_mut();
let mut incidence_matrix = CooMatrix::new(nodes, branches.len());
for (idx, (ad, topo, vbase)) in branches.enumerate() {
admit_br[idx] = ad.0 * (vbase.0 * vbase.0) / s_base;
if topo.0[0] >= 0 {
incidence_matrix.push(topo.0[0] as usize, idx, Complex64::one());
}
if topo.0[1] >= 0 {
incidence_matrix.push(topo.0[1] as usize, idx, -Complex64::one());
}
}
let incidence_matrix = CsrMatrix::from(&incidence_matrix);
let y_bus = &incidence_matrix * (diag_admit * incidence_matrix.transpose());
let mut trans_patch_matrix = CooMatrix::new(nodes, nodes);
for (patch, trans, from, to) in trans.iter() {
let vbase = trans.vn_lv_kv;
let p = patch.0.scale((vbase * vbase) / s_base);
if from.0 >= 0 {
trans_patch_matrix.push(from.0 as usize, from.0 as usize, p[(0, 0)]);
}
if to.0 >= 0 {
trans_patch_matrix.push(to.0 as usize, to.0 as usize, p[(1, 1)]);
}
if from.0 >= 0 && to.0 >= 0 {
trans_patch_matrix.push(from.0 as usize, to.0 as usize, p[(0, 1)]);
trans_patch_matrix.push(to.0 as usize, from.0 as usize, p[(1, 0)]);
}
}
let y_bus = y_bus + CsrMatrix::from(&trans_patch_matrix);
(incidence_matrix, y_bus)
}
pub fn init_states(world: &mut World) {
let (_incidence_matrix, y_bus) = world.run_system_once(create_y_bus).unwrap();
let cfg = world.run_system_once(init_bus_status).unwrap();
let y_bus = y_bus.transpose_as_csc();
let s_bus = cfg.s_bus;
let v_bus_init = cfg.v_bus_init;
let mut to_perm = vec![0; v_bus_init.len()]; let mut from_perm = vec![0; v_bus_init.len()]; for (new_idx, &original_idx) in cfg.reorder.col_indices().iter().enumerate() {
to_perm[original_idx] = new_idx;
from_perm[new_idx] = original_idx;
}
world.insert_resource(PowerFlowMat {
reorder: cfg.reorder,
y_bus,
s_bus,
v_bus_init,
npv: cfg.npv,
npq: cfg.npq,
to_perm,
from_perm,
});
}
pub(crate) struct SystemBusStatus {
reorder: CsrMatrix<Complex64>,
s_bus: DVector<Complex64>,
v_bus_init: DVector<Complex64>,
npv: usize,
npq: usize,
}
pub(crate) fn init_bus_status(
node_lookup: Res<NodeLookup>,
pq: Query<(&BusID, &PQBus)>,
pv: Query<(&BusID, &PVBus), Without<SlackBus>>,
ext: Query<(&BusID, &SlackBus)>,
sbus: Query<(&BusID, &SBusInjPu)>,
vbus: Query<(&BusID, &VBusPu)>,
) -> SystemBusStatus {
let nodes = node_lookup.len();
let mut s_bus = DVector::zeros(nodes);
let mut v_bus_init = DVector::from_element(nodes, Complex64::one());
let mut pq_only: Vec<_> = pq.iter().map(|x| x.0.0).collect();
let mut pv_only: Vec<_> = pv.iter().map(|x| x.0.0).collect();
let mut exts: Vec<_> = ext.iter().map(|x| x.0.0).collect();
sbus.iter().for_each(|(bus_id, s)| {
let idx = bus_id.0 as usize;
s_bus[idx] = s.0;
});
vbus.iter().for_each(|(bus_id, s)| {
let idx = bus_id.0 as usize;
v_bus_init[idx] = s.0;
});
let npv = pv_only.len();
let npq = pq_only.len();
pv_only.sort_unstable();
pq_only.sort_unstable();
exts.sort_unstable();
let reorder_coo = create_permutation_matrix(
pv_only.as_slice(),
pq_only.as_slice(),
exts.as_slice(),
nodes,
);
let reorder_csr = CsrMatrix::from(&reorder_coo);
let reorder: CsrMatrix<Complex64> = CsrMatrix::try_from_pattern_and_values(
reorder_csr.pattern().clone(),
reorder_csr
.values()
.iter()
.map(|&x| Complex64::new(x as f64, 0.0))
.collect(),
)
.expect("Failed to create complex permutation matrix");
SystemBusStatus {
reorder,
s_bus,
v_bus_init,
npv,
npq,
}
}