11
11
12
12
def test_calculate_all_pool_updates (dummy_carbon_data , fixture_core_components ):
13
13
"""Test that the two pool update functions work correctly."""
14
+ from virtual_ecosystem .core .constants import CoreConsts
14
15
from virtual_ecosystem .models .soil .pools import SoilPools
15
16
from virtual_ecosystem .models .soil .soil_model import SoilModel , make_slices
16
17
@@ -33,24 +34,32 @@ def test_calculate_all_pool_updates(dummy_carbon_data, fixture_core_components):
33
34
pools = {
34
35
str (pool ): y0 [slc ] for slc , pool in zip (slices , delta_pools_ordered .keys ())
35
36
}
36
- soil_pools = SoilPools (data = dummy_carbon_data , pools = pools , constants = SoilConsts )
37
+ soil_pools = SoilPools (
38
+ data = dummy_carbon_data ,
39
+ pools = pools ,
40
+ constants = SoilConsts ,
41
+ max_depth_of_microbial_activity = CoreConsts .max_depth_of_microbial_activity ,
42
+ )
37
43
38
44
change_in_pools = {
39
- "soil_c_pool_lmwc" : [0.01510858 , 0.01400719 , 0.03697928 , 0.02426899 ],
45
+ "soil_c_pool_lmwc" : [0.01498062 , 0.01332954 , 0.03697927 , 0.02425546 ],
40
46
"soil_c_pool_maom" : [0.038767651 , 0.00829848 , 0.05982197 , 0.07277182 ],
41
- "soil_c_pool_microbe" : [- 0.0544059 , - 0.02282691 , - 0.11965575 , - 0.00720166 ],
47
+ "soil_c_pool_microbe" : [- 0.05435984 , - 0.02260329 , - 0.11965575 , - 0.00719517 ],
42
48
"soil_c_pool_pom" : [0.00177803841 , - 0.007860960795 , - 0.012016245 , 0.00545032 ],
43
49
"soil_c_pool_necromass" : [0.001137474 , 0.009172067 , 0.033573266 , - 0.08978050 ],
44
50
"soil_enzyme_pom" : [1.18e-8 , 1.67e-8 , 1.8e-9 , - 1.12e-8 ],
45
51
"soil_enzyme_maom" : [- 0.00031009 , - 5.09593e-5 , 0.0005990658 , - 3.72112e-5 ],
46
- "soil_n_pool_don" : [0.00119921 , 0.00470261 , 0.00496839 , 0.00251442 ],
52
+ "soil_n_pool_don" : [0.00119058 , 0.00466305 , 0.00497108 , 0.00257023 ],
47
53
"soil_n_pool_particulate" : [1.102338e-5 , 6.422491e-5 , 0.000131687 , 1.461799e-5 ],
48
54
"soil_n_pool_necromass" : [0.00786114 , - 0.01209909 , 0.00432363 , - 0.00891218 ],
49
55
"soil_n_pool_maom" : [0.00148604 , 0.01179891 , 0.01365197 , 0.0077315 ],
50
- "soil_p_pool_dop" : [1.92918032e -4 , 6.24454858e -5 , 1.57222238e -4 , 9.94118894e-5 ],
56
+ "soil_p_pool_dop" : [1.94452573e -4 , 7.10041449e -5 , 1.86586343e -4 , 1.01700974e-4 ],
51
57
"soil_p_pool_particulate" : [7.22218e-6 , - 1.13464e-6 , 7.86083e-7 , 5.85634364e-7 ],
52
58
"soil_p_pool_necromass" : [2.674836e-3 , 1.333056e-3 , 6.8090685e-3 , 4.1429847e-5 ],
53
59
"soil_p_pool_maom" : [5.52086672e-4 , 3.68566732e-5 , 4.7566130e-4 , 3.09257058e-4 ],
60
+ "soil_p_pool_primary" : [- 4.473516e-10 , - 1.222973e-9 , - 6.33411e-10 , - 1.3674e-10 ],
61
+ "soil_p_pool_secondary" : [- 5.050797e-7 , - 2.77311e-6 , - 7.40324e-7 , - 2.187697e-7 ],
62
+ "soil_p_pool_labile" : [- 3.851076e-6 , - 1.965147e-5 , - 2.749871e-5 , - 1.591909e-7 ],
54
63
}
55
64
56
65
# Make order of pools object
@@ -76,18 +85,22 @@ def test_calculate_microbial_changes(
76
85
77
86
from virtual_ecosystem .models .soil .pools import calculate_microbial_changes
78
87
79
- expected_lmwc_uptake = [6.90989514e-5 , 4.76229800e-4 , 1.55609440e-3 , 4.42097002e-5 ]
80
- expected_don_uptake = [4.78377356e-6 , 3.02222758e-5 , 8.97746767e-5 , 4.08089540e-6 ]
81
- expected_dop_uptake = [1.55472641e-6 , 9.82223962e-6 , 2.91767699e-5 , 1.32629101e-6 ]
82
- expected_microbe = [- 0.0544059 , - 0.02282691 , - 0.11965575 , - 0.00720166 ]
83
- expected_pom_enzyme = [1.17571917e-8 , 1.67442231e-8 , 1.83311362e-9 , - 1.11675865e-8 ]
84
- expected_maom_enzyme = [- 3.1009224e-4 , - 5.0959256e-5 , 5.9906583e-4 , - 3.7211168e-5 ]
85
- expected_necromass = [0.05474086 , 0.02303502 , 0.11952352 , 0.00726011 ]
88
+ expected_mic_changes = {
89
+ "lmwc_uptake" : [1.97060348e-4 , 1.15388472e-3 , 1.55610000e-3 , 5.77363558e-5 ],
90
+ "don_uptake" : [1.36426394e-5 , 7.32272994e-5 , 8.9775e-5 , 5.32950977e-6 ],
91
+ "dop_uptake" : [2.25200566e-8 , 1.3187241e-6 , 8.8919911e-7 , 1.3196877e-6 ],
92
+ "labile_p_change" : [4.4113378e-6 , 2.2480148e-5 , 2.8287676e-5 , 4.124029e-7 ],
93
+ "microbe_change" : [- 0.05435984 , - 0.02260329 , - 0.11965575 , - 0.00719517 ],
94
+ "pom_enzyme_change" : [1.17571917e-8 , 1.6744223e-8 , 1.8331136e-9 , - 1.1167587e-8 ],
95
+ "maom_enzyme_change" : [- 3.1009224e-4 , - 5.0959256e-5 , 5.990658e-4 , - 3.721117e-5 ],
96
+ "necromass_generation" : [0.05474086 , 0.02303502 , 0.11952352 , 0.00726011 ],
97
+ }
86
98
87
- mic_changes = calculate_microbial_changes (
99
+ actual_mic_changes = calculate_microbial_changes (
88
100
soil_c_pool_lmwc = dummy_carbon_data ["soil_c_pool_lmwc" ],
89
101
soil_n_pool_don = dummy_carbon_data ["soil_n_pool_don" ],
90
102
soil_p_pool_dop = dummy_carbon_data ["soil_p_pool_dop" ],
103
+ soil_p_pool_labile = dummy_carbon_data ["soil_p_pool_labile" ],
91
104
soil_c_pool_microbe = dummy_carbon_data ["soil_c_pool_microbe" ],
92
105
soil_enzyme_pom = dummy_carbon_data ["soil_enzyme_pom" ],
93
106
soil_enzyme_maom = dummy_carbon_data ["soil_enzyme_maom" ],
@@ -98,14 +111,12 @@ def test_calculate_microbial_changes(
98
111
constants = SoilConsts ,
99
112
)
100
113
101
- # Check that each rate matches expectation
102
- assert np .allclose (mic_changes .lmwc_uptake , expected_lmwc_uptake )
103
- assert np .allclose (mic_changes .don_uptake , expected_don_uptake )
104
- assert np .allclose (mic_changes .dop_uptake , expected_dop_uptake )
105
- assert np .allclose (mic_changes .microbe_change , expected_microbe )
106
- assert np .allclose (mic_changes .pom_enzyme_change , expected_pom_enzyme )
107
- assert np .allclose (mic_changes .maom_enzyme_change , expected_maom_enzyme )
108
- assert np .allclose (mic_changes .necromass_generation , expected_necromass )
114
+ for attr in dir (actual_mic_changes ):
115
+ if not attr .startswith ("_" ):
116
+ assert attr in expected_mic_changes .keys (), f"Attribute { attr } not tested"
117
+ assert np .allclose (
118
+ getattr (actual_mic_changes , attr ), expected_mic_changes [attr ]
119
+ )
109
120
110
121
111
122
def test_calculate_enzyme_mediated_rates (
@@ -115,8 +126,10 @@ def test_calculate_enzyme_mediated_rates(
115
126
116
127
from virtual_ecosystem .models .soil .pools import calculate_enzyme_mediated_rates
117
128
118
- expected_pom_to_lmwc = [3.39844565e-4 , 8.91990315e-3 , 1.25055119e-2 , 4.14247999e-5 ]
119
- expected_maom_to_lmwc = [1.45988485e-3 , 2.10172756e-3 , 4.69571604e-3 , 8.62951373e-6 ]
129
+ expected_rates = {
130
+ "pom_to_lmwc" : [3.39844565e-4 , 8.91990315e-3 , 1.25055119e-2 , 4.14247999e-5 ],
131
+ "maom_to_lmwc" : [1.45988485e-3 , 2.10172756e-3 , 4.69571604e-3 , 8.62951373e-6 ],
132
+ }
120
133
121
134
actual_rates = calculate_enzyme_mediated_rates (
122
135
soil_enzyme_pom = dummy_carbon_data ["soil_enzyme_pom" ],
@@ -130,8 +143,39 @@ def test_calculate_enzyme_mediated_rates(
130
143
constants = SoilConsts ,
131
144
)
132
145
133
- assert np .allclose (actual_rates .pom_to_lmwc , expected_pom_to_lmwc )
134
- assert np .allclose (actual_rates .maom_to_lmwc , expected_maom_to_lmwc )
146
+ for attr in dir (actual_rates ):
147
+ if not attr .startswith ("_" ):
148
+ assert attr in expected_rates .keys (), f"Attribute { attr } not tested"
149
+ assert np .allclose (getattr (actual_rates , attr ), expected_rates [attr ])
150
+
151
+
152
+ def test_calculate_nutrient_leaching (dummy_carbon_data , fixture_core_components ):
153
+ """Check that the calculation of dissolved nutrient leaching rates is correct."""
154
+ from virtual_ecosystem .models .soil .pools import calculate_nutrient_leaching
155
+
156
+ expected_leaching = {
157
+ "lmwc" : [1.0747349e-6 , 2.5395235e-6 , 9.9154571e-5 , 5.2557152e-6 ],
158
+ "don" : [1.22826724e-8 , 1.81394352e-7 , 1.41642304e-7 , 3.00326494e-6 ],
159
+ "dop" : [1.2282071e-10 , 2.90230964e-9 , 5.66596981e-8 , 1.20130598e-7 ],
160
+ "labile_P" : [2.274653e-11 , 4.130485e-10 , 6.749199e-9 , 2.045141e-8 ],
161
+ }
162
+
163
+ actual_leaching = calculate_nutrient_leaching (
164
+ soil_c_pool_lmwc = dummy_carbon_data ["soil_c_pool_lmwc" ],
165
+ soil_n_pool_don = dummy_carbon_data ["soil_n_pool_don" ],
166
+ soil_p_pool_dop = dummy_carbon_data ["soil_p_pool_dop" ],
167
+ soil_p_pool_labile = dummy_carbon_data ["soil_p_pool_labile" ],
168
+ vertical_flow_rate = dummy_carbon_data ["vertical_flow" ].to_numpy (),
169
+ soil_moisture = dummy_carbon_data ["soil_moisture" ][
170
+ fixture_core_components .layer_structure .index_topsoil_scalar
171
+ ].to_numpy (),
172
+ constants = SoilConsts ,
173
+ )
174
+
175
+ for attr in dir (actual_leaching ):
176
+ if not attr .startswith ("_" ):
177
+ assert attr in expected_leaching .keys (), f"Attribute { attr } not tested"
178
+ assert np .allclose (getattr (actual_leaching , attr ), expected_leaching [attr ])
135
179
136
180
137
181
def test_calculate_enzyme_changes (dummy_carbon_data ):
@@ -232,17 +276,19 @@ def test_calculate_nutrient_uptake_rates(
232
276
calculate_nutrient_uptake_rates ,
233
277
)
234
278
235
- expected_carbon_gain = [2.48756225e -5 , 1.57155834e -4 , 4.66828319e-4 , 2.12206561e -5 ]
279
+ expected_carbon_gain = [7.09417251e -5 , 3.80781957e -4 , 0.00046683 , 2.77134508e -5 ]
236
280
expected_consumption_rates = {
237
- "nitrogen" : [4.78377356e-6 , 3.02222758e-5 , 8.97746767e-5 , 4.08089540e-6 ],
238
- "phosphorus" : [1.55472641e-6 , 9.82223962e-6 , 2.91767699e-5 , 1.32629101e-6 ],
239
- "carbon" : [6.90989514e-5 , 4.76229800e-4 , 1.55609440e-3 , 4.42097002e-5 ],
281
+ "organic_nitrogen" : [1.36426394e-5 , 7.32272994e-5 , 8.9775e-5 , 5.32950977e-6 ],
282
+ "organic_phosphorus" : [2.25200566e-8 , 1.3187241e-6 , 8.8919911e-7 , 1.3196877e-6 ],
283
+ "carbon" : [1.97060348e-4 , 1.15388472e-3 , 1.55610000e-3 , 5.77363558e-5 ],
284
+ "inorganic_phosphorus" : [4.4113378e-6 , 2.2480148e-5 , 2.8287676e-5 , 4.124029e-7 ],
240
285
}
241
286
242
287
actual_carbon_gain , actual_consumption_rates = calculate_nutrient_uptake_rates (
243
288
soil_c_pool_lmwc = dummy_carbon_data ["soil_c_pool_lmwc" ],
244
289
soil_n_pool_don = dummy_carbon_data ["soil_n_pool_don" ],
245
290
soil_p_pool_dop = dummy_carbon_data ["soil_p_pool_dop" ],
291
+ soil_p_pool_labile = dummy_carbon_data ["soil_p_pool_labile" ],
246
292
soil_c_pool_microbe = dummy_carbon_data ["soil_c_pool_microbe" ],
247
293
water_factor = environmental_factors .water ,
248
294
pH_factor = environmental_factors .pH ,
@@ -254,14 +300,15 @@ def test_calculate_nutrient_uptake_rates(
254
300
255
301
assert np .allclose (actual_carbon_gain , expected_carbon_gain )
256
302
257
- assert set (expected_consumption_rates .keys ()) == set (
258
- actual_consumption_rates .keys ()
259
- )
260
-
261
- for key in expected_consumption_rates .keys ():
262
- assert np .allclose (
263
- expected_consumption_rates [key ], actual_consumption_rates [key ]
264
- )
303
+ for attr in dir (actual_consumption_rates ):
304
+ if not attr .startswith ("_" ):
305
+ assert attr in expected_consumption_rates .keys (), (
306
+ f"Attribute { attr } not tested"
307
+ )
308
+ assert np .allclose (
309
+ getattr (actual_consumption_rates , attr ),
310
+ expected_consumption_rates [attr ],
311
+ )
265
312
266
313
267
314
def test_calculate_highest_achievable_nutrient_uptake (
@@ -377,26 +424,58 @@ def test_calculate_necromass_breakdown(dummy_carbon_data):
377
424
assert np .allclose (actual_breakdown , expected_breakdown )
378
425
379
426
427
+ def test_calculate_litter_mineralisation_fluxes (dummy_carbon_data ):
428
+ """Test that calculation of litter mineralisation fluxes works correctly."""
429
+ from virtual_ecosystem .models .soil .pools import (
430
+ calculate_litter_mineralisation_fluxes ,
431
+ )
432
+
433
+ expected_fluxes = {
434
+ "lmwc" : [3.181590e-6 , 1.590795e-6 , 7.350000e-7 , 8.250000e-6 ],
435
+ "pom" : [0.00211788 , 0.00105894 , 0.00048927 , 0.00549175 ],
436
+ "don" : [5.302650e-8 , 1.060530e-7 , 2.745000e-7 , 2.449995e-8 ],
437
+ "particulate_n" : [3.52979735e-5 , 7.05959470e-5 , 1.82725500e-4 , 1.63088001e-5 ],
438
+ "dop" : [7.32000e-10 , 1.41404e-10 , 2.82808e-10 , 6.53332e-11 ],
439
+ "particulate_p" : [7.31926800e-6 , 1.41389860e-6 , 2.82779719e-6 , 6.53266667e-7 ],
440
+ "labile_p" : [0.0 , 0.0 , 0.0 , 0.0 ],
441
+ }
442
+
443
+ actual_fluxes = calculate_litter_mineralisation_fluxes (
444
+ litter_C_mineralisation_rate = dummy_carbon_data [
445
+ "litter_C_mineralisation_rate"
446
+ ].to_numpy (),
447
+ litter_N_mineralisation_rate = dummy_carbon_data [
448
+ "litter_N_mineralisation_rate"
449
+ ].to_numpy (),
450
+ litter_P_mineralisation_rate = dummy_carbon_data [
451
+ "litter_P_mineralisation_rate"
452
+ ].to_numpy (),
453
+ constants = SoilConsts ,
454
+ )
455
+
456
+ # Check all (non-private) dataclass attributes against the dictionary
457
+ for attr in dir (actual_fluxes ):
458
+ if not attr .startswith ("_" ):
459
+ assert attr in expected_fluxes .keys (), f"Attribute { attr } not tested"
460
+ assert np .allclose (getattr (actual_fluxes , attr ), expected_fluxes [attr ])
461
+
462
+
380
463
def test_calculate_litter_mineralisation_split (dummy_carbon_data ):
381
464
"""Test that the calculation of the mineralisation split works as expected."""
382
465
from virtual_ecosystem .models .soil .pools import (
383
466
calculate_litter_mineralisation_split ,
384
467
)
385
468
386
- expected_split = {
387
- "dissolved" : [3.18159e-6 , 1.590795e-6 , 7.35e-7 , 8.25e-6 ],
388
- "particulate" : [0.00211787841 , 0.001058939205 , 0.000489265 , 0.00549175 ],
389
- }
469
+ expected_dissolved = [3.18159e-6 , 1.590795e-6 , 7.35e-7 , 8.25e-6 ]
470
+ expected_particulate = [0.00211787841 , 0.001058939205 , 0.000489265 , 0.00549175 ]
390
471
391
- actual_split = calculate_litter_mineralisation_split (
472
+ actual_particulate , expected_dissolved = calculate_litter_mineralisation_split (
392
473
mineralisation_rate = dummy_carbon_data ["litter_C_mineralisation_rate" ],
393
474
litter_leaching_coefficient = SoilConsts .litter_leaching_fraction_carbon ,
394
475
)
395
476
396
- assert set (expected_split .keys ()) == set (actual_split .keys ())
397
-
398
- for key in actual_split .keys ():
399
- assert np .allclose (actual_split [key ], expected_split [key ])
477
+ assert np .allclose (actual_particulate , expected_particulate )
478
+ assert np .allclose (expected_dissolved , expected_dissolved )
400
479
401
480
402
481
def test_calculate_soil_nutrient_mineralisation (
@@ -493,3 +572,21 @@ def test_calculate_net_nutrient_transfers_from_maom_to_lmwc(
493
572
494
573
for key in expected_transfers .keys ():
495
574
assert np .allclose (expected_transfers [key ], actual_transfers [key ])
575
+
576
+
577
+ def test_calculate_net_formation_of_secondary_P (dummy_carbon_data ):
578
+ """Test that calculation of the net formation of secondary P is correct."""
579
+ from virtual_ecosystem .models .soil .pools import (
580
+ calculate_net_formation_of_secondary_P ,
581
+ )
582
+
583
+ expected_formation = [- 5.05079715e-7 , - 2.77311435e-6 , - 7.4032388e-7 , - 2.18769722e-7 ]
584
+
585
+ actual_formation = calculate_net_formation_of_secondary_P (
586
+ soil_p_pool_labile = dummy_carbon_data ["soil_p_pool_labile" ],
587
+ soil_p_pool_secondary = dummy_carbon_data ["soil_p_pool_secondary" ],
588
+ secondary_p_breakdown_rate = SoilConsts .secondary_phosphorus_breakdown_rate ,
589
+ labile_p_sorption_rate = SoilConsts .labile_phosphorus_sorption_rate ,
590
+ )
591
+
592
+ assert np .allclose (actual_formation , expected_formation )
0 commit comments