use std::marker::PhantomData;
use crate::laser::LaserPlugin;
use crate::{constant, simulation::Plugin};
use crate::initiate::NewlyCreated;
use crate::integrator::INTEGRATE_POSITION_SYSTEM_NAME;
use crate::laser::index::LaserIndex;
use crate::ramp::Lerp;
use serde::{Deserialize, Serialize};
use specs::prelude::*;
use transition::AtomicTransition;
use self::transition::TransitionComponent;
pub mod doppler;
pub mod force;
pub mod photons_scattered;
pub mod rate;
pub mod repump;
pub mod sampler;
pub mod twolevel;
pub mod transition;
pub mod zeeman;
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct CoolingLight {
pub polarization: i32,
pub wavelength: f64,
}
impl Lerp<CoolingLight> for CoolingLight {
fn lerp(&self, b: &CoolingLight, amount: f64) -> Self {
CoolingLight {
polarization: self.polarization,
wavelength: self.wavelength - (self.wavelength - b.wavelength) * amount,
}
}
}
impl CoolingLight {
pub fn frequency(&self) -> f64 {
constant::C / self.wavelength
}
pub fn wavenumber(&self) -> f64 {
2.0 * constant::PI / self.wavelength
}
pub fn for_transition<T>(detuning: f64, polarization: i32) -> Self where T : AtomicTransition {
let freq = T::frequency() + detuning * 1.0e6;
CoolingLight {
wavelength: constant::C / freq,
polarization,
}
}
}
impl Component for CoolingLight {
type Storage = HashMapStorage<Self>;
}
#[derive(Default)]
pub struct AttachLaserCoolingComponentsToNewlyCreatedAtomsSystem<T, const N: usize>(PhantomData<T>) where T : TransitionComponent;
impl<'a, T, const N: usize> System<'a> for AttachLaserCoolingComponentsToNewlyCreatedAtomsSystem<T, N> where T : TransitionComponent {
type SystemData = (
Entities<'a>,
ReadStorage<'a, NewlyCreated>,
Read<'a, LazyUpdate>,
);
fn run(&mut self, (ent, newly_created, updater): Self::SystemData) {
for (ent, _) in (&ent, &newly_created).join() {
updater.insert(
ent,
doppler::DopplerShiftSamplers {
contents: [doppler::DopplerShiftSampler::default(); N],
},
);
updater.insert(
ent,
sampler::LaserDetuningSamplers::<T,N> {
contents: [sampler::LaserDetuningSampler::default(); N],
},
);
updater.insert(
ent,
rate::RateCoefficients {
contents: [rate::RateCoefficient::<T>::default(); N],
},
);
updater.insert(ent, twolevel::TwoLevelPopulation::<T>::default());
updater.insert(ent, photons_scattered::TotalPhotonsScattered::<T>::default());
updater.insert(
ent,
photons_scattered::ExpectedPhotonsScatteredVector::<T,N> {
contents: [photons_scattered::ExpectedPhotonsScattered::default(); N],
},
);
updater.insert(
ent,
photons_scattered::ActualPhotonsScatteredVector::<T,N> {
contents: [photons_scattered::ActualPhotonsScattered::default(); N],
},
);
}
}
}
pub struct AttachIndexToCoolingLightSystem;
impl<'a> System<'a> for AttachIndexToCoolingLightSystem {
type SystemData = (
Entities<'a>,
ReadStorage<'a, CoolingLight>,
ReadStorage<'a, LaserIndex>,
Read<'a, LazyUpdate>,
);
fn run(&mut self, (ent, cooling_light, cooling_light_index, updater): Self::SystemData) {
for (ent, _, _) in (&ent, &cooling_light, !&cooling_light_index).join() {
updater.insert(ent, LaserIndex::default());
}
}
}
#[derive(Default)]
pub struct LaserCoolingPlugin<T, const N : usize>(PhantomData<T>) where T : TransitionComponent;
impl<T, const N : usize> Plugin for LaserCoolingPlugin<T, N> where T : TransitionComponent {
fn build(&self, builder: &mut crate::simulation::SimulationBuilder) {
add_systems_to_dispatch::<T, N>(&mut builder.dispatcher_builder, &[]);
}
fn deps(&self) -> Vec::<Box<dyn Plugin>> {
vec![Box::new(LaserPlugin::<{N}>)]
}
}
fn add_systems_to_dispatch<T, const N: usize>(
builder: &mut DispatcherBuilder<'static, 'static>,
deps: &[&str],
) where T : TransitionComponent {
builder.add(
AttachLaserCoolingComponentsToNewlyCreatedAtomsSystem::<T, N>::default(),
"attach_laser_cooling_components",
deps,
);
builder.add(
photons_scattered::InitialiseExpectedPhotonsScatteredVectorSystem::<T, N>::default(),
"initialise_expected_photons",
deps,
);
builder.add(
rate::InitialiseRateCoefficientsSystem::<T, N>::default(),
"initialise_rate_coefficients",
deps,
);
builder.add(
doppler::CalculateDopplerShiftSystem::<N>,
"calculate_doppler_shift",
&["index_lasers"],
);
builder.add(
zeeman::CalculateZeemanShiftSystem::<T>::default(),
"zeeman_shift",
&["magnetics_magnitude"],
);
builder.add(
sampler::CalculateLaserDetuningSystem::<T, N>::default(),
"calculate_laser_detuning",
&["calculate_doppler_shift", "zeeman_shift", "index_lasers"],
);
builder.add(
rate::CalculateRateCoefficientsSystem::<T, N>::default(),
"calculate_rate_coefficients",
&["calculate_laser_detuning", "initialise_rate_coefficients"],
);
builder.add(
twolevel::CalculateTwoLevelPopulationSystem::<T, N>::default(),
"calculate_twolevel",
&["calculate_rate_coefficients", "fill_laser_sampler_masks"],
);
builder.add(
photons_scattered::CalculateMeanTotalPhotonsScatteredSystem::<T>::default(),
"calculate_total_photons",
&["calculate_twolevel"],
);
builder.add(
photons_scattered::CalculateExpectedPhotonsScatteredSystem::<T, N>::default(),
"calculate_expected_photons",
&[
"calculate_total_photons",
"fill_laser_sampler_masks",
"initialise_expected_photons",
],
);
builder.add(
photons_scattered::CalculateActualPhotonsScatteredSystem::<T,N>::default(),
"calculate_actual_photons",
&["calculate_expected_photons"],
);
builder.add(
force::CalculateAbsorptionForcesSystem::<T, N>::default(),
"calculate_absorption_forces",
&["calculate_actual_photons", INTEGRATE_POSITION_SYSTEM_NAME],
);
builder.add(
repump::RepumpSystem::<T>::default(),
"repump",
&["calculate_absorption_forces"],
);
builder.add(
force::ApplyEmissionForceSystem::<T, N>::default(),
"calculate_emission_forces",
&[
"calculate_absorption_forces",
INTEGRATE_POSITION_SYSTEM_NAME,
],
);
builder.add(
zeeman::AttachZeemanShiftSamplersToNewlyCreatedAtomsSystem::<T>::default(),
"attach_zeeman_shift_samplers",
&[],
);
builder.add(
AttachIndexToCoolingLightSystem,
"attach_cooling_index",
deps,
);
}
#[cfg(test)]
pub mod tests {
use crate::species::Rubidium87_780D2;
use super::*;
use assert_approx_eq::assert_approx_eq;
#[test]
fn test_add_index_component_to_cooling_lights() {
let mut test_world = World::new();
test_world.register::<LaserIndex>();
test_world.register::<CoolingLight>();
let test_entity = test_world
.create_entity()
.with(CoolingLight {
polarization: 1,
wavelength: 780e-9,
})
.build();
let mut system = AttachIndexToCoolingLightSystem;
system.run_now(&test_world);
test_world.maintain();
assert!(test_world
.read_storage::<LaserIndex>()
.get(test_entity)
.is_some());
}
#[test]
fn test_for_species() {
let detuning = 12.0;
let light = CoolingLight::for_transition::<Rubidium87_780D2>(detuning, 1);
assert_approx_eq!(
light.frequency(),
Rubidium87_780D2::frequency() + 1.0e6 * detuning
);
}
}