use gridoxide::cgmes::{cgmes_to_network, load_profiles};
use std::path::{Path, PathBuf};
fn main() {
let root =
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/data/CGMES-Test-Configurations/v3.0");
let names: Vec<String> = std::env::args().skip(1).collect();
for name in names {
let dir = root.join(&name);
if !dir.is_dir() {
println!("{name}: absent");
continue;
}
let mut paths: Vec<PathBuf> = std::fs::read_dir(&dir)
.unwrap()
.filter_map(|e| e.ok().map(|e| e.path()))
.filter(|p| p.extension().is_some_and(|x| x == "xml"))
.filter(|p| {
let n = p.file_name().unwrap().to_string_lossy().to_uppercase();
["_EQ", "_SSH", "_TP", "_SV", "EQ_BD", "EQBD"]
.iter()
.any(|k| n.contains(k))
})
.collect();
paths.sort();
let refs: Vec<&Path> = paths.iter().map(|p| p.as_path()).collect();
let Ok(ds) = load_profiles(&refs) else {
println!("{name}: decode failed");
continue;
};
let Ok(net) = cgmes_to_network(&ds, 100e6) else {
println!("{name}: convert failed");
continue;
};
let mut bad: Vec<(usize, f64, f64)> = Vec::new();
for (i, l) in net.lines.iter().enumerate() {
let (a, b) = (net.buses[l.from].u_rated, net.buses[l.to].u_rated);
if a > 0.0 && b > 0.0 && (a - b).abs() / a.max(b) > 1e-6 {
bad.push((i, a, b));
}
}
let mut levels: std::collections::BTreeMap<u64, usize> = Default::default();
for b in &net.buses {
*levels.entry((b.u_rated / 1e3).round() as u64).or_default() += 1;
}
println!(
"{name}: {} line(s), {} spanning two bases after harmonizing (should be 0); \
levels {:?}",
net.lines.len(),
bad.len(),
levels
);
let h = &net.base_harmonization;
if h.buses > 0 {
println!(
" harmonized {} bus(es) over {} group(s): {}",
h.buses,
h.groups,
h.merged
.iter()
.map(|(k, r, c)| format!("{c} bus(es) {r:.0} kV -> {k:.0} kV"))
.collect::<Vec<_>>()
.join(", ")
);
}
for (i, a, b) in bad.iter().take(6) {
let l = &net.lines[*i];
println!(
" line {i}: bus {} ({:.0} kV) -> bus {} ({:.0} kV) ratio {:.4}, x = {:.5} pu",
l.from,
a / 1e3,
l.to,
b / 1e3,
a / b,
l.x
);
}
}
}