importtools

This module implements features related to importing models.

The implemented tools are primarily designed for hiding model initialisation routines from model users and for allowing writing readable doctests.

Module importtools implements the following members:

  • TD Type variable.

  • TI_contra Type variable.

  • TM_contra Type variable.

  • PrepSub0D Specification for defining custom “add_submodel” methods to be wrapped by function prepare_submodel() when dealing with scalar submodel references.

  • PrepSub1D Specification for model-specific “add_submodel” methods to be wrapped by function prepare_submodel() when dealing with vectorial submodel references.

  • parameterstep() Define a parameter time step size within a parameter control file.

  • prepare_parameters() Prepare a Parameters object based on the given dictionary information and return it.

  • prepare_sequences() Prepare a Sequences object based on the given dictionary information and return it.

  • reverse_model_wildcard_import() Clear the local namespace from a model wildcard import.

  • load_modelmodule() Load the given model module (if necessary) and return it.

  • prepare_model() Prepare and return the model of the given module.

  • prepare_submodel() Wrap a model-specific method for preparing a submodel into a SubmodelAdder instance.

  • SubmodelAdder Wrapper that extends the functionality of model-specific methods for adding submodels to main models.

  • define_targetparameter() Wrap a submodel-specific method that allows the main model to set the value of a single control parameter of the submodel into a TargetParameterUpdater instance.

  • TargetParameterUpdater Wrapper that extends the functionality of a submodel-specific method that allows the main model to set the value of a single control parameter of the submodel.

  • simulationstep() Define a simulation time step size within a parameter control file for testing purposes.

  • controlcheck() Define the corresponding control file within a condition file.


class hydpy.core.importtools.PrepSub0D(*args, **kwargs)[source]

Bases: Protocol[TM_contra, TI_contra]

Specification for defining custom “add_submodel” methods to be wrapped by function prepare_submodel() when dealing with scalar submodel references.

class hydpy.core.importtools.PrepSub1D(*args, **kwargs)[source]

Bases: Protocol[TM_contra, TI_contra]

Specification for model-specific “add_submodel” methods to be wrapped by function prepare_submodel() when dealing with vectorial submodel references.

hydpy.core.importtools.parameterstep(timestep: timetools.PeriodConstrArg | None = None) None[source]

Define a parameter time step size within a parameter control file.

Function parameterstep() should usually be applied in a line immediately behind the model import or behind calling simulationstep(). Defining the step size of time-dependent parameters is a prerequisite to accessing any model-specific parameter.

Note that parameterstep() implements some namespace magic utilising the module inspect, which complicates things for framework developers. Still, it eases the definition of parameter control files for framework users.

hydpy.core.importtools.prepare_parameters(dict_: dict[str, Any]) Parameters[source]

Prepare a Parameters object based on the given dictionary information and return it.

hydpy.core.importtools.prepare_sequences(dict_: dict[str, Any]) Sequences[source]

Prepare a Sequences object based on the given dictionary information and return it.

hydpy.core.importtools.reverse_model_wildcard_import() None[source]

Clear the local namespace from a model wildcard import.

This method should remove the critical imports into the local namespace due to the last wildcard import of a particular application model. In this manner, it secures the repeated preparation of different types of models via wildcard imports. See the following example of to apply it.

>>> from hydpy import reverse_model_wildcard_import

Assume you import lland_dd:

>>> from hydpy.models.lland_dd import *

This import adds, for example, the collection class for handling control parameters of lland_dd into the local namespace:

>>> print(ControlParameters(None).name)
control

Function parameterstep() prepares, for example, the control parameter object of class NHRU:

>>> parameterstep("1d")
>>> nhru
nhru(?)

Function reverse_model_wildcard_import() tries to clear the local namespace (even from unexpected classes like the one we define now):

>>> class Test:
...     __module__ = "hydpy.models.lland_dd"
>>> test = Test()
>>> reverse_model_wildcard_import()
>>> ControlParameters
Traceback (most recent call last):
...
NameError: name 'ControlParameters' is not defined
>>> nhru
Traceback (most recent call last):
...
NameError: name 'nhru' is not defined
>>> Test
Traceback (most recent call last):
...
NameError: name 'Test' is not defined
>>> test
Traceback (most recent call last):
...
NameError: name 'test' is not defined
hydpy.core.importtools.load_modelmodule(module: ModuleType | str, /) ModuleType[source]

