use super::*;
use crate::utils::frequency_to_wavenumber;
use crate::Complex;
use dim::ucum::{M, RAD};
#[allow(non_snake_case)]
pub fn phasematch_singles_fiber_coupling(
omega_s: Frequency,
omega_i: Frequency,
spdc: &SPDC,
integrator: Integrator,
) -> PerMeter3<f64> {
let M2 = M * M; let L = spdc.crystal_setup.length;
let theta_s = spdc.signal.theta_internal();
let phi_s = spdc.signal.phi();
let theta_s_e = spdc.signal.theta_external(&spdc.crystal_setup);
let Ws_SQ = spdc.signal.waist().x_by_y();
let Wx_SQ = sq(spdc.pump.waist().x);
let Wy_SQ = sq(spdc.pump.waist().y);
let sign_ks = spdc.signal.direction().z.signum();
let sign_ki = spdc.idler.direction().z.signum();
let omega_p = omega_s + omega_i; let n_p = spdc.pump.refractive_index(omega_p, &spdc.crystal_setup);
let k_p = frequency_to_wavenumber(omega_p, n_p);
let n_s = spdc.signal.refractive_index(omega_s, &spdc.crystal_setup);
let n_i = spdc.idler.refractive_index(omega_i, &spdc.crystal_setup);
let k_s = sign_ks * frequency_to_wavenumber(omega_s, n_s);
let k_i = sign_ki * frequency_to_wavenumber(omega_i, n_i);
let PHI_s = cos(theta_s_e).powi(-2);
let z0 = 0. * M; let z0s = spdc.signal_waist_position;
let hs = L * 0.5 * tan(theta_s) * cos(phi_s);
let RHOpx = tan(spdc.pump.walkoff_angle(&spdc.crystal_setup));
use dim::Abs;
let ks_f = k_s.abs() / n_s; let SIN_THETA_s_e = sin(theta_s_e); let COS_PHI_s = cos(phi_s);
let GAM2s = -0.25 * Ws_SQ; let GAM1s = GAM2s * PHI_s; let GAM3s = -2. * ks_f * GAM1s * SIN_THETA_s_e * COS_PHI_s; let GAM4s = -0.5 * ks_f * SIN_THETA_s_e * COS_PHI_s * GAM3s; let zhs = z0s + hs * SIN_THETA_s_e * COS_PHI_s; let DEL2s = (0.5 / ks_f) * zhs; let DEL1s = DEL2s * PHI_s; let DEL3s = -hs - zhs * PHI_s * SIN_THETA_s_e * COS_PHI_s; let KpKs = *(k_p * k_s * M2 / RAD / RAD);
let dksi = k_s + k_i + spdc.pp.k_eff();
let C7 = k_p - dksi; let C3 = L * C7; let C4 = L * (1. / k_i - 1. / k_p); let C5 = k_s / k_p; let C9 = Complex::new(*(k_p * Wx_SQ / M / RAD), 0.); let C10 = Complex::new(*(k_p * Wy_SQ / M / RAD), 0.); let LRho = L * RHOpx; let LRho_sq = LRho * LRho;
let alpha1 = 4. * KpKs * Complex::new(*(GAM1s / M2), -*(DEL1s / M2 * RAD));
let alpha2 = 4. * KpKs * Complex::new(*(GAM2s / M2), -*(DEL2s / M2 * RAD));
let alpha3 = Complex::new(*(GAM3s / RAD / M), -*(DEL3s / M));
let k_p_L = k_p * L;
let KpKs4inv = 1. / (4. * KpKs);
let imag = Complex::i();
let fn_z = |z1: f64, z2: f64| {
let B0 = z1 - z2;
let A1 = 2. * z0 - L * z1;
let B1 = 1. - z1;
let B3 = 1. + z1;
let A2 = 2. * z0 - L * z2;
let B2 = 1. - z2;
let B4 = 1. + z2;
let B6a = *(C4 * B0 * RAD / M2);
let gamma1 = *(-k_p_L * B1 / RAD + k_s * A1 / RAD) * imag; let gamma2 = *(-k_p_L * B2 / RAD + k_s * A2 / RAD) * imag; let Ha = alpha1 + gamma1;
let Hb = alpha2 + gamma1;
let Hc = alpha1.conj() - gamma2;
let Hd = alpha2.conj() - gamma2;
let ks = *(k_s * M / RAD);
let AA1 = (Ha - C9 * ks) * KpKs4inv;
let AA2 = (Hc - C9 * ks) * KpKs4inv;
let BB1 = (Hb - C10 * ks) * KpKs4inv;
let BB2 = (Hd - C10 * ks) * KpKs4inv;
let X11 = C9 * ks - Ha;
let X12 = (Hc - C9 * ks) * imag;
let Y21 = C10 * ks - Hb;
let Y22 = (Hd - C10 * ks) * imag;
let EE = 0.25
* (-Complex::new(2. * (*(Wx_SQ / M2)), 0.)
+ imag * B6a
+ (*C5) / X11 * sq(C9 - imag * (*(A1 / M)))
- imag * (*C5) / X12 * sq(C9 + imag * (*(A2 / M))));
let FF = 0.25
* (-Complex::new(2. * (*(Wy_SQ / M2)), 0.) + imag * B6a
- (*C5) / Y21 * sq(imag * C10 + (*(A1 / M)))
+ imag * (*C5) / Y22 * sq(-imag * C10 + (*(A2 / M))));
let GG = ks
* (alpha3.conj() / X12 * (imag * C9 - (*(A2 / M)))
+ alpha3 / X11 * (-C9 + imag * (*(A1 / M))));
let HH = 0.5
* (*(LRho / M))
* (imag * B0
+ ks * (B3 / Y21 * (-imag * C10 - (*(A1 / M))) + B4 / Y22 * (C10 + imag * (*(A2 / M)))));
let IIrho = 0.25 * KpKs * (*(LRho_sq / M2)) * (-B3.powi(2) / Y21 + imag * B4.powi(2) / Y22);
let IIgam = KpKs * (sq(alpha3) / X11 - imag * sq(alpha3.conj()) / X12);
let IIdelk = 2. * *(GAM4s / RAD / RAD) + 0.5 * imag * *(C3 / RAD) * B0;
let II = IIrho + IIgam + IIdelk;
let numerator = (-sq(GG) / (4. * EE) - sq(HH) / (4. * FF) + II).exp();
let denominator = 8. * (AA1 * BB1 * AA2 * BB2 * EE * FF).sqrt();
let pmzcoeff = spdc.pp.integration_constant(z1, L) * spdc.pp.integration_constant(z2, L);
pmzcoeff * numerator / denominator
};
let result = 0.25 * integrator.integrate2d(fn_z, -1., 1., -1., 1.).norm();
PerMeter3::new(result)
}
#[cfg(test)]
mod tests {
use super::*;
fn percent_diff(actual: f64, expected: f64) -> f64 {
100. * ((expected - actual) / expected).abs()
}
#[ignore]
#[test]
fn phasematch_singles_test() {
let mut spdc = SPDC::default();
spdc.crystal_setup.theta = 0.5515891191131287 * RAD;
spdc.signal.set_angles(0. * RAD, 0.03253866877817829 * RAD);
spdc.idler.set_angles(PI * RAD, 0.03178987094602039 * RAD);
spdc.signal_waist_position = -0.0007348996031796276 * M;
spdc.idler_waist_position = -0.0007348996031796276 * M;
let amp = *(phasematch_singles_fiber_coupling(
spdc.signal.frequency(),
spdc.idler.frequency(),
&spdc,
Integrator::default(),
) / PerMeter3::new(1.));
let actual = amp;
let expected = Complex::new(9.518_572_188_658_382e23, 95667755.72451791).norm();
let accept_diff = 1e-4;
let normdiff = percent_diff(actual, expected);
assert!(
normdiff < accept_diff,
"norm percent difference: {}",
normdiff
);
}
#[ignore]
#[test]
fn phasematch_singles_pp_test() {
let mut spdc = SPDC::default();
spdc.assign_poling_period(0.000018041674656364844 * M);
spdc.signal.set_angles(0. * RAD, 0.0341877166429185 * RAD);
spdc.idler.set_angles(PI * RAD, 0.031789820056487665 * RAD);
spdc.crystal_setup.theta = std::f64::consts::FRAC_PI_2 * RAD;
spdc.signal_waist_position = -0.0006311635856188344 * M;
spdc.idler_waist_position = -0.0006311635856188344 * M;
let amp = *(phasematch_singles_fiber_coupling(
spdc.signal.frequency(),
spdc.idler.frequency(),
&spdc,
Integrator::default(),
) / PerMeter3::new(1.));
let actual = amp;
let expected = Complex::new(1.667_581_141_397_712_8e24, -126659122.3067034).norm();
let accept_diff = 1e-4;
let normdiff = percent_diff(actual, expected);
assert!(
normdiff < accept_diff,
"norm percent difference: {}",
normdiff
);
}
}