auxfiletools

This module supports writing auxiliary files.

In HydPy, parameter values are usually not shared between different model objects handled by different elements, even if the model objects are of the same type (e.g. lland_dd). This approach offers flexibility in applying different parameterisation schemes. However, often modellers prefer to use a minimal amount of values for specific parameters (at least within hydrologically homogeneous regions). Hence, the downside of this flexibility is that the same parameter values might appear in hundreds or even thousands of parameter control files (one file for each model/element).

To decrease this redundancy, HydPy allows for passing names of auxiliary control files to parameters defined within regular control files. HydPy then reads the actual parameter values from the auxiliary files, each one possibly referenced within a large number of control files.

Reading parameters from regular and from auxiliary control files is straightforward. However, storing some parameters in a large number of regular control files and some other parameters in a small number of auxiliary files can be complicated. The features implemented in module auxfiletools are a means to perform such actions in a semi-automated manner (other means are the selection mechanism implemented in module selectiontools).

Module auxfiletools implements the following members:


class hydpy.core.auxfiletools.Auxfiler(*models: str | ModuleType | Model)[source]

Bases: object

Structures auxiliary file information.

For writing some parameter information to auxiliary files, it is advisable to prepare (only) one Auxfiler object:

>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler()

Each Auxfiler object is capable of handling parameter information for different kinds of models and performs some plausibility checks on added data. Assume we want to store the control files of a “LARSIM type” HydPy project involving the application models lland_dd, lland_knauf and kinw_williams. The following example shows how we add these models to the Auxfiler object by passing through module (lland_dd), a working model object (lland_knauf) or their name (kinw_williams):

>>> from hydpy import prepare_model
>>> from hydpy.models import lland_dd as module
>>> model = prepare_model("lland_knauf")
>>> string = "kinw_williams"

You can add all model types individually or in groups:

>>> auxfiler.add_models(module)
>>> auxfiler.add_models(model, string)
>>> auxfiler
Auxfiler("kinw_williams", "lland_dd", "lland_knauf")

Alternatively, you can pass the models directly to the constructor:

>>> Auxfiler(model, string, module)
Auxfiler("kinw_williams", "lland_dd", "lland_knauf")

Wrong model specifications result in errors like the following:

>>> auxfiler.add_models("asdf")
Traceback (most recent call last):
...
ModuleNotFoundError: While trying to add one ore more models to the actual `Auxfiler` object, the following error occurred: No module named 'hydpy.models.asdf'

The Auxfiler object allocates a separate SubAuxfiler object to each model type. These are available via keyword and attribute access:

>>> auxfiler["lland_dd"]
SubAuxfiler()
>>> auxfiler.lland_knauf
SubAuxfiler()

Adding new and deleting existing SubAuxfiler objects via attribute access is disabled for safety purposes:

>>> auxfiler.lland_knauf = auxfiler.lland_dd
Traceback (most recent call last):
...
AttributeError: Class `Auxfiler` does not support adding `SubAuxfiler` objects via attribute access.  Use method `add_models` to register additional models.
>>> del auxfiler.lland_dd
Traceback (most recent call last):
...
AttributeError: Class `Auxfiler` does not support deleting `SubAuxfiler` objects via attribute access.  Use method `remove_models` to remove registered models.

As stated by the last error message, you should remove models and their SubAuxfiler objects via method remove_models():

>>> auxfiler.remove_models(module, string)
>>> auxfiler
Auxfiler("lland_knauf")
>>> auxfiler.remove_models(module, string)
Traceback (most recent call last):
...
RuntimeError: While trying to remove one or more models from the actual `Auxfiler` object, the following error occurred: Model `lland_dd` is currently not registered.
>>> auxfiler.lland_dd  
Traceback (most recent call last):
...
AttributeError: The actual `Auxfiler` object does neither have a normal attribute nor does it handle a model named `lland_dd`...
>>> auxfiler["lland_dd"]
Traceback (most recent call last):
...
KeyError: 'The actual `Auxfiler` object does not handle a model named `lland_dd`.'

