Skip to content

Friction

1D roughness .ini files

The friction module provides the specific logic for accessing friction/1D roughness files for a D-Flow FM model.

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

A 1D roughness file is described by the classes below.

Model

Friction model definitions for D-Flow FM roughness files.

FrictBranch

Bases: INIBasedModel

A [Branch] block for use inside a friction file.

Each block can define the roughness value(s) on a particular branch.

Source code in hydrolib/core/dflowfm/friction/models.py
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
222
223
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
262
263
264
265
266
267
268
269
270
271
272
class FrictBranch(INIBasedModel):
    """A `[Branch]` block for use inside a friction file.

    Each block can define the roughness value(s) on a particular branch.
    """

    class Comments(INIBasedModel.Comments):
        """Comments for the FrictBranch section fields."""

        branchid: Optional[str] = Field("The name of the branch.", alias="branchId")
        frictiontype: Optional[str] = Field(
            "The roughness type to be used on this branch.", alias="frictionType"
        )
        functiontype: Optional[str] = Field(
            "Function type for the calculation of the value. "
            + "Possible values: constant, timeSeries, absDischarge, waterlevel.",
            alias="functionType",
        )
        timeseriesid: Optional[str] = Field(
            "Refers to a data block in the <*.bc> frictionValuesFile. "
            + "Only if functionType = timeSeries.",
            alias="timeSeriesId",
        )
        numlevels: Optional[str] = Field(
            "Number of levels in table. Only if functionType is not constant.",
            alias="numLevels",
        )
        levels: Optional[str] = Field(
            "Space separated list of discharge [m3/s] or water level [m AD] values. "
            + "Only if functionType is absDischarge or waterLevel."
        )
        numlocations: Optional[str] = Field(
            "Number of locations on branch. The default 0 implies branch uniform values.",
            alias="numLocations",
        )
        chainage: Optional[str] = Field(
            "Space separated list of locations on the branch [m]. Locations sorted by "
            + "increasing chainage. The keyword must be specified if numLocations>0."
        )
        frictionvalues: Optional[str] = Field(
            "numLevels lines containing space separated lists of roughness values: "
            + "numLocations values per line. If the functionType is constant, then a "
            + "single line is required. For a uniform roughness per branch "
            + "(numLocations = 0) a single entry per line is required. The meaning "
            + "of the values depends on the roughness type selected (see frictionType).",
            alias="frictionValues",
        )

    comments: Comments = Comments()
    _header: Literal["Branch"] = "Branch"
    branchid: str = Field(alias="branchId")
    frictiontype: FrictionType = Field(alias="frictionType")
    functiontype: Optional[str] = Field("constant", alias="functionType")
    timeseriesid: Optional[str] = Field(None, alias="timeSeriesId")
    numlevels: Optional[PositiveInt] = Field(None, alias="numLevels")
    levels: Optional[List[float]] = Field(None)
    numlocations: Optional[NonNegativeInt] = Field(0, alias="numLocations")
    chainage: Optional[List[float]] = Field(None)
    frictionvalues: Optional[List[float]] = Field(None, alias="frictionValues")

    @field_validator("levels", "chainage", "frictionvalues", mode="before")
    @classmethod
    def split_field_values(cls, v, info: ValidationInfo) -> Optional[List[float]]:
        return split_string_on_delimiter(cls, v, info)

    @field_validator("frictiontype", mode="before")
    @classmethod
    def _validate_frictiontype(cls, v: str) -> FrictionType:
        return enum_value_parser(v, FrictionType)

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

    @field_validator("levels", mode="after")
    @classmethod
    def _validate_levels(cls, v: Optional[List[float]], info: ValidationInfo):
        numlevels = info.data.get("numlevels")
        branchid = info.data.get("branchid", "")
        if v is not None and (numlevels is None or len(v) != numlevels):
            raise ValueError(
                f"Number of values for levels should be equal to the numLevels value (branchId={branchid})."
            )

        return v

    @field_validator("chainage", mode="after")
    @classmethod
    def _validate_chainage(cls, v: Optional[List[float]], info: ValidationInfo):
        numlocations = info.data.get("numlocations")
        branchid = info.data.get("branchid", "")
        if v is not None and len(v) != numlocations:
            raise ValueError(
                f"Number of values for chainage should be equal to the numLocations value (branchId={branchid})."
            )

        return v

    @field_validator("frictionvalues", mode="after")
    @classmethod
    def _validate_frictionvalues(cls, v: Optional[List[float]], info: ValidationInfo):
        numlevels = info.data.get("numlevels") or 1
        numlocations = info.data.get("numlocations") or 0
        branchid = info.data.get("branchid", "")
        # number of values should be equal to numlocations*numlevels
        numvals = max(1, numlocations) * numlevels

        if v is not None and len(v) != numvals:
            raise ValueError(
                f"Number of values for frictionValues should be equal to the numLocations*numLevels value (branchId={branchid})."
            )

        return v

    @model_validator(mode="after")
    def validate_all(self) -> "FrictBranch":
        if self.levels is not None:
            if self.numlevels is None or len(self.levels) != self.numlevels:
                raise ValueError(
                    f"Number of values for levels should be equal to the numLevels value (branchId={self.branchid})."
                )

        if self.chainage is not None and len(self.chainage) != self.numlocations:
            raise ValueError(
                f"Number of values for chainage should be equal to the numLocations value (branchId={self.branchid})."
            )

        if self.frictionvalues is not None:
            numlevels = 1 if not self.numlevels else self.numlevels
            numvals = max(1, self.numlocations) * numlevels
            if len(self.frictionvalues) != numvals:
                raise ValueError(
                    f"Number of values for frictionValues should be equal to the numLocations*numLevels value (branchId={self.branchid})."
                )

        return self

