use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct Pc1dCavity {
pub r_mirror: f64,
pub optical_length: f64,
pub n_defect: f64,
pub pc_period: f64,
pub n_periods: usize,
}
impl Pc1dCavity {
pub fn new(
r_mirror: f64,
optical_length: f64,
n_defect: f64,
pc_period: f64,
n_periods: usize,
) -> Self {
Self {
r_mirror,
optical_length,
n_defect,
pc_period,
n_periods,
}
}
pub fn half_wave_cavity_1550nm() -> Self {
let r = 0.995;
let n_defect = 1.5; let l_defect = 1550e-9 / (2.0 * n_defect); Self::new(r, n_defect * l_defect, n_defect, 266e-9, 10)
}
pub fn resonance_frequency(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
PI * SPEED_OF_LIGHT / self.optical_length
}
pub fn resonance_wavelength(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
2.0 * PI * SPEED_OF_LIGHT / self.resonance_frequency()
}
pub fn free_spectral_range(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
PI * SPEED_OF_LIGHT / self.optical_length
}
pub fn quality_factor(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let omega_res = self.resonance_frequency();
let round_trip_loss = -2.0 * self.r_mirror.ln(); let photon_lifetime = 2.0 * self.optical_length / (SPEED_OF_LIGHT * round_trip_loss);
omega_res * photon_lifetime
}
pub fn linewidth(&self) -> f64 {
self.resonance_frequency() / self.quality_factor()
}
pub fn finesse(&self) -> f64 {
PI * self.r_mirror.sqrt() / (1.0 - self.r_mirror)
}
pub fn evanescent_decay_length(&self) -> f64 {
let r_per_period = self.r_mirror.powf(1.0 / self.n_periods as f64);
if r_per_period <= 0.0 || r_per_period >= 1.0 {
return f64::INFINITY;
}
-self.pc_period / r_per_period.ln()
}
pub fn transmission(&self, omega: f64) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let phi = omega * self.optical_length / SPEED_OF_LIGHT; let r = self.r_mirror;
let a = (1.0 - r) * (1.0 - r);
let b = (1.0 - r) * (1.0 - r) + 4.0 * r * (phi.sin()).powi(2);
a / b
}
}
#[derive(Debug, Clone, Copy)]
pub struct Pc2dPointDefect {
pub q_factor: f64,
pub mode_volume: f64,
pub omega_res: f64,
}
impl Pc2dPointDefect {
pub fn new(q_factor: f64, mode_volume: f64, omega_res: f64) -> Self {
Self {
q_factor,
mode_volume,
omega_res,
}
}
pub fn l3_silicon_1550() -> Self {
use crate::units::conversion::SPEED_OF_LIGHT;
let lambda = 1550e-9;
let n_si = 3.476;
let omega = 2.0 * PI * SPEED_OF_LIGHT / lambda;
let v_eff = 0.7 * (lambda / n_si).powi(3);
Self::new(1e5, v_eff, omega)
}
pub fn purcell_factor(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let lambda = 2.0 * PI * SPEED_OF_LIGHT / self.omega_res;
let n = 3.476; (3.0 / (4.0 * PI * PI)) * (lambda / n).powi(3) * self.q_factor / self.mode_volume
}
pub fn photon_lifetime(&self) -> f64 {
self.q_factor / self.omega_res
}
pub fn linewidth_wavelength(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let lambda = 2.0 * PI * SPEED_OF_LIGHT / self.omega_res;
lambda / self.q_factor
}
}
#[derive(Debug, Clone, Copy)]
pub struct H1Defect {
pub lattice_const: f64,
pub rod_radius: f64,
pub n_rod: f64,
pub n_bg: f64,
}
impl H1Defect {
pub fn new(lattice_const: f64, rod_radius: f64, n_rod: f64, n_bg: f64) -> Self {
Self {
lattice_const,
rod_radius,
n_rod,
n_bg,
}
}
fn midgap_freq_normalized(&self) -> f64 {
let n_ref = 3.476; let f_ref = 0.305; f_ref * n_ref / self.n_bg
}
pub fn resonance_frequency(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let f_norm = self.midgap_freq_normalized() * (1.0 - 0.05);
2.0 * PI * SPEED_OF_LIGHT * f_norm / self.lattice_const
}
pub fn mode_volume_estimate(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = self.resonance_frequency();
let lambda = 2.0 * PI * SPEED_OF_LIGHT / omega;
let coeff = 1.2;
coeff * (lambda / self.n_bg).powi(3)
}
pub fn quality_factor_estimate(&self) -> f64 {
300.0
}
pub fn purcell_factor_estimate(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = self.resonance_frequency();
let lambda = 2.0 * PI * SPEED_OF_LIGHT / omega;
let v = self.mode_volume_estimate();
let q = self.quality_factor_estimate();
(3.0 / (4.0 * PI * PI)) * (lambda / self.n_bg).powi(3) * q / v
}
}
#[derive(Debug, Clone, Copy)]
pub struct L3Defect {
pub lattice_const: f64,
pub rod_radius: f64,
pub n_rod: f64,
pub n_bg: f64,
}
impl L3Defect {
pub fn new(lattice_const: f64, rod_radius: f64, n_rod: f64, n_bg: f64) -> Self {
Self {
lattice_const,
rod_radius,
n_rod,
n_bg,
}
}
fn midgap_freq_normalized(&self) -> f64 {
let n_ref = 3.476;
let f_ref = 0.305;
f_ref * n_ref / self.n_bg
}
pub fn resonance_frequency(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let f_norm = self.midgap_freq_normalized() * (1.0 + 0.02);
2.0 * PI * SPEED_OF_LIGHT * f_norm / self.lattice_const
}
pub fn quality_factor_estimate(&self) -> f64 {
10_000.0
}
pub fn mode_volume_estimate(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = self.resonance_frequency();
let lambda = 2.0 * PI * SPEED_OF_LIGHT / omega;
0.7 * (lambda / self.n_bg).powi(3)
}
pub fn purcell_factor_estimate(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = self.resonance_frequency();
let lambda = 2.0 * PI * SPEED_OF_LIGHT / omega;
let v = self.mode_volume_estimate();
let q = self.quality_factor_estimate();
(3.0 / (4.0 * PI * PI)) * (lambda / self.n_bg).powi(3) * q / v
}
pub fn photon_lifetime(&self) -> f64 {
self.quality_factor_estimate() / self.resonance_frequency()
}
}
#[derive(Debug, Clone, Copy)]
pub struct W1Waveguide {
pub lattice_const: f64,
pub rod_radius: f64,
pub n_slab: f64,
}
impl W1Waveguide {
pub fn new(lattice_const: f64, rod_radius: f64, n_slab: f64) -> Self {
Self {
lattice_const,
rod_radius,
n_slab,
}
}
fn fill_factor(&self) -> f64 {
let a = self.lattice_const;
let r = self.rod_radius;
PI * r * r / (3_f64.sqrt() / 2.0 * a * a)
}
fn gap_centre_normalized(&self) -> f64 {
let n_ref = 3.476;
let f_ref = 0.305;
f_ref * n_ref / self.n_slab
}
pub fn cutoff_frequency(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let ff = self.fill_factor().clamp(0.0, 0.8);
let f_lower = self.gap_centre_normalized() * (0.94 - 0.05 * ff);
2.0 * PI * SPEED_OF_LIGHT * f_lower / self.lattice_const
}
pub fn bandwidth(&self) -> f64 {
let ff = self.fill_factor().clamp(0.0, 0.8);
(0.12 - 0.05 * ff).max(0.01)
}
pub fn upper_frequency(&self) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let f_centre = self.gap_centre_normalized();
let f_upper = f_centre * (0.94 - 0.05 * self.fill_factor().clamp(0.0, 0.8))
+ f_centre * self.bandwidth();
2.0 * PI * SPEED_OF_LIGHT * f_upper / self.lattice_const
}
pub fn group_index_at(&self, freq_normalized: f64) -> f64 {
let ff = self.fill_factor().clamp(0.0, 0.8);
let f_lower = self.gap_centre_normalized() * (0.94 - 0.05 * ff);
let bw = self.bandwidth() * self.gap_centre_normalized();
let df_slow = bw * 0.5;
let delta_f = freq_normalized - f_lower;
if delta_f <= 0.0 {
return 200.0;
}
let n_g0 = 5.0;
let x = (delta_f / df_slow).min(1.0);
let denom = x.max(1e-4);
(n_g0 / denom).clamp(n_g0, 200.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pc1d_resonance_wavelength_near_1550() {
let cav = Pc1dCavity::half_wave_cavity_1550nm();
let lambda_res = cav.resonance_wavelength() * 1e9;
assert!(
(lambda_res - 1550.0).abs() < 10.0,
"λ_res={lambda_res:.1}nm"
);
}
#[test]
fn pc1d_quality_factor_large() {
let cav = Pc1dCavity::half_wave_cavity_1550nm();
let q = cav.quality_factor();
assert!(q > 100.0, "Q={q:.0}");
}
#[test]
fn pc1d_finesse_positive() {
let cav = Pc1dCavity::half_wave_cavity_1550nm();
assert!(cav.finesse() > 1.0);
}
#[test]
fn pc1d_transmission_at_resonance_near_one() {
let cav = Pc1dCavity::half_wave_cavity_1550nm();
let omega_res = cav.resonance_frequency();
let t = cav.transmission(omega_res);
assert!(t > 0.5, "T={t:.3}");
}
#[test]
fn pc2d_l3_purcell_large() {
let cav = Pc2dPointDefect::l3_silicon_1550();
let fp = cav.purcell_factor();
assert!(fp > 100.0, "F_P={fp:.0}");
}
#[test]
fn pc2d_photon_lifetime_positive() {
let cav = Pc2dPointDefect::l3_silicon_1550();
assert!(cav.photon_lifetime() > 0.0);
}
#[test]
fn evanescent_decay_length_positive() {
let cav = Pc1dCavity::half_wave_cavity_1550nm();
assert!(cav.evanescent_decay_length() > 0.0);
}
#[test]
fn h1_resonance_frequency_positive() {
let h1 = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
let omega = h1.resonance_frequency();
assert!(omega > 0.0, "ω must be positive");
}
#[test]
fn h1_resonance_in_telecom_band() {
let h1 = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = h1.resonance_frequency();
let lambda_nm = 2.0 * PI * SPEED_OF_LIGHT / omega * 1e9;
assert!(
lambda_nm > 1200.0 && lambda_nm < 1800.0,
"λ_res = {lambda_nm:.0} nm, expected 1200–1800 nm"
);
}
#[test]
fn h1_mode_volume_positive() {
let h1 = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
assert!(h1.mode_volume_estimate() > 0.0);
}
#[test]
fn h1_quality_factor_estimate() {
let h1 = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
let q = h1.quality_factor_estimate();
assert!((100.0..=3000.0).contains(&q), "Q={q}");
}
#[test]
fn h1_purcell_factor_positive() {
let h1 = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
assert!(h1.purcell_factor_estimate() > 0.0);
}
#[test]
fn h1_different_background_index() {
let h1_si = H1Defect::new(400e-9, 120e-9, 1.0, 3.476);
let h1_gaas = H1Defect::new(400e-9, 120e-9, 1.0, 3.374);
assert!(
h1_si.resonance_frequency() < h1_gaas.resonance_frequency(),
"Si has lower resonance freq than GaAs at same lattice"
);
}
#[test]
fn l3_resonance_frequency_positive() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
assert!(l3.resonance_frequency() > 0.0);
}
#[test]
fn l3_resonance_in_telecom_band() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
use crate::units::conversion::SPEED_OF_LIGHT;
let omega = l3.resonance_frequency();
let lambda_nm = 2.0 * PI * SPEED_OF_LIGHT / omega * 1e9;
assert!(
lambda_nm > 1200.0 && lambda_nm < 1900.0,
"λ_res = {lambda_nm:.0} nm"
);
}
#[test]
fn l3_q_estimate_order_of_magnitude() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
let q = l3.quality_factor_estimate();
assert!((1_000.0..=1_000_000.0).contains(&q), "Q={q}");
}
#[test]
fn l3_mode_volume_positive() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
assert!(l3.mode_volume_estimate() > 0.0);
}
#[test]
fn l3_mode_volume_smaller_than_h1() {
let h1 = H1Defect::new(420e-9, 126e-9, 1.0, 3.476);
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
assert!(
l3.mode_volume_estimate() < h1.mode_volume_estimate(),
"L3 mode volume should be smaller than H1"
);
}
#[test]
fn l3_purcell_factor_large() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
let fp = l3.purcell_factor_estimate();
assert!(fp > 10.0, "F_P={fp:.1}");
}
#[test]
fn l3_photon_lifetime_positive() {
let l3 = L3Defect::new(420e-9, 126e-9, 1.0, 3.476);
assert!(l3.photon_lifetime() > 0.0);
}
#[test]
fn w1_cutoff_frequency_positive() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
assert!(w1.cutoff_frequency() > 0.0);
}
#[test]
fn w1_bandwidth_in_range() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
let bw = w1.bandwidth();
assert!((0.01..=0.20).contains(&bw), "bandwidth={bw:.4}");
}
#[test]
fn w1_upper_frequency_above_cutoff() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
assert!(
w1.upper_frequency() > w1.cutoff_frequency(),
"Upper edge must be above lower cut-off"
);
}
#[test]
fn w1_group_index_increases_near_band_edge() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
let ff = w1.fill_factor();
let f_lower = w1.gap_centre_normalized() * (0.94 - 0.05 * ff.clamp(0.0, 0.8));
let ng_near_edge = w1.group_index_at(f_lower + 0.001);
let ng_far = w1.group_index_at(f_lower + 0.05);
assert!(
ng_near_edge > ng_far,
"Group index near band edge ({ng_near_edge:.1}) should exceed far value ({ng_far:.1})"
);
}
#[test]
fn w1_group_index_below_cutoff_large() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
let ng = w1.group_index_at(0.1);
assert!(ng >= 100.0, "Below cut-off n_g should be large, got {ng}");
}
#[test]
fn w1_fill_factor_typical_range() {
let w1 = W1Waveguide::new(430e-9, 129e-9, 3.476);
let ff = w1.fill_factor();
assert!(ff > 0.1 && ff < 0.6, "fill factor={ff:.3}");
}
}