For other types, attribute access works as usual:

>>> auxfiler.test = 123
>>> hasattr(auxfiler, "test")
True
>>> del auxfiler.test
>>> hasattr(auxfiler, "test")
False
add_models(*models: str | ModuleType | Model) None[source]

Register an arbitrary number of Model types.

For further information, see the main documentation on class Auxfiler.

remove_models(*models: str | ModuleType | Model) None[source]

Unregister an arbitrary number of Model classes.

For further information, see the main documentation on class Auxfiler.

get(model: str | Model) SubAuxfiler | None[source]

Get the SubAuxfiler object related to the given Model type.

In contrast to attribute and keyword access, method get() returns None when it does not handle the requested SubAuxfiler object:

>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler("lland_dd")
>>> auxfiler.get("lland_dd")
SubAuxfiler()
>>> auxfiler.get("lland_knauf")
property modelnames: tuple[str, ...]

A sorted tuple of all names of the handled models.

>>> from hydpy import Auxfiler
>>> Auxfiler("lland_knauf", "kinw_williams", "lland_dd").modelnames
('kinw_williams', 'lland_dd', 'lland_knauf')
write(parameterstep: timetools.PeriodConstrArg | None = None, simulationstep: timetools.PeriodConstrArg | None = None) None[source]

Write all defined auxiliary control files.

Before being able to write parameter information into auxiliary files, you need to know how to add this information to your Auxfiler object. Hence, please read the documentation on method add_parameter() of class SubAuxfiler first, from which we borrow the following (slightly modified) test-setting:

>>> from hydpy.models.lland import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqd1(200.0)
>>> eqd2(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler("lland_dd", "lland_knauf")
>>> auxfiler.lland_dd.add_parameter(eqd1, filename="file1")
>>> auxfiler.lland_dd.add_parameter(parameter=tgr,
...                                 filename="file1",
...                                 keywordarguments=tgr.keywordarguments)
>>> auxfiler.lland_knauf.add_parameters(eqd1, eqd2, filename="file2")

Class Auxfiler takes the target path from the ControlManager object stored in the global pub object. For testing, we initialise one and override its property currentpath with a simple str object defining the test target path:

>>> from hydpy import pub
>>> pub.projectname = "test"
>>> from hydpy.core.filetools import ControlManager
>>> class Test(ControlManager):
...     currentpath = "test_directory"
>>> pub.controlmanager = Test()

Usually, Auxfiler objects write control files to disk, of course. But to show (and to test) the results in the following test, we redirected file writing via class Open:

>>> from hydpy import Open
>>> with Open():
...     auxfiler.write(parameterstep="1d", simulationstep="12h")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_directory/file1.py
-----------------------------------
# -*- coding: utf-8 -*-

from hydpy.models.lland_dd import *

simulationstep("12h")
parameterstep("1d")

tgr(acker=2.0, laubw=1.0)
eqd1(200.0)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_directory/file2.py
--------------------------------------
# -*- coding: utf-8 -*-

from hydpy.models.lland_knauf import *

simulationstep("12h")
parameterstep("1d")

eqd1(200.0)
eqd2(100.0)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class hydpy.core.auxfiletools.SubAuxfiler(master: Auxfiler | None = None, model: Model | None = None)[source]

Bases: object

Map different Parameter objects to the names of auxiliary files.

Usually, SubAuxfiler objects are not initialised by the user explicitly but made available by their master Auxfiler object. After that, users can access them and subsequently register different Parameter objects. See the documentation on method add_parameter() for further information.

add_parameter(parameter: Parameter, filename: str, keywordarguments: KeywordArguments[T] | None = None) None[source]

Add a single Parameter to the actual SubAuxfiler object.

To show how SubAuxfiler works, we first prepare an instance of application model lland_dd and define the values of some of its parameters:

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqi2(1000.0)
>>> eqd1(100.0)
>>> eqd2(50.0)

Next, we initialise an Auxfiler object handling a single SubAuxfiler object. The purpose of the SubAuxfiler object is to allocate the above parameters to two auxiliary files named file1 and file2:

>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)

