floodmodeller-api 0.5.0.post1__py3-none-any.whl → 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- floodmodeller_api/__init__.py +11 -1
- floodmodeller_api/_base.py +55 -36
- floodmodeller_api/backup.py +15 -12
- floodmodeller_api/dat.py +191 -121
- floodmodeller_api/diff.py +4 -4
- floodmodeller_api/hydrology_plus/hydrology_plus_export.py +15 -14
- floodmodeller_api/ied.py +8 -10
- floodmodeller_api/ief.py +56 -42
- floodmodeller_api/ief_flags.py +1 -1
- floodmodeller_api/inp.py +7 -10
- floodmodeller_api/logs/lf.py +25 -26
- floodmodeller_api/logs/lf_helpers.py +20 -20
- floodmodeller_api/logs/lf_params.py +1 -5
- floodmodeller_api/mapping.py +11 -2
- floodmodeller_api/test/__init__.py +2 -2
- floodmodeller_api/test/conftest.py +2 -3
- floodmodeller_api/test/test_backup.py +2 -2
- floodmodeller_api/test/test_conveyance.py +13 -7
- floodmodeller_api/test/test_dat.py +168 -20
- floodmodeller_api/test/test_data/EX18_DAT_expected.json +164 -144
- floodmodeller_api/test/test_data/EX3_DAT_expected.json +6 -2
- floodmodeller_api/test/test_data/EX6_DAT_expected.json +12 -46
- floodmodeller_api/test/test_data/encoding_test_cp1252.dat +1081 -0
- floodmodeller_api/test/test_data/encoding_test_utf8.dat +1081 -0
- floodmodeller_api/test/test_data/integrated_bridge/AR_NoSP_NoBl_2O_NO_OneFRC.ied +33 -0
- floodmodeller_api/test/test_data/integrated_bridge/AR_vSP_25pc_1O.ied +32 -0
- floodmodeller_api/test/test_data/integrated_bridge/PL_vSP_25pc_1O.ied +34 -0
- floodmodeller_api/test/test_data/integrated_bridge/SBTwoFRCsStaggered.IED +32 -0
- floodmodeller_api/test/test_data/integrated_bridge/US_NoSP_NoBl_OR_RN.ied +28 -0
- floodmodeller_api/test/test_data/integrated_bridge/US_SP_NoBl_OR_frc_PT2-5_RN.ied +34 -0
- floodmodeller_api/test/test_data/integrated_bridge/US_fSP_NoBl_1O.ied +30 -0
- floodmodeller_api/test/test_data/integrated_bridge/US_nSP_NoBl_1O.ied +49 -0
- floodmodeller_api/test/test_data/integrated_bridge/US_vSP_NoBl_2O_Para.ied +35 -0
- floodmodeller_api/test/test_data/integrated_bridge.dat +40 -0
- floodmodeller_api/test/test_data/network.ied +2 -2
- floodmodeller_api/test/test_data/network_dat_expected.json +141 -243
- floodmodeller_api/test/test_data/network_ied_expected.json +2 -2
- floodmodeller_api/test/test_data/network_with_comments.ied +2 -2
- floodmodeller_api/test/test_data/structure_logs/EX17_expected.csv +4 -0
- floodmodeller_api/test/test_data/structure_logs/EX17_expected.json +69 -0
- floodmodeller_api/test/test_data/structure_logs/EX18_expected.csv +20 -0
- floodmodeller_api/test/test_data/structure_logs/EX18_expected.json +292 -0
- floodmodeller_api/test/test_data/structure_logs/EX6_expected.csv +4 -0
- floodmodeller_api/test/test_data/structure_logs/EX6_expected.json +35 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_flow.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_fr.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_mode.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_stage.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_state.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_velocity.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_left_fp_h.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_left_fp_mode.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_link_inflow.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_max.csv +87 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_right_fp_h.csv +182 -0
- floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_right_fp_mode.csv +182 -0
- floodmodeller_api/test/test_flowtimeprofile.py +2 -2
- floodmodeller_api/test/test_hydrology_plus_export.py +4 -2
- floodmodeller_api/test/test_ied.py +3 -3
- floodmodeller_api/test/test_ief.py +12 -4
- floodmodeller_api/test/test_inp.py +2 -2
- floodmodeller_api/test/test_integrated_bridge.py +159 -0
- floodmodeller_api/test/test_json.py +14 -13
- floodmodeller_api/test/test_logs_lf.py +50 -29
- floodmodeller_api/test/test_read_file.py +1 -0
- floodmodeller_api/test/test_river.py +12 -12
- floodmodeller_api/test/test_tool.py +8 -5
- floodmodeller_api/test/test_toolbox_structure_log.py +148 -158
- floodmodeller_api/test/test_xml2d.py +14 -16
- floodmodeller_api/test/test_zz.py +143 -0
- floodmodeller_api/to_from_json.py +9 -9
- floodmodeller_api/tool.py +15 -11
- floodmodeller_api/toolbox/example_tool.py +5 -1
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +13 -9
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +500 -194
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +5 -1
- floodmodeller_api/units/__init__.py +15 -0
- floodmodeller_api/units/_base.py +87 -20
- floodmodeller_api/units/_helpers.py +343 -0
- floodmodeller_api/units/boundaries.py +59 -71
- floodmodeller_api/units/comment.py +1 -1
- floodmodeller_api/units/conduits.py +57 -54
- floodmodeller_api/units/connectors.py +112 -0
- floodmodeller_api/units/controls.py +107 -0
- floodmodeller_api/units/conveyance.py +1 -1
- floodmodeller_api/units/iic.py +2 -9
- floodmodeller_api/units/losses.py +44 -45
- floodmodeller_api/units/sections.py +52 -51
- floodmodeller_api/units/structures.py +361 -531
- floodmodeller_api/units/units.py +27 -26
- floodmodeller_api/units/unsupported.py +5 -7
- floodmodeller_api/units/variables.py +2 -2
- floodmodeller_api/urban1d/_base.py +13 -17
- floodmodeller_api/urban1d/conduits.py +11 -21
- floodmodeller_api/urban1d/general_parameters.py +1 -1
- floodmodeller_api/urban1d/junctions.py +7 -11
- floodmodeller_api/urban1d/losses.py +13 -17
- floodmodeller_api/urban1d/outfalls.py +18 -22
- floodmodeller_api/urban1d/raingauges.py +5 -10
- floodmodeller_api/urban1d/subsections.py +5 -4
- floodmodeller_api/urban1d/xsections.py +14 -17
- floodmodeller_api/util.py +23 -6
- floodmodeller_api/validation/parameters.py +7 -3
- floodmodeller_api/validation/urban_parameters.py +1 -4
- floodmodeller_api/validation/validation.py +11 -5
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +27 -31
- floodmodeller_api/xml2d_template.py +1 -1
- floodmodeller_api/zz.py +539 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/LICENSE.txt +1 -1
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/METADATA +30 -16
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/RECORD +116 -83
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/WHEEL +1 -1
- floodmodeller_api/test/test_zzn.py +0 -36
- floodmodeller_api/units/helpers.py +0 -123
- floodmodeller_api/zzn.py +0 -414
- /floodmodeller_api/test/test_data/{network_from_tabularCSV.csv → tabular_csv_outputs/network_zzn_max.csv} +0 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/entry_points.txt +0 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -14,7 +14,7 @@ If you have any query about this program or this License, please contact us at s
|
|
|
14
14
|
address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from floodmodeller_api.units.
|
|
17
|
+
from floodmodeller_api.units._helpers import join_n_char_ljust, to_float, to_int
|
|
18
18
|
from floodmodeller_api.validation import _validate_unit
|
|
19
19
|
|
|
20
20
|
from ._base import UrbanSubsection, UrbanUnit
|
|
@@ -31,13 +31,13 @@ class XSECTION(UrbanUnit):
|
|
|
31
31
|
geom3 (float): auxiliary parameter (width, side slopes, etc.) as listed in Table D-1. (required, applicable to shape types)
|
|
32
32
|
geom4 (float):auxiliary parameter (width, side slopes, etc.) as listed in Table D-1. (required, applicable to shape types)
|
|
33
33
|
barrels (float): Barrels type number of barrels (i.e., number of parallel pipes of equal size, slope, and roughness) associated with a conduit of shape type , or "CUSTOM" type (optional, default is 1).
|
|
34
|
-
culvert (int): Culvert code number from Table A.10 for the conduit
|
|
34
|
+
culvert (int): Culvert code number from Table A.10 for the conduit's inlet geometry if it is a culvert subject to possible inlet flow control. Only an option for shape type (leave blank otherwise) (optional, default is "").
|
|
35
35
|
curve (str): Curve name of a Shape Curve in the [CURVES] section that defines how width varies with depth. (optional, applicable to shape types only)
|
|
36
36
|
tsect (str): Name of an entry in the [TRANSECTS] section that describes the cross-section geometry of an irregular channel. (required, applicable to "IREGUALAR types only)
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
Returns:
|
|
40
|
-
XSECTION: Flood Modeller XSECTION Unit class object
|
|
40
|
+
XSECTION: Flood Modeller XSECTION Unit class object
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
43
|
_unit = "XSECTION"
|
|
@@ -48,8 +48,6 @@ class XSECTION(UrbanUnit):
|
|
|
48
48
|
def _read(self, line):
|
|
49
49
|
unit_data = line.split()
|
|
50
50
|
|
|
51
|
-
# TODO: add functionality to read comments
|
|
52
|
-
# TODO: consider appropriate defaults
|
|
53
51
|
self.name = str(unit_data[0])
|
|
54
52
|
|
|
55
53
|
if unit_data[1] in _shape_options:
|
|
@@ -59,20 +57,20 @@ class XSECTION(UrbanUnit):
|
|
|
59
57
|
unit_data.append("")
|
|
60
58
|
|
|
61
59
|
self.shape = str(unit_data[1])
|
|
62
|
-
self.geom1 =
|
|
63
|
-
self.geom2 =
|
|
64
|
-
self.geom3 =
|
|
65
|
-
self.geom4 =
|
|
66
|
-
self.barrels =
|
|
67
|
-
self.culvert =
|
|
60
|
+
self.geom1 = to_float(unit_data[2], 0.0)
|
|
61
|
+
self.geom2 = to_float(unit_data[3], 0.0)
|
|
62
|
+
self.geom3 = to_float(unit_data[4], 0.0)
|
|
63
|
+
self.geom4 = to_float(unit_data[5], 0.0)
|
|
64
|
+
self.barrels = to_int(unit_data[6], 1)
|
|
65
|
+
self.culvert = to_int(unit_data[7], "")
|
|
68
66
|
|
|
69
67
|
elif unit_data[1] == "CUSTOM":
|
|
70
68
|
while len(unit_data) < self.MIN_LENGTH_CUSTOM:
|
|
71
69
|
unit_data.append("")
|
|
72
70
|
|
|
73
71
|
self.shape = str(unit_data[1])
|
|
74
|
-
self.geom1 =
|
|
75
|
-
self.barrels =
|
|
72
|
+
self.geom1 = to_float(unit_data[2], "")
|
|
73
|
+
self.barrels = to_int(unit_data[6], 1)
|
|
76
74
|
|
|
77
75
|
elif unit_data[1] == "IRREGULAR":
|
|
78
76
|
while len(unit_data) < self.MIN_LENGTH_IRREGULAR:
|
|
@@ -86,8 +84,6 @@ class XSECTION(UrbanUnit):
|
|
|
86
84
|
|
|
87
85
|
_validate_unit(self, urban=True)
|
|
88
86
|
|
|
89
|
-
# TODO:Improve indentation format when writing and include header for completeness
|
|
90
|
-
|
|
91
87
|
params1 = join_n_char_ljust(17, self.name)
|
|
92
88
|
|
|
93
89
|
if self.shape in _shape_options:
|
|
@@ -109,7 +105,8 @@ class XSECTION(UrbanUnit):
|
|
|
109
105
|
params2 = join_n_char_ljust(15, self.shape, self.tsect)
|
|
110
106
|
|
|
111
107
|
else:
|
|
112
|
-
|
|
108
|
+
msg = f"{self.shape} not supported"
|
|
109
|
+
raise RuntimeError(msg)
|
|
113
110
|
|
|
114
111
|
return params1 + params2
|
|
115
112
|
|
floodmodeller_api/util.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -18,7 +18,7 @@ from __future__ import annotations
|
|
|
18
18
|
|
|
19
19
|
import sys
|
|
20
20
|
import webbrowser
|
|
21
|
-
from functools import wraps
|
|
21
|
+
from functools import cache, wraps
|
|
22
22
|
from pathlib import Path
|
|
23
23
|
from typing import TYPE_CHECKING
|
|
24
24
|
|
|
@@ -58,7 +58,7 @@ def read_file(filepath: str | Path) -> FMFile:
|
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
"""
|
|
61
|
-
from . import DAT, IED, IEF, INP, LF1, LF2, XML2D, ZZN
|
|
61
|
+
from . import DAT, IED, IEF, INP, LF1, LF2, XML2D, ZZN, ZZX
|
|
62
62
|
from .hydrology_plus import HydrologyPlusExport
|
|
63
63
|
|
|
64
64
|
suffix_to_class = {
|
|
@@ -67,6 +67,7 @@ def read_file(filepath: str | Path) -> FMFile:
|
|
|
67
67
|
".ied": IED,
|
|
68
68
|
".xml": XML2D,
|
|
69
69
|
".zzn": ZZN,
|
|
70
|
+
".zzx": ZZX,
|
|
70
71
|
".inp": INP,
|
|
71
72
|
".lf1": LF1,
|
|
72
73
|
".lf2": LF2,
|
|
@@ -77,13 +78,27 @@ def read_file(filepath: str | Path) -> FMFile:
|
|
|
77
78
|
if api_class:
|
|
78
79
|
return api_class(filepath)
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
msg = f"Unsupported file type: {filepath.suffix}"
|
|
82
|
+
raise ValueError(msg)
|
|
81
83
|
|
|
82
84
|
|
|
85
|
+
@cache
|
|
83
86
|
def is_windows() -> bool:
|
|
84
87
|
return sys.platform.startswith("win")
|
|
85
88
|
|
|
86
89
|
|
|
90
|
+
def get_associated_file(original_file: Path, new_suffix: str) -> Path:
|
|
91
|
+
new_file = original_file.with_suffix(new_suffix)
|
|
92
|
+
if not new_file.exists():
|
|
93
|
+
msg = (
|
|
94
|
+
f"Error: Could not find associated {new_suffix} file."
|
|
95
|
+
f" Ensure that the {original_file.suffix} results"
|
|
96
|
+
f" have an associated {new_suffix} file with matching name."
|
|
97
|
+
)
|
|
98
|
+
raise FileNotFoundError(msg)
|
|
99
|
+
return new_file
|
|
100
|
+
|
|
101
|
+
|
|
87
102
|
def handle_exception(when: str) -> Callable:
|
|
88
103
|
"""Decorator factory to wrap a method with exception handling."""
|
|
89
104
|
|
|
@@ -103,7 +118,7 @@ def handle_exception(when: str) -> Callable:
|
|
|
103
118
|
class FloodModellerAPIError(Exception):
|
|
104
119
|
"""Custom exception class for Flood Modeller API errors."""
|
|
105
120
|
|
|
106
|
-
def __init__(self, original_exception, when, filetype, filepath) -> None:
|
|
121
|
+
def __init__(self, original_exception, when, filetype, filepath: Path | None = None) -> None:
|
|
107
122
|
tb = original_exception.__traceback__
|
|
108
123
|
while tb.tb_next is not None:
|
|
109
124
|
tb = tb.tb_next
|
|
@@ -111,9 +126,11 @@ class FloodModellerAPIError(Exception):
|
|
|
111
126
|
tb_path = Path(tb.tb_frame.f_code.co_filename)
|
|
112
127
|
fname = "/".join(tb_path.parts[-2:])
|
|
113
128
|
|
|
129
|
+
end_of_str = f"{filepath}" if filepath is not None else "from blank"
|
|
130
|
+
|
|
114
131
|
message = (
|
|
115
132
|
"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
|
116
|
-
f"\nAPI Error: Problem encountered when trying to {when} {filetype} file {
|
|
133
|
+
f"\nAPI Error: Problem encountered when trying to {when} {filetype} file {end_of_str}."
|
|
117
134
|
f"\n\nDetails: {__version__}-{fname}-{line_no}"
|
|
118
135
|
f"\nMsg: {original_exception}"
|
|
119
136
|
"\n\nFor additional support, go to: https://github.com/People-Places-Solutions/floodmodeller-api"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -201,10 +201,10 @@ parameter_options = {
|
|
|
201
201
|
"type": "value-match",
|
|
202
202
|
"options": ["RECTANGLE", "CYLINDER", "SQUARE", "I-BEAM"],
|
|
203
203
|
},
|
|
204
|
-
# Pier_faces is an optional
|
|
204
|
+
# Pier_faces is an optional parameter
|
|
205
205
|
"pier_faces": {
|
|
206
206
|
"type": "value-match",
|
|
207
|
-
"options": ["", "STREAMLINE", "SEMICIRCLE", "TRIANGLE", "DIAPHRAGM"],
|
|
207
|
+
"options": ["", "STREAMLINE", "SEMICIRCLE", "TRIANGLE", "DIAPHRAGM", "DIA"],
|
|
208
208
|
},
|
|
209
209
|
"soffit_shape": {
|
|
210
210
|
"type": "value-match",
|
|
@@ -657,4 +657,8 @@ parameter_options = {
|
|
|
657
657
|
"type": "type-match",
|
|
658
658
|
"options": (float, int),
|
|
659
659
|
},
|
|
660
|
+
"labels": {
|
|
661
|
+
"type": "list-string-length",
|
|
662
|
+
"max_length": 12,
|
|
663
|
+
},
|
|
660
664
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -44,9 +44,7 @@ urban_parameter_options = {
|
|
|
44
44
|
"options": ["INTENSITY", "VOLUME", "CUMULATIVE"],
|
|
45
45
|
},
|
|
46
46
|
"interval": {
|
|
47
|
-
# TODO: UPDATE TO CONSIDER - decimal hours or hours:minutes format (e.g., 0:15 for 15-minute readings). search for presence of ";" during _read maybe?
|
|
48
47
|
# try turing to float else keep as string
|
|
49
|
-
# TODO: add new a type of match called RegEx match for example "[0-9]:[0-9]",
|
|
50
48
|
"type": "type-match",
|
|
51
49
|
"options": (float, int, str),
|
|
52
50
|
},
|
|
@@ -200,7 +198,6 @@ urban_parameter_options = {
|
|
|
200
198
|
"options": (type(None), int),
|
|
201
199
|
},
|
|
202
200
|
"culvert": {
|
|
203
|
-
# TODO: str added to allow case when optional parameter not provided (and so is ""). Update to improve handling,
|
|
204
201
|
"type": "type-match",
|
|
205
202
|
"options": (type(None), int, str),
|
|
206
203
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -23,9 +23,8 @@ def _validate_unit(unit, urban=False):
|
|
|
23
23
|
param_validation_dict = {}
|
|
24
24
|
all_valid = True
|
|
25
25
|
for param in dir(unit):
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
) # define which dictionary to use
|
|
26
|
+
# define which dictionary to use
|
|
27
|
+
options = parameter_options if not urban else urban_parameter_options
|
|
29
28
|
|
|
30
29
|
if param in options:
|
|
31
30
|
value = getattr(unit, param)
|
|
@@ -41,7 +40,8 @@ def _validate_unit(unit, urban=False):
|
|
|
41
40
|
if not value[0]
|
|
42
41
|
],
|
|
43
42
|
)
|
|
44
|
-
|
|
43
|
+
msg = f"One or more parameters in {unit!r} are invalid:\n {errors}"
|
|
44
|
+
raise ValueError(msg)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def _validate_parameter(param, value): # noqa: C901, PLR0911, PLR0912
|
|
@@ -87,6 +87,12 @@ def _validate_parameter(param, value): # noqa: C901, PLR0911, PLR0912
|
|
|
87
87
|
f'-> Exceeds {param["max_length"]} characters',
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
+
if param["type"] == "list-string-length":
|
|
91
|
+
return (
|
|
92
|
+
all(len(item) <= param["max_length"] for item in value),
|
|
93
|
+
f'-> Contains labels exceeding {param["max_length"]} characters',
|
|
94
|
+
)
|
|
95
|
+
|
|
90
96
|
if param["type"] == "dict-match":
|
|
91
97
|
for key, rule in param["options"].items():
|
|
92
98
|
if key not in value:
|
floodmodeller_api/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.5.
|
|
1
|
+
__version__ = "0.5.2"
|
floodmodeller_api/xml2d.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
@@ -17,7 +17,7 @@ address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
19
|
import io
|
|
20
|
-
import
|
|
20
|
+
import logging
|
|
21
21
|
import time
|
|
22
22
|
from copy import deepcopy
|
|
23
23
|
from pathlib import Path
|
|
@@ -205,12 +205,9 @@ class XML2D(FMFile):
|
|
|
205
205
|
|
|
206
206
|
def _validate(self):
|
|
207
207
|
try:
|
|
208
|
-
self._xsdschema.assert_(self._xmltree)
|
|
208
|
+
self._xsdschema.assert_(self._xmltree) # noqa: PT009
|
|
209
209
|
except AssertionError as err:
|
|
210
|
-
msg = (
|
|
211
|
-
f"XML Validation Error for {repr(self)}:\n"
|
|
212
|
-
f" {err.args[0].replace(self._ns, '')}"
|
|
213
|
-
)
|
|
210
|
+
msg = f"XML Validation Error for {self!r}:\n {err.args[0].replace(self._ns, '')}"
|
|
214
211
|
raise ValueError(msg) from err
|
|
215
212
|
|
|
216
213
|
def _recursive_update_xml( # noqa: C901, PLR0912
|
|
@@ -220,11 +217,10 @@ class XML2D(FMFile):
|
|
|
220
217
|
parent_key,
|
|
221
218
|
list_idx=None,
|
|
222
219
|
):
|
|
223
|
-
# TODO: Handle removing params
|
|
224
|
-
|
|
225
220
|
for key, item in new_dict.items():
|
|
226
221
|
if key in self._multi_value_keys and not isinstance(item, list):
|
|
227
|
-
|
|
222
|
+
msg = f"Element: '{key}' must be added as list"
|
|
223
|
+
raise Exception(msg)
|
|
228
224
|
if parent_key == "ROOT":
|
|
229
225
|
parent = self._xmltree.getroot()
|
|
230
226
|
else:
|
|
@@ -294,7 +290,8 @@ class XML2D(FMFile):
|
|
|
294
290
|
from_list=False,
|
|
295
291
|
):
|
|
296
292
|
if add_key in self._multi_value_keys and not isinstance(add_item, list) and not from_list:
|
|
297
|
-
|
|
293
|
+
msg = f"Element: '{add_key}' must be added as list"
|
|
294
|
+
raise Exception(msg)
|
|
298
295
|
if isinstance(add_item, dict):
|
|
299
296
|
new_element = etree.SubElement(parent, f"{self._ns}{add_key}")
|
|
300
297
|
for key, item in add_item.items():
|
|
@@ -336,6 +333,10 @@ class XML2D(FMFile):
|
|
|
336
333
|
f".//{self._w3_schema}*[@name='{add_key}']",
|
|
337
334
|
)
|
|
338
335
|
|
|
336
|
+
if schema_elem is None:
|
|
337
|
+
msg = f"Schema element for key '{add_key}' not found in XSD."
|
|
338
|
+
raise ValueError(msg)
|
|
339
|
+
|
|
339
340
|
if schema_elem.tag.endswith("attribute"):
|
|
340
341
|
parent.set(add_key, str(add_item))
|
|
341
342
|
|
|
@@ -501,20 +502,15 @@ class XML2D(FMFile):
|
|
|
501
502
|
|
|
502
503
|
"""
|
|
503
504
|
|
|
504
|
-
# TODO:
|
|
505
|
-
# - Clean up the lf code?
|
|
506
|
-
# - Remove or sort out get results
|
|
507
|
-
|
|
508
505
|
self.range_function = range_function
|
|
509
506
|
self.range_settings = range_settings if range_settings else {}
|
|
510
507
|
|
|
511
508
|
if self._filepath is None:
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
)
|
|
509
|
+
msg = "xml2D must be saved to a specific filepath before simulate() can be called."
|
|
510
|
+
raise UserWarning(msg)
|
|
515
511
|
if precision.upper() == "DEFAULT":
|
|
516
512
|
precision = "SINGLE" # defaults to single precision
|
|
517
|
-
for
|
|
513
|
+
for domain in self.domains.values():
|
|
518
514
|
if domain["run_data"].get("double_precision") == "required":
|
|
519
515
|
precision = "DOUBLE"
|
|
520
516
|
break
|
|
@@ -525,14 +521,12 @@ class XML2D(FMFile):
|
|
|
525
521
|
else:
|
|
526
522
|
_enginespath = enginespath
|
|
527
523
|
if not Path(_enginespath).exists():
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
)
|
|
524
|
+
msg = f"Flood Modeller non-default engine path not found! {_enginespath!s}"
|
|
525
|
+
raise Exception(msg)
|
|
531
526
|
|
|
532
527
|
# checking if all schemes used are fast, if so will use FAST.exe
|
|
533
|
-
# TODO: Add in option to choose to use or not to use if you can
|
|
534
528
|
is_fast = True
|
|
535
|
-
for
|
|
529
|
+
for domain in self.domains.values():
|
|
536
530
|
if domain["run_data"]["scheme"] != "FAST":
|
|
537
531
|
is_fast = False
|
|
538
532
|
break
|
|
@@ -545,7 +539,8 @@ class XML2D(FMFile):
|
|
|
545
539
|
isis2d_fp = str(Path(_enginespath, "ISIS2d_DP.exe"))
|
|
546
540
|
|
|
547
541
|
if not Path(isis2d_fp).exists():
|
|
548
|
-
|
|
542
|
+
msg = f"Flood Modeller engine not found! Expected location: {isis2d_fp}"
|
|
543
|
+
raise Exception(msg)
|
|
549
544
|
|
|
550
545
|
console_output = console_output.lower()
|
|
551
546
|
run_command = (
|
|
@@ -554,9 +549,9 @@ class XML2D(FMFile):
|
|
|
554
549
|
stdout = DEVNULL if console_output == "simple" else None
|
|
555
550
|
|
|
556
551
|
if method.upper() == "WAIT":
|
|
557
|
-
|
|
552
|
+
logging.info("Executing simulation ... ")
|
|
558
553
|
# execute simulation
|
|
559
|
-
process = Popen(run_command, cwd=
|
|
554
|
+
process = Popen(run_command, cwd=Path(self._filepath).parent, stdout=stdout)
|
|
560
555
|
|
|
561
556
|
# progress bar based on log files:
|
|
562
557
|
if console_output == "simple":
|
|
@@ -571,9 +566,9 @@ class XML2D(FMFile):
|
|
|
571
566
|
self._interpret_exit_code(exitcode, raise_on_failure)
|
|
572
567
|
|
|
573
568
|
elif method.upper() == "RETURN_PROCESS":
|
|
574
|
-
|
|
569
|
+
logging.info("Executing simulation ...")
|
|
575
570
|
# execute simulation
|
|
576
|
-
return Popen(run_command, cwd=
|
|
571
|
+
return Popen(run_command, cwd=Path(self._filepath).parent, stdout=stdout)
|
|
577
572
|
|
|
578
573
|
return None
|
|
579
574
|
|
|
@@ -584,7 +579,8 @@ class XML2D(FMFile):
|
|
|
584
579
|
floodmodeller_api.LF2 class object
|
|
585
580
|
"""
|
|
586
581
|
if not self._log_path.exists():
|
|
587
|
-
|
|
582
|
+
msg = "Log file (LF2) not found"
|
|
583
|
+
raise FileNotFoundError(msg)
|
|
588
584
|
|
|
589
585
|
return LF2(self._log_path)
|
|
590
586
|
|
|
@@ -636,4 +632,4 @@ class XML2D(FMFile):
|
|
|
636
632
|
|
|
637
633
|
if raise_on_failure and exitcode != self.GOOD_EXIT_CODE:
|
|
638
634
|
raise Exception(msg)
|
|
639
|
-
|
|
635
|
+
logging.info(msg)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
3
|
+
Copyright (C) 2025 Jacobs U.K. Limited
|
|
4
4
|
|
|
5
5
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
6
|
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|