Skip to content

Commit

Permalink
Merge pull request #477 from ImperialCollegeLondon/feature/microbe_ne…
Browse files Browse the repository at this point in the history
…cromass

Adding a microbial necromass pool
  • Loading branch information
jacobcook1995 authored Jul 3, 2024
2 parents 92de3ee + d8afa00 commit 48e4b0a
Show file tree
Hide file tree
Showing 14 changed files with 549 additions and 263 deletions.
11 changes: 6 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,16 +247,17 @@ def dummy_carbon_data(fixture_core_components):
# Setup the data object with four cells.
data = Data(fixture_core_components.grid)

# The required data is now added. This includes the four carbon pools: mineral
# associated organic matter, low molecular weight carbon, microbial carbon and
# particulate organic matter. It also includes various factors of the physical
# environment: pH, bulk density, soil moisture, soil temperature, percentage clay in
# soil.
# The required data is now added. This includes the five carbon pools: mineral
# associated organic matter, low molecular weight carbon, microbial biomass and
# necromass carbon and particulate organic matter. It also includes various factors
# of the physical environment: pH, bulk density, soil moisture, soil temperature,
# percentage clay in soil.
data_values = {
"soil_c_pool_lmwc": [0.05, 0.02, 0.1, 0.005],
"soil_c_pool_maom": [2.5, 1.7, 4.5, 0.5],
"soil_c_pool_microbe": [5.8, 2.3, 11.3, 1.0],
"soil_c_pool_pom": [0.1, 1.0, 0.7, 0.35],
"soil_c_pool_necromass": [0.058, 0.015, 0.093, 0.105],
"soil_enzyme_pom": [0.022679, 0.009576, 0.050051, 0.003010],
"soil_enzyme_maom": [0.0356, 0.0117, 0.02509, 0.00456],
"pH": [3.0, 7.5, 9.0, 5.7],
Expand Down
1 change: 1 addition & 0 deletions tests/core/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,7 @@ def test_output_current_state(mocker, dummy_carbon_data, time_index):
"soil_c_pool_lmwc",
"soil_c_pool_microbe",
"soil_c_pool_pom",
"soil_c_pool_necromass",
"soil_enzyme_pom",
"soil_enzyme_maom",
],
Expand Down
15 changes: 4 additions & 11 deletions tests/models/soil/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import pytest

from virtual_ecosystem.models.soil.env_factors import EnvironmentalEffectFactors


@pytest.fixture
def fixture_soil_config():
Expand Down Expand Up @@ -41,7 +43,6 @@ def environmental_factors(dummy_carbon_data, fixture_core_components):
from virtual_ecosystem.models.soil.constants import SoilConsts
from virtual_ecosystem.models.soil.env_factors import (
calculate_clay_impact_on_enzyme_saturation,
calculate_clay_impact_on_necromass_decay,
calculate_pH_suitability,
calculate_water_potential_impact_on_microbes,
)
Expand Down Expand Up @@ -71,14 +72,6 @@ def environmental_factors(dummy_carbon_data, fixture_core_components):
protection_with_clay=soil_constants.soil_protection_with_clay,
)

