use num_complex::Complex64;
use serde::{Deserialize, Serialize};
use std::f64::consts::PI;
use super::conversion::{PLANCK, SPEED_OF_LIGHT, Z0};
#[derive(Debug, Clone, Copy)]
pub struct ElectricField(pub [Complex64; 3]);
#[derive(Debug, Clone, Copy)]
pub struct MagneticField(pub [Complex64; 3]);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Intensity(pub f64);
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
pub struct Poynting(pub f64);
impl ElectricField {
pub fn zero() -> Self {
Self([Complex64::new(0.0, 0.0); 3])
}
pub fn intensity_squared(&self) -> f64 {
self.0.iter().map(|c| c.norm_sqr()).sum()
}
pub fn irradiance(&self, n: f64) -> f64 {
0.5 * n * self.intensity_squared() / Z0
}
}
impl MagneticField {
pub fn zero() -> Self {
Self([Complex64::new(0.0, 0.0); 3])
}
pub fn intensity_squared(&self) -> f64 {
self.0.iter().map(|c| c.norm_sqr()).sum()
}
}
pub fn irradiance_from_e_field(e_amplitude: f64, n: f64) -> f64 {
0.5 * n * e_amplitude * e_amplitude / Z0
}
pub fn e_field_from_irradiance(irradiance: f64, n: f64) -> f64 {
(2.0 * Z0 * irradiance / n).sqrt()
}
pub fn e_field_to_power_density(e: f64, n: f64) -> f64 {
irradiance_from_e_field(e, n)
}
pub fn h_from_e(e: f64, n: f64) -> f64 {
n * e / Z0
}
pub fn time_averaged_poynting(e_amplitude: f64, n: f64) -> f64 {
irradiance_from_e_field(e_amplitude, n)
}
pub fn peak_intensity_from_pulse(energy_j: f64, duration_s: f64, area_m2: f64) -> f64 {
energy_j / (duration_s * area_m2)
}
pub fn fluence(intensity_wm2: f64, duration_s: f64) -> f64 {
intensity_wm2 * duration_s
}
pub fn intensity_from_fluence(fluence_jm2: f64, duration_s: f64) -> f64 {
fluence_jm2 / duration_s
}
pub fn photon_flux(irradiance_wm2: f64, photon_energy_j: f64) -> f64 {
irradiance_wm2 / photon_energy_j
}
pub fn power_to_photon_rate(power_w: f64, wavelength_m: f64) -> f64 {
power_w * wavelength_m / (PLANCK * SPEED_OF_LIGHT)
}
pub fn photon_rate_to_power(rate: f64, wavelength_m: f64) -> f64 {
rate * PLANCK * SPEED_OF_LIGHT / wavelength_m
}
pub fn optical_power(e_amplitude: f64, mode_area_m2: f64, n: f64) -> f64 {
irradiance_from_e_field(e_amplitude, n) * mode_area_m2
}
pub fn mode_e_field_amplitude(power_w: f64, mode_area_m2: f64, n: f64) -> f64 {
e_field_from_irradiance(power_w / mode_area_m2, n)
}
pub fn b_integral(intensity: f64, n2: f64, length: f64, lambda: f64) -> f64 {
2.0 * PI / lambda * n2 * intensity * length
}
pub fn spm_phase_shift(gamma: f64, power_w: f64, l_eff: f64) -> f64 {
gamma * power_w * l_eff
}
pub fn effective_length(alpha: f64, length: f64) -> f64 {
if alpha.abs() < 1e-30 {
length
} else {
(1.0 - (-alpha * length).exp()) / alpha
}
}
pub fn field_enhancement_resonator(q: f64, mode_volume: f64, lambda: f64, n: f64) -> f64 {
let lambda_n = lambda / n;
(q * lambda_n * lambda_n * lambda_n / mode_volume).sqrt()
}
pub fn purcell_factor(q: f64, mode_volume: f64, lambda: f64, n: f64) -> f64 {
let lambda_n = lambda / n;
(3.0 / (4.0 * PI * PI)) * lambda_n * lambda_n * lambda_n * q / mode_volume
}
pub fn photon_density(irradiance_wm2: f64, wavelength_m: f64) -> f64 {
irradiance_wm2 * wavelength_m / (PLANCK * SPEED_OF_LIGHT * SPEED_OF_LIGHT)
}
pub fn multiphoton_absorption_scaling(intensity: f64, order: u32) -> f64 {
intensity.powi(order as i32)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn irradiance_roundtrip() {
let e0 = 1e6; let n = 1.5;
let i = irradiance_from_e_field(e0, n);
let e0_back = e_field_from_irradiance(i, n);
assert_relative_eq!(e0, e0_back, epsilon = 1e-8);
}
#[test]
fn irradiance_air_formula() {
let i = irradiance_from_e_field(1.0, 1.0);
assert_relative_eq!(i, 0.5 / Z0, epsilon = 1e-14);
}
#[test]
fn h_from_e_vacuum() {
let h = h_from_e(376.73, 1.0);
assert_relative_eq!(h, 1.0, epsilon = 1e-3);
}
#[test]
fn poynting_equals_irradiance() {
let e = 1e5;
let n = 1.5;
let i = irradiance_from_e_field(e, n);
let s = time_averaged_poynting(e, n);
assert_relative_eq!(i, s, epsilon = 1e-12);
}
#[test]
fn peak_intensity_basic() {
let i = peak_intensity_from_pulse(1e-6, 1e-12, 1e-6);
assert_relative_eq!(i, 1e12, epsilon = 1.0);
}
#[test]
fn fluence_basic() {
let f = fluence(1e12, 1e-12);
assert_relative_eq!(f, 1.0, epsilon = 1e-10);
}
#[test]
fn photon_rate_roundtrip() {
let power = 1e-3; let wl = 1550e-9;
let rate = power_to_photon_rate(power, wl);
let power_back = photon_rate_to_power(rate, wl);
assert_relative_eq!(power, power_back, epsilon = 1e-17);
}
#[test]
fn photon_rate_1mw_at_1550nm() {
let rate = power_to_photon_rate(1e-3, 1550e-9);
assert!(rate > 7e15 && rate < 9e15, "Ṅ={rate:.2e}");
}
#[test]
fn optical_power_from_e_field() {
let e = 1e6;
let area = 1e-12; let n = 3.48; let p = optical_power(e, area, n);
let i = irradiance_from_e_field(e, n);
assert_relative_eq!(p, i * area, epsilon = 1e-10);
}
#[test]
fn b_integral_silica() {
let b = b_integral(1e13, 2.6e-20, 1.0, 1550e-9);
assert!(b > 0.5 && b < 2.0, "B={b:.3}");
}
#[test]
fn effective_length_lossless() {
let l_eff = effective_length(0.0, 10.0);
assert_relative_eq!(l_eff, 10.0, epsilon = 1e-12);
}
#[test]
fn effective_length_high_loss() {
let alpha = 1e6;
let l = 100.0;
let l_eff = effective_length(alpha, l);
assert_relative_eq!(l_eff, 1.0 / alpha, epsilon = 1e-10);
}
#[test]
fn purcell_factor_reasonable() {
let lambda: f64 = 1.5e-6;
let n: f64 = 3.5;
let v_mode = (lambda / n).powi(3);
let fp = purcell_factor(1e4, v_mode, lambda, n);
assert!(fp > 100.0, "F_P={fp:.1}");
}
#[test]
fn electric_field_zero() {
let e = ElectricField::zero();
assert_relative_eq!(e.intensity_squared(), 0.0, epsilon = 1e-30);
}
#[test]
fn magnetic_field_zero() {
let h = MagneticField::zero();
assert_relative_eq!(h.intensity_squared(), 0.0, epsilon = 1e-30);
}
}