Auxiliary file file1 shall contain the actual values of parameters EQB, EQI1, and EQI2:

>>> auxfiler.lland_dd.add_parameters(eqb, eqi1, eqi2, filename="file1")
>>> auxfiler.lland_dd.file1
(eqb(5000.0), eqi1(2000.0), eqi2(1000.0))

Auxiliary file file2 shall contain the actual values of parameters EQD1, EQD2, and (also!) of parameter EQB:

>>> auxfiler.lland_dd.add_parameters(eqd1, eqd2, filename="file2")
>>> auxfiler.lland_dd.add_parameter(eqb, filename="file2")
Traceback (most recent call last):
...
RuntimeError: While trying to extend the range of parameters handled by the actual `SubAuxfiler` object, the following error occurred: You tried to allocate parameter `eqb(5000.0)` to filename `file2`, but an equal `EQB` object has already been allocated to filename `file1`.
>>> auxfiler.lland_dd.file2
(eqd1(100.0), eqd2(50.0))

As explained by the error message, allocating the same parameter type with equal values to two different auxiliary files is not allowed. Nevertheless, after changing the value of parameter EQB, it can be allocated to filename file2:

>>> eqb *= 2
>>> auxfiler.lland_dd.add_parameter(eqb, filename="file2")
>>> auxfiler.lland_dd.file2
(eqb(10000.0), eqd1(100.0), eqd2(50.0))

The following example shows that parameter EQB already allocated to file1 has still the same value (we implemented this safety mechanism via deep copying) and that one can view all registered parameters by using their names as attribute names:

>>> auxfiler.lland_dd.eqb
(eqb(10000.0), eqb(5000.0))

During adding parameters method add_parameter() performs some additional plausibility checks. First, it prevents from using the same filename twice:

>>> auxfiler.add_models("kinw_williams")
>>> auxfiler.kinw_williams.add_parameter(tgr, filename="file1")
Traceback (most recent call last):
...
RuntimeError: While trying to extend the range of parameters handled by the actual `SubAuxfiler` object, the following error occurred: Filename `file1` is already allocated to another `SubAuxfiler` object.

Second, it checks that an assigned parameter belongs to the corresponding model:

>>> auxfiler.kinw_williams.add_parameter(tgr, filename="file3")
Traceback (most recent call last):
...
TypeError: While trying to extend the range of parameters handled by the actual `SubAuxfiler` object, the following error occurred: Variable type `TGr` is not handled by model `kinw_williams`.

The examples above deal with simple 0-dimensional Parameter subclasses where there is no question in how to define equality. However, for multidimensional Parameter subclasses requiring that the shape and all values are equal might often be too strict.

The auxiliary file functionalities of HydPy allow using the keywordarguments property of a parameter to check for equality instead (put more concretely, method get_parameterstrings() uses method subset_of() of class KeywordArguments for comparisons). If we want to apply this feature for the instances of the ZipParameter subclass TGr, we need to additionally pass a KeywordArguments object to method add_parameter():

>>> auxfiler.lland_dd.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> auxfiler.lland_dd.tgr
(KeywordArguments(acker=2.0, laubw=1.0),)

Alternatively, we can also pass a manually defined KeywordArguments object (for which we perform similar plausibility checks as discussed above):

