use crate::error::OxiPhotonError;
const HBAR: f64 = 1.054_571_817e-34;
const KB: f64 = 1.380_649e-23;
#[derive(Debug, Clone)]
pub struct OptomechanicalCavity {
pub omega_c: f64,
pub kappa: f64,
pub kappa_ex: f64,
pub omega_m: f64,
pub gamma_m: f64,
pub mass_kg: f64,
pub g0: f64,
pub zero_point_motion: f64,
}
impl OptomechanicalCavity {
pub fn new(
omega_c: f64,
kappa: f64,
kappa_ex: f64,
omega_m: f64,
gamma_m: f64,
mass_kg: f64,
g0: f64,
) -> Self {
let x_zpf = (HBAR / (2.0 * mass_kg * omega_m)).sqrt();
Self {
omega_c,
kappa,
kappa_ex,
omega_m,
gamma_m,
mass_kg,
g0,
zero_point_motion: x_zpf,
}
}
pub fn zero_point_motion(&self) -> f64 {
(HBAR / (2.0 * self.mass_kg * self.omega_m)).sqrt()
}
pub fn coupling_rate_per_m(&self) -> f64 {
let x_zpf = self.zero_point_motion();
self.g0 / x_zpf
}
pub fn enhanced_coupling(&self, n_bar_photons: f64) -> f64 {
self.g0 * n_bar_photons.sqrt()
}
pub fn cooperativity(&self, n_bar_photons: f64) -> f64 {
let g = self.enhanced_coupling(n_bar_photons);
4.0 * g * g / (self.kappa * self.gamma_m)
}
pub fn optical_damping(&self, detuning: f64, n_bar: f64) -> f64 {
let g2 = self.enhanced_coupling(n_bar).powi(2);
let kappa_half = self.kappa / 2.0;
let kh2 = kappa_half * kappa_half;
let delta_anti_stokes = detuning + self.omega_m;
let gamma_cooling = g2 * self.kappa / (kh2 + delta_anti_stokes * delta_anti_stokes);
let delta_stokes = detuning - self.omega_m;
let gamma_heating = g2 * self.kappa / (kh2 + delta_stokes * delta_stokes);
gamma_cooling - gamma_heating
}
pub fn optical_spring_shift(&self, detuning: f64, n_bar: f64) -> f64 {
let g2 = self.enhanced_coupling(n_bar).powi(2);
let kh2 = (self.kappa / 2.0).powi(2);
let delta_minus = detuning - self.omega_m;
let delta_plus = detuning + self.omega_m;
g2 * (-delta_minus / (kh2 + delta_minus * delta_minus)
+ delta_plus / (kh2 + delta_plus * delta_plus))
}
pub fn sideband_resolution(&self) -> f64 {
self.omega_m / (self.kappa / 2.0)
}
pub fn is_resolved_sideband(&self) -> bool {
self.omega_m > self.kappa / 2.0
}
pub fn thermal_phonons(&self, temperature_k: f64) -> f64 {
if temperature_k < 1.0e-10 {
return 0.0;
}
let x = HBAR * self.omega_m / (KB * temperature_k);
if x > 700.0 {
(-x).exp()
} else {
1.0 / (x.exp() - 1.0)
}
}
pub fn sql_position_m(&self) -> f64 {
(HBAR / (self.mass_kg * self.omega_m)).sqrt()
}
pub fn minimum_phonon_number(&self, n_bar_photons: f64) -> f64 {
let n_ba = (self.kappa / (4.0 * self.omega_m)).powi(2);
let cooperativity = self.cooperativity(n_bar_photons);
if cooperativity < 1.0e-30 {
return f64::INFINITY;
}
let n_classical = 1.0 / cooperativity;
n_ba.max(n_classical)
}
pub fn critical_photon_number(&self) -> f64 {
self.kappa * self.gamma_m / (4.0 * self.g0 * self.g0)
}
pub fn intracavity_photons(&self, input_power_w: f64, detuning: f64) -> f64 {
let photon_energy = HBAR * self.omega_c;
let input_photon_rate = input_power_w / photon_energy;
let kh2 = (self.kappa / 2.0).powi(2);
self.kappa_ex * input_photon_rate / (kh2 + detuning * detuning)
}
pub fn entanglement_possible(&self, n_bar_photons: f64, temperature_k: f64) -> bool {
let c = self.cooperativity(n_bar_photons);
let n_th = self.thermal_phonons(temperature_k);
c > n_th
}
}
#[derive(Debug, Clone)]
pub struct MembraneOptomechanics {
pub cavity: OptomechanicalCavity,
pub membrane_reflectivity: f64,
pub membrane_position_fraction: f64,
}
impl MembraneOptomechanics {
pub fn new(
cavity: OptomechanicalCavity,
reflectivity: f64,
position_fraction: f64,
) -> Result<Self, OxiPhotonError> {
if !(0.0..=1.0).contains(&reflectivity) {
return Err(OxiPhotonError::NumericalError(format!(
"Membrane reflectivity {:.4} must be in [0, 1]",
reflectivity
)));
}
if !(0.0..=1.0).contains(&position_fraction) {
return Err(OxiPhotonError::NumericalError(format!(
"Membrane position fraction {:.4} must be in [0, 1]",
position_fraction
)));
}
Ok(Self {
cavity,
membrane_reflectivity: reflectivity,
membrane_position_fraction: position_fraction,
})
}
pub fn coupling_enhancement(&self) -> f64 {
let phase = 2.0 * std::f64::consts::PI * self.membrane_position_fraction;
phase.cos().abs()
}
pub fn effective_g0(&self) -> f64 {
self.cavity.g0 * self.membrane_reflectivity.sqrt() * self.coupling_enhancement()
}
}
#[derive(Debug, Clone)]
pub struct PhotonRecoil {
pub atom_mass_amu: f64,
pub wavelength_nm: f64,
}
const AMU_KG: f64 = 1.660_539_066_6e-27;
impl PhotonRecoil {
pub fn new(atom_mass_amu: f64, lambda_nm: f64) -> Self {
Self {
atom_mass_amu,
wavelength_nm: lambda_nm,
}
}
fn mass_kg(&self) -> f64 {
self.atom_mass_amu * AMU_KG
}
fn k_vector(&self) -> f64 {
2.0 * std::f64::consts::PI / (self.wavelength_nm * 1.0e-9)
}
pub fn recoil_velocity_m_per_s(&self) -> f64 {
HBAR * self.k_vector() / self.mass_kg()
}
pub fn recoil_energy_j(&self) -> f64 {
let vr = self.recoil_velocity_m_per_s();
0.5 * self.mass_kg() * vr * vr
}
pub fn recoil_temperature_nk(&self) -> f64 {
let e_r = self.recoil_energy_j();
let t_r_k = e_r / KB;
t_r_k * 1.0e9 }
pub fn doppler_cooling_limit_uk(&self, linewidth_hz: f64) -> f64 {
let gamma = 2.0 * std::f64::consts::PI * linewidth_hz;
let t_d_k = HBAR * gamma / (2.0 * KB);
t_d_k * 1.0e6 }
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
fn standard_cavity() -> OptomechanicalCavity {
let two_pi = 2.0 * std::f64::consts::PI;
OptomechanicalCavity::new(
two_pi * 200.0e12, two_pi * 15.0e6, two_pi * 7.5e6, two_pi * 50.0e6, two_pi * 100.0, 10.0e-12, two_pi * 100.0, )
}
#[test]
fn test_zero_point_motion() {
let cav = standard_cavity();
let x_zpf_calc = cav.zero_point_motion();
let x_zpf_expected = (HBAR / (2.0 * cav.mass_kg * cav.omega_m)).sqrt();
assert_abs_diff_eq!(x_zpf_calc, x_zpf_expected, epsilon = 1.0e-25);
assert!(x_zpf_calc > 1.0e-16, "x_zpf should be in femtometer range");
assert!(x_zpf_calc < 1.0e-12, "x_zpf should be in femtometer range");
}
#[test]
fn test_cooperativity_formula() {
let cav = standard_cavity();
let n_bar = 1000.0;
let c = cav.cooperativity(n_bar);
let expected = 4.0 * cav.g0 * cav.g0 * n_bar / (cav.kappa * cav.gamma_m);
assert_abs_diff_eq!(c, expected, epsilon = 1.0e-10);
}
#[test]
fn test_resolved_sideband() {
let cav = standard_cavity();
assert!(
cav.is_resolved_sideband(),
"50 MHz mechanical, 15 MHz linewidth should be resolved sideband"
);
let resolution = cav.sideband_resolution();
assert!(
(resolution - 6.67).abs() < 0.1,
"Sideband resolution = Ω_m/(κ/2)"
);
}
#[test]
fn test_thermal_phonons_at_zero() {
let cav = standard_cavity();
let n_th_cold = cav.thermal_phonons(1.0e-6); let n_th_zero = cav.thermal_phonons(0.0);
assert!(n_th_cold < 1.0e-10, "At 1 μK, n_th should be negligible");
assert_abs_diff_eq!(n_th_zero, 0.0, epsilon = 1.0e-30);
}
#[test]
fn test_thermal_phonons_high_temperature() {
let cav = standard_cavity();
let n_th = cav.thermal_phonons(300.0);
let n_th_classical = KB * 300.0 / (HBAR * cav.omega_m);
assert!(
(n_th / n_th_classical - 1.0).abs() < 0.01,
"High-T limit: n_th ≈ kT/ħΩ_m, got {} vs {}",
n_th,
n_th_classical
);
}
#[test]
fn test_minimum_phonon_number() {
let cav = standard_cavity();
let n_bar_high = 1.0e8; let n_min = cav.minimum_phonon_number(n_bar_high);
assert!(
n_min < 1.0,
"Ground state cooling should be achievable, n_min = {}",
n_min
);
}
#[test]
fn test_photon_recoil_velocity() {
let recoil = PhotonRecoil::new(87.0, 780.0);
let vr = recoil.recoil_velocity_m_per_s();
let k = 2.0 * std::f64::consts::PI / (780.0e-9);
let m = 87.0 * AMU_KG;
let expected = HBAR * k / m;
assert_abs_diff_eq!(vr, expected, epsilon = 1.0e-10);
assert!(
(vr - 5.9e-3).abs() < 0.1e-3,
"Rb recoil ~5.9 mm/s, got {} mm/s",
vr * 1000.0
);
}
#[test]
fn test_recoil_temperature() {
let recoil = PhotonRecoil::new(87.0, 780.0);
let t_r_nk = recoil.recoil_temperature_nk();
let k = recoil.k_vector();
let m = recoil.mass_kg();
let e_r = HBAR * HBAR * k * k / (2.0 * m);
let t_r_expected_nk = e_r / KB * 1.0e9;
assert_abs_diff_eq!(t_r_nk, t_r_expected_nk, epsilon = 1.0e-6);
assert!(
(t_r_nk - 180.9).abs() < 1.0,
"Rb recoil T ~180.9 nK, got {} nK",
t_r_nk
);
}
#[test]
fn test_optical_damping_red_detuned() {
let cav = standard_cavity();
let n_bar = 1000.0;
let gamma_opt = cav.optical_damping(-cav.omega_m, n_bar);
assert!(
gamma_opt > 0.0,
"Red-detuned driving should give positive (cooling) optical damping"
);
}
#[test]
fn test_optical_damping_blue_detuned() {
let cav = standard_cavity();
let n_bar = 1000.0;
let gamma_opt = cav.optical_damping(cav.omega_m, n_bar);
assert!(
gamma_opt < 0.0,
"Blue-detuned driving should give negative (amplifying) optical damping"
);
}
#[test]
fn test_membrane_system_valid() {
let cav = standard_cavity();
let mem = MembraneOptomechanics::new(cav, 0.5, 0.25);
assert!(mem.is_ok(), "Valid membrane parameters should succeed");
let mem = mem.expect("valid parameters");
assert!(
mem.effective_g0() >= 0.0,
"Effective g0 must be non-negative"
);
}
#[test]
fn test_membrane_invalid_reflectivity() {
let cav = standard_cavity();
let result = MembraneOptomechanics::new(cav, 1.5, 0.5);
assert!(result.is_err(), "Reflectivity > 1 should fail");
}
#[test]
fn test_intracavity_photons_resonance() {
let cav = standard_cavity();
let power = 1.0e-3; let n_on = cav.intracavity_photons(power, 0.0);
let n_off = cav.intracavity_photons(power, cav.omega_m);
assert!(
n_on > n_off,
"On resonance should give more intracavity photons"
);
assert!(n_on > 0.0, "Must have positive photon number");
}
#[test]
fn test_sql_vs_zpf() {
let cav = standard_cavity();
let x_sql = cav.sql_position_m();
let x_zpf = cav.zero_point_motion();
assert_abs_diff_eq!(x_sql, 2.0_f64.sqrt() * x_zpf, epsilon = 1.0e-25);
}
}