use crate::{
core::{
observer::{Observer, SnapshotSink},
state::{FieldHandle, State},
storage::StorageBackend,
},
geometry::{
cartesian::{CartesianGrid, for_each_interior},
grid::{BlockId, Grid},
},
};
use indicatif::{ProgressBar, ProgressStyle};
#[must_use]
pub fn progress_bar(total_steps: u64) -> ProgressBar {
let bar = ProgressBar::new(total_steps);
bar.set_style(
ProgressStyle::with_template(
"{spinner} [{elapsed_precise}] {bar:32.cyan/dim} {pos}/{len} ({per_sec}, eta {eta})\n {msg}",
)
.expect("static template"),
);
bar
}
pub struct ProgressObserver {
bar: ProgressBar,
}
impl ProgressObserver {
#[must_use]
pub const fn new(bar: ProgressBar) -> Self {
Self { bar }
}
}
impl<T: crate::core::storage::Scalar, S: StorageBackend<T>> Observer<T, S> for ProgressObserver {
fn observe(&mut self, step: u64, _t: f64, _state: &State<T, S>) {
self.bar.set_position(step);
}
}
pub struct FieldStat {
pub name: &'static str,
pub handle: FieldHandle<f64>,
pub fraction_above: Option<f64>,
}
pub struct FieldStatsSink<const D: usize = 2> {
grid: CartesianGrid<D>,
stats: Vec<FieldStat>,
bar: ProgressBar,
}
impl<const D: usize> FieldStatsSink<D> {
#[must_use]
pub const fn new(grid: CartesianGrid<D>, stats: Vec<FieldStat>, bar: ProgressBar) -> Self {
Self { grid, stats, bar }
}
}
impl<S: StorageBackend<f64>, const D: usize> SnapshotSink<f64, S> for FieldStatsSink<D> {
fn consume(&mut self, _step: u64, t: f64, state: &State<f64, S>) {
use std::fmt::Write as _;
let mut msg = format!("t={t:.1}");
for stat in &self.stats {
let (mut lo, mut hi, mut sum, mut above, mut n) =
(f64::MAX, f64::MIN, 0.0f64, 0u64, 0u64);
for b in 0..self.grid.num_blocks() {
let v = state.view(&self.grid, BlockId(b as u32), stat.handle);
for_each_interior(self.grid.block_cells(), |idx| {
let x = v.get(idx);
lo = lo.min(x);
hi = hi.max(x);
sum += x;
n += 1;
if let Some(thr) = stat.fraction_above
&& x > thr
{
above += 1;
}
});
}
let mean = sum / n.max(1) as f64;
let _ = write!(
msg,
" | {name}∈[{lo:.2},{hi:.2}] ⟨{name}⟩={mean:.3}",
name = stat.name
);
if let Some(thr) = stat.fraction_above {
let _ = write!(
msg,
" frac>{thr:.0}: {:.1}%",
100.0 * above as f64 / n.max(1) as f64
);
}
tracing::info!(field = stat.name, t, lo, hi, mean, "snapshot stats");
}
self.bar.set_message(msg);
}
fn finish(&mut self) {
self.bar.finish_with_message("done");
}
}