sciforge 0.0.3

A comprehensive scientific computing library in pure Rust with zero dependencies
Documentation
pub fn ramachandran_energy(phi: f64, psi: f64) -> f64 {
    let v1_phi = 1.0 * (1.0 + phi.cos());
    let v2_phi = 0.5 * (1.0 - (2.0 * phi).cos());
    let v3_phi = 0.2 * (1.0 + (3.0 * phi).cos());
    let v1_psi = 1.0 * (1.0 + psi.cos());
    let v2_psi = 0.5 * (1.0 - (2.0 * psi).cos());
    let v3_psi = 0.2 * (1.0 + (3.0 * psi).cos());
    v1_phi + v2_phi + v3_phi + v1_psi + v2_psi + v3_psi
}

pub fn hydrophobic_free_energy(sasa_nonpolar: f64, gamma: f64) -> f64 {
    gamma * sasa_nonpolar
}

pub fn hydrogen_bond_energy(r: f64, theta: f64, epsilon: f64, r0: f64) -> f64 {
    let distance_term = -(((r - r0) / 0.5).powi(2)).exp();
    let angle_term = theta.cos().powi(2);
    epsilon * distance_term * angle_term
}

pub fn electrostatic_solvation(charge: f64, radius: f64, epsilon_solvent: f64) -> f64 {
    let k = 8.9875e9;
    -k * charge * charge / (2.0 * radius) * (1.0 - 1.0 / epsilon_solvent)
}

pub fn fold_stability(delta_h: f64, delta_s: f64, delta_cp: f64, t: f64, t_ref: f64) -> f64 {
    let dh = delta_h + delta_cp * (t - t_ref);
    let ds = delta_s + delta_cp * (t / t_ref).ln();
    dh - t * ds
}

pub fn fraction_folded(delta_g: f64, t: f64) -> f64 {
    use crate::constants::{K_B, N_A};
    let rt = K_B * N_A * t;
    let k_eq = (-delta_g / rt).exp();
    k_eq / (1.0 + k_eq)
}

pub fn two_state_folding_rate(k0: f64, delta_g_dagger: f64, t: f64) -> f64 {
    use crate::constants::{K_B, N_A};
    let rt = K_B * N_A * t;
    k0 * (-delta_g_dagger / rt).exp()
}

pub fn zimm_bragg_helix_coil(s: f64, sigma: f64, n: usize) -> f64 {
    let sn = s.powi(n as i32);
    sigma * sn / (1.0 + sigma * sn)
}

pub fn contact_order(contacts: &[(usize, usize)], chain_length: usize) -> f64 {
    if contacts.is_empty() || chain_length == 0 {
        return 0.0;
    }
    let total_sep: f64 = contacts
        .iter()
        .map(|&(i, j)| (j as f64 - i as f64).abs())
        .sum();
    total_sep / (contacts.len() as f64 * chain_length as f64)
}

pub fn phi_value(
    delta_g_mut_folding: f64,
    delta_g_wt_folding: f64,
    delta_g_mut_ts: f64,
    delta_g_wt_ts: f64,
) -> f64 {
    let ddg_fold = delta_g_mut_folding - delta_g_wt_folding;
    let ddg_ts = delta_g_mut_ts - delta_g_wt_ts;
    if ddg_fold.abs() < 1e-30 {
        return 0.0;
    }
    ddg_ts / ddg_fold
}

pub fn kauzmann_hydrophobic(delta_cp: f64, t: f64, t_s: f64, t_h: f64, delta_h_h: f64) -> f64 {
    let dh = delta_h_h + delta_cp * (t - t_h);
    let ds = delta_cp * (t / t_s).ln();
    dh - t * ds
}

pub fn go_model_energy(
    contacts: &[(usize, usize)],
    distances: &[f64],
    native_distances: &[f64],
    epsilon: f64,
) -> f64 {
    let mut energy = 0.0;
    for (k, &(_, _)) in contacts.iter().enumerate() {
        if k < distances.len() && k < native_distances.len() {
            let sr6 = (native_distances[k] / distances[k].max(1e-30)).powi(6);
            energy += epsilon * (5.0 * sr6 * sr6 - 6.0 * sr6);
        }
    }
    energy
}

pub fn native_contact_fraction(
    current_distances: &[f64],
    native_distances: &[f64],
    cutoff: f64,
) -> f64 {
    if native_distances.is_empty() {
        return 0.0;
    }
    let n = current_distances.len().min(native_distances.len());
    let formed = (0..n)
        .filter(|&i| (current_distances[i] - native_distances[i]).abs() < cutoff)
        .count();
    formed as f64 / native_distances.len() as f64
}

pub fn radius_of_gyration_3d(coords: &[(f64, f64, f64)]) -> f64 {
    let n = coords.len() as f64;
    if n < 1.0 {
        return 0.0;
    }
    let cx = coords.iter().map(|c| c.0).sum::<f64>() / n;
    let cy = coords.iter().map(|c| c.1).sum::<f64>() / n;
    let cz = coords.iter().map(|c| c.2).sum::<f64>() / n;
    let sum_sq: f64 = coords
        .iter()
        .map(|c| (c.0 - cx).powi(2) + (c.1 - cy).powi(2) + (c.2 - cz).powi(2))
        .sum();
    (sum_sq / n).sqrt()
}

pub fn denaturation_midpoint(delta_h: f64, delta_s: f64) -> f64 {
    if delta_s.abs() < 1e-30 {
        return 0.0;
    }
    delta_h / delta_s
}

pub fn chevron_plot_folding(k_f_water: f64, m_f: f64, denaturant: f64) -> f64 {
    k_f_water * (-m_f * denaturant).exp()
}

pub fn chevron_plot_unfolding(k_u_water: f64, m_u: f64, denaturant: f64) -> f64 {
    k_u_water * (m_u * denaturant).exp()
}