Note
Last update 04/05/2021
Interfacing SuperflexPy with other frameworks
SuperflexPy does not integrate tools for calibration or uncertainty analysis. In this page we show an example on how a model built using SuperflexPy can be interfaced with other tools to perform this task.
SuperflexPy + SPOTPY
Note
This example is for illustration purposes only, and as such does not represent a specific recommendation of SPOTPY or of any specific calibration algorithm.
SPOTPY is a Python framework for calibration, uncertainty, and sensitivity analysis.
A model can be interfaced with SPOTPY by defining a class that wraps the model and implements the following methods:
__init__
: initializes the class, defining some attributes;parameters
: returns the parameters considered in the analysis (note that they may not be all the parameters used by the SuperflexPy model but only the ones that we want to vary in the analysis);simulation
: returns the output of the simulation;evaluation
: returns the observed output;objectivefunction
: defines the objective function to use to evaluate the simulation results.
Method __init__
1import spotpy
2
3class spotpy_model(object):
4
5 def __init__(self, model, inputs, dt, observations, parameters, parameter_names, output_index):
6
7 self._model = model
8 self._model.set_input(inputs)
9 self._model.set_timestep(dt)
10
11 self._parameters = parameters
12 self._parameter_names = parameter_names
13 self._observarions = observations
14 self._output_index = output_index
The class spotpy_model
is initialized defining the SuperflexPy model
that is used.
The model
, which can be any SuperflexPy component (from element to
network), must be defined before; the spotpy_model
class sets only the inputs
and the dt
.
Other variables necessary to initialize the class spotpy_model
are:
parameters
andparameters_names
, which define the parameters considered in the calibration. The first variable is a list ofspotpy.parameter
objects, the second variable is a list of the names of the SuperflexPy parameters;observations
, which is an array of observed output values;output_index
, which is the index of the output flux to be considered when evaluating the SuperflexPy simulation. This specification is necessary in the case of multiple output fluxes.
Method parameters
1 def parameters(self):
2 return spotpy.parameter.generate(self._parameters)
The method parameters
generates a new parameter set using the SPOTPY functionalities.
Method simulation
1 def simulation(self, parameters):
2
3 named_parameters = {}
4 for p_name, p in zip(self._parameter_names, parameters):
5 named_parameters[p_name] = p
6
7 self._model.set_parameters(named_parameters)
8 self._model.reset_states()
9 output = self._model.get_output()
10
11 return output[self._output_index]
The method simulation
sets the parameters (lines 3-7), resets the states to their initial
value (line 8), runs the SuperflexPy model (line 9), and returns the output
flux for the evaluation of the objective function (line 11).
Method evaluation
1 def evaluation(self):
2 return self._observarions
The method evaluation
returns the observed flux, used for the evaluation of the objective function.
Method objectivefunction
1 def objectivefunction(self, simulation, evaluation):
2
3 obj_fun = spotpy.objectivefunctions.nashsutcliffe(evaluation=evaluation,
4 simulation=simulation)
5
6 return obj_fun
The method objectivefunction
defines the objective function used to measure the model fit to the observed data. In this
case, the Nash-Sutcliffe efficiency is used.
Example of use
We now show how to employ the implementation above to calibrate a lumped model composed of 2 reservoirs.
First, we initialize the SuperflexPy model, as follows (see How to build a model with SuperflexPy for more details on how to set-up a model).
1from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython
2from superflexpy.implementation.root_finders.pegasus import PegasusPython
3from superflexpy.implementation.elements.hbv import PowerReservoir
4from superflexpy.framework.unit import Unit
5
6root_finder = PegasusPython()
7num_app = ImplicitEulerPython(root_finder=root_finder)
8
9reservoir_1 = PowerReservoir(parameters={'k': 0.1, 'alpha': 2.0},
10 states={'S0': 10.0},
11 approximation=num_app,
12 id='FR1')
13reservoir_2 = PowerReservoir(parameters={'k': 0.5, 'alpha': 1.0},
14 states={'S0': 10.0},
15 approximation=num_app,
16 id='FR2')
17
18hyd_mod = Unit(layers=[[reservoir_1],
19 [reservoir_2]],
20 id='model')
Then, we initialize an instance of the spotpy_model
class
1spotpy_hyd_mod = spotpy_model(
2 model=hyd_mod,
3 inputs=[P],
4 dt=1.0,
5 observations=Q_obs,
6 parameters=[
7 spotpy.parameter.Uniform('model_FR1_k', 1e-4, 1e-1),
8 spotpy.parameter.Uniform('model_FR2_k', 1e-3, 1.0),
9 ],
10 parameter_names=['model_FR1_k', 'model_FR2_k'],
11 output_index=0
12)
The arrays P
and Q_obs
in lines 3 and 5 contain time series of precipitation (input)
and observed streamflow (output). In this example, lines 6-10 indicate the two parameters that we calibrate
(model_FR1_k
and model_FR2_k
) together with their range of
variability.
We can now call the SPOTPY method to calibrate the model. Here, the SCE algorithm option is used.
1sampler = spotpy.algorithms.sceua(spotpy_hyd_mod, dbname='calibration', dbformat='csv')
2sampler.sample(repetitions=5000)