#[derive(Debug, Clone)]
pub struct MurAbc1d {
pub coeff: f64,
pub e_prev_left: f64,
pub e_prev_right: f64,
pub e_prev_left1: f64,
pub e_prev_right1: f64,
}
impl MurAbc1d {
const C: f64 = 2.998e8;
pub fn new(dz: f64, dt: f64) -> Self {
let c_dt = Self::C * dt;
let coeff = (c_dt - dz) / (c_dt + dz);
Self {
coeff,
e_prev_left: 0.0,
e_prev_right: 0.0,
e_prev_left1: 0.0,
e_prev_right1: 0.0,
}
}
pub fn save(&mut self, ex: &[f64]) {
self.e_prev_left = ex[0];
self.e_prev_left1 = ex[1];
self.e_prev_right = *ex
.last()
.expect("ex slice must be non-empty for Mur ABC boundary");
self.e_prev_right1 = ex[ex.len() - 2];
}
pub fn apply(&self, ex: &mut [f64]) {
let n = ex.len();
ex[0] = self.e_prev_left1 + self.coeff * (ex[1] - self.e_prev_left);
ex[n - 1] = self.e_prev_right1 + self.coeff * (ex[n - 2] - self.e_prev_right);
}
}
#[derive(Debug, Clone)]
pub struct MurAbc2ndOrder1d {
pub c1: f64,
pub c2: f64,
pub e_left: [[f64; 3]; 2],
pub e_right: [[f64; 3]; 2],
}
impl MurAbc2ndOrder1d {
const C: f64 = 2.998e8;
pub fn new(dz: f64, dt: f64) -> Self {
let c_dt = Self::C * dt;
let q = c_dt / dz;
let c1 = (q - 1.0) / (q + 1.0);
let c2 = 2.0 * q / (q + 1.0);
Self {
c1,
c2,
e_left: [[0.0; 3]; 2],
e_right: [[0.0; 3]; 2],
}
}
pub fn save(&mut self, ex: &[f64]) {
let n = ex.len();
self.e_left[1] = self.e_left[0];
self.e_right[1] = self.e_right[0];
self.e_left[0] = [ex[0], ex[1], ex[2]];
self.e_right[0] = [ex[n - 1], ex[n - 2], ex[n - 3]];
}
pub fn apply(&self, ex: &mut [f64]) {
let n = ex.len();
ex[0] = -self.e_left[1][1]
+ self.c1 * (ex[1] + self.e_left[1][0])
+ self.c2 * (self.e_left[0][0] + self.e_left[0][1]);
ex[n - 1] = -self.e_right[1][1]
+ self.c1 * (ex[n - 2] + self.e_right[1][0])
+ self.c2 * (self.e_right[0][0] + self.e_right[0][1]);
}
}
pub struct FdtdMurTest {
pub ex: Vec<f64>,
pub hy: Vec<f64>,
pub dz: f64,
pub dt: f64,
pub mur: MurAbc1d,
pub step: usize,
}
impl FdtdMurTest {
const EPS0: f64 = 8.854e-12;
const MU0: f64 = 1.2566e-6;
const C: f64 = 2.998e8;
pub fn new(n: usize, dz: f64) -> Self {
let dt = 0.99 * dz / Self::C;
let mur = MurAbc1d::new(dz, dt);
Self {
ex: vec![0.0; n],
hy: vec![0.0; n],
dz,
dt,
mur,
step: 0,
}
}
pub fn advance(&mut self, src_i: usize, src_amp: f64) {
use std::f64::consts::PI;
self.mur.save(&self.ex);
let n = self.n();
for i in 0..n - 1 {
self.hy[i] -= self.dt / (Self::MU0 * self.dz) * (self.ex[i + 1] - self.ex[i]);
}
for i in 1..n {
self.ex[i] -= self.dt / (Self::EPS0 * self.dz) * (self.hy[i] - self.hy[i - 1]);
}
let t = self.step as f64 * self.dt;
let f0 = Self::C / (30.0 * self.dz);
self.ex[src_i] += src_amp * (2.0 * PI * f0 * t).sin();
self.mur.apply(&mut self.ex);
self.step += 1;
}
pub fn n(&self) -> usize {
self.ex.len()
}
pub fn total_energy(&self) -> f64 {
self.ex.iter().map(|e| e * e).sum::<f64>() * Self::EPS0 * self.dz
+ self.hy.iter().map(|h| h * h).sum::<f64>() * Self::MU0 * self.dz
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mur_coefficient_between_minus_one_and_one() {
let mur = MurAbc1d::new(10e-9, 0.99 * 10e-9 / 2.998e8);
assert!(
mur.coeff > -1.0 && mur.coeff < 1.0,
"coeff={:.4}",
mur.coeff
);
}
#[test]
fn mur_apply_after_save() {
let dz = 10e-9;
let dt = 0.99 * dz / 2.998e8;
let mut mur = MurAbc1d::new(dz, dt);
let mut ex = vec![1.0, 0.5, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.5];
mur.save(&ex);
for v in &mut ex {
*v *= 0.9;
}
mur.apply(&mut ex);
assert!(ex[0].is_finite());
assert!(ex[ex.len() - 1].is_finite());
}
#[test]
fn mur_2nd_order_coefficients() {
let dz = 10e-9;
let dt = 0.99 * dz / 2.998e8;
let abc = MurAbc2ndOrder1d::new(dz, dt);
assert!(abc.c1.is_finite());
assert!(abc.c2.is_finite());
}
#[test]
fn fdtd_mur_energy_bounded_with_source() {
let mut sim = FdtdMurTest::new(200, 10e-9);
for _ in 0..200 {
sim.advance(50, 1.0);
}
let max_e = sim
.ex
.iter()
.cloned()
.fold(0.0_f64, |a, b| a.abs().max(b.abs()));
assert!(max_e.is_finite(), "field blew up: {max_e:.2e}");
assert!(max_e < 1e6, "field too large (instability?): {max_e:.2e}");
}
#[test]
fn fdtd_mur_field_finite() {
let mut sim = FdtdMurTest::new(100, 5e-9);
for _ in 0..500 {
sim.advance(25, 1.0);
}
let max_e = sim
.ex
.iter()
.cloned()
.fold(0.0_f64, |a, b| a.abs().max(b.abs()));
assert!(max_e.is_finite() && max_e < 1e10, "max_e={max_e:.2e}");
}
#[test]
fn mur_2nd_order_save_and_apply() {
let dz = 10e-9;
let dt = 0.99 * dz / 2.998e8;
let mut abc = MurAbc2ndOrder1d::new(dz, dt);
let mut ex = vec![0.5, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.5];
abc.save(&ex);
abc.save(&ex); for v in &mut ex {
*v *= 0.5;
}
abc.apply(&mut ex);
assert!(ex[0].is_finite());
assert!(ex[ex.len() - 1].is_finite());
}
}