Model coupling scripts
This book has moved to the FINAM documentation. You are viewing an outdated version!
Coupling setups are created and executed using Python scripts.
Simple example
Here is a simple example coupling two components:
# simple-coupling.py
import random
from datetime import datetime, timedelta
import finam as fm
from finam.modules.generators import CallbackGenerator
from finam.modules.visual.time_series import TimeSeriesView
if __name__ == "__main__":
# Instantiate components, e.g. models
# Here, we use a simple component that outputs a random number each step
generator = CallbackGenerator(
{"Value": (lambda _t: random.uniform(0, 1), fm.Info(grid=fm.NoGrid()))},
start=datetime(2000, 1, 1),
step=timedelta(days=1),
)
# A live plotting component
plot = TimeSeriesView(
inputs=["Value"],
start=datetime(2000, 1, 1),
step=timedelta(days=1),
intervals=[1],
)
# Create a `Composition` containing all components
composition = fm.Composition([generator, plot])
# Initialize the `Composition`
composition.initialize()
# Couple inputs to outputs
generator.outputs["Value"] >> plot.inputs["Value"]
# Run the composition until January 2001
composition.run(datetime(2001, 1, 1))
In the above example, we couple a simple generator component (CallbackGenerator
)
with a live plotting component (TimeSeriesView
).
Note: with package
finam
installed, simply run the above scripts with:$ python simple-coupling.py
The typical steps in a script are:
- Instantiate components and adapters (see next example)
- Create a
Composition
and initialize it - Connect outputs to inputs using the overloaded
>>
operator (__rshift__
) - Run the
Composition
Inputs and outputs
Inputs and outputs of a component can be retrieved via inputs
and outputs
properties.
Both methods return a Python dict-like
, with strings as keys and input or output objects as values, respectively.
An input can be connected to an output using either >>
(as in the examples), or the output's method chain(input)
. Both lines here are equivalent:
generator.outputs["Value"] >> plot.inputs["Value"]
generator.outputs["Value"].chain(plot.inputs["Value"])
Adapters
In the above example, both coupled components match in terms of the exchanged data (numeric value) as well as their time step (1).
This is not necessarily the case for all coupling setups. To mediate between components, FINAM uses adapters. Those can be used to transform data (regridding, geographic projections, ...) or for temporal interpolation or aggregation.
The following examples uses a similar setup like the previous one, but with differing time steps and two chained adapters:
# adapter-coupling.py
import random
from datetime import datetime, timedelta
import finam as fm
from finam.adapters import base, time
from finam.modules import generators, visual
if __name__ == "__main__":
# Instantiate components, e.g. models
# Here, we use a simple component that outputs a random number each step
generator = generators.CallbackGenerator(
{"Value": (lambda _t: random.uniform(0, 1), fm.Info(grid=fm.NoGrid()))},
start=datetime(2000, 1, 1),
step=timedelta(days=10),
)
# A live plotting component
plot = visual.time_series.TimeSeriesView(
inputs=["Value"],
start=datetime(2000, 1, 1),
step=timedelta(days=1),
intervals=[1],
)
# Create two adapters for...
# temporal interpolation
time_interpolation_adapter = time.LinearTime()
# data transformation
square_adapter = base.Callback(lambda x, _time: x * x)
# Create a `Composition` containing all components
composition = fm.Composition([generator, plot])
# Initialize the `Composition`
composition.initialize()
# Couple inputs to outputs, via multiple adapters
(
generator.outputs["Value"]
>> time_interpolation_adapter
>> square_adapter
>> plot.inputs["Value"]
)
# Run the composition until January 2000
composition.run(datetime(2001, 1, 1))
Adapter chaining
As can be seen from the example, components and adapters can be chained using the >>
operator (or the chain(...)
method).
This is achieved by:
- An adapter is an input, and at the same time an output
- The chained input is returned by
>>
andchain(...)
. In case the chained input is an adapter (and thus also an output), it can be immediately reused in a further chaining operation
Logging
FINAM provides a comprehensive logging framework built on Pythons standard logging package.
You can configure the base logger when creating the Composition
as shown above:
import logging
comp = Composition(
modules,
logger_name="FINAM",
print_log=True,
log_file=True,
log_level=logging.INFO,
)
There you have several options:
logger_name
: (str) Base name of the logger in the output (FINAM
by default)print_log
: (bool) Whether logging should be shown in the terminal outputlog_file
: (None, bool, pathlike) Whether a log-file should be createdNone
orFalse
: no log file will be writtenTrue
: a log file with the name{logger_name}_{time.strftime('%Y-%m-%d_%H-%M-%S')}.log
will be created in the current working directory (e.g.FINAM_2022-09-26_12-58-15.log
)<pathlike>
: log file will be created under the given path
log_level
: (int) this will control the level of logging (logging.INFO
by default)- only log messages with a level equal or higher than the given logging level will be shown
- options are (from most to least verbose):
logging.DEBUG
,logging.INFO
,logging.WARNING
,logging.ERROR
,logging.CRITICAL
or any positive integer number
A log file could look like this, when setting the logging level to logging.INFO
:
2022-08-26 11:31:28,283 - FINAM - INFO - doing fine
2022-08-26 11:31:28,284 - FINAM - WARNING - Boo
or like this, when setting logging level to logging.DEBUG
:
2022-08-26 11:31:28,283 - FINAM - INFO - doing fine
2022-08-26 11:31:28,284 - FINAM - WARNING - Boo
2022-08-26 11:31:28,285 - FINAM - DEBUG - Some debugging message