Comments

Bases: Comments

Comments for the FrictBranch section fields.

Source code in hydrolib/core/dflowfm/friction/models.py
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
class Comments(INIBasedModel.Comments):
    """Comments for the FrictBranch section fields."""

    branchid: Optional[str] = Field("The name of the branch.", alias="branchId")
    frictiontype: Optional[str] = Field(
        "The roughness type to be used on this branch.", alias="frictionType"
    )
    functiontype: Optional[str] = Field(
        "Function type for the calculation of the value. "
        + "Possible values: constant, timeSeries, absDischarge, waterlevel.",
        alias="functionType",
    )
    timeseriesid: Optional[str] = Field(
        "Refers to a data block in the <*.bc> frictionValuesFile. "
        + "Only if functionType = timeSeries.",
        alias="timeSeriesId",
    )
    numlevels: Optional[str] = Field(
        "Number of levels in table. Only if functionType is not constant.",
        alias="numLevels",
    )
    levels: Optional[str] = Field(
        "Space separated list of discharge [m3/s] or water level [m AD] values. "
        + "Only if functionType is absDischarge or waterLevel."
    )
    numlocations: Optional[str] = Field(
        "Number of locations on branch. The default 0 implies branch uniform values.",
        alias="numLocations",
    )
    chainage: Optional[str] = Field(
        "Space separated list of locations on the branch [m]. Locations sorted by "
        + "increasing chainage. The keyword must be specified if numLocations>0."
    )
    frictionvalues: Optional[str] = Field(
        "numLevels lines containing space separated lists of roughness values: "
        + "numLocations values per line. If the functionType is constant, then a "
        + "single line is required. For a uniform roughness per branch "
        + "(numLocations = 0) a single entry per line is required. The meaning "
        + "of the values depends on the roughness type selected (see frictionType).",
        alias="frictionValues",
    )

FrictGeneral

Bases: INIGeneral

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

Source code in hydrolib/core/dflowfm/friction/models.py
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class FrictGeneral(INIGeneral):
    """The friction file's `[General]` section with file meta data."""

    class Comments(INIBasedModel.Comments):
        """Comments for the FrictGeneral section fields."""

        fileversion: Optional[str] = Field(
            "File version. Do not edit this.", alias="fileVersion"
        )
        filetype: Optional[str] = Field(
            "File type. Should be 'roughness'. Do not edit this.",
            alias="fileType",
        )
        frictionvaluesfile: Optional[str] = Field(
            "Name of <*.bc> file containing the timeseries with friction values. "
            + "Only needed for functionType = timeSeries.",
            alias="frictionValuesFile",
        )

    comments: Comments = Comments()
    _header: Literal["General"] = "General"
    fileversion: str = Field("3.01", alias="fileVersion")
    filetype: Literal["roughness"] = Field("roughness", alias="fileType")
    frictionvaluesfile: Annotated[
        DiskOnlyFileModel, BeforeValidator(set_default_disk_only_file_model)
    ] = Field(
        default_factory=lambda: DiskOnlyFileModel(None), alias="frictionValuesFile"
    )

    @field_validator("frictionvaluesfile", mode="before")
    @classmethod
    def _validate_frictionvaluesfile(cls, v: Optional[DiskOnlyFileModel]):
        """Validate the frictionValuesFile field to ensure it is a DiskOnlyFileModel."""
        if isinstance(v, (str, Path)):
            return DiskOnlyFileModel(v)
        return v

Comments

Bases: Comments

Comments for the FrictGeneral section fields.

Source code in hydrolib/core/dflowfm/friction/models.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
class Comments(INIBasedModel.Comments):
    """Comments for the FrictGeneral section fields."""

    fileversion: Optional[str] = Field(
        "File version. Do not edit this.", alias="fileVersion"
    )
    filetype: Optional[str] = Field(
        "File type. Should be 'roughness'. Do not edit this.",
        alias="fileType",
    )
    frictionvaluesfile: Optional[str] = Field(
        "Name of <*.bc> file containing the timeseries with friction values. "
        + "Only needed for functionType = timeSeries.",
        alias="frictionValuesFile",
    )

