#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymmetryBc {
Pec,
Pmc,
}
impl SymmetryBc {
pub fn mirror_sign(&self) -> f64 {
match self {
SymmetryBc::Pec => -1.0,
SymmetryBc::Pmc => 1.0,
}
}
pub fn apply_1d(&self, field: &mut [f64], i_wall: usize) {
match self {
SymmetryBc::Pec => {
if i_wall < field.len() {
field[i_wall] = 0.0;
}
}
SymmetryBc::Pmc => {
}
}
}
pub fn apply_both_1d(&self, field: &mut [f64], i_left: usize, i_right: usize) {
self.apply_1d(field, i_left);
self.apply_1d(field, i_right);
}
}
#[derive(Debug, Clone, Copy)]
pub struct SymmetryBc2d {
pub x_left: Option<SymmetryBc>,
pub x_right: Option<SymmetryBc>,
pub y_bottom: Option<SymmetryBc>,
pub y_top: Option<SymmetryBc>,
}
impl SymmetryBc2d {
pub fn none() -> Self {
Self {
x_left: None,
x_right: None,
y_bottom: None,
y_top: None,
}
}
pub fn pec_box() -> Self {
Self {
x_left: Some(SymmetryBc::Pec),
x_right: Some(SymmetryBc::Pec),
y_bottom: Some(SymmetryBc::Pec),
y_top: Some(SymmetryBc::Pec),
}
}
pub fn even_x_pec_y() -> Self {
Self {
x_left: Some(SymmetryBc::Pmc),
x_right: Some(SymmetryBc::Pmc),
y_bottom: Some(SymmetryBc::Pec),
y_top: Some(SymmetryBc::Pec),
}
}
pub fn apply_ez(&self, ez: &mut [f64], nx: usize, ny: usize) {
if let Some(bc) = self.x_left {
for iy in 0..ny {
bc.apply_1d(ez, iy); }
}
if let Some(bc) = self.x_right {
for iy in 0..ny {
bc.apply_1d(ez, (nx - 1) * ny + iy);
}
}
if let Some(bc) = self.y_bottom {
for ix in 0..nx {
bc.apply_1d(ez, ix * ny);
}
}
if let Some(bc) = self.y_top {
for ix in 0..nx {
bc.apply_1d(ez, ix * ny + (ny - 1));
}
}
}
pub fn n_active_walls(&self) -> usize {
[self.x_left, self.x_right, self.y_bottom, self.y_top]
.iter()
.filter(|b| b.is_some())
.count()
}
}
pub fn apply_mirror_bc_1d(field: &mut [f64], i_mirror: usize, bc: SymmetryBc) {
let sign = bc.mirror_sign();
let n = field.len();
let max_k = i_mirror.min(n - 1 - i_mirror);
for k in 1..=max_k {
let lo = i_mirror - k;
let hi = i_mirror + k;
if hi < n {
field[lo] = sign * field[hi];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pec_zero_at_wall() {
let mut field = vec![1.0, 2.0, 3.0, 4.0, 5.0];
SymmetryBc::Pec.apply_1d(&mut field, 2);
assert_eq!(field[2], 0.0);
assert_eq!(field[0], 1.0); }
#[test]
fn pmc_noop_on_field() {
let mut field = vec![1.0, 2.0, 3.0];
SymmetryBc::Pmc.apply_1d(&mut field, 1);
assert_eq!(field[1], 2.0); }
#[test]
fn pec_mirror_sign_negative() {
assert_eq!(SymmetryBc::Pec.mirror_sign(), -1.0);
}
#[test]
fn pmc_mirror_sign_positive() {
assert_eq!(SymmetryBc::Pmc.mirror_sign(), 1.0);
}
#[test]
fn symmetry_bc_2d_none_no_walls() {
let s = SymmetryBc2d::none();
assert_eq!(s.n_active_walls(), 0);
}
#[test]
fn symmetry_bc_2d_pec_box_4_walls() {
let s = SymmetryBc2d::pec_box();
assert_eq!(s.n_active_walls(), 4);
}
#[test]
fn apply_ez_pec_zeros_boundary() {
let nx = 4;
let ny = 4;
let mut ez = vec![1.0_f64; nx * ny];
let bc = SymmetryBc2d::pec_box();
bc.apply_ez(&mut ez, nx, ny);
for (iy, val) in ez.iter().enumerate().take(ny) {
assert_eq!(*val, 0.0, "x-left boundary at iy={iy}");
}
for ix in 0..nx {
assert_eq!(ez[ix * ny], 0.0, "y-bottom boundary at ix={ix}");
}
}
#[test]
fn mirror_bc_1d_even() {
let mut field = vec![0.0, 0.0, 0.0, 1.0, 2.0];
apply_mirror_bc_1d(&mut field, 2, SymmetryBc::Pmc);
assert_eq!(field[1], 1.0);
assert_eq!(field[0], 2.0);
}
#[test]
fn mirror_bc_1d_odd() {
let mut field = vec![0.0, 0.0, 0.0, 1.0, 2.0];
apply_mirror_bc_1d(&mut field, 2, SymmetryBc::Pec);
assert_eq!(field[1], -1.0);
assert_eq!(field[0], -2.0);
}
}