Skip to content

Commit

Permalink
Merge branch 'develop' into feature/add_enzymes
Browse files Browse the repository at this point in the history
  • Loading branch information
jacobcook1995 authored Nov 20, 2023
2 parents 33746ba + 8a586fe commit 71dd83f
Show file tree
Hide file tree
Showing 14 changed files with 571 additions and 300 deletions.
56 changes: 55 additions & 1 deletion tests/models/animals/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import numpy as np
import pytest
import xarray
from xarray import DataArray


@pytest.fixture
Expand All @@ -22,6 +23,59 @@ def plant_data_instance():
return data


@pytest.fixture
def plant_climate_data_instance(layer_roles_fixture):
"""Fixture returning a combination of plant and air temperature data."""

from virtual_rainforest.core.data import Data
from virtual_rainforest.core.grid import Grid

# Setup the data object with four cells.
grid = Grid(
grid_type="square",
cell_nx=3,
cell_ny=1,
)
data = Data(grid)

leaf_mass = np.full((15, 3), fill_value=np.nan)
leaf_mass[1:4, :] = 10000
data["layer_leaf_mass"] = xarray.DataArray(
data=leaf_mass, dims=["layers", "cell_id"]
)
data["air_temperature"] = xarray.concat(
[
DataArray(
[
[30.0, 30.0, 30.0],
[29.844995, 29.844995, 29.844995],
[28.87117, 28.87117, 28.87117],
[27.206405, 27.206405, 27.206405],
],
dims=["layers", "cell_id"],
),
DataArray(np.full((7, 3), np.nan), dims=["layers", "cell_id"]),
DataArray(
[
[22.65, 22.65, 22.65],
[16.145945, 16.145945, 16.145945],
],
dims=["layers", "cell_id"],
),
DataArray(np.full((2, 3), np.nan), dims=["layers", "cell_id"]),
],
dim="layers",
).assign_coords(
{
"layers": np.arange(0, 15),
"layer_roles": ("layers", layer_roles_fixture[0:15]),
"cell_id": data.grid.cell_id,
}
)

return data


@pytest.fixture
def functional_group_list_instance(shared_datadir):
"""Fixture for an animal functional group used in tests."""
Expand Down Expand Up @@ -79,7 +133,7 @@ def herbivore_cohort_instance(herbivore_functional_group_instance):
"""Fixture for an animal cohort used in tests."""
from virtual_rainforest.models.animals.animal_cohorts import AnimalCohort

return AnimalCohort(herbivore_functional_group_instance, 10000.0, 1)
return AnimalCohort(herbivore_functional_group_instance, 10000.0, 1, 10)


