//! Observer trait for progress reporting during convergence. //! //! Replaces the old `verbose: bool` + `println!` path. Callers wire in any //! observer that implements the trait; default methods are no-ops so users //! override only what they need. use crate::time::Time; /// Receives progress callbacks during `History::converge`. /// /// All methods have default no-op implementations; implement only what's /// interesting. pub trait Observer: Send + Sync { /// Called after each convergence iteration across the whole history. fn on_iteration_end(&self, _iter: usize, _max_step: (f64, f64)) {} /// Called after each time slice is processed within an iteration. fn on_batch_processed(&self, _time: &T, _slice_idx: usize, _n_events: usize) {} /// Called once when convergence completes (or max iters is reached). fn on_converged(&self, _iters: usize, _final_step: (f64, f64), _converged: bool) {} } /// ZST no-op observer; the default when none is configured. #[derive(Copy, Clone, Debug, Default)] pub struct NullObserver; impl Observer for NullObserver {} #[cfg(test)] mod tests { use super::*; #[test] fn null_observer_compiles_for_i64() { let o = NullObserver; >::on_iteration_end(&o, 1, (0.0, 0.0)); >::on_converged(&o, 5, (1e-6, 1e-6), true); } #[test] fn null_observer_compiles_for_untimed() { use crate::Untimed; let o = NullObserver; >::on_iteration_end(&o, 1, (0.0, 0.0)); } }