Skip to content

Storage node .ini files

The storage node module provides the specific logic for accessing storage node files for a D-Flow FM model.

Generic parsing and serializing functionality comes from the generic hydrolib.core.dflowfm.ini modules.

A storage node .ini file is described by the classes below.

Model

Interpolation

Bases: StrEnum

Enum class containing the valid values for the interpolation type as used for a storage area table in StorageNode.

Source code in hydrolib/core/dflowfm/storagenode/models.py
40
41
42
43
44
45
46
47
class Interpolation(StrEnum):
    """
    Enum class containing the valid values for the interpolation type
    as used for a storage area table in StorageNode.
    """

    linear = "linear"
    block = "block"

NodeType

Bases: StrEnum

Enum class containing the valid values for the node type as used in StorageNode.

Source code in hydrolib/core/dflowfm/storagenode/models.py
18
19
20
21
22
23
24
25
26
27
class NodeType(StrEnum):
    """
    Enum class containing the valid values for the node type
    as used in StorageNode.
    """

    inspection = "inspection"
    soakawaydrain = "soakawayDrain"
    compartment = "compartment"
    unspecified = "unspecified"

StorageNode

Bases: INIBasedModel

A storage node that is included in the storage node file.

All lowercased attributes match with the storage node input as described in UM Sec.C.17.

Source code in hydrolib/core/dflowfm/storagenode/models.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
class StorageNode(INIBasedModel):
    """
    A storage node that is included in the storage node file.

    All lowercased attributes match with the storage node input as described in
    [UM Sec.C.17](https://content.oss.deltares.nl/delft3dfm1d2d/D-Flow_FM_User_Manual_1D2D.pdf#section.C.17).
    """

    class Comments(INIBasedModel.Comments):
        id: Optional[str] = Field("Unique id of the storage node.", alias="id")
        name: Optional[str] = Field("Long name in the user interface.", alias="name")
        manholeid: Optional[str] = Field(
            "(optional) Unique id of manhole that this (compartment) node is part of.",
            alias="manholeId",
        )
        nodetype: Optional[str] = Field(
            "(optional) Type of the node. Possible values are: "
            + "inspection: inspection chamber, "
            + "soakawayDrain: soakaway drain (infiltration), "
            + "compartment: manhole compartment, "
            + "unspecified: general storage node of unspecified type",
            alias="nodeType",
        )
        nodeid: Optional[str] = Field(
            "Connection node on which the storage node is located.", alias="nodeId"
        )
        usetable: Optional[str] = Field(
            "Switch to select a simple (false) or tabulated (true) storage area.",
            alias="useTable",
        )
        bedlevel: Optional[str] = Field(
            "Bed level of the storage area [m AD].", alias="bedLevel"
        )
        area: Optional[str] = Field(
            "Storage area from bedLevel up to streetLevel (and beyond if useStreetStorage = false) [m2].",
            alias="area",
        )
        streetlevel: Optional[str] = Field(
            "Street level of the storage area [m AD].", alias="streetLevel"
        )
        streetstoragearea: Optional[str] = Field(
            "Storage area from streetLevel upwards if useStreetStorage = true [m2].",
            alias="streetStorageArea",
        )
        storagetype: Optional[str] = Field(
            "Possible values: "
            + "reservoir: Above streetLevel the storage area of this node is also taken into account. "
            + "closed: Above streetLevel this storage node has no storage area.",
            alias="storageType",
        )
        numlevels: Optional[str] = Field(
            "Number of levels in storage area table.", alias="numLevels"
        )
        levels: Optional[str] = Field(
            "Levels in storage area table [m].", alias="levels"
        )
        storagearea: Optional[str] = Field(
            "Areas in storage area table [m2].", alias="storageArea"
        )
        interpolate: Optional[str] = Field(
            "Interpolation type for storage area table. Possible values: linear or block.",
            alias="interpolate",
        )

    comments: Comments = Comments()
    _header: Literal["StorageNode"] = "StorageNode"
    id: str = Field(alias="id")
    name: str = Field(alias="name")
    manholeid: Optional[str] = Field(alias="manholeId")

    nodetype: Optional[NodeType] = Field(NodeType.unspecified.value, alias="nodeType")
    nodeid: str = Field(alias="nodeId")
    usetable: bool = Field(alias="useTable")

    # useTable is True
    bedlevel: Optional[float] = Field(alias="bedLevel")
    area: Optional[float] = Field(alias="area")
    streetlevel: Optional[float] = Field(alias="streetLevel")
    streetstoragearea: Optional[float] = Field(alias="streetStorageArea")
    storagetype: Optional[StorageType] = Field(
        StorageType.reservoir.value, alias="storageType"
    )

    # useTable is False
    numlevels: Optional[int] = Field(alias="numLevels")
    levels: Optional[List[float]] = Field(alias="levels")
    storagearea: Optional[List[float]] = Field(alias="storageArea")
    interpolate: Optional[Interpolation] = Field(
        Interpolation.linear.value, alias="interpolate"
    )

    @classmethod
    def _get_unknown_keyword_error_manager(cls) -> Optional[UnknownKeywordErrorManager]:
        """
        The StorageNode does not currently support raising an error on unknown keywords.
        """
        return None

    _interpolation_validator = get_enum_validator("interpolate", enum=Interpolation)
    _nodetype_validator = get_enum_validator("nodetype", enum=NodeType)
    _storagetype_validator = get_enum_validator("storagetype", enum=StorageType)
    _split_to_list = get_split_string_on_delimiter_validator(
        "levels",
        "storagearea",
    )

    @root_validator(allow_reuse=True)
    def check_list_length_levels(cls, values):
        """Validates that the length of the levels field is as expected."""
        return validate_correct_length(
            values,
            "levels",
            "storagearea",
            length_name="numlevels",
            list_required_with_length=True,
        )

    @root_validator(allow_reuse=True)
    def validate_that_required_fields_are_present_when_using_tables(
        cls, values: Dict
    ) -> Dict:
        """Validates that the specified fields are present when the usetable field is also present."""
        return validate_required_fields(
            values,
            "numlevels",
            "levels",
            "storagearea",
            conditional_field_name="usetable",
            conditional_value=True,
        )

    @root_validator(allow_reuse=True)
    def validate_that_required_fields_are_present_when_not_using_tables(
        cls, values: Dict
    ) -> Dict:
        """Validates that the specified fields are present."""
        return validate_required_fields(
            values,
            "bedlevel",
            "area",
            "streetlevel",
            conditional_field_name="usetable",
            conditional_value=False,
        )

    def _get_identifier(self, data: dict) -> Optional[str]:
        return data.get("id") or data.get("name")

