use crate::error::Result;
use crate::math::Vec3;
use crate::param::{ParameterSet, Scalar};
use super::weight::{Aniso, WeightFn, weight_vars};
#[derive(Debug, Clone, PartialEq)]
pub struct Material {
pub epsilon: Aniso,
pub mue: Aniso,
pub kappa: Aniso,
pub sigma: Aniso,
pub density: Scalar,
pub density_weight: WeightFn,
pub isotropic: bool,
}
impl Default for Material {
fn default() -> Self {
Material {
epsilon: Aniso::iso(1.0),
mue: Aniso::iso(1.0),
kappa: Aniso::iso(0.0),
sigma: Aniso::iso(0.0),
density: Scalar::new(0.0),
density_weight: WeightFn::default(),
isotropic: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MaterialQuantity {
Epsilon,
Mue,
Kappa,
Sigma,
}
impl MaterialQuantity {
pub const ALL: [MaterialQuantity; 4] = [
MaterialQuantity::Epsilon,
MaterialQuantity::Mue,
MaterialQuantity::Kappa,
MaterialQuantity::Sigma,
];
pub fn name(self) -> &'static str {
match self {
MaterialQuantity::Epsilon => "Epsilon",
MaterialQuantity::Mue => "Mue",
MaterialQuantity::Kappa => "Kappa",
MaterialQuantity::Sigma => "Sigma",
}
}
}
impl Material {
pub fn new(epsilon: f64, kappa: f64) -> Self {
let mut m = Material::default();
m.epsilon.set(epsilon);
m.kappa.set(kappa);
m
}
pub fn quantity(&self, q: MaterialQuantity) -> &Aniso {
match q {
MaterialQuantity::Epsilon => &self.epsilon,
MaterialQuantity::Mue => &self.mue,
MaterialQuantity::Kappa => &self.kappa,
MaterialQuantity::Sigma => &self.sigma,
}
}
pub fn quantity_mut(&mut self, q: MaterialQuantity) -> &mut Aniso {
match q {
MaterialQuantity::Epsilon => &mut self.epsilon,
MaterialQuantity::Mue => &mut self.mue,
MaterialQuantity::Kappa => &mut self.kappa,
MaterialQuantity::Sigma => &mut self.sigma,
}
}
#[inline]
fn axis(&self, dir: usize) -> usize {
if self.isotropic { 0 } else { dir.min(2) }
}
pub fn value_at(&self, q: MaterialQuantity, dir: usize, p: Vec3) -> f64 {
let a = self.quantity(q);
let d = self.axis(dir);
if a.weight[d].is_unity() {
a.base(d)
} else {
a.at_vars(d, &weight_vars(p))
}
}
pub fn values_at(&self, q: MaterialQuantity, p: Vec3) -> [f64; 3] {
let a = self.quantity(q);
if !a.is_weighted() {
return core::array::from_fn(|d| a.base(self.axis(d)));
}
let v = weight_vars(p);
core::array::from_fn(|d| a.at_vars(self.axis(d), &v))
}
pub fn base_value(&self, q: MaterialQuantity, dir: usize) -> f64 {
self.quantity(q).base(self.axis(dir))
}
pub fn density_at(&self, p: Vec3) -> f64 {
self.density.value() * self.density_weight.eval(p)
}
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
for q in MaterialQuantity::ALL {
self.quantity_mut(q).update(p)?;
}
self.density.update(p)?;
self.density_weight.update(p)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct LorentzPole {
pub eps_plasma_frequency: Aniso,
pub eps_lorentz_pole_frequency: Aniso,
pub eps_relax_time: Aniso,
pub mue_plasma_frequency: Aniso,
pub mue_lorentz_pole_frequency: Aniso,
pub mue_relax_time: Aniso,
}
impl LorentzPole {
fn quantities_mut(&mut self) -> [&mut Aniso; 6] {
[
&mut self.eps_plasma_frequency,
&mut self.eps_lorentz_pole_frequency,
&mut self.eps_relax_time,
&mut self.mue_plasma_frequency,
&mut self.mue_lorentz_pole_frequency,
&mut self.mue_relax_time,
]
}
pub fn zero() -> Self {
LorentzPole {
eps_plasma_frequency: Aniso::iso(0.0),
eps_lorentz_pole_frequency: Aniso::iso(0.0),
eps_relax_time: Aniso::iso(0.0),
mue_plasma_frequency: Aniso::iso(0.0),
mue_lorentz_pole_frequency: Aniso::iso(0.0),
mue_relax_time: Aniso::iso(0.0),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DebyePole {
pub eps_delta: Aniso,
pub eps_relax_time: Aniso,
}
impl Default for DebyePole {
fn default() -> Self {
DebyePole {
eps_delta: Aniso::iso(0.0),
eps_relax_time: Aniso::iso(0.0),
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct LorentzMaterial {
pub material: Material,
pub poles: Vec<LorentzPole>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct DebyeMaterial {
pub material: Material,
pub poles: Vec<DebyePole>,
}
impl LorentzMaterial {
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.material.update(p)?;
for pole in &mut self.poles {
pole.quantities_mut()
.into_iter()
.try_for_each(|q| q.update(p))?;
}
Ok(())
}
}
impl DebyeMaterial {
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.material.update(p)?;
for pole in &mut self.poles {
pole.eps_delta.update(p)?;
pole.eps_relax_time.update(p)?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct DiscMaterial {
pub material: Material,
pub file: String,
pub scale: Scalar,
pub attributes: Vec<(String, String)>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct ConductingSheet {
pub conductivity: Scalar,
pub thickness: Scalar,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Excitation {
pub number: u32,
pub enabled: bool,
pub excitation_type: i32,
pub excite: [Scalar; 3],
pub weight: [WeightFn; 3],
pub prop_dir: [Scalar; 3],
pub delay: Scalar,
pub frequency: Scalar,
}
impl Default for Excitation {
fn default() -> Self {
Excitation {
number: 0,
enabled: true,
excitation_type: 0,
excite: Default::default(),
weight: Default::default(),
prop_dir: Default::default(),
delay: Scalar::new(0.0),
frequency: Scalar::new(0.0),
}
}
}
impl Excitation {
pub fn amplitude_at(&self, dir: usize, p: Vec3) -> f64 {
self.excite[dir].value() * self.weight[dir].eval(p)
}
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.excite.iter_mut().try_for_each(|s| s.update(p))?;
self.weight.iter_mut().try_for_each(|w| w.update(p))?;
self.prop_dir.iter_mut().try_for_each(|s| s.update(p))?;
self.delay.update(p)?;
self.frequency.update(p)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProbeBox {
pub number: u32,
pub probe_type: i32,
pub weight: Scalar,
pub norm_dir: Option<i32>,
pub start_time: Scalar,
pub stop_time: Scalar,
pub frequencies: Vec<f64>,
}
impl Default for ProbeBox {
fn default() -> Self {
ProbeBox {
number: 0,
probe_type: 0,
weight: Scalar::new(1.0),
norm_dir: None,
start_time: Scalar::new(0.0),
stop_time: Scalar::new(0.0),
frequencies: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DumpBox {
pub probe: ProbeBox,
pub dump_type: i32,
pub dump_mode: i32,
pub file_type: i32,
pub sub_sampling: Option<[u32; 3]>,
pub opt_resolution: Option<[Scalar; 3]>,
pub multi_grid_level: i32,
}
impl Default for DumpBox {
fn default() -> Self {
DumpBox {
probe: ProbeBox::default(),
dump_type: 0,
dump_mode: 1,
file_type: 0,
sub_sampling: None,
opt_resolution: None,
multi_grid_level: 0,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LumpedElement {
pub direction: i32,
pub caps: bool,
pub r: Scalar,
pub c: Scalar,
pub l: Scalar,
pub le_type: Option<i32>,
}
impl Default for LumpedElement {
fn default() -> Self {
LumpedElement {
direction: 0,
caps: true,
r: Scalar::new(f64::NAN),
c: Scalar::new(f64::NAN),
l: Scalar::new(f64::NAN),
le_type: None,
}
}
}
impl LumpedElement {
pub(crate) fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.r.update(p)?;
self.c.update(p)?;
self.l.update(p)
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct AbsorbingBC {
pub normal_sign_positive: bool,
pub phase_velocity: Scalar,
pub abc_type: i32,
}