use std::sync::Arc;
use super::super::{
advecator::Advector,
integrator::{StepProducts, StepTimings, TimeIntegrator},
phasespace::PhaseSpaceRepr,
progress::{StepPhase, StepProgress},
solver::PoissonSolver,
types::*,
};
use super::helpers;
use crate::CausticError;
pub struct StrangSplitting {
pub g: f64,
last_timings: StepTimings,
progress: Option<Arc<StepProgress>>,
}
impl StrangSplitting {
pub fn new(g: f64) -> Self {
Self {
g,
last_timings: StepTimings::default(),
progress: None,
}
}
}
impl TimeIntegrator for StrangSplitting {
fn advance(
&mut self,
repr: &mut dyn PhaseSpaceRepr,
solver: &dyn PoissonSolver,
advector: &dyn Advector,
dt: f64,
) -> Result<StepProducts, CausticError> {
let _span = tracing::info_span!("strang_advance").entered();
let mut timings = StepTimings::default();
if let Some(ref p) = self.progress {
p.start_step();
}
helpers::report_phase!(self.progress, StepPhase::DriftHalf1, 0, 5);
{
let _s = tracing::info_span!("drift_half").entered();
helpers::time_ms!(timings, drift_ms, advector.drift(repr, dt / 2.0));
}
helpers::report_phase!(self.progress, StepPhase::PoissonSolve, 1, 5);
let accel = {
let _s = tracing::info_span!("poisson_solve").entered();
let (density, potential, accel) = helpers::time_ms!(
timings,
poisson_ms,
helpers::solve_poisson(repr, solver, self.g)
);
let _ = (density, potential);
accel
};
helpers::report_phase!(self.progress, StepPhase::Kick, 2, 5);
{
let _s = tracing::info_span!("kick").entered();
helpers::time_ms!(timings, kick_ms, advector.kick(repr, &accel, dt));
}
helpers::apply_hypercollision_if_spectral(repr, dt);
helpers::report_phase!(self.progress, StepPhase::DriftHalf2, 3, 5);
{
let _s = tracing::info_span!("drift_half").entered();
helpers::time_ms!(timings, drift_ms, advector.drift(repr, dt / 2.0));
}
helpers::report_phase!(self.progress, StepPhase::StepComplete, 4, 5);
let (density, potential, acceleration) = helpers::time_ms!(
timings,
density_ms,
helpers::solve_poisson(repr, solver, self.g)
);
self.last_timings = timings;
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 last_step_timings(&self) -> Option<&StepTimings> {
Some(&self.last_timings)
}
fn set_progress(&mut self, progress: Arc<StepProgress>) {
self.progress = Some(progress);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tooling::core::algos::lagrangian::SemiLagrangian;
use crate::tooling::core::algos::spectral::SpectralV;
use crate::tooling::core::init::domain::{Domain, SpatialBoundType, VelocityBoundType};
use crate::tooling::core::poisson::fft::FftPoisson;
#[test]
fn test_hypercollision_applied_for_spectral_v() {
let domain = Domain::builder()
.spatial_extent(2.0)
.velocity_extent(3.0)
.spatial_resolution(4)
.velocity_resolution(4)
.t_final(1.0)
.spatial_bc(SpatialBoundType::Periodic)
.velocity_bc(VelocityBoundType::Open)
.build()
.unwrap();
let n_modes = 4;
let n_modes3 = n_modes * n_modes * n_modes;
let mut spec = SpectralV::new(domain.clone(), n_modes);
for si in 0..(4 * 4 * 4) {
let base = si * n_modes3;
spec.coefficients[base] = 1.0; let high = 3 * n_modes * n_modes + 3 * n_modes + 3;
spec.coefficients[base + high] = 0.5;
}
spec.hypercollision_nu = 1.0;
spec.hypercollision_order = 2;
let high_idx = 3 * n_modes * n_modes + 3 * n_modes + 3;
let high_mode_before: f64 = (0..(4 * 4 * 4))
.map(|si| spec.coefficients[si * n_modes3 + high_idx].abs())
.sum::<f64>();
let poisson = FftPoisson::new(&domain);
let advector = SemiLagrangian::new();
let mut integrator = StrangSplitting::new(1.0);
integrator
.advance(&mut spec, &poisson, &advector, 0.1)
.unwrap();
let high_mode_after: f64 = (0..(4 * 4 * 4))
.map(|si| spec.coefficients[si * n_modes3 + high_idx].abs())
.sum::<f64>();
assert!(
high_mode_after < high_mode_before * 0.01,
"High modes should be strongly damped by hypercollision: before={}, after={}",
high_mode_before,
high_mode_after
);
let zeroth_mode_after: f64 = (0..(4 * 4 * 4))
.map(|si| spec.coefficients[si * n_modes3].abs())
.sum::<f64>();
let zeroth_mode_before = 64.0;
assert!(
zeroth_mode_after > zeroth_mode_before * 0.5,
"Zeroth mode should be approximately preserved: before={}, after={}",
zeroth_mode_before,
zeroth_mode_after
);
}
}