sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn kepler_period(a: f64, mu: f64) -> f64 {
    2.0 * std::f64::consts::PI * (a.powi(3) / mu).sqrt()
}

pub fn kepler_velocity(mu: f64, r: f64, a: f64) -> f64 {
    (mu * (2.0 / r - 1.0 / a)).sqrt()
}

pub fn circular_velocity(mu: f64, r: f64) -> f64 {
    (mu / r).sqrt()
}

pub fn escape_velocity(mu: f64, r: f64) -> f64 {
    (2.0 * mu / r).sqrt()
}

pub fn vis_viva(mu: f64, r: f64, a: f64) -> f64 {
    mu * (2.0 / r - 1.0 / a)
}

pub fn orbital_energy(mu: f64, a: f64) -> f64 {
    -mu / (2.0 * a)
}

pub fn angular_momentum(mu: f64, a: f64, e: f64) -> f64 {
    (mu * a * (1.0 - e * e)).sqrt()
}

pub fn periapsis(a: f64, e: f64) -> f64 {
    a * (1.0 - e)
}

pub fn apoapsis(a: f64, e: f64) -> f64 {
    a * (1.0 + e)
}

pub fn true_anomaly_to_radius(a: f64, e: f64, theta: f64) -> f64 {
    a * (1.0 - e * e) / (1.0 + e * theta.cos())
}

pub fn hohmann_delta_v(mu: f64, r1: f64, r2: f64) -> f64 {
    let v1 = (mu / r1).sqrt();
    let a_t = (r1 + r2) / 2.0;
    let v_t1 = (mu * (2.0 / r1 - 1.0 / a_t)).sqrt();
    let v_t2 = (mu * (2.0 / r2 - 1.0 / a_t)).sqrt();
    let v2 = (mu / r2).sqrt();
    (v_t1 - v1).abs() + (v2 - v_t2).abs()
}

pub fn sphere_of_influence(a: f64, m_planet: f64, m_star: f64) -> f64 {
    a * (m_planet / m_star).powf(2.0 / 5.0)
}

pub fn roche_limit(r_primary: f64, rho_primary: f64, rho_secondary: f64) -> f64 {
    2.456 * r_primary * (rho_primary / rho_secondary).powf(1.0 / 3.0)
}

pub fn solve_kepler(m: f64, e: f64, tol: f64) -> f64 {
    let mut ea = m;
    loop {
        let delta = (ea - e * ea.sin() - m) / (1.0 - e * ea.cos());
        ea -= delta;
        if delta.abs() < tol {
            break;
        }
    }
    ea
}

pub fn gr_perihelion_precession(mass: f64, a: f64, e: f64) -> f64 {
    use crate::constants::{C, G};
    6.0 * std::f64::consts::PI * G * mass / (a * (1.0 - e * e) * C * C)
}

pub fn j2_value(body: &str) -> Option<f64> {
    use crate::constants::{J2_EARTH, J2_JUPITER, J2_MARS, J2_SATURN, J2_SUN};
    match body.to_ascii_lowercase().as_str() {
        "earth" => Some(J2_EARTH),
        "jupiter" => Some(J2_JUPITER),
        "saturn" => Some(J2_SATURN),
        "mars" => Some(J2_MARS),
        "sun" => Some(J2_SUN),
        _ => None,
    }
}

pub fn j2_nodal_regression(j2: f64, r_body: f64, a: f64, e: f64, i: f64, n: f64) -> f64 {
    -1.5 * n * j2 * (r_body / (a * (1.0 - e * e))).powi(2) * i.cos()
}

pub fn j2_apsidal_precession(j2: f64, r_body: f64, a: f64, e: f64, i: f64, n: f64) -> f64 {
    0.75 * n * j2 * (r_body / (a * (1.0 - e * e))).powi(2) * (5.0 * i.cos().powi(2) - 1.0)
}

pub fn yoshida4_step(q: &mut [f64], p: &mut [f64], dt: f64, force: &dyn Fn(&[f64]) -> Vec<f64>) {
    use crate::constants::{
        YOSHIDA4_C1, YOSHIDA4_C2, YOSHIDA4_C3, YOSHIDA4_C4, YOSHIDA4_D1, YOSHIDA4_D2, YOSHIDA4_D3,
    };
    let cs = [YOSHIDA4_C1, YOSHIDA4_C2, YOSHIDA4_C3, YOSHIDA4_C4];
    let ds = [YOSHIDA4_D1, YOSHIDA4_D2, YOSHIDA4_D3];

    for i in 0..q.len() {
        q[i] += cs[0] * dt * p[i];
    }
    for k in 0..3 {
        let f = force(q);
        for i in 0..p.len() {
            p[i] += ds[k] * dt * f[i];
        }
        for i in 0..q.len() {
            q[i] += cs[k + 1] * dt * p[i];
        }
    }
}

pub fn yoshida4_weights() -> (f64, f64) {
    use crate::constants::{YOSHIDA4_W0, YOSHIDA4_W1};
    (YOSHIDA4_W0, YOSHIDA4_W1)
}