use super::{Reader, allocate_name};
use crate::glm::ObjectKey;
use crate::{DistLocationTarget, DistTransformer, DistWinding, DistWindingConn};
use powerio_core::{Diagnostic, Error};
impl Reader<'_> {
pub(super) fn delta_grounded_transformer(
&mut self,
key: ObjectKey,
cfg: ObjectKey,
phases: &[usize],
) -> Result<(), Error> {
if phases != [0, 1, 2] {
return Err(self.error(key, "DELTA_GWYE requires all three phases"));
}
if self.newton_raphson
&& let Some(bus) =
crate::glm::connections::source_with_zero_sequence(self.net.sources())
{
return Err(self.error(key, format!(
"NR DELTA_GWYE with source zero-sequence voltage at {bus} requires an additional native primary-stamp mapping"
)));
}
let primary = self.positive_geometry(cfg, "primary_voltage")?;
let secondary_ll = self.positive_geometry(cfg, "secondary_voltage")?;
let secondary = secondary_ll / 3.0_f64.sqrt();
let rating = self.transformer_rating(cfg, true)? / 3.0;
let z = self.quantity(cfg, "impedance").ok_or_else(|| {
self.error(
cfg,
"DELTA_GWYE requires positive series resistance and reactance",
)
})?;
if z.re <= 0.0 || z.im <= 0.0 {
return Err(self.error(
cfg,
"DELTA_GWYE requires positive series resistance and reactance",
));
}
let mut endpoints = Vec::new();
for field in ["from", "to"] {
let object = self
.index
.resolve_property(key, field)?
.ok_or_else(|| self.error(key, format!("transformer requires {field}")))?;
let root = self
.roots
.get(&object)
.ok_or_else(|| self.error(key, "transformer endpoint is not an electrical bus"))?;
if self.triplex.contains_key(root)
|| phases.iter().any(|p| !self.phases[root].contains(p))
{
return Err(self.error(key, "transformer phase is absent at an endpoint"));
}
endpoints.push(self.names[root].clone());
}
let negative = if primary > secondary_ll {
[2, 0, 1]
} else {
[1, 2, 0]
};
let original = self.names[&key].clone();
for (phase, other) in negative.into_iter().enumerate() {
let hot = (phase + 1).to_string();
let name = allocate_name(
&mut self.identities,
&format!("{original}__glm_{}", ['A', 'B', 'C'][phase]),
);
let from = DistWinding::new(
&endpoints[0],
vec![hot.clone(), (other + 1).to_string()],
DistWindingConn::Wye,
primary,
rating,
);
let mut to = DistWinding::new(
&endpoints[1],
vec![hot, "n".into()],
DistWindingConn::Wye,
secondary,
rating,
);
to.r_pct = 100.0 * z.re;
let mut transformer =
DistTransformer::new(&name, vec![from, to], vec![100.0 * z.im], 1);
transformer
.extras
.insert("ppm_antifloat".into(), serde_json::json!(0.0));
self.net.transformers_mut().push(transformer);
self.attach(key, DistLocationTarget::Transformer, name);
}
self.diagnostics.push(Diagnostic::of(&crate::diagnostics::codes::READ_GLM_TRANSFORMER_EQUIVALENT,
format!("{original}: DELTA_GWYE becomes three phase-pair coils with native step-up/down polarity and no core branch; all external port currents are preserved")));
Ok(())
}
}