Structure .ini files¶
Structure .ini files contain the definition of hydraulic structures for a D-Flow FM model.
Generic parsing and serializing functionality comes from the generic hydrolib.core.dflowfm.ini modules.
A structures.ini file is described by the classes below.
Models¶
structure namespace for storing the contents of an FMModel's structure file.
Bridge (Structure)
pydantic-model
¶
Hydraulic structure with type=bridge
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the bridge input as described in UM Sec.C.12.5.
Compound (Structure)
pydantic-model
¶
Hydraulic structure with type=compound
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the compound input as described in UM Sec.C.12.11.
Culvert (Structure)
pydantic-model
¶
Hydraulic structure with type=culvert
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the culvert input as described in UM Sec.C.12.3.
check_list_lengths(values)
classmethod
¶
Validates that the length of the relopening and losscoeff fields are as expected.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def check_list_lengths(cls, values):
"""Validates that the length of the relopening and losscoeff fields are as expected."""
return validate_correct_length(
values,
"relopening",
"losscoeff",
length_name="numlosscoeff",
list_required_with_length=True,
)
validate_that_bendlosscoeff_field_is_present_for_invertedsyphons(values: Dict) -> Dict
classmethod
¶
Validates that the bendlosscoeff value is present when dealing with inverted syphons.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def validate_that_bendlosscoeff_field_is_present_for_invertedsyphons(
cls, values: Dict
) -> Dict:
"""Validates that the bendlosscoeff value is present when dealing with inverted syphons."""
return validate_required_fields(
values,
"bendlosscoeff",
conditional_field_name="subtype",
conditional_value=CulvertSubType.invertedSiphon,
)
validate_that_bendlosscoeff_is_not_provided_for_culverts(values: Dict) -> Dict
classmethod
¶
Validates that the bendlosscoeff field is not provided when the subtype is a culvert.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def validate_that_bendlosscoeff_is_not_provided_for_culverts(
cls, values: Dict
) -> Dict:
"""Validates that the bendlosscoeff field is not provided when the subtype is a culvert."""
return validate_forbidden_fields(
values,
"bendlosscoeff",
conditional_field_name="subtype",
conditional_value=CulvertSubType.culvert,
)
validate_that_valve_related_fields_are_present_for_culverts_with_valves(values: Dict) -> Dict
classmethod
¶
Validates that valve-related fields are present when there is a valve present.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def validate_that_valve_related_fields_are_present_for_culverts_with_valves(
cls, values: Dict
) -> Dict:
"""Validates that valve-related fields are present when there is a valve present."""
return validate_required_fields(
values,
"valveopeningheight",
"numlosscoeff",
"relopening",
"losscoeff",
conditional_field_name="valveonoff",
conditional_value=True,
)
Dambreak (Structure)
pydantic-model
¶
Hydraulic structure with type=dambreak
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the dambreak input as described in UM Sec.C.12.10.
check_location_dambreak(values: dict) -> dict
classmethod
¶
Verifies whether the location for this structure contains valid values for numCoordinates, xCoordinates and yCoordinates or instead is using a polyline file. Verifies whether de water level location specifications are valid.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
values |
dict |
Dictionary of validated values to create a Dambreak. |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When the values dictionary does not contain valid coordinates or polyline file or when the water level location specifications are not valid. |
Returns:
Type | Description |
---|---|
dict |
Dictionary of validated values. |
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator
@classmethod
def check_location_dambreak(cls, values: dict) -> dict:
"""
Verifies whether the location for this structure contains valid values for
numCoordinates, xCoordinates and yCoordinates or instead is using a polyline file.
Verifies whether de water level location specifications are valid.
Args:
values (dict): Dictionary of validated values to create a Dambreak.
Raises:
ValueError: When the values dictionary does not contain valid coordinates or polyline file or when the water level location specifications are not valid.
Returns:
dict: Dictionary of validated values.
"""
def _validate_waterlevel_location(x_key: str, y_key: str, node_key: str):
x_is_given = values.get(x_key.lower()) is not None
y_is_given = values.get(y_key.lower()) is not None
node_is_given = values.get(node_key.lower()) is not None
if (x_is_given and y_is_given and not node_is_given) or (
node_is_given and not x_is_given and not y_is_given
):
return
raise ValueError(
f"Either `{node_key}` should be specified or `{x_key}` and `{y_key}`."
)
_validate_waterlevel_location(
"waterLevelUpstreamLocationX",
"waterLevelUpstreamLocationY",
"waterLevelUpstreamNodeId",
)
_validate_waterlevel_location(
"waterLevelDownstreamLocationX",
"waterLevelDownstreamLocationY",
"waterLevelDownstreamNodeId",
)
return values
validate_algorithm(value: str) -> DambreakAlgorithm
classmethod
¶
Validates the algorithm parameter for the dambreak structure.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
value |
int |
algorithm value read from the user's input. |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When the value given is not of type int. |
ValueError |
When the value given is not in the range [1,3] |
Returns:
Type | Description |
---|---|
int |
Validated value. |
Source code in hydrolib/core/dflowfm/structure/models.py
@validator("algorithm", pre=True)
@classmethod
def validate_algorithm(cls, value: str) -> DambreakAlgorithm:
"""
Validates the algorithm parameter for the dambreak structure.
Args:
value (int): algorithm value read from the user's input.
Raises:
ValueError: When the value given is not of type int.
ValueError: When the value given is not in the range [1,3]
Returns:
int: Validated value.
"""
int_value = -1
try:
int_value = int(value)
except Exception:
raise ValueError("Dambreak algorithm value should be of type int.")
if 0 < int_value <= 3:
return DambreakAlgorithm(int_value)
raise ValueError("Dambreak algorithm value should be 1, 2 or 3.")
validate_dambreak_levels_and_widths(field_value: Union[hydrolib.core.dflowfm.tim.models.TimModel, hydrolib.core.dflowfm.bc.models.ForcingModel], values: dict) -> Union[hydrolib.core.dflowfm.tim.models.TimModel, hydrolib.core.dflowfm.bc.models.ForcingModel]
classmethod
¶
Validates whether a dambreak can be created with the given dambreakLevelsAndWidths property. This property should be given when the algorithm value is 3.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
field_value |
Optional[Union[TimModel, ForcingModel]] |
Value given for dambreakLevelsAndWidths. |
required |
values |
dict |
Dictionary of values already validated (assuming algorithm is in it). |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When algorithm value is not 3 and field_value has a value. |
Returns:
Type | Description |
---|---|
Optional[Union[TimModel, ForcingModel]] |
The value given for dambreakLevelsAndwidths. |
Source code in hydrolib/core/dflowfm/structure/models.py
@validator("dambreaklevelsandwidths")
@classmethod
def validate_dambreak_levels_and_widths(
cls, field_value: Optional[Union[TimModel, ForcingModel]], values: dict
) -> Optional[Union[TimModel, ForcingModel]]:
"""
Validates whether a dambreak can be created with the given dambreakLevelsAndWidths
property. This property should be given when the algorithm value is 3.
Args:
field_value (Optional[Union[TimModel, ForcingModel]]): Value given for dambreakLevelsAndWidths.
values (dict): Dictionary of values already validated (assuming algorithm is in it).
Raises:
ValueError: When algorithm value is not 3 and field_value has a value.
Returns:
Optional[Union[TimModel, ForcingModel]]: The value given for dambreakLevelsAndwidths.
"""
# Retrieve the algorithm value (if not found use 0).
algorithm_value = values.get("algorithm", 0)
if field_value is not None and algorithm_value != 3:
# dambreakLevelsAndWidths can only be set when algorithm = 3
raise ValueError(
f"Dambreak field dambreakLevelsAndWidths can only be set when algorithm = 3, current value: {algorithm_value}."
)
return field_value
DambreakAlgorithm (int, Enum)
¶
An enumeration.
FlowDirection (str, Enum)
¶
Enum class containing the valid values for the allowedFlowDirection attribute in several subclasses of Structure.
GateOpeningHorizontalDirection (str, Enum)
¶
Horizontal opening direction of gate door[s].
GeneralStructure (Structure)
pydantic-model
¶
Hydraulic structure with type=generalStructure
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the orifice input as described in UM Sec.C.12.9.
Orientation (str, Enum)
¶
Enum class containing the valid values for the orientation attribute in several subclasses of Structure.
Orifice (Structure)
pydantic-model
¶
Hydraulic structure with type=orifice
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the orifice input as described in UM Sec.C.12.7.
Pump (Structure)
pydantic-model
¶
Hydraulic structure with type=pump
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the pump input as described in UM Sec.C.12.6.
check_list_lengths_head_and_reductionfactor(values)
classmethod
¶
Validates that the lengths of the head and reductionfactor fields are as expected.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def check_list_lengths_head_and_reductionfactor(cls, values):
"""Validates that the lengths of the head and reductionfactor fields are as expected."""
return validate_correct_length(
values,
"head",
"reductionfactor",
length_name="numreductionlevels",
list_required_with_length=True,
)
conditionally_check_list_lengths_deliveryside(values: Dict) -> Dict
classmethod
¶
Validates the length of the delivery side fields, but only if there is a controlside value present in the values and the controlside is not equal to the suctionSide.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def conditionally_check_list_lengths_deliveryside(cls, values: Dict) -> Dict:
"""
Validates the length of the delivery side fields, but only if there is a controlside value
present in the values and the controlside is not equal to the suctionSide.
"""
return validate_conditionally(
cls,
values,
Pump._check_list_lengths_deliveryside,
"controlside",
"suctionSide",
ne,
)
conditionally_check_list_lengths_suctionside(values: Dict) -> Dict
classmethod
¶
Validates the length of the suction side fields, but only if there is a controlside value present in the values and the controlside is not equal to the deliverySide.
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator(allow_reuse=True)
def conditionally_check_list_lengths_suctionside(cls, values: Dict) -> Dict:
"""
Validates the length of the suction side fields, but only if there is a controlside value
present in the values and the controlside is not equal to the deliverySide.
"""
return validate_conditionally(
cls,
values,
Pump._check_list_lengths_suctionside,
"controlside",
"deliverySide",
ne,
)
Structure (INIBasedModel)
pydantic-model
¶
check_location(values: dict) -> dict
classmethod
¶
Validates the location of the structure based on the given parameters. For instance, if a branchid is given, then it is expected also the chainage, otherwise numcoordinates xcoordinates and ycoordinates shall be expected.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
values |
dict |
Dictionary of values validated for the new structure. |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When branchid or chainage values are not valid (empty strings). |
ValueError |
When the number of xcoordinates and ycoordinates do not match numcoordinates. |
Returns:
Type | Description |
---|---|
dict |
Dictionary of values validated for the new structure. |
Source code in hydrolib/core/dflowfm/structure/models.py
@root_validator
@classmethod
def check_location(cls, values: dict) -> dict:
"""
Validates the location of the structure based on the given parameters.
For instance, if a branchid is given, then it is expected also the chainage,
otherwise numcoordinates xcoordinates and ycoordinates shall be expected.
Args:
values (dict): Dictionary of values validated for the new structure.
Raises:
ValueError: When branchid or chainage values are not valid (empty strings).
ValueError: When the number of xcoordinates and ycoordinates do not match numcoordinates.
Returns:
dict: Dictionary of values validated for the new structure.
"""
filtered_values = {k: v for k, v in values.items() if v is not None}
structype = filtered_values.get("type", "").lower()
if structype == "compound" or issubclass(cls, (Compound)):
# Compound structure does not require a location specification.
return values
# Backwards compatibility for old-style polylinefile input field (instead of num/x/yCoordinates):
polyline_compatible_structures = dict(
pump="Pump",
dambreak="Dambreak",
gate="Gate",
weir="Weir",
generalstructure="GeneralStructure",
)
polylinefile_in_model = (
structype in polyline_compatible_structures.keys()
and filtered_values.get("polylinefile") is not None
)
# No branchId+chainage for some structures:
only_coordinates_structures = dict(
longculvert="LongCulvert", dambreak="Dambreak"
)
coordinates_in_model = Structure.validate_coordinates_in_model(filtered_values)
# Error: do not allow both x/y and polyline file:
assert not (
polylinefile_in_model and coordinates_in_model
), f"`Specify location either by `num/x/yCoordinates` or `polylinefile`, but not both."
# Error: require x/y or polyline file:
if (
structype in polyline_compatible_structures.keys()
and structype in only_coordinates_structures.keys()
):
assert (
coordinates_in_model or polylinefile_in_model
), f"Specify location either by setting `num/x/yCoordinates` or `polylinefile` fields for a {polyline_compatible_structures[structype]} structure."
# Error: Some structures require coordinates_in_model, but not branchId and chainage.
if (
not polylinefile_in_model
and structype in only_coordinates_structures.keys()
):
assert (
coordinates_in_model
), f"Specify location by setting `num/x/yCoordinates` for a {only_coordinates_structures[structype]} structure."
# Error: final check: at least one of x/y, branchId+chainage or polyline file must be given
branch_and_chainage_in_model = Structure.validate_branch_and_chainage_in_model(
filtered_values
)
assert (
branch_and_chainage_in_model
or coordinates_in_model
or polylinefile_in_model
), "Specify location either by setting `branchId` and `chainage` or `num/x/yCoordinates` or `polylinefile` fields."
return values
validate(v)
classmethod
¶
Try to initialize subclass based on the type
field.
This field is compared to each type
field of the derived models of Structure
.
The derived model with an equal structure type will be initialized.
Exceptions:
Type | Description |
---|---|
ValueError |
When the given type is not a known structure type. |
Source code in hydrolib/core/dflowfm/structure/models.py
@classmethod
def validate(cls, v):
"""Try to initialize subclass based on the `type` field.
This field is compared to each `type` field of the derived models of `Structure`.
The derived model with an equal structure type will be initialized.
Raises:
ValueError: When the given type is not a known structure type.
"""
# should be replaced by discriminated unions once merged
# https://github.com/samuelcolvin/pydantic/pull/2336
if isinstance(v, dict):
for c in cls.__subclasses__():
if (
c.__fields__.get("type").default.lower()
== v.get("type", "").lower()
):
v = c(**v)
break
else:
raise ValueError(
f"Type of {cls.__name__} with id={v.get('id', '')} and type={v.get('type', '')} is not recognized."
)
return super().validate(v)
validate_branch_and_chainage_in_model(values: dict) -> bool
staticmethod
¶
Static method to validate whether the given branchid and chainage values match the expectation of a new structure.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
values |
dict |
Dictionary of values to be used to generate a structure. |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When the value for branchid or chainage are not valid. |
Returns:
Type | Description |
---|---|
bool |
Result of valid branchid / chainage in dictionary. |
Source code in hydrolib/core/dflowfm/structure/models.py
@staticmethod
def validate_branch_and_chainage_in_model(values: dict) -> bool:
"""
Static method to validate whether the given branchid and chainage values
match the expectation of a new structure.
Args:
values (dict): Dictionary of values to be used to generate a structure.
Raises:
ValueError: When the value for branchid or chainage are not valid.
Returns:
bool: Result of valid branchid / chainage in dictionary.
"""
branchid = values.get("branchid", None)
if branchid is None:
return False
chainage = values.get("chainage", None)
if str_is_empty_or_none(branchid) or chainage is None:
raise ValueError(
"A valid value for branchId and chainage is required when branchId key is specified."
)
return True
validate_coordinates_in_model(values: dict) -> bool
staticmethod
¶
Static method to validate whether the given values match the expectations of a structure to define its coordinates.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
values |
dict |
Dictionary of values to be used to generate a structure. |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
When the given coordinates is less than 2. |
ValueError |
When the given coordinates do not match in expected size. |
Returns:
Type | Description |
---|---|
bool |
Result of valid coordinates in dictionary. |
Source code in hydrolib/core/dflowfm/structure/models.py
@staticmethod
def validate_coordinates_in_model(values: dict) -> bool:
"""
Static method to validate whether the given values match the expectations
of a structure to define its coordinates.
Args:
values (dict): Dictionary of values to be used to generate a structure.
Raises:
ValueError: When the given coordinates is less than 2.
ValueError: When the given coordinates do not match in expected size.
Returns:
bool: Result of valid coordinates in dictionary.
"""
searched_keys = ["numcoordinates", "xcoordinates", "ycoordinates"]
if any(values.get(k, None) is None for k in searched_keys):
return False
n_coords = values["numcoordinates"]
if n_coords < 2:
raise ValueError(
f"Expected at least 2 coordinates, but only {n_coords} declared."
)
def get_coord_len(coord: str) -> int:
if values[coord] is None:
return 0
return len(values[coord])
len_x_coords = get_coord_len("xcoordinates")
len_y_coords = get_coord_len("ycoordinates")
if n_coords == len_x_coords == len_y_coords:
return True
raise ValueError(
f"Expected {n_coords} coordinates, given {len_x_coords} for xCoordinates and {len_y_coords} for yCoordinates."
)
StructureGeneral (INIGeneral)
pydantic-model
¶
[General]
section with structure file metadata.
StructureModel (INIModel)
pydantic-model
¶
The overall structure model that contains the contents of one structure file.
This model is typically referenced under a FMModel.geometry.structurefile[..]
.
Attributes:
Name | Type | Description |
---|---|---|
general |
StructureGeneral |
|
branch |
List[Structure] |
List of |
UniversalWeir (Structure)
pydantic-model
¶
Hydraulic structure with type=universalWeir
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the universal weir input as described in UM Sec.C.12.2.
Weir (Structure)
pydantic-model
¶
Hydraulic structure with type=weir
, to be included in a structure file.
Typically inside the structure list of a FMModel.geometry.structurefile[0].structure[..]
All lowercased attributes match with the weir input as described in UM Sec.C.12.1.