Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for static models #373

Merged
merged 49 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
a96e90a
Adds psudocode for static models
dalonsoa Jan 23, 2024
45fb958
Integrate renamed project
dalonsoa Aug 21, 2024
6f9b5c5
Add bypassing of methods
dalonsoa Aug 21, 2024
f0f90f7
Prevent overriding init
dalonsoa Aug 21, 2024
fcdc3ee
Prevent overriding update
dalonsoa Aug 21, 2024
32a08d4
Merge branch 'develop' into frozen_models
dalonsoa Sep 3, 2024
7e730a4
Merge branch 'develop' into frozen_models
dalonsoa Sep 30, 2024
0976da4
Merge branch 'develop' into frozen_models
dalonsoa Oct 15, 2024
f9520dc
Merge branch 'develop' into frozen_models
dalonsoa Oct 15, 2024
6ae09ea
:recycle: Rename update with _update in models.
dalonsoa Oct 15, 2024
7c8b987
:recycle: Update setup in abiotic model.
dalonsoa Oct 15, 2024
a528278
:recycle: Update setup in abiotic simple model.
dalonsoa Oct 15, 2024
dedb2a6
:recycle: Update setup in abiotic animal model.
dalonsoa Oct 15, 2024
6176eb2
:recycle: Add class attribute descriptions to abiotic and abiotic sim…
dalonsoa Oct 15, 2024
bbabc61
:recycle: Remove __init__ from hydorlogy model.
dalonsoa Oct 15, 2024
b309d52
:recycle: Remove __init__ from litter model.
dalonsoa Oct 15, 2024
f336981
:recycle: Remove __init__ from plants model.
dalonsoa Oct 15, 2024
9d6e4af
:recycle: Remove __init__ from soil model.
dalonsoa Oct 15, 2024
d9a4fb8
:recycle: Add static flag to abiotic model.
dalonsoa Oct 15, 2024
590846a
:recycle: Add static flag to abiotic simple model.
dalonsoa Oct 15, 2024
b94526a
:recycle: Add static flag to animal model.
dalonsoa Oct 15, 2024
38c527e
:recycle: Add static flag to animal model.
dalonsoa Oct 15, 2024
93efe7b
:recycle: Add static flag to litter model.
dalonsoa Oct 15, 2024
bcfb968
:recycle: Add static flag to plants model.
dalonsoa Oct 15, 2024
3a4627d
:recycle: Add static flag to soil model.
dalonsoa Oct 15, 2024
7b26336
:recycle: Fix abiotic and abiotic_simple tests.
dalonsoa Oct 15, 2024
04468d0
:recycle: Simplify fix abiotic and abiotic_simple tests.
dalonsoa Oct 15, 2024
d335b08
:construction: Fix hydrology tests.
dalonsoa Oct 15, 2024
3bdf682
Merge branch 'develop' into frozen_models
dalonsoa Oct 24, 2024
0a43623
:white_check_mark: Fix remaining tests.
dalonsoa Oct 24, 2024
90fb257
:white_check_mark: Add tests for _bypass_setup_due_to_static_configur…
dalonsoa Oct 24, 2024
4951376
:white_check_mark: Add tests for _run_update_due_to_static_configurat…
dalonsoa Oct 24, 2024
58ada3e
:memo: Adds docs about the static flag.
dalonsoa Oct 24, 2024
05ff05b
:recycle: Remove vars requires for init from update check.
dalonsoa Oct 25, 2024
5d3a200
:recycle: Adapt cli and main tests.
dalonsoa Oct 25, 2024
0171525
:recycle: Adapt litter tests.
dalonsoa Oct 25, 2024
763289f
:recycle: Adapt soil tests.
dalonsoa Oct 25, 2024
7a7e27b
:recycle: Clean up a bit hydrology tests.
dalonsoa Oct 25, 2024
d691a34
Apply suggestions from code review
dalonsoa Oct 30, 2024
75f1253
:recycle: Include reviewers comments.
dalonsoa Oct 30, 2024
1ce3aa4
:white_check_mark: Fix failing tests.
dalonsoa Oct 30, 2024
cc0590d
Merge branch 'develop' into frozen_models
dalonsoa Nov 6, 2024
2bcb3e9
Merge branch 'develop' into frozen_models
dalonsoa Nov 6, 2024
4948a80
:recycle: Fix issue resulting from merge conflict.
dalonsoa Nov 6, 2024
e70adb2
Merge branch 'develop' into frozen_models
dalonsoa Nov 14, 2024
23ab6d7
:twisted_rightwards_arrows: Fix conflicts and finish merge.
dalonsoa Dec 11, 2024
d513125
:recycle: Remove unnecessary call to setup.
dalonsoa Dec 11, 2024
78d1a2a
Fixing error with animal respiration in testing data.
TaranRallings Dec 13, 2024
732cea3
Merge branch 'develop' into frozen_models
TaranRallings Dec 13, 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
19 changes: 19 additions & 0 deletions docs/source/using_the_ve/configuration/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,22 @@ formatted the configuration process will critically fail.