Load the given model module (if necessary) and return it.

>>> from hydpy.core.importtools import load_modelmodule
>>> from hydpy.models import evap_aet_hbv96
>>> assert evap_aet_hbv96 is load_modelmodule(evap_aet_hbv96)
>>> assert evap_aet_hbv96 is load_modelmodule("evap_aet_hbv96")
hydpy.core.importtools.prepare_model(module: ModuleType | str) Model[source]

Prepare and return the model of the given module.

In usual HydPy projects, each control file only prepares an individual model instance, which allows for “polluting” the namespace with different model attributes. There is no danger of name conflicts as long as we do not perform other (wildcard) imports.

However, there are situations where we need to load different models into the same namespace. Then it is advisable to use function prepare_model(), which returns a reference to the model and nothing else.

See the documentation of dam_v001 on how to apply function prepare_model() properly.

hydpy.core.importtools.prepare_submodel(submodelname: str, submodelinterface: type[TI_contra], *methods: Callable[[NoReturn, NoReturn], None], dimensionality: TD = 0, landtype_constants: Constants | None = None, soiltype_constants: Constants | None = None, landtype_refindices: type[NameParameter] | None = None, soiltype_refindices: type[NameParameter] | None = None, refweights: type[Parameter] | None = None) Callable[[PrepSub0D[TM_contra, TI_contra] | PrepSub1D[TM_contra, TI_contra]], SubmodelAdder[TD, TM_contra, TI_contra]][source]

Wrap a model-specific method for preparing a submodel into a SubmodelAdder instance.

class hydpy.core.importtools.SubmodelAdder(*, wrapped: PrepSub0D[TM_contra, TI_contra] | PrepSub1D[TM_contra, TI_contra], submodelname: str, submodelinterface: type[TI_contra], methods: Iterable[Callable[[NoReturn, NoReturn], None]], dimensionality: TD, landtype_constants: Constants | None, soiltype_constants: Constants | None, landtype_refindices: type[NameParameter] | None, soiltype_refindices: type[NameParameter] | None, refweights: type[Parameter] | None)[source]

Bases: _DoctestAdder, Generic[TD, TM_contra, TI_contra]

Wrapper that extends the functionality of model-specific methods for adding submodels to main models.

SubmodelAdder offers the user-relevant feature of preparing submodels with the with statement. When entering the with block, SubmodelAdder uses the given string or module to initialise the desired application model and hands it as a submodel to the model-specific method, which usually sets some control parameters based on the main model’s configuration. Next, SubmodelAdder makes many attributes of the submodel directly available, most importantly, the instances of the remaining control parameter, so that users can set their values as conveniently as the ones of the main model. As long as no name conflicts occur, all main model parameter instances are also accessible:

>>> from hydpy.models.lland_knauf import *
>>> parameterstep()
>>> nhru(2)
>>> ft(10.0)
>>> fhru(0.2, 0.8)
>>> lnk(ACKER, MISCHW)
>>> measuringheightwindspeed(10.0)
>>> lai(3.0)
>>> wmax(acker=100.0, mischw=200.0)
>>> with model.add_aetmodel_v1("evap_aet_hbv96"):
...     nhru
...     nmbhru
...     maxsoilwater
...     soilmoisturelimit(mischw=1.0, acker=0.5)
nhru(2)
nmbhru(2)
maxsoilwater(acker=100.0, mischw=200.0)
>>> model.aetmodel.parameters.control.soilmoisturelimit
soilmoisturelimit(acker=0.5, mischw=1.0)

After leaving the with block, the submodel’s parameters are no longer available:

>>> nhru
nhru(2)
>>> nmbhru
Traceback (most recent call last):
...
NameError: name 'nmbhru' is not defined

By calling the submodel interface method add_mainmodel_as_subsubmodel() when entering the with block, SubmodelAdder enables each submodel to accept the nearest main model as a sub-submodel:

>>> model.aetmodel.tempmodel is model
True

Additionally, SubmodelAdder checks if the selected application model follows the appropriate interface:

>>> with model.add_aetmodel_v1("ga_garto_submodel1"):
...     ...
Traceback (most recent call last):
...
TypeError: While trying to add a submodel to the main model `lland_knauf`, the following error occurred: Submodel `ga_garto_submodel1` does not comply with the `AETModel_V1` interface.

Each SubmodelAdder instance provides access to the appropriate interface and the interface methods the wrapped method uses (this information helps framework developers figure out which parameters the submodel prepares on its own):

