mod kinds;
mod types;
mod weight;
pub use kinds::*;
pub use types::{
AbcType, Axis, DumpFileType, DumpMode, DumpType, ExcitationType, LumpedElementType, ProbeType,
};
pub use weight::{Aniso, WEIGHT_VARS, WeightFn, weight_vars};
use crate::error::Result;
use crate::param::{ParameterSet, Scalar};
use crate::primitives::Primitive;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Rgba {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Default for Rgba {
fn default() -> Self {
Rgba {
r: 0,
g: 0,
b: 0,
a: 255,
}
}
}
impl Rgba {
pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Rgba { r, g, b, a }
}
pub fn from_seed(seed: u32) -> Self {
let mut x = seed.wrapping_mul(0x9E37_79B9).wrapping_add(0x7F4A_7C15);
x ^= x >> 16;
x = x.wrapping_mul(0x85EB_CA6B);
x ^= x >> 13;
Rgba {
r: x as u8,
g: (x >> 8) as u8,
b: (x >> 16) as u8,
a: 255,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum PropertyType {
Unknown = 0x0001,
Material = 0x0002,
Metal = 0x0004,
Excitation = 0x0008,
ProbeBox = 0x0010,
DumpBox = 0x0020,
LorentzMaterial = 0x0040,
DebyeMaterial = 0x0080,
DiscMaterial = 0x0100,
ConductingSheet = 0x0200,
LumpedElement = 0x0400,
AbsorbingBC = 0x0800,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeMask(pub u32);
impl TypeMask {
pub const ANY: TypeMask = TypeMask(u32::MAX);
pub const NONE: TypeMask = TypeMask(0);
pub const MATERIALS: TypeMask = TypeMask(
PropertyType::Material as u32
| PropertyType::LorentzMaterial as u32
| PropertyType::DebyeMaterial as u32
| PropertyType::DiscMaterial as u32,
);
pub const PHYSICAL: TypeMask = TypeMask(
TypeMask::MATERIALS.0
| PropertyType::Metal as u32
| PropertyType::ConductingSheet as u32
| PropertyType::LumpedElement as u32,
);
pub const fn of(t: PropertyType) -> TypeMask {
TypeMask(t as u32)
}
pub const fn contains(self, t: PropertyType) -> bool {
self.0 & t as u32 != 0
}
}
impl From<PropertyType> for TypeMask {
fn from(t: PropertyType) -> Self {
TypeMask::of(t)
}
}
impl core::ops::BitOr for TypeMask {
type Output = TypeMask;
fn bitor(self, o: TypeMask) -> TypeMask {
TypeMask(self.0 | o.0)
}
}
impl core::ops::BitOr<PropertyType> for TypeMask {
type Output = TypeMask;
fn bitor(self, o: PropertyType) -> TypeMask {
TypeMask(self.0 | o as u32)
}
}
impl core::ops::BitOrAssign for TypeMask {
fn bitor_assign(&mut self, o: TypeMask) {
self.0 |= o.0;
}
}
impl core::ops::BitOrAssign<PropertyType> for TypeMask {
fn bitor_assign(&mut self, o: PropertyType) {
self.0 |= o as u32;
}
}
impl core::ops::BitOr for PropertyType {
type Output = TypeMask;
fn bitor(self, o: PropertyType) -> TypeMask {
TypeMask(self as u32 | o as u32)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyKind {
Unknown {
property: String,
element: Option<String>,
},
Material(Material),
Metal,
Excitation(Excitation),
ProbeBox(ProbeBox),
DumpBox(DumpBox),
LorentzMaterial(LorentzMaterial),
DebyeMaterial(DebyeMaterial),
DiscMaterial(DiscMaterial),
ConductingSheet(ConductingSheet),
LumpedElement(LumpedElement),
AbsorbingBC(AbsorbingBC),
}
impl PropertyKind {
pub fn property_type(&self) -> PropertyType {
match self {
PropertyKind::Unknown { .. } => PropertyType::Unknown,
PropertyKind::Material(_) => PropertyType::Material,
PropertyKind::Metal => PropertyType::Metal,
PropertyKind::Excitation(_) => PropertyType::Excitation,
PropertyKind::ProbeBox(_) => PropertyType::ProbeBox,
PropertyKind::DumpBox(_) => PropertyType::DumpBox,
PropertyKind::LorentzMaterial(_) => PropertyType::LorentzMaterial,
PropertyKind::DebyeMaterial(_) => PropertyType::DebyeMaterial,
PropertyKind::DiscMaterial(_) => PropertyType::DiscMaterial,
PropertyKind::ConductingSheet(_) => PropertyType::ConductingSheet,
PropertyKind::LumpedElement(_) => PropertyType::LumpedElement,
PropertyKind::AbsorbingBC(_) => PropertyType::AbsorbingBC,
}
}
pub fn type_name(&self) -> &'static str {
match self {
PropertyKind::Unknown { .. } => "Unknown",
PropertyKind::Material(_) => "Material",
PropertyKind::Metal => "Metal",
PropertyKind::Excitation(_) => "Excitation",
PropertyKind::ProbeBox(_) => "ProbeBox",
PropertyKind::DumpBox(_) => "DumpBox",
PropertyKind::LorentzMaterial(_) => "LorentzMaterial",
PropertyKind::DebyeMaterial(_) => "DebyeMaterial",
PropertyKind::DiscMaterial(_) => "DiscMaterial",
PropertyKind::ConductingSheet(_) => "ConductingSheet",
PropertyKind::LumpedElement(_) => "LumpedElement",
PropertyKind::AbsorbingBC(_) => "AbsorbingBC",
}
}
pub fn material(&self) -> Option<&Material> {
match self {
PropertyKind::Material(m) => Some(m),
PropertyKind::LorentzMaterial(m) => Some(&m.material),
PropertyKind::DebyeMaterial(m) => Some(&m.material),
PropertyKind::DiscMaterial(m) => Some(&m.material),
_ => None,
}
}
pub fn material_mut(&mut self) -> Option<&mut Material> {
match self {
PropertyKind::Material(m) => Some(m),
PropertyKind::LorentzMaterial(m) => Some(&mut m.material),
PropertyKind::DebyeMaterial(m) => Some(&mut m.material),
PropertyKind::DiscMaterial(m) => Some(&mut m.material),
_ => None,
}
}
fn update(&mut self, p: &ParameterSet) -> Result<()> {
match self {
PropertyKind::Material(m) => m.update(p),
PropertyKind::LorentzMaterial(m) => m.update(p),
PropertyKind::DebyeMaterial(m) => m.update(p),
PropertyKind::DiscMaterial(m) => {
m.material.update(p)?;
m.scale.update(p)
}
PropertyKind::ConductingSheet(s) => {
s.conductivity.update(p)?;
s.thickness.update(p)
}
PropertyKind::Excitation(e) => e.update(p),
PropertyKind::ProbeBox(b) => update_probe(b, p),
PropertyKind::DumpBox(d) => {
update_probe(&mut d.probe, p)?;
if let Some(r) = &mut d.opt_resolution {
r.iter_mut().try_for_each(|s| s.update(p))?;
}
Ok(())
}
PropertyKind::LumpedElement(l) => l.update(p),
PropertyKind::AbsorbingBC(a) => a.phase_velocity.update(p),
PropertyKind::Unknown { .. } | PropertyKind::Metal => Ok(()),
}
}
}
fn update_probe(b: &mut ProbeBox, p: &ParameterSet) -> Result<()> {
b.weight.update(p)?;
b.start_time.update(p)?;
b.stop_time.update(p)
}
#[derive(Debug, Clone, PartialEq)]
pub struct Property {
pub(crate) id: u32,
pub name: String,
pub fill_color: Rgba,
pub edge_color: Rgba,
pub visible: bool,
pub kind: PropertyKind,
pub primitives: Vec<Primitive>,
pub attributes: Vec<(String, String)>,
pub extra_attributes: Vec<(String, String)>,
pub extra_elements: Vec<crate::io::dom::Element>,
}
impl Property {
pub fn new(name: impl Into<String>, kind: PropertyKind) -> Self {
Property {
id: 0,
name: name.into(),
fill_color: Rgba::default(),
edge_color: Rgba::default(),
visible: true,
kind,
primitives: Vec::new(),
attributes: Vec::new(),
extra_attributes: Vec::new(),
extra_elements: Vec::new(),
}
}
pub fn material(name: impl Into<String>, m: Material) -> Self {
Property::new(name, PropertyKind::Material(m))
}
pub fn metal(name: impl Into<String>) -> Self {
Property::new(name, PropertyKind::Metal)
}
pub fn excitation(name: impl Into<String>, e: Excitation) -> Self {
Property::new(name, PropertyKind::Excitation(e))
}
pub fn probe(name: impl Into<String>, p: ProbeBox) -> Self {
Property::new(name, PropertyKind::ProbeBox(p))
}
pub fn dump(name: impl Into<String>, d: DumpBox) -> Self {
Property::new(name, PropertyKind::DumpBox(d))
}
pub fn lumped_element(name: impl Into<String>, l: LumpedElement) -> Self {
Property::new(name, PropertyKind::LumpedElement(l))
}
pub fn conducting_sheet(
name: impl Into<String>,
conductivity: impl Into<Scalar>,
thickness: impl Into<Scalar>,
) -> Self {
Property::new(
name,
PropertyKind::ConductingSheet(ConductingSheet::new(conductivity, thickness)),
)
}
pub fn lorentz_material(name: impl Into<String>, m: LorentzMaterial) -> Self {
Property::new(name, PropertyKind::LorentzMaterial(m))
}
pub fn debye_material(name: impl Into<String>, m: DebyeMaterial) -> Self {
Property::new(name, PropertyKind::DebyeMaterial(m))
}
pub fn disc_material(name: impl Into<String>, m: DiscMaterial) -> Self {
Property::new(name, PropertyKind::DiscMaterial(m))
}
pub fn absorbing_bc(name: impl Into<String>, a: AbsorbingBC) -> Self {
Property::new(name, PropertyKind::AbsorbingBC(a))
}
#[must_use]
pub fn with_color(mut self, c: Rgba) -> Self {
self.fill_color = c;
self.edge_color = c;
self
}
pub fn id(&self) -> u32 {
self.id
}
pub fn property_type(&self) -> PropertyType {
self.kind.property_type()
}
#[must_use]
pub fn with_primitive(mut self, p: impl Into<Primitive>) -> Self {
self.primitives.push(p.into());
self
}
pub fn add_primitive(&mut self, p: impl Into<Primitive>) -> &mut Primitive {
let i = self.primitives.len();
self.primitives.push(p.into());
&mut self.primitives[i]
}
pub fn attribute(&self, name: &str) -> Option<&str> {
self.attributes
.iter()
.find(|(k, _)| k == name)
.map(|(_, v)| v.as_str())
}
pub fn set_attribute(&mut self, name: impl Into<String>, value: impl Into<String>) {
let (name, value) = (name.into(), value.into());
match self.attributes.iter_mut().find(|(k, _)| *k == name) {
Some(e) => e.1 = value,
None => self.attributes.push((name, value)),
}
}
pub fn update(&mut self, p: &ParameterSet) -> Result<()> {
self.kind.update(p).map_err(|e| context(e, &self.name))?;
for prim in &mut self.primitives {
prim.update(p).map_err(|e| context(e, &self.name))?;
}
Ok(())
}
}
fn context(e: crate::Error, name: &str) -> crate::Error {
match e {
crate::Error::Invalid(m) => crate::Error::Invalid(format!("property {name:?}: {m}")),
other => other,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::math::Vec3;
#[test]
fn material_values() {
let mut m = Material::new(4.0, 0.1);
assert_eq!(m.value_at(MaterialQuantity::Epsilon, 2, Vec3::ZERO), 4.0);
m.epsilon.set_weight("1+x");
assert_eq!(
m.value_at(MaterialQuantity::Epsilon, 1, Vec3::new(1.0, 0.0, 0.0)),
8.0
);
m.isotropic = false;
m.epsilon.value[2].set(2.0);
assert_eq!(
m.values_at(MaterialQuantity::Epsilon, Vec3::ZERO),
[4.0, 4.0, 2.0]
);
m.isotropic = true;
assert_eq!(
m.values_at(MaterialQuantity::Epsilon, Vec3::ZERO),
[4.0, 4.0, 4.0]
);
}
#[test]
fn constructors() {
let e = Property::excitation(
"port",
Excitation::new([0.0, 0.0, 1.0])
.with_type(ExcitationType::EFieldHard)
.with_number(2),
);
let PropertyKind::Excitation(x) = &e.kind else {
panic!()
};
assert_eq!(
(x.excitation_type, x.number, x.excite[2].value()),
(ExcitationType::EFieldHard, 2, 1.0)
);
assert!(x.enabled);
let l = LumpedElement::resistor(Axis::Z, 50.0).with_c(1e-12);
assert!(l.l.value().is_nan() && l.c.value() == 1e-12);
assert_eq!(
Property::lumped_element("r", l.clone()).kind,
PropertyKind::LumpedElement(l)
);
let p = Property::probe(
"u",
ProbeBox::new(ProbeType::Voltage)
.with_weight(-1.0)
.with_frequencies([1e9]),
);
assert_eq!(p.property_type(), PropertyType::ProbeBox);
assert_eq!(
Property::dump("et", DumpBox::new(DumpType::EFieldFd)).property_type(),
PropertyType::DumpBox
);
let c = Property::conducting_sheet("cu", 5.8e7, 35e-6).with_color(Rgba::new(1, 2, 3, 255));
assert_eq!(c.fill_color, c.edge_color);
let pt: crate::ScalarPoint = ["w/2", "1", "h"].into();
assert!(pt.0[0].is_expr() && !pt.0[1].is_expr());
}
#[test]
fn masks() {
assert!(TypeMask::MATERIALS.contains(PropertyType::DebyeMaterial));
assert!(!TypeMask::MATERIALS.contains(PropertyType::Metal));
assert!((PropertyType::Metal | PropertyType::Excitation).contains(PropertyType::Metal));
let p = Property::metal("pec");
assert_eq!(p.kind.type_name(), "Metal");
assert!(TypeMask::PHYSICAL.contains(p.property_type()));
}
}