use crate::{
core::{
observer::Observer,
scheduler::Scheduler,
scratch::ScratchPool,
state::{State, StateBuilder},
storage::Allocator,
},
geometry::grid::Grid,
integrators::Integrator,
physics::model::Model,
};
pub struct Simulation<G, D, M, I, Sch, A>
where
G: Grid,
M: Model<G, D>,
I: Integrator<G, D>,
Sch: Scheduler,
A: Allocator<M::Scalar>,
{
grid: G,
disc: D,
model: M,
integrator: I,
scheduler: Sch,
alloc: A,
state: State<M::Scalar, A::Storage>,
stages: Vec<State<M::Scalar, A::Storage>>,
pool: ScratchPool<M::Scalar, A::Storage>,
observers: Vec<Box<dyn Observer<M::Scalar, A::Storage>>>,
t: f64,
step_index: u64,
}
impl<G, D, M, I, Sch, A> Simulation<G, D, M, I, Sch, A>
where
G: Grid,
M: Model<G, D>,
I: Integrator<G, D>,
Sch: Scheduler,
A: Allocator<M::Scalar>,
{
pub fn new(grid: G, disc: D, mut model: M, integrator: I, scheduler: Sch, alloc: A) -> Self {
let mut builder = StateBuilder::new();
model.register_fields(&mut builder);
let state = builder.build(&grid, &alloc);
let layout = integrator.stage_layout();
let stages = (0..layout.tendency)
.map(|_| state.like_tendency(&grid, &alloc))
.chain((0..layout.stage_state).map(|_| state.like(&grid, &alloc)))
.collect();
let pool = ScratchPool::allocate(
model.scratch_spec(&grid),
&alloc,
scheduler.max_concurrency(),
);
Self {
grid,
disc,
model,
integrator,
scheduler,
alloc,
state,
stages,
pool,
observers: Vec::new(),
t: 0.0,
step_index: 0,
}
}
pub fn attach_observer(&mut self, observer: Box<dyn Observer<M::Scalar, A::Storage>>) {
self.observers.push(observer);
}
pub const fn grid(&self) -> &G {
&self.grid
}
pub const fn model(&self) -> &M {
&self.model
}
pub const fn time(&self) -> f64 {
self.t
}
pub const fn step_index(&self) -> u64 {
self.step_index
}
pub const fn state(&self) -> &State<M::Scalar, A::Storage> {
&self.state
}
pub const fn state_mut(&mut self) -> (&G, &mut State<M::Scalar, A::Storage>) {
(&self.grid, &mut self.state)
}
pub fn snapshot_buffers(&self, n: usize) -> Vec<State<M::Scalar, A::Storage>> {
(0..n)
.map(|_| self.state.like(&self.grid, &self.alloc))
.collect()
}
pub fn step(&mut self, dt: f64) {
self.integrator.step(
&self.model,
&self.grid,
&self.disc,
&self.scheduler,
&self.pool,
&mut self.state,
&mut self.stages,
self.t,
dt,
);
self.t += dt;
self.step_index += 1;
for obs in &mut self.observers {
obs.observe(self.step_index, self.t, &self.state);
}
}
pub fn stable_dt(&self) -> Option<f64> {
self.model.stable_dt(&self.grid)
}
}