Skip to content

Commit

Permalink
Merge branch 'feature/new_abiotic_model' into feature/abiotic_tools
Browse files Browse the repository at this point in the history
  • Loading branch information
vgro committed Nov 27, 2023
2 parents 0811816 + 6b97373 commit fdb6780
Show file tree
Hide file tree
Showing 41 changed files with 1,265 additions and 785 deletions.
40 changes: 40 additions & 0 deletions docs/source/development/defining_new_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,46 @@ properties.
}
```

#### Model dependencies

Your model may depend on a particular execution order for other models. For example, the
`freshwater` model might rely on data set up by the `hydrology` model, and so the
`hydrology` model needs to be initialised and updated before the `freshwater` model.
This is controlled using model configuration: although these dependencies may be
strong, it is more flexible to set them up through the configuration process than by
hard coding dependencies into the model objects themselves.

Your JSON Schema document therefore needs to include the following at the root level, so
that the model configuration includes a `[freshwater.depends]` section:

```json
"depends": {
"type": "object",
"default": {},
"properties": {
"init": {
"type": "array",
"default": ["hydrology"],
"items": {
"type": "string"
}
},
"update": {
"type": "array",
"default": ["hydrology"],
"items": {
"type": "string"
}
}
}
}
```

Note that this schema provides **default dependencies**, which set which models should
run before your model. There is no guarantee that users will necessarily include all of
these models in their configuration and the dependencies can always be overridden by
users. Configurations that do this may well not work, but that is for users to tackle.

### The `from_config` factory method

Configuration files are used to create a configuration object (see
Expand Down
83 changes: 52 additions & 31 deletions docs/source/virtual_rainforest/core/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ configuration is shown below:

```toml
[core]
modules = ["plants"]

[core.grid]
cell_nx = 10
cell_ny = 10
Expand All @@ -30,25 +28,57 @@ Here, the first tag indicates the module in question (e.g. `core`), and subseque
indicate (potentially nested) module level configuration details (e.g. horizontal grid
size `cell_nx`).

The configuration system does not require a single input config file, instead the config
input can be spread across an arbitrarily large number of config files. However,
information cannot be repeated between files as there is no way to establish which of
two values (of e.g. `core.grid.cell_nx`) the user intended to provide. In this case, the
module will throw critical error and the `virtual_rainforest` model will not configure.
The user supplies a list of config files and/or folders to look for config files, within
the supplied folders every `toml` file will be read in. Once the complete configuration
is validated, it is saved as a single file using a user provided name and location. If a
file already exists with this name at this location, the configuration process will
critically fail. This minimises the chance of significant confusion downstream.
The configuration system does not require a single input config file, instead the
configuration can be separated out into a set of config files. This allows different
configuration files to be re-used in a modular way, allowing a library of configuration
options to be set up.

When a simulation is run, users can identify a set of specific configuration files or
specific folders containing a set of files that should all be used. This set of files
will be loaded and assembled into a complete configuration. Optionally, the
configuration can include instructions to export the assembled configuration as single
file that provides a useful record of the setup for a particular simulation.

```toml
[core.data_output_options]
save_merged_config = true
output = "/output/directory"
out_merge_file_name = "merged_configuration.toml"
```

Note that **configuration setting cannot be repeated between files** as there is no way
to establish which of two values (of e.g. `core.grid.cell_nx`) the user intended to
provide. When settings are repeated, the configuration process will report a critical
error and the simulation will terminate.

## Optional module loading

The config system allows for different module implementations and combinations to be
configured. While the `core` module must always be configured, all other modules are
optional. A list of modules to be configured can be specified using the tag
`core.modules`. If this list includes repeated module names configuration will
critically fail. If this tag isn't provided the default set of modules will be loaded,
i.e. the standard versions of `animals`, `plants`, `soil`, and `abiotic`.
configured. The choice of models to be configured is indicated by including the required
model names as top level entries in the model configuration. Note that the model name is
required, even if the configuration uses all of the default settings. For example, this
configuration specifies that four models are to be used, all with their default
settings:

```toml
[core] # optional
[soil]
[hydrology]
[plants]
[abiotic_simple]
```

The `[core]` element is optional as the Virtual Rainforest core module is always
required and the default core settings will be used if it is omitted. It can be useful
to include it as a reminder that a particular configuration is intentionally using the
default settings. Each module configuration section can of course be expanded to change
defaults.

```{warning}
Note that there is no guarantee that a particular set of configured models work in
combination. You will need to look at model details to understand which other modules
might be required.
```

## JSON schema

Expand Down Expand Up @@ -92,22 +122,11 @@ follows the pattern below:
"nx",
"ny"
]
},
"modules": {
"description": "List of modules to be configured",
"type": "array",
"items": {
"type": "string"
},
"default": [
"plants"
]
}
},
"default": {},
"required": [
"grid",
"modules"
]
}
},
Expand All @@ -118,11 +137,13 @@ follows the pattern below:
```

The type of every single tag should be specified, with `object` as the type for tags
that are mere containers for lower level tags (i.e. `core`). In cases where strictly
that are containers for more nested tags (i.e. `core`). In cases where strictly
positive values are required this is achieved by setting `exclusiveMinimum` to zero. For
each `object`, the `required` key specifies the tags that must be included for
validation to pass. We don't allow tags that are not included within a schema, therefore
the config module automatically sets `additionalProperties` as false for every object in
validation to pass.

We do not permit configuration tags that are not included within a schema, therefore the
config module automatically sets `additionalProperties` as `false` for every object in
the schema. The `default` key is used to specify the default value that should be
inserted if the tag in question is not provided for the user. The default value for all
objects should be set as `{}` to ensure that nested defaults can be found and populated.
Expand Down
20 changes: 20 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,26 @@ def log_check(
)


def record_found_in_log(
caplog: pytest.LogCaptureFixture,
find: tuple[int, str],
) -> bool:
"""Helper function to look for a specific logging record in the captured log.
Arguments:
caplog: An instance of the caplog fixture
find: A tuple giving the logging level and message to look for
"""

try:
# Iterate over the record tuples, ignoring the leading element
# giving the logger name
_ = next(msg for msg in caplog.record_tuples if msg[1:] == find)
return True
except StopIteration:
return False


@pytest.fixture(autouse=True)
def reset_module_registry():
"""Reset the module registry.
Expand Down
2 changes: 0 additions & 2 deletions tests/core/data/all_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# modules. Each value has been chosen to be different from the default value, so that
# this file can be used to test that providing non-default values works.
[core]
modules = ["plants", "soil", "animals"]

[core.grid]
cell_nx = 10
Expand Down Expand Up @@ -77,4 +76,3 @@ diet = "herbivore"
metabolic_type = "ectothermic"
birth_mass = 0.0005
adult_mass = 0.005

1 change: 0 additions & 1 deletion tests/core/data/all_config_bad.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[core]
modules = [plants, soil]

[core.grid]
nx = 10
Expand Down
7 changes: 5 additions & 2 deletions tests/core/data/default_config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# This schema is currently empty as every soil and simple_abiotic config option has a
# default
# This configuration loads core, abiotic_simple and soil - assumes these can be fully
# configured from defaults alone
[core]
[abiotic_simple]
[soil]
5 changes: 5 additions & 0 deletions tests/core/data/toml_errors.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[core

[core.grid]
nx = 10
ny = 10
Loading

0 comments on commit fdb6780

Please sign in to comment.