Skip to content

Commit

Permalink
initial set of files created
Browse files Browse the repository at this point in the history
  • Loading branch information
vgro committed Oct 10, 2023
1 parent 8d826e7 commit e0401ae
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 2 deletions.
21 changes: 21 additions & 0 deletions docs/source/api/abiotic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
jupytext:
cell_metadata_filter: -all
formats: md:myst
main_language: python
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.13.8
kernelspec:
display_name: vr_python3
language: python
name: vr_python3
---

# API reference for `abiotic` modules

```{eval-rst}
.. automodule:: virtual_rainforest.models.abiotic
```
24 changes: 24 additions & 0 deletions docs/source/api/abiotic/abiotic_constants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
jupytext:
cell_metadata_filter: -all
formats: md:myst
main_language: python
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.13.8
kernelspec:
display_name: vr_python3
language: python
name: vr_python3
---

#  API for the {mod}`~virtual_rainforest.models.abiotic.constants` module

```{eval-rst}
.. automodule:: virtual_rainforest.models.abiotic.constants
:autosummary:
:members:
:special-members: __init__
```
31 changes: 31 additions & 0 deletions docs/source/api/abiotic/abiotic_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
jupytext:
cell_metadata_filter: -all
formats: md:myst
main_language: python
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.13.8
kernelspec:
display_name: vr_python3
language: python
name: vr_python3
---

<!-- markdownlint-disable-next-line MD013 -->
# API documentation for the {mod}`~virtual_rainforest.models.abiotic.abiotic_model` module

```{eval-rst}
.. automodule:: virtual_rainforest.models.abiotic.abiotic_model
```

## The {mod}`~virtual_rainforest.models.abiotic.abiotic_model.AbioticModel` class

```{eval-rst}
.. autoclass:: virtual_rainforest.models.abiotic.abiotic_model.AbioticModel
:autosummary:
:members:
:exclude-members: model_name
```
5 changes: 4 additions & 1 deletion docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ team.
Abiotic Simple Overview <api/abiotic_simple.md>
Abiotic Simple Model <api/abiotic_simple/abiotic_simple_model.md>
Abiotic Simple Microclimate <api/abiotic_simple/microclimate.md>
Abiotic Simple Constants <api/abiotic_simple/abiotic_constants.md>
Abiotic Simple Constants <api/abiotic_simple/abiotic_simple_constants.md>
Abiotic Mechanistic Overview <api/abiotic.md>
Abiotic Mechanistic Model <api/abiotic/abiotic_model.md>
Abiotic Mechanistic Constants <api/abiotic/abiotic_constants.md>
Hydrology Overview <api/hydrology.md>
Hydrology Model <api/hydrology/hydrology_model.md>
Hydrology Above-ground <api/hydrology/above_ground.md>
Expand Down
99 changes: 99 additions & 0 deletions tests/models/abiotic/test_abiotic_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
"""Test module for abiotic.abiotic_model.py."""

from contextlib import nullcontext as does_not_raise
from logging import INFO

import pint
import pytest

from tests.conftest import log_check
from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel


def test_abiotic_model_initialization(
dummy_climate_data,
layer_roles_fixture,
):
"""Test `AbioticModel` initialization."""
from virtual_rainforest.models.abiotic.constants import AbioticConsts

# Initialize model
model = AbioticModel(
dummy_climate_data,
pint.Quantity("1 hour"),
soil_layers=[-0.5, -1.0],
canopy_layers=10,
constants=AbioticConsts(),
)

# In cases where it passes then checks that the object has the right properties
assert set(
[
"setup",
"spinup",
"update",
"cleanup",
]
).issubset(dir(model))
assert model.model_name == "abiotic"
assert repr(model) == "AbioticModel(update_interval = 1 hour)"
assert model.layer_roles == layer_roles_fixture


@pytest.mark.parametrize(
"config,time_interval,raises,expected_log_entries",
[
(
{},
None,
pytest.raises(KeyError),
(), # This error isn't handled so doesn't generate logging
),
(
{
"core": {
"timing": {
"start_date": "2020-01-01",
"update_interval": "1 day",
},
"layers": {
"soil_layers": [-0.5, -1.0],
"canopy_layers": 10,
},
},
},
pint.Quantity("1 day"),
does_not_raise(),
(
(
INFO,
"Information required to initialise the abiotic model "
"successfully extracted.",
),
),
),
],
)
def test_generate_abiotic_model(
caplog,
dummy_climate_data,
config,
time_interval,
raises,
expected_log_entries,
layer_roles_fixture,
):
"""Test that the initialisation of the abiotic model works as expected."""

# Check whether model is initialised (or not) as expected
with raises:
model = AbioticModel.from_config(
dummy_climate_data,
config,
pint.Quantity(config["core"]["timing"]["update_interval"]),
)
assert model.layer_roles == layer_roles_fixture
assert model.update_interval == time_interval

# Final check that expected logging entries are produced
log_check(caplog, expected_log_entries)
20 changes: 20 additions & 0 deletions virtual_rainforest/models/abiotic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
r"""The :mod:`~virtual_rainforest.models.abiotic` module is one of the component
models of the Virtual Rainforest. It is comprised of several submodules that calculate
the microclimate for the Virtual Rainforest.
Each of the abiotic sub-modules has its own API reference page:
* The :mod:`~virtual_rainforest.models.abiotic.abiotic_model` submodule
instantiates the AbioticModel class which consolidates the functionality of the
abiotic model into a single class, which the high level functions of the
Virtual Rainforest can then use.
* The :mod:`~virtual_rainforest.models.abiotic.constants` submodule provides a
set of dataclasses containing the constants required by the broader abiotic model.
""" # noqa: D205, D415

