use nalgebra::DMatrix;
use num_complex::Complex;
use serde::{Deserialize, Serialize};
use crate::mesh::Port;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct SweepResult {
pub schema_version: u32,
pub ports: Vec<Port>,
pub frequencies_hz: Vec<f64>,
#[serde(with = "impedance_rows")]
pub impedance_ohm: Vec<DMatrix<Complex<f64>>>,
pub provenance: Provenance,
}
impl SweepResult {
pub const SCHEMA_VERSION: u32 = 1;
#[must_use]
pub fn without_timing(&self) -> Self {
let mut copy = self.clone();
copy.provenance.timing = None;
copy
}
pub fn resistance(&self, index: usize) -> DMatrix<f64> {
self.impedance_ohm[index].map(|z| z.re)
}
pub fn inductance(&self, index: usize) -> Option<DMatrix<f64>> {
let omega = std::f64::consts::TAU * self.frequencies_hz[index];
(omega != 0.0).then(|| self.impedance_ohm[index].map(|z| z.im / omega))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Provenance {
pub fasterhenry_version: String,
pub counts: Counts,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timing: Option<Timing>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Counts {
pub nodes: usize,
pub segments: usize,
pub filaments: usize,
pub meshes: usize,
pub internal_meshes: usize,
pub ports: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Timing {
pub threads: usize,
pub inductance_s: f64,
pub assembly_s: f64,
pub solve_s: f64,
pub total_s: f64,
}
mod impedance_rows {
use super::{Complex, DMatrix};
use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Serialize, Deserialize)]
struct Entry {
re: f64,
im: f64,
}
pub fn serialize<S: Serializer>(
matrices: &[DMatrix<Complex<f64>>],
serializer: S,
) -> Result<S::Ok, S::Error> {
let rows: Vec<Vec<Vec<Entry>>> = matrices
.iter()
.map(|m| {
m.row_iter()
.map(|row| row.iter().map(|z| Entry { re: z.re, im: z.im }).collect())
.collect()
})
.collect();
rows.serialize(serializer)
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<DMatrix<Complex<f64>>>, D::Error> {
let matrices = Vec::<Vec<Vec<Entry>>>::deserialize(deserializer)?;
matrices
.into_iter()
.map(|rows| {
let order = rows.len();
if rows.iter().any(|row| row.len() != order) {
return Err(D::Error::custom("impedance matrix is not square"));
}
Ok(DMatrix::from_fn(order, order, |i, j| {
Complex::new(rows[i][j].re, rows[i][j].im)
}))
})
.collect()
}
}