ImperialCollegeLondon / virtual_ecosystem

This repository is the home for the codebase for the Virtual Ecosystem project.
https://virtual-ecosystem.readthedocs.io
BSD 3-Clause "New" or "Revised" License
8 stars 1 forks source link

Refine and optimize the Madingley foraging system #416

Open TaranRallings opened 4 months ago

TaranRallings commented 4 months ago

The Madingley foraging system is complicated and contains many functions over three files and two classes. I am reasonably confident that my implementation is accurate to the published equations but another detailed sweep is necessary

After the double-check to make sure the implementation is accurate to the science, we need to see what can be improved. The foraging system was the major computational bottleneck of the original Madingley simulation and we are likely to have the same problem. We will need to improve:

Any thoughts on good ways to do this are encouraged.

TaranRallings commented 4 months ago

David suggested a method consolidation within AnimalCohort:

def consume_prey(
    self, target_cohort: AnimalCohort, carcass_pool: DecayPool
) -> float:
    """Consumes prey from a target cohort.

    This method utilizes the F_i_j_individual method to determine the rate at which
    the target cohort is consumed, and then calculates the number of individuals
    killed and the actual mass to be consumed based on this rate and other model
    parameters. The target cohort and carcass pool are also adjusted to reflect the
    predation.

    Args:
        target_cohort: The prey cohort from which mass will be consumed.
        carcass_pool: The pool to which remains of eaten individuals are delivered.

    Returns:
        The actual mass consumed by the predator,(in kg).
    """
    F = self.F_i_j_individual(
        [target_cohort], target_cohort
    )  # Note: Adjusting this call may be necessary
    delta_t = 30.0  # days, TODO: Replace with actual reference or model parameter

    # Calculate the potential consumed mass based on Mad. formula for
    # delta_mass_predation
    potential_consumed_mass = (
        target_cohort.mass_current
        * target_cohort.individuals
        * (
            1
            - exp(-(F * delta_t * self.constants.tau_f * self.constants.sigma_f_t))
        )
    )

    # Calculate the number of individuals killed
    max_individuals_killed = ceil(
        potential_consumed_mass / target_cohort.mass_current
    )
    actual_individuals_killed = min(
        max_individuals_killed, target_cohort.individuals
    )

    # Calculate the mass represented by the individuals killed
    actual_killed_mass = actual_individuals_killed * target_cohort.mass_current

    # Calculate the actual amount of mass consumed by the predator, which can be
    # lower that the killed mass when there is overkill
    actual_consumed_mass = min(actual_killed_mass, potential_consumed_mass)

    # Update the number of individuals in the prey cohort
    target_cohort.individuals -= actual_individuals_killed

    # Calculate the amount of mass that goes into carcass pool and update the pool
    carcass_mass = (actual_killed_mass - actual_consumed_mass) + (
        actual_consumed_mass * (1 - self.functional_group.mechanical_efficiency)
    )

    # Update the carcass pool with carcass mass
    target_cohort.update_carcass_pool(carcass_mass, self, carcass_pool)

    return actual_consumed_mass