extern crate rayon;
use rand;
use rand_distr::{Distribution, Poisson};
use crate::{integrator::Timestep};
use crate::laser::sampler::CoolingLaserSamplerMasks;
use crate::laser_cooling::rate::RateCoefficients;
use crate::laser_cooling::twolevel::TwoLevelPopulation;
use serde::{Deserialize, Serialize};
use specs::prelude::*;
use std::fmt;
use std::marker::PhantomData;
use super::transition::{TransitionComponent};
#[derive(Clone, Copy, Serialize)]
pub struct TotalPhotonsScattered<T> where T : TransitionComponent {
pub total: f64,
phantom: PhantomData<T>
}
impl<T> Default for TotalPhotonsScattered<T> where T : TransitionComponent {
fn default() -> Self {
TotalPhotonsScattered {
total: f64::NAN,
phantom: PhantomData
}
}
}
impl<T> Component for TotalPhotonsScattered<T> where T : TransitionComponent + 'static {
type Storage = VecStorage<Self>;
}
#[derive(Default)]
pub struct CalculateMeanTotalPhotonsScatteredSystem<T>(PhantomData<T>) where T : TransitionComponent;
impl<'a, T> System<'a> for CalculateMeanTotalPhotonsScatteredSystem<T>
where T: TransitionComponent {
type SystemData = (
ReadExpect<'a, Timestep>,
ReadStorage<'a, T>,
ReadStorage<'a, TwoLevelPopulation<T>>,
WriteStorage<'a, TotalPhotonsScattered<T>>,
);
fn run(
&mut self,
(timestep, transition, twolevel_population, mut total_photons_scattered): Self::SystemData,
) {
use rayon::prelude::*;
(
&transition,
&twolevel_population,
&mut total_photons_scattered,
)
.par_join()
.for_each(|(_atominfo, twolevel, total)| {
total.total = timestep.delta * T::gamma() * twolevel.excited;
});
}
}
#[derive(Clone, Copy, Serialize, Deserialize)]
pub struct ExpectedPhotonsScattered<T> where T : TransitionComponent {
scattered: f64,
phantom: PhantomData<T>
}
impl<T> Default for ExpectedPhotonsScattered<T> where T : TransitionComponent {
fn default() -> Self {
ExpectedPhotonsScattered {
scattered: f64::NAN,
phantom: PhantomData
}
}
}
#[derive(Deserialize, Serialize, Clone)]
pub struct ExpectedPhotonsScatteredVector<T, const N: usize> where T : TransitionComponent {
#[serde(with = "serde_arrays")]
pub contents: [ExpectedPhotonsScattered<T>; N],
}
impl<T, const N: usize> Component for ExpectedPhotonsScatteredVector<T, N> where T : TransitionComponent {
type Storage = VecStorage<Self>;
}
impl<T, const N: usize> fmt::Display for ExpectedPhotonsScatteredVector<T, N> where T : TransitionComponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut result = f.write_str("");
for aps in &self.contents {
result = f.write_fmt(format_args!("{},", aps.scattered));
}
result
}
}
#[derive(Default)]
pub struct InitialiseExpectedPhotonsScatteredVectorSystem<T, const N: usize>(PhantomData<T>) where T : TransitionComponent;
impl<'a, T, const N: usize> System<'a> for InitialiseExpectedPhotonsScatteredVectorSystem<T, N> where T : TransitionComponent {
type SystemData = (WriteStorage<'a, ExpectedPhotonsScatteredVector<T, N>>,);
fn run(&mut self, (mut expected_photons,): Self::SystemData) {
use rayon::prelude::*;
(&mut expected_photons).par_join().for_each(|mut expected| {
expected.contents = [ExpectedPhotonsScattered::default(); N];
});
}
}
#[derive(Default)]
pub struct CalculateExpectedPhotonsScatteredSystem<T, const N: usize>(PhantomData<T>) where T : TransitionComponent;
impl<'a, T, const N: usize> System<'a> for CalculateExpectedPhotonsScatteredSystem<T, N> where T : TransitionComponent {
type SystemData = (
ReadStorage<'a, RateCoefficients<T, N>>,
ReadStorage<'a, TotalPhotonsScattered<T>>,
ReadStorage<'a, CoolingLaserSamplerMasks<N>>,
WriteStorage<'a, ExpectedPhotonsScatteredVector<T, N>>,
);
fn run(
&mut self,
(rate_coefficients, total_photons_scattered, masks, mut expected_photons_vector): Self::SystemData,
) {
use rayon::prelude::*;
(
&rate_coefficients,
&total_photons_scattered,
&masks,
&mut expected_photons_vector,
)
.par_join()
.for_each(|(rates, total, mask, expected)| {
let mut sum_rates: f64 = 0.;
for index in 0..rates.contents.len() {
if mask.contents[index].filled {
sum_rates += rates.contents[index].rate;
}
}
for index in 0..expected.contents.len() {
if mask.contents[index].filled {
expected.contents[index].scattered =
rates.contents[index].rate / sum_rates * total.total;
}
}
});
}
}
#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct ActualPhotonsScattered<T> where T : TransitionComponent {
pub scattered: f64,
phantom: PhantomData<T>
}
impl<T> Default for ActualPhotonsScattered<T> where T : TransitionComponent {
fn default() -> Self {
ActualPhotonsScattered {
scattered: 0.0,
phantom: PhantomData
}
}
}
#[derive(Deserialize, Serialize, Clone)]
pub struct ActualPhotonsScatteredVector<T, const N: usize> where T : TransitionComponent {
#[serde(with = "serde_arrays")]
pub contents: [ActualPhotonsScattered<T>; N],
}
impl<T, const N: usize> ActualPhotonsScatteredVector<T, N> where T : TransitionComponent{
pub fn calculate_total_scattered(&self) -> u64 {
let mut sum: f64 = 0.0;
for item in &self.contents {
sum += item.scattered;
}
sum as u64
}
}
impl<T, const N: usize> fmt::Display for ActualPhotonsScatteredVector<T, N> where T : TransitionComponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut result = f.write_str("");
for aps in &self.contents {
result = f.write_fmt(format_args!("{},", aps.scattered));
}
result
}
}
impl<T, const N: usize> Component for ActualPhotonsScatteredVector<T, N> where T : TransitionComponent + 'static {
type Storage = VecStorage<Self>;
}
#[derive(Clone, Copy)]
pub enum ScatteringFluctuationsOption {
Off,
On,
}
impl Default for ScatteringFluctuationsOption {
fn default() -> Self {
ScatteringFluctuationsOption::On
}
}
#[derive(Default)]
pub struct CalculateActualPhotonsScatteredSystem<T, const N: usize>(PhantomData<T>) where T : TransitionComponent;
impl<'a, T, const N: usize> System<'a> for CalculateActualPhotonsScatteredSystem<T, N> where T : TransitionComponent {
type SystemData = (
Option<Read<'a, ScatteringFluctuationsOption>>,
ReadStorage<'a, ExpectedPhotonsScatteredVector<T, N>>,
WriteStorage<'a, ActualPhotonsScatteredVector<T, N>>,
);
fn run(
&mut self,
(fluctuations_option, expected_photons_vector, mut actual_photons_vector): Self::SystemData,
) {
use rayon::prelude::*;
match fluctuations_option {
None => {
(&expected_photons_vector, &mut actual_photons_vector)
.par_join()
.for_each(|(expected, actual)| {
for index in 0..expected.contents.len() {
actual.contents[index].scattered = expected.contents[index].scattered;
}
});
}
Some(rand_option) => match *rand_option {
ScatteringFluctuationsOption::Off => {
(&expected_photons_vector, &mut actual_photons_vector)
.par_join()
.for_each(|(expected, actual)| {
for index in 0..expected.contents.len() {
actual.contents[index].scattered =
expected.contents[index].scattered;
}
});
}
ScatteringFluctuationsOption::On => {
(&expected_photons_vector, &mut actual_photons_vector)
.par_join()
.for_each(|(expected, actual)| {
for index in 0..expected.contents.len() {
let lambda = expected.contents[index].scattered;
actual.contents[index].scattered =
if lambda <= 1.0e-5 || lambda.is_nan() {
0.0
} else {
let poisson = Poisson::new(lambda).unwrap();
let drawn_number = poisson.sample(&mut rand::thread_rng());
drawn_number as f64
}
}
});
}
},
}
}
}
#[cfg(test)]
pub mod tests {
use crate::{laser::{DEFAULT_BEAM_LIMIT, sampler::LaserSamplerMask}, species::Strontium88_461, laser_cooling::{rate::RateCoefficient, transition::AtomicTransition}};
use super::*;
extern crate specs;
use assert_approx_eq::assert_approx_eq;
use specs::{Builder, RunNow, World};
extern crate nalgebra;
#[test]
fn test_calculate_mean_total_photons_scattered_system() {
let mut test_world = World::new();
let time_delta = 1.0e-6;
test_world.register::<TwoLevelPopulation<Strontium88_461>>();
test_world.register::<Strontium88_461>();
test_world.register::<TotalPhotonsScattered<Strontium88_461>>();
test_world.insert(Timestep { delta: time_delta });
let mut tlp = TwoLevelPopulation::<Strontium88_461>::default();
tlp.ground = 0.7;
tlp.excited = 0.3;
let atom1 = test_world
.create_entity()
.with(TotalPhotonsScattered::<Strontium88_461>::default())
.with(Strontium88_461)
.with(tlp)
.build();
let mut system = CalculateMeanTotalPhotonsScatteredSystem::<Strontium88_461>::default();
system.run_now(&test_world);
test_world.maintain();
let sampler_storage = test_world.read_storage::<TotalPhotonsScattered<Strontium88_461>>();
let scattered = Strontium88_461::gamma() * 0.3 * time_delta;
assert_approx_eq!(
sampler_storage.get(atom1).expect("entity not found").total,
scattered,
1e-5_f64
);
}
#[test]
fn test_calculate_expected_photons_scattered_system() {
let mut test_world = World::new();
test_world.register::<RateCoefficients<Strontium88_461, { DEFAULT_BEAM_LIMIT }>>();
test_world.register::<CoolingLaserSamplerMasks<{ DEFAULT_BEAM_LIMIT }>>();
test_world.register::<TotalPhotonsScattered<Strontium88_461>>();
test_world.register::<ExpectedPhotonsScatteredVector<Strontium88_461, { DEFAULT_BEAM_LIMIT }>>();
let mut rc = RateCoefficient::<Strontium88_461>::default();
rc.rate = 1_000_000.0;
let mut tps = TotalPhotonsScattered::<Strontium88_461>::default();
tps.total = 8.0;
let atom1 = test_world
.create_entity()
.with(tps)
.with(CoolingLaserSamplerMasks {
contents: [LaserSamplerMask { filled: true };
DEFAULT_BEAM_LIMIT],
})
.with(RateCoefficients {
contents: [rc; DEFAULT_BEAM_LIMIT],
})
.with(ExpectedPhotonsScatteredVector {
contents: [ExpectedPhotonsScattered::<Strontium88_461>::default(); crate::laser::DEFAULT_BEAM_LIMIT],
})
.build();
let mut system = CalculateExpectedPhotonsScatteredSystem::<Strontium88_461, { DEFAULT_BEAM_LIMIT }>::default();
system.run_now(&test_world);
test_world.maintain();
let sampler_storage =
test_world.read_storage::<ExpectedPhotonsScatteredVector<Strontium88_461, { DEFAULT_BEAM_LIMIT }>>();
let scattered = 8.0 / crate::laser::DEFAULT_BEAM_LIMIT as f64;
assert_approx_eq!(
sampler_storage
.get(atom1)
.expect("entity not found")
.contents[12] .scattered,
scattered,
1e-5_f64
);
}
}