use anyhow::{format_err, Result};
use std::fmt::{Display, Formatter};
use uom::fmt::DisplayStyle::Abbreviation;
use uom::si::electric_potential::kilovolt;
use uom::si::f64::*;
use uom::si::length::inch;
use uom::si::length::millimeter as mm;
use crate::common::{ElectrodeConfiguration, EnclosureType};
use crate::tables::{TABLE_2, TABLE_7};
pub struct Cubicle {
pub v_oc: ElectricPotential,
pub ec: ElectrodeConfiguration,
pub g: Length,
pub d: Length,
pub dim: BoxDimensions,
pub enclosure_type: EnclosureType,
pub var_cf: f64,
pub cf: f64,
pub(crate) debug: Option<EnclosureDebug>,
pub hv: bool,
}
pub(crate) struct EnclosureDebug {
pub(crate) height: Length,
pub(crate) width: Length,
pub(crate) ees: f64,
}
impl Cubicle {
pub fn new(
v_oc: ElectricPotential,
ec: ElectrodeConfiguration,
g: Length,
d: Length,
height: Length,
width: Length,
depth: Length,
) -> Result<Self> {
let var_cf = Self::calc_var_cf(v_oc, ec);
let enclosure_type = if (v_oc < ElectricPotential::new::<kilovolt>(0.6))
&& (height < Length::new::<mm>(508.0))
&& (width < Length::new::<mm>(508.0))
&& (depth <= Length::new::<mm>(203.2))
{
EnclosureType::Shallow
} else {
EnclosureType::Typical
};
let (cf, debug) = Self::calc_cf(v_oc, ec, height, width, enclosure_type);
Self::check_model_bounds(v_oc, g, d, width)?;
if cf < 0.0 || cf > 3.0 {
return Err(format_err!(
"enclosure size correction factor ({}) must be between 0 and 3",
cf
));
}
let hv = if ElectricPotential::new::<kilovolt>(0.600) < v_oc
&& v_oc <= ElectricPotential::new::<kilovolt>(15.000)
{
true
} else if v_oc <= ElectricPotential::new::<kilovolt>(0.600) {
false
} else {
unreachable!()
};
Ok(Self {
v_oc,
ec,
g,
d,
dim: BoxDimensions {
height,
width,
depth,
},
enclosure_type,
var_cf,
cf,
debug,
hv,
})
}
fn check_model_bounds(
v_oc: ElectricPotential,
g: Length,
d: Length,
width: Length,
) -> Result<()> {
if v_oc <= ElectricPotential::new::<kilovolt>(0.600) {
if g < Length::new::<mm>(6.35) {
return Err(format_err!(
"busbar gap G must be >= 6.35 mm: {}",
g.into_format_args(mm, Abbreviation)
));
}
if g > Length::new::<mm>(76.2) {
return Err(format_err!(
"busbar gap G must be <= 76.2 mm: {}",
g.into_format_args(mm, Abbreviation)
));
}
} else {
if g < Length::new::<mm>(19.05) {
return Err(format_err!(
"busbar gap G must be >= 19.05 mm: {}",
g.into_format_args(mm, Abbreviation)
));
}
if g > Length::new::<mm>(254.0) {
return Err(format_err!(
"busbar gap G must be <= 254 mm: {}",
g.into_format_args(mm, Abbreviation)
));
}
}
if d < Length::new::<mm>(305.0) {
return Err(format_err!(
"working distance D must be >= 305 mm: {}",
d.into_format_args(mm, Abbreviation)
));
}
if width < 4.0 * g {
return Err(format_err!(
"width of enclosure ({}) must be at least four times the busbar gap G (4 * {} = {})",
width.into_format_args(mm, Abbreviation),
g.into_format_args(mm, Abbreviation),
(4.0 * g).into_format_args(mm, Abbreviation)
));
}
Ok(())
}
fn calc_var_cf(v_oc: ElectricPotential, ec: ElectrodeConfiguration) -> f64 {
let v_oc = v_oc.get::<kilovolt>();
let k = TABLE_2.get(&ec).unwrap();
k.k1 * v_oc.powi(6)
+ k.k2 * v_oc.powi(5)
+ k.k3 * v_oc.powi(4)
+ k.k4 * v_oc.powi(3)
+ k.k5 * v_oc.powi(2)
+ k.k6 * v_oc.powi(1)
+ k.k7
}
fn calc_cf(
v_oc: ElectricPotential,
ec: ElectrodeConfiguration,
h: Length,
w: Length,
enclosure_type: EnclosureType,
) -> (f64, Option<EnclosureDebug>) {
if ec == ElectrodeConfiguration::HOA || ec == ElectrodeConfiguration::VOA {
return (1.0, None);
}
let (a, b) = match ec {
ElectrodeConfiguration::VCB => (4.0, 20.0),
ElectrodeConfiguration::VCBB => (10.0, 24.0),
ElectrodeConfiguration::HCB => (10.0, 22.0),
_ => {
unreachable!()
}
};
let eq_11_12 = |dim: Length| -> Length {
let v_oc = v_oc.get::<kilovolt>();
let dim = dim.get::<mm>();
let y1 = dim - 660.4;
let y2 = (v_oc + a) / b;
let dim_1 = (660.4 + (y1 * y2)) / 25.4;
Length::new::<inch>(dim_1)
};
const MM_TO_IN: f64 = 0.03937;
let width = if w < Length::new::<mm>(508.0) {
match enclosure_type {
EnclosureType::Typical => Length::new::<inch>(20.0),
EnclosureType::Shallow => Length::new::<inch>(MM_TO_IN * w.get::<mm>()),
}
} else if w <= Length::new::<mm>(660.4) {
Length::new::<inch>(MM_TO_IN * w.get::<mm>())
} else if w <= Length::new::<mm>(1244.6) {
eq_11_12(w)
} else {
eq_11_12(Length::new::<mm>(1244.6))
};
let height = if h < Length::new::<mm>(508.0) {
match enclosure_type {
EnclosureType::Typical => Length::new::<inch>(20.0),
EnclosureType::Shallow => Length::new::<inch>(MM_TO_IN * h.get::<mm>()),
}
} else if h <= Length::new::<mm>(660.4) {
Length::new::<inch>(MM_TO_IN * h.get::<mm>())
} else if h <= Length::new::<mm>(1244.6) {
match ec {
ElectrodeConfiguration::VCB => Length::new::<inch>(MM_TO_IN * h.get::<mm>()),
ElectrodeConfiguration::VCBB | ElectrodeConfiguration::HCB => eq_11_12(h),
_ => {
unreachable!()
}
}
} else {
match ec {
ElectrodeConfiguration::VCB => Length::new::<inch>(49.0),
ElectrodeConfiguration::VCBB | ElectrodeConfiguration::HCB => {
eq_11_12(Length::new::<mm>(1244.6))
}
_ => {
unreachable!()
}
}
};
let ees = (height + width) / 2.0;
if enclosure_type == EnclosureType::Typical {
debug_assert!(ees >= Length::new::<inch>(19.999))
}
let b = TABLE_7.get(&(enclosure_type, ec)).unwrap();
let ees = ees.get::<inch>();
let x1 = b.b1 * ees.powi(2) + b.b2 * ees + b.b3;
let cf = match enclosure_type {
EnclosureType::Typical => x1,
EnclosureType::Shallow => 1.0 / x1,
};
(cf, Some(EnclosureDebug { height, width, ees }))
}
}
pub struct BoxDimensions {
pub height: Length,
pub width: Length,
pub depth: Length,
}
impl Display for Cubicle {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Cubicle parameters:
V_oc (nominal voltage) = {}
D (working distance) = {}
G (busbar gap) = {}
EC (electrode configuration) = {:?}
Box dimensions:
height = {}
width = {}
depth = {}",
self.v_oc.into_format_args(kilovolt, Abbreviation),
self.d.into_format_args(mm, Abbreviation),
self.g.into_format_args(mm, Abbreviation),
self.ec,
self.dim.height.into_format_args(mm, Abbreviation),
self.dim.width.into_format_args(mm, Abbreviation),
self.dim.depth.into_format_args(mm, Abbreviation),
)?;
if let Some(debug) = &self.debug {
write!(
f,
"
Enclosure correction factor
enclosure_type = {:?}
height = {}
width = {}
EES = {} in
CF = {}",
self.enclosure_type,
debug.height.into_format_args(mm, Abbreviation),
debug.width.into_format_args(mm, Abbreviation),
debug.ees,
self.cf
)?;
}
Ok(())
}
}