marss 0.0.3

Mars celestial simulation crate for the MilkyWay SolarSystem workspace
Documentation
use sciforge::hub::prelude::biology::population::logistic_growth;

pub struct Population {
    pub species_name: &'static str,
    pub count: f64,
    pub carrying_capacity: f64,
    pub intrinsic_growth_rate: f64,
    pub body_mass_kg: f64,
}

impl Population {
    pub fn growth_rate(&self) -> f64 {
        logistic_growth(
            self.intrinsic_growth_rate,
            self.count,
            self.carrying_capacity,
        )
    }

    pub fn project_forward(&self, dt_years: f64) -> f64 {
        let r = self.intrinsic_growth_rate;
        let k = self.carrying_capacity;
        let n = self.count;
        k / (1.0 + ((k - n) / n) * (-r * dt_years).exp())
    }

    pub fn metabolic_rate_w(&self) -> f64 {
        crate::KLEIBER_A * self.body_mass_kg.powf(0.75)
    }

    pub fn home_range_km2(&self) -> f64 {
        crate::MCNAB_A * self.body_mass_kg.powf(1.0)
    }

    pub fn generation_time_years(&self) -> f64 {
        crate::GEN_TIME_A * self.body_mass_kg.powf(0.25)
    }

    pub fn max_lifespan_years(&self) -> f64 {
        crate::LIFESPAN_A * self.body_mass_kg.powf(0.20)
    }
}

pub struct PredatorPrey {
    pub prey: Population,
    pub predator: Population,
    pub attack_rate: f64,
    pub conversion_efficiency: f64,
    pub predator_death_rate: f64,
}

impl PredatorPrey {
    pub fn prey_growth_rate(&self) -> f64 {
        let prey_logistic = self.prey.growth_rate();
        let predation = self.attack_rate * self.prey.count * self.predator.count;
        prey_logistic - predation
    }

    pub fn predator_growth_rate(&self) -> f64 {
        let births =
            self.conversion_efficiency * self.attack_rate * self.prey.count * self.predator.count;
        let deaths = self.predator_death_rate * self.predator.count;
        births - deaths
    }

    pub fn step(&mut self, dt: f64) {
        let (n, p) = (self.prey.count, self.predator.count);

        let deriv = |prey: f64, pred: f64| -> (f64, f64) {
            let prey_logistic =
                self.prey.intrinsic_growth_rate * prey * (1.0 - prey / self.prey.carrying_capacity);
            let predation = self.attack_rate * prey * pred;
            let dn = prey_logistic - predation;
            let dp = self.conversion_efficiency * self.attack_rate * prey * pred
                - self.predator_death_rate * pred;
            (dn, dp)
        };

        let (k1n, k1p) = deriv(n, p);
        let (k2n, k2p) = deriv(n + 0.5 * dt * k1n, p + 0.5 * dt * k1p);
        let (k3n, k3p) = deriv(n + 0.5 * dt * k2n, p + 0.5 * dt * k2p);
        let (k4n, k4p) = deriv(n + dt * k3n, p + dt * k3p);

        self.prey.count = (n + dt / 6.0 * (k1n + 2.0 * k2n + 2.0 * k3n + k4n)).max(0.0);
        self.predator.count = (p + dt / 6.0 * (k1p + 2.0 * k2p + 2.0 * k3p + k4p)).max(0.0);
    }
}