>>> from hydpy import KeywordArguments
>>> auxfiler.lland_dd.add_parameter(
...     tgr, filename="file2",
...     keywordarguments=KeywordArguments(acker=2.0, laubw=1.0))
Traceback (most recent call last):
...
RuntimeError: While trying to extend the range of parameters handled by the actual `SubAuxfiler` object, the following error occurred: You tried to allocate parameter `tgr(acker=2.0, laubw=1.0)` with keyword arguments `KeywordArguments(acker=2.0, laubw=1.0)` to filename `file2`, but an `TGr` with equal keyword arguments has already been allocated to filename `file1`.

Often, we want such a manually defined KeywordArguments object to be more general so that it covers as many actual parameter objects as possible:

>>> auxfiler.lland_dd.add_parameter(
...     tgr, filename="file2",
...     keywordarguments=KeywordArguments(acker=2.0, laubw=1.0, nadelw=0.0))
>>> auxfiler.lland_dd.tgr
(KeywordArguments(acker=2.0, laubw=1.0), KeywordArguments(acker=2.0, laubw=1.0, nadelw=0.0))

Note that due to the current implementation of method get_parameterstrings() the final state of the Auxfiler object results in some ambiguity (see the documentation on method get_parameterstrings() for further information). Hence, we might add more detailed plausibility checks regarding equality of KeywordArguments objects in the future.

Unfortunately, the string representations of SubAuxfiler objects are not executable at the moment:

>>> auxfiler.lland_dd
SubAuxfiler(file1, file2)

Erroneous attribute access results in the following error:

>>> auxfiler.lland_dd.wrong
Traceback (most recent call last):
...
AttributeError: `wrong` is neither a filename nor a name of a parameter handled by the actual `SubAuxfiler` object.
add_parameters(*parameters: Parameter, filename: str) None[source]

Add an arbitrary number of Parameter objects to the actual SubAuxfiler object.

Method add_parameters() works like method add_parameter() but allows to add multiple parameters at once. On the downside, it does not allow to define alternative keyword arguments.

remove_parameters(parametertype: type[Parameter] | None = None, filename: str | None = None) None[source]

Remove the registered Parameter objects of the given type related to the given filename.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")
>>> subauxfiler.file1
(KeywordArguments(acker=2.0, laubw=1.0), eqb(5000.0), eqi1(2000.0))
>>> subauxfiler.file2
(eqb(10000.0), eqd1(100.0))

To be able to start from this setting repeatedly, we make a deep copy of an internal dictionary:

>>> from copy import deepcopy
>>> copy = deepcopy(subauxfiler._type2filename2reference)

First, you can remove all parameters related to a specific filename:

>>> subauxfiler.remove_parameters(filename="file1")
>>> subauxfiler.get_filenames()
('file2',)
>>> subauxfiler.file2
(eqb(10000.0), eqd1(100.0))

Second, you can remove all parameters of a specific type:

>>> subauxfiler._type2filename2reference = deepcopy(copy)
>>> subauxfiler.remove_parameters(parametertype=type(eqb))
>>> subauxfiler.file1
(KeywordArguments(acker=2.0, laubw=1.0), eqi1(2000.0))
>>> subauxfiler.file2
(eqd1(100.0),)

Third, you can remove a specific parameter object by defining both its time and its filename:

>>> subauxfiler._type2filename2reference = deepcopy(copy)
>>> subauxfiler.remove_parameters(parametertype=type(eqb), filename="file1")
>>> subauxfiler.file1
(KeywordArguments(acker=2.0, laubw=1.0), eqi1(2000.0))
>>> subauxfiler.file2
(eqb(10000.0), eqd1(100.0))

Fourth, you can remove all parameters at once:

>>> subauxfiler._type2filename2reference = deepcopy(copy)
>>> subauxfiler.remove_parameters()
>>> subauxfiler.get_filenames()
()

Each combination of arguments comes with a particular error message:

