use std::fmt::Display;
use dim::f64prefixes::*;
use dim::ucum::*;
use crate::jsa_raw;
use crate::jsi_normalization;
use crate::math::Integrator;
use crate::utils::from_celsius_to_kelvin;
use crate::utils::Iterator2D;
use crate::utils::Steps2D;
use crate::JsiNorm;
use super::*;
type PropSetter = dyn Fn(&mut SPDC, f64) + Send + Sync;
fn get_setter(prop: String) -> Result<Box<PropSetter>, String> {
Ok(match prop.as_str() {
"crystal.phi_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.crystal_setup.phi = v * DEG;
}),
"crystal.theta_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.crystal_setup.theta = v * DEG;
}),
"crystal.length_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.crystal_setup.length = v * MICRO * M;
}),
"crystal.temperature_c" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.crystal_setup.temperature = from_celsius_to_kelvin(v);
}),
"signal.theta_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_theta_internal(v * DEG);
}),
"signal.theta_external_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_theta_external(v * DEG, &spdc.crystal_setup);
}),
"signal.phi_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_phi(v * DEG);
}),
"signal.frequency_thz" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_frequency(v * TERA * HZ * RAD);
}),
"signal.wavelength_nm" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_vacuum_wavelength(v * NANO * M);
}),
"signal.waist_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal.set_waist(v * MICRO * M);
}),
"signal.waist_position_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.signal_waist_position = v * MICRO * M;
}),
"idler.theta_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_theta_internal(v * DEG);
}),
"idler.theta_external_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_theta_external(v * DEG, &spdc.crystal_setup);
}),
"idler.phi_deg" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_phi(v * DEG);
}),
"idler.frequency_thz" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_frequency(v * TERA * HZ * RAD);
}),
"idler.wavelength_nm" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_vacuum_wavelength(v * NANO * M);
}),
"idler.waist_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler.set_waist(v * MICRO * M);
}),
"idler.waist_position_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.idler_waist_position = v * MICRO * M;
}),
"pump.frequency_thz" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.pump.set_frequency(v * TERA * HZ * RAD);
}),
"pump.wavelength_nm" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.pump.set_vacuum_wavelength(v * NANO * M);
}),
"pump.waist_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.pump.set_waist(v * MICRO * M);
}),
"pump.average_power_mw" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.pump_average_power = v * MILLI * W;
}),
"pump.bandwidth_nm" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.pump_bandwidth = v * NANO * M;
}),
"periodic_poling.poling_period_um" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.assign_poling_period(v * MICRO * M);
}),
"deff_pm_per_volt" => Box::new(|spdc: &mut SPDC, v: f64| {
spdc.deff = v * PICO * M / V;
}),
_ => return Err(format!("Unknown property: {}", prop)),
})
}
pub struct SPDCIter {
spdc: SPDC,
setters: (Box<PropSetter>, Box<PropSetter>),
steps: Steps2D<f64>,
}
impl SPDCIter {
pub fn new(spdc: SPDC, setters: (Box<PropSetter>, Box<PropSetter>), steps: Steps2D<f64>) -> Self {
Self {
spdc,
setters,
steps,
}
}
pub fn try_new<S: Display>(
spdc: SPDC,
prop1: S,
prop2: S,
steps: Steps2D<f64>,
) -> Result<Self, String> {
Ok(Self::new(
spdc,
(
get_setter(prop1.to_string())?,
get_setter(prop2.to_string())?,
),
steps,
))
}
pub fn jsi_values(self, integrator: Integrator) -> Vec<f64> {
self
.into_iter()
.map(|spdc| {
let jsi = jsa_raw(
spdc.signal.frequency(),
spdc.idler.frequency(),
&spdc,
integrator,
)
.norm_sqr();
if jsi == 0. {
0.
} else {
jsi
* *(jsi_normalization(spdc.signal.frequency(), spdc.idler.frequency(), &spdc)
/ JsiNorm::new(1.))
}
})
.collect()
}
pub fn jsi_values_normalized(self, integrator: Integrator) -> Vec<f64> {
let opt = self.spdc.clone().try_as_optimum().unwrap();
let jsi_center = jsa_raw(
opt.signal.frequency(),
opt.idler.frequency(),
&opt,
integrator,
)
.norm_sqr()
* jsi_normalization(opt.signal.frequency(), opt.idler.frequency(), &opt);
self
.into_iter()
.map(|spdc| {
let jsi = jsa_raw(
spdc.signal.frequency(),
spdc.idler.frequency(),
&spdc,
integrator,
)
.norm_sqr();
if jsi == 0. {
0.
} else {
jsi
* *(jsi_normalization(spdc.signal.frequency(), spdc.idler.frequency(), &spdc)
/ jsi_center)
}
})
.collect()
}
}
impl IntoIterator for SPDCIter {
type Item = SPDC;
type IntoIter = std::iter::Map<Iterator2D<f64>, Box<dyn FnMut((f64, f64)) -> SPDC>>;
fn into_iter(self) -> Self::IntoIter {
self.steps.into_iter().map(Box::new(move |(v1, v2)| {
let (setter1, setter2) = &self.setters;
let mut spdc = self.spdc.clone();
setter1(&mut spdc, v1);
setter2(&mut spdc, v2);
spdc
}))
}
}