check_list_length_levels(values)

Validates that the length of the levels field is as expected.

Source code in hydrolib/core/dflowfm/storagenode/models.py
181
182
183
184
185
186
187
188
189
190
@root_validator(allow_reuse=True)
def check_list_length_levels(cls, values):
    """Validates that the length of the levels field is as expected."""
    return validate_correct_length(
        values,
        "levels",
        "storagearea",
        length_name="numlevels",
        list_required_with_length=True,
    )

validate_that_required_fields_are_present_when_not_using_tables(values)

Validates that the specified fields are present.

Source code in hydrolib/core/dflowfm/storagenode/models.py
206
207
208
209
210
211
212
213
214
215
216
217
218
@root_validator(allow_reuse=True)
def validate_that_required_fields_are_present_when_not_using_tables(
    cls, values: Dict
) -> Dict:
    """Validates that the specified fields are present."""
    return validate_required_fields(
        values,
        "bedlevel",
        "area",
        "streetlevel",
        conditional_field_name="usetable",
        conditional_value=False,
    )

validate_that_required_fields_are_present_when_using_tables(values)

Validates that the specified fields are present when the usetable field is also present.

Source code in hydrolib/core/dflowfm/storagenode/models.py
192
193
194
195
196
197
198
199
200
201
202
203
204
@root_validator(allow_reuse=True)
def validate_that_required_fields_are_present_when_using_tables(
    cls, values: Dict
) -> Dict:
    """Validates that the specified fields are present when the usetable field is also present."""
    return validate_required_fields(
        values,
        "numlevels",
        "levels",
        "storagearea",
        conditional_field_name="usetable",
        conditional_value=True,
    )

StorageNodeGeneral

Bases: INIGeneral

The storage node file's [General] section with file meta data.

Source code in hydrolib/core/dflowfm/storagenode/models.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
class StorageNodeGeneral(INIGeneral):
    """The storage node file's `[General]` section with file meta data."""

    class Comments(INIBasedModel.Comments):
        fileversion: Optional[str] = Field(
            "File version. Do not edit this.", alias="fileVersion"
        )
        filetype: Optional[str] = Field(
            "File type. Should be 'storageNodes'. Do not edit this.",
            alias="fileType",
        )
        usestreetstorage: Optional[str] = Field(
            "Indicates whether above streetLevel the streetStorageArea must be used (true) "
            + "or the regular storage area continues (false). This option is only applicable "
            + "for storage nodes with useTable = false.",
            alias="useStreetStorage",
        )

    comments: Comments = Comments()
    _header: Literal["General"] = "General"
    fileversion: str = Field("2.00", alias="fileVersion")
    filetype: Literal["storageNodes"] = Field("storageNodes", alias="fileType")
    usestreetstorage: Optional[bool] = Field(True, alias="useStreetStorage")

StorageNodeModel

Bases: INIModel

The overall storage node model that contains the contents of one storage node file.

This model is typically referenced under a FMModel.geometry.storagenodefile[..].

Attributes:

Name Type Description
general StorageNodeGeneral

[General] block with file metadata.

storagenode List[StorageNode]

List of [StorageNode] blocks for all storage nodes.

Source code in hydrolib/core/dflowfm/storagenode/models.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
class StorageNodeModel(INIModel):
    """
    The overall storage node model that contains the contents of one storage node file.

    This model is typically referenced under a [FMModel][hydrolib.core.dflowfm.mdu.models.FMModel]`.geometry.storagenodefile[..]`.

    Attributes:
        general (StorageNodeGeneral): `[General]` block with file metadata.
        storagenode (List[StorageNode]): List of `[StorageNode]` blocks for all storage nodes.
    """

    general: StorageNodeGeneral = StorageNodeGeneral()
    storagenode: List[StorageNode] = []

    _make_list = make_list_validator("storagenode")

    @classmethod
    def _filename(cls) -> str:
        return "nodeFile"

    @validator("storagenode", each_item=True)
    def _validate(cls, storagenode: StorageNode, values: dict):
        """Validates for each storage node whether the streetStorageArea value is provided
        when the general useStreetStorage is True and the storage node useTable is False.
        """

        usestreetstorage = values["general"].usestreetstorage

        if (
            usestreetstorage
            and not storagenode.usetable
            and storagenode.streetstoragearea is None
        ):
            raise ValueError(
                f"streetStorageArea should be provided when useStreetStorage is True and useTable is False for storage node with id {storagenode.id}"
            )

        return storagenode

StorageType

Bases: StrEnum

Enum class containing the valid values for the storage type as used in StorageNode.

Source code in hydrolib/core/dflowfm/storagenode/models.py
30
31
32
33
34
35
36
37
class StorageType(StrEnum):
    """
    Enum class containing the valid values for the storage type
    as used in StorageNode.
    """

    reservoir = "reservoir"
    closed = "closed"