use std::collections::HashMap;
use super::objects::{DssCatalog, DssObject, LineCodeData, LineData};
pub fn strip_phases(bus: &str) -> &str {
bus.split('.').next().unwrap_or(bus)
}
pub fn resolve_linecodes(catalog: &mut DssCatalog) {
let linecodes: HashMap<String, LineCodeData> = catalog
.objects
.iter()
.filter_map(|o| {
if let DssObject::LineCode(lc) = o {
Some((lc.name.to_lowercase(), lc.clone()))
} else {
None
}
})
.collect();
for obj in &mut catalog.objects {
if let DssObject::Line(line) = obj {
if line.linecode.is_empty() {
continue;
}
let key = line.linecode.to_lowercase();
if let Some(lc) = linecodes.get(&key) {
apply_linecode_to_line(line, lc);
} else {
tracing::warn!(
"Line.{}: linecode '{}' not found — using defaults",
line.name,
line.linecode
);
}
}
}
}
fn apply_linecode_to_line(line: &mut LineData, lc: &LineCodeData) {
let factor = lc.units.to_km_factor();
let inv_factor = if factor > 1e-12 { 1.0 / factor } else { 1.0 };
if !lc.rmatrix.is_empty() && line.rmatrix.is_empty() {
line.rmatrix = lc.rmatrix.iter().map(|&v| v * inv_factor).collect();
}
if !lc.xmatrix.is_empty() && line.xmatrix.is_empty() {
line.xmatrix = lc.xmatrix.iter().map(|&v| v * inv_factor).collect();
}
if !lc.cmatrix.is_empty() && line.cmatrix.is_empty() {
line.cmatrix = lc.cmatrix.iter().map(|&v| v * inv_factor).collect();
}
if line.rmatrix.is_empty() {
if lc.r1 != 0.0 {
line.r1 = lc.r1 * inv_factor;
}
if lc.x1 != 0.0 {
line.x1 = lc.x1 * inv_factor;
}
if lc.r0 != 0.0 {
line.r0 = lc.r0 * inv_factor;
}
if lc.x0 != 0.0 {
line.x0 = lc.x0 * inv_factor;
}
if lc.c1 != 0.0 {
line.c1 = lc.c1 * inv_factor;
}
if lc.c0 != 0.0 {
line.c0 = lc.c0 * inv_factor;
}
}
if line.phases == 3 && lc.phases != 0 {
line.phases = lc.phases;
}
}
pub fn resolve_xfmrcodes(catalog: &mut DssCatalog) {
use super::objects::XfmrCodeData;
let codes: HashMap<String, XfmrCodeData> = catalog
.objects
.iter()
.filter_map(|o| {
if let DssObject::XfmrCode(xc) = o {
Some((xc.name.to_lowercase(), xc.clone()))
} else {
None
}
})
.collect();
for obj in &mut catalog.objects {
if let DssObject::Transformer(xfmr) = obj {
if xfmr.xfmrcode.is_empty() {
continue;
}
let key = xfmr.xfmrcode.to_lowercase();
if let Some(xc) = codes.get(&key) {
xfmr.windings = xc.windings;
xfmr.phases = xc.phases;
xfmr.xhl = xc.xhl;
xfmr.xht = xc.xht;
xfmr.xlt = xc.xlt;
xfmr.pct_load_loss = xc.pct_load_loss;
xfmr.pct_no_load_loss = xc.pct_no_load_loss;
xfmr.pct_imag = xc.pct_imag;
let nw = xc.windings as usize;
xfmr.buses.resize(nw, String::new());
xfmr.conns.resize(nw, super::objects::WdgConn::Wye);
xfmr.kvs.resize(nw, 0.0);
xfmr.kvas.resize(nw, 0.0);
xfmr.pct_rs.resize(nw, 0.5);
xfmr.taps.resize(nw, 1.0);
for (i, &kv) in xc.kvs.iter().enumerate() {
if i < xfmr.kvs.len() {
xfmr.kvs[i] = kv;
}
}
for (i, &kva) in xc.kvas.iter().enumerate() {
if i < xfmr.kvas.len() {
xfmr.kvas[i] = kva;
}
}
for (i, conn) in xc.conns.iter().enumerate() {
if i < xfmr.conns.len() {
xfmr.conns[i] = conn.clone();
}
}
for (i, &r) in xc.pct_rs.iter().enumerate() {
if i < xfmr.pct_rs.len() {
xfmr.pct_rs[i] = r;
}
}
} else {
tracing::warn!(
"Transformer.{}: xfmrcode '{}' not found",
xfmr.name,
xfmr.xfmrcode
);
}
}
}
}
pub fn build_bus_map(catalog: &DssCatalog) -> HashMap<String, u32> {
let mut names: std::collections::BTreeSet<String> = Default::default();
if let Some(ref circ) = catalog.circuit {
names.insert(circ.bus.to_lowercase());
}
for obj in &catalog.objects {
match obj {
DssObject::Line(l) => {
if !l.bus1.is_empty() {
names.insert(strip_phases(&l.bus1.to_lowercase()).to_string());
}
if !l.bus2.is_empty() {
names.insert(strip_phases(&l.bus2.to_lowercase()).to_string());
}
}
DssObject::Transformer(t) => {
for b in &t.buses {
if !b.is_empty() {
names.insert(strip_phases(&b.to_lowercase()).to_string());
}
}
}
DssObject::AutoTrans(a) => {
for b in &a.transformer.buses {
if !b.is_empty() {
names.insert(strip_phases(&b.to_lowercase()).to_string());
}
}
}
DssObject::Load(l) => {
if !l.bus1.is_empty() {
names.insert(strip_phases(&l.bus1.to_lowercase()).to_string());
}
}
DssObject::Generator(g) => {
if !g.bus1.is_empty() {
names.insert(strip_phases(&g.bus1.to_lowercase()).to_string());
}
}
DssObject::PvSystem(p) => {
if !p.bus1.is_empty() {
names.insert(strip_phases(&p.bus1.to_lowercase()).to_string());
}
}
DssObject::Storage(s) => {
if !s.bus1.is_empty() {
names.insert(strip_phases(&s.bus1.to_lowercase()).to_string());
}
}
DssObject::Capacitor(c) => {
if !c.bus1.is_empty() {
names.insert(strip_phases(&c.bus1.to_lowercase()).to_string());
}
}
DssObject::Reactor(r) => {
if !r.bus1.is_empty() {
names.insert(strip_phases(&r.bus1.to_lowercase()).to_string());
}
if !r.bus2.is_empty() {
names.insert(strip_phases(&r.bus2.to_lowercase()).to_string());
}
}
DssObject::VsConverter(v) => {
if !v.bus.is_empty() {
names.insert(strip_phases(&v.bus.to_lowercase()).to_string());
}
}
_ => {}
}
}
names
.into_iter()
.enumerate()
.map(|(i, name)| (name, (i + 1) as u32))
.collect()
}