pub fn impact_velocity(v_rel: f64, escape_vel: f64) -> f64 {
    (v_rel * v_rel + escape_vel * escape_vel).sqrt()
}

pub fn specific_impact_energy(v_impact: f64, mass_target: f64, mass_projectile: f64) -> f64 {
    0.5 * mass_projectile * v_impact * v_impact / (mass_target + mass_projectile)
}

pub fn catastrophic_disruption_threshold(radius: f64, density: f64) -> f64 {
    let diameter_cm = radius * 200.0;
    let q_star_s = 0.4 * diameter_cm.powf(-0.36);
    let q_star_g = 0.3 * (density / 2500.0) * diameter_cm.powf(1.36);
    (q_star_s + q_star_g) * 1.0e4
}

pub fn crater_diameter_pi_scaling(
    projectile_radius: f64,
    projectile_density: f64,
    target_density: f64,
    impact_velocity: f64,
    surface_gravity: f64,
) -> f64 {
    let a = projectile_radius;
    let pi_2 = surface_gravity * a / (impact_velocity * impact_velocity);
    let rho_ratio = target_density / projectile_density;
    1.03 * a * pi_2.powf(-0.22) * rho_ratio.powf(-0.31)
}

pub fn ejecta_velocity_fraction(crater_radius: f64, distance_from_center: f64) -> f64 {
    let x = distance_from_center / crater_radius;
    if x < 0.1 { 1.0 } else { (0.1 / x).powf(1.5) }
}

pub fn collision_probability(n_density: f64, cross_section: f64, relative_velocity: f64) -> f64 {
    n_density * cross_section * relative_velocity
}
