Skip to content

Commit

Permalink
fix: Add water level location validation DamBreaks
Browse files Browse the repository at this point in the history
* #105: Add water level location specification validation for DamBreaks.

* #105: Fix existing failing tests

* #105: Update error message

* #105: Add test cases

* autoformat: isort & black

Co-authored-by: priscavdsluis <priscavdsluis@users.noreply.github.com>

Refs: #105
  • Loading branch information
priscavdsluis committed Jul 7, 2022
1 parent 4dc6fd4 commit bbfe5a6
Show file tree
Hide file tree
Showing 2 changed files with 198 additions and 21 deletions.
29 changes: 28 additions & 1 deletion hydrolib/core/io/structure/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -881,16 +881,43 @@ def check_location(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..
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",
)

if (
Structure.validate_coordinates_in_model(values)
or values.get("polylinefile", None) is not None
Expand Down
190 changes: 170 additions & 20 deletions tests/io/test_structure.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,8 @@ class DambreakTestCases:
check_location_err = (
"`num/x/yCoordinates` or `polylineFile` are mandatory for a Dambreak structure."
)
check_upstream_waterlevel_location_err = "Either `waterLevelUpstreamNodeId` should be specified or `waterLevelUpstreamLocationX` and `waterLevelUpstreamLocationY`."
check_downstream_waterlevel_location_err = "Either `waterLevelDownstreamNodeId` should be specified or `waterLevelDownstreamLocationX` and `waterLevelDownstreamLocationY`."
too_few_coords = "Expected at least 2 coordinates, but only {} declared."
mismatch_coords = (
"Expected {} coordinates, given {} for xCoordinates and {} for yCoordinates."
Expand Down Expand Up @@ -915,10 +917,6 @@ def default_dambreak_values(self) -> dict:
f1=22.4,
f2=44.2,
ucrit=44.22,
waterlevelupstreamlocationx=1.2,
waterlevelupstreamlocationy=2.3,
waterleveldownstreamlocationx=3.4,
waterleveldownstreamlocationy=4.5,
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
)
Expand Down Expand Up @@ -1020,10 +1018,6 @@ def test_given_structure_text_with_num_x_y_coordinates_parses_structure(self):
f1 = 22.4 # Factor f1.
f2 = 44.2 # Factor f2.
uCrit = 44.22 # Critical flow velocity uc for erosion [m/s].
waterLevelUpstreamLocationX = 1.2 # x-coordinate of custom upstream water level point.
waterLevelUpstreamLocationY = 2.3 # y-coordinate of custom upstream water level point.
waterLevelDownstreamLocationX = 3.4 # x-coordinate of custom downstream water level point.
waterLevelDownstreamLocationY = 4.5 # y-coordinate of custom downstream water level point.
waterLevelUpstreamNodeId = anUpstreamNodeId # Node Id of custom upstream water level point.
waterLevelDownstreamNodeId = aDownstreamNodeId # Node Id of custom downstream water level point.
"""
Expand Down Expand Up @@ -1052,6 +1046,10 @@ def test_given_structure_text_with_polylinefile_parses_structure(self):
t0 = 0.0001 # make it a boolean
dambreakLevelsAndWidths = dambreak.tim #used only in algorithm 3
materialtype = 1 #1 clay 2 sand, used only in algorithm 1
waterLevelUpstreamLocationX = 1.2 # x-coordinate of custom upstream water level point.
waterLevelUpstreamLocationY = 2.3 # y-coordinate of custom upstream water level point.
waterLevelDownstreamLocationX = 3.4 # x-coordinate of custom downstream water level point.
waterLevelDownstreamLocationY = 4.5 # y-coordinate of custom downstream water level point.
"""
)

Expand All @@ -1074,6 +1072,12 @@ def test_given_structure_text_with_polylinefile_parses_structure(self):
assert dambreak_obj.t0 == 0.0001
assert dambreak_obj.dambreaklevelsandwidths == Path("dambreak.tim")
assert dambreak_obj.dict()["materialtype"] == "1"
assert dambreak_obj.waterlevelupstreamlocationx == 1.2
assert dambreak_obj.waterlevelupstreamlocationy == 2.3
assert dambreak_obj.waterleveldownstreamlocationx == 3.4
assert dambreak_obj.waterleveldownstreamlocationy == 4.5
assert dambreak_obj.waterlevelupstreamnodeid == None
assert dambreak_obj.waterleveldownstreamnodeid == None