clay_decay_factors = calculate_clay_impact_on_necromass_decay(
clay_fraction=dummy_carbon_data["clay_fraction"].to_numpy(),
decay_exponent=soil_constants.clay_necromass_decay_exponent,
return EnvironmentalEffectFactors(
water=water_factors, pH=pH_factors, clay_saturation=clay_saturation_factors
)

return {
"water": water_factors,
"pH": pH_factors,
"clay_saturation": clay_saturation_factors,
"clay_decay": clay_decay_factors,
}
167 changes: 135 additions & 32 deletions tests/models/soil/test_carbon.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, fixture_core_component
from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates

change_in_pools = {
"soil_c_pool_lmwc": [-0.00371115, 0.00278502, -0.01849181, 0.00089995],
"soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5],
"soil_c_pool_lmwc": [0.0022585928, 0.0060483065, -0.019175058, 0.024247214],
"soil_c_pool_maom": [0.038767651, 0.00829848, 0.05982197, 0.07277182],
"soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517],
"soil_c_pool_pom": [0.04809165, 0.01023544, 0.07853728, 0.01167564],
"soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857],
"soil_c_pool_necromass": [0.001137474, 0.009172067, 0.033573266, -0.08978050],
"soil_enzyme_pom": [1.18e-8, 1.67e-8, 1.8e-9, -1.12e-8],
"soil_enzyme_maom": [-0.00031009, -5.09593e-5, 0.0005990658, -3.72112e-5],
}
Expand All @@ -33,6 +34,7 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, fixture_core_component
soil_c_pool_maom=dummy_carbon_data["soil_c_pool_maom"].to_numpy(),
soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"].to_numpy(),
soil_c_pool_pom=dummy_carbon_data["soil_c_pool_pom"].to_numpy(),
soil_c_pool_necromass=dummy_carbon_data["soil_c_pool_necromass"].to_numpy(),
soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"].to_numpy(),
soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"].to_numpy(),
pH=dummy_carbon_data["pH"],
Expand Down Expand Up @@ -60,44 +62,87 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, fixture_core_component
assert np.allclose(delta_pools[i * 4 : (i + 1) * 4], change_in_pools[pool])


def test_determine_microbial_biomass_losses(
def test_calculate_microbial_changes(
dummy_carbon_data, fixture_core_components, environmental_factors
):
"""Check that the determination of microbial biomass losses works correctly."""
from virtual_ecosystem.models.soil.carbon import determine_microbial_biomass_losses
"""Check that calculation of microbe related changes works correctly."""

expected_maintenance = [0.05443078, 0.02298407, 0.12012258, 0.00722288]
expected_pom_enzyme = [0.0005443078, 0.0002298407, 0.0012012258, 7.22288e-5]
expected_maom_enzyme = [0.0005443078, 0.0002298407, 0.0012012258, 7.22288e-5]
expected_decay_to_pom = [0.04631043, 0.01809481, 0.09055279, 0.00621707]
expected_decay_to_lmwc = [0.007031729, 0.004429577, 0.027167343, 8.613595e-4]
from virtual_ecosystem.models.soil.carbon import calculate_microbial_changes

losses = determine_microbial_biomass_losses(
expected_lmwc_uptake = [1.29159055e-2, 8.43352433e-3, 5.77096991e-2, 5.77363558e-5]
expected_microbe = [-0.04978105, -0.02020101, -0.10280967, -0.00719517]
expected_pom_enzyme = [1.17571917e-8, 1.67442231e-8, 1.83311362e-9, -1.11675865e-8]
expected_maom_enzyme = [-3.1009224e-4, -5.0959256e-5, 5.9906583e-4, -3.7211168e-5]
expected_necromass = [0.05474086, 0.02303502, 0.11952352, 0.00726011]

mic_changes = calculate_microbial_changes(
soil_c_pool_lmwc=dummy_carbon_data["soil_c_pool_lmwc"],
soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"],
soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"],
soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"],
soil_temp=dummy_carbon_data["soil_temperature"][
fixture_core_components.layer_structure.index_topsoil_scalar
],
clay_factor_decay=environmental_factors["clay_decay"],
env_factors=environmental_factors,
constants=SoilConsts,
)

# Check that each rate matches expectation
assert np.allclose(losses.maintenance_synthesis, expected_maintenance)
assert np.allclose(losses.pom_enzyme_production, expected_pom_enzyme)
assert np.allclose(losses.maom_enzyme_production, expected_maom_enzyme)
assert np.allclose(losses.necromass_decay_to_lmwc, expected_decay_to_lmwc)
assert np.allclose(losses.necromass_decay_to_pom, expected_decay_to_pom)

# Then check that sum of other rates is the same as the overall
# maintenance_synthesis rate
assert np.allclose(
losses.maintenance_synthesis,
losses.pom_enzyme_production
+ losses.maom_enzyme_production
+ losses.necromass_decay_to_lmwc
+ losses.necromass_decay_to_pom,
assert np.allclose(mic_changes.lmwc_uptake, expected_lmwc_uptake)
assert np.allclose(mic_changes.microbe_change, expected_microbe)
assert np.allclose(mic_changes.pom_enzyme_change, expected_pom_enzyme)
assert np.allclose(mic_changes.maom_enzyme_change, expected_maom_enzyme)
assert np.allclose(mic_changes.necromass_generation, expected_necromass)


def test_calculate_enzyme_mediated_rates(
dummy_carbon_data, environmental_factors, fixture_core_components
):
"""Check that calculation of enzyme mediated rates works as expected."""

from virtual_ecosystem.models.soil.carbon import calculate_enzyme_mediated_rates

expected_pom_to_lmwc = [3.39844565e-4, 8.91990315e-3, 1.25055119e-2, 4.14247999e-5]
expected_maom_to_lmwc = [1.45988485e-3, 2.10172756e-3, 4.69571604e-3, 8.62951373e-6]

actual_rates = calculate_enzyme_mediated_rates(
soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"],
soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"],
soil_c_pool_pom=dummy_carbon_data["soil_c_pool_pom"],
soil_c_pool_maom=dummy_carbon_data["soil_c_pool_maom"],
soil_temp=dummy_carbon_data["soil_temperature"][
fixture_core_components.layer_structure.index_topsoil_scalar
],
env_factors=environmental_factors,
constants=SoilConsts,
)

assert np.allclose(actual_rates.pom_to_lmwc, expected_pom_to_lmwc)
assert np.allclose(actual_rates.maom_to_lmwc, expected_maom_to_lmwc)


def test_calculate_enzyme_changes(dummy_carbon_data):
"""Check that the determination of enzyme pool changes works correctly."""

from virtual_ecosystem.models.soil.carbon import calculate_enzyme_changes

biomass_loss = np.array([0.05443078, 0.02298407, 0.12012258, 0.00722288])

expected_pom = [1.17571917e-8, 1.67442231e-8, 1.83311362e-9, -1.11675865e-8]
expected_maom = [-3.10092243e-4, -5.09592558e-5, 5.99065833e-4, -3.72111676e-5]
expected_denat = [0.0013987, 0.00051062, 0.00180338, 0.00018168]

actual_pom, actual_maom, actual_denat = calculate_enzyme_changes(
soil_enzyme_pom=dummy_carbon_data["soil_enzyme_pom"],
soil_enzyme_maom=dummy_carbon_data["soil_enzyme_maom"],
biomass_loss=biomass_loss,
constants=SoilConsts,
)

assert np.allclose(actual_pom, expected_pom)
assert np.allclose(actual_maom, expected_maom)
assert np.allclose(actual_denat, expected_denat)


def test_calculate_maintenance_biomass_synthesis(
dummy_carbon_data, fixture_core_components
Expand Down Expand Up @@ -178,8 +223,8 @@ def test_calculate_microbial_carbon_uptake(
actual_uptake, actual_assimilation = calculate_microbial_carbon_uptake(
soil_c_pool_lmwc=dummy_carbon_data["soil_c_pool_lmwc"],
soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"],
water_factor=environmental_factors["water"],
pH_factor=environmental_factors["pH"],
water_factor=environmental_factors.water,
pH_factor=environmental_factors.pH,
soil_temp=dummy_carbon_data["soil_temperature"][
fixture_core_components.layer_structure.index_topsoil_scalar
].to_numpy(),
Expand All @@ -203,12 +248,10 @@ def test_calculate_enzyme_mediated_decomposition(
actual_decomp = calculate_enzyme_mediated_decomposition(
soil_c_pool=dummy_carbon_data["soil_c_pool_pom"],
soil_enzyme=dummy_carbon_data["soil_enzyme_pom"],
water_factor=environmental_factors["water"],
pH_factor=environmental_factors["pH"],
clay_factor_saturation=environmental_factors["clay_saturation"],
soil_temp=dummy_carbon_data["soil_temperature"][
fixture_core_components.layer_structure.index_topsoil_scalar
],
env_factors=environmental_factors,
reference_temp=SoilConsts.arrhenius_reference_temp,
max_decomp_rate=SoilConsts.max_decomp_rate_pom,
activation_energy_rate=SoilConsts.activation_energy_pom_decomp_rate,
Expand All @@ -217,3 +260,63 @@ def test_calculate_enzyme_mediated_decomposition(
)

assert np.allclose(actual_decomp, expected_decomp)


def test_calculate_maom_desorption(dummy_carbon_data):
"""Check that mineral associated matter desorption is calculated correctly."""

from virtual_ecosystem.models.soil.carbon import calculate_maom_desorption

expected_desorption = [2.5e-5, 1.7e-5, 4.5e-5, 5.0e-6]

actual_desorption = calculate_maom_desorption(
soil_c_pool_maom=dummy_carbon_data["soil_c_pool_maom"],
desorption_rate_constant=SoilConsts.maom_desorption_rate,
)

assert np.allclose(actual_desorption, expected_desorption)


@pytest.mark.parametrize(
"pool_name,sorption_rate_constant,expected_sorption",
[
(
"soil_c_pool_lmwc",
SoilConsts.lmwc_sorption_rate,
[5.0e-5, 2.0e-5, 0.0001, 5.0e-6],
),
(
"soil_c_pool_necromass",
SoilConsts.necromass_sorption_rate,
[0.04020253647, 0.01039720771, 0.06446268779, 0.07278045396],
),
],
)
def test_calculate_sorption_to_maom(
dummy_carbon_data, pool_name, sorption_rate_constant, expected_sorption
):
"""Check that sorption to mineral associated matter is calculated correctly."""

from virtual_ecosystem.models.soil.carbon import calculate_sorption_to_maom

actual_sorption = calculate_sorption_to_maom(
soil_c_pool=dummy_carbon_data[pool_name],
sorption_rate_constant=sorption_rate_constant,
)

assert np.allclose(actual_sorption, expected_sorption)


def test_calculate_necromass_breakdown(dummy_carbon_data):
"""Check that necromass breakdown to lmwc is calculated correctly."""

from virtual_ecosystem.models.soil.carbon import calculate_necromass_breakdown

expected_breakdown = [0.0134008455, 0.0034657359, 0.0214875626, 0.0242601513]

actual_breakdown = calculate_necromass_breakdown(
soil_c_pool_necromass=dummy_carbon_data["soil_c_pool_necromass"],
necromass_decay_rate=SoilConsts.necromass_decay_rate,
)

assert np.allclose(actual_breakdown, expected_breakdown)
19 changes: 0 additions & 19 deletions tests/models/soil/test_env_factors.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def test_calculate_environmental_effect_factors(
expected_water = [1.0, 0.94414168, 0.62176357, 0.07747536]
expected_pH = [0.25, 1.0, 0.428571428, 1.0]
expected_clay_sat = [1.782, 1.102, 0.83, 1.918]
expected_clay_decay = [0.52729242, 0.78662786, 0.92311634, 0.48675225]

env_factors = calculate_environmental_effect_factors(
soil_water_potential=dummy_carbon_data["matric_potential"][
Expand All @@ -53,7 +52,6 @@ def test_calculate_environmental_effect_factors(
assert np.allclose(env_factors.water, expected_water)
assert np.allclose(env_factors.pH, expected_pH)
assert np.allclose(env_factors.clay_saturation, expected_clay_sat)
assert np.allclose(env_factors.clay_decay, expected_clay_decay)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -205,23 +203,6 @@ def test_calculate_clay_impact_on_enzyme_saturation(dummy_carbon_data):
assert np.allclose(expected_factor, actual_factor)


def test_calculate_clay_impact_on_necromass_decay(dummy_carbon_data):
"""Test calculation of the effect of soil clay fraction on necromass decay."""
from virtual_ecosystem.models.soil.constants import SoilConsts
from virtual_ecosystem.models.soil.env_factors import (
calculate_clay_impact_on_necromass_decay,
)

expected_factor = [0.52729242, 0.78662786, 0.92311634, 0.48675225]

actual_factor = calculate_clay_impact_on_necromass_decay(
clay_fraction=dummy_carbon_data["clay_fraction"],
decay_exponent=SoilConsts.clay_necromass_decay_exponent,
)

assert np.allclose(expected_factor, actual_factor)


def test_calculate_leaching_rate(dummy_carbon_data, fixture_core_components):
"""Test calculation of solute leaching rates."""
from virtual_ecosystem.models.soil.constants import SoilConsts
Expand Down
Loading

0 comments on commit 48e4b0a

Please sign in to comment.