>>> model.add_aetmodel_v1.submodelinterface.__name__
'AETModel_V1'
>>> for method in model.add_aetmodel_v1.methods:
...     method.__name__
'prepare_nmbzones'
'prepare_zonetypes'
'prepare_subareas'
'prepare_water'
'prepare_interception'
'prepare_soil'
'prepare_plant'
'prepare_tree'
'prepare_conifer'
'prepare_measuringheightwindspeed'
'prepare_leafareaindex'
'prepare_maxsoilwater'

SubmodelAdder supports arbitrarily deep submodel nesting. It conveniently moves some information from main models to sub-submodels or the other way round if the intermediate submodel does not consume or provide the corresponding data. The following example shows that the main model of type lland_knauf shares some of its class-level configurations with the sub-submodel of type evap_pet_hbv96 and that the sub-submodel knows about the zone areas of its main model (which the submodel is not aware of) and uses it for querying air temperature data:

>>> lnk(ACKER, WASSER)
>>> with model.add_aetmodel_v1("evap_aet_hbv96"):
...     nmbhru
...     hasattr(control, "hrualtitude")
...     soil
...     excessreduction(acker=1.0)
...     with model.add_petmodel_v1("evap_pet_hbv96"):
...         nmbhru
...         hruarea
...         hrualtitude(2.0)
...         evapotranspirationfactor(acker=1.2, default=1.0)
nmbhru(2)
False
soil(acker=True, wasser=False)
nmbhru(2)
hruarea(2.0, 8.0)
>>> model.aetmodel.parameters.control.excessreduction
excessreduction(1.0)
>>> model.aetmodel.petmodel.parameters.control.evapotranspirationfactor
evapotranspirationfactor(acker=1.2, wasser=1.0)
>>> model is model.aetmodel.tempmodel
True
>>> model is model.aetmodel.petmodel.tempmodel
True

SubmodelAdder tries to update the submodel’s derived parameters at the end of the with block. Hence, all required information must be available at that time:

>>> from hydpy import pub
>>> pub.timegrids = "2000-01-01", "2000-01-02", "1d"
>>> with model.add_radiationmodel_v1("meteo_glob_morsim"):
...     pass
Traceback (most recent call last):
...
hydpy.core.exceptiontools.AttributeNotReady: While trying to add submodel `meteo_glob_morsim` to the main model `lland_knauf`, the following error occurred: While trying to update parameter `latituderad` of element `?`, the following error occurred: While trying to multiply variable `latitude` and `float` instance `0.017453`, the following error occurred: For variable `latitude`, no value has been defined so far.

You can turn off this behaviour by setting update to False:

>>> with model.add_radiationmodel_v1("meteo_glob_morsim", update=False) as meteo_glob_morsim:
...     pass

meteo_glob_morsim is a “sharable” submodel, meaning one of its instances can be used by multiple main models. We demonstrate this by selecting evap_aet_morsim as the evaporation submodel, which requires the same radiation-related data as lland_knauf. We reuse the meteo_glob_morsim instance prepared above, which is already a submodel of lland_knauf, and make it also a submodel of the evap_aet_morsim instance:

>>> with model.add_aetmodel_v1("evap_aet_morsim"):
...     model.add_radiationmodel_v1(meteo_glob_morsim)
>>> model.radiationmodel is model.aetmodel.radiationmodel
True

When handing over already initialised submodel instances, the with statement cannot be used because later modifications of the submodel’s configuration would affect the submodel’s use by both main models.

Not all submodels are sharable, and model users might find it hard to see which one is. Hence, we introduced SharableSubmodelInterface and decided that model developers must derive a submodel from this class if convinced it is sharable. For safety, SubmodelAdder checks if a given model instance inherits from SharableSubmodelInterface:

>>> model.add_radiationmodel_v1(model)
Traceback (most recent call last):
...
TypeError: While trying to add a submodel to the main model `lland_knauf`, the following error occurred: The given `lland_knauf` instance is not considered sharable.
landtype_refindices: type[NameParameter] | None

Reference to a land cover type-related index parameter.

soiltype_refindices: type[NameParameter] | None

Reference to a soil type-related index parameter.

refweights: type[Parameter] | None

Reference to a weighting parameter.

submodelname: str

The submodel’s attribute name.

submodelinterface: type[TI_contra]

The relevant submodel interface.