def parse_dambreak_from_text(self, structure_text: str) -> Dambreak:
"""
Expand Down Expand Up @@ -1121,10 +1125,10 @@ def validate_valid_default_dambreak(self, dambreak: Dambreak):
assert dambreak.numcoordinates == 2
assert dambreak.xcoordinates == [4.2, 2.4]
assert dambreak.ycoordinates == [2.4, 4.2]
assert dambreak.waterlevelupstreamlocationx == 1.2
assert dambreak.waterlevelupstreamlocationy == 2.3
assert dambreak.waterleveldownstreamlocationx == 3.4
assert dambreak.waterleveldownstreamlocationy == 4.5
assert dambreak.waterlevelupstreamlocationx == None
assert dambreak.waterlevelupstreamlocationy == None
assert dambreak.waterleveldownstreamlocationx == None
assert dambreak.waterleveldownstreamlocationy == None
assert dambreak.waterlevelupstreamnodeid == "anUpstreamNodeId"
assert dambreak.waterleveldownstreamnodeid == "aDownstreamNodeId"

Expand Down Expand Up @@ -1221,18 +1225,44 @@ class TestCheckLocation:
"dict_values",
[
pytest.param(
dict(numcoordinates=2, xcoordinates=[0, 1], ycoordinates=[2, 3]),
id="With 2 coordinates",
dict(
numcoordinates=2,
xcoordinates=[0, 1],
ycoordinates=[2, 3],
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
),
id="With 2 coordinates and waterlevelupstreamnodeid and waterleveldownstreamnodeid",
),
pytest.param(
dict(
numcoordinates=3,
xcoordinates=[0, 1, 2],
ycoordinates=[2, 3, 4],
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamlocationx=3.4,
waterleveldownstreamlocationy=4.5,
),
id="With 3 coordinates and waterlevelupstreamnodeid and waterleveldownstreamlocationx and waterleveldownstreamlocationy",
),
pytest.param(
dict(
numcoordinates=3, xcoordinates=[0, 1, 2], ycoordinates=[2, 3, 4]
polylinefile=Path(),
waterlevelupstreamlocationx=1.2,
waterlevelupstreamlocationy=2.3,
waterleveldownstreamnodeid="aDownstreamNodeId",
),
id="With 3 coordinates",
id="Empty path and waterlevelupstreamlocationx and waterlevelupstreamlocationy and waterleveldownstreamnodeid",
),
pytest.param(dict(polylinefile=Path()), id="Empty path"),
pytest.param(
dict(polylinefile=Path("aFilePath")), id="Path with file name"
dict(
polylinefile=Path("aFilePath"),
waterlevelupstreamlocationx=1.2,
waterlevelupstreamlocationy=2.3,
waterleveldownstreamlocationx=3.4,
waterleveldownstreamlocationy=4.5,
),
id="Path with file name and waterlevelupstreamlocationx and waterlevelupstreamlocationy and waterleveldownstreamlocationx and waterleveldownstreamlocationy",
),
],
)
Expand All @@ -1244,17 +1274,137 @@ def test_given_valid_values_returns_values(self, dict_values: dict):
"invalid_values, expected_err",
[
pytest.param(
dict(), DambreakTestCases.check_location_err, id="Empty dict."
dict(
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
),
DambreakTestCases.check_location_err,
id="No locations specified",
),
pytest.param(
dict(
numcoordinates=None,
xcoordinates=None,
ycoordinates=None,
polylinefile=None,
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
),
DambreakTestCases.check_location_err,
id="Dict with Nones.",
id="Specified locations None",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="No upstream water level locations specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
waterlevelupstreamlocationx=1.2,
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="Only upstream water level location x specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
waterlevelupstreamlocationy=2.3,
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="Only upstream water level location y specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
waterlevelupstreamnodeid="anUpstreamNodeId",
waterlevelupstreamlocationx=1.2,
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="Upstream water level location node id and x specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
waterlevelupstreamnodeid="anUpstreamNodeId",
waterlevelupstreamlocationy=2.3,
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="Upstream water level location node id and y specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterleveldownstreamnodeid="aDownstreamNodeId",
waterlevelupstreamnodeid="anUpstreamNodeId",
waterlevelupstreamlocationx=1.2,
waterlevelupstreamlocationy=2.3,
),
DambreakTestCases.check_upstream_waterlevel_location_err,
id="Upstream water level location node id, x and y specified",
),
pytest.param(
dict(
polylinefile=Path(), waterlevelupstreamnodeid="anUpstreamNodeId"
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="No downstream water level locations specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamlocationx=3.4,
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="Only downstream water level location x specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamlocationy=4.5,
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="Only downstream water level location y specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
waterleveldownstreamlocationx=3.4,
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="Downstream water level location node id and x specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
waterleveldownstreamlocationy=4.5,
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="Downstream water level location node id and y specified",
),
pytest.param(
dict(
polylinefile=Path(),
waterlevelupstreamnodeid="anUpstreamNodeId",
waterleveldownstreamnodeid="aDownstreamNodeId",
waterleveldownstreamlocationx=3.4,
waterleveldownstreamlocationy=4.5,
),
DambreakTestCases.check_downstream_waterlevel_location_err,
id="Downstream water level location node id, x and y specified",
),
],
)
Expand Down

0 comments on commit bbfe5a6

Please sign in to comment.