use std::f64::consts::PI;
const C_LIGHT: f64 = 2.997_924_58e8;
#[derive(Debug, Clone)]
pub struct ValleyHallPhC {
pub lattice_constant_m: f64,
pub delta_epsilon: f64,
pub base_epsilon: f64,
}
impl ValleyHallPhC {
pub fn new(a_m: f64, delta_eps: f64, eps: f64) -> Self {
Self {
lattice_constant_m: a_m,
delta_epsilon: delta_eps,
base_epsilon: eps,
}
}
pub fn valley_chern_number_k(&self) -> f64 {
self.delta_epsilon.signum() * 0.5
}
pub fn valley_chern_number_kprime(&self) -> f64 {
-self.valley_chern_number_k()
}
pub fn interface_states_exist(&self) -> bool {
self.delta_epsilon.abs() > 0.0
}
pub fn band_gap_fraction(&self) -> f64 {
self.delta_epsilon.abs() / (2.0 * self.base_epsilon)
}
pub fn valley_polarization(&self, excitation_angle_rad: f64) -> f64 {
excitation_angle_rad.cos() * self.delta_epsilon.signum()
}
pub fn valley_refraction_angle(&self, incident_angle_rad: f64) -> f64 {
let fg = self.band_gap_fraction();
let n_ratio = (1.0 + fg / 2.0) / (1.0 - fg / 2.0);
let sin_r = incident_angle_rad.sin() * n_ratio;
sin_r.clamp(-1.0, 1.0).asin()
}
pub fn k_point_frequency_hz(&self) -> f64 {
let k_mag = 4.0 * PI / (3.0 * self.lattice_constant_m);
let n_eff = self.base_epsilon.sqrt();
C_LIGHT * k_mag / (2.0 * PI * n_eff)
}
pub fn berry_curvature_sign_at_k(&self) -> f64 {
self.delta_epsilon.signum()
}
}
#[derive(Debug, Clone)]
pub struct ValleyKinkState {
pub group_velocity_fraction: f64,
pub valley: i8,
pub propagation_direction: f64,
}
impl ValleyKinkState {
pub fn new(group_velocity_fraction: f64, valley: i8, propagation_direction: f64) -> Self {
Self {
group_velocity_fraction,
valley,
propagation_direction,
}
}
pub fn is_counter_propagating(&self, other: &ValleyKinkState) -> bool {
self.valley != other.valley
}
pub fn immune_to_sharp_corners(&self) -> bool {
true
}
pub fn is_right_propagating(&self) -> bool {
let angle = self.propagation_direction.rem_euclid(2.0 * PI);
!(PI / 2.0..=3.0 * PI / 2.0).contains(&angle)
}
pub fn transmission_at_60_bend(&self) -> f64 {
(1.0 - 0.1 * (1.0 - self.group_velocity_fraction.clamp(0.0, 1.0))).max(0.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valley_hall_phc_interface_states_with_delta_eps() {
let phc = ValleyHallPhC::new(500e-9, 0.5, 12.0);
assert!(phc.interface_states_exist());
}
#[test]
fn valley_hall_phc_no_interface_states_symmetric() {
let phc = ValleyHallPhC::new(500e-9, 0.0, 12.0);
assert!(!phc.interface_states_exist());
}
#[test]
fn valley_hall_phc_band_gap_fraction() {
let phc = ValleyHallPhC::new(500e-9, 1.0, 10.0);
let fg = phc.band_gap_fraction();
assert!((fg - 0.05).abs() < 1e-12, "fg = {fg}");
}
#[test]
fn valley_chern_numbers_opposite() {
let phc = ValleyHallPhC::new(500e-9, 0.5, 12.0);
let ck = phc.valley_chern_number_k();
let ck_prime = phc.valley_chern_number_kprime();
assert!(
(ck + ck_prime).abs() < 1e-12,
"C_v(K) + C_v(K') should be 0"
);
assert!((ck.abs() - 0.5).abs() < 1e-12, "|C_v(K)| should be 0.5");
}
#[test]
fn valley_chern_sign_flips_with_delta_eps() {
let phc_pos = ValleyHallPhC::new(500e-9, 0.5, 12.0);
let phc_neg = ValleyHallPhC::new(500e-9, -0.5, 12.0);
assert!(phc_pos.valley_chern_number_k() > 0.0);
assert!(phc_neg.valley_chern_number_k() < 0.0);
}
#[test]
fn valley_polarization_maximum_at_zero_angle() {
let phc = ValleyHallPhC::new(500e-9, 1.0, 12.0);
let p_zero = phc.valley_polarization(0.0).abs();
let p_ortho = phc.valley_polarization(PI / 2.0).abs();
assert!(p_zero > p_ortho, "Polarisation should be maximal at θ=0");
}
#[test]
fn valley_refraction_angle_positive_for_positive_incidence() {
let phc = ValleyHallPhC::new(500e-9, 0.5, 12.0);
let theta_r = phc.valley_refraction_angle(0.3);
assert!(
theta_r > 0.0,
"Refraction angle should be positive for positive incidence"
);
}
#[test]
fn k_point_frequency_positive() {
let phc = ValleyHallPhC::new(500e-9, 0.5, 12.0);
let fk = phc.k_point_frequency_hz();
assert!(fk > 0.0, "K-point frequency must be positive, got {fk}");
}
#[test]
fn berry_curvature_sign_at_k_matches_delta_eps() {
let phc_pos = ValleyHallPhC::new(500e-9, 0.5, 12.0);
let phc_neg = ValleyHallPhC::new(500e-9, -0.5, 12.0);
assert!(phc_pos.berry_curvature_sign_at_k() > 0.0);
assert!(phc_neg.berry_curvature_sign_at_k() < 0.0);
}
#[test]
fn valley_kink_counter_propagating() {
let k_mode = ValleyKinkState::new(0.7, 1, 0.0);
let kprime_mode = ValleyKinkState::new(0.7, -1, PI);
assert!(k_mode.is_counter_propagating(&kprime_mode));
assert!(!k_mode.is_counter_propagating(&ValleyKinkState::new(0.7, 1, PI)));
}
#[test]
fn valley_kink_immune_to_sharp_corners() {
let state = ValleyKinkState::new(0.7, 1, 0.0);
assert!(state.immune_to_sharp_corners());
}
#[test]
fn valley_kink_transmission_at_bend_near_unity() {
let state = ValleyKinkState::new(0.9, 1, 0.0);
let t = state.transmission_at_60_bend();
assert!(t > 0.8 && t <= 1.0, "T = {t}");
}
}