@pytest.fixture
Expand Down
195 changes: 129 additions & 66 deletions tests/models/animals/test_animal_cohorts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Test module for animal_cohorts.py."""

import pytest
from numpy import timedelta64
from numpy import isclose, timedelta64


@pytest.fixture
Expand All @@ -22,15 +22,36 @@ def predator_cohort_instance(predator_functional_group_instance):
"""Fixture for an animal cohort used in tests."""
from virtual_rainforest.models.animals.animal_cohorts import AnimalCohort

return AnimalCohort(predator_functional_group_instance, 10000.0, 1)
return AnimalCohort(predator_functional_group_instance, 10000.0, 1, 10)


@pytest.fixture
def ectotherm_functional_group_instance(shared_datadir):
"""Fixture for an animal functional group used in tests."""
from virtual_rainforest.models.animals.functional_group import (
import_functional_groups,
)

file = shared_datadir / "example_functional_group_import.csv"
fg_list = import_functional_groups(file)

return fg_list[5]


@pytest.fixture
def ectotherm_cohort_instance(ectotherm_functional_group_instance):
"""Fixture for an animal cohort used in tests."""
from virtual_rainforest.models.animals.animal_cohorts import AnimalCohort

return AnimalCohort(ectotherm_functional_group_instance, 100.0, 1, 10)


@pytest.fixture
def prey_cohort_instance(herbivore_functional_group_instance):
"""Fixture for an animal cohort used in tests."""
from virtual_rainforest.models.animals.animal_cohorts import AnimalCohort

return AnimalCohort(herbivore_functional_group_instance, 100.0, 1)
return AnimalCohort(herbivore_functional_group_instance, 100.0, 1, 10)


@pytest.fixture
Expand All @@ -46,19 +67,14 @@ class TestAnimalCohort:

def test_initialization(self, herbivore_cohort_instance):
"""Testing initialization of derived parameters for animal cohorts."""
assert herbivore_cohort_instance.individuals == 1
assert herbivore_cohort_instance.metabolic_rate == pytest.approx(
3200.9029380, rel=1e-6
)
assert herbivore_cohort_instance.stored_energy == pytest.approx(
56531469253.03123, rel=1e-6
)
assert herbivore_cohort_instance.individuals == 10
assert herbivore_cohort_instance.mass_current == 10000.0

@pytest.mark.parametrize(
"functional_group, mass, age, error_type",
"functional_group, mass, age, individuals, error_type",
[
(lambda fg: fg, -1000.0, 1.0, ValueError),
(lambda fg: fg, 1000.0, -1.0, ValueError),
(lambda fg: fg, -1000.0, 1.0, 10, ValueError),
(lambda fg: fg, 1000.0, -1.0, 10, ValueError),
],
)
def test_invalid_animal_cohort_initialization(
Expand All @@ -67,53 +83,81 @@ def test_invalid_animal_cohort_initialization(
functional_group,
mass,
age,
individuals,
error_type,
):
"""Test for invalid inputs during AnimalCohort initialization."""
from virtual_rainforest.models.animals.animal_cohorts import AnimalCohort

with pytest.raises(error_type):
AnimalCohort(
functional_group(herbivore_functional_group_instance), mass, age
functional_group(herbivore_functional_group_instance),
mass,
age,
individuals,
)

@pytest.mark.parametrize(
"dt, initial_energy, final_energy",
"dt, initial_mass, temperature, final_mass",
[
(timedelta64(1, "D"), 28266000000.0, 27989441986.150745),
(timedelta64(1, "D"), 500.0, 0.0),
(timedelta64(1, "D"), 0.0, 0.0),
(timedelta64(3, "D"), 28266000000.0, 27436325958.45224),
(timedelta64(1, "D"), 1000.0, 298.0, 998.5205247106326), # normal case
(timedelta64(1, "D"), 0.0, 298.0, 0.0), # edge case: zero mass
(timedelta64(3, "D"), 1000.0, 298.0, 995.5615741318977), # 3 days
],
)
def test_metabolize(
self, herbivore_cohort_instance, dt, initial_energy, final_energy
def test_metabolize_endotherm(
self, herbivore_cohort_instance, dt, initial_mass, temperature, final_mass
):
"""Testing metabolize at varying energy levels."""
herbivore_cohort_instance.stored_energy = initial_energy
herbivore_cohort_instance.metabolize(dt)
assert herbivore_cohort_instance.stored_energy == final_energy
"""Testing metabolize with an endothermic metabolism."""
herbivore_cohort_instance.mass_current = initial_mass
herbivore_cohort_instance.metabolize(temperature, dt)
assert herbivore_cohort_instance.mass_current == final_mass
assert isclose(herbivore_cohort_instance.mass_current, final_mass, rtol=1e-9)

@pytest.mark.parametrize(
"dt, initial_energy, error_type",
"dt, initial_mass, temperature, final_mass",
[
(-1, 28266000000.0, ValueError),
(timedelta64(1, "D"), -100.0, ValueError),
(timedelta64(1, "D"), 100.0, 20.0, 99.95896219913648), # normal case
(timedelta64(1, "D"), 0.0, 20.0, 0.0), # edge case: zero mass
(
timedelta64(1, "D"),
100.0,
0.0,
99.99436706014961,
), # edge case: zero temperature
],
)
def test_metabolize_ectotherm(
self, ectotherm_cohort_instance, dt, initial_mass, temperature, final_mass
):
"""Testing metabolize with ectotherms."""
# from math import isclose

ectotherm_cohort_instance.mass_current = initial_mass
ectotherm_cohort_instance.metabolize(temperature, dt)
assert ectotherm_cohort_instance.mass_current == final_mass

@pytest.mark.parametrize(
"dt, initial_mass, temperature, error_type",
[
(timedelta64(-1, "D"), 28266000000.0, 298.0, ValueError),
(timedelta64(1, "D"), -100.0, 298.0, ValueError),
# Add more invalid cases as needed
],
)
def test_metabolize_invalid_input(
self, herbivore_cohort_instance, dt, initial_energy, error_type
self, herbivore_cohort_instance, dt, initial_mass, temperature, error_type
):
"""Testing metabolize with invalid inputs."""
herbivore_cohort_instance.stored_energy = initial_energy
"""Testing metabolize for invalid input."""
herbivore_cohort_instance.mass_current = initial_mass
with pytest.raises(error_type):
herbivore_cohort_instance.metabolize(dt)
herbivore_cohort_instance.metabolize(temperature, dt)

@pytest.mark.parametrize(
"scav_initial, scav_final, decomp_initial, decomp_final, consumed_energy",
[
(1000.0, 1050.0, 0.0, 50.0, 1000.0),
(0.0, 50.0, 1000.0, 1050.0, 1000.0),
(1000.0, 1500.0, 0.0, 500.0, 1000.0),
(0.0, 500.0, 1000.0, 1500.0, 1000.0),
(1000.0, 1000.0, 0.0, 0.0, 0.0),
(0.0, 0.0, 1000.0, 1000.0, 0.0),
],
Expand Down Expand Up @@ -162,8 +206,8 @@ def test_increase_age(self, herbivore_cohort_instance, dt, initial_age, final_ag
argvalues=[
(0, 0, 0, 0.0, 0.0, 0.0),
(0, 1000, 1000, 0.0, 0.0, 0.0),
(1, 1, 0, 1.0, 56000001.0, 1.4e7),
(100, 200, 100, 0.0, 5.6e9, 1.4e9),
(1, 1, 0, 1.0, 8001.0, 2000.0),
(100, 200, 100, 0.0, 800000.0, 200000.0),
],
)
def test_die_individual(
Expand Down Expand Up @@ -263,58 +307,77 @@ def test_eat(self, herbivore_cohort_instance, mocker):
mock_food = mocker.MagicMock(spec=Resource)
mock_pool = mocker.MagicMock(spec=Pool)

herbivore_cohort_instance.individuals = (
10 # Setting a non-zero value for individuals
# Common Setup
herbivore_cohort_instance.individuals = 10
mock_mass_return = 100
mock_food.get_eaten.return_value = mock_mass_return

# Scenario 1: Test mass_current is updated when below threshold
herbivore_cohort_instance.mass_current = 0 # Resetting for test
herbivore_cohort_instance.reproductive_mass = 0 # Resetting for test
herbivore_cohort_instance.is_below_mass_threshold = mocker.MagicMock(
return_value=True
)
herbivore_cohort_instance.stored_energy = (
0 # Setting initial energy to 0 for simplicity

# Execution
herbivore_cohort_instance.eat(mock_food, mock_pool)

# Assertions for Scenario 1
assert (
herbivore_cohort_instance.mass_current
== mock_mass_return / herbivore_cohort_instance.individuals
)
assert herbivore_cohort_instance.reproductive_mass == 0

# Mocking get_eaten to return a fixed energy value
mock_energy_return = 100 # Example energy return value
mock_food.get_eaten.return_value = mock_energy_return
# Scenario 2: Test reproductive_mass is updated when above threshold
herbivore_cohort_instance.mass_current = 0 # Resetting for test
herbivore_cohort_instance.reproductive_mass = 0 # Resetting for test
herbivore_cohort_instance.is_below_mass_threshold = mocker.MagicMock(
return_value=False
)

# Execution
herbivore_cohort_instance.eat(mock_food, mock_pool)

# Assertions
mock_food.get_eaten.assert_called_once_with(
herbivore_cohort_instance, mock_pool
)
# Assertions for Scenario 2
assert (
herbivore_cohort_instance.stored_energy
== mock_energy_return / herbivore_cohort_instance.individuals
herbivore_cohort_instance.reproductive_mass
== mock_mass_return / herbivore_cohort_instance.individuals
)
assert herbivore_cohort_instance.mass_current == 0

# Test ValueError for zero individuals
herbivore_cohort_instance.individuals = 0
with pytest.raises(ValueError, match="Individuals cannot be 0."):
herbivore_cohort_instance.eat(mock_food, mock_pool)

def test_can_reproduce_method(self, herbivore_cohort_instance):
def test_is_below_mass_threshold(self, herbivore_cohort_instance):
"""Test the can_reproduce method of AnimalCohort."""
from virtual_rainforest.models.animals.constants import BIRTH_MASS_THRESHOLD

# TODO: test other mass thresholds
# 1. Test when stored_energy is exactly equal to the threshold
herbivore_cohort_instance.stored_energy = (
herbivore_cohort_instance.reproduction_energy_threshold
herbivore_cohort_instance.mass_current = (
herbivore_cohort_instance.functional_group.adult_mass * BIRTH_MASS_THRESHOLD
)
assert not herbivore_cohort_instance.is_below_mass_threshold(
BIRTH_MASS_THRESHOLD
)
assert herbivore_cohort_instance.can_reproduce()

# 2. Test when stored_energy is just below the threshold
herbivore_cohort_instance.stored_energy = (
herbivore_cohort_instance.reproduction_energy_threshold - 0.01
herbivore_cohort_instance.mass_current = (
herbivore_cohort_instance.functional_group.adult_mass * BIRTH_MASS_THRESHOLD
- 0.01
)
assert not herbivore_cohort_instance.can_reproduce()
assert herbivore_cohort_instance.is_below_mass_threshold(BIRTH_MASS_THRESHOLD)

# 3. Test when stored_energy is above the threshold
herbivore_cohort_instance.stored_energy = (
herbivore_cohort_instance.reproduction_energy_threshold + 0.01
herbivore_cohort_instance.mass_current = (
herbivore_cohort_instance.functional_group.adult_mass * BIRTH_MASS_THRESHOLD
+ 0.01
)
assert not herbivore_cohort_instance.is_below_mass_threshold(
BIRTH_MASS_THRESHOLD
)
assert herbivore_cohort_instance.can_reproduce()

# 4. Test with stored_energy set to 0
herbivore_cohort_instance.stored_energy = 0.0
assert not herbivore_cohort_instance.can_reproduce()
herbivore_cohort_instance.mass_current = 0.0
assert herbivore_cohort_instance.is_below_mass_threshold(BIRTH_MASS_THRESHOLD)

@pytest.mark.parametrize(
"initial_individuals, number_days, mortality_prob",
Expand Down
Loading

0 comments on commit 71dd83f

Please sign in to comment.