>>> subauxfiler.remove_parameters(filename="file2")
Traceback (most recent call last):
...
RuntimeError: While trying to remove the parameter object(s) allocated to filename `file2`, the following error occurred: No such parameter object(s) available.
>>> subauxfiler.remove_parameters(parametertype=type(eqd2))
Traceback (most recent call last):
...
RuntimeError: While trying to remove the parameter object(s) of type `EQD2`, the following error occurred: No such parameter object(s) available.
>>> subauxfiler.remove_parameters(parametertype=type(eqd2), filename="file2")
Traceback (most recent call last):
...
RuntimeError: While trying to remove a parameter object of type `EQD2` allocated to filename `file2`, the following error occurred: No such parameter object(s) available.
>>> subauxfiler._type2filename2reference = None
>>> subauxfiler.remove_parameters()
Traceback (most recent call last):
...
AttributeError: While trying to remove all parameter objects, the following error occurred: 'NoneType' object has no attribute 'items'
get_filenames(parametertype: type[Parameter] | None = None) tuple[str, ...][source]

Return a tuple of all or a selection of the handled auxiliary file names.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")

Without an argument, method get_filenames() returns all auxiliary filenames:

>>> subauxfiler.get_filenames()
('file1', 'file2')

For a given parameter type, method get_filenames() returns only the auxiliary filenames allocating such a type:

>>> subauxfiler.get_filenames(parametertype=type(eqi1))
('file1',)
get_parametertypes(filename: str | None = None) tuple[type[Parameter], ...][source]

Return a tuple of all or a selection of the handled parameter types.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")

Without an argument, method get_parametertypes() returns all registered parameter types:

>>> for parametertype in subauxfiler.get_parametertypes():
...     print(parametertype.__name__)
TGr
EQB
EQI1
EQD1

For a given filename, method get_parametertypes() returns only the registered parameter types allocated for this filename:

>>> for parametertype in subauxfiler.get_parametertypes(filename="file1"):
...     print(parametertype.__name__)
TGr
EQB
EQI1
get_parameterstrings(filename: str | None = None, parametertype: type[Parameter] | None = None) tuple[str, ...][source]

Return a tuple of string representations of all or a selection of the handled parameter objects.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")

Without an argument, method get_parameterstrings() returns the string representations of all registered parameter objects:

>>> for string in subauxfiler.get_parameterstrings():
...     print(string)
tgr(acker=2.0, laubw=1.0)
eqb(5000.0)
eqb(10000.0)
eqi1(2000.0)
eqd1(100.0)

For a given filename, method get_parameterstrings() returns only the string representations of the registered parameter objects allocated for this filename:

>>> for string in subauxfiler.get_parameterstrings(filename="file1"):
...     print(string)
tgr(acker=2.0, laubw=1.0)
eqb(5000.0)
eqi1(2000.0)

For a given parameter type, method get_parameterstrings() returns only the string representations of the registered parameter objects of such a type:

>>> for string in subauxfiler.get_parameterstrings(parametertype=type(eqb)):
...     print(string)
eqb(5000.0)
eqb(10000.0)

For a given filename and a given parameter type, method get_parameterstrings() returns only the string representation of the registered parameter object of such a type allocated by such a filename:

>>> subauxfiler.get_parameterstrings(filename="file1", parametertype=type(eqb))
('eqb(5000.0)',)
get_references(filename: str | None = None, parametertype: type[Parameter] | None = None) tuple[Parameter | KeywordArguments, ...][source]

Return a tuple of all or a selection of the reference parameter objects or their related reference keyword arguments.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")

Despite returning the reference objects instead of parameter string representations, method get_references() works precisely like method get_parameterstrings():

>>> for reference in subauxfiler.get_references():
...     print(reference)
KeywordArguments(acker=2.0, laubw=1.0)
eqb(5000.0)
eqb(10000.0)
eqi1(2000.0)
eqd1(100.0)
>>> for reference in subauxfiler.get_references(filename="file1"):
...     print(reference)
KeywordArguments(acker=2.0, laubw=1.0)
eqb(5000.0)
eqi1(2000.0)
>>> for reference in subauxfiler.get_references(parametertype=type(eqb)):
...     print(reference)
eqb(5000.0)
eqb(10000.0)
>>> subauxfiler.get_references(filename="file1", parametertype=type(eqb))
(eqb(5000.0),)
get_filename(parameter: Parameter) str | None[source]