FrictGlobal

Bases: INIBasedModel

A [Global] block for use inside a friction file.

Multiple of such blocks may be present to define multiple frictionId classes.

Source code in hydrolib/core/dflowfm/friction/models.py
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
class FrictGlobal(INIBasedModel):
    """A `[Global]` block for use inside a friction file.

    Multiple of such blocks may be present to define multiple frictionId classes.
    """

    class Comments(INIBasedModel.Comments):
        """Comments for the FrictGlobal section fields."""

        frictionid: Optional[str] = Field(
            "Name of the roughness variable.", alias="frictionId"
        )
        frictiontype: Optional[str] = Field(
            "The global roughness type for this variable, which is used "
            + "if no branch specific roughness definition is given.",
            alias="frictionType",
        )
        frictionvalue: Optional[str] = Field(
            "The global default value for this roughness variable.",
            alias="frictionValue",
        )

    comments: Comments = Comments()
    _header: Literal["Global"] = "Global"
    frictionid: str = Field(alias="frictionId")
    frictiontype: FrictionType = Field(alias="frictionType")
    frictionvalue: float = Field(alias="frictionValue")

    @field_validator("frictiontype", mode="before")
    @classmethod
    def _validate_frictiontype(cls, v: str) -> FrictionType:
        return enum_value_parser(v, FrictionType)

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

Comments

Bases: Comments

Comments for the FrictGlobal section fields.

Source code in hydrolib/core/dflowfm/friction/models.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
class Comments(INIBasedModel.Comments):
    """Comments for the FrictGlobal section fields."""

    frictionid: Optional[str] = Field(
        "Name of the roughness variable.", alias="frictionId"
    )
    frictiontype: Optional[str] = Field(
        "The global roughness type for this variable, which is used "
        + "if no branch specific roughness definition is given.",
        alias="frictionType",
    )
    frictionvalue: Optional[str] = Field(
        "The global default value for this roughness variable.",
        alias="frictionValue",
    )

FrictionModel

Bases: INIModel

The overall friction model that contains the contents of one friction file.

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

Attributes:

Name Type Description
general FrictGeneral

[General] block with file metadata.

global_ List[FrictGlobal]

Definitions of [Global] friction classes.

branch List[FrictBranch]

Definitions of [Branch] friction values.

Source code in hydrolib/core/dflowfm/friction/models.py
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
class FrictionModel(INIModel):
    """
    The overall friction model that contains the contents of one friction file.

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

    Attributes:
        general (FrictGeneral): `[General]` block with file metadata.
        global_ (List[FrictGlobal]): Definitions of `[Global]` friction classes.
        branch (List[FrictBranch]): Definitions of `[Branch]` friction values.
    """

    general: FrictGeneral = FrictGeneral()
    global_: Annotated[List[FrictGlobal], BeforeValidator(make_list)] = Field(
        default_factory=list, alias="global"
    )  # to circumvent built-in kw
    branch: Annotated[List[FrictBranch], BeforeValidator(make_list)] = []

    @classmethod
    def _ext(cls) -> str:
        return ".ini"

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

FrictionType

Bases: StrEnum

Enum class containing the valid values for the frictionType attribute.

Contains valid values for the frictionType attribute in several subclasses of Structure/CrossSection/friction.models.

Parameters:

Name Type Description Default
chezy

str Chézy C [m 1/2 /s]

required
manning

str Manning n [s/m 1/3 ]

required
walllawnikuradse

str Nikuradse k_n [m]

required
whitecolebrook

str Nikuradse k_n [m]

required
stricklernikuradse

str Nikuradse k_n [m]

required
strickler

str Strickler k_s [m 1/3 /s]

required
debosbijkerk

str De Bos-Bijkerk γ [1/s]

required
Source code in hydrolib/core/dflowfm/friction/models.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class FrictionType(StrEnum):
    """Enum class containing the valid values for the frictionType attribute.

    Contains valid values for the frictionType attribute in several subclasses
    of Structure/CrossSection/friction.models.

    Args:
        chezy: str
            Chézy C [m 1/2 /s]
        manning: str
            Manning n [s/m 1/3 ]
        walllawnikuradse: str
            Nikuradse k_n [m]
        whitecolebrook: str
            Nikuradse k_n [m]
        stricklernikuradse: str
            Nikuradse k_n [m]
        strickler: str
            Strickler k_s [m 1/3 /s]
        debosbijkerk: str
            De Bos-Bijkerk γ [1/s]
    """

    chezy = "Chezy"
    manning = "Manning"
    walllawnikuradse = "wallLawNikuradse"
    whitecolebrook = "WhiteColebrook"
    stricklernikuradse = "StricklerNikuradse"
    strickler = "Strickler"
    debosbijkerk = "deBosBijkerk"