use std::f64::consts::PI;
const C_LIGHT: f64 = 2.997_924_58e8;
#[derive(Debug, Clone, Copy)]
pub struct PhotonicTimeCrystal {
pub eps0: f64,
pub delta_eps: f64,
pub omega_mod: f64,
pub loss_rate: f64,
}
impl PhotonicTimeCrystal {
pub fn base_index(&self) -> f64 {
self.eps0.sqrt()
}
pub fn modulation_depth(&self) -> f64 {
self.delta_eps / (2.0 * self.eps0)
}
pub fn resonant_omega0(&self) -> f64 {
self.omega_mod / 2.0
}
pub fn band_gap_width_rad(&self, omega0: f64) -> f64 {
self.modulation_depth() / 2.0 * omega0
}
pub fn momentum_gap_per_m(&self, omega0: f64) -> f64 {
self.band_gap_width_rad(omega0) / C_LIGHT
}
pub fn amplification_rate(&self, omega0: f64) -> f64 {
let coupling_sq = (self.modulation_depth() * omega0 / 4.0).powi(2);
let loss_half_sq = (self.loss_rate / 2.0).powi(2);
let discriminant = coupling_sq - loss_half_sq;
if discriminant <= 0.0 {
0.0
} else {
discriminant.sqrt()
}
}
pub fn is_above_threshold(&self, omega0: f64) -> bool {
self.amplification_rate(omega0) > 0.0
}
pub fn squeezing_factor_db(&self, omega0: f64, time_s: f64) -> f64 {
if self.is_above_threshold(omega0) {
return 0.0;
}
let loss_half_sq = (self.loss_rate / 2.0).powi(2);
let coupling_sq = (self.modulation_depth() * omega0 / 4.0).powi(2);
let gamma_sub = (loss_half_sq - coupling_sq).max(0.0).sqrt();
20.0 * gamma_sub * time_s / 10_f64.ln()
}
pub fn temporal_winding_number(&self, omega0: f64) -> i32 {
let gap_half = self.band_gap_width_rad(omega0) / 2.0;
let detuning = (omega0 - self.omega_mod / 2.0).abs();
if detuning < gap_half {
1
} else {
0
}
}
pub fn quasi_energy_splitting(&self, omega0: f64) -> f64 {
self.modulation_depth() * omega0 / 2.0
}
pub fn group_velocity(&self) -> f64 {
let n0 = self.base_index();
if n0 == 0.0 {
return C_LIGHT;
}
C_LIGHT / n0
}
}
#[derive(Debug, Clone, Copy)]
pub struct SpatiotemporalCrystal {
pub spatial_period_m: f64,
pub temporal_period_s: f64,
pub eps_00: f64,
pub eps_10: f64,
pub eps_01: f64,
pub eps_11: f64,
}
impl SpatiotemporalCrystal {
fn bragg_vector(&self) -> f64 {
if self.spatial_period_m == 0.0 {
return 0.0;
}
2.0 * PI / self.spatial_period_m
}
fn temporal_freq(&self) -> f64 {
if self.temporal_period_s == 0.0 {
return 0.0;
}
2.0 * PI / self.temporal_period_s
}
pub fn light_cone_tilt(&self) -> f64 {
let g = self.bragg_vector();
if g == 0.0 {
return 0.0;
}
self.temporal_freq() / g
}
pub fn nonreciprocal_bandwidth_hz(&self) -> f64 {
if self.eps_00 == 0.0 || self.spatial_period_m == 0.0 {
return 0.0;
}
let n0 = self.eps_00.sqrt();
let v_g = C_LIGHT / n0;
let nr_ratio = self.eps_11.abs() / self.eps_00;
nr_ratio * v_g / (2.0 * self.spatial_period_m)
}
pub fn isolation_ratio_db(&self) -> f64 {
if self.eps_00 == 0.0 {
return 0.0;
}
20.0 * (1.0 + self.eps_11.abs() / self.eps_00).log10()
}
pub fn total_modulation_depth(&self) -> f64 {
if self.eps_00 == 0.0 {
return 0.0;
}
(self.eps_10.powi(2) + self.eps_01.powi(2) + self.eps_11.powi(2)).sqrt() / self.eps_00
}
}
#[cfg(test)]
mod tests {
use super::*;
fn default_ptc() -> PhotonicTimeCrystal {
PhotonicTimeCrystal {
eps0: 2.25, delta_eps: 0.5,
omega_mod: 4e14, loss_rate: 1e10,
}
}
#[test]
fn ptc_above_threshold() {
let ptc = default_ptc();
let omega0 = ptc.omega_mod / 2.0;
assert!(
ptc.is_above_threshold(omega0),
"PTC should be above threshold at resonance"
);
}
#[test]
fn amplification_rate_positive_above_threshold() {
let ptc = default_ptc();
let omega0 = ptc.omega_mod / 2.0;
let gamma = ptc.amplification_rate(omega0);
assert!(
gamma > 0.0,
"amplification rate should be positive: {gamma}"
);
}
#[test]
fn squeezing_zero_above_threshold() {
let ptc = default_ptc();
let omega0 = ptc.omega_mod / 2.0;
let sq = ptc.squeezing_factor_db(omega0, 1e-9);
assert_eq!(sq, 0.0, "squeezing should be zero above threshold");
}
#[test]
fn squeezing_positive_below_threshold() {
let ptc = PhotonicTimeCrystal {
eps0: 2.25,
delta_eps: 1e-3, omega_mod: 4e14,
loss_rate: 1e12, };
let omega0 = ptc.omega_mod / 2.0;
let sq = ptc.squeezing_factor_db(omega0, 1e-9);
assert!(sq >= 0.0, "squeezing should be non-negative: {sq}");
}
#[test]
fn winding_number_inside_gap() {
let ptc = default_ptc();
let omega0 = ptc.omega_mod / 2.0; assert_eq!(ptc.temporal_winding_number(omega0), 1);
}
#[test]
fn winding_number_outside_gap() {
let ptc = default_ptc();
let omega0_far = ptc.omega_mod * 3.0; assert_eq!(ptc.temporal_winding_number(omega0_far), 0);
}
#[test]
fn base_index_correct() {
let ptc = default_ptc();
let n = ptc.base_index();
assert!(
(n - 1.5).abs() < 1e-10,
"base index should be √2.25 = 1.5: {n}"
);
}
#[test]
fn spatiotemporal_isolation_positive() {
let stc = SpatiotemporalCrystal {
spatial_period_m: 500e-9,
temporal_period_s: 1e-12,
eps_00: 2.25,
eps_10: 0.1,
eps_01: 0.1,
eps_11: 0.2,
};
let iso = stc.isolation_ratio_db();
assert!(iso > 0.0, "isolation should be positive: {iso}");
}
}