If possible, return an auxiliary filename suitable for the given parameter object.

The following (slightly modified) test-setting stems from the documentation on method add_parameter():

>>> from hydpy.models.lland_dd import *
>>> parameterstep()
>>> nhru(4)
>>> lnk(ACKER, LAUBW, WASSER, ACKER)
>>> tgr(acker=2.0, laubw=1.0)
>>> eqb(5000.0)
>>> eqi1(2000.0)
>>> eqd1(100.0)
>>> from hydpy import Auxfiler
>>> auxfiler = Auxfiler(model)
>>> subauxfiler = auxfiler.lland_dd
>>> subauxfiler.add_parameters(eqb, eqi1, filename="file1")
>>> subauxfiler.add_parameter(
...     tgr, filename="file1", keywordarguments=tgr.keywordarguments)
>>> eqb *= 2.0
>>> subauxfiler.add_parameters(eqb, eqd1, filename="file2")

In simple cases, method get_filename() searches for an equal reference parameter object and returns the related auxiliary filename:

>>> subauxfiler.get_filename(eqb)
'file2'
>>> eqb /= 2.0
>>> subauxfiler.get_filename(eqb)
'file1'

If it cannot find an equal reference parameter, it returns None:

>>> eqb /= 2.0
>>> subauxfiler.get_filename(eqb)

If a registered parameter object is associated with a KeywordArguments object (see the documentation on method add_parameter() on how to do this), get_filename() does not check for equality. Instead, it uses method subset_of() to check if the keyword arguments of the given parameter are a subset of keyword arguments of the registered parameter:

>>> subauxfiler.get_filename(tgr)
'file1'
>>> tgr(acker=2.0, laubw=0.0)
>>> subauxfiler.get_filename(tgr)

As a result, auxiliary file file1 is also considered suitable for a TGr object related to land-use type ACKER (acre) only:

>>> lnk(ACKER)
>>> tgr(acker=3.0)
>>> subauxfiler.get_filename(tgr)
>>> tgr(acker=2.0)
>>> subauxfiler.get_filename(tgr)
'file1'

The above mechanism is convenient (and possibly even necessary to make writing auxiliary files feasible for many parameter types) but can lead to ambiguous situations. To demonstrate this, we register the currently relevant keyword arguments of parameter TGr:

>>> subauxfiler.add_parameter(
...     tgr, filename="file2", keywordarguments=tgr.keywordarguments)

Now, the keyword arguments of TGr are a subset of both registered KeywordArguments objects:

>>> tgr.keywordarguments
KeywordArguments(acker=2.0)
>>> kwargs = subauxfiler.get_references(
...     parametertype=type(tgr), filename="file1")[0]
>>> kwargs
KeywordArguments(acker=2.0, laubw=1.0)
>>> tgr.keywordarguments.subset_of(kwargs)
True
>>> kwargs = subauxfiler.get_references(
...     parametertype=type(tgr), filename="file2")[0]
>>> kwargs
KeywordArguments(acker=2.0)
>>> tgr.keywordarguments.subset_of(kwargs)
True

Method get_filename() emits a warning when it finds multiple suitable auxiliary files:

>>> subauxfiler.get_filename(tgr)
Traceback (most recent call last):
...
UserWarning: Parameter `tgr(2.0)` matches several auxiliary files: file1 and file2

Nevertheless, it returns the first match (which might be confusing due to its arbitrariness but at least results in a working project configuration):

>>> import warnings
>>> with warnings.catch_warnings() :
...     warnings.filterwarnings("ignore")
...     subauxfiler.get_filename(tgr)
'file1'