fdtd-coremem/crates/cross/src/mat/compound.rs

265 lines
7.9 KiB
Rust

use crate::compound::enumerated::{DiscriminantCodable, Enum, EnumRequirements, Visitor};
use crate::compound::list::{Indexable, List2, List3};
use crate::compound::peano::{Peano, P0, P1, P2, P3};
use crate::real::Real;
use crate::vec::Vec3;
use crate::mat::{
AnisomorphicConductor,
Ferroxcube3R1MH,
IsomorphicConductor,
Material,
MBPgram,
MHPgram,
Vacuum,
};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
/// a material which can take on 1 of N types.
/// it's assumed that the first type in the material list is capable of storing the discriminant
/// (i.e. that it implements DiscriminantCodable).
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "fmt", derive(Debug))]
#[derive(Copy, Clone, Default, PartialEq)]
pub struct DiscrMat<Mats>(Enum<(), Mats>);
pub type DiscrMat2<M0, M1> = DiscrMat<List2<M0, M1>>;
pub type DiscrMat3<M0, M1, M2> = DiscrMat<List3<M0, M1, M2>>;
impl<Mats: Default> DiscrMat<Mats> {
fn new<P: Peano>(m: Mats::Element) -> Self
where
Mats: Indexable<P>,
Enum<(), Mats>: EnumRequirements,
{
let mut me = Self::default();
me.0.set::<P>(m);
me
}
}
/// invokes Material::conductivity on any Material
struct ConductivityDispatcher;
impl<P: Peano, R: Real, T: Material<R>> Visitor<P, T, Vec3<R>> for ConductivityDispatcher {
fn call(self, v: T) -> Vec3<R> {
v.conductivity()
}
}
/// invokes Material::move_b_vec on any Material
struct MoveBVecDispatcher<R> {
m: Vec3<R>,
target_b: Vec3<R>,
}
impl<R: Real, P: Peano, T: Material<R>> Visitor<P, T, Vec3<R>> for MoveBVecDispatcher<R> {
fn call(self, v: T) -> Vec3<R> {
v.move_b_vec(self.m, self.target_b)
}
}
/// invokes Into<T>::into on any variant
struct IntoDispatcher;
impl<P: Peano, T: Into<I>, I> Visitor<P, T, I> for IntoDispatcher {
fn call(self, v: T) -> I {
v.into()
}
}
impl<R, M0, M1> Into<GenericMaterial<R>> for DiscrMat2<M0, M1>
where
M0: DiscriminantCodable<P2> + Into<GenericMaterial<R>> + Copy,
M1: Into<GenericMaterial<R>> + Copy,
{
fn into(self) -> GenericMaterial<R> {
self.0.dispatch(IntoDispatcher)
}
}
impl<R: Real, M0, M1> Material<R> for DiscrMat2<M0, M1>
where
M0: DiscriminantCodable<P2> + Material<R> + Copy,
M1: Material<R> + Copy,
{
fn conductivity(&self) -> Vec3<R> {
self.0.dispatch(ConductivityDispatcher)
}
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
self.0.dispatch(MoveBVecDispatcher { m, target_b })
}
}
impl<R, M0, M1, M2> Into<GenericMaterial<R>> for DiscrMat3<M0, M1, M2>
where
M0: DiscriminantCodable<P3> + Into<GenericMaterial<R>> + Copy,
M1: Into<GenericMaterial<R>> + Copy,
M2: Into<GenericMaterial<R>> + Copy,
{
fn into(self) -> GenericMaterial<R> {
self.0.dispatch(IntoDispatcher)
}
}
impl<R: Real, M0, M1, M2> Material<R> for DiscrMat3<M0, M1, M2>
where
M0: DiscriminantCodable<P3> + Material<R> + Copy,
M1: Material<R> + Copy,
M2: Material<R> + Copy,
{
fn conductivity(&self) -> Vec3<R> {
self.0.dispatch(ConductivityDispatcher)
}
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
self.0.dispatch(MoveBVecDispatcher { m, target_b })
}
}
/// represents a Material which is either an isomorphic conductor, or some other Material `M1`
pub type IsoConductorOr<R, M1> = DiscrMat2<IsomorphicConductor<R>, M1>;
// XXX: can't do this for generic M, because that creates duplicate `From` impls for the
// IsomorphicConductor itself
impl<R: Real> From<Ferroxcube3R1MH> for IsoConductorOr<R, Ferroxcube3R1MH> {
fn from(mat: Ferroxcube3R1MH) -> Self {
IsoConductorOr::new::<P1>(mat)
}
}
impl<R: Real> From<IsomorphicConductor<R>> for IsoConductorOr<R, Ferroxcube3R1MH> {
fn from(mat: IsomorphicConductor<R>) -> Self {
IsoConductorOr::new::<P0>(mat)
}
}
/// muxes operations to either the conductor or the magnetic material
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "fmt", derive(Debug))]
#[derive(Copy, Clone, Default, PartialEq)]
pub struct DualMaterial<C, M> {
conductor: C,
magnetic: M,
}
impl<C, M> DualMaterial<C, M> {
pub fn new(conductor: C, magnetic: M) -> Self {
Self { conductor, magnetic }
}
}
impl<R: Real, C: Material<R>, M: Material<R>> Material<R> for DualMaterial<C, M> {
fn conductivity(&self) -> Vec3<R> {
self.conductor.conductivity()
}
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
self.magnetic.move_b_vec(m, target_b)
}
}
/// Material which can encode any of the well-known magnetic materials (or Vacuum)
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "fmt", derive(Debug))]
#[derive(Copy, Clone, PartialEq)]
pub struct GenericMagnetic<R>(DiscrMat3<MBPgram<R>, MHPgram<R>, Vacuum>);
impl<R: Real> Default for GenericMagnetic<R> {
fn default() -> Self {
// N.B.: the default is not the first variant.
// we order the variants specifically so that the first one can store the discriminant, but
// we NEED Vacuum to be the default.
Vacuum.into()
}
}
impl<R: Real> Material<R> for GenericMagnetic<R> {
fn conductivity(&self) -> Vec3<R> {
self.0.conductivity()
}
fn move_b_vec(&self, m: Vec3<R>, target_b: Vec3<R>) -> Vec3<R> {
self.0.move_b_vec(m, target_b)
}
}
impl<R: Real> From<MBPgram<R>> for GenericMagnetic<R> {
fn from(mat: MBPgram<R>) -> Self {
GenericMagnetic(DiscrMat3::new::<P0>(mat))
}
}
impl<R: Real> From<MHPgram<R>> for GenericMagnetic<R> {
fn from(mat: MHPgram<R>) -> Self {
GenericMagnetic(DiscrMat3::new::<P1>(mat))
}
}
impl<R: Real> From<Vacuum> for GenericMagnetic<R> {
fn from(mat: Vacuum) -> Self {
GenericMagnetic(DiscrMat3::new::<P2>(mat))
}
}
/// "Fully Generic" in that one can set both the conductivity,
/// and set any of the well-known magnetic materials, simultaneously.
pub type GenericMaterial<R> = DualMaterial<
AnisomorphicConductor<R>,
GenericMagnetic<R>,
>;
impl<R: Real> From<AnisomorphicConductor<R>> for GenericMaterial<R> {
fn from(mat: AnisomorphicConductor<R>) -> Self {
Self::new(mat, Default::default())
}
}
impl<R: Real> From<MBPgram<R>> for GenericMaterial<R> {
fn from(mat: MBPgram<R>) -> Self {
Self::new(Default::default(), mat.into())
}
}
impl<R: Real> From<MHPgram<R>> for GenericMaterial<R> {
fn from(mat: MHPgram<R>) -> Self {
Self::new(Default::default(), mat.into())
}
}
impl<R: Real> From<Vacuum> for GenericMaterial<R> {
fn from(mat: Vacuum) -> Self {
Self::new(Default::default(), mat.into())
}
}
impl<R: Real> From<IsomorphicConductor<R>> for GenericMaterial<R> {
fn from(mat: IsomorphicConductor<R>) -> Self {
let mat: AnisomorphicConductor<R> = mat.into();
mat.into()
}
}
impl<R: Real> From<Ferroxcube3R1MH> for GenericMaterial<R> {
fn from(mat: Ferroxcube3R1MH) -> Self {
let mat: MHPgram<R> = mat.into();
mat.into()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::mat::AnisomorphicConductor;
use crate::real::R32;
#[test]
fn iso_conductor_or_3r1() {
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = IsomorphicConductor::new(22.0f32).into();
assert!(c.conductivity() == Vec3::uniform(22.0f32));
let c: IsoConductorOr<f32, Ferroxcube3R1MH> = Ferroxcube3R1MH::new().into();
assert!(c.conductivity() == Vec3::zero());
}
#[test]
fn iso_conductor_or_aniso() {
type I = IsoConductorOr<R32, AnisomorphicConductor<R32>>;
let c = I::new::<P0>(IsomorphicConductor::new(22f32.cast()));
assert!(c.conductivity() == Vec3::uniform(22.0).cast());
let c = I::new::<P1>(AnisomorphicConductor::new(
Vec3::new(2.0, 3.0, 4.0).cast()
));
assert!(c.conductivity() == Vec3::new(2.0, 3.0, 4.0).cast());
}
}