use std::f64::consts::PI;
const GAMMA_E_RAD_S_T: f64 = 1.760859e11;
#[derive(Debug, Clone)]
pub struct GradientEchoMemory {
pub optical_depth: f64,
pub length_m: f64,
pub bandwidth_hz: f64,
pub decoherence_rate: f64,
}
impl GradientEchoMemory {
pub fn storage_efficiency(&self) -> f64 {
1.0 - (-self.optical_depth).exp()
}
pub fn retrieval_efficiency(&self) -> f64 {
self.storage_efficiency()
}
pub fn total_efficiency(&self) -> f64 {
let eta = self.storage_efficiency();
eta * eta
}
#[inline]
pub fn echo_time_s(&self, gradient_time_s: f64) -> f64 {
2.0 * gradient_time_s.max(0.0)
}
pub fn spin_wave_lifetime_s(&self) -> f64 {
1.0 / self.decoherence_rate.max(f64::MIN_POSITIVE)
}
pub fn multimode_capacity(&self, gradient_time_s: f64) -> f64 {
self.optical_depth * self.bandwidth_hz * gradient_time_s.max(0.0)
}
#[inline]
pub fn is_on_demand(&self) -> bool {
true
}
pub fn required_gradient_t_m(&self, g_factor: f64) -> f64 {
let gyro = g_factor * GAMMA_E_RAD_S_T;
2.0 * PI * self.bandwidth_hz / (gyro.max(f64::MIN_POSITIVE) * self.length_m.max(f64::MIN_POSITIVE))
}
pub fn efficiency_vs_od(od_max: f64, n_points: usize) -> Vec<(f64, f64)> {
let n = n_points.max(2);
(1..=n)
.map(|i| {
let od = od_max * i as f64 / n as f64;
let eta_single = 1.0 - (-od).exp();
(od, eta_single * eta_single)
})
.collect()
}
}
#[derive(Debug, Clone)]
pub struct RamanGem {
pub control_rabi_freq: f64,
pub raman_detuning: f64,
pub optical_depth: f64,
pub bandwidth_hz: f64,
}
impl RamanGem {
pub fn effective_coupling(&self) -> f64 {
self.control_rabi_freq * self.control_rabi_freq
/ (4.0 * self.raman_detuning.abs().max(f64::MIN_POSITIVE))
}
pub fn is_adiabatic(&self, pulse_duration_s: f64) -> bool {
self.control_rabi_freq * pulse_duration_s.max(0.0) > 1.0
}
pub fn spontaneous_emission_noise(&self) -> f64 {
let gamma_e_est = self.control_rabi_freq / 10.0;
gamma_e_est / (4.0 * self.raman_detuning.abs().max(f64::MIN_POSITIVE))
* self.optical_depth
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_gem() -> GradientEchoMemory {
GradientEchoMemory {
optical_depth: 5.0,
length_m: 0.05, bandwidth_hz: 1e6, decoherence_rate: 2.0 * PI * 1e3, }
}
#[test]
fn gem_storage_efficiency_high_od() {
let gem = test_gem();
let eta = gem.storage_efficiency();
assert!(
(eta - (1.0 - (-5.0_f64).exp())).abs() < 1e-10,
"η={}",
eta
);
}
#[test]
fn gem_total_efficiency_less_than_storage() {
let gem = test_gem();
assert!(gem.total_efficiency() <= gem.storage_efficiency());
}
#[test]
fn gem_echo_time_twice_gradient_time() {
let gem = test_gem();
let tau = 1e-6_f64; let t_echo = gem.echo_time_s(tau);
assert!(
(t_echo - 2e-6).abs() < 1e-15,
"t_echo={} s",
t_echo
);
}
#[test]
fn gem_spin_wave_lifetime_milliseconds() {
let gem = test_gem();
let t2 = gem.spin_wave_lifetime_s();
assert!(t2 > 1e-6 && t2 < 1.0, "T_2*={}", t2);
}
#[test]
fn gem_multimode_capacity_positive() {
let gem = test_gem();
let n = gem.multimode_capacity(1e-4); assert!(n > 0.0, "N_modes={}", n);
}
#[test]
fn gem_is_on_demand() {
let gem = test_gem();
assert!(gem.is_on_demand());
}
#[test]
fn gem_required_gradient_reasonable() {
let gem = test_gem();
let g = gem.required_gradient_t_m(1.0);
assert!(g > 0.0 && g < 10.0, "G={} T/m", g);
}
#[test]
fn gem_efficiency_vs_od_monotone() {
let curve = GradientEchoMemory::efficiency_vs_od(10.0, 50);
assert_eq!(curve.len(), 50);
for w in curve.windows(2) {
assert!(
w[1].1 >= w[0].1,
"efficiency should increase with OD: {:?}",
w
);
}
}
#[test]
fn raman_gem_effective_coupling() {
let raman = RamanGem {
control_rabi_freq: 2.0 * PI * 1e9, raman_detuning: 2.0 * PI * 10e9, optical_depth: 100.0,
bandwidth_hz: 1e9,
};
let g_eff = raman.effective_coupling();
assert!(g_eff > 0.0, "g_eff={}", g_eff);
}
#[test]
fn raman_gem_adiabaticity() {
let raman = RamanGem {
control_rabi_freq: 2.0 * PI * 1e9,
raman_detuning: 2.0 * PI * 10e9,
optical_depth: 100.0,
bandwidth_hz: 1e9,
};
assert!(raman.is_adiabatic(1e-6));
assert!(!raman.is_adiabatic(1e-12));
}
}