sagittariusas 0.0.3

Simulation engine for Sagittarius A* — Kerr spacetime, accretion, jets, lensing, and shadow observables
Documentation
use crate::config::parameters::{C, G};
use std::f64::consts::PI;

#[derive(Debug, Clone, Copy)]
pub struct GravitationalLens {
    pub mass: f64,
    pub distance_lens: f64,
    pub distance_source: f64,
}

impl GravitationalLens {
    pub fn new(mass: f64, distance_lens: f64, distance_source: f64) -> Self {
        Self {
            mass,
            distance_lens,
            distance_source,
        }
    }

    pub fn einstein_radius(&self) -> f64 {
        let d_ls = self.distance_source - self.distance_lens;
        let theta_e_sq =
            4.0 * G * self.mass * d_ls / (C * C * self.distance_lens * self.distance_source);
        theta_e_sq.sqrt()
    }

    pub fn einstein_radius_physical(&self) -> f64 {
        self.einstein_radius() * self.distance_lens
    }

    pub fn deflection_angle(&self, impact_parameter: f64) -> f64 {
        4.0 * G * self.mass / (C * C * impact_parameter)
    }

    pub fn magnification(&self, beta: f64) -> (f64, f64) {
        let theta_e = self.einstein_radius();
        if theta_e < 1e-30 {
            return (1.0, 0.0);
        }
        let u = beta / theta_e;
        let u2 = u * u;
        let sqrt_term = (u2 + 4.0).sqrt();
        let mu_plus = 0.5 * (u2 + 2.0) / (u * sqrt_term) + 0.5;
        let mu_minus = 0.5 * (u2 + 2.0) / (u * sqrt_term) - 0.5;
        (mu_plus, mu_minus)
    }

    pub fn total_magnification(&self, beta: f64) -> f64 {
        let (mu_p, mu_m) = self.magnification(beta);
        mu_p.abs() + mu_m.abs()
    }

    pub fn image_positions(&self, beta: f64) -> (f64, f64) {
        let theta_e = self.einstein_radius();
        let u = beta / theta_e;
        let sqrt_term = (u * u + 4.0).sqrt();
        let theta_plus = 0.5 * (u + sqrt_term) * theta_e;
        let theta_minus = 0.5 * (u - sqrt_term) * theta_e;
        (theta_plus, theta_minus)
    }

    pub fn time_delay(&self, theta_1: f64, theta_2: f64) -> f64 {
        let theta_e = self.einstein_radius();
        let factor = 4.0 * G * self.mass / (C * C * C)
            * (1.0 + self.distance_lens / (self.distance_source - self.distance_lens));
        let psi_1 = 0.5 * (theta_1 / theta_e).powi(2) - (theta_1 / theta_e).abs().ln();
        let psi_2 = 0.5 * (theta_2 / theta_e).powi(2) - (theta_2 / theta_e).abs().ln();
        factor * (psi_1 - psi_2)
    }
}

pub fn shapiro_delay(mass: f64, closest_approach: f64, distance: f64) -> f64 {
    4.0 * G * mass / (C * C * C) * (2.0 * distance / closest_approach).ln()
}

pub fn light_bending_strong_field(mass: f64, impact_parameter: f64, n_orbits: u32) -> f64 {
    let rs = sciforge::hub::prelude::astronomy::stellar::schwarzschild_radius(mass);
    let b_crit = rs * 3.0_f64.sqrt() * 1.5;
    let base_angle = 4.0 * G * mass / (C * C * impact_parameter);
    base_angle + 2.0 * PI * n_orbits as f64 * (1.0 - impact_parameter / b_crit).abs()
}