use csx::properties::{DumpFileType, DumpType, MaterialQuantity};
use csx::{CoordSystem, PropertyKind, PropertyType, TypeMask, Vec3};
use super::ProbeSpec;
use crate::engine::CellBox;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SarKind {
Local,
Averaged(f64),
Raw,
}
impl SarKind {
pub fn of(t: DumpType) -> Option<Self> {
match t {
DumpType::SarLocal => Some(Self::Local),
DumpType::Sar1g => Some(Self::Averaged(1e-3)),
DumpType::Sar10g => Some(Self::Averaged(1e-2)),
DumpType::SarRaw => Some(Self::Raw),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct SarSpec {
pub name: String,
pub id: u32,
pub kind: SarKind,
pub file_type: DumpFileType,
pub frequencies: Vec<f64>,
pub cells: CellBox,
pub sigma: Vec<[f64; 3]>,
pub density: Vec<f64>,
}
pub(super) fn sar_specs(
model: &csx::ContinuousStructure,
dumps: &[ProbeSpec<csx::properties::DumpBox>],
subsampling: usize,
warnings: &mut Vec<String>,
) -> Vec<SarSpec> {
let mut out = Vec::new();
let mut index = None;
let nodes = [0, 1, 2].map(|a| model.grid.lines(a).len());
for d in dumps {
let Some(kind) = SarKind::of(d.settings.dump_type) else {
continue;
};
if d.settings.probe.frequencies.is_empty() {
continue;
}
let cells = match CellBox::new(d.start, d.stop).fitted(nodes) {
Ok(c) => c,
Err(e) => {
warnings.push(format!("SAR dump {:?}: {e}; ignored", d.name));
continue;
}
};
let index = index.get_or_insert_with(|| {
model.spatial_index(
CoordSystem::Cartesian,
TypeMask::MATERIALS | TypeMask::of(PropertyType::Metal),
)
});
let (mut sigma, density) = cell_media(model, index, &cells, subsampling.max(1));
let mut bad = 0usize;
for s in sigma.iter_mut().flatten() {
if !(s.is_finite() && *s >= 0.0) {
*s = 0.0;
bad += 1;
}
}
if bad > 0 {
warnings.push(format!(
"SAR dump {:?}: {bad} negative or non-finite cell conductivities set to 0",
d.name
));
}
if density.iter().all(|&r| r == 0.0) {
warnings.push(format!(
"SAR dump {:?}: no material with a density in its box; the SAR is zero",
d.name
));
}
out.push(SarSpec {
name: d.name.clone(),
id: d.id,
kind,
file_type: d.settings.file_type,
frequencies: d.settings.probe.frequencies.clone(),
cells,
sigma,
density,
});
}
out
}
fn cell_media(
model: &csx::ContinuousStructure,
index: &csx::structure::SpatialIndex<'_>,
cells: &CellBox,
n: usize,
) -> (Vec<[f64; 3]>, Vec<f64>) {
let lines = [0, 1, 2].map(|a| model.grid.lines(a));
let bg = model.background.kappa.value();
let (lo, c) = (cells.lo(), cells.cells());
let mut sigma = Vec::with_capacity(cells.num_cells());
let mut density = Vec::with_capacity(cells.num_cells());
let w = 1.0 / (n * n * n) as f64;
for i in 0..c[0] {
for j in 0..c[1] {
for k in 0..c[2] {
let cell = [lo[0] + i, lo[1] + j, lo[2] + k];
let mut s = [0.0; 3];
let mut r = 0.0;
for a in 0..n {
for b in 0..n {
for cc in 0..n {
let t = [a, b, cc];
let p: [f64; 3] = core::array::from_fn(|d| {
let (x0, x1) = (lines[d][cell[d]], lines[d][cell[d] + 1]);
x0 + (x1 - x0) * (t[d] as f64 + 0.5) / n as f64
});
let p = Vec3::new(p[0], p[1], p[2]);
match index.query(p).map(|h| &h.property.kind) {
Some(PropertyKind::Metal) => {}
Some(k) if let Some(m) = k.material() => {
let kap = m.values_at(MaterialQuantity::Kappa, p);
for d in 0..3 {
s[d] += w * kap[d];
}
r += w * m.density_at(p);
}
_ => {
for x in &mut s {
*x += w * bg;
}
}
}
}
}
}
sigma.push(s);
density.push(if r.is_finite() { r.max(0.0) } else { 0.0 });
}
}
}
(sigma, density)
}