Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
fc6c530
first round of abiotic and hydrology vars, need some code fixes to co…
vgro Jun 4, 2024
c388762
toml organised and unnecessary vars removed
vgro Jun 5, 2024
5412a20
Merge branch 'develop' into variables_1_vgro
vgro Jun 5, 2024
50b1e39
Merge branch 'develop' into variables_1_vgro
vgro Jun 5, 2024
3ac4ff0
Merge branch 'variables_1' into variables_1_vgro
vgro Jun 5, 2024
406e325
Update virtual_ecosystem/models/abiotic/abiotic_model.py
vgro Jun 5, 2024
7b83192
space and time as strings, required_init_vars updated to drop validat…
vgro Jun 7, 2024
52ab933
variable as float and non-abiotic variables deleted
vgro Jun 7, 2024
7e0c54c
variables from other models added back in
vgro Jun 7, 2024
682530f
layer_absorbed_irradiance removed (different name agreed)
vgro Jun 7, 2024
edab21d
Updating plant model variables in data_variables
davidorme Jun 7, 2024
eda9b8f
Updating variable names in plants model code
davidorme Jun 7, 2024
c9b4d66
Litter and soil variable edits
davidorme Jun 7, 2024
fbd4599
Animal model variable edits
davidorme Jun 7, 2024
bcd5082
Missing variable and populating vars_initialised for animal_model
davidorme Jun 7, 2024
b96b97b
Merge branch 'variables_1' into variables_1_vgro
davidorme Jun 7, 2024
86656f7
Added missing variables
davidorme Jun 10, 2024
e2f1c75
Aligning variable names, removing duplicates under different names
davidorme Jun 10, 2024
ae18e18
Removed surface_runoff from hydrology.vars_initialised
davidorme Jun 10, 2024
acfed0d
Merge branch 'develop' into variables_1_vgro
davidorme Jun 11, 2024
6060cf0
Merge branch 'variables_1' into variables_1_vgro
davidorme Jun 11, 2024
b08e7bb
Adding missing init vars to abiotic model tests
davidorme Jun 11, 2024
8da2908
Updating BaseModel validation of variable list attributes
davidorme Jun 11, 2024
8a07083
Repeated names in variables definition
davidorme Jun 11, 2024
5a59615
Tweaks to BaseModel variables attribute checking
davidorme Jun 11, 2024
24b6434
Fixing BaseModel tests for new variable attribute structures
davidorme Jun 11, 2024
893e9dd
Updating test_variables.py
davidorme Jun 11, 2024
463d907
Update virtual_ecosystem/core/base_model.py
davidorme Jun 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 30 additions & 45 deletions tests/core/test_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,9 @@ def data_instance():
pytest.param(
{
"model_name": "should_pass",
"required_init_vars": (
(
"temperature",
("spatial",),
),
),
"required_init_vars": ("temperature", "wind_speed"),
"model_update_bounds": ("1 day", "1 month"),
"vars_updated": [],
"vars_updated": (),
},
does_not_raise(),
None,
Expand All @@ -72,7 +67,7 @@ def data_instance():
"model_name": 9,
"required_init_vars": (),
"model_update_bounds": ("1 day", "1 month"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(TypeError),
"Class attribute model_name in UnnamedModel is not a string",
Expand All @@ -87,7 +82,7 @@ def data_instance():
"model_name": "should_pass",
"required_init_vars": (),
"model_update_bounds": ("1 day", "1 time"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(ValueError),
"Class attribute model_update_bounds for UnnamedModel "
Expand All @@ -107,7 +102,7 @@ def data_instance():
"model_name": "should_pass",
"required_init_vars": (),
"model_update_bounds": ("1 day", "1 day"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(ValueError),
"Lower time bound for UnnamedModel is not less than the upper bound.",
Expand All @@ -126,7 +121,7 @@ def data_instance():
"model_name": "should_pass",
"required_init_vars": (),
"model_update_bounds": ("1 day", "1 second"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(ValueError),
"Lower time bound for UnnamedModel is not less than the upper bound.",
Expand All @@ -145,7 +140,7 @@ def data_instance():
"model_name": "should_pass",
"required_init_vars": (),
"model_update_bounds": ("1 meter", "1 day"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(ValueError),
"Class attribute model_update_bounds for UnnamedModel "
Expand All @@ -165,7 +160,7 @@ def data_instance():
"model_name": "should_pass",
"required_init_vars": (),
"model_update_bounds": ("1 spongebob", "1 day"),
"vars_updated": [],
"vars_updated": (),
},
pytest.raises(ValueError),
"Class attribute model_update_bounds for UnnamedModel "
Expand Down Expand Up @@ -206,54 +201,40 @@ class UnnamedModel(BaseModel, **init_args):


@pytest.mark.parametrize(
argnames="riv_value, exp_raise, exp_msg",
argnames="value, exp_raise, exp_msg",
argvalues=[
pytest.param(
1,
pytest.raises(TypeError),
"Class attribute required_init_vars has the wrong structure in UM",
id="RIV is integer",
),
pytest.param(
["temperature", (1, 2)],
pytest.raises(TypeError),
"Class attribute required_init_vars has the wrong structure in UM",
id="RIV is list",
),
pytest.param(
("temperature", ("spatial",)),
pytest.raises(TypeError),
"Class attribute required_init_vars has the wrong structure in UM",
id="RIV is not nested enough",
id="value is integer",
),
pytest.param(
(("temperature", (1,)),),
["temperature", "wind_speed"],
pytest.raises(TypeError),
"Class attribute required_init_vars has the wrong structure in UM",
id="RIV axis is not string",
id="value is list",
),
pytest.param(
(("temperature", (1,), (2,)),),
("temperature", 1),
pytest.raises(TypeError),
"Class attribute required_init_vars has the wrong structure in UM",
id="RIV entry is too long",
id="value not all strings",
),
pytest.param(
(("temperature", ("special",)),),
pytest.raises(ValueError),
"Class attribute required_init_vars uses unknown core axes in UM: special",
id="RIV entry has bad axis name",
),
pytest.param(
(("temperature", ("spatial",)),),
("temperature", "wind_speed"),
does_not_raise(),
None,
id="RIV ok",
id="value ok",
),
],
)
def test_check_required_init_var_structure(riv_value, exp_raise, exp_msg):
"""Test that __init_subclass__ traps bad values for required_init_vars."""
def test_check_variable_attribute_structure(value, exp_raise, exp_msg):
"""Test that __init_subclass__ traps bad values for required_init_vars.

This could also test the other BaseModel variable attributes, but this checks
the mechanism.
"""

# BaseModel is required here in the code being exec'd from the params.
from virtual_ecosystem.core.base_model import BaseModel
Expand All @@ -263,9 +244,9 @@ def test_check_required_init_var_structure(riv_value, exp_raise, exp_msg):
class UM(
BaseModel,
model_name="should_also_pass",
required_init_vars=riv_value,
required_init_vars=value,
model_update_bounds=("1 day", "1 month"),
vars_updated=[],
vars_updated=(),
):
pass

Expand All @@ -287,7 +268,7 @@ class InitVarModel(
model_name="init_var",
model_update_bounds=("1 second", "1 year"),
required_init_vars=(),
vars_updated=[],
vars_updated=(),
):
pass

Expand All @@ -303,6 +284,10 @@ class InitVarModel(
assert str(err.value).startswith("Can't instantiate abstract class InitVarModel ")


@pytest.mark.skip(
"This functionality is going to be handed off to the variables system "
"so skipping for now but this will probably be deleted"
)
@pytest.mark.parametrize(
argnames="req_init_vars, raises, exp_err_msg, exp_log",
argvalues=[
Expand Down Expand Up @@ -489,7 +474,7 @@ class TimingTestModel(
model_name="timing_test",
model_update_bounds=("1 day", "1 month"),
required_init_vars=(),
vars_updated=[],
vars_updated=(),
):
def setup(self) -> None:
pass
Expand Down
6 changes: 4 additions & 2 deletions tests/core/test_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ def test_output_known_variables(known_variables, mocker, tmpdir):
assert "test_var" in variables.RUN_VARIABLES_REGISTRY
variables.register_all_variables.assert_called_once()
variables._discover_models.assert_called_once()
variables._collect_initialise_by_vars.assert_called_once_with([])
variables._collect_initialise_by_vars.assert_called_once_with(
[], check_unique_initialisation=False
)
variables._collect_required_init_vars.assert_called_once_with([])
variables._collect_updated_by_vars.assert_called_once_with([])
variables._collect_required_update_vars.assert_called_once_with([])
Expand Down Expand Up @@ -239,7 +241,7 @@ def test_collect_required_init_vars(known_variables, run_variables):

class TestModel:
model_name = "TestModel"
required_init_vars = (("test_var", tuple()),)
required_init_vars = ("test_var",)

with pytest.raises(ValueError, match="not in the known variables registry."):
variables._collect_required_init_vars([TestModel])
Expand Down
34 changes: 20 additions & 14 deletions tests/models/abiotic/test_abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
from tests.conftest import log_check
from virtual_ecosystem.core.exceptions import ConfigurationError

REQUIRED_INIT_VAR_CHECKS = (
(DEBUG, "abiotic model: required var 'air_temperature_ref' checked"),
(DEBUG, "abiotic model: required var 'relative_humidity_ref' checked"),
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
(DEBUG, "abiotic model: required var 'leaf_area_index' checked"),
(DEBUG, "abiotic model: required var 'layer_heights' checked"),
)


def test_abiotic_model_initialization(
caplog, dummy_climate_data, fixture_core_components
Expand All @@ -36,11 +44,7 @@ def test_abiotic_model_initialization(
# Final check that expected logging entries are produced
log_check(
caplog,
expected_log=(
(DEBUG, "abiotic model: required var 'air_temperature_ref' checked"),
(DEBUG, "abiotic model: required var 'relative_humidity_ref' checked"),
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
),
expected_log=REQUIRED_INIT_VAR_CHECKS,
)


Expand Down Expand Up @@ -80,6 +84,14 @@ def test_abiotic_model_initialization_no_data(caplog, fixture_core_components):
ERROR,
"abiotic model: init data missing required var 'topofcanopy_radiation'",
),
(
ERROR,
"abiotic model: init data missing required var 'leaf_area_index'",
),
(
ERROR,
"abiotic model: init data missing required var 'layer_heights'",
),
(ERROR, "abiotic model: error checking required_init_vars, see log."),
),
)
Expand All @@ -99,9 +111,7 @@ def test_abiotic_model_initialization_no_data(caplog, fixture_core_components):
"Information required to initialise the abiotic model successfully "
"extracted.",
),
(DEBUG, "abiotic model: required var 'air_temperature_ref' checked"),
(DEBUG, "abiotic model: required var 'relative_humidity_ref' checked"),
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
*REQUIRED_INIT_VAR_CHECKS,
),
id="default_config",
),
Expand All @@ -117,9 +127,7 @@ def test_abiotic_model_initialization_no_data(caplog, fixture_core_components):
"Information required to initialise the abiotic model successfully "
"extracted.",
),
(DEBUG, "abiotic model: required var 'air_temperature_ref' checked"),
(DEBUG, "abiotic model: required var 'relative_humidity_ref' checked"),
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
*REQUIRED_INIT_VAR_CHECKS,
),
id="modified_config_correct",
),
Expand Down Expand Up @@ -182,9 +190,7 @@ def test_generate_abiotic_model(
"Information required to initialise the abiotic model "
"successfully extracted.",
),
(DEBUG, "abiotic model: required var 'air_temperature_ref' checked"),
(DEBUG, "abiotic model: required var 'relative_humidity_ref' checked"),
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
*REQUIRED_INIT_VAR_CHECKS,
(
ERROR,
"The update interval is slower than the abiotic upper "
Expand Down
4 changes: 0 additions & 4 deletions tests/models/hydrology/test_above_ground.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ def test_calculate_soil_evaporation(dens_air, latvap):
)

exp_evap = np.array([0.745206, 0.092515, 0.135078])
exp_ra = np.array([12.5, 50.0, 1250.0])
np.testing.assert_allclose(result["soil_evaporation"], exp_evap, rtol=0.01)
np.testing.assert_allclose(
result["aerodynamic_resistance_surface"], exp_ra, rtol=0.01
)


def test_find_lowest_neighbour(dummy_climate_data):
Expand Down
2 changes: 1 addition & 1 deletion tests/models/plants/test_canopy.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def test_initialise_canopy_layers(plants_data, fixture_core_components):
"leaf_area_index",
"layer_fapar",
"layer_leaf_mass",
"layer_absorbed_irradiation",
"canopy_absorption",
)

