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.