Architecture
CarboKitten is modular in design. This gives us the advantage of few repetitions in the code, but it also means a little study getting in to developing CarboKitten.
Models
A model is a module that must have certain data structures and methods implemented.
struct Input <: AbstractInput
: contains all input parametersstruct State <: AbstractState
: contains all run-time statefunction initial_state(input)
: creates an initialState
from anInput
function step!(input)(state)
: curried function to advance state and return aH5Writer.DataFrame
function write_header(fid, input)
: writes meta-data to an open HDF5 file
For most models the Input
and State
structures are generated from a hierarchy of components, which is explained below.
step!
The step!
method deserves some extra attention: this is where the high-level logic of a model goes. Running a model is nothing but a repeated application of step!(input)
on the state. Note that the step!
function is curried: the input
and state
parameters are given on separate occasions. This allows the model to prepare later execution on state
in a more efficient manner. For example, we may allocate memory or prepare quantities derived from input variables.
Components
A model in CarboKitten is composed of components. Each component extends the Input
and/or State
data structures with members and can also provide functions that work on those elements. Components can inherit from each other. For instance, the WaterDepth
component inherits from Boxes
component to have a notion of the grid size, and the TimeIntegration
component to obtain a time coordinate from the state.
A component has the following syntax:
@compose module MyComponent
@mixin DependencyA, DependencyB
using ..Common
@kwdef struct Input <: AbstractInput
...
end
...
end
Here the Common
module is a normal Julia module living in the same scope as the component. This Common
module contains common definitions used throughout the CarboKitten code base.
H5Writer
The H5Writer
component is special as it serves to execute a model. For example, if we have a model MyModel
with all the required definitions,
@component module MyModel
@mixin Tag, H5Writer, ...
...
function step!(input)
...
return function (state)
...
return H5Writer.DataFrame(...)
end
end
...
end
We can run that model as follows:
H5Writer.run(Model{MyModel}, MyModel.Input(), "output.h5")
This will call MyModel.initial_state
first, then repeatedly MyModel.step!
, which should return a H5Writer.DataFrame
for writing to HDF5. Here Model
is a value type (defined in CarboKitten.Components.Common
).