n_layer = 1 + 10 + 2 + 2
Expand Down
10 changes: 5 additions & 5 deletions tests/models/plants/test_plants_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_PlantsModel__init__(plants_data, flora, fixture_core_components):
("layer_heights", (32 + 30 + 20 + 10 + 1.5 + 0.1 - 0.25 - 1) * 4),
("leaf_area_index", 3 * 4),
("layer_fapar", (0.4 + 0.2 + 0.1) * 4),
("layer_absorbed_irradiation", 1000 * 4),
("canopy_absorption", 1000 * 4),
]

for layer_name, layer_sum in expected_layers:
Expand All @@ -51,7 +51,7 @@ def test_PlantsModel_from_config(plants_data, fixture_config, fixture_core_compo
("layer_heights", (32 + 30 + 20 + 10 + 1.5 + 0.1 - 0.25 - 1) * 4),
("leaf_area_index", 3 * 4),
("layer_fapar", (0.4 + 0.2 + 0.1) * 4),
("layer_absorbed_irradiation", 1000 * 4),
("canopy_absorption", 1000 * 4),
)

for layer_name, layer_sum in expected_layers:
Expand All @@ -66,7 +66,7 @@ def test_PlantsModel_update_canopy_layers(fxt_plants_model):
("layer_heights", (32 + 30 + 20 + 10) * 4),
("leaf_area_index", 3 * 4),
("layer_fapar", (0.4 + 0.2 + 0.1) * 4),
("layer_absorbed_irradiation", 0), # Note that this layer should not be updated
("canopy_absorption", 0), # Note that this layer should not be updated
)

# Overwrite the existing data in each layer
Expand All @@ -89,7 +89,7 @@ def test_PlantsModel_set_absorbed_irradiance(fxt_plants_model):
("layer_heights", (32 + 30 + 20 + 10) * 4),
("leaf_area_index", 3 * 4),
("layer_fapar", (0.4 + 0.2 + 0.1) * 4),
("layer_absorbed_irradiation", 1000 * 4), # Is restored by additional call.
("canopy_absorption", 1000 * 4), # Is restored by additional call.
)
# Overwrite the existing data in each layer
for layer, _ in expected_layers:
Expand Down Expand Up @@ -164,7 +164,7 @@ def test_PlantsModel_update(fxt_plants_model):
("leaf_area_index", 3 * 4),
("layer_fapar", (0.4 + 0.2 + 0.1) * 4),
("layer_leaf_mass", 30000 * 4),
("layer_absorbed_irradiation", 1000 * 4),
("canopy_absorption", 1000 * 4),
)

# Overwrite the existing data in each layer
Expand Down
Loading