methods: tuple[Callable[[NoReturn, NoReturn], None], ...]

The submodel interface methods the wrapped method uses.

dimensionality: TD

The dimensionality of the handled submodel reference(s) (either zero or one).

get_wrapped() PrepSub0D[TM_contra, TI_contra] | PrepSub1D[TM_contra, TI_contra][source]

Return the wrapped, model-specific method for automatically preparing some control parameters.

update(model: TM_contra, submodel: TI_contra, /, *, refresh: bool, position: int | None = None) None[source]

Update the connections between the given main model and its submodel, which can become necessary after disruptive configuration changes.

For now, we recommend using update() only for testing, not applications, because we cannot give clear recommendations for using it under different settings yet.

hydpy.core.importtools.define_targetparameter(parameter: type[Parameter]) Callable[[Callable[[Concatenate[TM_contra, P]], None]], TargetParameterUpdater[TM_contra, P]][source]

Wrap a submodel-specific method that allows the main model to set the value of a single control parameter of the submodel into a TargetParameterUpdater instance.

class hydpy.core.importtools.TargetParameterUpdater(wrapped: Callable[[Concatenate[TM_contra, P]], None], targetparameter: type[Parameter])[source]

Bases: _DoctestAdder, Generic[TM_contra, P]

Wrapper that extends the functionality of a submodel-specific method that allows the main model to set the value of a single control parameter of the submodel.

When calling a TargetParameterUpdater instance, it calls the wrapped method with unmodified arguments, so that model users might not even realise the TargetParameterUpdater instance exists:

>>> from hydpy import prepare_model
>>> model = prepare_model("evap_ret_tw2002")
>>> model.prepare_nmbzones(3)
>>> model.parameters.control.nmbhru
nmbhru(3)

However, each TargetParameterUpdater instance provides other framework functions access to the target control parameter type (the one the wrapped method prepares):

>>> model.prepare_nmbzones.targetparameter.__name__
'NmbHRU'

They also memorise the passed data and resulting parameter values:

>>> model.prepare_nmbzones.values_orig  
{evap_ret_tw2002: (((3,), {}), 3)}

With testmode enabled, TargetParameterUpdater instances do not pass the given data to the wrapped method but memorise it together with the already available parameter values in another dictionary:

>>> type(model.prepare_nmbzones).testmode = True
>>> model.prepare_nmbzones(4)
>>> model.parameters.control.nmbhru
nmbhru(3)
>>> model.prepare_nmbzones.values_test  
{evap_ret_tw2002: (((4,), {}), 3)}
testmode: ClassVar[bool] = False

The mode of all TargetParameterUpdater instances.

False indicates the normal “active” mode, where TargetParameterUpdater instances actually change the values of their target parameters and save the input data and the resulting parameter values in the values_orig dictionary. True indicates the testing mode where TargetParameterUpdater instances just collect the input data and the already available parameter values in the values_test dictionary.

targetparameter: type[Parameter]

The control parameter the wrapped method modifies.

values_orig: dict[~hydpy.core.modeltools.Model, tuple[tuple[~typing.~P, ~typing.~P], ~typing.Any]]

Deep copies of the input data (separated by positional and keyword arguments) and the resulting values of the target parameters of the respective model instances.

values_test: dict[~hydpy.core.modeltools.Model, tuple[tuple[~typing.~P, ~typing.~P], ~typing.Any]]

Deep copies of the input data (separated by positional and keyword arguments) and the already available values of the target parameters of the respective model instances.

hydpy.core.importtools.simulationstep(timestep: timetools.PeriodConstrArg) None[source]

Define a simulation time step size within a parameter control file for testing purposes.

Using simulationstep() only affects the values of time-dependent parameters when pub.timegrids.stepsize is not defined. It thus does not influence usual HydPy simulations at all. Use it to check your parameter control files. Write it in a line immediately behind the model import.

To clarify its purpose, function simulationstep() raises a warning when executed from within a control file:

>>> from hydpy.core.testtools import warn_later
>>> from hydpy import pub
>>> with warn_later(), pub.options.warnsimulationstep(True):
...     from hydpy.models.hland_96 import *
...     simulationstep("1h")
...     parameterstep("1d")
UserWarning: Note that the applied function `simulationstep` is intended for testing purposes only.  When doing a HydPy simulation, parameter values are initialised based on the actual simulation time step as defined under `pub.timegrids.stepsize` and the value given to `simulationstep` is ignored.
>>> pub.options.simulationstep
Period("1h")
hydpy.core.importtools.controlcheck(controldir: str = 'default', projectdir: str | None = None, controlfile: str | None = None, firstdate: timetools.DateConstrArg | None = None, stepsize: timetools.PeriodConstrArg | None = None) None[source]

