#[inline]
pub(crate) fn ohm_to_pu(ohm: f64, base_kv: f64, base_mva: f64) -> f64 {
if base_kv <= 0.0 || base_mva <= 0.0 {
return 0.0;
}
ohm * base_mva / (base_kv * base_kv)
}
#[inline]
pub(crate) fn siemens_to_pu(s: f64, base_kv: f64, base_mva: f64) -> f64 {
if base_mva <= 0.0 {
return 0.0;
}
s * base_kv * base_kv / base_mva
}
pub(crate) fn interpolate_rcc(points: &[(f64, f64, f64)], p: f64) -> (f64, f64) {
match points {
[] => (-9999.0, 9999.0),
[(_, y1, y2)] => (*y1, *y2),
_ => {
let first = &points[0];
let last = points
.last()
.expect("points has 2+ entries in this match arm");
if p <= first.0 {
return (first.1, first.2);
}
if p >= last.0 {
return (last.1, last.2);
}
let i = points.partition_point(|pt| pt.0 <= p);
let (x0, y1_0, y2_0) = points[i - 1];
let (x1, y1_1, y2_1) = points[i];
let t = (p - x0) / (x1 - x0);
(y1_0 + t * (y1_1 - y1_0), y2_0 + t * (y2_1 - y2_0))
}
}
}
pub(crate) fn ptc_table_angle(pts: &[(f64, f64)], step: f64) -> f64 {
if pts.is_empty() {
return 0.0;
}
if let Some(&(_, a)) = pts.iter().find(|&&(s, _)| (s - step).abs() < 0.5) {
return a;
}
let idx = pts.partition_point(|&(s, _)| s < step);
if idx == 0 {
return pts[0].1;
}
if idx >= pts.len() {
return pts[pts.len() - 1].1;
}
let (s0, a0) = pts[idx - 1];
let (s1, a1) = pts[idx];
if (s1 - s0).abs() < 1e-9 {
return a0;
}
a0 + (step - s0) * (a1 - a0) / (s1 - s0)
}
pub(crate) fn rtc_table_ratio(pts: &[(f64, f64)], step: f64) -> f64 {
if pts.is_empty() {
return 1.0;
}
if let Some(&(_, r)) = pts.iter().find(|&&(s, _)| (s - step).abs() < 0.5) {
return r;
}
let idx = pts.partition_point(|&(s, _)| s < step);
if idx == 0 {
return pts[0].1;
}
if idx >= pts.len() {
return pts[pts.len() - 1].1;
}
let (s0, r0) = pts[idx - 1];
let (s1, r1) = pts[idx];
if (s1 - s0).abs() < 1e-9 {
return r0;
}
r0 + (step - s0) * (r1 - r0) / (s1 - s0)
}