In addition to saving the configuration as an output file, it is also returned so that
downstream functions can make use of it. This is as a simple nested dictionary.

## Static models

All models (except `core`) accept a boolean configuration option, `static`, that
indicates if such a model should be updated every iteration (`static=false`, the
default behaviour) or not.

If `static=true`, there are several possibilities depending on the data
variables available in the [`Data` object](../data/data.md):

- For the initialisation, if the variables that are populated are:
- **All present**: The model initialisation process is bypassed.
- **None present**: The model initialisation process runs normally.
- For the update, if the variables that are populated are:
- **All present**: Then the update process is bypassed.
- **None present**: The update *is run just once*, keeping the same values for those
variables throughout the simulation.

Providing some but not all of the variables case will result in an error in all cases.
dalonsoa marked this conversation as resolved.
Show resolved Hide resolved
dalonsoa marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Collection of fixtures to assist the testing scripts."""

from logging import DEBUG
from unittest.mock import patch

import numpy as np
import pytest
Expand Down Expand Up @@ -562,3 +563,17 @@ def dummy_climate_data_varying_canopy(fixture_core_components, dummy_climate_dat
]

return dummy_climate_data


def patch_run_update(model: str):
"""Patch the run_update_check udring the init of the model."""
klass = "".join([w.capitalize() for w in model.split("_")] + ["Model"])
object_to_patch = f"virtual_ecosystem.models.{model}.{model}_model.{klass}"
return patch(f"{object_to_patch}._run_update_due_to_static_configuration")


def patch_bypass_setup(model: str):
"""Patch the run_update_check udring the init of the model."""
klass = "".join([w.capitalize() for w in model.split("_")] + ["Model"])
object_to_patch = f"virtual_ecosystem.models.{model}.{model}_model.{klass}"
return patch(f"{object_to_patch}._bypass_setup_due_to_static_configuration")
257 changes: 255 additions & 2 deletions tests/core/test_base_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,13 +522,13 @@ class TimingTestModel(
vars_populated_by_init=tuple(),
vars_populated_by_first_update=tuple(),
):
def setup(self) -> None:
def _setup(self) -> None:
pass

def spinup(self) -> None:
pass

def update(self, time_index: int, **kwargs: Any) -> None:
def _update(self, time_index: int, **kwargs: Any) -> None:
pass

def cleanup(self) -> None:
Expand Down Expand Up @@ -557,3 +557,256 @@ def from_config(
)

log_check(caplog, expected_log)


@pytest.mark.parametrize(
"static, vars_populated_by_init, data_vars, expected_result, expected_exception, "
"expected_message",
[
# Test case where model is static and all variables are present
pytest.param(
True,
("var1", "var2"),
{"var1": 1, "var2": 2},
True,
does_not_raise(),
None,
id="static_all_vars_present",
),
# Test case where model is static and no variables are present
pytest.param(
True,
("var1", "var2"),
{},
True,
does_not_raise(),
None,
id="static_no_vars_present",
),
# Test case where model is static and some variables are present
pytest.param(
True,
("var1", "var2"),
{"var1": 1},
None,
pytest.raises(ConfigurationError),
"Static model test_model requires to either all variables in "
"vars_populated_by_init to be present in the data object or all to be "
"absent. 1 out of 2 found: ['var1'].",
id="static_some_vars_present",
),
# Test case where model is not static and no variables are present
pytest.param(
False,
("var1", "var2"),
{},
False,
does_not_raise(),
None,
id="non_static_no_vars_present",
),
# Test case where model is not static and some variables are present
pytest.param(
False,
("var1", "var2"),
{"var1": 1},
None,
pytest.raises(ConfigurationError),
"Non-static model test_model requires none of the variables in "
"vars_populated_by_init to be present in the data object. "
"Present variables: ['var1']",
id="non_static_some_vars_present",
),
],
)
def test_bypass_setup_due_to_static_configuration(
static,
vars_populated_by_init,
data_vars,
expected_result,
expected_exception,
expected_message,
fixture_data,
fixture_config,
):
"""Test the _bypass_setup_due_to_static_configuration method."""
from virtual_ecosystem.core.base_model import BaseModel
from virtual_ecosystem.core.config import Config
from virtual_ecosystem.core.core_components import (
CoreComponents,
)
from virtual_ecosystem.core.data import Data

class TestModel(
BaseModel,
model_name="test_model",
model_update_bounds=("1 day", "1 month"),
vars_required_for_init=(),
vars_updated=(),
vars_required_for_update=(),
vars_populated_by_init=vars_populated_by_init,
vars_populated_by_first_update=(),
):
def _setup(self, *args: Any, **kwargs: Any) -> None:
pass

def spinup(self) -> None:
pass

def _update(self, time_index: int, **kwargs: Any) -> None:
pass

def cleanup(self) -> None:
pass

@classmethod
def from_config(
cls, data: Data, core_components: CoreComponents, config: Config
) -> BaseModel:
return super().from_config(
data=data, core_components=core_components, config=config
)

for var in data_vars.keys():
fixture_data[var] = fixture_data["existing_var"].copy()

core_components = CoreComponents(config=fixture_config)

with expected_exception as exc:
model = TestModel(
data=fixture_data, core_components=core_components, static=static
)
result = model._bypass_setup_due_to_static_configuration()
assert result == expected_result

if expected_message:
assert str(exc.value) == expected_message


@pytest.mark.parametrize(
"static, vars_populated_by_first_update, vars_updated, data_vars, expected_result,"
" expected_exception, expected_message",
[
# Test case where model is static and all variables are present
pytest.param(
True,
("var1", "var2"),
("var3",),
{"var1": 1, "var2": 2, "var3": 3},
False,
does_not_raise(),
None,
id="static_all_vars_present",
),
# Test case where model is static and no variables are present
pytest.param(
True,
("var1", "var2"),
("var3",),
{},
True,
does_not_raise(),
None,
id="static_no_vars_present",
),
# Test case where model is static and some variables are present
pytest.param(
True,
("var1", "var2"),
("var3",),
{"var1": 1},
None,
pytest.raises(ConfigurationError),
"Static model test_model requires to either all variables in "
"vars_populated_by_first_update and vars_updated to be present "
"in the data object or all to be absent. 1 out of 3 found: ['var1'].",
id="static_some_vars_present",
),
# Test case where model is not static and no variables are present
pytest.param(
False,
("var1", "var2"),
("var3",),
{},
True,
does_not_raise(),
None,
id="non_static_no_vars_present",
),
# Test case where model is not static and some variables are present
pytest.param(
False,
("var1", "var2"),
("var3",),
{"var1": 1},
None,
pytest.raises(ConfigurationError),
"Non-static model test_model requires none of the variables in "
"vars_populated_by_first_update or vars_updated to be present in the "
"data object. Present variables: ['var1']",
id="non_static_some_vars_present",
),
],
)
def test_run_update_due_to_static_configuration(
static,
vars_populated_by_first_update,
vars_updated,
data_vars,
expected_result,
expected_exception,
expected_message,
fixture_data,
fixture_config,
):
"""Test the _run_update_due_to_static_configuration method."""

from virtual_ecosystem.core.base_model import BaseModel
from virtual_ecosystem.core.config import Config
from virtual_ecosystem.core.core_components import CoreComponents
from virtual_ecosystem.core.data import Data

class TestModel(
BaseModel,
model_name="test_model",
model_update_bounds=("1 day", "1 month"),
vars_required_for_init=(),
vars_updated=vars_updated,
vars_required_for_update=(),
vars_populated_by_init=(),
vars_populated_by_first_update=vars_populated_by_first_update,
):
def _setup(self, *args: Any, **kwargs: Any) -> None:
pass

def spinup(self) -> None:
pass

def _update(self, time_index: int, **kwargs: Any) -> None:
pass

def cleanup(self) -> None:
pass

@classmethod
def from_config(
cls, data: Data, core_components: CoreComponents, config: Config
) -> BaseModel:
return super().from_config(
data=data, core_components=core_components, config=config
)

for var in data_vars.keys():
fixture_data[var] = fixture_data["existing_var"].copy()
dalonsoa marked this conversation as resolved.
Show resolved Hide resolved

core_components = CoreComponents(config=fixture_config)

with expected_exception as exc:
model = TestModel(
data=fixture_data, core_components=core_components, static=static
)
result = model._run_update_due_to_static_configuration()
assert result == expected_result

if expected_message:
assert str(exc.value) == expected_message
Loading