Define the corresponding control file within a condition file.

Function controlcheck() serves similar purposes as function parameterstep(). It is why one can interactively access the state and the log sequences within condition files as land_dill_assl.py of the example project HydPy-H-Lahn. It is called controlcheck due to its feature to check for possible inconsistencies between control and condition files. The following test, where we write several soil moisture values (SM) into condition file land_dill_assl.py, which does not agree with the number of hydrological response units (NmbZones) defined in control file land_dill_assl.py, verifies that this works within a separate Python process:

>>> from hydpy.core.testtools import prepare_full_example_1
>>> prepare_full_example_1()
>>> import os
>>> from hydpy import run_subprocess, TestIO
>>> cwd = os.path.join("HydPy-H-Lahn", "conditions", "init_1996_01_01_00_00_00")
>>> with TestIO():   
...     os.chdir(cwd)
...     with open("land_dill_assl.py") as file_:
...         lines = file_.readlines()
...     lines[10:12] = "sm(185.13164, 181.18755)", ""
...     with open("land_dill_assl.py", "w") as file_:
...         _ = file_.write("\n".join(lines))
...     print()
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")

...
While trying to set the value(s) of variable `sm`, the following error occurred: While trying to convert the value(s) `(185.13164, 181.18755)` to a numpy ndarray with shape `(12,)` and type `float`, the following error occurred: could not broadcast input array from shape (2...) into shape (12...)
...

With a little trick, we can fake to be “inside” condition file land_dill_assl.py. Calling controlcheck() then, for example, prepares the shape of sequence Ic as specified by the value of parameter NmbZones given in the corresponding control file:

>>> from hydpy.models.hland_96 import *
>>> __file__ = "land_dill_assl.py"
>>> with TestIO():
...     os.chdir(cwd)
...     controlcheck(firstdate="1996-01-01", stepsize="1d")
>>> ic.shape
(12,)

In the above example, we use the default names for the project directory (the one containing the executed condition file) and the control directory (default). The following example shows how to change them:

>>> del model
>>> with TestIO():   
...     os.chdir(cwd)
...     controlcheck(projectdir="somewhere", controldir="nowhere")
Traceback (most recent call last):
...
FileNotFoundError: While trying to load the control file `land_dill_assl.py` from directory `...hydpy/tests/iotesting/somewhere/control/nowhere`, the following error occurred: ...

For some models, the appropriate states may depend on the initialisation date. One example is the interception storage (Inzp) of application model lland_dd, which should not exceed the interception capacity (KInz). However, KInz depends on the leaf area index parameter LAI, which offers different values depending on land-use type and month. Hence, one can assign higher values to state Inzp during periods with high leaf area indices than during periods with small leaf area indices.

To show the related functionalities, we first replace the hland_96 application model of element land_dill_assl with a lland_dd model object, define some of its parameter values, and write its control and condition files. Note that the LAI value of the only relevant land use the (ACKER) is 0.5 during January and 5.0 during July:

>>> from hydpy import HydPy, prepare_model, pub
>>> from hydpy.models.lland_dd import ACKER
>>> pub.timegrids = "2000-06-01", "2000-07-01", "1d"
>>> with TestIO():
...     hp = HydPy("HydPy-H-Lahn")
...     hp.prepare_network()
...     land_dill_assl = hp.elements["land_dill_assl"]
...     with pub.options.usedefaultvalues(True):
...         land_dill_assl.model = prepare_model("lland_dd")
...         control = land_dill_assl.model.parameters.control
...         control.nhru(2)
...         control.ft(1.0)
...         control.fhru(0.5)
...         control.lnk(ACKER)
...         control.lai.acker_jan = 0.5
...         control.lai.acker_jul = 5.0
...         land_dill_assl.model.parameters.update()
...         land_dill_assl.model.sequences.states.inzp(1.0)
...     land_dill_assl.model.save_controls()
...     land_dill_assl.model.save_conditions()

Unfortunately, state Inzp does not define a trim() method taking the actual value of parameter KInz into account (due to compatibility with the original LARSIM model). As an auxiliary solution, we define such a function within the land_dill_assl.py condition file (and modify some warning settings in favour of the next examples):

