sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
use std::time::Instant;

#[derive(Clone, Debug)]
pub struct SimState {
    pub positions: Vec<[f64; 3]>,
    pub velocities: Vec<[f64; 3]>,
    pub time: f64,
    pub step: u64,
}

#[derive(Clone, Copy, Debug)]
pub struct SimConfig {
    pub dt: f64,
    pub max_steps: u64,
    pub body_count: usize,
}

pub trait StepFn {
    fn integrate(&self, state: &mut SimState, dt: f64);
}

pub trait RenderSink {
    fn frame(&mut self, state: &SimState);
}

struct NullSink;

impl RenderSink for NullSink {
    fn frame(&mut self, _state: &SimState) {}
}

impl SimState {
    pub fn new(body_count: usize) -> Self {
        Self {
            positions: vec![[0.0; 3]; body_count],
            velocities: vec![[0.0; 3]; body_count],
            time: 0.0,
            step: 0,
        }
    }
}

pub struct SimResult {
    pub final_state: SimState,
    pub total_steps: u64,
    pub elapsed_ms: u64,
    pub avg_step_ns: f64,
}

pub fn run<S: StepFn, R: RenderSink>(
    config: &SimConfig,
    step_fn: &S,
    sink: &mut R,
    state: &mut SimState,
) -> SimResult {
    let start = Instant::now();
    for _ in 0..config.max_steps {
        step_fn.integrate(state, config.dt);
        state.time += config.dt;
        state.step += 1;
        sink.frame(state);
    }
    let elapsed = start.elapsed();
    let elapsed_ms = elapsed.as_millis() as u64;
    let avg_step_ns = if config.max_steps > 0 {
        elapsed.as_nanos() as f64 / config.max_steps as f64
    } else {
        0.0
    };
    SimResult {
        final_state: state.clone(),
        total_steps: state.step,
        elapsed_ms,
        avg_step_ns,
    }
}

pub fn run_headless<S: StepFn>(config: &SimConfig, step_fn: &S, state: &mut SimState) -> SimResult {
    run(config, step_fn, &mut NullSink, state)
}