Adding models to eWaterCycle
eWaterCycle has been designed to make it easy to use hydrological models. Using a model doesn’t require much technical expertise, so researchers can focus on scientific discovery instead. By contrast, adding new models to eWaterCycle requires a deep understanding of the technologies used to build the system and the models of which it is comprised.
There are roughly three steps to adding a model to eWaterCycle:
Use the flowchart below to determine which steps are required for your model.
Create a model which exposes the Basic Model Interface (BMI)
Models in eWaterCycle follow the Basic Model Interface. You can either write a model that directly exposes this interface, but the more common use case is to add a wrapper around an existing model.
The https://github.com/eWaterCycle/leakybucket-bmi repository contains a simple example of a BMI model. It can be used as a template for your own model.
The following BMI methods are called by the eWaterCycle library and should be implemented:
initialize()
finalize()
update()
get_current_time()
get_end_time()
get_grid_type()
get_grid_rank()
get_grid_shape()
get_grid_size()
get_grid_x()
get_grid_y()
get_output_var_names()
get_start_time()
get_time_step()
get_time_units()
get_value_at_indices()
get_value()
get_var_grid()
get_var_itemsize()
get_var_nbytes()
get_var_type()
set_value_at_indices()
set_value()
Note that for get_time_units()
eWaterCycle expects are more specific string than the BMI specification.
The output should be formatted in the form of <time_units> since <reference_time>
, for example: seconds since 1970-01-01 00:00:00.0 +0000
.
More information on the format of the units can be found on the cftime documentation.
Add the model as eWaterCycle plugin
Models in eWaterCycle are added as plugins. A plugin is a Python package. The https://github.com/eWaterCycle/ewatercycle-plugin/tree/leakybucket repo contains a simple example of a plugin. It can be used as a template for your own plugin.
Custom forcing
If your model can use generic forcing data such as forcings based off
(GenericDistributedForcing
or GenericLumpedForcing
),
e.g. the MakkinkForcing classes, you can skip this section.
For more information on forcing, see the user guide .
If your model needs custom forcing data, you need to create your own forcing class.
The forcing class should sub class DefaultForcing
.
In the class you have to define attributes for the forcing files your model will need.
To use a ESMValTool recipe you have to implement the _build_recipe()
method.
It should return a Recipe
object which can be build using the
RecipeBuilder
class.
For example if your model only needs precipitation you can implement the method like this:
from ewatercycle.forcing import RecipeBuilder
...
@classmethod
def _build_recipe(cls,
start_time: datetime,
end_time: datetime,
shape: Path,
dataset: Dataset | str | dict = "ERA5",
):
return (
RecipeBuilder()
.start(start_time.year)
.end(end_time.year)
.shape(shape)
.dataset(dataset)
.add_variable("pr")
.build()
)
If your ESMValTool recipe needs additional arguments you can add and document them by implementing the generate()
method like
so
@classmethod
def generate(
cls,
<arguments of DefaultForcing>,
my_argument: str,
):
"""Generate forcing data for my model.
Args:
<arguments of DefaultForcing>
my_argument: My argument
"""
return super().generate(
<arguments of DefaultForcing>,
my_argument=my_argument,
)
The recipe output is mapped to the forcing class arguments with the _recipe_output_to_forcing_arguments()
method.
If you want to change the mapping you can override this method.
If you do not want to use ESMValTool to generate recipes you can override the generate()
method.
To list your forcing class in ewatercycle.forcing.sources
you have to register in the ewatercycle.forcings entry point group.
It can then be imported with
from ewatercycle.forcings import sources
forcing = source['MyForcing'](
...
)