>>> cwd = os.path.join("HydPy-H-Lahn", "conditions", "init_2000_07_01_00_00_00")
>>> with TestIO():
...     os.chdir(cwd)
...     with open("land_dill_assl.py") as file_:
...         lines = file_.readlines()
...     with open("land_dill_assl.py", "w") as file_:
...         file_.writelines([
...             "from hydpy import pub\n",
...             "pub.options.warnsimulationstep = False\n",
...             "import warnings\n",
...             'warnings.filterwarnings("error", message="For variable")\n'])
...         file_.writelines(lines[:5])
...         file_.writelines([
...             "from hydpy.core.variabletools import trim as trim_\n",
...             "def trim(self, lower=None, upper=None):\n",
...             "    der = self.subseqs.seqs.model.parameters.derived\n",
...             "    trim_(self, 0.0, der.kinz.acker[der.moy[0]])\n",
...             "type(inzp).trim = trim\n"])
...         file_.writelines(lines[5:])

Now, executing the condition file (and thereby calling function controlcheck()) does not raise any warnings due to extracting the initialisation date from the name of the condition directory:

>>> with TestIO():
...     os.chdir(cwd)
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")

If the directory name does imply the initialisation date to be within January 2000 instead of July 2000, we correctly get the following warning:

>>> cwd_old = cwd
>>> cwd_new = os.path.join("HydPy-H-Lahn", "conditions", "init_2000_01_01")
>>> with TestIO():   
...     os.rename(cwd_old, cwd_new)
...     os.chdir(cwd_new)
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")
Invoking hyd.py with arguments `exec_script, land_dill_assl.py` resulted in the following error:
For variable `inzp` at least one value needed to be trimmed.  The old and the new value(s) are `1.0, 1.0` and `0.1, 0.1`, respectively.
...

One can define an alternative initialisation date via argument firstdate:

>>> text_old = ('controlcheck(projectdir=r"HydPy-H-Lahn", '
...             'controldir="default", stepsize="1d")')
>>> text_new = ('controlcheck(projectdir=r"HydPy-H-Lahn", controldir="default", '
...             'firstdate="2100-07-15", stepsize="1d")')
>>> with TestIO():
...     os.chdir(cwd_new)
...     with open("land_dill_assl.py") as file_:
...         text = file_.read()
...     text = text.replace(text_old, text_new)
...     with open("land_dill_assl.py", "w") as file_:
...         _ = file_.write(text)
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")

Default condition directory names do not contain information about the simulation step size. Hence, one needs to define it explicitly for all application models relying on the functionalities of class Indexer:

>>> with TestIO():   
...     os.chdir(cwd_new)
...     with open("land_dill_assl.py") as file_:
...         text = file_.read()
...     text = text.replace('stepsize="1d"', "")
...     with open("land_dill_assl.py", "w") as file_:
...         _ = file_.write(text)
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")
Invoking hyd.py with arguments `exec_script, land_dill_assl.py` resulted in the following error:
To apply function `controlcheck` requires time information for some model types.  Please define the `Timegrids` object of module `pub` manually or pass the required information (`stepsize` and eventually `firstdate`) as function arguments.
...

The same error occurs we do not use the argument firstdate to define the initialisation time point, and method controlcheck() cannot extract it from the directory name:

>>> cwd_old = cwd_new
>>> cwd_new = os.path.join("HydPy-H-Lahn", "conditions", "init")
>>> with TestIO():   
...     os.rename(cwd_old, cwd_new)
...     os.chdir(cwd_new)
...     with open("land_dill_assl.py") as file_:
...         text = file_.read()
...     text = text.replace('firstdate="2100-07-15"', 'stepsize="1d"')
...     with open("land_dill_assl.py", "w") as file_:
...         _ = file_.write(text)
...     result = run_subprocess("hyd.py exec_script land_dill_assl.py")
Invoking hyd.py with arguments `exec_script, land_dill_assl.py` resulted in the following error:
To apply function `controlcheck` requires time information for some model types.  Please define the `Timegrids` object of module `pub` manually or pass the required information (`stepsize` and eventually `firstdate`) as function arguments.
...

Note that the functionalities of function controlcheck() do not come into action if there is a model variable in the namespace, which is the case when a condition file is executed within the context of a complete HydPy project.