calibtools¶
This module implements features for calibrating model parameters.
Module calibtools
implements the following members:
RuleType1
Type variable.
RuleType2
Type variable.
TargetFunction
Protocol class for the target function required by classCalibrationInterface
.
Adaptor
Protocol class for defining adoptors required byReplace
objects.
SumAdaptor
Adaptor which calculates the sum of the values of multipleRule
objects and assigns it to the value(s) of the targetParameter
object.
FactorAdaptor
Adaptor which calculates the product of the value of the parentReplace
object and the value(s) of a given referenceParameter
object and assigns it to the value(s) of the targetParameter
object.
Rule
Base class for defining calibration rules.
Replace
Rule
class which simply replaces the current model parameter value(s) with the current calibration parameter value.
Add
Rule
class which adds its calibration delta to the original model parameter value(s).
Multiply
Rule
class which multiplies the original model parameter value(s) by its calibration factor.
CalibrationInterface
Interface for the coupling of HydPy to optimisation libraries like NLopt.
ReplaceIUH
ARule
class specialised forIUH
parameters.
CalibSpec
Helper class for specifying the properties of a single calibration parameter.
CalibSpecs
Collection class for handlingCalibSpec
objects.
-
class
hydpy.auxs.calibtools.
TargetFunction
(*args, **kwds)[source]¶ Bases:
typing_extensions.Protocol
Protocol class for the target function required by class
CalibrationInterface
.The target functions must calculate and return a floating-point number reflecting the quality of the current parameterisation of the models of the current project. Often, as in the following example, the target function relies on objective functions as
nse()
, applied on the time series of theSim
andObs
sequences handled by theHydPy
object:>>> from hydpy import HydPy, nse, TargetFunction >>> class Target(TargetFunction): ... def __init__(self, hp): ... self.hp = hp ... def __call__(self): ... return sum(nse(node=node) for node in self.hp.nodes) >>> target = Target(HydPy())
See the documentation on class
CalibrationInterface
for more information.
-
class
hydpy.auxs.calibtools.
Adaptor
(*args, **kwds)[source]¶ Bases:
typing_extensions.Protocol
Protocol class for defining adoptors required by
Replace
objects.Often, one calibration parameter (represented by one
Replace
object) depends on other calibration parameters (represented by otherReplace
objects) or other “real” parameter values. Please select an existing or define an individual adaptor and assign it to aReplace
object to introduce such dependencies.See class
SumAdaptor
or classFactorAdaptor
for concrete examples.
-
class
hydpy.auxs.calibtools.
SumAdaptor
(*args, **kwds)[source]¶ Bases:
hydpy.auxs.calibtools.Adaptor
Adaptor which calculates the sum of the values of multiple
Rule
objects and assigns it to the value(s) of the targetParameter
object.Class
SumAdaptor
helps to introduce “larger than” relationships between calibration parameters. A common use-case is the time of concentration of different runoff components. The time of concentration of base flow should be larger than the one of direct runoff. Accordingly, when modelling runoff concentration with linear storages, the recession coefficient of direct runoff should be larger. Principally, we could ensure this during a calibration process by defining twoRule
objects with fixed non-overlapping parameter ranges. For example, we could search for the best direct runoff delay between 1 and 5 days and the base flow delay between 5 and 100 days. We demonstrate this for the recession coefficient parametersK
andK4
of application modelhland_v1
(assuming the nonlinearity parameterAlpha
to be zero):>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import Replace, SumAdaptor >>> k = Replace( ... name="k", ... parameter="k", ... value=2.0**-1, ... lower=5.0**-1, ... upper=1.0**-1, ... parameterstep="1d", ... model="hland_v1", ... ) >>> k4 = Replace( ... name="k4", ... parameter="k4", ... value=10.0**-1, ... lower=100.0**-1, ... upper=5.0**-1, ... parameterstep="1d", ... model="hland_v1", ... )
To allow for non-fixed non-overlapping ranges, we can prepare a
SumAdaptor
object, knowing both ourRule
objects, assign it the direct runoff-relatedRule
object, and, for example, set its lower boundary to zero:>>> k.adaptor = SumAdaptor(k, k4) >>> k.lower = 0.0
Calling method
apply_value()
of theReplace
objects makes ourSumAdaptor
object apply the sum of the values of all of itsRule
objects:>>> control = hp.elements.land_dill.model.parameters.control >>> k.apply_value() >>> with pub.options.parameterstep("1d"): ... control.k k(0.6)
-
class
hydpy.auxs.calibtools.
FactorAdaptor
(*args, **kwds)[source]¶ Bases:
hydpy.auxs.calibtools.Adaptor
Adaptor which calculates the product of the value of the parent
Replace
object and the value(s) of a given referenceParameter
object and assigns it to the value(s) of the targetParameter
object.Class
FactorAdaptor
helps to respect dependencies between model parameters. If you, for example, aim at calibrating the permanent wilting point (PWP
) of modellland_v1
, you need to make sure it always agrees with the maximum soil water storage (WMax
). Especially, one should avoid permanent wilting points larger than total porosity. Due to the high variability of soil properties within most catchments, it is no real option to define a fixed upper threshold forPWP
. By using classFactorAdaptor
you can instead calibrate a multiplication factor. Setting the bounds of such a factor to 0.0 and 0.5, for example, would result inPWP
values ranging from zero up to half ofWMax
for each respective response unit.To show how class
FactorAdaptor
works, we select another use-case based on the Lahn example project prepared by functionprepare_full_example_2()
:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2()
hland_v1
calculates the “normal” potential snow-melt with the degree-day factorCFMax
. For glacial zones, it also calculates a separate potential glacier-melt with the additional degree-day factorGMelt
. Suppose, we haveCFMax
readily available for the different hydrological response units of the Lahn catchment. We might find it useful to calibrateGMelt
based on the spatial pattern ofCFMax
. Therefore, we first define aReplace
rule for parameterGMelt
:>>> from hydpy import Replace, FactorAdaptor >>> gmelt = Replace( ... name="gmelt", ... parameter="gmelt", ... value=2.0, ... lower=0.5, ... upper=2.0, ... parameterstep="1d", ... model="hland_v1", ... )
Second, we initialise a
FactorAdaptor
object based on target rule gmelt and our reference parameterCFMax
and assign it our rule object:>>> gmelt.adaptor = FactorAdaptor(gmelt, "cfmax")
The Dill subcatchment, as the whole Lahn basin, does not contain any glaciers. Hence it defines (identical)
CFMax
values for the zones of typeFIELD
andFOREST
, but must not specify any value forGMelt
:>>> control = hp.elements.land_dill.model.parameters.control >>> control.cfmax cfmax(field=4.55853, forest=2.735118) >>> control.gmelt gmelt(nan)
Next, we call method
apply_value()
of theReplace
object to apply theFactorAdaptor
object on all relevantGMelt
instances of the Lahn catchment:>>> gmelt.adaptor(control.gmelt)
The string representation of the
GMelt
instance of Dill catchment seems to indicate nothing happened:>>> control.gmelt gmelt(nan)
However, inspecting the individual values of the respective response units reveals the multiplication was successful:
>>> from hydpy import print_values >>> print_values(control.gmelt.values) 9.11706, 5.470236, 9.11706, 5.470236, 9.11706, 5.470236, 9.11706, 5.470236, 9.11706, 5.470236, 9.11706, 5.470236
Calculating values for response units that do not require these values can be misleading. We can improve the situation by using the masks provided by the respective model, in our example mask
Glacier
. To make this clearer, we set the first six response units toZoneType
GLACIER
:>>> from hydpy.models.hland_v1 import * >>> control.zonetype(GLACIER, GLACIER, GLACIER, GLACIER, GLACIER, GLACIER, ... FIELD, FOREST, ILAKE, FIELD, FOREST, ILAKE)
We now can assign the
SumAdaptor
object to the direct runoff-relatedReplace
object and, for example, set its lower boundary to zero:Now we create a new
FactorAdaptor
object, handling the same parameters but also theGlacier
mask:>>> gmelt.adaptor = FactorAdaptor(gmelt, "cfmax", "glacier")
To be able to see the results of our new adaptor object, we change the values both of our reference parameter and our rule object:
>>> control.cfmax(field=5.0, forest=3.0, glacier=6.0) >>> gmelt.value = 0.5
The string representation of our target parameter shows that the glacier-related day degree factor of all glacier zones is now half as large as the snow-related one:
>>> gmelt.apply_value() >>> control.gmelt gmelt(3.0)
Note that all remaining values (for zone types
FIELD
,FOREST
, andILAKE
are still the same. This intended behaviour allows calibrating, for example, hydrological response units of different types with different rule objects:>>> print_values(control.gmelt.values) 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 9.11706, 5.470236, 9.11706, 5.470236, 9.11706, 5.470236
-
class
hydpy.auxs.calibtools.
Rule
(*, name: str, parameter: Union[Type[hydpy.core.parametertools.Parameter], hydpy.core.parametertools.Parameter, str], value: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, model: Optional[Union[module, str]] = None)[source]¶ Bases:
abc.ABC
Base class for defining calibration rules.
Each
Rule
object relates one calibration parameter with some model parameters. We select the classReplace
as a concrete example for the following explanations and use the Lahn example project, which we prepare by calling functionprepare_full_example_2()
:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2()
We define a
Rule
object supposed to replace the values of parameterFC
of application modellland_v1
. Note that argument name is the name of the rule itself, whereas the argument parameter is the name of the parameter:>>> from hydpy import Replace >>> rule = Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... )
The following string representation shows us the full list of available arguments:
>>> rule Replace( name="fc", parameter="fc", lower=-inf, upper=inf, parameterstep=None, value=100.0, model="hland_v1", selections=("complete",), )
The initial value of parameter
FC
is 206 mm:>>> fc = hp.elements.land_lahn_1.model.parameters.control.fc >>> fc fc(206.0)
We can modify it by calling method
apply_value()
:>>> rule.apply_value() >>> fc fc(100.0)
You can change and apply the value at any time:
>>> rule.value = 200.0 >>> rule.apply_value() >>> fc fc(200.0)
Sometimes, one needs to make a difference between the original value to be calibrated and the actually applied value. Therefore, (only) the
Replace
class allows defining custom “adaptors”. Prepare anAdaptor
function and assign it to the relevantReplace
object (see the documentation on classSumAdaptor
orFactorAdaptor
for more realistic examples):>>> rule.adaptor = lambda target: target(2.0*rule.value)
Now, our rule does not apply the original but the adapted calibration parameter value:
>>> rule.apply_value() >>> fc fc(400.0)
Use method
reset_parameters()
to restore the original states of the affected parameters (“original” here means at the time of initialisation of theRule
object):>>> rule.reset_parameters() >>> fc fc(206.0)
The value of parameter
FC
is not time-dependent. Anyparameterstep
information given to itsRule
object is ignored (note that we pass an example parameter object of typeFC
instead of the string fc this time):>>> Replace( ... name="fc", ... parameter=fc, ... value=100.0, ... model="hland_v1", ... parameterstep="1d", ... ) Replace( name="fc", parameter="fc", lower=-inf, upper=inf, parameterstep=None, value=100.0, model="hland_v1", selections=("complete",), )
For time-dependent parameters, the rule queries the current global
parameterstep
value, if you do not specify one explicitly (note that we pass the parameter typePercMax
this time):>>> from hydpy.models.hland.hland_control import PercMax >>> rule = Replace( ... name="percmax", ... parameter=PercMax, ... value=5.0, ... model="hland_v1", ... )
The
Rule
object internally handles, to avoid confusion, a copy ofparameterstep
.>>> from hydpy import pub >>> pub.options.parameterstep = None >>> rule Replace( name="percmax", parameter="percmax", lower=-inf, upper=inf, parameterstep="1d", value=5.0, model="hland_v1", selections=("complete",), ) >>> rule.apply_value() >>> percmax = hp.elements.land_lahn_1.model.parameters.control.percmax >>> with pub.options.parameterstep("1d"): ... percmax percmax(5.0)
Alternatively, you can pass a parameter step size yourself:
>>> rule = Replace( ... name="percmax", ... parameter="percmax", ... value=5.0, ... model="hland_v1", ... parameterstep="2d", ... ) >>> rule.apply_value() >>> with pub.options.parameterstep("1d"): ... percmax percmax(2.5)
Missing parameter step-size information results in the following error:
>>> Replace( ... name="percmax", ... parameter="percmax", ... value=5.0, ... model="hland_v1", ... ) Traceback (most recent call last): ... RuntimeError: While trying to initialise the `Replace` rule object `percmax`, the following error occurred: Rules which handle time-dependent parameters require information on the parameter timestep size. Either assign it directly or define it via option `parameterstep`.
With the following definition, the
Rule
object queries allElement
objects handlinghland_v1
instances from the globalSelections
object pub.selections:>>> rule = Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... ) >>> rule.elements Elements("land_dill", "land_lahn_1", "land_lahn_2", "land_lahn_3")
Alternatively, you can specify selections by passing themselves or their names (the latter requires them to be a member of pub.selections):
>>> rule = Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... selections=[pub.selections.headwaters, "nonheadwaters"], ... ) >>> rule.elements Elements("land_dill", "land_lahn_1", "land_lahn_2", "land_lahn_3")
Without using the model argument, you must make sure the selected elements handle the correct model instance yourself:
>>> Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... ) Traceback (most recent call last): ... RuntimeError: While trying to initialise the `Replace` rule object `fc`, the following error occurred: Model `hstream_v1` of element `stream_dill_lahn_2` does not define a control parameter named `fc`.
>>> Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hstream_v1", ... selections=[pub.selections.headwaters, "nonheadwaters"], ... ) Traceback (most recent call last): ... ValueError: While trying to initialise the `Replace` rule object `fc`, the following error occurred: Object `Selections("headwaters", "nonheadwaters")` does not handle any `hstream_v1` model instances.
-
name
: str¶ The name of the
Rule
object.Often, the name of the target parameter, but this is arbitrary.
-
elements
: hydpy.core.devicetools.Elements¶ The
Element
objects which handle the relevant targetParameter
instances.
-
parametertype
: Type[hydpy.core.parametertools.Parameter]¶ The type of the addressed
Parameter
objects.
-
property
value
¶ The calibration parameter value.
Property
value
ensures that the given value adheres to the defined lower and upper boundaries:>>> from hydpy import Replace >>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> rule = Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... lower=50.0, ... upper=200.0, ... model="hland_v1", ... )
>>> rule.value = 0.0 >>> rule.value 50.0
With option
warntrim
enabled (the default), propertyvalue
also emits a warning like the following:>>> with pub.options.warntrim(True): ... rule.value = 300.0 Traceback (most recent call last): ... UserWarning: The value of the `Replace` object `fc` must not be smaller than `50.0` or larger than `200.0`, but the given value is `300.0`. Applying the trimmed value `200.0` instead. >>> rule.value 200.0
-
abstract
apply_value
() → None[source]¶ Apply the current value on the relevant
Parameter
objects.To be overridden by the concrete subclasses.
-
reset_parameters
() → None[source]¶ Reset all relevant parameter objects to their original states.
>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import Replace >>> rule = Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... ) >>> fc = hp.elements.land_lahn_1.model.parameters.control.fc >>> fc fc(206.0) >>> fc(100.0) >>> fc fc(100.0) >>> rule.reset_parameters() >>> fc fc(206.0)
-
property
parameterstep
¶ The parameter step size relevant to the related model parameter.
For non-time-dependent parameters, property
parameterstep
is (usually)None
.
-
-
class
hydpy.auxs.calibtools.
Replace
(*, name: str, parameter: Union[Type[hydpy.core.parametertools.Parameter], hydpy.core.parametertools.Parameter, str], value: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, model: Optional[Union[module, str]] = None)[source]¶ Bases:
hydpy.auxs.calibtools.Rule
Rule
class which simply replaces the current model parameter value(s) with the current calibration parameter value.See the documentation on class
Rule
for further information.-
adaptor
: Optional[hydpy.auxs.calibtools.Adaptor] = None¶ An optional function object for customising individual calibration strategies.
See the documentation on the classes
Rule
,SumAdaptor
, andFactorAdaptor
for further information.
-
-
class
hydpy.auxs.calibtools.
Add
(*, name: str, parameter: Union[Type[hydpy.core.parametertools.Parameter], hydpy.core.parametertools.Parameter, str], value: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, model: Optional[Union[module, str]] = None)[source]¶ Bases:
hydpy.auxs.calibtools.Rule
Rule
class which adds its calibration delta to the original model parameter value(s).Please read the examples of the documentation on class
Rule
first. Here, we modify some of these examples to show the unique features of classAdd
.The first example deals with the non-time-dependent parameter
FC
. The followingAdd
object adds its current value to the original value of the parameter:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import Add >>> rule = Add( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... ) >>> rule.adaptor = lambda parameter: 2.0*rule.value >>> fc = hp.elements.land_lahn_1.model.parameters.control.fc >>> fc fc(206.0) >>> rule.apply_value() >>> fc fc(306.0)
The second example deals with the time-dependent parameter
PercMax
and shows that everything works even for situations where the actualparameterstep
(2 days) differs from the currentsimulationstep
(1 day):>>> rule = Add( ... name="percmax", ... parameter="percmax", ... value=5.0, ... model="hland_v1", ... parameterstep="2d", ... ) >>> percmax = hp.elements.land_lahn_1.model.parameters.control.percmax >>> percmax percmax(1.02978) >>> rule.apply_value() >>> percmax percmax(3.52978)
-
name
: str¶ The name of the
Rule
object.Often, the name of the target parameter, but this is arbitrary.
-
parametertype
: Type[hydpy.core.parametertools.Parameter]¶ The type of the addressed
Parameter
objects.
-
elements
: hydpy.core.devicetools.Elements¶ The
Element
objects which handle the relevant targetParameter
instances.
-
-
class
hydpy.auxs.calibtools.
Multiply
(*, name: str, parameter: Union[Type[hydpy.core.parametertools.Parameter], hydpy.core.parametertools.Parameter, str], value: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, model: Optional[Union[module, str]] = None)[source]¶ Bases:
hydpy.auxs.calibtools.Rule
Rule
class which multiplies the original model parameter value(s) by its calibration factor.Please read the examples of the documentation on class
Rule
first. Here, we modify some of these examples to show the unique features of classMultiply
.The first example deals with the non-time-dependent parameter
FC
. The followingMultiply
object multiplies the original value of the parameter by its current calibration factor:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import Add >>> rule = Multiply( ... name="fc", ... parameter="fc", ... value=2.0, ... model="hland_v1", ... ) >>> fc = hp.elements.land_lahn_1.model.parameters.control.fc >>> fc fc(206.0) >>> rule.apply_value() >>> fc fc(412.0)
The second example deals with the time-dependent parameter
PercMax
and shows that everything works even for situations where the actualparameterstep
(2 days) differs from the currentsimulationstep
(1 day):>>> rule = Multiply( ... name="percmax", ... parameter="percmax", ... value=2.0, ... model="hland_v1", ... parameterstep="2d", ... ) >>> percmax = hp.elements.land_lahn_1.model.parameters.control.percmax >>> percmax percmax(1.02978) >>> rule.apply_value() >>> percmax percmax(2.05956)
-
name
: str¶ The name of the
Rule
object.Often, the name of the target parameter, but this is arbitrary.
-
parametertype
: Type[hydpy.core.parametertools.Parameter]¶ The type of the addressed
Parameter
objects.
-
elements
: hydpy.core.devicetools.Elements¶ The
Element
objects which handle the relevant targetParameter
instances.
-
-
class
hydpy.auxs.calibtools.
CalibrationInterface
(hp: hydpy.core.hydpytools.HydPy, targetfunction: hydpy.auxs.calibtools.TargetFunction)[source]¶ Bases:
Generic
[hydpy.auxs.calibtools.RuleType1
]Interface for the coupling of HydPy to optimisation libraries like NLopt.
Essentially, class
CalibrationInterface
is supposed for the structured handling of multiple objects of the differentRule
subclasses. Hence, please read the documentation on classRule
before continuing, on which we base the following explanations.We work with the Lahn example project again:
>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2()
First, we create a
CalibrationInterface
object. Initially, it needs to know the relevantHydPy
object and the target or objective function (here, we define the target function sloppily via the lambda statement; see the documentation on the protocol classTargetFunction
for a more formal definition and further explanations):>>> from hydpy import CalibrationInterface, nse >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: sum(nse(node=node) for node in hp.nodes) ... )
Next, we use method
make_rules()
, which generates oneReplace
rule related to parameterFC
and another one related to parameterPercMax
in one step:>>> from hydpy import Replace >>> ci.make_rules( ... rule=Replace, ... names=["fc", "percmax"], ... parameters=["fc", "percmax"], ... values=[100.0, 5.0], ... lowers=[50.0, 1.0], ... uppers=[200.0, 10.0], ... parametersteps="1d", ... model="hland_v1", ... )
>>> print(ci) CalibrationInterface >>> ci Replace( name="fc", parameter="fc", lower=50.0, upper=200.0, parameterstep=None, value=100.0, model="hland_v1", selections=("complete",), ) Replace( name="percmax", parameter="percmax", lower=1.0, upper=10.0, parameterstep="1d", value=5.0, model="hland_v1", selections=("complete",), )
You can also add existing rules via method
add_rules()
. We add one for calibrating parameterDamp
of application modelhstream_v1
:>>> len(ci) 2 >>> ci.add_rules( ... Replace( ... name="damp", ... parameter="damp", ... value=0.2, ... lower=0.0, ... upper=0.5, ... selections=["complete"], ... model="hstream_v1", ... ) ... ) >>> len(ci) 3
All rules are available via attribute and keyword access:
>>> ci.fc Replace( name="fc", parameter="fc", lower=50.0, upper=200.0, parameterstep=None, value=100.0, model="hland_v1", selections=("complete",), )
>>> ci.FC Traceback (most recent call last): ... AttributeError: The actual calibration interface does neither handle a normal attribute nor a rule object named `FC`.
>>> ci["damp"] Replace( name="damp", parameter="damp", lower=0.0, upper=0.5, parameterstep=None, value=0.2, model="hstream_v1", selections=("complete",), )
>>> ci["Damp"] Traceback (most recent call last): ... KeyError: 'The actual calibration interface does not handle a rule object named `Damp`.'
The following properties return consistently sorted information on the handles
Rule
objects:>>> ci.names ('fc', 'percmax', 'damp') >>> ci.values (100.0, 5.0, 0.2) >>> ci.lowers (50.0, 1.0, 0.0) >>> ci.uppers (200.0, 10.0, 0.5)
All tuples reflect the current state of all rules:
>>> ci.damp.value = 0.3 >>> ci.values (100.0, 5.0, 0.3)
For the following examples, we perform a simulation run and assign the values of the simulated time-series to the observed series:
>>> conditions = hp.conditions >>> hp.simulate() >>> for node in hp.nodes: ... node.sequences.obs.series = node.sequences.sim.series >>> hp.conditions = conditions
As the agreement between the simulated and the “observed” time-series is perfect all four gauges, method
calculate_likelihood()
returns the highest possible sum of fournse()
values and also stores it under the attribute result:>>> from hydpy import round_ >>> round_(ci.calculate_likelihood()) 4.0 >>> round_(ci.result) 4.0
When performing a manual calibration, it might be convenient to use method
apply_values()
. To explain how it works, we first show the values of the relevant parameters of some randomly selected model instances:>>> stream = hp.elements.stream_lahn_1_lahn_2.model >>> stream.parameters.control lag(0.583) damp(0.0) >>> stream.parameters.derived nmbsegments(1) c1(0.0) c3(0.0) c2(1.0) >>> land = hp.elements.land_lahn_1.model >>> land.parameters.control.fc fc(206.0) >>> land.parameters.control.percmax percmax(1.02978)
Method
apply_values()
of classCalibrationInterface
calls the methodapply_value()
of all handledRule
objects, performs some preparations (for example, it derives the values of the secondary parameters (see parameterNmbSegments
), executes a simulation run, calls methodcalculate_likelihood()
, and returns the result:>>> result = ci.apply_values() >>> stream.parameters.control lag(0.583) damp(0.3) >>> stream.parameters.derived nmbsegments(1) c1(0.230769) c3(0.230769) c2(0.538462) >>> land.parameters.control.fc fc(100.0) >>> land.parameters.control.percmax percmax(5.0)
Due to the changes in our parameter values, our simulation is not “perfect” anymore:
>>> round_(ci.result) 1.605136
Use method
reset_parameters()
to restore the initial states of all affected parameters:>>> ci.reset_parameters() >>> stream.parameters.control lag(0.583) damp(0.0) >>> stream.parameters.derived nmbsegments(1) c1(0.0) c3(0.0) c2(1.0) >>> land = hp.elements.land_lahn_1.model >>> land.parameters.control.fc fc(206.0) >>> land.parameters.control.percmax percmax(1.02978)
Now we get the same “perfect” efficiency again:
>>> hp.simulate() >>> round_(ci.calculate_likelihood()) 4.0 >>> hp.conditions = conditions
Note the perform_simulation argument of method
apply_values()
, which allows changing the model parameter values and updating theHydPy
object only without to trigger a simulation run (and to calculate and return a new likelihood value):>>> ci.apply_values(perform_simulation=False) >>> stream.parameters.control lag(0.583) damp(0.3) >>> stream.parameters.derived nmbsegments(1) c1(0.230769) c3(0.230769) c2(0.538462) >>> land.parameters.control.fc fc(100.0) >>> land.parameters.control.percmax percmax(5.0)
Optimisers, like those implemented in NLopt, often provide their new parameter estimates via vectors. Method
perform_calibrationstep()
accepts such vectors and updates the handledRule
objects accordingly. After that, it performs the same steps as described for methodapply_values()
:>>> round_(ci.perform_calibrationstep([100.0, 5.0, 0.3])) 1.605136
>>> stream.parameters.control lag(0.583) damp(0.3) >>> stream.parameters.derived nmbsegments(1) c1(0.230769) c3(0.230769) c2(0.538462)
>>> land.parameters.control.fc fc(100.0) >>> land.parameters.control.percmax percmax(5.0)
Method
perform_calibrationstep()
writes intermediate results into a log file, if available. Prepares it beforehand via methodprepare_logfile()
:>>> with TestIO(): ... ci.prepare_logfile(logfilepath="example_calibration.log", ... objectivefunction="NSE", ... documentation="Just a doctest example.")
To continue “manually”, we now can call method
update_logfile()
to write the lastly calculated efficiency and the corresponding calibration parameter values to the log file:>>> with TestIO(): ... ci.update_logfile() ... print(open("example_calibration.log").read()) # Just a doctest example. NSE fc percmax damp parameterstep None 1d None 1.605136 100.0 5.0 0.3
For automatic calibration, one needs a calibration algorithm like the following, which simply checks the lower and upper boundaries as well as the initial values of all
Rule
objects:>>> def find_max(function, lowers, uppers, inits): ... best_result = -999.0 ... best_parameters = None ... for values in (lowers, uppers, inits): ... result = function(values) ... if result > best_result: ... best_result = result ... best_parameters = values ... return best_parameters
Now we can assign method
perform_calibrationstep()
to this oversimplified optimiser, which then returns the best examined calibration parameter values:>>> with TestIO(): ... find_max(function=ci.perform_calibrationstep, ... lowers=ci.lowers, ... uppers=ci.uppers, ... inits=ci.values) (200.0, 10.0, 0.5)
The log file now contains one line for our old result and three lines for the results of our optimiser:
>>> with TestIO(): ... print(open("example_calibration.log").read()) # Just a doctest example. NSE fc percmax damp parameterstep None 1d None 1.605136 100.0 5.0 0.3 -0.710211 50.0 1.0 0.0 2.313934 200.0 10.0 0.5 1.605136 100.0 5.0 0.3
Class
CalibrationInterface
also provides methodread_logfile()
, which automatically selects the best calibration result. Therefore, it needs to know that the highest result is the best, which we indicate by setting argument maximisation toTrue
:>>> with TestIO(): ... ci.read_logfile( ... logfilepath="example_calibration.log", ... maximisation=True, ... ) >>> ci.fc.value 200.0 >>> ci.percmax.value 10.0 >>> ci.damp.value 0.5 >>> round_(ci.result) 2.313934 >>> round_(ci.apply_values()) 2.313934
On the contrary, if we set argument maximisation to
False
, methodread_logfile()
returns the worst result in our example:>>> with TestIO(): ... ci.read_logfile( ... logfilepath="example_calibration.log", ... maximisation=False, ... ) >>> ci.fc.value 50.0 >>> ci.percmax.value 1.0 >>> ci.damp.value 0.0 >>> round_(ci.result) -0.710211 >>> round_(ci.apply_values()) -0.710211
To prevent errors due to different parameter step-sizes, method
read_logfile()
raises the following error whenever it detects inconsistencies:>>> ci.percmax.parameterstep = "2d" >>> with TestIO(): ... ci.read_logfile( ... logfilepath="example_calibration.log", ... maximisation=True, ... ) Traceback (most recent call last): ... RuntimeError: The current parameterstep of the `Replace` rule `percmax` (`2d`) does not agree with the one documentated in log file `example_calibration.log` (`1d`).
Method
read_logfile()
reports inconsistent rule names as follows:>>> ci.remove_rules(ci.percmax) >>> with TestIO(): ... ci.read_logfile( ... logfilepath="example_calibration.log", ... maximisation=True, ... ) Traceback (most recent call last): ... RuntimeError: The names of the rules handled by the actual calibration interface (damp and fc) do not agree with the names in the header of logfile `example_calibration.log` (damp, fc, and percmax).
The last consistency check is optional. Set argument check to
False
to force methodread_logfile()
to query all available data instead of raising an error:>>> ci.add_rules( ... Replace( ... name="beta", ... parameter="beta", ... value=2.0, ... lower=1.0, ... upper=4.0, ... selections=["complete"], ... model="hland_v1", ... ) ... ) >>> ci.fc.value = 0.0 >>> ci.damp.value = 0.0 >>> with TestIO(): ... ci.read_logfile( ... logfilepath="example_calibration.log", ... maximisation=True, ... check=False, ... ) >>> ci.beta.value 2.0 >>> ci.fc.value 200.0 >>> ci.damp.value 0.5
-
conditions
: Dict[str, Dict[str, Dict[str, Union[float, hydpy.core.typingtools.Vector[float], hydpy.core.typingtools.Matrix[float]]]]]¶ The
conditions
of the givenHydPy
object.CalibrationInterface
queries the conditions during its initialisation and uses them later to reset all relevant conditions before each new simulation run.
-
add_rules
(*rules: RuleType1) → None[source]¶ Add some
Rule
objects to the actualCalibrationInterface
object.>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import CalibrationInterface >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: None, ... ) >>> from hydpy import Replace >>> ci.add_rules( ... Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... ), ... Replace( ... name="percmax", ... parameter="percmax", ... value=5.0, ... model="hland_v1", ... ), ... )
Note that method
add_rules()
might change the number ofElement
objects relevant for theCalibrationInterface
object:>>> damp = Replace( ... name="damp", ... parameter="damp", ... value=0.2, ... model="hstream_v1", ... )
>>> len(ci._elements) 4 >>> ci.add_rules(damp) >>> len(ci._elements) 7
-
get_rule
(name: str, type_: Optional[Type[RuleType2]] = None) → hydpy.auxs.calibtools.Rule[source]¶ Return a
Rule
object (of a specific type).Method
get_rule()
is a more typesafe alternative to simple keyword access. Besides the name of the requiredRule
object, pass its subclass, to convince your IDE (and yourself) that the returned rule follows this more specific type:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import Add, CalibrationInterface, nse, Replace >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: sum(nse(node=node) for node in hp.nodes) ... ) >>> ci.make_rules( ... rule=Replace, ... names=["fc", "percmax"], ... parameters=["fc", "percmax"], ... values=[100.0, 5.0], ... lowers=[50.0, 1.0], ... uppers=[200.0, 10.0], ... parametersteps="1d", ... model="hland_v1", ... )
>>> ci.get_rule("fc", Replace).name 'fc'
>>> ci.get_rule("Fc", Replace).name Traceback (most recent call last): ... RuntimeError: The actual calibration interface does not handle a rule object named `Fc`.
>>> ci.get_rule("fc", Replace).name 'fc'
>>> ci.get_rule("fc", Add).name Traceback (most recent call last): ... RuntimeError: The actual calibration interface does not handle a rule object named `fc` of type `Add`.
-
remove_rules
(*rules: Union[str, RuleType1]) → None[source]¶ Remove some
Rule
objects from the actualCalibrationInterface
object.>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import CalibrationInterface >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: None, ... ) >>> from hydpy import Replace >>> ci.add_rules( ... Replace( ... name="fc", ... parameter="fc", ... value=100.0, ... model="hland_v1", ... ), ... Replace( ... name="percmax", ... parameter="percmax", ... value=5.0, ... model="hland_v1", ... ), ... Replace( ... name="damp", ... parameter="damp", ... value=0.2, ... model="hstream_v1", ... ) ... )
You can remove each rule either by passing itself or its name (note that method
remove_rules()
might change the number ofElement
objects relevant for theCalibrationInterface
object):>>> len(ci._elements) 7 >>> fc = ci.fc >>> fc in ci True >>> "damp" in ci True >>> ci.remove_rules(fc, "damp") >>> fc in ci False >>> "damp" in ci False >>> len(ci._elements) 4
Trying to remove a non-existing rule results in the following error:
>>> ci.remove_rules("fc") Traceback (most recent call last): ... RuntimeError: The actual calibration interface object does not handle a rule object named `fc`.
-
make_rules
(*, rule: Type[RuleType1], calibspecs: Optional[CalibSpecs] = None, names: Optional[Sequence[str]] = None, parameters: Optional[Sequence[Union[hydpy.core.parametertools.Parameter, str]]] = None, values: Optional[Sequence[float]] = None, lowers: Optional[Sequence[float]] = None, uppers: Optional[Sequence[float]] = None, parametersteps: Union[Period, datetime.timedelta, str, None, Sequence[Optional[Union[Period, datetime.timedelta, str]]]] = None, model: Optional[Union[module, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, product: bool = False) → None[source]¶ Create and store new
Rule
objects.Please see the main documentation on class
CalibrationInterface
first, from which we borrow the general setup:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2()
>>> from hydpy import CalibrationInterface, nse >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: sum(nse(node=node) for node in hp.nodes) ... )
Here, we show only the supplemental features of method
make_rules()
in some brevity.Method
make_rules()
checks that all given sequences have the same length:>>> from hydpy import Replace >>> ci.make_rules( ... rule=Replace, ... names=["fc", "percmax"], ... parameters=["fc", "percmax"], ... values=[100.0, 5.0], ... lowers=[50.0, 1.0], ... uppers=[200.0], ... parametersteps="1d", ... model="hland_v1", ... ) Traceback (most recent call last): ... ValueError: When creating rules via method `make_rules`, all given sequences must be of equal length.
The separate handling of the specifications of all calibration parameters is error-prone. For more safety and convenience, you can bundle all specifications within a
CalibSpecs
object instead and pass them at once:>>> from hydpy import CalibSpec, CalibSpecs >>> calibspecs = CalibSpecs( ... CalibSpec(name="fc", default=100.0, lower=50.0, upper=200.0), ... CalibSpec( ... name="percmax", default=5.0, lower=1.0, upper=10.0, parameterstep="1d" ... ), ... )
>>> ci.make_rules( ... rule=Replace, ... calibspecs=calibspecs, ... parametersteps="1d", ... model="hland_v1", ... ) >>> ci["percmax"] Replace( name="percmax", parameter="percmax", lower=1.0, upper=10.0, parameterstep="1d", value=5.0, model="hland_v1", selections=("complete",), ) >>> ci.remove_rules("fc", "percmax")
You are free also to use the individual arguments (e.g. names) to override the related specifications defined by the
CalibSpecs
object:>>> ci.make_rules( ... rule=Replace, ... calibspecs=calibspecs, ... names=[name.upper() for name in calibspecs.names], ... parametersteps="1d", ... model="hland_v1", ... ) >>> ci["PERCMAX"] Replace( name="PERCMAX", parameter="percmax", lower=1.0, upper=10.0, parameterstep="1d", value=5.0, model="hland_v1", selections=("complete",), ) >>> ci.remove_rules("FC", "PERCMAX")
Method
make_rules()
raises the following error if you neither pass aCalibSpecs
object nor the complete list of individual calibration parameter specifications:>>> ci.make_rules( ... rule=Replace, ... names=["fc", "percmax"], ... parameters=["fc", "percmax"], ... values=[100.0, 5.0], ... lowers=[50.0, 1.0], ... parametersteps="1d", ... model="hland_v1", ... ) Traceback (most recent call last): ... TypeError: When creating rules via method `make_rules`, you must pass a `CalibSpecs` object or provide complete information for the following arguments: names, parameters, values, lowers, and uppers.
You can run method
make_rules()
in “product mode”, meaning that its execution results in distinctRule
objects for all combinations of the given calibration parameters and selections:>>> ci.make_rules( ... rule=Replace, ... calibspecs=calibspecs, ... model="hland_v1", ... selections=("headwaters", "nonheadwaters"), ... product=True, ... ) >>> tuple(par.__name__ for par in ci.parametertypes) ('FC', 'PercMax') >>> ci.selections ('headwaters', 'nonheadwaters') >>> ci["percmax_headwaters"] Replace( name="percmax_headwaters", parameter="percmax", lower=1.0, upper=10.0, parameterstep="1d", value=5.0, model="hland_v1", selections=("headwaters",), ) >>> ci["percmax_nonheadwaters"].selections ('nonheadwaters',) >>> ci["fc_headwaters"].selections ('headwaters',) >>> ci["fc_nonheadwaters"].selections ('nonheadwaters',)
Trying to run in “product mode” without defining the target selections results in the following error message:
>>> ci.make_rules( ... rule=Replace, ... calibspecs=calibspecs, ... parametersteps="1d", ... model="hland_v1", ... product=True, ... ) Traceback (most recent call last): ... TypeError: When creating rules via method `make_rules` in "product mode" (with the argument `product` being `True`), you must supply all target selection objects via argument `selections`.
-
prepare_logfile
(logfilepath: str, objectivefunction: str = 'result', documentation: Optional[str] = None) → None[source]¶ Prepare a log file.
Use argument objectivefunction to describe the
TargetFunction
used for calculating the efficiency and argument documentation to add some information to the header of the logfile.See the main documentation on class
CalibrationInterface
for further information.
-
update_logfile
() → None[source]¶ Update the current log file, if available.
See the main documentation on class
CalibrationInterface
for further information.
-
read_logfile
(logfilepath: str, maximisation: bool, check: bool = True) → None[source]¶ Read the log file with the given file path.
See the main documentation on class
CalibrationInterface
for further information.
-
property
names
¶ The names of all handled
Rule
objects.See the main documentation on class
CalibrationInterface
for further information.
-
property
values
¶ The values of all handled
Rule
objects.See the main documentation on class
CalibrationInterface
for further information.
-
property
lowers
¶ The lower boundaries of all handled
Rule
objects.See the main documentation on class
CalibrationInterface
for further information.
-
property
uppers
¶ The upper boundaries of all handled
Rule
objects.See the main documentation on class
CalibrationInterface
for further information.
-
property
selections
¶ The names of all
Selection
objects addressed at least one of the handledRule
objects.See the documentation on method
make_rules()
for further information.
-
property
parametertypes
¶ The types of all
Parameter
objects addressed by at least one of the handledRule
objects.See the documentation on method
make_rules()
for further information.
-
apply_values
(perform_simulation: bool = True) → Optional[float][source]¶ Apply all current calibration parameter values on all relevant parameters.
Set argument perform_simulation to
False
to only change the actual parameter values and update theHydPy
object without performing a simulation run.See the main documentation on class
CalibrationInterface
for further information.
-
reset_parameters
() → None[source]¶ Reset all relevant parameters to their original states.
See the main documentation on class
CalibrationInterface
for further information.
-
calculate_likelihood
() → float[source]¶ Apply the defined
TargetFunction
and return the result.See the main documentation on class
CalibrationInterface
for further information.
-
perform_calibrationstep
(values: Iterable[float], *args: Any, **kwargs: Any) → float[source]¶ Update all calibration parameters with the given values, update the
HydPy
object, perform a simulation run, and calculate and return the achieved efficiency.See the main documentation on class
CalibrationInterface
for further information.
-
print_table
(parametertypes: Optional[Sequence[Type[hydpy.core.parametertools.Parameter]]] = None, selections: Optional[Sequence[str]] = None, bounds: Optional[Tuple[str, str]] = ('lower', 'upper'), fillvalue: str = '/', sep: str = '\t', file_: Optional[TextIO] = None) → None[source]¶ Print the current calibration parameter values in a table format.
Please see the documentation on method
make_rules()
first, from which we borrow the general setup:>>> from hydpy.examples import prepare_full_example_2 >>> hp, pub, TestIO = prepare_full_example_2() >>> from hydpy import CalibSpec, CalibSpecs, CalibrationInterface, nse >>> ci = CalibrationInterface( ... hp=hp, ... targetfunction=lambda: sum(nse(node=node) for node in hp.nodes) ... ) >>> calibspecs = CalibSpecs( ... CalibSpec(name="fc", default=100.0, lower=50.0, upper=200.0), ... CalibSpec( ... name="percmax", default=5.0, lower=1.0, upper=10.0, parameterstep="1d" ... ), ... ) >>> ci.make_rules( ... rule=Replace, ... calibspecs=calibspecs, ... model="hland_v1", ... selections=("headwaters", "nonheadwaters"), ... product=True, ... )
First, we change the values of two
Rule
objects to clarify that all values appear in the correct table cells:>>> ci["fc_headwaters"].value = 200.0 >>> ci["percmax_nonheadwaters"].value = 10.0
By default, method
print_table()
prints the values of all handledRule
objects. We varies the target control parameters on the first axis and the target selections on the second axis. Row two and three contain the (identical) lower and upper boundary values corresponding to the respective control parameters:>>> ci.print_table() lower upper headwaters nonheadwaters FC 50.0 200.0 200.0 100.0 PercMax 1.0 10.0 5.0 10.0
For non-identical boundary values, method
print_table()
prints fill values in the relevant cells. Besides this, the following example shows how to define alternative titles for the boundary value columns:>>> ci["fc_headwaters"].lower = 60.0 >>> ci["percmax_nonheadwaters"].upper = 20.0 >>> ci.print_table(bounds=("min", "max")) min max headwaters nonheadwaters FC / 200.0 200.0 100.0 PercMax 1.0 / 5.0 10.0
Pass
None
to argument bounds to omit writing any boundary value column:>>> ci.print_table(bounds=None) headwaters nonheadwaters FC 200.0 100.0 PercMax 5.0 10.0
The next example shows how to change the tabulated target parameters and selections. Method
print_table()
uses the (given alternative) fill value for each parameter-selection-combination not met by any of the availableRule
objects:>>> from hydpy.models.hland.hland_control import CFlux, PercMax >>> ci.print_table( ... parametertypes=(PercMax, CFlux), ... selections=("streams", "headwaters"), ... bounds=None, ... fillvalue="-") streams headwaters PercMax - 5.0 CFlux - -
Note that the value of the same calibration parameter might appear multiple times when targeting multiple
Selection
objects:>>> ci["fc_headwaters"].selections = ("headwaters", "streams") >>> ci.print_table(bounds=None) headwaters nonheadwaters streams FC 200.0 100.0 200.0 PercMax 5.0 10.0 /
-
-
class
hydpy.auxs.calibtools.
ReplaceIUH
(*, name: str, parameter: Union[Type[hydpy.core.parametertools.Parameter], hydpy.core.parametertools.Parameter, str], value: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None, selections: Optional[Iterable[Union[hydpy.core.selectiontools.Selection, str]]] = None, model: Optional[Union[module, str]] = None)[source]¶ Bases:
hydpy.auxs.calibtools.Rule
A
Rule
class specialised forIUH
parameters.Usually, it is not a good idea to calibrate the AR and MA coefficients of parameters like
Responses
of modelarma_v1
individually. Instead, we need to calibrate the few coefficients of the underlyingIUH
objects, which calculate the ARMA coefficients. ClassReplaceIUH
helps to accomplish this task.Note
Class
ReplaceIUH
is still under development. For example, it does not address the possibility of different ARMA coefficients related to different discharge thresholds. Hence, the usage of classReplaceIUH
might change in the future.So far, there is no example project containing
arma_v1
models instances. Therefore, we generate a simple one consisting of twoElement
objects only:>>> from hydpy import Element, prepare_model, Selection >>> element1 = Element("element1", inlets="in1", outlets="out1") >>> element2 = Element("element2", inlets="in2", outlets="out2") >>> complete = Selection("complete", elements=[element1, element2]) >>> element1.model = prepare_model("arma_v1") >>> element2.model = prepare_model("arma_v1")
We focus on class
TranslationDiffusionEquation
in the following. We create two separate instances and use to calculate the response coefficients of botharma_v1
instances:>>> from hydpy import TranslationDiffusionEquation >>> tde1 = TranslationDiffusionEquation(u=5.0, d=15.0, x=1.0) >>> tde2 = TranslationDiffusionEquation(u=5.0, d=15.0, x=2.0) >>> element1.model.parameters.control.responses(tde1.arma.coefs) >>> element1.model.parameters.control.responses responses(th_0_0=((0.906536, -0.197555, 0.002128, 0.000276), (0.842788, -0.631499, 0.061685, 0.015639, 0.0, 0.0, 0.0, -0.000001, 0.0, 0.0, 0.0, 0.0))) >>> element2.model.parameters.control.responses(tde2.arma.coefs) >>> element2.model.parameters.control.responses responses(th_0_0=((1.298097, -0.536702, 0.072903, -0.001207, -0.00004), (0.699212, -0.663835, 0.093935, 0.046177, -0.00854)))
Next, we define one
ReplaceIUH
for modifying parameteru
and another one for changingd
:>>> from hydpy import ReplaceIUH >>> u = ReplaceIUH( ... name="u", ... parameter="responses", ... value=5.0, ... lower=1.0, ... upper=10.0, ... selections=[complete], ... ) >>> d = ReplaceIUH( ... name="d", ... parameter="responses", ... value=15.0, ... lower=5.0, ... upper=50.0, ... selections=[complete], ... )
We add and thereby connect the
Element
andTranslationDiffusionEquation
objects to bothReplaceIUH
objects via methodadd_iuhs()
:>>> u.add_iuhs(element1=tde1, element2=tde2) >>> d.add_iuhs(element1=tde1, element2=tde2)
Note that method
add_iuhs()
enforces to add allIUH
objects at ones to avoid inconsistencies that might be hard to track later:>>> d.add_iuhs(element1=tde1) Traceback (most recent call last): ... RuntimeError: While trying to add `IUH` objects to the `ReplaceIUH` rule `d`, the following error occurred: The given elements (element1) do not agree with the complete set of relevant elements (element1 and element2).
By default, each
ReplaceIUH
objects triggers the calculation of the ARMA coefficients during the execution of its methodapply_value()
, which can be a waste of computation time if we want to calibrate multipleIUH
coefficients. To save computation time in such cases, set optionupdate_parameters
toFalse
for all except the lastly executedReplaceIUH
objects:>>> u.update_parameters = False
Now, changing the value of rule u and calling method
apply_value()
does not affect the coefficients of bothResponses
parameters:>>> u.value = 10.0 >>> u.apply_value() >>> tde1 TranslationDiffusionEquation(d=15.0, u=10.0, x=1.0) >>> element1.model.parameters.control.responses responses(th_0_0=((0.906536, -0.197555, 0.002128, 0.000276), (0.842788, -0.631499, 0.061685, 0.015639, 0.0, 0.0, 0.0, -0.000001, 0.0, 0.0, 0.0, 0.0))) >>> tde2 TranslationDiffusionEquation(d=15.0, u=10.0, x=2.0) >>> element2.model.parameters.control.responses responses(th_0_0=((1.298097, -0.536702, 0.072903, -0.001207, -0.00004), (0.699212, -0.663835, 0.093935, 0.046177, -0.00854)))
On the other side, calling method
apply_value()
of rule d does activate the freshly set value of rule d and the previously set value of rule u, as well:>>> d.value = 50.0 >>> d.apply_value() >>> tde1 TranslationDiffusionEquation(d=50.0, u=10.0, x=1.0) >>> element1.model.parameters.control.responses responses(th_0_0=((0.811473, -0.15234, -0.000256, 0.000177), (0.916619, -0.670781, 0.087185, 0.007923))) >>> tde2 TranslationDiffusionEquation(d=50.0, u=10.0, x=2.0) >>> element2.model.parameters.control.responses responses(th_0_0=((0.832237, -0.167205, 0.002007, 0.000184), (0.836513, -0.555399, 0.037628, 0.014035)))
Use method
reset_parameters()
to restore the original ARMA coefficients:>>> d.reset_parameters() >>> element1.model.parameters.control.responses responses(th_0_0=((0.906536, -0.197555, 0.002128, 0.000276), (0.842788, -0.631499, 0.061685, 0.015639, 0.0, 0.0, 0.0, -0.000001, 0.0, 0.0, 0.0, 0.0))) >>> element2.model.parameters.control.responses responses(th_0_0=((1.298097, -0.536702, 0.072903, -0.001207, -0.00004), (0.699212, -0.663835, 0.093935, 0.046177, -0.00854)))
-
update_parameters
: bool = True¶ Flag indicating whether method
apply_value()
should calculate thecoefs
and pass them to the relevant model parameter or not.Set this flag to
False
for the firstReplaceIUH
object when another one handles the same elements and is applied afterwards.
-
add_iuhs
(**iuhs: hydpy.auxs.iuhtools.IUH) → None[source]¶ Add one
IUH
object for each relevantElement
objects.See the main documentation on class
ReplaceIUH
for further information.
-
apply_value
() → None[source]¶ Apply all current calibration parameter values on all relevant
IUH
objects and eventually update the ARMA coefficients of the related parameter.See the main documentation on class
CalibrationInterface
for further information.
-
reset_parameters
() → None[source]¶ Reset all relevant parameter objects to their original states.
See the main documentation on class
ReplaceIUH
for further information.
-
-
class
hydpy.auxs.calibtools.
CalibSpec
(*, name: str, default: float, lower: float = - inf, upper: float = inf, parameterstep: Optional[Union[Period, datetime.timedelta, str]] = None)[source]¶ Bases:
object
Helper class for specifying the properties of a single calibration parameter.
So far, class
CalibSpec
does not provide much functionality, besides checking upon initialisation that the given default and boundary values are consistent:>>> from hydpy import CalibSpec >>> CalibSpec( ... name="par1", ... default=1.0, ... ) CalibSpec(name="par1", default=1.0)
>>> CalibSpec( ... name="par1", ... default=1.0, ... lower=2.0, ... ) Traceback (most recent call last): ... ValueError: The following values given for calibration parameter `par1` are not consistent: default=1.0, lower=2.0, upper=inf.
>>> CalibSpec( ... name="par1", ... default=1.0, ... upper=0.5, ... ) Traceback (most recent call last): ... ValueError: The following values given for calibration parameter `par1` are not consistent: default=1.0, lower=-inf, upper=0.5.
>>> CalibSpec( ... name="par1", ... default=1.0, ... lower=0.0, ... upper=2.0, ... ) CalibSpec(name="par1", default=1.0, lower=0.0, upper=2.0)
Use the parameterstep argument for time-dependent calibration parameters:
>>> CalibSpec( ... name="par1", ... default=1.0/3.0, ... lower=1.0/3.0, ... upper=1.0/3.0, ... parameterstep="1d", ... ) CalibSpec( name="par1", default=0.333333, lower=0.333333, upper=0.333333, parameterstep="1d" )
See the documentation on class
CalibSpecs
for further information.-
parameterstep
: Optional[hydpy.core.timetools.Period]¶ The parameter step size to be set before applying the defined calibration parameter values.
-
-
class
hydpy.auxs.calibtools.
CalibSpecs
(*parspecs: hydpy.auxs.calibtools.CalibSpec)[source]¶ Bases:
object
Collection class for handling
CalibSpec
objects.The primary purpose of class
CalibSpecs
is to handle multipleCalibSpec
objects and to make all their attributes accessible in the same order. See propertynames
as one example. Note that all such properties are sorted in the order or the attachment of the differentCalibSpec
objects:>>> from hydpy import CalibSpec, CalibSpecs >>> calibspecs = CalibSpecs( ... CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ), ... CalibSpec(name="second", default=1.0, lower=0.0), ... CalibSpec(name="first",default=2.0, upper=2.0), ... ) >>> calibspecs CalibSpecs( CalibSpec(name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d"), CalibSpec(name="second", default=1.0, lower=0.0), CalibSpec(name="first", default=2.0, upper=2.0), )
You can query and remove
CalibSpec
objects via keyword and attribute access:>>> print(calibspecs) CalibSpecs("third", "second", "first")
>>> third = calibspecs["third"] >>> third in calibspecs True >>> del calibspecs["third"] >>> third in calibspecs False >>> calibspecs["third"] Traceback (most recent call last): ... KeyError: 'The current `CalibSpecs` object does not handle a `CalibSpec` object named `third`.' >>> del calibspecs["third"] Traceback (most recent call last): ... KeyError: 'The current `CalibSpecs` object does not handle a `CalibSpec` object named `third`.'
>>> second = calibspecs.second >>> "second" in calibspecs True >>> del calibspecs.second >>> "second" in calibspecs False >>> calibspecs.second Traceback (most recent call last): ... AttributeError: The current `CalibSpecs` object does neither handle a `CalibSpec` object nor a normal attribute named `second`. >>> del calibspecs.second Traceback (most recent call last): ... AttributeError: The current `CalibSpecs` object does not handle a `CalibSpec` object named `second`.
>>> len(calibspecs) 1
Now we can re-append the previously removed
CalibSpec
objects (and thereby bring the order of attachment in agreement with theCalibSpec
names):>>> calibspecs.append(second, third) >>> for calibspec in calibspecs: ... print(calibspec) first second third
-
append
(*calibspecs: hydpy.auxs.calibtools.CalibSpec) → None[source]¶ Append one or more
CalibSpec
objects.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> first = CalibSpec(name="first", default=1.0, lower=0.0) >>> second = CalibSpec(name="second",default=2.0, upper=2.0) >>> calibspecs = CalibSpecs() >>> calibspecs.append(first) >>> calibspecs.append(second, third) >>> calibspecs CalibSpecs( CalibSpec(name="first", default=1.0, lower=0.0), CalibSpec(name="second", default=2.0, upper=2.0), CalibSpec(name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d"), )
-
property
names
¶ The names of all
CalibSpec
objects in the order of attachment.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> calibspecs = CalibSpecs(CalibSpec(name="first", default=1.0, lower=0.0), ... CalibSpec(name="second",default=2.0, upper=2.0)) >>> calibspecs.append(third) >>> calibspecs.names ('first', 'second', 'third')
-
property
defaults
¶ The default values of all
CalibSpec
objects in the order of attachment.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> calibspecs = CalibSpecs(CalibSpec(name="first", default=1.0, lower=0.0), ... CalibSpec(name="second",default=2.0, upper=2.0)) >>> calibspecs.append(third) >>> calibspecs.defaults (1.0, 2.0, 3.0)
-
property
lowers
¶ The lower boundary values of all
CalibSpec
objects in the order of attachment.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> calibspecs = CalibSpecs(CalibSpec(name="first", default=1.0, lower=0.0), ... CalibSpec(name="second",default=2.0, upper=2.0)) >>> calibspecs.append(third) >>> calibspecs.lowers (0.0, -inf, -10.0)
-
property
uppers
¶ The upper boundary values of all
CalibSpec
objects in the order of attachment.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> calibspecs = CalibSpecs(CalibSpec(name="first", default=1.0, lower=0.0), ... CalibSpec(name="second",default=2.0, upper=2.0)) >>> calibspecs.append(third) >>> calibspecs.uppers (inf, 2.0, 10.0)
-
property
parametersteps
¶ The parameter steps of all
CalibSpec
objects in the order of attachment.>>> from hydpy import CalibSpec, CalibSpecs >>> third = CalibSpec( ... name="third", default=3.0, lower=-10.0, upper=10.0, parameterstep="1d" ... ) >>> calibspecs = CalibSpecs(CalibSpec(name="first", default=1.0, lower=0.0), ... CalibSpec(name="second",default=2.0, upper=2.0)) >>> calibspecs.append(third) >>> calibspecs.parametersteps (None, None, Period("1d"))
-