use num_complex::Complex64;
use std::f64::consts::PI;
use super::laguerre_gaussian::LgBeam;
const C: f64 = 2.997_924_58e8;
#[derive(Debug, Clone)]
pub struct SpaceTimeWavePacket {
pub beam_waist: f64,
pub pulse_duration: f64,
pub wavelength: f64,
pub group_velocity: f64,
pub tilt_angle: f64,
}
impl SpaceTimeWavePacket {
pub fn new(w0: f64, tau: f64, wavelength: f64, v_g: f64) -> Self {
let cos_theta = (C / v_g).clamp(-1.0, 1.0);
let tilt_angle = cos_theta.acos();
Self {
beam_waist: w0,
pulse_duration: tau,
wavelength,
group_velocity: v_g,
tilt_angle,
}
}
pub fn from_tilt_angle(w0: f64, tau: f64, wavelength: f64, theta: f64) -> Self {
let v_g = if theta.cos().abs() < 1e-30 {
f64::INFINITY
} else {
C / theta.cos()
};
Self {
beam_waist: w0,
pulse_duration: tau,
wavelength,
group_velocity: v_g,
tilt_angle: theta,
}
}
pub fn group_velocity(&self) -> f64 {
self.group_velocity
}
pub fn invariant_bandwidth_ghz(&self) -> f64 {
let sin2_theta = self.tilt_angle.sin().powi(2);
let cos_theta = self.tilt_angle.cos();
if cos_theta.abs() < 1e-30 || self.beam_waist < 1e-30 {
return 0.0;
}
let bw_hz = C * sin2_theta / (self.wavelength * cos_theta * 2.0 * PI * self.beam_waist);
bw_hz / 1e9
}
pub fn field(&self, x: f64, t: f64, z: f64) -> Complex64 {
let k0 = 2.0 * PI / self.wavelength;
let omega0 = k0 * C;
let tau_ret = if self.group_velocity.is_finite() {
t - z / self.group_velocity
} else {
t
};
let spatial_env = (-x * x / (2.0 * self.beam_waist * self.beam_waist)).exp();
let temporal_env =
(-tau_ret * tau_ret / (2.0 * self.pulse_duration * self.pulse_duration)).exp();
let carrier = Complex64::from_polar(1.0, k0 * z - omega0 * t);
Complex64::new(spatial_env * temporal_env, 0.0) * carrier
}
pub fn nondiffracting_length_m(&self) -> f64 {
let k0 = 2.0 * PI / self.wavelength;
let tan2 = self.tilt_angle.tan().powi(2);
if tan2 < 1e-30 {
return f64::INFINITY;
}
k0 * self.beam_waist * self.beam_waist / tan2
}
pub fn peak_intensity_profile(&self, t: f64, z: f64) -> f64 {
self.field(0.0, t, z).norm_sqr()
}
}
#[derive(Debug, Clone)]
pub struct FlyingFocus {
pub lens_focal_length: f64,
pub chirped_pulse_duration: f64,
pub bandwidth: f64,
pub propagation_velocity: f64,
}
impl FlyingFocus {
pub fn new(f: f64, duration: f64, bandwidth: f64) -> Self {
let v_f = f * 2.0 * PI * bandwidth / duration;
Self {
lens_focal_length: f,
chirped_pulse_duration: duration,
bandwidth,
propagation_velocity: v_f,
}
}
pub fn focal_velocity(&self) -> f64 {
self.propagation_velocity
}
pub fn focal_position_m(&self, t: f64) -> f64 {
self.lens_focal_length + self.propagation_velocity * t
}
pub fn interaction_length_m(&self) -> f64 {
self.propagation_velocity.abs() * self.chirped_pulse_duration
}
}
#[derive(Debug, Clone)]
pub struct PulsedOamBeam {
pub lg: LgBeam,
pub pulse_duration_fs: f64,
pub center_wavelength: f64,
}
impl PulsedOamBeam {
pub fn new(ell: i32, w0: f64, wavelength: f64, duration_fs: f64) -> Self {
let lg = LgBeam::new(ell, 0, w0, wavelength);
Self {
lg,
pulse_duration_fs: duration_fs,
center_wavelength: wavelength,
}
}
pub fn field(&self, r: f64, phi: f64, z: f64, t: f64) -> Complex64 {
let omega0 = 2.0 * PI * C / self.center_wavelength;
let tau = self.pulse_duration_fs * 1e-15;
let temporal_env = (-t * t / (2.0 * tau * tau)).exp();
let spatial = self.lg.field(r, phi, z);
let carrier = Complex64::from_polar(1.0, -omega0 * t);
spatial * Complex64::new(temporal_env, 0.0) * carrier
}
pub fn time_bandwidth_product(&self) -> f64 {
2.0 * (2.0_f64.ln() * 2.0).sqrt() / PI
}
pub fn peak_power_w(&self, energy_nj: f64) -> f64 {
let tau = self.pulse_duration_fs * 1e-15;
let energy_j = energy_nj * 1e-9;
energy_j / (PI.sqrt() * tau)
}
pub fn oam_bandwidth(&self) -> f64 {
let omega0 = 2.0 * PI * C / self.center_wavelength;
let tau = self.pulse_duration_fs * 1e-15;
let delta_omega = 1.0 / tau; self.lg.ell.unsigned_abs() as f64 * delta_omega / omega0
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
#[test]
fn stwp_group_velocity_from_tilt() {
let stwp = SpaceTimeWavePacket::from_tilt_angle(1e-3, 100e-15, 800e-9, 0.0);
assert_abs_diff_eq!(stwp.group_velocity(), C, epsilon = 1.0);
}
#[test]
fn stwp_subluminal_group_velocity() {
let v_g = C * 0.5;
let stwp = SpaceTimeWavePacket::new(1e-3, 100e-15, 800e-9, v_g);
assert_abs_diff_eq!(stwp.group_velocity(), v_g, epsilon = 1.0);
}
#[test]
fn stwp_peak_intensity_at_retarded_time() {
let stwp = SpaceTimeWavePacket::new(1e-3, 100e-15, 800e-9, C);
let z = 0.1; let t_retarded = z / C;
let i_peak = stwp.peak_intensity_profile(t_retarded, z);
let i_off = stwp.peak_intensity_profile(0.0, z);
assert!(i_peak > i_off);
}
#[test]
fn stwp_nondiffracting_length_finite() {
let stwp = SpaceTimeWavePacket::from_tilt_angle(1e-3, 100e-15, 800e-9, 0.1);
let l = stwp.nondiffracting_length_m();
assert!(l > 0.0 && l.is_finite());
}
#[test]
fn flying_focus_interaction_length() {
let ff = FlyingFocus::new(0.1, 1e-12, 1e13);
let length = ff.interaction_length_m();
assert!(length > 0.0);
}
#[test]
fn flying_focus_position_at_t0() {
let ff = FlyingFocus::new(0.5, 1e-12, 1e13);
assert_abs_diff_eq!(ff.focal_position_m(0.0), 0.5, epsilon = 1e-12);
}
#[test]
fn flying_focus_velocity_positive() {
let ff = FlyingFocus::new(0.1, 1e-12, 1e13);
assert!(ff.focal_velocity() != 0.0);
}
#[test]
fn pulsed_oam_time_bandwidth_product() {
let beam = PulsedOamBeam::new(2, 1e-3, 1064e-9, 100.0);
let expected = 2.0 * (2.0_f64.ln() * 2.0).sqrt() / std::f64::consts::PI;
assert_abs_diff_eq!(beam.time_bandwidth_product(), expected, epsilon = 1e-10);
}
#[test]
fn pulsed_oam_peak_power_scales_with_energy() {
let beam = PulsedOamBeam::new(1, 1e-3, 1064e-9, 100.0);
let p1 = beam.peak_power_w(1.0);
let p2 = beam.peak_power_w(2.0);
assert_abs_diff_eq!(p2 / p1, 2.0, epsilon = 1e-10);
}
#[test]
fn pulsed_oam_oam_bandwidth_increases_with_ell() {
let b1 = PulsedOamBeam::new(1, 1e-3, 1064e-9, 100.0);
let b2 = PulsedOamBeam::new(3, 1e-3, 1064e-9, 100.0);
assert!(b2.oam_bandwidth() > b1.oam_bandwidth());
}
#[test]
fn pulsed_oam_field_is_zero_at_axis_for_nonzero_ell() {
let beam = PulsedOamBeam::new(2, 1e-3, 1064e-9, 100.0);
let f = beam.field(1e-12, 0.0, 0.0, 0.0);
assert!(f.norm_sqr() < 1e-10);
}
}