use std::sync::Arc;
use super::super::{
advecator::Advector,
integrator::{StepProducts, TimeIntegrator},
phasespace::PhaseSpaceRepr,
progress::{StepPhase, StepProgress},
solver::PoissonSolver,
types::*,
};
use super::helpers;
use crate::CausticError;
use rayon::prelude::*;
pub struct RkeiIntegrator {
pub g: f64,
progress: Option<Arc<StepProgress>>,
}
impl RkeiIntegrator {
pub fn new(g: f64) -> Self {
Self { g, progress: None }
}
}
impl TimeIntegrator for RkeiIntegrator {
fn advance(
&mut self,
repr: &mut dyn PhaseSpaceRepr,
solver: &dyn PoissonSolver,
advector: &dyn Advector,
dt: f64,
) -> Result<StepProducts, CausticError> {
let _span = tracing::info_span!("rkei_advance").entered();
let snap_err =
|| CausticError::Solver("RKEI integrator requires to_snapshot support".into());
if let Some(ref p) = self.progress {
p.start_step();
p.set_phase(StepPhase::RkeiStage1);
p.set_sub_step(0, 3);
}
let f_n = repr.to_snapshot(0.0).ok_or_else(snap_err)?;
{
let _s = tracing::info_span!("rkei_stage_1").entered();
let density = repr.compute_density();
let potential = solver.solve(&density, self.g);
let accel = solver.compute_acceleration(&potential);
advector.drift(repr, dt);
advector.kick(repr, &accel, dt);
}
if let Some(ref p) = self.progress {
p.set_phase(StepPhase::RkeiStage2);
p.set_sub_step(1, 3);
}
let _f_1 = repr.to_snapshot(0.0).ok_or_else(snap_err)?;
{
let _s = tracing::info_span!("rkei_stage_2").entered();
let density = repr.compute_density();
let potential = solver.solve(&density, self.g);
let accel = solver.compute_acceleration(&potential);
advector.drift(repr, dt);
advector.kick(repr, &accel, dt);
}
{
let adv_snap = repr.to_snapshot(0.0).ok_or_else(snap_err)?;
let combined: Vec<f64> = f_n
.data
.par_iter()
.zip(adv_snap.data.par_iter())
.map(|(&a, &b)| 0.75 * a + 0.25 * b)
.collect();
repr.load_snapshot(PhaseSpaceSnapshot {
data: combined,
shape: f_n.shape,
time: 0.0,
})?;
}
if let Some(ref p) = self.progress {
p.set_phase(StepPhase::RkeiStage3);
p.set_sub_step(2, 3);
}
{
let _s = tracing::info_span!("rkei_stage_3").entered();
let density = repr.compute_density();
let potential = solver.solve(&density, self.g);
let accel = solver.compute_acceleration(&potential);
advector.drift(repr, dt);
advector.kick(repr, &accel, dt);
}
{
let adv_snap = repr.to_snapshot(0.0).ok_or_else(snap_err)?;
let combined: Vec<f64> = f_n
.data
.par_iter()
.zip(adv_snap.data.par_iter())
.map(|(&a, &b)| (1.0 / 3.0) * a + (2.0 / 3.0) * b)
.collect();
repr.load_snapshot(PhaseSpaceSnapshot {
data: combined,
shape: f_n.shape,
time: 0.0,
})?;
}
let density = repr.compute_density();
let potential = solver.solve(&density, self.g);
let acceleration = solver.compute_acceleration(&potential);
Ok(StepProducts {
density,
potential,
acceleration,
})
}
fn max_dt(&self, repr: &dyn PhaseSpaceRepr, cfl_factor: f64) -> f64 {
helpers::dynamical_timestep(repr, self.g, cfl_factor)
}
fn set_progress(&mut self, progress: Arc<StepProgress>) {
self.progress = Some(progress);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rkei_smoke_test() {
use crate::tooling::core::algos::lagrangian::SemiLagrangian;
use crate::tooling::core::algos::uniform::UniformGrid6D;
use crate::tooling::core::init::domain::{Domain, SpatialBoundType, VelocityBoundType};
use crate::tooling::core::phasespace::PhaseSpaceRepr as _;
use crate::tooling::core::poisson::fft::FftPoisson;
let domain = Domain::builder()
.spatial_extent(1.0)
.velocity_extent(1.0)
.spatial_resolution(4)
.velocity_resolution(4)
.t_final(0.1)
.spatial_bc(SpatialBoundType::Periodic)
.velocity_bc(VelocityBoundType::Open)
.build()
.unwrap();
let mut grid = UniformGrid6D::new(domain.clone());
for v in grid.data.iter_mut() {
*v = 1.0;
}
let poisson = FftPoisson::new(&domain);
let advector = SemiLagrangian::new();
let mut integrator = RkeiIntegrator::new(1.0);
integrator
.advance(&mut grid, &poisson, &advector, 0.01)
.unwrap();
assert!(
!grid.data.iter().any(|x| x.is_nan()),
"RKEI produced NaN values"
);
}
}