From 6b5cb53a4cad3389a5b557bf6f6d08c921964a7d Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 5 Jun 2024 14:27:10 +0100 Subject: [PATCH 01/22] Added a (for now inert) necromass pool --- tests/conftest.py | 12 ++++++++---- tests/core/test_data.py | 1 + tests/models/soil/test_carbon.py | 2 ++ tests/models/soil/test_soil_model.py | 9 +++++++++ .../example_data/data/example_soil_data.nc | Bin 20792 -> 21262 bytes .../generation_scripts/soil_example_data.py | 5 +++++ virtual_ecosystem/models/soil/carbon.py | 5 +++++ virtual_ecosystem/models/soil/soil_model.py | 2 ++ 8 files changed, 32 insertions(+), 4 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 0d772ca0b..9e8b9c75a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -276,11 +276,11 @@ def dummy_carbon_data(fixture_core_components): grid = Grid(cell_nx=4, cell_ny=1) data = Data(grid) - # The required data is now added. This includes the four carbon pools: mineral + # The required data is now added. This includes the five 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. + # particulate organic matter, microbial necromass. It also includes various factors + # of the physical environment: pH, bulk density, soil moisture, soil temperature, + # percentage clay in soil. data["soil_c_pool_lmwc"] = DataArray([0.05, 0.02, 0.1, 0.005], dims=["cell_id"]) """Low molecular weight carbon pool (kg C m^-3)""" data["soil_c_pool_maom"] = DataArray([2.5, 1.7, 4.5, 0.5], dims=["cell_id"]) @@ -289,6 +289,10 @@ def dummy_carbon_data(fixture_core_components): """Microbial biomass (carbon) pool (kg C m^-3)""" data["soil_c_pool_pom"] = DataArray([0.1, 1.0, 0.7, 0.35], dims=["cell_id"]) """Particulate organic matter pool (kg C m^-3)""" + data["soil_c_pool_necromass"] = DataArray( + [0.058, 0.015, 0.093, 0.105], dims=["cell_id"] + ) + """Microbial biomass (carbon) pool (kg C m^-3)""" data["soil_enzyme_pom"] = DataArray( [0.022679, 0.009576, 0.050051, 0.003010], dims=["cell_id"] ) diff --git a/tests/core/test_data.py b/tests/core/test_data.py index 808290922..9071a4f09 100644 --- a/tests/core/test_data.py +++ b/tests/core/test_data.py @@ -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", ], diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 9354e2d7f..35a6e78fc 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -19,6 +19,7 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): "soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5], "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_necromass": [0.0, 0.0, 0.0, 0.0], "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], } @@ -33,6 +34,7 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): 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"], diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 3282b9b9a..01eb03e67 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -229,6 +229,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): end_maom = [2.50019883, 1.70000589, 4.50007171, 0.50000014] end_microbe = [5.8, 2.3, 11.3, 1.0] end_pom = [0.25, 2.34, 0.746, 0.3467] + end_necromass = [0.058, 0.015, 0.093, 0.105] mock_integrate = mocker.patch.object(fixture_soil_model, "integrate") @@ -238,6 +239,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): soil_c_pool_maom=DataArray(end_maom, dims="cell_id"), soil_c_pool_microbe=DataArray(end_microbe, dims="cell_id"), soil_c_pool_pom=DataArray(end_pom, dims="cell_id"), + soil_c_pool_necromass=DataArray(end_necromass, dims="cell_id"), ) ) @@ -251,6 +253,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): assert np.allclose(dummy_carbon_data["soil_c_pool_maom"], end_maom) assert np.allclose(dummy_carbon_data["soil_c_pool_microbe"], end_microbe) assert np.allclose(dummy_carbon_data["soil_c_pool_pom"], end_pom) + assert np.allclose(dummy_carbon_data["soil_c_pool_necromass"], end_necromass) @pytest.mark.parametrize( @@ -273,6 +276,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): pom=DataArray( [0.12397575, 1.00508662, 0.7389913, 0.35583206], dims="cell_id" ), + necromass=DataArray([0.058, 0.015, 0.093, 0.105], dims="cell_id"), enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" ), @@ -317,6 +321,7 @@ def test_integrate_soil_model( assert np.allclose(new_pools["soil_c_pool_maom"], final_pools["maom"]) assert np.allclose(new_pools["soil_c_pool_microbe"], final_pools["microbe"]) assert np.allclose(new_pools["soil_c_pool_pom"], final_pools["pom"]) + assert np.allclose(new_pools["soil_c_pool_necromass"], final_pools["necromass"]) assert np.allclose(new_pools["soil_enzyme_pom"], final_pools["enzyme_pom"]) assert np.allclose(new_pools["soil_enzyme_maom"], final_pools["enzyme_maom"]) @@ -413,6 +418,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): 1.02354410e-2, 7.85372753e-2, 1.16756409e-2, + 0.0, + 0.0, + 0.0, + 0.0, 1.17571917e-8, 1.67442231e-8, 1.83311362e-9, diff --git a/virtual_ecosystem/example_data/data/example_soil_data.nc b/virtual_ecosystem/example_data/data/example_soil_data.nc index ba2cee6667462d69f0bda95fd616957d2a75a5a8..1426655daa044735d103decab4cc1cec9ce4c47d 100644 GIT binary patch delta 2046 zcmb_dYfMx}6rQ`FK?A+O%EusC{`7_^{bIv#4 zIdf<3^|#>b7h$_^kVifsLt~qO(Z4y}Xh_LpB=0kUWX;K=Lze`zJFI>%vZHsiTWY1* za@f*TRo`)YlQZCRM+Z9=tDy&aH(@<$gvZ&+^zcsf7d(ghqW#!q0Ho+fbhew=&M!t& z+80o(R-;gxY)jDYxv^)v`YO*monZXa@e)~<*?a~(ef+Xyg$R>+qbrOrI^+_;G}ZUW1Y*08R;13A4}7q6D{rpo}%#yRdz&dFGfg*^ntC z`weLkE7r367beG1yg0~10wg#_7~`FHM^K$uwQ6ZaRA}Kl#kI_iOdPOUbQZSr)DP;? z@(d>6iVX(X%fT2twT~vWO&S6vXj~bBjz|O8Km39g`HRw{P@avWR`cZ`qjQYX{P5@$ zl_~-4jKBpzAbOM%&!Xu;7j#n>!$0wShd}`{5%fcHnG$Y9J7r4r!@7QC!fR?`^?S*P z6)IT#9j|66n8>6EC3Bm79#u3FBE3L_bUrd-r4XypZlw{c-)UQYXkRCzjF>h8g&$|H)7}NrrN&2Zi}@G8MoHTh0CzoT_E(_{d1?DBiO-K zq<)9SqN;hbMgaqI*M#O_8-Rina*J?0l4bN!wCUepeaPCcenXgR#Xsk{Ah6;goM(*v z%G?@mo8X^lGn}pE+CokE@MiA+ns@v;vG3B}AWr&;=8bSwF!$4|eP3Lf3FgK!BuvC{ zHP`Oh5<8LPkt3q73L5^3234{!4Y?3p)WD=>u|2F zxn&?SIg(ScMIKS_MRA{=i9QsyX{&>W3ZVH9)X}21eQBOGl6yEkslQyag}Z**<#!*0 zh7oww5CuJQu?i;r@JE7R&ucS`z0Vq7_jgv;Zoc=If5!pF00xEGP8$ znfq~ETJVcZw#9ic*K2WSCQ z0M3^Xg5}{yUuIY6`04nQ5Z0i=#B}k2(y@8qkMB(=>Phq!r(+ZdHa-rxi|^$@h?0_8 z^`U_N0D#TyZu`&ysKL>j9WN|U;B{{xrQR@N{&@D}9n7W2T)JO3lr>$5fh>KKVe_Ir z7_w57vU4R_VKLX#l~tBCn$2}(4ONu~&GqFLiz?X`23e}alf+duv>dLl!ol@jsWieJ z?a`|kwF$qvfGWW*KKuW3|5uF#x_R=&UR}@9jqTqi62>9Agpr>qh)Mn z8W#r1q9#gK-VilT(-0UW8buBPG{zY5heYGlAN(LjO%#4il3|Q7Y!W^9^mg8m?EJXz zd7tNbpL5PV?>Rn4uACw>Rkhm^AVxzz08QQeEfxv&QspA85tzl94YZSJ_K26%XWv;b z_wF4!m`aZh93IPFTPKY#57X>oia7D3?G>0IFH3<>$DYAuauNfQH~W_tA9q;W)>VTf z%zWUngXajJ7f~Pp7Fktw;+9p=a3UYTT_p-Eif$`C!cp|uHdW@ui)p5h;N ze-(EGSYyDZFg*LV!kFb%ss%VIRAO0xAkVMvA4N=Bqx7c`9%?9GIZFq6Y){)AKh_}< z=QcKByG)c>fc<(s&eKh}z4%OR$9;yKJU`yOM~QVtcICG_R)UheRXTdNB8hEs?HUI- zxJDm!$%R2YAZsS*^{ZRHU?=2Z+8W{l^G?G&@9%3}=<1Ec!NwB`#u@k={x!xdHaY8v z1G8Ek9&`F6>xHksVRO!D;_Bimv)@jP6*X(u9+RgETXn$3xb6i|N{}e6{7(9Awm9(f zKr_2r@*%~zYA0R{svh#7{>5KPbLivPMv>*pRFV1hjD1%&LG%>kZoi0AMm+D%6eo@A z?m!o}36!fVNJtu# zV_Sw@ySdtY9X2wT2i-H2{m^!dkRa~av2(+@is%1^ zL%hZZXQ$s^0ym$oh0m7U|3EcgyasZm*Wcu4o+Q}b{!&v7$ikn0f}CEz($hwvQeYnv zKBbN0gSb%kDO|no!e0<>? zZ}ueK=v5hpj}n*u9L!D${gR|VTX*ck`Nj?6ho25)xZcNVBCk>QXwMzWa@s2tZzt}% M7(*wGex-!}1KL3za{vGU diff --git a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py index 593b3dfa3..4ada5c15f 100644 --- a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py @@ -35,6 +35,10 @@ # Generate a range of plausible values (0.1-1.0) for the POM pool [kg C m^-3]. pom_values = 0.1 + 0.9 * (gradient) / (64) +# Generate a range of plausible values (0.00015-0.0005) for the microbial necromass pool +# [kg C m^-3]. +necromass_values = 0.00015 + 0.00035 * (gradient) / (64) + # Generate a range of plausible values (0.01-0.5) for the POM enzyme pool [kg C m^-3]. pom_enzyme_values = 0.01 + 0.49 * (gradient) / (64) @@ -51,6 +55,7 @@ soil_c_pool_maom=(["x", "y"], maom_values), soil_c_pool_microbe=(["x", "y"], microbial_C_values), soil_c_pool_pom=(["x", "y"], pom_values), + soil_c_pool_necromass=(["x", "y"], necromass_values), soil_enzyme_pom=(["x", "y"], pom_enzyme_values), soil_enzyme_maom=(["x", "y"], maom_enzyme_values), ), diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 838566069..778e2fa88 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -42,6 +42,7 @@ def calculate_soil_carbon_updates( soil_c_pool_maom: NDArray[np.float32], soil_c_pool_microbe: NDArray[np.float32], soil_c_pool_pom: NDArray[np.float32], + soil_c_pool_necromass: NDArray[np.float32], soil_enzyme_pom: NDArray[np.float32], soil_enzyme_maom: NDArray[np.float32], pH: NDArray[np.float32], @@ -67,6 +68,7 @@ def calculate_soil_carbon_updates( soil_c_pool_maom: Mineral associated organic matter pool [kg C m^-3] soil_c_pool_microbe: Microbial biomass (carbon) pool [kg C m^-3] soil_c_pool_pom: Particulate organic matter pool [kg C m^-3] + soil_c_pool_necromass: Microbial necromass pool [kg C m^-3] soil_enzyme_pom: Amount of enzyme class which breaks down particulate organic matter [kg C m^-3] soil_enzyme_maom: Amount of enzyme class which breaks down mineral associated @@ -178,6 +180,9 @@ def calculate_soil_carbon_updates( + biomass_losses.necromass_decay_to_pom - pom_decomposition_rate ) + delta_pools_ordered["soil_c_pool_necromass"] = np.zeros( + soil_c_pool_necromass.size, dtype=np.float32 + ) delta_pools_ordered["soil_enzyme_pom"] = ( biomass_losses.pom_enzyme_production - pom_enzyme_turnover ) diff --git a/virtual_ecosystem/models/soil/soil_model.py b/virtual_ecosystem/models/soil/soil_model.py index 137d307ce..abe14534e 100644 --- a/virtual_ecosystem/models/soil/soil_model.py +++ b/virtual_ecosystem/models/soil/soil_model.py @@ -59,6 +59,7 @@ class SoilModel( "soil_c_pool_lmwc", "soil_c_pool_microbe", "soil_c_pool_pom", + "soil_c_pool_necromass", "soil_enzyme_pom", "soil_enzyme_maom", ), @@ -93,6 +94,7 @@ def __init__( or np.any(data["soil_c_pool_lmwc"] < 0.0) or np.any(data["soil_c_pool_microbe"] < 0.0) or np.any(data["soil_c_pool_pom"] < 0.0) + or np.any(data["soil_c_pool_necromass"] < 0.0) ): to_raise = InitialisationError( "Initial carbon pools contain at least one negative value!" From f02bcc9d4f279a713da04100673f9b35337fad09 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 5 Jun 2024 14:40:46 +0100 Subject: [PATCH 02/22] Remembered to actually add the new necromass pool to the data config --- virtual_ecosystem/example_data/config/data_config.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtual_ecosystem/example_data/config/data_config.toml b/virtual_ecosystem/example_data/config/data_config.toml index 3f872cc1b..a59913ac2 100644 --- a/virtual_ecosystem/example_data/config/data_config.toml +++ b/virtual_ecosystem/example_data/config/data_config.toml @@ -58,6 +58,9 @@ file = "../data/example_soil_data.nc" var_name = "soil_c_pool_pom" [[core.data.variable]] file = "../data/example_soil_data.nc" +var_name = "soil_c_pool_necromass" +[[core.data.variable]] +file = "../data/example_soil_data.nc" var_name = "soil_enzyme_pom" [[core.data.variable]] file = "../data/example_soil_data.nc" From e5b7ffb011f9e3cfcc70864227d98c9ccc71641a Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 5 Jun 2024 15:03:51 +0100 Subject: [PATCH 03/22] Started adding enzyme turnover to necromass pool --- tests/models/soil/test_carbon.py | 4 ++-- tests/models/soil/test_soil_model.py | 22 ++++++++++++---------- virtual_ecosystem/models/soil/carbon.py | 5 ++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 35a6e78fc..3eaf220fc 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,11 +15,11 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): 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_lmwc": [-0.004255446, 0.002555196, -0.019693034, 0.00082771], "soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5], "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_necromass": [0.0, 0.0, 0.0, 0.0], + "soil_c_pool_necromass": [0.001398696, 0.000510624, 0.001803384, 0.00018168], "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], } diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 01eb03e67..dc141232e 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,7 +265,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.04826774, 0.02126701, 0.09200601, 0.00544793], dims="cell_id" + [0.04801161, 0.02116316, 0.09147897, 0.00541192], dims="cell_id" ), maom=DataArray( [2.49936689, 1.70118553, 4.50085129, 0.50000614], dims="cell_id" @@ -276,7 +276,9 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): pom=DataArray( [0.12397575, 1.00508662, 0.7389913, 0.35583206], dims="cell_id" ), - necromass=DataArray([0.058, 0.015, 0.093, 0.105], dims="cell_id"), + necromass=DataArray( + [0.05869842, 0.01525516, 0.09390347, 0.10509073], dims="cell_id" + ), enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" ), @@ -402,10 +404,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - -0.00371115, - 0.00278502, - -0.01849181, - 0.00089995, + -0.004255446, + 0.002555196, + -0.019693034, + 0.00082771, -1.28996257e-3, 2.35822401e-3, 1.5570399e-3, @@ -418,10 +420,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): 1.02354410e-2, 7.85372753e-2, 1.16756409e-2, - 0.0, - 0.0, - 0.0, - 0.0, + 0.001398696, + 0.000510624, + 0.001803384, + 0.00018168, 1.17571917e-8, 1.67442231e-8, 1.83311362e-9, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 778e2fa88..4f1632a53 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -164,7 +164,6 @@ def calculate_soil_carbon_updates( delta_pools_ordered["soil_c_pool_lmwc"] = ( pom_decomposition_to_lmwc + biomass_losses.necromass_decay_to_lmwc - + pom_enzyme_turnover + maom_decomposition_to_lmwc - microbial_uptake - labile_carbon_leaching @@ -180,8 +179,8 @@ def calculate_soil_carbon_updates( + biomass_losses.necromass_decay_to_pom - pom_decomposition_rate ) - delta_pools_ordered["soil_c_pool_necromass"] = np.zeros( - soil_c_pool_necromass.size, dtype=np.float32 + delta_pools_ordered["soil_c_pool_necromass"] = ( + pom_enzyme_turnover + maom_enzyme_turnover ) delta_pools_ordered["soil_enzyme_pom"] = ( biomass_losses.pom_enzyme_production - pom_enzyme_turnover From ac3a2e3e509328cc201d5e741825eb0d75423be3 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 5 Jun 2024 15:39:32 +0100 Subject: [PATCH 04/22] Lost microbial biomass now becomes necromass --- tests/models/soil/test_carbon.py | 15 ++++------ tests/models/soil/test_soil_model.py | 35 +++++++++++----------- virtual_ecosystem/models/soil/carbon.py | 28 +++++------------ virtual_ecosystem/models/soil/constants.py | 6 ---- 4 files changed, 31 insertions(+), 53 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 3eaf220fc..69678bc5f 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,11 +15,11 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates change_in_pools = { - "soil_c_pool_lmwc": [-0.004255446, 0.002555196, -0.019693034, 0.00082771], + "soil_c_pool_lmwc": [-0.011287175, -0.001874381, -0.046860377, -3.36495e-5], "soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5], "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_necromass": [0.001398696, 0.000510624, 0.001803384, 0.00018168], + "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], + "soil_c_pool_necromass": [0.054740855, 0.023035011, 0.119523517, 0.0072601095], "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], } @@ -69,8 +69,7 @@ def test_determine_microbial_biomass_losses( 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] + expected_necromass_formation = [0.053342159, 0.022524387, 0.117720133, 0.0070784295] losses = determine_microbial_biomass_losses( soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"], @@ -83,8 +82,7 @@ def test_determine_microbial_biomass_losses( 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) + assert np.allclose(losses.necromass_formation, expected_necromass_formation) # Then check that sum of other rates is the same as the overall # maintenance_synthesis rate @@ -92,8 +90,7 @@ def test_determine_microbial_biomass_losses( losses.maintenance_synthesis, losses.pom_enzyme_production + losses.maom_enzyme_production - + losses.necromass_decay_to_lmwc - + losses.necromass_decay_to_pom, + + losses.necromass_formation, ) diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index dc141232e..4f1079956 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,19 +265,20 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.04801161, 0.02116316, 0.09147897, 0.00541192], dims="cell_id" + [0.04470103, 0.01915703, 0.07950671, 0.00498331], dims="cell_id" ), maom=DataArray( - [2.49936689, 1.70118553, 4.50085129, 0.50000614], dims="cell_id" + [2.49935708, 1.70117552, 4.50075105, 0.50000609], dims="cell_id" ), microbe=DataArray( - [5.77512315, 2.2899636, 11.24827514, 0.99640928], dims="cell_id" + [5.77504346, 2.28989195, 11.24785387, 0.99640886], + dims="cell_id", ), pom=DataArray( - [0.12397575, 1.00508662, 0.7389913, 0.35583206], dims="cell_id" + [0.10088985, 0.99607906, 0.69401895, 0.35272921], dims="cell_id" ), necromass=DataArray( - [0.05869842, 0.01525516, 0.09390347, 0.10509073], dims="cell_id" + [0.08531216, 0.02649261, 0.15262834, 0.10862358], dims="cell_id" ), enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" @@ -404,10 +405,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - -0.004255446, - 0.002555196, - -0.019693034, - 0.00082771, + -0.011287175, + -0.001874381, + -0.046860377, + -3.36495e-5, -1.28996257e-3, 2.35822401e-3, 1.5570399e-3, @@ -416,14 +417,14 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): -0.02020101, -0.10280967, -0.00719517, - 4.80916464e-2, - 1.02354410e-2, - 7.85372753e-2, - 1.16756409e-2, - 0.001398696, - 0.000510624, - 0.001803384, - 0.00018168, + 0.00178122, + -0.00785937, + -0.01201551, + 0.00545857, + 0.054740855, + 0.023035011, + 0.119523517, + 0.0072601095, 1.17571917e-8, 1.67442231e-8, 1.83311362e-9, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 4f1632a53..a2b0a994d 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -29,10 +29,8 @@ class MicrobialBiomassLoss: """Rate at which POM degrading enzymes are produced [kg C m^-3 day^-1].""" maom_enzyme_production: NDArray[np.float32] """Rate at which MAOM degrading enzymes are produced [kg C m^-3 day^-1].""" - necromass_decay_to_lmwc: NDArray[np.float32] - """Rate at which biomass is lost to the LMWC pool [kg C m^-3 day^-1].""" - necromass_decay_to_pom: NDArray[np.float32] - """Rate at which biomass is lost to the POM pool [kg C m^-3 day^-1].""" + necromass_formation: NDArray[np.float32] + """Rate at which living biomass enters the necromass pool [kg C m^-3 day^-1].""" # TODO - This function should probably be shortened, leaving as is for the moment as a @@ -163,7 +161,6 @@ def calculate_soil_carbon_updates( # Determine net changes to the pools delta_pools_ordered["soil_c_pool_lmwc"] = ( pom_decomposition_to_lmwc - + biomass_losses.necromass_decay_to_lmwc + maom_decomposition_to_lmwc - microbial_uptake - labile_carbon_leaching @@ -175,12 +172,10 @@ def calculate_soil_carbon_updates( microbial_assimilation - biomass_losses.maintenance_synthesis ) delta_pools_ordered["soil_c_pool_pom"] = ( - mineralisation_rate - + biomass_losses.necromass_decay_to_pom - - pom_decomposition_rate + mineralisation_rate - pom_decomposition_rate ) delta_pools_ordered["soil_c_pool_necromass"] = ( - pom_enzyme_turnover + maom_enzyme_turnover + biomass_losses.necromass_formation + pom_enzyme_turnover + maom_enzyme_turnover ) delta_pools_ordered["soil_enzyme_pom"] = ( biomass_losses.pom_enzyme_production - pom_enzyme_turnover @@ -209,8 +204,6 @@ def determine_microbial_biomass_losses( Args: soil_c_pool_microbe: Microbial biomass (carbon) pool [kg C m^-3] soil_temp: soil temperature for each soil grid cell [degrees C] - clay_factor_decay: A factor capturing the impact of soil clay fraction on - necromass decay destination [unitless] constants: Set of constants for the soil model. Returns: @@ -229,23 +222,16 @@ def determine_microbial_biomass_losses( maom_enzyme_production = constants.maintenance_maom_enzyme * maintenance_synthesis # Remaining maintenance synthesis is used to replace degraded proteins and cells - replacement_synthesis = ( + # (i.e. forms necromass) + necromass_formation = ( 1 - constants.maintenance_pom_enzyme - constants.maintenance_maom_enzyme ) * maintenance_synthesis - # TODO - This split will change when a necromass pool is introduced - # Calculate fraction of necromass that decays to LMWC - necromass_proportion_to_lmwc = constants.necromass_to_lmwc * clay_factor_decay - # These proteins and cells that are replaced decay into either the POM or LMWC pool - necromass_to_lmwc = necromass_proportion_to_lmwc * replacement_synthesis - necromass_to_pom = (1 - necromass_proportion_to_lmwc) * replacement_synthesis - return MicrobialBiomassLoss( maintenance_synthesis=maintenance_synthesis, pom_enzyme_production=pom_enzyme_production, maom_enzyme_production=maom_enzyme_production, - necromass_decay_to_lmwc=necromass_to_lmwc, - necromass_decay_to_pom=necromass_to_pom, + necromass_formation=necromass_formation, ) diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index 4f505e817..d237d9dbe 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -200,12 +200,6 @@ class SoilConsts(ConstantsDataclass): [unitless]. Value taken from :cite:t:`wang_development_2013`. """ - necromass_to_lmwc: float = 0.25 - """Proportion of necromass that flows to LMWC rather than POM [unitless]. - - Value taken from :cite:t:`wang_development_2013`. - """ - # TODO - The 4 constants below should take different values for fungi and bacteria, # once that separation is implemented. min_pH_microbes: float = 2.5 From b3a6aa6b3824021667f54956076ed86e2615ad0f Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Tue, 18 Jun 2024 15:29:09 +0100 Subject: [PATCH 05/22] Added simple function to calculate necromass decay to lmwc --- tests/models/soil/test_carbon.py | 1 - virtual_ecosystem/models/soil/carbon.py | 24 ++++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 69678bc5f..1354e71bb 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -74,7 +74,6 @@ def test_determine_microbial_biomass_losses( losses = determine_microbial_biomass_losses( soil_c_pool_microbe=dummy_carbon_data["soil_c_pool_microbe"], soil_temp=dummy_carbon_data["soil_temperature"][top_soil_layer_index], - clay_factor_decay=environmental_factors["clay_decay"], constants=SoilConsts, ) diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 9a6836b57..80345b863 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -108,7 +108,6 @@ def calculate_soil_carbon_updates( biomass_losses = determine_microbial_biomass_losses( soil_c_pool_microbe=soil_c_pool_microbe, soil_temp=soil_temp, - clay_factor_decay=env_factors.clay_decay, constants=model_constants, ) pom_enzyme_turnover = calculate_enzyme_turnover( @@ -192,7 +191,6 @@ def calculate_soil_carbon_updates( def determine_microbial_biomass_losses( soil_c_pool_microbe: NDArray[np.float32], soil_temp: NDArray[np.float32], - clay_factor_decay: NDArray[np.float32], constants: SoilConsts, ) -> MicrobialBiomassLoss: """Calculate all of the losses from the microbial biomass pool. @@ -444,3 +442,25 @@ def calculate_enzyme_mediated_decomposition( return ( rate_constant * soil_enzyme * soil_c_pool / (saturation_constant + soil_c_pool) ) + + +def calculate_necromass_breakdown( + soil_c_pool_necromass: NDArray[np.float32], necromass_breakdown_rate: float +) -> NDArray[np.float32]: + """Calculate breakdown rate of necromass into low molecular weight carbon (LMWC). + + This function calculate necromass breakdown to LMWC as a simple exponential decay. + This decay is not effected by temperature or any other environmental factor. The + idea is to keep this function as simple as possible, because it will be hard to + parametrise even without additional complications. However, this is a simplification + to bear in mind when planning future model improvements. + + Args: + soil_c_pool_necromass: Size of the microbial necromass pool [kg C m^-3] + necromass_breakdown_rate: Rate at which necromass breaks down into LMWC [day^-1] + + Returns: + The amount of necromass that breakdown to LMWC [kg C m^-3 day^-1] + """ + + return -necromass_breakdown_rate * soil_c_pool_necromass From 351f1d3a6e87885c4db26ff78744684986ad0aff Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Thu, 27 Jun 2024 10:45:53 +0100 Subject: [PATCH 06/22] Removed outdated function for clay sorption --- tests/models/soil/conftest.py | 7 ----- tests/models/soil/test_env_factors.py | 19 -------------- virtual_ecosystem/models/soil/env_factors.py | 27 -------------------- 3 files changed, 53 deletions(-) diff --git a/tests/models/soil/conftest.py b/tests/models/soil/conftest.py index 75dff6e43..11b9bb4d7 100644 --- a/tests/models/soil/conftest.py +++ b/tests/models/soil/conftest.py @@ -41,7 +41,6 @@ def environmental_factors(dummy_carbon_data, top_soil_layer_index): 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, ) @@ -71,14 +70,8 @@ def environmental_factors(dummy_carbon_data, top_soil_layer_index): 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 { "water": water_factors, "pH": pH_factors, "clay_saturation": clay_saturation_factors, - "clay_decay": clay_decay_factors, } diff --git a/tests/models/soil/test_env_factors.py b/tests/models/soil/test_env_factors.py index e1a5d0bb8..f35855148 100644 --- a/tests/models/soil/test_env_factors.py +++ b/tests/models/soil/test_env_factors.py @@ -34,7 +34,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"][ @@ -48,7 +47,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( @@ -196,23 +194,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, top_soil_layer_index): """Test calculation of solute leaching rates.""" from virtual_ecosystem.models.soil.constants import SoilConsts diff --git a/virtual_ecosystem/models/soil/env_factors.py b/virtual_ecosystem/models/soil/env_factors.py index b8e5983d8..18f3e6623 100644 --- a/virtual_ecosystem/models/soil/env_factors.py +++ b/virtual_ecosystem/models/soil/env_factors.py @@ -23,8 +23,6 @@ class EnvironmentalEffectFactors: """Impact of soil pH on enzymatic rates [unitless].""" clay_saturation: NDArray[np.float32] """Impact of soil clay fraction on enzyme saturation constants [unitless].""" - clay_decay: NDArray[np.float32] - """Impact of soil clay fraction on necromass decay destination [unitless].""" def calculate_environmental_effect_factors( @@ -71,17 +69,12 @@ def calculate_environmental_effect_factors( base_protection=constants.base_soil_protection, protection_with_clay=constants.soil_protection_with_clay, ) - clay_factor_decay = calculate_clay_impact_on_necromass_decay( - clay_fraction=clay_fraction, - decay_exponent=constants.clay_necromass_decay_exponent, - ) # Combine all factors into a single EnvironmentalFactors object return EnvironmentalEffectFactors( water=water_factor, pH=pH_factor, clay_saturation=clay_factor_saturation, - clay_decay=clay_factor_decay, ) @@ -248,26 +241,6 @@ def calculate_clay_impact_on_enzyme_saturation( return base_protection + protection_with_clay * clay_fraction -def calculate_clay_impact_on_necromass_decay( - clay_fraction: NDArray[np.float32], decay_exponent: float -) -> NDArray[np.float32]: - """Calculate the impact that soil clay has on necromass decay to LMWC. - - Necromass which doesn't breakdown fully gets added to the POM pool instead. - - Args: - clay_fraction: The fraction of the soil which is clay [unitless] - decay_exponent: Controls the impact that differences in soil clay content - have on the proportion of necromass that decays to LMWC [unitless] - - Returns: - A multiplicative factor capturing the impact that soil clay has on the - proportion of necromass decay which sorbs to form POM [unitless] - """ - - return np.exp(decay_exponent * clay_fraction) - - def calculate_leaching_rate( solute_density: NDArray[np.float32], vertical_flow_rate: NDArray[np.float32], From 5c63b7be98b7cafc823d7143eba286200cf021bd Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Thu, 27 Jun 2024 13:03:29 +0100 Subject: [PATCH 07/22] Added test of necromass decay function --- tests/models/soil/test_carbon.py | 15 +++++++++++++++ virtual_ecosystem/models/soil/carbon.py | 6 +++--- virtual_ecosystem/models/soil/constants.py | 18 ++++++++++-------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 1354e71bb..902f08fa0 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -205,3 +205,18 @@ def test_calculate_enzyme_mediated_decomposition( ) assert np.allclose(actual_decomp, expected_decomp) + + +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) diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 80345b863..43a782829 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -445,7 +445,7 @@ def calculate_enzyme_mediated_decomposition( def calculate_necromass_breakdown( - soil_c_pool_necromass: NDArray[np.float32], necromass_breakdown_rate: float + soil_c_pool_necromass: NDArray[np.float32], necromass_decay_rate: float ) -> NDArray[np.float32]: """Calculate breakdown rate of necromass into low molecular weight carbon (LMWC). @@ -457,10 +457,10 @@ def calculate_necromass_breakdown( Args: soil_c_pool_necromass: Size of the microbial necromass pool [kg C m^-3] - necromass_breakdown_rate: Rate at which necromass breaks down into LMWC [day^-1] + necromass_decay_rate: Rate at which necromass decays into LMWC [day^-1] Returns: The amount of necromass that breakdown to LMWC [kg C m^-3 day^-1] """ - return -necromass_breakdown_rate * soil_c_pool_necromass + return -necromass_decay_rate * soil_c_pool_necromass diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index d237d9dbe..84f95f137 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -5,6 +5,8 @@ from dataclasses import dataclass +from numpy import log + from virtual_ecosystem.core.constants_class import ConstantsDataclass # TODO - Once lignin is tracked a large number of constants will have to be duplicated @@ -247,14 +249,6 @@ class SoilConsts(ConstantsDataclass): The value of this constant is taken from :cite:t:`fatichi_mechanistic_2019`. """ - clay_necromass_decay_exponent: float = -0.8 - """Change in proportion of necromass which decays with increasing soil clay content. - - [unitless]. The function this is used in is an exponential, and the sign should be - negative so increases in clay leads to a lower proportion of necromass decaying to - LMWC. The value of this constant is taken from :cite:t:`fatichi_mechanistic_2019`. - """ - pom_decomposition_fraction_lmwc: float = 0.5 """Fraction of decomposed POM that becomes LMWC rather than MAOM [unitless]. @@ -267,3 +261,11 @@ class SoilConsts(ConstantsDataclass): Value taken from :cite:t:`fatichi_mechanistic_2019`, where it is estimated in quite a loose manner. """ + + necromass_decay_rate: float = (1 / 3) * log(2) + """Rate at which microbial necromass decays to low molecular weight carbon [day^-1] + + I have not been able to track down any data on this, so for now choosing a rate that + corresponds to halving every three days. This parameter is a key target for tracking + down data for and for sensitivity analysis. + """ From f3c380c18f8daff1052c7c1fc8bb25af8e75fa43 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Thu, 27 Jun 2024 13:32:31 +0100 Subject: [PATCH 08/22] Added necromass decay into main carbon integration --- tests/models/soil/test_carbon.py | 6 +++--- tests/models/soil/test_soil_model.py | 22 +++++++++++----------- virtual_ecosystem/models/soil/carbon.py | 13 +++++++++++-- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 902f08fa0..9df921232 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,11 +15,11 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates change_in_pools = { - "soil_c_pool_lmwc": [-0.011287175, -0.001874381, -0.046860377, -3.36495e-5], + "soil_c_pool_lmwc": [0.0021136705, 0.0015913549, -0.0253728144, 0.0242265018], "soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5], "soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517], "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], - "soil_c_pool_necromass": [0.054740855, 0.023035011, 0.119523517, 0.0072601095], + "soil_c_pool_necromass": [0.041340010, 0.019569275, 0.098035954, -0.017000042], "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], } @@ -212,7 +212,7 @@ def test_calculate_necromass_breakdown(dummy_carbon_data): from virtual_ecosystem.models.soil.carbon import calculate_necromass_breakdown - expected_breakdown = [-0.0134008455, -0.0034657359, -0.0214875626, -0.0242601513] + 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"], diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 4f1079956..38580e502 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,20 +265,20 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.04470103, 0.01915703, 0.07950671, 0.00498331], dims="cell_id" + [0.05210826, 0.02123014, 0.09141469, 0.01660416], dims="cell_id" ), maom=DataArray( [2.49935708, 1.70117552, 4.50075105, 0.50000609], dims="cell_id" ), microbe=DataArray( - [5.77504346, 2.28989195, 11.24785387, 0.99640886], + [5.7752017, 2.28995877, 11.24831909, 0.99642481], dims="cell_id", ), pom=DataArray( [0.10088985, 0.99607906, 0.69401895, 0.35272921], dims="cell_id" ), necromass=DataArray( - [0.08531216, 0.02649261, 0.15262834, 0.10862358], dims="cell_id" + [0.07746489, 0.02421675, 0.13916487, 0.09696637], dims="cell_id" ), enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" @@ -405,10 +405,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - -0.011287175, - -0.001874381, - -0.046860377, - -3.36495e-5, + 0.0021136705, + 0.0015913549, + -0.0253728144, + 0.0242265018, -1.28996257e-3, 2.35822401e-3, 1.5570399e-3, @@ -421,10 +421,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): -0.00785937, -0.01201551, 0.00545857, - 0.054740855, - 0.023035011, - 0.119523517, - 0.0072601095, + 0.041340010, + 0.019569275, + 0.098035954, + -0.017000042, 1.17571917e-8, 1.67442231e-8, 1.83311362e-9, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 43a782829..342b7e387 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -157,11 +157,17 @@ def calculate_soil_carbon_updates( half_saturation=model_constants.half_sat_maom_decomposition, activation_energy_sat=model_constants.activation_energy_maom_decomp_saturation, ) + # Calculate necromass decay to lmwc and sorption to maom + necromass_decay_to_lmwc = calculate_necromass_breakdown( + soil_c_pool_necromass=soil_c_pool_necromass, + necromass_decay_rate=model_constants.necromass_decay_rate, + ) # Determine net changes to the pools delta_pools_ordered["soil_c_pool_lmwc"] = ( pom_decomposition_to_lmwc + maom_decomposition_to_lmwc + + necromass_decay_to_lmwc - microbial_uptake - labile_carbon_leaching ) @@ -175,7 +181,10 @@ def calculate_soil_carbon_updates( mineralisation_rate - pom_decomposition_rate ) delta_pools_ordered["soil_c_pool_necromass"] = ( - biomass_losses.necromass_formation + pom_enzyme_turnover + maom_enzyme_turnover + biomass_losses.necromass_formation + + pom_enzyme_turnover + + maom_enzyme_turnover + - necromass_decay_to_lmwc ) delta_pools_ordered["soil_enzyme_pom"] = ( biomass_losses.pom_enzyme_production - pom_enzyme_turnover @@ -463,4 +472,4 @@ def calculate_necromass_breakdown( The amount of necromass that breakdown to LMWC [kg C m^-3 day^-1] """ - return -necromass_decay_rate * soil_c_pool_necromass + return necromass_decay_rate * soil_c_pool_necromass From 346e1d732ed18c49119c7d4ce21ff92c3c4cb648 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Thu, 27 Jun 2024 14:17:09 +0100 Subject: [PATCH 09/22] Removed the POM to MAOM flow --- tests/models/soil/test_carbon.py | 4 ++-- tests/models/soil/test_soil_model.py | 22 +++++++++++----------- virtual_ecosystem/models/soil/carbon.py | 15 +++------------ virtual_ecosystem/models/soil/constants.py | 6 ------ 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 9df921232..fa328fc52 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,8 +15,8 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates change_in_pools = { - "soil_c_pool_lmwc": [0.0021136705, 0.0015913549, -0.0253728144, 0.0242265018], - "soil_c_pool_maom": [-1.28996257e-3, 2.35822401e-3, 1.5570399e-3, 1.2082886e-5], + "soil_c_pool_lmwc": [0.0022835928, 0.0060513065, -0.019120058, 0.0242472142], + "soil_c_pool_maom": [-0.0014598849, -0.0021017276, -0.0046957161, -8.629514e-6], "soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517], "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], "soil_c_pool_necromass": [0.041340010, 0.019569275, 0.098035954, -0.017000042], diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 38580e502..f1924dbd2 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,13 +265,13 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.05210826, 0.02123014, 0.09141469, 0.01660416], dims="cell_id" + [0.05218859, 0.02324093, 0.09414528, 0.01661453], dims="cell_id" ), maom=DataArray( - [2.49935708, 1.70117552, 4.50075105, 0.50000609], dims="cell_id" + [2.49927175, 1.69895061, 4.49763884, 0.49999569], dims="cell_id" ), microbe=DataArray( - [5.7752017, 2.28995877, 11.24831909, 0.99642481], + [5.7752035, 2.29002929, 11.24843316, 0.99642482], dims="cell_id", ), pom=DataArray( @@ -405,14 +405,14 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - 0.0021136705, - 0.0015913549, - -0.0253728144, - 0.0242265018, - -1.28996257e-3, - 2.35822401e-3, - 1.5570399e-3, - 1.2082886e-5, + 0.0022835928, + 0.0060513065, + -0.019120058, + 0.0242472142, + -0.0014598849, + -0.0021017276, + -0.0046957161, + -8.629514e-6, -0.04978105, -0.02020101, -0.10280967, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 342b7e387..d0a7fb313 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -124,7 +124,7 @@ def calculate_soil_carbon_updates( soil_moisture=soil_moisture, solubility_coefficient=model_constants.solubility_coefficient_lmwc, ) - pom_decomposition_rate = calculate_enzyme_mediated_decomposition( + pom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( soil_c_pool=soil_c_pool_pom, soil_enzyme=soil_enzyme_pom, water_factor=env_factors.water, @@ -137,13 +137,6 @@ def calculate_soil_carbon_updates( half_saturation=model_constants.half_sat_pom_decomposition, activation_energy_sat=model_constants.activation_energy_pom_decomp_saturation, ) - # Calculate how pom decomposition is split between lmwc and maom pools - pom_decomposition_to_lmwc = ( - pom_decomposition_rate * model_constants.pom_decomposition_fraction_lmwc - ) - pom_decomposition_to_maom = pom_decomposition_rate * ( - 1 - model_constants.pom_decomposition_fraction_lmwc - ) maom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( soil_c_pool=soil_c_pool_maom, soil_enzyme=soil_enzyme_maom, @@ -171,14 +164,12 @@ def calculate_soil_carbon_updates( - microbial_uptake - labile_carbon_leaching ) - delta_pools_ordered["soil_c_pool_maom"] = ( - pom_decomposition_to_maom - maom_decomposition_to_lmwc - ) + delta_pools_ordered["soil_c_pool_maom"] = -maom_decomposition_to_lmwc delta_pools_ordered["soil_c_pool_microbe"] = ( microbial_assimilation - biomass_losses.maintenance_synthesis ) delta_pools_ordered["soil_c_pool_pom"] = ( - mineralisation_rate - pom_decomposition_rate + mineralisation_rate - pom_decomposition_to_lmwc ) delta_pools_ordered["soil_c_pool_necromass"] = ( biomass_losses.necromass_formation diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index 84f95f137..d48dfb1d9 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -249,12 +249,6 @@ class SoilConsts(ConstantsDataclass): The value of this constant is taken from :cite:t:`fatichi_mechanistic_2019`. """ - pom_decomposition_fraction_lmwc: float = 0.5 - """Fraction of decomposed POM that becomes LMWC rather than MAOM [unitless]. - - Value taken from :cite:t:`wang_development_2013`. - """ - solubility_coefficient_lmwc: float = 0.05 """Solubility coefficient for low molecular weight organic carbon [unitless]. From 5ecc1f0d32e7fd23a9d1bd6908c5b999e22a6e20 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 28 Jun 2024 11:09:28 +0100 Subject: [PATCH 10/22] Added a function to calculate MAOM desorption --- tests/models/soil/test_carbon.py | 15 +++++++++++++++ virtual_ecosystem/models/soil/carbon.py | 21 +++++++++++++++++++++ virtual_ecosystem/models/soil/constants.py | 8 ++++++++ 3 files changed, 44 insertions(+) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index fa328fc52..eadf2453a 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -207,6 +207,21 @@ 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) + + def test_calculate_necromass_breakdown(dummy_carbon_data): """Check that necromass breakdown to lmwc is calculated correctly.""" diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index d0a7fb313..fcd0db500 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -444,6 +444,27 @@ def calculate_enzyme_mediated_decomposition( ) +def calculate_maom_desorption( + soil_c_pool_maom: NDArray[np.float32], desorption_rate_constant: float +): + """Calculate the rate of mineral associated organic matter (MAOM) desorption. + + This function is independent of soil temperature, moisture, pH, clay fraction and + bulk density. All of these things are known to effect real world desorption rates. + However, to simplify the parameterisation we only include these effects on microbial + rates. This may be something we want to alter in future. + + Args: + soil_c_pool_maom: Size of the mineral associated organic matter pool [kg C m^-3] + desorption_rate_constant: Rate constant for MAOM desorption [day^-1] + + Returns: + The rate of MAOM desorption to LMWC [kg C m^-3 day^-1] + """ + + return desorption_rate_constant * soil_c_pool_maom + + def calculate_necromass_breakdown( soil_c_pool_necromass: NDArray[np.float32], necromass_decay_rate: float ) -> NDArray[np.float32]: diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index d48dfb1d9..d67f55e87 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -263,3 +263,11 @@ class SoilConsts(ConstantsDataclass): corresponds to halving every three days. This parameter is a key target for tracking down data for and for sensitivity analysis. """ + + maom_desorption_rate: float = 1e-5 + """Rate constant for mineral associated organic matter desorption [day^-1] + + The default value of this rate is not based on data. It was instead chosen to be + small relative to the rate at which microbes breakdown LMWC. This is another key + target for sensitivity analysis. + """ From 15e3e4443be7b8043e9181b0a102f2f8a82dcfe1 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 28 Jun 2024 11:22:34 +0100 Subject: [PATCH 11/22] Added MAOM desorption to the carbon integration --- tests/models/soil/test_carbon.py | 4 ++-- tests/models/soil/test_soil_model.py | 18 +++++++++--------- virtual_ecosystem/models/soil/carbon.py | 12 +++++++++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index eadf2453a..b6a203958 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,8 +15,8 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates change_in_pools = { - "soil_c_pool_lmwc": [0.0022835928, 0.0060513065, -0.019120058, 0.0242472142], - "soil_c_pool_maom": [-0.0014598849, -0.0021017276, -0.0046957161, -8.629514e-6], + "soil_c_pool_lmwc": [0.0023085928, 0.0060683065, -0.019075058, 0.0242522142], + "soil_c_pool_maom": [-0.001484885, -0.002118728, -0.004740716, -1.362951e-5], "soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517], "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], "soil_c_pool_necromass": [0.041340010, 0.019569275, 0.098035954, -0.017000042], diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index f1924dbd2..3bba3da6c 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,7 +265,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.05218859, 0.02324093, 0.09414528, 0.01661453], dims="cell_id" + [0.05220035, 0.02324861, 0.09416501, 0.01661702], dims="cell_id" ), maom=DataArray( [2.49927175, 1.69895061, 4.49763884, 0.49999569], dims="cell_id" @@ -405,14 +405,14 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - 0.0022835928, - 0.0060513065, - -0.019120058, - 0.0242472142, - -0.0014598849, - -0.0021017276, - -0.0046957161, - -8.629514e-6, + 0.0023085928, + 0.0060683065, + -0.019075058, + 0.0242522142, + -0.001484885, + -0.002118728, + -0.004740716, + -1.362951e-5, -0.04978105, -0.02020101, -0.10280967, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index fcd0db500..e54a3081e 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -35,6 +35,9 @@ class MicrobialBiomassLoss: # TODO - This function should probably be shortened, leaving as is for the moment as a # sensible split will probably be more obvious once more is added to this function. +# TODO - Some level of cleanup is needed before I submit this PR, because it's actually +# hard to read at this point. I don't need to fix it completely, but need to do +# something to improve the readability def calculate_soil_carbon_updates( soil_c_pool_lmwc: NDArray[np.float32], soil_c_pool_maom: NDArray[np.float32], @@ -150,6 +153,10 @@ def calculate_soil_carbon_updates( half_saturation=model_constants.half_sat_maom_decomposition, activation_energy_sat=model_constants.activation_energy_maom_decomp_saturation, ) + maom_desorption_to_lmwc = calculate_maom_desorption( + soil_c_pool_maom=soil_c_pool_maom, + desorption_rate_constant=model_constants.maom_desorption_rate, + ) # Calculate necromass decay to lmwc and sorption to maom necromass_decay_to_lmwc = calculate_necromass_breakdown( soil_c_pool_necromass=soil_c_pool_necromass, @@ -160,11 +167,14 @@ def calculate_soil_carbon_updates( delta_pools_ordered["soil_c_pool_lmwc"] = ( pom_decomposition_to_lmwc + maom_decomposition_to_lmwc + + maom_desorption_to_lmwc + necromass_decay_to_lmwc - microbial_uptake - labile_carbon_leaching ) - delta_pools_ordered["soil_c_pool_maom"] = -maom_decomposition_to_lmwc + delta_pools_ordered["soil_c_pool_maom"] = ( + -maom_decomposition_to_lmwc - maom_desorption_to_lmwc + ) delta_pools_ordered["soil_c_pool_microbe"] = ( microbial_assimilation - biomass_losses.maintenance_synthesis ) From 17fea27f0c5282f1a83322cc5b67b27320a18fe2 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 28 Jun 2024 13:07:38 +0100 Subject: [PATCH 12/22] Added simple function to calculate mineral sorption rates --- tests/models/soil/test_carbon.py | 30 ++++++++++++++++++++++ virtual_ecosystem/models/soil/carbon.py | 24 +++++++++++++++++ virtual_ecosystem/models/soil/constants.py | 17 ++++++++++++ 3 files changed, 71 insertions(+) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index b6a203958..80f672b36 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -222,6 +222,36 @@ def test_calculate_maom_desorption(dummy_carbon_data): 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.""" diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index e54a3081e..fb91a7380 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -475,6 +475,30 @@ def calculate_maom_desorption( return desorption_rate_constant * soil_c_pool_maom +def calculate_sorption_to_maom( + soil_c_pool: NDArray[np.float32], sorption_rate_constant: float +): + """Calculate that a carbon pool sorbs to become mineral associated organic matter. + + Carbon from both the low molecular weight carbon pool and the necromass pool can + sorb to minerals to form MAOM, so this function can be used for either pool. + + This function is independent of soil temperature, moisture, pH, clay fraction and + bulk density. All of these things are known to effect real world desorption rates. + However, to simplify the parameterisation we only include these effects on microbial + rates. This may be something we want to alter in future. + + Args: + soil_c_pool: Size of carbon pool [kg C m^-3] + sorption_rate_constant: Rate constant for sorption to MAOM [day^-1] + + Returns: + The rate of sorption to MAOM [kg C m^-3 day^-1] + """ + + return sorption_rate_constant * soil_c_pool + + def calculate_necromass_breakdown( soil_c_pool_necromass: NDArray[np.float32], necromass_decay_rate: float ) -> NDArray[np.float32]: diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index d67f55e87..1b17d9e7f 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -271,3 +271,20 @@ class SoilConsts(ConstantsDataclass): small relative to the rate at which microbes breakdown LMWC. This is another key target for sensitivity analysis. """ + + lmwc_sorption_rate: float = 1e-3 + """Rate constant for low molecular weight carbon sorption to minerals [day^-1] + + The default value of this rate is not based on data. It was instead chosen so that + the ratio of LWMC to mineral associated organic matter would tend to 1/100, in the + absence of microbes. This is another key target for sensitivity analysis. + """ + + necromass_sorption_rate: float = 1 * log(2) + """Rate constant for necromass sorption to minerals [day^-1] + + The default value was chosen to be three times the value of + :attr:`necromass_decay_rate`, this means that 75% of necromass becomes MAOM with the + remainder becoming LMWC. Replacing this with a function that depends on + environmental conditions is a post release goal. + """ From ed0f58a27b18b5a02f9c067a8fd63a7f826115ca Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 28 Jun 2024 13:24:00 +0100 Subject: [PATCH 13/22] Added necromass sorption to carbon integration --- tests/models/soil/test_carbon.py | 4 ++-- tests/models/soil/test_soil_model.py | 22 +++++++++++----------- virtual_ecosystem/models/soil/carbon.py | 9 ++++++++- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 80f672b36..ce5b6f69e 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -16,10 +16,10 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): change_in_pools = { "soil_c_pool_lmwc": [0.0023085928, 0.0060683065, -0.019075058, 0.0242522142], - "soil_c_pool_maom": [-0.001484885, -0.002118728, -0.004740716, -1.362951e-5], + "soil_c_pool_maom": [0.038717651, 0.008278480, 0.059721972, 0.072766824], "soil_c_pool_microbe": [-0.04978105, -0.02020101, -0.10280967, -0.00719517], "soil_c_pool_pom": [0.00178122, -0.00785937, -0.01201551, 0.00545857], - "soil_c_pool_necromass": [0.041340010, 0.019569275, 0.098035954, -0.017000042], + "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], } diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index 3bba3da6c..d7dd7d204 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,10 +265,10 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.05220035, 0.02324861, 0.09416501, 0.01661702], dims="cell_id" + [0.05112704, 0.02295503, 0.09244137, 0.01485784], dims="cell_id" ), maom=DataArray( - [2.49927175, 1.69895061, 4.49763884, 0.49999569], dims="cell_id" + [2.51943652, 1.70482162, 4.53233323, 0.52967525], dims="cell_id" ), microbe=DataArray( [5.7752035, 2.29002929, 11.24843316, 0.99642482], @@ -278,7 +278,7 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): [0.10088985, 0.99607906, 0.69401895, 0.35272921], dims="cell_id" ), necromass=DataArray( - [0.07746489, 0.02421675, 0.13916487, 0.09696637], dims="cell_id" + [0.05840539, 0.01865113, 0.10632815, 0.06904724], dims="cell_id" ), enzyme_pom=DataArray( [0.02267842, 0.00957576, 0.05004963, 0.00300993], dims="cell_id" @@ -409,10 +409,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): 0.0060683065, -0.019075058, 0.0242522142, - -0.001484885, - -0.002118728, - -0.004740716, - -1.362951e-5, + 0.038717651, + 0.008278480, + 0.059721972, + 0.072766824, -0.04978105, -0.02020101, -0.10280967, @@ -421,10 +421,10 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): -0.00785937, -0.01201551, 0.00545857, - 0.041340010, - 0.019569275, - 0.098035954, - -0.017000042, + 0.001137474, + 0.009172067, + 0.033573266, + -0.08978050, 1.17571917e-8, 1.67442231e-8, 1.83311362e-9, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index fb91a7380..021513ae4 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -162,6 +162,10 @@ def calculate_soil_carbon_updates( soil_c_pool_necromass=soil_c_pool_necromass, necromass_decay_rate=model_constants.necromass_decay_rate, ) + necromass_sorption_to_maom = calculate_sorption_to_maom( + soil_c_pool=soil_c_pool_necromass, + sorption_rate_constant=model_constants.necromass_sorption_rate, + ) # Determine net changes to the pools delta_pools_ordered["soil_c_pool_lmwc"] = ( @@ -173,7 +177,9 @@ def calculate_soil_carbon_updates( - labile_carbon_leaching ) delta_pools_ordered["soil_c_pool_maom"] = ( - -maom_decomposition_to_lmwc - maom_desorption_to_lmwc + necromass_sorption_to_maom + - maom_decomposition_to_lmwc + - maom_desorption_to_lmwc ) delta_pools_ordered["soil_c_pool_microbe"] = ( microbial_assimilation - biomass_losses.maintenance_synthesis @@ -186,6 +192,7 @@ def calculate_soil_carbon_updates( + pom_enzyme_turnover + maom_enzyme_turnover - necromass_decay_to_lmwc + - necromass_sorption_to_maom ) delta_pools_ordered["soil_enzyme_pom"] = ( biomass_losses.pom_enzyme_production - pom_enzyme_turnover From 1e6b3c718568fda85bb8827d2c142b11400ebe43 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Fri, 28 Jun 2024 13:45:51 +0100 Subject: [PATCH 14/22] Added lmwc sorption back into the carbon integration --- tests/models/soil/test_carbon.py | 4 ++-- tests/models/soil/test_soil_model.py | 20 ++++++++++---------- virtual_ecosystem/models/soil/carbon.py | 6 ++++++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index ce5b6f69e..51a0d0898 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -15,8 +15,8 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.carbon import calculate_soil_carbon_updates change_in_pools = { - "soil_c_pool_lmwc": [0.0023085928, 0.0060683065, -0.019075058, 0.0242522142], - "soil_c_pool_maom": [0.038717651, 0.008278480, 0.059721972, 0.072766824], + "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.00178122, -0.00785937, -0.01201551, 0.00545857], "soil_c_pool_necromass": [0.001137474, 0.009172067, 0.033573266, -0.08978050], diff --git a/tests/models/soil/test_soil_model.py b/tests/models/soil/test_soil_model.py index d7dd7d204..e89394274 100644 --- a/tests/models/soil/test_soil_model.py +++ b/tests/models/soil/test_soil_model.py @@ -265,10 +265,10 @@ def test_update(mocker, fixture_soil_model, dummy_carbon_data): Dataset( data_vars=dict( lmwc=DataArray( - [0.05112704, 0.02295503, 0.09244137, 0.01485784], dims="cell_id" + [0.05110324, 0.0229453, 0.09239938, 0.01485271], dims="cell_id" ), maom=DataArray( - [2.51943652, 1.70482162, 4.53233323, 0.52967525], dims="cell_id" + [2.5194618, 1.70483236, 4.53238116, 0.52968038], dims="cell_id" ), microbe=DataArray( [5.7752035, 2.29002929, 11.24843316, 0.99642482], @@ -405,14 +405,14 @@ def test_construct_full_soil_model(dummy_carbon_data, top_soil_layer_index): from virtual_ecosystem.models.soil.soil_model import construct_full_soil_model delta_pools = [ - 0.0023085928, - 0.0060683065, - -0.019075058, - 0.0242522142, - 0.038717651, - 0.008278480, - 0.059721972, - 0.072766824, + 0.0022585928, + 0.0060483065, + -0.019175058, + 0.024247214, + 0.038767651, + 0.00829848, + 0.05982197, + 0.07277182, -0.04978105, -0.02020101, -0.10280967, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 021513ae4..e62cc58c6 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -166,6 +166,10 @@ def calculate_soil_carbon_updates( soil_c_pool=soil_c_pool_necromass, sorption_rate_constant=model_constants.necromass_sorption_rate, ) + lmwc_sorption_to_maom = calculate_sorption_to_maom( + soil_c_pool=soil_c_pool_lmwc, + sorption_rate_constant=model_constants.lmwc_sorption_rate, + ) # Determine net changes to the pools delta_pools_ordered["soil_c_pool_lmwc"] = ( @@ -174,10 +178,12 @@ def calculate_soil_carbon_updates( + maom_desorption_to_lmwc + necromass_decay_to_lmwc - microbial_uptake + - lmwc_sorption_to_maom - labile_carbon_leaching ) delta_pools_ordered["soil_c_pool_maom"] = ( necromass_sorption_to_maom + + lmwc_sorption_to_maom - maom_decomposition_to_lmwc - maom_desorption_to_lmwc ) From 271ae8fb0e176803b73f7ba4d89a22439c508247 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Mon, 1 Jul 2024 10:12:04 +0100 Subject: [PATCH 15/22] Grouped microbial pool changes to simplify function flow --- tests/models/soil/conftest.py | 10 +- tests/models/soil/test_carbon.py | 73 +++++---- virtual_ecosystem/models/soil/carbon.py | 193 ++++++++++++++++-------- 3 files changed, 178 insertions(+), 98 deletions(-) diff --git a/tests/models/soil/conftest.py b/tests/models/soil/conftest.py index 11b9bb4d7..d48ef69cb 100644 --- a/tests/models/soil/conftest.py +++ b/tests/models/soil/conftest.py @@ -2,6 +2,8 @@ import pytest +from virtual_ecosystem.models.soil.env_factors import EnvironmentalEffectFactors + @pytest.fixture def fixture_soil_config(): @@ -70,8 +72,6 @@ def environmental_factors(dummy_carbon_data, top_soil_layer_index): protection_with_clay=soil_constants.soil_protection_with_clay, ) - return { - "water": water_factors, - "pH": pH_factors, - "clay_saturation": clay_saturation_factors, - } + return EnvironmentalEffectFactors( + water=water_factors, pH=pH_factors, clay_saturation=clay_saturation_factors + ) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 51a0d0898..b27df83c8 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -60,38 +60,59 @@ def test_calculate_soil_carbon_updates(dummy_carbon_data, top_soil_layer_index): assert np.allclose(delta_pools[i * 4 : (i + 1) * 4], change_in_pools[pool]) -def test_determine_microbial_biomass_losses( - dummy_carbon_data, top_soil_layer_index, environmental_factors +def test_calculate_microbial_changes( + dummy_carbon_data, environmental_factors, top_soil_layer_index ): - """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.""" + + from virtual_ecosystem.models.soil.carbon import calculate_microbial_changes - 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_necromass_formation = [0.053342159, 0.022524387, 0.117720133, 0.0070784295] + 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] - losses = determine_microbial_biomass_losses( + 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"][top_soil_layer_index], + 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_formation, expected_necromass_formation) - - # 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_formation, + assert np.allclose(mic_changes.lmwc_uptake, expected_lmwc_uptake) + assert np.allclose(mic_changes.microbe, expected_microbe) + assert np.allclose(mic_changes.pom_enzyme, expected_pom_enzyme) + assert np.allclose(mic_changes.maom_enzyme, expected_maom_enzyme) + assert np.allclose(mic_changes.necromass_generation, expected_necromass) + + +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, top_soil_layer_index @@ -168,8 +189,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"][ top_soil_layer_index ].to_numpy(), @@ -193,9 +214,9 @@ 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"], + water_factor=environmental_factors.water, + pH_factor=environmental_factors.pH, + clay_factor_saturation=environmental_factors.clay_saturation, soil_temp=dummy_carbon_data["soil_temperature"][top_soil_layer_index], reference_temp=SoilConsts.arrhenius_reference_temp, max_decomp_rate=SoilConsts.max_decomp_rate_pom, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index e62cc58c6..1bbfa490a 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -12,6 +12,7 @@ from virtual_ecosystem.core.constants import CoreConsts from virtual_ecosystem.models.soil.constants import SoilConsts from virtual_ecosystem.models.soil.env_factors import ( + EnvironmentalEffectFactors, calculate_environmental_effect_factors, calculate_leaching_rate, calculate_temperature_effect_on_microbes, @@ -19,25 +20,27 @@ @dataclass -class MicrobialBiomassLoss: - """A data class to store the various biomass losses from microbial biomass.""" +class MicrobialChanges: + """Changes due to microbial uptake, biomass production and losses.""" - maintenance_synthesis: NDArray[np.float32] - """Rate at which biomass must be synthesised to balance losses [kg C m^-3 day^-1]. - """ - pom_enzyme_production: NDArray[np.float32] - """Rate at which POM degrading enzymes are produced [kg C m^-3 day^-1].""" - maom_enzyme_production: NDArray[np.float32] - """Rate at which MAOM degrading enzymes are produced [kg C m^-3 day^-1].""" - necromass_formation: NDArray[np.float32] - """Rate at which living biomass enters the necromass pool [kg C m^-3 day^-1].""" + lmwc_uptake: NDArray[np.float32] + """Total rate of microbial LMWC uptake [kg C m^-3 day^-1].""" + + microbe: NDArray[np.float32] + """Rate of change of microbial biomass pool [kg C m^-3 day^-1].""" + + pom_enzyme: NDArray[np.float32] + """Rate of change of POM degrading enzyme pool [kg C m^-3 day^-1].""" + + maom_enzyme: NDArray[np.float32] + """Rate of change of MAOM degrading enzyme pool [kg C m^-3 day^-1].""" + + necromass_generation: NDArray[np.float32] + """Rate at which necromass is being produced [kg C m^-3 day^-1].""" # TODO - This function should probably be shortened, leaving as is for the moment as a # sensible split will probably be more obvious once more is added to this function. -# TODO - Some level of cleanup is needed before I submit this PR, because it's actually -# hard to read at this point. I don't need to fix it completely, but need to do -# something to improve the readability def calculate_soil_carbon_updates( soil_c_pool_lmwc: NDArray[np.float32], soil_c_pool_maom: NDArray[np.float32], @@ -99,34 +102,25 @@ def calculate_soil_carbon_updates( clay_fraction=clay_fraction, constants=model_constants, ) - - microbial_uptake, microbial_assimilation = calculate_microbial_carbon_uptake( + # find changes related to microbial uptake, growth and decay + microbial_changes = calculate_microbial_changes( soil_c_pool_lmwc=soil_c_pool_lmwc, soil_c_pool_microbe=soil_c_pool_microbe, - water_factor=env_factors.water, - pH_factor=env_factors.pH, + soil_enzyme_pom=soil_enzyme_pom, + soil_enzyme_maom=soil_enzyme_maom, soil_temp=soil_temp, + env_factors=env_factors, constants=model_constants, ) - biomass_losses = determine_microbial_biomass_losses( - soil_c_pool_microbe=soil_c_pool_microbe, - soil_temp=soil_temp, - constants=model_constants, - ) - pom_enzyme_turnover = calculate_enzyme_turnover( - enzyme_pool=soil_enzyme_pom, - turnover_rate=model_constants.pom_enzyme_turnover_rate, - ) - maom_enzyme_turnover = calculate_enzyme_turnover( - enzyme_pool=soil_enzyme_maom, - turnover_rate=model_constants.maom_enzyme_turnover_rate, - ) + labile_carbon_leaching = calculate_leaching_rate( solute_density=soil_c_pool_lmwc, vertical_flow_rate=vertical_flow_rate, soil_moisture=soil_moisture, solubility_coefficient=model_constants.solubility_coefficient_lmwc, ) + # TODO - This function should be changed to just receive env_factors + # TODO - These two should be combined into a function pom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( soil_c_pool=soil_c_pool_pom, soil_enzyme=soil_enzyme_pom, @@ -153,6 +147,7 @@ def calculate_soil_carbon_updates( half_saturation=model_constants.half_sat_maom_decomposition, activation_energy_sat=model_constants.activation_energy_maom_decomp_saturation, ) + # TODO - Maybe the 4 below should be a function maom_desorption_to_lmwc = calculate_maom_desorption( soil_c_pool_maom=soil_c_pool_maom, desorption_rate_constant=model_constants.maom_desorption_rate, @@ -177,7 +172,7 @@ def calculate_soil_carbon_updates( + maom_decomposition_to_lmwc + maom_desorption_to_lmwc + necromass_decay_to_lmwc - - microbial_uptake + - microbial_changes.lmwc_uptake - lmwc_sorption_to_maom - labile_carbon_leaching ) @@ -187,73 +182,137 @@ def calculate_soil_carbon_updates( - maom_decomposition_to_lmwc - maom_desorption_to_lmwc ) - delta_pools_ordered["soil_c_pool_microbe"] = ( - microbial_assimilation - biomass_losses.maintenance_synthesis - ) + delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe delta_pools_ordered["soil_c_pool_pom"] = ( mineralisation_rate - pom_decomposition_to_lmwc ) delta_pools_ordered["soil_c_pool_necromass"] = ( - biomass_losses.necromass_formation - + pom_enzyme_turnover - + maom_enzyme_turnover + microbial_changes.necromass_generation - necromass_decay_to_lmwc - necromass_sorption_to_maom ) - delta_pools_ordered["soil_enzyme_pom"] = ( - biomass_losses.pom_enzyme_production - pom_enzyme_turnover - ) - delta_pools_ordered["soil_enzyme_maom"] = ( - biomass_losses.maom_enzyme_production - maom_enzyme_turnover - ) + delta_pools_ordered["soil_enzyme_pom"] = microbial_changes.pom_enzyme + delta_pools_ordered["soil_enzyme_maom"] = microbial_changes.maom_enzyme # Create output array of pools in desired order return np.concatenate(list(delta_pools_ordered.values())) -def determine_microbial_biomass_losses( +def calculate_microbial_changes( + soil_c_pool_lmwc: NDArray[np.float32], soil_c_pool_microbe: NDArray[np.float32], + soil_enzyme_pom: NDArray[np.float32], + soil_enzyme_maom: NDArray[np.float32], soil_temp: NDArray[np.float32], + env_factors: EnvironmentalEffectFactors, constants: SoilConsts, -) -> MicrobialBiomassLoss: - """Calculate all of the losses from the microbial biomass pool. +): + """Calculate the changes for the microbial biomass and enzyme pools. - Microbes need to synthesis new biomass at a certain rate just to maintain their - current biomass. This function calculates this overall rate and the various losses - that contribute to this rate. The main sources of this loss are the external - excretion of enzymes, cell death, and protein degradation. + This function calculates the uptake of low molecular weight carbon by the microbial + biomass pool and uses this to calculate the net change in the pool. The net change + in each enzyme pool is found, and finally the total rate at which necromass is + created is found. Args: + soil_c_pool_lmwc: Low molecular weight carbon pool [kg C m^-3] soil_c_pool_microbe: Microbial biomass (carbon) pool [kg C m^-3] + soil_enzyme_pom: Amount of enzyme class which breaks down particulate organic + matter [kg C m^-3] + soil_enzyme_maom: Amount of enzyme class which breaks down mineral associated + organic matter [kg C m^-3] soil_temp: soil temperature for each soil grid cell [degrees C] + env_factors: Data class containing the various factors through which the + environment effects soil cycling rates. constants: Set of constants for the soil model. Returns: - A dataclass containing all the losses from the microbial biomass pool. + A dataclass containing, the rate at which microbes uptake LMWC, the rate of + change in the microbial biomass pool and the enzyme pools. """ - # Calculate the rate of maintenance synthesis - maintenance_synthesis = calculate_maintenance_biomass_synthesis( + # Calculate uptake, growth rate, and loss rate + microbial_uptake, biomass_growth = calculate_microbial_carbon_uptake( + soil_c_pool_lmwc=soil_c_pool_lmwc, soil_c_pool_microbe=soil_c_pool_microbe, + water_factor=env_factors.water, + pH_factor=env_factors.pH, soil_temp=soil_temp, constants=constants, ) + biomass_loss = calculate_maintenance_biomass_synthesis( + soil_c_pool_microbe=soil_c_pool_microbe, + soil_temp=soil_temp, + constants=constants, + ) + # Find changes in each enzyme pool + pom_enzyme_net_change, maom_enzyme_net_change, enzyme_denaturation = ( + calculate_enzyme_changes( + soil_enzyme_pom=soil_enzyme_pom, + soil_enzyme_maom=soil_enzyme_maom, + biomass_loss=biomass_loss, + constants=constants, + ) + ) - # Calculation the production of each enzyme class - pom_enzyme_production = constants.maintenance_pom_enzyme * maintenance_synthesis - maom_enzyme_production = constants.maintenance_maom_enzyme * maintenance_synthesis - - # Remaining maintenance synthesis is used to replace degraded proteins and cells - # (i.e. forms necromass) - necromass_formation = ( + # Find fraction of loss that isn't enzyme production + true_loss = ( 1 - constants.maintenance_pom_enzyme - constants.maintenance_maom_enzyme - ) * maintenance_synthesis + ) * biomass_loss + + return MicrobialChanges( + lmwc_uptake=microbial_uptake, + microbe=biomass_growth - biomass_loss, + pom_enzyme=pom_enzyme_net_change, + maom_enzyme=maom_enzyme_net_change, + necromass_generation=enzyme_denaturation + true_loss, + ) + + +def calculate_enzyme_changes( + soil_enzyme_pom: NDArray[np.float32], + soil_enzyme_maom: NDArray[np.float32], + biomass_loss: NDArray[np.float32], + constants: SoilConsts, +) -> tuple[NDArray[np.float32], NDArray[np.float32], NDArray[np.float32]]: + """Calculate the changes to the concentration of each enzyme pool. + + Enzyme production rates are assumed to scale linearly with the total biomass loss + rate of the microbes. These are combined with turnover rates to find the net change + in each enzyme pool. The total enzyme denaturation rate is also calculated. + + Args: + soil_enzyme_pom: Amount of enzyme class which breaks down particulate organic + matter [kg C m^-3] + soil_enzyme_maom: Amount of enzyme class which breaks down mineral associated + organic matter [kg C m^-3] + biomass_loss: Rate a which the microbial biomass pool loses biomass, this is a + combination of enzyme excretion, protein degradation, and cell death [kg C + m^-3 day^-1] + constants: Set of constants for the soil model. + + Returns: + A tuple containing the net rate of change in the POM enzyme pool, the net rate + of change in the MAOM enzyme pool, and the total enzyme denaturation rate. + """ - return MicrobialBiomassLoss( - maintenance_synthesis=maintenance_synthesis, - pom_enzyme_production=pom_enzyme_production, - maom_enzyme_production=maom_enzyme_production, - necromass_formation=necromass_formation, + # Calculate production an turnover of each enzyme class + pom_enzyme_production = constants.maintenance_pom_enzyme * biomass_loss + maom_enzyme_production = constants.maintenance_maom_enzyme * biomass_loss + pom_enzyme_turnover = calculate_enzyme_turnover( + enzyme_pool=soil_enzyme_pom, + turnover_rate=constants.pom_enzyme_turnover_rate, + ) + maom_enzyme_turnover = calculate_enzyme_turnover( + enzyme_pool=soil_enzyme_maom, + turnover_rate=constants.maom_enzyme_turnover_rate, + ) + + # return net changes in the two enzyme pools and the necromass + return ( + pom_enzyme_production - pom_enzyme_turnover, + maom_enzyme_production - maom_enzyme_turnover, + pom_enzyme_turnover + maom_enzyme_turnover, ) From 16513fc8a0a2780062fa9f9b3f4517d791d43e0b Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Mon, 1 Jul 2024 11:18:01 +0100 Subject: [PATCH 16/22] Combined enzyme mediated rates into a single function --- tests/models/soil/test_carbon.py | 28 ++++- virtual_ecosystem/models/soil/carbon.py | 140 ++++++++++++++++-------- 2 files changed, 118 insertions(+), 50 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index b27df83c8..78ba15031 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -91,6 +91,30 @@ def test_calculate_microbial_changes( assert np.allclose(mic_changes.necromass_generation, expected_necromass) +def test_calculate_enzyme_mediated_rates( + dummy_carbon_data, environmental_factors, top_soil_layer_index +): + """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"][top_soil_layer_index], + 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.""" @@ -214,9 +238,7 @@ 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, + env_factors=environmental_factors, soil_temp=dummy_carbon_data["soil_temperature"][top_soil_layer_index], reference_temp=SoilConsts.arrhenius_reference_temp, max_decomp_rate=SoilConsts.max_decomp_rate_pom, diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 1bbfa490a..07c95c7ab 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -39,8 +39,19 @@ class MicrobialChanges: """Rate at which necromass is being produced [kg C m^-3 day^-1].""" -# TODO - This function should probably be shortened, leaving as is for the moment as a -# sensible split will probably be more obvious once more is added to this function. +@dataclass +class EnzymeMediatedRates: + """Rates of each enzyme mediated transfer between pools.""" + + pom_to_lmwc: NDArray[np.float32] + """Rate of POM decomposition to LMWC [kg C m^-3 day^-1].""" + + maom_to_lmwc: NDArray[np.float32] + """Rate of MAOM decomposition to LMWC [kg C m^-3 day^-1].""" + + +# TODO - This function should probably be shortened. I've done some work on this +# already, but I need to keep an eye on it as new pools are added. def calculate_soil_carbon_updates( soil_c_pool_lmwc: NDArray[np.float32], soil_c_pool_maom: NDArray[np.float32], @@ -112,6 +123,16 @@ def calculate_soil_carbon_updates( env_factors=env_factors, constants=model_constants, ) + # find changes driven by the enzyme pools + enzyme_mediated = calculate_enzyme_mediated_rates( + soil_enzyme_pom=soil_enzyme_pom, + soil_enzyme_maom=soil_enzyme_maom, + soil_c_pool_pom=soil_c_pool_pom, + soil_c_pool_maom=soil_c_pool_maom, + soil_temp=soil_temp, + env_factors=env_factors, + constants=model_constants, + ) labile_carbon_leaching = calculate_leaching_rate( solute_density=soil_c_pool_lmwc, @@ -119,40 +140,12 @@ def calculate_soil_carbon_updates( soil_moisture=soil_moisture, solubility_coefficient=model_constants.solubility_coefficient_lmwc, ) - # TODO - This function should be changed to just receive env_factors - # TODO - These two should be combined into a function - pom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( - soil_c_pool=soil_c_pool_pom, - soil_enzyme=soil_enzyme_pom, - water_factor=env_factors.water, - pH_factor=env_factors.pH, - clay_factor_saturation=env_factors.clay_saturation, - soil_temp=soil_temp, - reference_temp=model_constants.arrhenius_reference_temp, - max_decomp_rate=model_constants.max_decomp_rate_pom, - activation_energy_rate=model_constants.activation_energy_pom_decomp_rate, - half_saturation=model_constants.half_sat_pom_decomposition, - activation_energy_sat=model_constants.activation_energy_pom_decomp_saturation, - ) - maom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( - soil_c_pool=soil_c_pool_maom, - soil_enzyme=soil_enzyme_maom, - water_factor=env_factors.water, - pH_factor=env_factors.pH, - clay_factor_saturation=env_factors.clay_saturation, - soil_temp=soil_temp, - reference_temp=model_constants.arrhenius_reference_temp, - max_decomp_rate=model_constants.max_decomp_rate_maom, - activation_energy_rate=model_constants.activation_energy_maom_decomp_rate, - half_saturation=model_constants.half_sat_maom_decomposition, - activation_energy_sat=model_constants.activation_energy_maom_decomp_saturation, - ) - # TODO - Maybe the 4 below should be a function + + # Calculate transfers between the lmwc, necromass and maom pools maom_desorption_to_lmwc = calculate_maom_desorption( soil_c_pool_maom=soil_c_pool_maom, desorption_rate_constant=model_constants.maom_desorption_rate, ) - # Calculate necromass decay to lmwc and sorption to maom necromass_decay_to_lmwc = calculate_necromass_breakdown( soil_c_pool_necromass=soil_c_pool_necromass, necromass_decay_rate=model_constants.necromass_decay_rate, @@ -168,8 +161,8 @@ def calculate_soil_carbon_updates( # Determine net changes to the pools delta_pools_ordered["soil_c_pool_lmwc"] = ( - pom_decomposition_to_lmwc - + maom_decomposition_to_lmwc + enzyme_mediated.pom_to_lmwc + + enzyme_mediated.maom_to_lmwc + maom_desorption_to_lmwc + necromass_decay_to_lmwc - microbial_changes.lmwc_uptake @@ -179,12 +172,12 @@ def calculate_soil_carbon_updates( delta_pools_ordered["soil_c_pool_maom"] = ( necromass_sorption_to_maom + lmwc_sorption_to_maom - - maom_decomposition_to_lmwc + - enzyme_mediated.maom_to_lmwc - maom_desorption_to_lmwc ) delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe delta_pools_ordered["soil_c_pool_pom"] = ( - mineralisation_rate - pom_decomposition_to_lmwc + mineralisation_rate - enzyme_mediated.pom_to_lmwc ) delta_pools_ordered["soil_c_pool_necromass"] = ( microbial_changes.necromass_generation @@ -269,6 +262,63 @@ def calculate_microbial_changes( ) +def calculate_enzyme_mediated_rates( + soil_enzyme_pom: NDArray[np.float32], + soil_enzyme_maom: NDArray[np.float32], + soil_c_pool_pom: NDArray[np.float32], + soil_c_pool_maom: NDArray[np.float32], + soil_temp: NDArray[np.float32], + env_factors: EnvironmentalEffectFactors, + constants: SoilConsts, +) -> EnzymeMediatedRates: + """Calculate the rates of each enzyme mediated reaction. + + Args: + soil_enzyme_pom: Amount of enzyme class which breaks down particulate organic + matter [kg C m^-3] + soil_enzyme_maom: Amount of enzyme class which breaks down mineral associated + organic matter [kg C m^-3] + soil_c_pool_pom: Particulate organic matter pool [kg C m^-3] + soil_c_pool_maom: Mineral associated organic matter pool [kg C m^-3] + soil_temp: soil temperature for each soil grid cell [degrees C] + env_factors: Data class containing the various factors through which the + environment effects soil cycling rates. + constants: Set of constants for the soil model. + + Returns: + A dataclass containing the enzyme mediated decomposition rates of both the + particulate organic matter (POM) and mineral associated organic matter (MAOM) + pool. + """ + + pom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( + soil_c_pool=soil_c_pool_pom, + soil_enzyme=soil_enzyme_pom, + soil_temp=soil_temp, + env_factors=env_factors, + reference_temp=constants.arrhenius_reference_temp, + max_decomp_rate=constants.max_decomp_rate_pom, + activation_energy_rate=constants.activation_energy_pom_decomp_rate, + half_saturation=constants.half_sat_pom_decomposition, + activation_energy_sat=constants.activation_energy_pom_decomp_saturation, + ) + maom_decomposition_to_lmwc = calculate_enzyme_mediated_decomposition( + soil_c_pool=soil_c_pool_maom, + soil_enzyme=soil_enzyme_maom, + soil_temp=soil_temp, + env_factors=env_factors, + reference_temp=constants.arrhenius_reference_temp, + max_decomp_rate=constants.max_decomp_rate_maom, + activation_energy_rate=constants.activation_energy_maom_decomp_rate, + half_saturation=constants.half_sat_maom_decomposition, + activation_energy_sat=constants.activation_energy_maom_decomp_saturation, + ) + + return EnzymeMediatedRates( + pom_to_lmwc=pom_decomposition_to_lmwc, maom_to_lmwc=maom_decomposition_to_lmwc + ) + + def calculate_enzyme_changes( soil_enzyme_pom: NDArray[np.float32], soil_enzyme_maom: NDArray[np.float32], @@ -461,10 +511,8 @@ def calculate_microbial_carbon_uptake( def calculate_enzyme_mediated_decomposition( soil_c_pool: NDArray[np.float32], soil_enzyme: NDArray[np.float32], - water_factor: NDArray[np.float32], - pH_factor: NDArray[np.float32], - clay_factor_saturation: NDArray[np.float32], soil_temp: NDArray[np.float32], + env_factors: EnvironmentalEffectFactors, reference_temp: float, max_decomp_rate: float, activation_energy_rate: float, @@ -481,13 +529,9 @@ def calculate_enzyme_mediated_decomposition( soil_c_pool: Size of organic matter pool [kg C m^-3] soil_enzyme: Amount of enzyme class which breaks down the organic matter pool in question [kg C m^-3] - water_factor: A factor capturing the impact of soil water potential on microbial - rates [unitless] - pH_factor: A factor capturing the impact of soil pH on microbial rates - [unitless] - clay_factor_saturation: A factor capturing the impact of soil clay fraction on - enzyme saturation constants [unitless] soil_temp: soil temperature for each soil grid cell [degrees C] + env_factors: Data class containing the various factors through which the + environment effects soil cycling rates. reference_temp: The reference temperature that enzyme rates were determined relative to [degrees C] max_decomp_rate: The maximum rate of substrate decomposition (at the reference @@ -516,9 +560,11 @@ def calculate_enzyme_mediated_decomposition( ) # Calculate the adjusted rate and saturation constants - rate_constant = max_decomp_rate * temp_factor_rate * water_factor * pH_factor + rate_constant = ( + max_decomp_rate * temp_factor_rate * env_factors.water * env_factors.pH + ) saturation_constant = ( - half_saturation * temp_factor_saturation * clay_factor_saturation + half_saturation * temp_factor_saturation * env_factors.clay_saturation ) return ( From 0b962c609c6d6419e931d8c55db6588de562bedd Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Tue, 2 Jul 2024 10:51:47 +0100 Subject: [PATCH 17/22] Switched to standard numpy import pattern --- virtual_ecosystem/models/soil/constants.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/virtual_ecosystem/models/soil/constants.py b/virtual_ecosystem/models/soil/constants.py index 1b17d9e7f..94596453d 100644 --- a/virtual_ecosystem/models/soil/constants.py +++ b/virtual_ecosystem/models/soil/constants.py @@ -5,7 +5,7 @@ from dataclasses import dataclass -from numpy import log +import numpy as np from virtual_ecosystem.core.constants_class import ConstantsDataclass @@ -256,7 +256,7 @@ class SoilConsts(ConstantsDataclass): a loose manner. """ - necromass_decay_rate: float = (1 / 3) * log(2) + necromass_decay_rate: float = (1 / 3) * np.log(2) """Rate at which microbial necromass decays to low molecular weight carbon [day^-1] I have not been able to track down any data on this, so for now choosing a rate that @@ -280,7 +280,7 @@ class SoilConsts(ConstantsDataclass): absence of microbes. This is another key target for sensitivity analysis. """ - necromass_sorption_rate: float = 1 * log(2) + necromass_sorption_rate: float = 1.0 * np.log(2) """Rate constant for necromass sorption to minerals [day^-1] The default value was chosen to be three times the value of From 9e016b3b09871b171c7e0c56e5f7e0bf83977481 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Tue, 2 Jul 2024 11:00:55 +0100 Subject: [PATCH 18/22] Spelt out the pool names in the docstrings --- virtual_ecosystem/models/soil/carbon.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 07c95c7ab..9f5d194c5 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -24,16 +24,24 @@ class MicrobialChanges: """Changes due to microbial uptake, biomass production and losses.""" lmwc_uptake: NDArray[np.float32] - """Total rate of microbial LMWC uptake [kg C m^-3 day^-1].""" + """Total rate of microbial uptake of low molecular weight carbon. + + Units of [kg C m^-3 day^-1].""" microbe: NDArray[np.float32] """Rate of change of microbial biomass pool [kg C m^-3 day^-1].""" pom_enzyme: NDArray[np.float32] - """Rate of change of POM degrading enzyme pool [kg C m^-3 day^-1].""" + """Rate of change of particulate organic matter degrading enzyme pool. + + Units of [kg C m^-3 day^-1]. + """ maom_enzyme: NDArray[np.float32] - """Rate of change of MAOM degrading enzyme pool [kg C m^-3 day^-1].""" + """Rate of change of mineral associated organic matter degrading enzyme pool. + + Units of [kg C m^-3 day^-1]. + """ necromass_generation: NDArray[np.float32] """Rate at which necromass is being produced [kg C m^-3 day^-1].""" @@ -44,10 +52,16 @@ class EnzymeMediatedRates: """Rates of each enzyme mediated transfer between pools.""" pom_to_lmwc: NDArray[np.float32] - """Rate of POM decomposition to LMWC [kg C m^-3 day^-1].""" + """Rate of particulate organic matter decomposition to low molecular weight carbon. + + Units of [kg C m^-3 day^-1]. + """ maom_to_lmwc: NDArray[np.float32] - """Rate of MAOM decomposition to LMWC [kg C m^-3 day^-1].""" + """Rate of mineral associated organic matter decomposition to LMWC. + + Units of [kg C m^-3 day^-1]. + """ # TODO - This function should probably be shortened. I've done some work on this From 26ad49da9a964d47872ece97e05eb4325ffe8e2e Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Tue, 2 Jul 2024 11:32:48 +0100 Subject: [PATCH 19/22] Change dataclass names to make clear that they are rates of change --- tests/models/soil/test_carbon.py | 6 +++--- virtual_ecosystem/models/soil/carbon.py | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/models/soil/test_carbon.py b/tests/models/soil/test_carbon.py index 78ba15031..056b07e46 100644 --- a/tests/models/soil/test_carbon.py +++ b/tests/models/soil/test_carbon.py @@ -85,9 +85,9 @@ def test_calculate_microbial_changes( # Check that each rate matches expectation assert np.allclose(mic_changes.lmwc_uptake, expected_lmwc_uptake) - assert np.allclose(mic_changes.microbe, expected_microbe) - assert np.allclose(mic_changes.pom_enzyme, expected_pom_enzyme) - assert np.allclose(mic_changes.maom_enzyme, expected_maom_enzyme) + 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) diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index 9f5d194c5..de28c79d5 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -28,16 +28,16 @@ class MicrobialChanges: Units of [kg C m^-3 day^-1].""" - microbe: NDArray[np.float32] + microbe_change: NDArray[np.float32] """Rate of change of microbial biomass pool [kg C m^-3 day^-1].""" - pom_enzyme: NDArray[np.float32] + pom_enzyme_change: NDArray[np.float32] """Rate of change of particulate organic matter degrading enzyme pool. Units of [kg C m^-3 day^-1]. """ - maom_enzyme: NDArray[np.float32] + maom_enzyme_change: NDArray[np.float32] """Rate of change of mineral associated organic matter degrading enzyme pool. Units of [kg C m^-3 day^-1]. @@ -59,7 +59,7 @@ class EnzymeMediatedRates: maom_to_lmwc: NDArray[np.float32] """Rate of mineral associated organic matter decomposition to LMWC. - + Units of [kg C m^-3 day^-1]. """ @@ -189,7 +189,7 @@ def calculate_soil_carbon_updates( - enzyme_mediated.maom_to_lmwc - maom_desorption_to_lmwc ) - delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe + delta_pools_ordered["soil_c_pool_microbe"] = microbial_changes.microbe_change delta_pools_ordered["soil_c_pool_pom"] = ( mineralisation_rate - enzyme_mediated.pom_to_lmwc ) @@ -198,8 +198,8 @@ def calculate_soil_carbon_updates( - necromass_decay_to_lmwc - necromass_sorption_to_maom ) - delta_pools_ordered["soil_enzyme_pom"] = microbial_changes.pom_enzyme - delta_pools_ordered["soil_enzyme_maom"] = microbial_changes.maom_enzyme + delta_pools_ordered["soil_enzyme_pom"] = microbial_changes.pom_enzyme_change + delta_pools_ordered["soil_enzyme_maom"] = microbial_changes.maom_enzyme_change # Create output array of pools in desired order return np.concatenate(list(delta_pools_ordered.values())) @@ -269,9 +269,9 @@ def calculate_microbial_changes( return MicrobialChanges( lmwc_uptake=microbial_uptake, - microbe=biomass_growth - biomass_loss, - pom_enzyme=pom_enzyme_net_change, - maom_enzyme=maom_enzyme_net_change, + microbe_change=biomass_growth - biomass_loss, + pom_enzyme_change=pom_enzyme_net_change, + maom_enzyme_change=maom_enzyme_net_change, necromass_generation=enzyme_denaturation + true_loss, ) From 2e059492e775a222629dd466084d82f0f407c81d Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 3 Jul 2024 08:39:26 +0100 Subject: [PATCH 20/22] Removed unnecessary brackets in data generation scripts --- .../generation_scripts/litter_example_data.py | 12 +++++------ .../generation_scripts/soil_example_data.py | 20 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py b/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py index 4c63314fb..c4b621884 100644 --- a/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py @@ -17,25 +17,25 @@ # Generate a range of plausible values (0.05-0.5) for the above ground metabolic litter # pools [kg C m^-2]. -above_metabolic_values = 0.05 + 0.45 * (gradient) / (64) +above_metabolic_values = 0.05 + 0.45 * gradient / (64) # Generate a range of plausible values (0.05-0.5) for the above ground structural litter # pools [kg C m^-2]. -above_structural_values = 0.05 + 0.45 * (gradient) / (64) +above_structural_values = 0.05 + 0.45 * gradient / (64) # Generate range of plausible values (4.75-12.0) for the woody litter pools [kg C m^-2]. -woody_values = 4.75 + 7.25 * (gradient) / (64) +woody_values = 4.75 + 7.25 * gradient / (64) # Generate a range of plausible values (0.03-0.08) for the below ground metabolic litter # pools [kg C m^-2]. -below_metabolic_values = 0.03 + 0.05 * (gradient) / (64) +below_metabolic_values = 0.03 + 0.05 * gradient / (64) # Generate range of plausible values (0.05-0.125) for the below ground structural litter # pools [kg C m^-2]. -below_structural_values = 0.05 + 0.075 * (gradient) / (64) +below_structural_values = 0.05 + 0.075 * gradient / (64) # Generate a range of plausible values (0.01-0.9) for lignin proportions of the pools. -lignin_values = 0.01 + 0.89 * (gradient) / (64) +lignin_values = 0.01 + 0.89 * gradient / (64) # Make example litter dataset example_litter_data = Dataset( diff --git a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py index 4ada5c15f..c4b710137 100644 --- a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py @@ -14,36 +14,36 @@ gradient = np.outer(cell_displacements / 90, cell_displacements / 90) # Generate a range of plausible values (3.5-4.5) for the soil pH [unitless]. -pH_values = 3.5 + 1.00 * (gradient) / (64) +pH_values = 3.5 + 1.00 * gradient / (64) # Generate a range of plausible values (1200-1800) for the bulk density [kg m^-3]. -bulk_density_values = 1200.0 + 600.0 * (gradient) / (64) +bulk_density_values = 1200.0 + 600.0 * gradient / (64) # Generate a range of plausible values (0.27-0.40) for the clay fraction [fraction]. -clay_fraction_values = 0.27 + 0.13 * (gradient) / (64) +clay_fraction_values = 0.27 + 0.13 * gradient / (64) # Generate a range of plausible values (0.005-0.01) for the lmwc pool [kg C m^-3]. -lmwc_values = 0.005 + 0.005 * (gradient) / (64) +lmwc_values = 0.005 + 0.005 * gradient / (64) # Generate a range of plausible values (1.0-3.0) for the maom pool [kg C m^-3]. -maom_values = 1.0 + 2.0 * (gradient) / (64) +maom_values = 1.0 + 2.0 * gradient / (64) # Generate a range of plausible values (0.0015-0.005) for the microbial C pool # [kg C m^-3]. -microbial_C_values = 0.0015 + 0.0035 * (gradient) / (64) +microbial_C_values = 0.0015 + 0.0035 * gradient / (64) # Generate a range of plausible values (0.1-1.0) for the POM pool [kg C m^-3]. -pom_values = 0.1 + 0.9 * (gradient) / (64) +pom_values = 0.1 + 0.9 * gradient / (64) # Generate a range of plausible values (0.00015-0.0005) for the microbial necromass pool # [kg C m^-3]. -necromass_values = 0.00015 + 0.00035 * (gradient) / (64) +necromass_values = 0.00015 + 0.00035 * gradient / (64) # Generate a range of plausible values (0.01-0.5) for the POM enzyme pool [kg C m^-3]. -pom_enzyme_values = 0.01 + 0.49 * (gradient) / (64) +pom_enzyme_values = 0.01 + 0.49 * gradient / (64) # Generate a range of plausible values (0.01-0.5) for the MAOM enzyme pool [kg C m^-3]. -maom_enzyme_values = 0.01 + 0.49 * (gradient) / (64) +maom_enzyme_values = 0.01 + 0.49 * gradient / (64) # Make example soil dataset example_soil_data = Dataset( From 5ae07d832b0648751f09ca2929e5f5f5360f0339 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 3 Jul 2024 08:41:50 +0100 Subject: [PATCH 21/22] Switched to using explict floating points in data generation scripts --- .../generation_scripts/litter_example_data.py | 12 +++++------ .../generation_scripts/soil_example_data.py | 20 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py b/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py index c4b621884..a291e7330 100644 --- a/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/litter_example_data.py @@ -17,25 +17,25 @@ # Generate a range of plausible values (0.05-0.5) for the above ground metabolic litter # pools [kg C m^-2]. -above_metabolic_values = 0.05 + 0.45 * gradient / (64) +above_metabolic_values = 0.05 + 0.45 * gradient / 64.0 # Generate a range of plausible values (0.05-0.5) for the above ground structural litter # pools [kg C m^-2]. -above_structural_values = 0.05 + 0.45 * gradient / (64) +above_structural_values = 0.05 + 0.45 * gradient / 64.0 # Generate range of plausible values (4.75-12.0) for the woody litter pools [kg C m^-2]. -woody_values = 4.75 + 7.25 * gradient / (64) +woody_values = 4.75 + 7.25 * gradient / 64.0 # Generate a range of plausible values (0.03-0.08) for the below ground metabolic litter # pools [kg C m^-2]. -below_metabolic_values = 0.03 + 0.05 * gradient / (64) +below_metabolic_values = 0.03 + 0.05 * gradient / 64.0 # Generate range of plausible values (0.05-0.125) for the below ground structural litter # pools [kg C m^-2]. -below_structural_values = 0.05 + 0.075 * gradient / (64) +below_structural_values = 0.05 + 0.075 * gradient / 64.0 # Generate a range of plausible values (0.01-0.9) for lignin proportions of the pools. -lignin_values = 0.01 + 0.89 * gradient / (64) +lignin_values = 0.01 + 0.89 * gradient / 64.0 # Make example litter dataset example_litter_data = Dataset( diff --git a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py index c4b710137..89092eb25 100644 --- a/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py +++ b/virtual_ecosystem/example_data/generation_scripts/soil_example_data.py @@ -14,36 +14,36 @@ gradient = np.outer(cell_displacements / 90, cell_displacements / 90) # Generate a range of plausible values (3.5-4.5) for the soil pH [unitless]. -pH_values = 3.5 + 1.00 * gradient / (64) +pH_values = 3.5 + 1.00 * gradient / 64.0 # Generate a range of plausible values (1200-1800) for the bulk density [kg m^-3]. -bulk_density_values = 1200.0 + 600.0 * gradient / (64) +bulk_density_values = 1200.0 + 600.0 * gradient / 64.0 # Generate a range of plausible values (0.27-0.40) for the clay fraction [fraction]. -clay_fraction_values = 0.27 + 0.13 * gradient / (64) +clay_fraction_values = 0.27 + 0.13 * gradient / 64.0 # Generate a range of plausible values (0.005-0.01) for the lmwc pool [kg C m^-3]. -lmwc_values = 0.005 + 0.005 * gradient / (64) +lmwc_values = 0.005 + 0.005 * gradient / 64.0 # Generate a range of plausible values (1.0-3.0) for the maom pool [kg C m^-3]. -maom_values = 1.0 + 2.0 * gradient / (64) +maom_values = 1.0 + 2.0 * gradient / 64.0 # Generate a range of plausible values (0.0015-0.005) for the microbial C pool # [kg C m^-3]. -microbial_C_values = 0.0015 + 0.0035 * gradient / (64) +microbial_C_values = 0.0015 + 0.0035 * gradient / 64.0 # Generate a range of plausible values (0.1-1.0) for the POM pool [kg C m^-3]. -pom_values = 0.1 + 0.9 * gradient / (64) +pom_values = 0.1 + 0.9 * gradient / 64.0 # Generate a range of plausible values (0.00015-0.0005) for the microbial necromass pool # [kg C m^-3]. -necromass_values = 0.00015 + 0.00035 * gradient / (64) +necromass_values = 0.00015 + 0.00035 * gradient / 64.0 # Generate a range of plausible values (0.01-0.5) for the POM enzyme pool [kg C m^-3]. -pom_enzyme_values = 0.01 + 0.49 * gradient / (64) +pom_enzyme_values = 0.01 + 0.49 * gradient / 64.0 # Generate a range of plausible values (0.01-0.5) for the MAOM enzyme pool [kg C m^-3]. -maom_enzyme_values = 0.01 + 0.49 * gradient / (64) +maom_enzyme_values = 0.01 + 0.49 * gradient / 64.0 # Make example soil dataset example_soil_data = Dataset( From d8afa00930da799afaddc4645c61e842dcd4a578 Mon Sep 17 00:00:00 2001 From: Jacob Cook Date: Wed, 3 Jul 2024 08:42:49 +0100 Subject: [PATCH 22/22] Removed stray comma --- virtual_ecosystem/models/soil/carbon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virtual_ecosystem/models/soil/carbon.py b/virtual_ecosystem/models/soil/carbon.py index de28c79d5..e95605680 100644 --- a/virtual_ecosystem/models/soil/carbon.py +++ b/virtual_ecosystem/models/soil/carbon.py @@ -234,7 +234,7 @@ def calculate_microbial_changes( constants: Set of constants for the soil model. Returns: - A dataclass containing, the rate at which microbes uptake LMWC, the rate of + A dataclass containing the rate at which microbes uptake LMWC, the rate of change in the microbial biomass pool and the enzyme pools. """