#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Symmetry {
None,
Even,
Odd,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MirrorPlane {
X,
Y,
Z,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Symmetry2d {
pub x_sym: Symmetry,
pub y_sym: Symmetry,
}
impl Symmetry2d {
pub fn even_even() -> Self {
Self {
x_sym: Symmetry::Even,
y_sym: Symmetry::Even,
}
}
pub fn odd_odd() -> Self {
Self {
x_sym: Symmetry::Odd,
y_sym: Symmetry::Odd,
}
}
pub fn none() -> Self {
Self {
x_sym: Symmetry::None,
y_sym: Symmetry::None,
}
}
pub fn reduction_factor(&self) -> usize {
let x = if self.x_sym != Symmetry::None { 2 } else { 1 };
let y = if self.y_sym != Symmetry::None { 2 } else { 1 };
x * y
}
pub fn apply_x(&self, value: f64) -> f64 {
match self.x_sym {
Symmetry::Even => value,
Symmetry::Odd => -value,
Symmetry::None => value,
}
}
pub fn apply_y(&self, value: f64) -> f64 {
match self.y_sym {
Symmetry::Even => value,
Symmetry::Odd => -value,
Symmetry::None => value,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PointGroup2d {
C1,
C2,
C4,
C6,
}
impl PointGroup2d {
pub fn reduction_factor(&self) -> usize {
match self {
PointGroup2d::C1 => 1,
PointGroup2d::C2 => 2,
PointGroup2d::C4 => 4,
PointGroup2d::C6 => 6,
}
}
pub fn ibz_fraction(&self) -> f64 {
1.0 / self.reduction_factor() as f64
}
pub fn rotation_angle_deg(&self) -> f64 {
match self {
PointGroup2d::C1 => 360.0,
PointGroup2d::C2 => 180.0,
PointGroup2d::C4 => 90.0,
PointGroup2d::C6 => 60.0,
}
}
}
pub fn expand_mirror_1d(half: &[f64], sym: Symmetry) -> Vec<f64> {
let sign = match sym {
Symmetry::Even => 1.0,
Symmetry::Odd => -1.0,
Symmetry::None => return half.to_vec(),
};
let n = half.len();
let mut full = Vec::with_capacity(2 * n);
full.extend_from_slice(half);
for i in (0..n).rev() {
full.push(sign * half[i]);
}
full
}
pub fn restrict_mirror_1d(full: &[f64]) -> Vec<f64> {
let n = full.len() / 2;
full[..n].to_vec()
}
pub fn enforce_x_symmetry_2d(field: &mut [f64], nx: usize, ny: usize, sym: Symmetry) {
if sym == Symmetry::None {
return;
}
let sign = if sym == Symmetry::Even { 1.0 } else { -1.0 };
for iy in 0..ny {
for ix in 0..nx / 2 {
let i_lo = ix * ny + iy;
let i_hi = (nx - 1 - ix) * ny + iy;
let avg = (field[i_lo] + sign * field[i_hi]) / 2.0;
field[i_lo] = avg;
field[i_hi] = sign * avg;
}
}
}
pub fn enforce_y_symmetry_2d(field: &mut [f64], nx: usize, ny: usize, sym: Symmetry) {
if sym == Symmetry::None {
return;
}
let sign = if sym == Symmetry::Even { 1.0 } else { -1.0 };
for ix in 0..nx {
for iy in 0..ny / 2 {
let i_lo = ix * ny + iy;
let i_hi = ix * ny + (ny - 1 - iy);
let avg = (field[i_lo] + sign * field[i_hi]) / 2.0;
field[i_lo] = avg;
field[i_hi] = sign * avg;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn symmetry2d_reduction_none() {
let s = Symmetry2d::none();
assert_eq!(s.reduction_factor(), 1);
}
#[test]
fn symmetry2d_reduction_one_axis() {
let s = Symmetry2d {
x_sym: Symmetry::Even,
y_sym: Symmetry::None,
};
assert_eq!(s.reduction_factor(), 2);
}
#[test]
fn symmetry2d_reduction_both_axes() {
let s = Symmetry2d::even_even();
assert_eq!(s.reduction_factor(), 4);
}
#[test]
fn expand_mirror_even() {
let half = vec![1.0, 2.0, 3.0];
let full = expand_mirror_1d(&half, Symmetry::Even);
assert_eq!(full, vec![1.0, 2.0, 3.0, 3.0, 2.0, 1.0]);
}
#[test]
fn expand_mirror_odd() {
let half = vec![1.0, 2.0, 3.0];
let full = expand_mirror_1d(&half, Symmetry::Odd);
assert_eq!(full, vec![1.0, 2.0, 3.0, -3.0, -2.0, -1.0]);
}
#[test]
fn expand_mirror_none_passthrough() {
let half = vec![1.0, 2.0, 3.0];
let full = expand_mirror_1d(&half, Symmetry::None);
assert_eq!(full, half);
}
#[test]
fn restrict_mirror_halves() {
let full = vec![1.0, 2.0, 3.0, 4.0];
let half = restrict_mirror_1d(&full);
assert_eq!(half, vec![1.0, 2.0]);
}
#[test]
fn point_group_c4_reduction() {
assert_eq!(PointGroup2d::C4.reduction_factor(), 4);
assert!((PointGroup2d::C4.ibz_fraction() - 0.25).abs() < 1e-10);
}
#[test]
fn point_group_c6_angle() {
assert!((PointGroup2d::C6.rotation_angle_deg() - 60.0).abs() < 1e-10);
}
#[test]
fn enforce_x_symmetry_even() {
let nx = 4;
let ny = 2;
let mut field = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
enforce_x_symmetry_2d(&mut field, nx, ny, Symmetry::Even);
assert!((field[0] - field[6]).abs() < 1e-10);
assert!((field[1] - field[7]).abs() < 1e-10);
}
#[test]
fn symmetry_apply_x_odd() {
let s = Symmetry2d {
x_sym: Symmetry::Odd,
y_sym: Symmetry::None,
};
assert_eq!(s.apply_x(3.0), -3.0);
}
}