from virtual_rainforest.core.base_model import register_model
from virtual_rainforest.models.abiotic.abiotic_model import AbioticModel

register_model(__name__, AbioticModel)
120 changes: 120 additions & 0 deletions virtual_rainforest/models/abiotic/abiotic_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""The :mod:`~virtual_rainforest.models.abiotic.abiotic_model` module creates a
:class:`~virtual_rainforest.models.abiotic.abiotic_model.AbioticModel`
class as a child of the :class:`~virtual_rainforest.core.base_model.BaseModel` class. At
present a lot of the abstract methods of the parent class (e.g.
:func:`~virtual_rainforest.core.base_model.BaseModel.spinup`) are overwritten using
placeholder functions that don't do anything. This will change as the Virtual Rainforest
model develops. The factory method
:func:`~virtual_rainforest.models.abiotic.abiotic_model.AbioticModel.from_config`
exists in a more complete state, and unpacks a small number of parameters from our
currently pretty minimal configuration dictionary. These parameters are then used to
generate a class instance. If errors crop here when converting the information from the
config dictionary to the required types they are caught and then logged, and at the end
of the unpacking an error is thrown. This error should be caught and handled by
downstream functions so that all model configuration failures can be reported as one.
""" # noqa: D205, D415

from __future__ import annotations

from typing import Any

from pint import Quantity

from virtual_rainforest.core.base_model import BaseModel
from virtual_rainforest.core.config import Config
from virtual_rainforest.core.constants import load_constants
from virtual_rainforest.core.data import Data
from virtual_rainforest.core.logger import LOGGER
from virtual_rainforest.core.utils import set_layer_roles
from virtual_rainforest.models.abiotic.constants import AbioticConsts


class AbioticModel(BaseModel):
"""A class describing the abiotic model.
Args:
data: The data object to be used in the model.
update_interval: Time to wait between updates of the model state.
soil_layers: A list setting the number and depths of soil layers to be modelled.
canopy_layers: The initial number of canopy layers to be modelled.
constants: Set of constants for the abiotic model.
"""

model_name = "abiotic"
"""The model name for use in registering the model and logging."""
lower_bound_on_time_scale = "1 minute"
"""Shortest time scale that abiotic model can sensibly capture."""
upper_bound_on_time_scale = "1 day"
"""Longest time scale that abiotic model can sensibly capture."""
required_init_vars = ()
"""The required variables and axes for the abiotic model"""
vars_updated = ()
"""Variables updated by the abiotic model"""

def __init__(
self,
data: Data,
update_interval: Quantity,
soil_layers: list[float],
canopy_layers: int,
constants: AbioticConsts,
**kwargs: Any,
):
super().__init__(data, update_interval, **kwargs)

# create a list of layer roles
layer_roles = set_layer_roles(canopy_layers, soil_layers)

self.data
"""A Data instance providing access to the shared simulation data."""
self.layer_roles = layer_roles
"""A list of vertical layer roles."""
self.update_interval
"""The time interval between model updates."""
self.constants = constants
"""Set of constants for the abiotic model"""

@classmethod
def from_config(
cls, data: Data, config: Config, update_interval: Quantity
) -> AbioticModel:
"""Factory function to initialise the abiotic model from configuration.
This function unpacks the relevant information from the configuration file, and
then uses it to initialise the model. If any information from the config is
invalid rather than returning an initialised model instance an error is raised.
Args:
data: A :class:`~virtual_rainforest.core.data.Data` instance.
config: A validated Virtual Rainforest model configuration object.
update_interval: Frequency with which all models are updated.
"""

# Find number of soil and canopy layers
soil_layers = config["core"]["layers"]["soil_layers"]
canopy_layers = config["core"]["layers"]["canopy_layers"]

# Load in the relevant constants
constants = load_constants(config, "abiotic", "AbioticConsts")

LOGGER.info(
"Information required to initialise the abiotic model successfully "
"extracted."
)
return cls(data, update_interval, soil_layers, canopy_layers, constants)

def setup(self) -> None:
"""Function to set up the abiotic model."""

def spinup(self) -> None:
"""Placeholder function to spin up the abiotic model."""

def update(self, time_index: int, **kwargs: Any) -> None:
"""Function to update the abiotic model.
Args:
time_index: The index of the current time step in the data object.
"""

def cleanup(self) -> None:
"""Placeholder function for abiotic model cleanup."""
12 changes: 12 additions & 0 deletions virtual_rainforest/models/abiotic/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""The ``models.abiotic.constants`` module contains a set of dataclasses which contain
parameters required by the broader :mod:`~virtual_rainforest.models.abiotic` model.
These parameters are constants in that they should not be changed during a particular
simulation.
""" # noqa: D205, D415

from dataclasses import dataclass


@dataclass(frozen=True)
class AbioticConsts:
"""Dataclass to store all constants for the `abiotic` model."""
28 changes: 28 additions & 0 deletions virtual_rainforest/models/abiotic/model_schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"type": "object",
"properties": {
"abiotic": {
"description": "Configuration settings for the abiotic module",
"type": "object",
"properties": {
"constants": {
"description": "Constants for the abiotic module",
"type": "object",
"properties": {
"AbioticConsts": {
"type": "object"
}
},
"required": [
"AbioticConsts"
]
}
},
"default": {},
"required": []
}
},
"required": [
"abiotic"
]
}
3 changes: 2 additions & 1 deletion virtual_rainforest/models/abiotic_simple/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
soil temperature profiles.
* The :mod:`~virtual_rainforest.models.abiotic_simple.constants` submodule provides a
set of dataclasses containing the constants required by the broader soil model.
set of dataclasses containing the constants required by the broader abiotic simple
model.
""" # noqa: D205, D415

Expand Down

0 comments on commit e0401ae

Please sign in to comment.