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): 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") _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType) def _get_identifier(self, data: dict) -> Optional[str]: return data.get("frictionid")
class Bridge(Structure): """ Hydraulic structure with `type=bridge`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the bridge input as described in [UM Sec.C.12.5](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.5). """ class Comments(Structure.Comments): type: Optional[str] = Field("Structure type; must read bridge", alias="type") allowedflowdir: Optional[str] = Field( FlowDirection.allowedvaluestext, alias="allowedFlowdir" ) csdefid: Optional[str] = Field( "Id of Cross-Section Definition.", alias="csDefId" ) shift: Optional[str] = Field( "Vertical shift of the cross section definition [m]. Defined positive upwards." ) inletlosscoeff: Optional[str] = Field( "Inlet loss coefficient [-], ξ_i.", alias="inletLossCoeff", ) outletlosscoeff: Optional[str] = Field( "Outlet loss coefficient [-], k.", alias="outletLossCoeff", ) frictiontype: Optional[str] = Field( "Friction type, possible values are: Chezy, Manning, wallLawNikuradse, WhiteColebrook, StricklerNikuradse, Strickler, deBosBijkerk.", alias="frictionType", ) friction: Optional[str] = Field( "Friction value, used in friction loss.", alias="friction", ) length: Optional[str] = Field("Length [m], L.") comments: Comments = Comments() type: Literal["bridge"] = Field("bridge", alias="type") allowedflowdir: FlowDirection = Field(alias="allowedFlowdir") csdefid: str = Field(alias="csDefId") shift: float inletlosscoeff: float = Field(alias="inletLossCoeff") outletlosscoeff: float = Field(alias="outletLossCoeff") frictiontype: FrictionType = Field(alias="frictionType") friction: float length: float _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType)
class Orifice(Structure): """ Hydraulic structure with `type=orifice`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the orifice input as described in [UM Sec.C.12.7](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.7). """ type: Literal["orifice"] = Field("orifice", alias="type") allowedflowdir: Optional[FlowDirection] = Field( FlowDirection.both.value, alias="allowedFlowDir" ) crestlevel: Union[float, Path] = Field(alias="crestLevel") crestwidth: Optional[float] = Field(None, alias="crestWidth") gateloweredgelevel: Union[float, Path] = Field(alias="gateLowerEdgeLevel") corrcoeff: float = Field(1.0, alias="corrCoeff") usevelocityheight: bool = Field(True, alias="useVelocityHeight") # TODO Use a validator here to check the optionals related to the bool field uselimitflowpos: Optional[bool] = Field(False, alias="useLimitFlowPos") limitflowpos: Optional[float] = Field(alias="limitFlowPos") uselimitflowneg: Optional[bool] = Field(False, alias="useLimitFlowNeg") limitflowneg: Optional[float] = Field(alias="limitFlowNeg") _flowdirection_validator = get_enum_validator("allowedflowdir", enum=FlowDirection) @validator("limitflowpos", always=True) @classmethod def _validate_limitflowpos(cls, v, values): return cls._validate_limitflow(v, values, "limitFlowPos", "useLimitFlowPos") @validator("limitflowneg", always=True) @classmethod def _validate_limitflowneg(cls, v, values): return cls._validate_limitflow(v, values, "limitFlowNeg", "useLimitFlowNeg") @classmethod def _validate_limitflow(cls, v, values, limitflow: str, uselimitflow: str): if v is None and values[uselimitflow.lower()] == True: raise ValueError( f"{limitflow} should be defined when {uselimitflow} is true" ) return v
class UniversalWeir(Structure): """ Hydraulic structure with `type=universalWeir`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the universal weir input as described in [UM Sec.C.12.2](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.2). """ class Comments(Structure.Comments): type: Optional[str] = Field( "Structure type; must read universalWeir", alias="type" ) allowedflowdir: Optional[str] = Field( FlowDirection.allowedvaluestext, alias="allowedFlowdir" ) numlevels: Optional[str] = Field("Number of yz-Values.", alias="numLevels") yvalues: Optional[str] = Field( "y-values of the cross section (m). (number of values = numLevels)", alias="yValues", ) zvalues: Optional[str] = Field( "z-values of the cross section (m). (number of values = numLevels)", alias="zValues", ) crestlevel: Optional[str] = Field( "Crest level of weir (m AD).", alias="crestLevel" ) dischargecoeff: Optional[str] = Field( "Discharge coefficient c_e (-).", alias="dischargeCoeff" ) comments: Comments = Comments() type: Literal["universalWeir"] = Field("universalWeir", alias="type") allowedflowdir: FlowDirection = Field(alias="allowedFlowDir") numlevels: int = Field(alias="numLevels") yvalues: List[float] = Field(alias="yValues") zvalues: List[float] = Field(alias="zValues") crestlevel: float = Field(alias="crestLevel") dischargecoeff: float = Field(alias="dischargeCoeff") _split_to_list = get_split_string_on_delimiter_validator("yvalues", "zvalues") _flowdirection_validator = get_enum_validator("allowedflowdir", enum=FlowDirection)
class RectangleCrsDef(CrossSectionDefinition): """ Crosssection definition with `type=rectangle`, to be included in a crossdef file. Typically inside the definition list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.crossdeffile.definition[..]` All lowercased attributes match with the rectangle input as described in [UM Sec.C.16.1.2](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.16.1.2). """ class Comments(CrossSectionDefinition.Comments): type: Optional[str] = Field("Cross section type; must read rectangle") width: Optional[str] = Field("Width of the rectangle [m].") height: Optional[str] = Field("Height of the rectangle [m].") closed: Optional[str] = Field("no: Open channel, yes: Closed channel.") frictionid: Optional[str] = Field( frictionid_description, alias="frictionId", ) frictiontype: Optional[str] = Field( frictiontype_description, alias="frictionType", ) frictionvalue: Optional[str] = Field( frictionvalue_description, alias="frictionValue", ) comments: Comments = Comments() type: Literal["rectangle"] = Field("rectangle") width: float height: float closed: bool = Field(True) frictionid: Optional[str] = Field(alias="frictionId") frictiontype: Optional[FrictionType] = Field(alias="frictionType") frictionvalue: Optional[float] = Field(alias="frictionValue") _friction_validator = CrossSectionDefinition._get_friction_root_validator( "frictionid", "frictiontype", "frictionvalue" ) _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType)
class Weir(Structure): """ Hydraulic structure with `type=weir`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the weir input as described in [UM Sec.C.12.1](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.1). """ class Comments(Structure.Comments): type: Optional[str] = Field("Structure type; must read weir", alias="type") allowedflowdir: Optional[str] = Field( FlowDirection.allowedvaluestext, alias="allowedFlowdir" ) crestlevel: Optional[str] = Field( "Crest level of weir (m AD).", alias="crestLevel" ) crestwidth: Optional[str] = Field("Width of the weir (m).", alias="crestWidth") corrcoeff: Optional[str] = Field( "Correction coefficient (-).", alias="corrCoeff" ) usevelocityheight: Optional[str] = Field( "Flag indicating whether the velocity height is to be calculated or not.", alias="useVelocityHeight", ) comments: Comments = Comments() type: Literal["weir"] = Field("weir", alias="type") allowedflowdir: Optional[FlowDirection] = Field( FlowDirection.both.value, alias="allowedFlowDir" ) crestlevel: Union[float, Path] = Field(alias="crestLevel") crestwidth: Optional[float] = Field(None, alias="crestWidth") corrcoeff: float = Field(1.0, alias="corrCoeff") usevelocityheight: bool = Field(True, alias="useVelocityHeight") _flowdirection_validator = get_enum_validator("allowedflowdir", enum=FlowDirection)
class CircleCrsDef(CrossSectionDefinition): """ Crosssection definition with `type=circle`, to be included in a crossdef file. Typically inside the definition list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.crossdeffile.definition[..]` All lowercased attributes match with the circle input as described in [UM Sec.C.16.1.1](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.16.1.1). """ class Comments(CrossSectionDefinition.Comments): type: Optional[str] = Field("Cross section type; must read circle") diameter: Optional[str] = Field("Internal diameter of the circle [m].") frictionid: Optional[str] = Field( frictionid_description, alias="frictionId", ) frictiontype: Optional[str] = Field( frictiontype_description, alias="frictionType", ) frictionvalue: Optional[str] = Field( frictionvalue_description, alias="frictionValue", ) comments: Comments = Comments() type: Literal["circle"] = Field("circle") diameter: float frictionid: Optional[str] = Field(alias="frictionId") frictiontype: Optional[FrictionType] = Field(alias="frictionType") frictionvalue: Optional[float] = Field(alias="frictionValue") _friction_validator = CrossSectionDefinition._get_friction_root_validator( "frictionid", "frictiontype", "frictionvalue" ) _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType)
class Pump(Structure): """ Hydraulic structure with `type=pump`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the pump input as described in [UM Sec.C.12.6](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.6). """ type: Literal["pump"] = Field("pump", alias="type") orientation: Optional[Orientation] = Field(alias="orientation") controlside: Optional[str] = Field(alias="controlSide") # TODO Enum numstages: Optional[int] = Field(alias="numStages") capacity: Union[float, Path] = Field(alias="capacity") startlevelsuctionside: Optional[List[float]] = Field(alias="startLevelSuctionSide") stoplevelsuctionside: Optional[List[float]] = Field(alias="stopLevelSuctionSide") startleveldeliveryside: Optional[List[float]] = Field( alias="startLevelDeliverySide" ) stopleveldeliveryside: Optional[List[float]] = Field(alias="stopLevelDeliverySide") numreductionlevels: Optional[int] = Field(alias="numReductionLevels") head: Optional[List[float]] = Field(alias="head") reductionfactor: Optional[List[float]] = Field(alias="reductionFactor") _split_to_list = get_split_string_on_delimiter_validator( "startlevelsuctionside", "stoplevelsuctionside", "startleveldeliveryside", "stopleveldeliveryside", "head", "reductionfactor", ) _orientation_validator = get_enum_validator("orientation", enum=Orientation) _control_side_check = get_required_fields_validator( "controlside", conditional_field_name="numstages", conditional_value=0, comparison_func=gt, ) _check_list_length1 = get_conditional_root_validator( make_list_length_root_validator( "startlevelsuctionside", "stoplevelsuctionside", length_name="numstages", list_required_with_length=True, ), "controlside", "deliverySide", ne, ) _check_list_length2 = get_conditional_root_validator( make_list_length_root_validator( "startleveldeliveryside", "stopleveldeliveryside", length_name="numstages", list_required_with_length=True, ), "controlside", "suctionSide", ne, ) _check_list_length3 = make_list_length_root_validator( "head", "reductionfactor", length_name="numreductionlevels", list_required_with_length=True, )
class Culvert(Structure): """ Hydraulic structure with `type=culvert`, to be included in a structure file. Typically inside the structure list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.structurefile[0].structure[..]` All lowercased attributes match with the culvert input as described in [UM Sec.C.12.3](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.12.3). """ type: Literal["culvert"] = Field("culvert", alias="type") allowedflowdir: FlowDirection = Field(alias="allowedFlowDir") leftlevel: float = Field(alias="leftLevel") rightlevel: float = Field(alias="rightLevel") csdefid: str = Field(alias="csDefId") length: float = Field(alias="length") inletlosscoeff: float = Field(alias="inletLossCoeff") outletlosscoeff: float = Field(alias="outletLossCoeff") valveonoff: bool = Field(alias="valveOnOff") valveopeningheight: Optional[Union[float, Path]] = Field(alias="valveOpeningHeight") numlosscoeff: Optional[int] = Field(alias="numLossCoeff") relopening: Optional[List[float]] = Field(alias="relOpening") losscoeff: Optional[List[float]] = Field(alias="lossCoeff") bedfrictiontype: Optional[FrictionType] = Field(alias="bedFrictionType") bedfriction: Optional[float] = Field(alias="bedFriction") subtype: Optional[CulvertSubType] = Field( CulvertSubType.culvert.value, alias="subType" ) bendlosscoeff: Optional[float] = Field(alias="bendLossCoeff") _split_to_list = get_split_string_on_delimiter_validator("relopening", "losscoeff") _flowdirection_validator = get_enum_validator("allowedflowdir", enum=FlowDirection) _subtype_validator = get_enum_validator("subtype", enum=CulvertSubType) _frictiontype_validator = get_enum_validator("bedfrictiontype", enum=FrictionType) _valveonoff_validator = get_required_fields_validator( "valveopeningheight", "numlosscoeff", "relopening", "losscoeff", conditional_field_name="valveonoff", conditional_value=True, ) _check_list_length = make_list_length_root_validator( "relopening", "losscoeff", length_name="numlosscoeff", list_required_with_length=True, ) _bendlosscoeff_invertedsiphon = get_required_fields_validator( "bendlosscoeff", conditional_field_name="subtype", conditional_value=CulvertSubType.invertedSiphon, ) _bendlosscoeff_culvert = get_forbidden_fields_validator( "bendlosscoeff", conditional_field_name="subtype", conditional_value=CulvertSubType.culvert, )
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/delft3d/manuals/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") _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", ) _check_list_length = make_list_length_root_validator( "levels", "storagearea", length_name="numlevels", list_required_with_length=True, ) _usetable_true_validator = get_required_fields_validator( "numlevels", "levels", "storagearea", conditional_field_name="usetable", conditional_value=True, ) _usetable_false_validator = get_required_fields_validator( "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")
class YZCrsDef(CrossSectionDefinition): """ Crosssection definition with `type=yz`, to be included in a crossdef file. Typically inside the definition list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.crossdeffile.definition[..]` All lowercased attributes match with the yz input as described in [UM Sec.C.16.1.6](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.16.1.6). """ class Comments(CrossSectionDefinition.Comments): type: Optional[str] = Field("Cross section type; must read yz", alias="type") conveyance: Optional[str] = Field( "lumped: Lumped, segmented: Vertically segmented. Only the default lumped " + "option is allowed if singleValuedZ = no. In the case of lumped conveyance, " + "only a single uniform roughness for the whole cross section is allowed, " + "i.e., sectionCount must equal 1.", ) yzcount: Optional[str] = Field("Number of YZ-coordinates.", alias="yzCount") yCoordinates: Optional[str] = Field( "Space separated list of monotonic increasing y-coordinates [m].", alias="yCoordinates", ) zCoordinates: Optional[str] = Field( "Space separated list of single-valued z-coordinates [m AD].", alias="zCoordinates", ) sectioncount: Optional[str] = Field( "Number of roughness sections. If the lumped conveyance is selected then " + "sectionCount must equal 1.", alias="sectionCount", ) frictionpositions: Optional[str] = Field( "Locations where the roughness sections start and end. Always one location more than " + "sectionCount. The first value should equal 0 and the last value should equal the " + "cross section length. Keyword may be skipped if sectionCount = 1.", alias="frictionPositions", ) frictionids: Optional[str] = Field( "Semicolon separated list of roughness variable names associated with the roughness " + "sections. Either this parameter or frictionTypes should be specified. If neither " + 'parameter is specified, the frictionIds default to "Main", "FloodPlain1" ' + 'and "FloodPlain2".', alias="frictionIds", ) frictiontypes: Optional[str] = Field( "Semicolon separated list of roughness types associated with the roughness sections. " + "Either this parameter or frictionIds should be specified. Can be specified as a " + "single value if all roughness sections use the same type.", alias="frictionTypes", ) frictionvalues: Optional[str] = Field( "Space separated list of roughness values; their meaning depends on the roughness " + "types selected (only used if frictionTypes specified).", alias="frictionValues", ) comments: Comments = Comments() type: Literal["yz"] = Field("yz") singlevaluedz: Optional[bool] = Field(alias="singleValuedZ") yzcount: int = Field(alias="yzCount") ycoordinates: List[float] = Field(alias="yCoordinates") zcoordinates: List[float] = Field(alias="zCoordinates") conveyance: Optional[str] = Field("segmented") sectioncount: Optional[int] = Field(1, alias="sectionCount") frictionpositions: Optional[List[float]] = Field(alias="frictionPositions") frictionids: Optional[List[str]] = Field(alias="frictionIds", delimiter=";") frictiontypes: Optional[List[FrictionType]] = Field( alias="frictionTypes", delimiter=";" ) frictionvalues: Optional[List[float]] = Field(alias="frictionValues") _split_to_list = get_split_string_on_delimiter_validator( "ycoordinates", "zcoordinates", "frictionpositions", "frictionvalues", "frictionids", "frictiontypes", ) _check_yzlist_length = make_list_length_root_validator( "ycoordinates", "zcoordinates", length_name="yzcount", ) _check_frictlist_length = make_list_length_root_validator( "frictionids", "frictiontypes", "frictionvalues", length_name="sectioncount", ) _check_frictlist_length1 = make_list_length_root_validator( "frictionpositions", length_name="sectioncount", length_incr=1, # 1 extra for frictionpositions ) _friction_validator = CrossSectionDefinition._get_friction_root_validator( "frictionids", "frictiontypes", "frictionvalues" ) _frictiontype_validator = get_enum_validator("frictiontypes", enum=FrictionType)
class ZWCrsDef(CrossSectionDefinition): """ Crosssection definition with `type=zw`, to be included in a crossdef file. Typically inside the definition list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.crossdeffile.definition[..]` All lowercased attributes match with the zw input as described in [UM Sec.C.16.1.4](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.16.1.4). """ class Comments(CrossSectionDefinition.Comments): type: Optional[str] = Field("Cross section type; must read zw", alias="type") # NOTE: Field "template" deliberately ignored for now. numlevels: Optional[str] = Field( "Number of levels in the table.", alias="numLevels" ) levels: Optional[str] = Field( "Space separated list of monotonic increasing heights/levels [m AD].", alias="levels", ) flowwidths: Optional[str] = Field( "Space separated list of flow widths at the selected heights [m)].", alias="flowWidths", ) totalwidths: Optional[str] = Field( "Space separated list of total widths at the selected heights [m]. " + "Equal to flowWidths if not specified. If specified, the totalWidths" + "should be larger than flowWidths.", alias="totalWidths", ) frictionid: Optional[str] = Field( frictionid_description, alias="frictionId", ) frictiontype: Optional[str] = Field( frictiontype_description, alias="frictionType", ) frictionvalue: Optional[str] = Field( frictionvalue_description, alias="frictionValue", ) comments: Comments = Comments() type: Literal["zw"] = Field("zw") numlevels: int = Field(alias="numLevels") levels: List[float] flowwidths: List[float] = Field(alias="flowWidths") totalwidths: Optional[List[float]] = Field(alias="totalWidths") frictionid: Optional[str] = Field(alias="frictionId") frictiontype: Optional[FrictionType] = Field(alias="frictionType") frictionvalue: Optional[float] = Field(alias="frictionValue") _split_to_list = get_split_string_on_delimiter_validator( "levels", "flowwidths", "totalwidths", ) _check_list_length = make_list_length_root_validator( "levels", "flowwidths", "totalwidths", length_name="numlevels", ) _friction_validator = CrossSectionDefinition._get_friction_root_validator( "frictionid", "frictiontype", "frictionvalue" ) _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType)
class ZWRiverCrsDef(CrossSectionDefinition): """ Crosssection definition with `type=zwRiver`, to be included in a crossdef file. Typically inside the definition list of a [FMModel][hydrolib.core.io.mdu.models.FMModel]`.geometry.crossdeffile.definition[..]` All lowercased attributes match with the zwRiver input as described in [UM Sec.C.16.1.3](https://content.oss.deltares.nl/delft3d/manuals/D-Flow_FM_User_Manual_1D2D.pdf#subsection.C.16.1.3). """ class Comments(CrossSectionDefinition.Comments): type: Optional[str] = Field( "Cross section type; must read zwRiver", alias="type" ) numlevels: Optional[str] = Field( "Number of levels in the table.", alias="numLevels" ) levels: Optional[str] = Field( "Space separated list of monotonic increasing heights/levels [m AD].", alias="levels", ) flowwidths: Optional[str] = Field( "Space separated list of flow widths at the selected heights [m)].", alias="flowWidths", ) totalwidths: Optional[str] = Field( "Space separated list of total widths at the selected heights [m]. " + "Equal to flowWidths if not specified. If specified, the totalWidths" + "should be larger than flowWidths.", alias="totalWidths", ) leveecrestLevel: Optional[str] = Field( "Crest level of levee [m AD].", alias="leveeCrestlevel" ) leveebaselevel: Optional[str] = Field( "Base level of levee [m AD].", alias="leveeBaseLevel" ) leveeflowarea: Optional[str] = Field( "Flow area behind levee [m2].", alias="leveeFlowArea" ) leveetotalarea: Optional[str] = Field( "Total area behind levee [m2].", alias="leveeTotalArea" ) mainwidth: Optional[str] = Field( "Width of main section [m]. Default value: max(flowWidths).", alias="mainWidth", ) fp1width: Optional[str] = Field( "Width of floodplain 1 section [m]. Default value: max(flowWidths)-mainWidth", alias="fp1Width", ) fp2width: Optional[str] = Field( "Width of floodplain 2 section [m]. Default value: max(flowWidths)-mainWidth-fp1Width", alias="fp2Width", ) frictionids: Optional[str] = Field( "Semicolon separated list of roughness variable names associated with the roughness " + "sections. Either this parameter or frictionTypes should be specified. If neither " + 'parameter is specified, the frictionIds default to "Main", "FloodPlain1" ' + 'and "FloodPlain2".', alias="frictionIds", ) frictiontypes: Optional[str] = Field( "Semicolon separated list of roughness types associated with the roughness sections. " + "Either this parameter or frictionIds should be specified. Can be specified as a " + "single value if all roughness sections use the same type.", alias="frictionTypes", ) frictionvalues: Optional[str] = Field( "Space separated list of roughness values; their meaning depends on the roughness " + "types selected (only used if frictionTypes specified).", alias="frictionValues", ) comments: Comments = Comments() type: Literal["zwRiver"] = Field("zwRiver") numlevels: int = Field(alias="numLevels") levels: List[float] flowwidths: List[float] = Field(alias="flowWidths") totalwidths: Optional[List[float]] = Field(alias="totalWidths") leveecrestLevel: Optional[float] = Field(alias="leveeCrestlevel") leveebaselevel: Optional[float] = Field(alias="leveeBaseLevel") leveeflowarea: Optional[float] = Field(alias="leveeFlowArea") leveetotalrea: Optional[float] = Field(alias="leveeTotalArea") mainwidth: Optional[float] = Field(alias="mainWidth") fp1width: Optional[float] = Field(alias="fp1Width") fp2width: Optional[float] = Field(alias="fp2Width") frictionids: Optional[List[str]] = Field(alias="frictionIds", delimiter=";") frictiontypes: Optional[List[FrictionType]] = Field( alias="frictionTypes", delimiter=";" ) frictionvalues: Optional[List[float]] = Field(alias="frictionValues") _split_to_list = get_split_string_on_delimiter_validator( "levels", "flowwidths", "totalwidths", "frictionvalues", "frictionids", "frictiontypes", ) _friction_validator = CrossSectionDefinition._get_friction_root_validator( "frictionids", "frictiontypes", "frictionvalues" ) _frictiontype_validator = get_enum_validator("frictiontypes", enum=FrictionType) _check_list_length = make_list_length_root_validator( "levels", "flowwidths", "totalwidths", length_name="numlevels", )
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): 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(alias="timeSeriesId") numlevels: Optional[PositiveInt] = Field(alias="numLevels") levels: Optional[List[float]] numlocations: Optional[NonNegativeInt] = Field(0, alias="numLocations") chainage: Optional[List[float]] frictionvalues: Optional[List[float]] = Field( alias="frictionValues" ) # TODO: turn this into List[List[float]], see issue #143. _split_to_list = get_split_string_on_delimiter_validator( "levels", "chainage", "frictionvalues", ) _frictiontype_validator = get_enum_validator("frictiontype", enum=FrictionType) def _get_identifier(self, data: dict) -> Optional[str]: return data.get("branchid") @validator("levels", always=True) @classmethod def _validate_levels(cls, v, values): if v is not None and (values["numlevels"] is None or len(v) != values["numlevels"]): raise ValueError( f"Number of values for levels should be equal to the numLevels value (branchId={values.get('branchid', '')})." ) return v @validator("chainage", always=True) @classmethod def _validate_chainage(cls, v, values): if v is not None and len(v) != values["numlocations"]: raise ValueError( f"Number of values for chainage should be equal to the numLocations value (branchId={values.get('branchid', '')})." ) return v @validator("frictionvalues", always=True) @classmethod def _validate_frictionvalues(cls, v, values): # number of values should be equal to numlocations*numlevels numlevels = (1 if ("numlevels" not in values or values["numlevels"] is None or values["numlevels"] == 0) else values["numlevels"]) numvals = max(1, values["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={values.get('branchid', '')})." ) return v
class AbstractSpatialField(INIBasedModel, ABC): """ Abstract base class for `[Initial]` and `[Parameter]` block data in inifield files. Defines all common fields. Used via subclasses InitialField and ParameterField. """ class Comments(INIBasedModel.Comments): quantity: Optional[str] = Field( "Name of the quantity. See UM Table D.2.", alias="quantity") datafile: Optional[str] = Field( "Name of file containing field data values.", alias="dataFile") datafiletype: Optional[str] = Field("Type of dataFile.", alias="dataFileType") interpolationmethod: Optional[str] = Field( "Type of (spatial) interpolation.", alias="interpolationmethod") operand: Optional[str] = Field( "How this data is combined with previous data for the same quantity (if any).", alias="operand", ) averagingtype: Optional[str] = Field( "Type of averaging, if interpolationMethod=averaging .", alias="averagingtype", ) averagingrelsize: Optional[str] = Field( "Relative search cell size for averaging.", alias="averagingrelsize") averagingnummin: Optional[str] = Field( "Minimum number of points in averaging. Must be ≥ 1.", alias="averagingnummin", ) averagingpercentile: Optional[str] = Field( "Percentile value for which data values to include in averaging. 0.0 means off.", alias="averagingpercentile", ) extrapolationmethod: Optional[str] = Field( "Option for (spatial) extrapolation.", alias="extrapolationmethod") locationtype: Optional[str] = Field( "Target location of interpolation.", alias="locationtype") value: Optional[str] = Field( "Only for dataFileType=polygon. The constant value to be set inside for all model points inside the polygon." ) comments: Comments = Comments() quantity: str = Field(alias="quantity") datafile: Path = Field(alias="dataFile") datafiletype: DataFileType = Field(alias="dataFileType") interpolationmethod: Optional[InterpolationMethod] = Field( alias="interpolationMethod") operand: Optional[Operand] = Field(Operand.override.value, alias="operand") averagingtype: Optional[AveragingType] = Field(AveragingType.mean.value, alias="averagingType") averagingrelsize: Optional[NonNegativeFloat] = Field( 1.01, alias="averagingRelSize") averagingnummin: Optional[PositiveInt] = Field(1, alias="averagingNumMin") averagingpercentile: Optional[NonNegativeFloat] = Field( 0, alias="averagingPercentile") extrapolationmethod: Optional[bool] = Field(False, alias="extrapolationMethod") locationtype: Optional[LocationType] = Field(LocationType.all.value, alias="locationType") value: Optional[float] = Field(alias="value") datafiletype_validator = get_enum_validator("datafiletype", enum=DataFileType) interpolationmethod_validator = get_enum_validator( "interpolationmethod", enum=InterpolationMethod) operand_validator = get_enum_validator("operand", enum=Operand) averagingtype_validator = get_enum_validator("averagingtype", enum=AveragingType) locationtype_validator = get_enum_validator("locationtype", enum=LocationType) _value_validator = get_required_fields_validator( "value", conditional_field_name="datafiletype", conditional_value=DataFileType.polygon, ) @validator("value", always=True) @classmethod def _validate_value_and_filetype(cls, v, values: dict): if v is not None and values.get( "datafiletype") != DataFileType.polygon: raise ValueError( f"When value={v} is given, dataFileType={DataFileType.polygon} is required." ) return v