use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct SolitonFiber {
pub gamma: f64,
pub beta2_abs: f64,
pub t_raman: f64,
}
impl SolitonFiber {
pub fn new(gamma: f64, beta2_abs: f64) -> Self {
Self {
gamma,
beta2_abs,
t_raman: 3e-15,
} }
pub fn smf28_1550() -> Self {
Self::new(1.3e-3, 21.7e-27)
}
pub fn soliton_order(&self, p0_w: f64, t0_s: f64) -> f64 {
(self.gamma * p0_w * t0_s * t0_s / self.beta2_abs).sqrt()
}
pub fn fundamental_peak_power(&self, t0_s: f64) -> f64 {
self.beta2_abs / (self.gamma * t0_s * t0_s)
}
pub fn dispersion_length(&self, t0_s: f64) -> f64 {
t0_s * t0_s / self.beta2_abs
}
pub fn nonlinear_length(&self, p0_w: f64) -> f64 {
1.0 / (self.gamma * p0_w)
}
pub fn soliton_period(&self, t0_s: f64) -> f64 {
PI * self.dispersion_length(t0_s) / 2.0
}
pub fn fwhm_from_t0(t0_s: f64) -> f64 {
2.0 * (1.0 + 2.0_f64.sqrt()).ln() * t0_s
}
pub fn t0_from_fwhm(fwhm_s: f64) -> f64 {
fwhm_s / (2.0 * (1.0 + 2.0_f64.sqrt()).ln())
}
pub fn ssfs_rate(&self, p0_w: f64) -> f64 {
-8.0 * self.t_raman * self.gamma * self.gamma * p0_w * p0_w / (15.0 * self.beta2_abs)
}
pub fn ssfs_frequency_shift(&self, p0_w: f64, length_m: f64) -> f64 {
self.ssfs_rate(p0_w) * length_m
}
pub fn ssfs_wavelength_shift(&self, p0_w: f64, length_m: f64, wavelength_m: f64) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
let d_omega = self.ssfs_frequency_shift(p0_w, length_m);
-wavelength_m * wavelength_m * d_omega / (2.0 * PI * SPEED_OF_LIGHT)
}
pub fn pulse_profile(&self, p0_w: f64, t0_s: f64, n_pts: usize) -> Vec<(f64, f64)> {
let t_max = 4.0 * t0_s;
(0..n_pts)
.map(|i| {
let t = -t_max + 2.0 * t_max * i as f64 / (n_pts - 1) as f64;
let sech = 1.0 / (t / t0_s).cosh();
(t, p0_w * sech * sech)
})
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn soliton_order_one_for_fundamental() {
let f = SolitonFiber::smf28_1550();
let t0 = 1e-12; let p0 = f.fundamental_peak_power(t0);
let n = f.soliton_order(p0, t0);
assert!((n - 1.0).abs() < 1e-6, "N={n:.6}");
}
#[test]
fn soliton_dispersion_length_scales_t0_squared() {
let f = SolitonFiber::smf28_1550();
let ld1 = f.dispersion_length(1e-12);
let ld2 = f.dispersion_length(2e-12);
assert!((ld2 / ld1 - 4.0).abs() < 1e-6);
}
#[test]
fn soliton_period_positive() {
let f = SolitonFiber::smf28_1550();
let z0 = f.soliton_period(1e-12);
assert!(z0 > 0.0);
}
#[test]
fn soliton_fwhm_t0_roundtrip() {
let t0 = 1e-12;
let fwhm = SolitonFiber::fwhm_from_t0(t0);
let t0_back = SolitonFiber::t0_from_fwhm(fwhm);
assert!((t0_back - t0).abs() / t0 < 1e-10);
}
#[test]
fn soliton_ssfs_red_shifts() {
let f = SolitonFiber::smf28_1550();
let p0 = f.fundamental_peak_power(1e-12);
let d_lambda = f.ssfs_wavelength_shift(p0, 1e3, 1550e-9);
assert!(d_lambda > 0.0, "SSFS should red-shift the soliton");
}
#[test]
fn soliton_pulse_profile_peak_at_center() {
let f = SolitonFiber::smf28_1550();
let p0 = 0.1;
let t0 = 1e-12;
let profile = f.pulse_profile(p0, t0, 101);
let center_idx = 50;
let peak = profile[center_idx].1;
for &(_, intensity) in &profile {
assert!(intensity <= peak + 1e-10);
}
}
}