sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
use super::types::Vec3;

pub fn euler_step(pos: &Vec3, vel: &Vec3, accel: &Vec3, dt: f64) -> (Vec3, Vec3) {
    let new_vel = &vel.clone() + &accel.scale(dt);
    let new_pos = &pos.clone() + &new_vel.scale(dt);
    (new_pos, new_vel)
}

pub fn verlet_step(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let a = accel_fn(pos);
    let new_pos = &(&pos.clone() + &vel.scale(dt)) + &a.scale(0.5 * dt * dt);
    let a_new = accel_fn(&new_pos);
    let new_vel = &vel.clone() + &(&a + &a_new).scale(0.5 * dt);
    (new_pos, new_vel)
}

pub fn rk4_step(y: &Vec3, dydt: impl Fn(&Vec3) -> Vec3, dt: f64) -> Vec3 {
    let k1 = dydt(y);
    let k2 = dydt(&(&y.clone() + &k1.scale(dt * 0.5)));
    let k3 = dydt(&(&y.clone() + &k2.scale(dt * 0.5)));
    let k4 = dydt(&(&y.clone() + &k3.scale(dt)));
    let sum = &(&(&k1 + &k2.scale(2.0)) + &k3.scale(2.0)) + &k4;
    &y.clone() + &sum.scale(dt / 6.0)
}

pub fn leapfrog_step(
    pos: &Vec3,
    vel_half: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let new_pos = &pos.clone() + &vel_half.scale(dt);
    let a = accel_fn(&new_pos);
    let new_vel_half = &vel_half.clone() + &a.scale(dt);
    (new_pos, new_vel_half)
}

pub fn symplectic_euler_step(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let a = accel_fn(pos);
    let new_vel = &vel.clone() + &a.scale(dt);
    let new_pos = &pos.clone() + &new_vel.scale(dt);
    (new_pos, new_vel)
}

pub fn yoshida4_step(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let w1 = 1.0 / (2.0 - 2.0_f64.powf(1.0 / 3.0));
    let w0 = -2.0_f64.powf(1.0 / 3.0) * w1;
    let c = [w1 / 2.0, (w0 + w1) / 2.0, (w0 + w1) / 2.0, w1 / 2.0];
    let d = [w1, w0, w1];
    let mut p = pos.clone();
    let mut v = vel.clone();
    p = &p + &v.scale(c[0] * dt);
    let a0 = accel_fn(&p);
    v = &v + &a0.scale(d[0] * dt);
    p = &p + &v.scale(c[1] * dt);
    let a1 = accel_fn(&p);
    v = &v + &a1.scale(d[1] * dt);
    p = &p + &v.scale(c[2] * dt);
    let a2 = accel_fn(&p);
    v = &v + &a2.scale(d[2] * dt);
    p = &p + &v.scale(c[3] * dt);
    (p, v)
}

pub fn rk4_vec3_step(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3, &Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let k1v = accel_fn(pos, vel);
    let k1x = vel.clone();

    let p2 = &pos.clone() + &k1x.scale(0.5 * dt);
    let v2 = &vel.clone() + &k1v.scale(0.5 * dt);
    let k2v = accel_fn(&p2, &v2);
    let k2x = v2.clone();

    let p3 = &pos.clone() + &k2x.scale(0.5 * dt);
    let v3 = &vel.clone() + &k2v.scale(0.5 * dt);
    let k3v = accel_fn(&p3, &v3);
    let k3x = v3.clone();

    let p4 = &pos.clone() + &k3x.scale(dt);
    let v4 = &vel.clone() + &k3v.scale(dt);
    let k4v = accel_fn(&p4, &v4);
    let k4x = v4;

    let dx = &(&(&k1x + &k2x.scale(2.0)) + &k3x.scale(2.0)) + &k4x;
    let dv = &(&(&k1v + &k2v.scale(2.0)) + &k3v.scale(2.0)) + &k4v;

    (
        &pos.clone() + &dx.scale(dt / 6.0),
        &vel.clone() + &dv.scale(dt / 6.0),
    )
}

pub fn stormer_verlet_step(
    pos: &Vec3,
    pos_prev: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> Vec3 {
    let a = accel_fn(pos);
    &(&pos.scale(2.0) - pos_prev) + &a.scale(dt * dt)
}

pub fn forest_ruth_step(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
) -> (Vec3, Vec3) {
    let theta = 1.0 / (2.0 - 2.0_f64.powf(1.0 / 3.0));
    let mut p = &pos.clone() + &vel.scale(theta * dt / 2.0);
    let a1 = accel_fn(&p);
    let mut v = &vel.clone() + &a1.scale(theta * dt);
    p = &p + &v.scale((1.0 - theta) * dt / 2.0);
    let a2 = accel_fn(&p);
    v = &v + &a2.scale((1.0 - 2.0 * theta) * dt);
    p = &p + &v.scale((1.0 - theta) * dt / 2.0);
    let a3 = accel_fn(&p);
    v = &v + &a3.scale(theta * dt);
    p = &p + &v.scale(theta * dt / 2.0);
    (p, v)
}

pub fn adaptive_verlet(
    pos: &Vec3,
    vel: &Vec3,
    accel_fn: impl Fn(&Vec3) -> Vec3,
    dt: f64,
    tol: f64,
) -> (Vec3, Vec3, f64) {
    let (p1, v1) = verlet_step(pos, vel, &accel_fn, dt);
    let half_dt = dt * 0.5;
    let (p_mid, v_mid) = verlet_step(pos, vel, &accel_fn, half_dt);
    let (p2, v2) = verlet_step(&p_mid, &v_mid, &accel_fn, half_dt);
    let err = (&p2 - &p1).magnitude();
    let new_dt = if err > 1e-30 {
        dt * (tol / err).powf(1.0 / 3.0) * 0.9
    } else {
        dt * 2.0
    };
    if err < tol {
        (p2, v2, new_dt)
    } else {
        (p1, v1, new_dt)
    }
}