use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct Chi2Material {
pub n1: f64,
pub n2: f64,
pub d_eff: f64,
}
impl Chi2Material {
pub fn new(n1: f64, n2: f64, d_eff: f64) -> Self {
Self { n1, n2, d_eff }
}
pub fn ktp() -> Self {
Self {
n1: 1.7396,
n2: 1.7468,
d_eff: 3.64e-12,
} }
pub fn lithium_niobate() -> Self {
Self {
n1: 2.156,
n2: 2.232,
d_eff: 27e-12,
} }
pub fn bbo() -> Self {
Self {
n1: 1.661,
n2: 1.672,
d_eff: 2.0e-12,
}
}
pub fn phase_mismatch_shg(&self, omega: f64) -> f64 {
use crate::units::conversion::SPEED_OF_LIGHT;
2.0 * omega / SPEED_OF_LIGHT * (self.n2 - self.n1)
}
pub fn shg_efficiency_normalized(&self, omega: f64, length: f64, delta_k: f64) -> f64 {
let sinc_val = if (delta_k * length / 2.0).abs() < 1e-10 {
1.0
} else {
(delta_k * length / 2.0).sin() / (delta_k * length / 2.0)
};
let d2 = self.d_eff * self.d_eff;
let omega2 = omega * omega;
d2 * omega2 * length * length * sinc_val * sinc_val
}
pub fn coherence_length(&self, omega: f64) -> f64 {
let dk = self.phase_mismatch_shg(omega);
if dk.abs() < 1e-30 {
f64::INFINITY
} else {
PI / dk.abs()
}
}
pub fn qpm_period(&self, omega: f64) -> f64 {
let dk = self.phase_mismatch_shg(omega);
if dk.abs() < 1e-30 {
f64::INFINITY
} else {
2.0 * PI / dk.abs()
}
}
}
pub fn opa_gain(gamma: f64, delta_k: f64, length: f64) -> f64 {
let g_sq = gamma * gamma - (delta_k / 2.0) * (delta_k / 2.0);
if g_sq > 0.0 {
let g = g_sq.sqrt();
(g * length).cosh().powi(2)
} else {
let g = (-g_sq).sqrt();
1.0 + (gamma / g).powi(2) * (g * length).sin().powi(2)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::units::conversion::SPEED_OF_LIGHT;
#[test]
fn ktp_d_eff_physical() {
let ktp = Chi2Material::ktp();
assert!(ktp.d_eff > 1e-12 && ktp.d_eff < 1e-11);
}
#[test]
fn linbo3_d_eff_physical() {
let lnbo = Chi2Material::lithium_niobate();
assert!(lnbo.d_eff > 1e-11 && lnbo.d_eff < 1e-10);
}
#[test]
fn shg_phase_mismatch_nonzero_for_dispersive() {
let ktp = Chi2Material::ktp();
let omega = 2.0 * PI * SPEED_OF_LIGHT / 1064e-9;
let dk = ktp.phase_mismatch_shg(omega);
assert!(dk.abs() > 0.0 || ktp.n1 == ktp.n2);
}
#[test]
fn coherence_length_positive() {
let ktp = Chi2Material::ktp();
let omega = 2.0 * PI * SPEED_OF_LIGHT / 1064e-9;
let lc = ktp.coherence_length(omega);
assert!(lc > 0.0);
}
#[test]
fn shg_efficiency_max_at_zero_mismatch() {
let mat = Chi2Material::lithium_niobate();
let omega = 2.0 * PI * SPEED_OF_LIGHT / 1064e-9;
let length = 10e-3; let eta_pm = mat.shg_efficiency_normalized(omega, length, 0.0);
let eta_mismatch = mat.shg_efficiency_normalized(omega, length, 1000.0);
assert!(
eta_pm > eta_mismatch,
"Phase-matched SHG should be more efficient"
);
}
#[test]
fn qpm_period_positive() {
let ktp = Chi2Material::ktp();
let omega = 2.0 * PI * SPEED_OF_LIGHT / 1064e-9;
let period = ktp.qpm_period(omega);
assert!(period > 0.0);
}
#[test]
fn opa_gain_no_phase_mismatch() {
let g = 100.0; let gain = opa_gain(g, 0.0, 1e-3);
assert!(gain >= 1.0);
}
#[test]
fn opa_gain_decreases_with_mismatch() {
let g = 100.0;
let l = 1e-3;
let gain_pm = opa_gain(g, 0.0, l);
let gain_mis = opa_gain(g, 1e5, l); assert!(gain_pm >= gain_mis);
}
}