use std::path::Path;
use crate::engine::{FieldSampler, FreqDump, RunOutcome, Snapshot, VtrSeries};
use crate::post::{Complex64, dft};
use crate::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ProbeKind {
Voltage,
Current,
EField,
HField,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ProbeValues {
Scalar(Vec<f64>),
Vector(Vec<[f64; 3]>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProbeResult {
pub(crate) name: String,
pub(crate) id: u32,
pub(crate) number: u32,
pub(crate) kind: ProbeKind,
pub(crate) frequencies: Vec<f64>,
pub(crate) dt: f64,
pub(crate) times: Vec<f64>,
pub(crate) values: ProbeValues,
}
impl ProbeResult {
pub fn name(&self) -> &str {
&self.name
}
pub fn id(&self) -> u32 {
self.id
}
pub fn number(&self) -> u32 {
self.number
}
pub fn kind(&self) -> ProbeKind {
self.kind
}
pub fn frequencies(&self) -> &[f64] {
&self.frequencies
}
pub fn dt(&self) -> f64 {
self.dt
}
pub fn times(&self) -> &[f64] {
&self.times
}
pub fn values(&self) -> &ProbeValues {
&self.values
}
pub fn scalar(&self) -> Option<&[f64]> {
match &self.values {
ProbeValues::Scalar(v) => Some(v),
ProbeValues::Vector(_) => None,
}
}
pub fn vector(&self) -> Option<&[[f64; 3]]> {
match &self.values {
ProbeValues::Vector(v) => Some(v),
ProbeValues::Scalar(_) => None,
}
}
fn t0(&self) -> f64 {
self.times.first().copied().unwrap_or(0.0)
}
pub fn dft(&self, freqs: &[f64]) -> Result<Vec<Complex64>> {
let v = self.scalar().ok_or_else(|| {
Error::Invalid(format!("probe {:?} records a field vector", self.name))
})?;
if v.is_empty() {
return Err(Error::Invalid(format!(
"probe {:?} has no samples",
self.name
)));
}
Ok(dft(self.t0(), self.dt, v, freqs))
}
pub fn dft_vector(&self, freqs: &[f64]) -> Result<Vec<[Complex64; 3]>> {
let v = self
.vector()
.ok_or_else(|| Error::Invalid(format!("probe {:?} records a scalar", self.name)))?;
if v.is_empty() {
return Err(Error::Invalid(format!(
"probe {:?} has no samples",
self.name
)));
}
let comp: Vec<Vec<Complex64>> = (0..3)
.map(|c| {
let x: Vec<f64> = v.iter().map(|s| s[c]).collect();
dft(self.t0(), self.dt, &x, freqs)
})
.collect();
Ok((0..freqs.len())
.map(|k| [comp[0][k], comp[1][k], comp[2][k]])
.collect())
}
pub fn spectrum(&self) -> Result<Vec<Complex64>> {
self.dft(&self.frequencies)
}
}
#[derive(Debug, Clone)]
pub enum DumpData {
Time {
sampler: FieldSampler,
snapshots: Vec<Snapshot>,
},
Freq(FreqDump),
}
#[derive(Debug, Clone)]
pub struct DumpResult {
pub(crate) name: String,
pub(crate) id: u32,
pub(crate) data: DumpData,
}
impl DumpResult {
pub fn name(&self) -> &str {
&self.name
}
pub fn id(&self) -> u32 {
self.id
}
pub fn data(&self) -> &DumpData {
&self.data
}
pub fn sampler(&self) -> &FieldSampler {
match &self.data {
DumpData::Time { sampler, .. } => sampler,
DumpData::Freq(f) => f.sampler(),
}
}
pub fn snapshots(&self) -> Option<&[Snapshot]> {
match &self.data {
DumpData::Time { snapshots, .. } => Some(snapshots),
DumpData::Freq(_) => None,
}
}
pub fn freq(&self) -> Option<&FreqDump> {
match &self.data {
DumpData::Freq(f) => Some(f),
DumpData::Time { .. } => None,
}
}
pub fn write_vtr(&self, dir: impl AsRef<Path>, basename: &str) -> Result<()> {
match &self.data {
DumpData::Time { sampler, snapshots } => {
use crate::engine::DumpSink;
let mut series = VtrSeries::new(dir.as_ref(), basename);
for s in snapshots {
series.snapshot(sampler, s.clone())?;
}
Ok(())
}
DumpData::Freq(f) => f.write_vtr(dir, basename),
}
}
}
#[derive(Debug, Clone)]
pub struct Results {
pub(crate) outcome: Option<RunOutcome>,
pub(crate) dt: f64,
pub(crate) probes: Vec<ProbeResult>,
pub(crate) lumped: Vec<ProbeResult>,
pub(crate) dumps: Vec<DumpResult>,
}
impl Results {
pub fn outcome(&self) -> Option<&RunOutcome> {
self.outcome.as_ref()
}
pub fn dt(&self) -> f64 {
self.dt
}
pub fn probes(&self) -> &[ProbeResult] {
&self.probes
}
pub fn probe(&self, name: &str) -> Option<&ProbeResult> {
self.probes.iter().find(|p| p.name == name)
}
pub fn probe_by_id(&self, id: csx::PropertyId) -> Option<&ProbeResult> {
self.probes.iter().find(|p| p.id == id.get())
}
pub fn lumped_current(&self, name: &str) -> Option<&ProbeResult> {
self.lumped.iter().find(|p| p.name == name)
}
pub fn lumped_currents(&self) -> &[ProbeResult] {
&self.lumped
}
pub fn dumps(&self) -> &[DumpResult] {
&self.dumps
}
pub fn dump(&self, name: &str) -> Option<&DumpResult> {
self.dumps.iter().find(|d| d.name == name)
}
pub fn dump_by_id(&self, id: csx::PropertyId) -> Option<&DumpResult> {
self.dumps.iter().find(|d| d.id == id.get())
}
}