floodmodeller-api 0.5.0.post1__py3-none-any.whl → 0.5.1__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 +1 -1
- floodmodeller_api/_base.py +26 -16
- floodmodeller_api/backup.py +3 -2
- floodmodeller_api/dat.py +29 -30
- floodmodeller_api/diff.py +3 -3
- floodmodeller_api/hydrology_plus/hydrology_plus_export.py +14 -13
- floodmodeller_api/ied.py +6 -6
- floodmodeller_api/ief.py +27 -25
- floodmodeller_api/inp.py +3 -4
- floodmodeller_api/logs/lf.py +9 -16
- floodmodeller_api/logs/lf_helpers.py +18 -18
- floodmodeller_api/mapping.py +2 -0
- 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 +4 -3
- floodmodeller_api/test/test_dat.py +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 +2 -2
- floodmodeller_api/test/test_ief.py +2 -2
- floodmodeller_api/test/test_inp.py +2 -2
- floodmodeller_api/test/test_json.py +5 -10
- floodmodeller_api/test/test_logs_lf.py +6 -6
- floodmodeller_api/test/test_read_file.py +1 -0
- floodmodeller_api/test/test_river.py +11 -11
- floodmodeller_api/test/test_tool.py +8 -5
- floodmodeller_api/test/test_toolbox_structure_log.py +149 -158
- floodmodeller_api/test/test_xml2d.py +9 -11
- floodmodeller_api/test/test_zz.py +143 -0
- floodmodeller_api/to_from_json.py +8 -8
- floodmodeller_api/tool.py +12 -6
- floodmodeller_api/toolbox/example_tool.py +5 -1
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +12 -8
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +498 -196
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +5 -1
- floodmodeller_api/units/_base.py +14 -10
- floodmodeller_api/units/conveyance.py +1 -1
- floodmodeller_api/units/helpers.py +1 -3
- floodmodeller_api/units/losses.py +2 -3
- floodmodeller_api/units/sections.py +14 -10
- floodmodeller_api/units/structures.py +9 -9
- floodmodeller_api/units/units.py +2 -0
- floodmodeller_api/urban1d/_base.py +6 -9
- floodmodeller_api/urban1d/outfalls.py +2 -1
- floodmodeller_api/urban1d/raingauges.py +2 -1
- floodmodeller_api/urban1d/subsections.py +2 -0
- floodmodeller_api/urban1d/xsections.py +3 -2
- floodmodeller_api/util.py +16 -2
- floodmodeller_api/validation/validation.py +2 -1
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +18 -20
- floodmodeller_api/zz.py +538 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.1.dist-info}/METADATA +20 -14
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.1.dist-info}/RECORD +78 -60
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.1.dist-info}/WHEEL +1 -1
- floodmodeller_api/test/test_zzn.py +0 -36
- 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.1.dist-info}/LICENSE.txt +0 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.1.dist-info}/entry_points.txt +0 -0
- {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.1.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import ClassVar
|
|
4
|
+
|
|
1
5
|
from floodmodeller_api.tool import FMTool, Parameter
|
|
2
6
|
|
|
3
7
|
from .structure_log import StructureLogBuilder
|
|
@@ -50,7 +54,7 @@ class StructureLog(FMTool):
|
|
|
50
54
|
|
|
51
55
|
name = "Structure Log"
|
|
52
56
|
description = "Creates a structure log"
|
|
53
|
-
parameters = [
|
|
57
|
+
parameters: ClassVar[list[Parameter]] = [
|
|
54
58
|
Parameter(
|
|
55
59
|
name="input_path",
|
|
56
60
|
dtype=str,
|
floodmodeller_api/units/_base.py
CHANGED
|
@@ -40,7 +40,11 @@ class Unit(Jsonable):
|
|
|
40
40
|
self._create_from_blank(**kwargs)
|
|
41
41
|
|
|
42
42
|
@property
|
|
43
|
-
def
|
|
43
|
+
def unit(self) -> str:
|
|
44
|
+
return self._unit
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def name(self) -> str | None:
|
|
44
48
|
return self._name
|
|
45
49
|
|
|
46
50
|
@name.setter
|
|
@@ -48,20 +52,21 @@ class Unit(Jsonable):
|
|
|
48
52
|
try:
|
|
49
53
|
new_name = str(new_name)
|
|
50
54
|
if " " in new_name:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
55
|
+
msg = f'Cannot set unit name to "{new_name}" as it contains one or more spaces'
|
|
56
|
+
raise Exception(msg)
|
|
54
57
|
self._name = new_name
|
|
55
58
|
except Exception as e:
|
|
56
|
-
|
|
59
|
+
msg = f'Failed to set unit name to "{new_name}" due to error: {e}'
|
|
60
|
+
raise Exception(msg) from e
|
|
57
61
|
|
|
58
62
|
@property
|
|
59
|
-
def subtype(self):
|
|
63
|
+
def subtype(self) -> str | None:
|
|
60
64
|
return self._subtype
|
|
61
65
|
|
|
62
66
|
@subtype.setter
|
|
63
67
|
def subtype(self, new_value):
|
|
64
|
-
|
|
68
|
+
msg = "You cannot change the subtype of a unit once it has been instantiated"
|
|
69
|
+
raise ValueError(msg)
|
|
65
70
|
|
|
66
71
|
def __repr__(self):
|
|
67
72
|
if self._subtype is None:
|
|
@@ -71,9 +76,8 @@ class Unit(Jsonable):
|
|
|
71
76
|
)
|
|
72
77
|
|
|
73
78
|
def _create_from_blank(self):
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
79
|
+
msg = f"Creating new {self._unit} units is not yet supported by floodmodeller_api, only existing units can be read"
|
|
80
|
+
raise NotImplementedError(msg)
|
|
77
81
|
|
|
78
82
|
def __str__(self):
|
|
79
83
|
return "\n".join(self._write())
|
|
@@ -72,7 +72,7 @@ def calculate_cross_section_conveyance(
|
|
|
72
72
|
total_length = np.where(in_panel_and_section, length, 0).sum(axis=1)
|
|
73
73
|
total_mannings = np.where(in_panel_and_section, mannings, 0).sum(axis=1)
|
|
74
74
|
|
|
75
|
-
with np.errstate(invalid="ignore"):
|
|
75
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
76
76
|
conveyance += np.where(
|
|
77
77
|
total_length >= MINIMUM_PERIMETER_THRESHOLD,
|
|
78
78
|
total_area ** (5 / 3) * total_length ** (1 / 3) / (total_mannings * rpl_panel),
|
|
@@ -116,8 +116,6 @@ def _to_data_list(block: list[str], num_cols: int | None = None, date_col: int |
|
|
|
116
116
|
else:
|
|
117
117
|
row_split = [_to_float(itm) for itm in row_split]
|
|
118
118
|
|
|
119
|
-
row_list =
|
|
120
|
-
for var in row_split:
|
|
121
|
-
row_list.append(var)
|
|
119
|
+
row_list = list(row_split)
|
|
122
120
|
data_list.append(row_list)
|
|
123
121
|
return data_list
|
|
@@ -238,9 +238,8 @@ class BLOCKAGE(Unit):
|
|
|
238
238
|
|
|
239
239
|
# Custom validation for blockage percentage
|
|
240
240
|
if self.data.max() > 1 or self.data.min() < 0:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
)
|
|
241
|
+
msg = f"Parameter error with {self!r} - blockage percentage must be between 0 and 1"
|
|
242
|
+
raise ValueError(msg)
|
|
244
243
|
|
|
245
244
|
header = f"BLOCKAGE #revision#{self._revision} {self.comment}"
|
|
246
245
|
labels = join_n_char_ljust(
|
|
@@ -14,6 +14,10 @@ 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 __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import ClassVar
|
|
20
|
+
|
|
17
21
|
import pandas as pd
|
|
18
22
|
|
|
19
23
|
from floodmodeller_api.validation import _validate_unit
|
|
@@ -54,7 +58,7 @@ class RIVER(Unit):
|
|
|
54
58
|
"""
|
|
55
59
|
|
|
56
60
|
_unit = "RIVER"
|
|
57
|
-
_required_columns = [
|
|
61
|
+
_required_columns: ClassVar[list[str]] = [
|
|
58
62
|
"X",
|
|
59
63
|
"Y",
|
|
60
64
|
"Mannings n",
|
|
@@ -204,7 +208,7 @@ class RIVER(Unit):
|
|
|
204
208
|
# Manual so slope can have more sf
|
|
205
209
|
params = f'{self.dist_to_next:>10.3f}{"":>10}{self.slope:>10.6f}{self.density:>10.3f}'
|
|
206
210
|
self.nrows = len(self._data)
|
|
207
|
-
riv_block = [header, self.subtype, labels, params, f"{
|
|
211
|
+
riv_block = [header, self.subtype, labels, params, f"{self.nrows!s:>10}"]
|
|
208
212
|
|
|
209
213
|
riv_data = []
|
|
210
214
|
for (
|
|
@@ -257,11 +261,11 @@ class RIVER(Unit):
|
|
|
257
261
|
@data.setter
|
|
258
262
|
def data(self, new_df: pd.DataFrame) -> None:
|
|
259
263
|
if not isinstance(new_df, pd.DataFrame):
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
)
|
|
264
|
+
msg = "The updated data table for a cross section must be a pandas DataFrame."
|
|
265
|
+
raise ValueError(msg)
|
|
263
266
|
if list(map(str.lower, new_df.columns)) != list(map(str.lower, self._required_columns)):
|
|
264
|
-
|
|
267
|
+
msg = f"The DataFrame must only contain columns: {self._required_columns}"
|
|
268
|
+
raise ValueError(msg)
|
|
265
269
|
self._data = new_df
|
|
266
270
|
|
|
267
271
|
@property
|
|
@@ -325,11 +329,11 @@ class RIVER(Unit):
|
|
|
325
329
|
@active_data.setter
|
|
326
330
|
def active_data(self, new_df: pd.DataFrame) -> None:
|
|
327
331
|
if not isinstance(new_df, pd.DataFrame):
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
)
|
|
332
|
+
msg = "The updated data table for a cross section must be a pandas DataFrame."
|
|
333
|
+
raise ValueError(msg)
|
|
331
334
|
if new_df.columns.to_list() != self._required_columns:
|
|
332
|
-
|
|
335
|
+
msg = f"The DataFrame must only contain columns: {self._required_columns}"
|
|
336
|
+
raise ValueError(msg)
|
|
333
337
|
|
|
334
338
|
# Ensure activation markers are present
|
|
335
339
|
new_df = new_df.copy()
|
|
@@ -376,7 +376,7 @@ class BRIDGE(Unit):
|
|
|
376
376
|
self.orifice_discharge_coefficient,
|
|
377
377
|
)
|
|
378
378
|
self.section_nrows = len(self.section_data)
|
|
379
|
-
br_block.extend(["MANNING", params, f"{
|
|
379
|
+
br_block.extend(["MANNING", params, f"{self.section_nrows!s:>10}"])
|
|
380
380
|
|
|
381
381
|
section_data = []
|
|
382
382
|
for _, x, y, n, embankments in self.section_data.itertuples():
|
|
@@ -388,7 +388,7 @@ class BRIDGE(Unit):
|
|
|
388
388
|
br_block.extend(section_data)
|
|
389
389
|
|
|
390
390
|
self.opening_nrows = len(self.opening_data)
|
|
391
|
-
br_block.append(f"{
|
|
391
|
+
br_block.append(f"{self.opening_nrows!s:>10}")
|
|
392
392
|
opening_data = []
|
|
393
393
|
for _, start, finish, spring, soffit in self.opening_data.itertuples():
|
|
394
394
|
row = join_10_char(start, finish, spring, soffit)
|
|
@@ -424,10 +424,10 @@ class BRIDGE(Unit):
|
|
|
424
424
|
[
|
|
425
425
|
"MANNING",
|
|
426
426
|
params,
|
|
427
|
-
f"{
|
|
427
|
+
f"{self.abutment_type!s:>10}",
|
|
428
428
|
pier_params,
|
|
429
429
|
self.abutment_alignment,
|
|
430
|
-
f"{
|
|
430
|
+
f"{self.section_nrows!s:>10}",
|
|
431
431
|
],
|
|
432
432
|
)
|
|
433
433
|
|
|
@@ -440,7 +440,7 @@ class BRIDGE(Unit):
|
|
|
440
440
|
br_block.extend(section_data)
|
|
441
441
|
|
|
442
442
|
self.opening_nrows = len(self.opening_data)
|
|
443
|
-
br_block.append(f"{
|
|
443
|
+
br_block.append(f"{self.opening_nrows!s:>10}")
|
|
444
444
|
opening_data = []
|
|
445
445
|
for _, start, finish, spring, soffit in self.opening_data.itertuples():
|
|
446
446
|
row = join_10_char(start, finish, spring, soffit)
|
|
@@ -448,7 +448,7 @@ class BRIDGE(Unit):
|
|
|
448
448
|
br_block.extend(opening_data)
|
|
449
449
|
|
|
450
450
|
self.culvert_nrows = len(self.culvert_data)
|
|
451
|
-
br_block.append(f"{
|
|
451
|
+
br_block.append(f"{self.culvert_nrows!s:>10}")
|
|
452
452
|
culvert_data = []
|
|
453
453
|
for (
|
|
454
454
|
_,
|
|
@@ -481,7 +481,7 @@ class BRIDGE(Unit):
|
|
|
481
481
|
"YARNELL",
|
|
482
482
|
params,
|
|
483
483
|
additional_params,
|
|
484
|
-
f"{
|
|
484
|
+
f"{self.us_section_nrows!s:>10}",
|
|
485
485
|
],
|
|
486
486
|
)
|
|
487
487
|
|
|
@@ -495,7 +495,7 @@ class BRIDGE(Unit):
|
|
|
495
495
|
br_block.extend(us_section_data)
|
|
496
496
|
|
|
497
497
|
self.ds_section_nrows = len(self.ds_section_data)
|
|
498
|
-
br_block.append(f"{
|
|
498
|
+
br_block.append(f"{self.ds_section_nrows!s:>10}")
|
|
499
499
|
ds_section_data = []
|
|
500
500
|
for _, x, y, n, embankments, top_level in self.ds_section_data.itertuples():
|
|
501
501
|
# Adding extra 10 spaces before embankment flag
|
|
@@ -506,7 +506,7 @@ class BRIDGE(Unit):
|
|
|
506
506
|
br_block.extend(ds_section_data)
|
|
507
507
|
|
|
508
508
|
self.pier_locs_nrows = len(self.pier_locs_data)
|
|
509
|
-
br_block.append(f"{
|
|
509
|
+
br_block.append(f"{self.pier_locs_nrows!s:>10}")
|
|
510
510
|
pier_locs_data = []
|
|
511
511
|
for (
|
|
512
512
|
_,
|
floodmodeller_api/units/units.py
CHANGED
|
@@ -14,6 +14,8 @@ 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 __future__ import annotations
|
|
18
|
+
|
|
17
19
|
from typing import TypedDict
|
|
18
20
|
|
|
19
21
|
|
|
@@ -51,9 +51,8 @@ class UrbanUnit(Jsonable):
|
|
|
51
51
|
return None
|
|
52
52
|
|
|
53
53
|
def _create_from_blank(self):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
54
|
+
msg = f"Creating new {self._unit} units is not yet supported by floodmodeller_api, only existing units can be read"
|
|
55
|
+
raise NotImplementedError(msg)
|
|
57
56
|
|
|
58
57
|
def __str__(self):
|
|
59
58
|
return self._write()
|
|
@@ -110,9 +109,8 @@ class UrbanSubsection(Jsonable):
|
|
|
110
109
|
return f"<floodmodeller_api UrbanSubsection Class: {self._attribute}>"
|
|
111
110
|
|
|
112
111
|
def _create_from_blank(self):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
)
|
|
112
|
+
msg = f"Creating new {self._name} subsections is not yet supported by floodmodeller_api, only existing subsections can be read"
|
|
113
|
+
raise NotImplementedError(msg)
|
|
116
114
|
|
|
117
115
|
def __str__(self):
|
|
118
116
|
return "\n".join(self._write())
|
|
@@ -155,9 +153,8 @@ class UrbanSubsection(Jsonable):
|
|
|
155
153
|
# Miss-match found
|
|
156
154
|
# check that it is not an existing label in units
|
|
157
155
|
if unit.name in units:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
156
|
+
msg = f'Error: Cannot update label "{name}" to "{unit.name}" beacuase "{unit.name}" already exists in the {self._attribute} subsection'
|
|
157
|
+
raise Exception(msg)
|
|
161
158
|
|
|
162
159
|
units[unit.name] = unit
|
|
163
160
|
del units[name]
|
|
@@ -102,7 +102,8 @@ class OUTFALL(UrbanUnit):
|
|
|
102
102
|
params2 = join_n_char_ljust(15, self.tseries, self.gated, self.routeto)
|
|
103
103
|
|
|
104
104
|
else:
|
|
105
|
-
|
|
105
|
+
msg = f"{self.type} not supported"
|
|
106
|
+
raise RuntimeError(msg)
|
|
106
107
|
|
|
107
108
|
return params1 + params2
|
|
108
109
|
|
|
@@ -97,7 +97,8 @@ class RAINGAUGE(UrbanUnit):
|
|
|
97
97
|
params2 = join_n_char_ljust(15, self.filename, self.station, self.units)
|
|
98
98
|
|
|
99
99
|
else:
|
|
100
|
-
|
|
100
|
+
msg = f"{self.data_option} not supported"
|
|
101
|
+
raise RuntimeError(msg)
|
|
101
102
|
|
|
102
103
|
return params1 + params2
|
|
103
104
|
|
|
@@ -14,6 +14,8 @@ 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 __future__ import annotations
|
|
18
|
+
|
|
17
19
|
from typing import Type, TypedDict
|
|
18
20
|
|
|
19
21
|
from ._base import UrbanSubsection
|
|
@@ -31,7 +31,7 @@ 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
|
|
|
@@ -109,7 +109,8 @@ class XSECTION(UrbanUnit):
|
|
|
109
109
|
params2 = join_n_char_ljust(15, self.shape, self.tsect)
|
|
110
110
|
|
|
111
111
|
else:
|
|
112
|
-
|
|
112
|
+
msg = f"{self.shape} not supported"
|
|
113
|
+
raise RuntimeError(msg)
|
|
113
114
|
|
|
114
115
|
return params1 + params2
|
|
115
116
|
|
floodmodeller_api/util.py
CHANGED
|
@@ -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,26 @@ 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
|
|
|
83
85
|
def is_windows() -> bool:
|
|
84
86
|
return sys.platform.startswith("win")
|
|
85
87
|
|
|
86
88
|
|
|
89
|
+
def get_associated_file(original_file: Path, new_suffix: str) -> Path:
|
|
90
|
+
new_file = original_file.with_suffix(new_suffix)
|
|
91
|
+
if not new_file.exists():
|
|
92
|
+
msg = (
|
|
93
|
+
f"Error: Could not find associated {new_suffix} file."
|
|
94
|
+
f" Ensure that the {original_file.suffix} results"
|
|
95
|
+
f" have an associated {new_suffix} file with matching name."
|
|
96
|
+
)
|
|
97
|
+
raise FileNotFoundError(msg)
|
|
98
|
+
return new_file
|
|
99
|
+
|
|
100
|
+
|
|
87
101
|
def handle_exception(when: str) -> Callable:
|
|
88
102
|
"""Decorator factory to wrap a method with exception handling."""
|
|
89
103
|
|
|
@@ -41,7 +41,8 @@ def _validate_unit(unit, urban=False):
|
|
|
41
41
|
if not value[0]
|
|
42
42
|
],
|
|
43
43
|
)
|
|
44
|
-
|
|
44
|
+
msg = f"One or more parameters in {unit!r} are invalid:\n {errors}"
|
|
45
|
+
raise ValueError(msg)
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def _validate_parameter(param, value): # noqa: C901, PLR0911, PLR0912
|
floodmodeller_api/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.5.
|
|
1
|
+
__version__ = "0.5.1"
|
floodmodeller_api/xml2d.py
CHANGED
|
@@ -17,7 +17,6 @@ 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 os
|
|
21
20
|
import time
|
|
22
21
|
from copy import deepcopy
|
|
23
22
|
from pathlib import Path
|
|
@@ -205,12 +204,9 @@ class XML2D(FMFile):
|
|
|
205
204
|
|
|
206
205
|
def _validate(self):
|
|
207
206
|
try:
|
|
208
|
-
self._xsdschema.assert_(self._xmltree)
|
|
207
|
+
self._xsdschema.assert_(self._xmltree) # noqa: PT009
|
|
209
208
|
except AssertionError as err:
|
|
210
|
-
msg = (
|
|
211
|
-
f"XML Validation Error for {repr(self)}:\n"
|
|
212
|
-
f" {err.args[0].replace(self._ns, '')}"
|
|
213
|
-
)
|
|
209
|
+
msg = f"XML Validation Error for {self!r}:\n {err.args[0].replace(self._ns, '')}"
|
|
214
210
|
raise ValueError(msg) from err
|
|
215
211
|
|
|
216
212
|
def _recursive_update_xml( # noqa: C901, PLR0912
|
|
@@ -224,7 +220,8 @@ class XML2D(FMFile):
|
|
|
224
220
|
|
|
225
221
|
for key, item in new_dict.items():
|
|
226
222
|
if key in self._multi_value_keys and not isinstance(item, list):
|
|
227
|
-
|
|
223
|
+
msg = f"Element: '{key}' must be added as list"
|
|
224
|
+
raise Exception(msg)
|
|
228
225
|
if parent_key == "ROOT":
|
|
229
226
|
parent = self._xmltree.getroot()
|
|
230
227
|
else:
|
|
@@ -294,7 +291,8 @@ class XML2D(FMFile):
|
|
|
294
291
|
from_list=False,
|
|
295
292
|
):
|
|
296
293
|
if add_key in self._multi_value_keys and not isinstance(add_item, list) and not from_list:
|
|
297
|
-
|
|
294
|
+
msg = f"Element: '{add_key}' must be added as list"
|
|
295
|
+
raise Exception(msg)
|
|
298
296
|
if isinstance(add_item, dict):
|
|
299
297
|
new_element = etree.SubElement(parent, f"{self._ns}{add_key}")
|
|
300
298
|
for key, item in add_item.items():
|
|
@@ -509,12 +507,11 @@ class XML2D(FMFile):
|
|
|
509
507
|
self.range_settings = range_settings if range_settings else {}
|
|
510
508
|
|
|
511
509
|
if self._filepath is None:
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
)
|
|
510
|
+
msg = "xml2D must be saved to a specific filepath before simulate() can be called."
|
|
511
|
+
raise UserWarning(msg)
|
|
515
512
|
if precision.upper() == "DEFAULT":
|
|
516
513
|
precision = "SINGLE" # defaults to single precision
|
|
517
|
-
for
|
|
514
|
+
for domain in self.domains.values():
|
|
518
515
|
if domain["run_data"].get("double_precision") == "required":
|
|
519
516
|
precision = "DOUBLE"
|
|
520
517
|
break
|
|
@@ -525,14 +522,13 @@ class XML2D(FMFile):
|
|
|
525
522
|
else:
|
|
526
523
|
_enginespath = enginespath
|
|
527
524
|
if not Path(_enginespath).exists():
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
)
|
|
525
|
+
msg = f"Flood Modeller non-default engine path not found! {_enginespath!s}"
|
|
526
|
+
raise Exception(msg)
|
|
531
527
|
|
|
532
528
|
# checking if all schemes used are fast, if so will use FAST.exe
|
|
533
529
|
# TODO: Add in option to choose to use or not to use if you can
|
|
534
530
|
is_fast = True
|
|
535
|
-
for
|
|
531
|
+
for domain in self.domains.values():
|
|
536
532
|
if domain["run_data"]["scheme"] != "FAST":
|
|
537
533
|
is_fast = False
|
|
538
534
|
break
|
|
@@ -545,7 +541,8 @@ class XML2D(FMFile):
|
|
|
545
541
|
isis2d_fp = str(Path(_enginespath, "ISIS2d_DP.exe"))
|
|
546
542
|
|
|
547
543
|
if not Path(isis2d_fp).exists():
|
|
548
|
-
|
|
544
|
+
msg = f"Flood Modeller engine not found! Expected location: {isis2d_fp}"
|
|
545
|
+
raise Exception(msg)
|
|
549
546
|
|
|
550
547
|
console_output = console_output.lower()
|
|
551
548
|
run_command = (
|
|
@@ -556,7 +553,7 @@ class XML2D(FMFile):
|
|
|
556
553
|
if method.upper() == "WAIT":
|
|
557
554
|
print("Executing simulation ... ")
|
|
558
555
|
# execute simulation
|
|
559
|
-
process = Popen(run_command, cwd=
|
|
556
|
+
process = Popen(run_command, cwd=Path(self._filepath).parent, stdout=stdout)
|
|
560
557
|
|
|
561
558
|
# progress bar based on log files:
|
|
562
559
|
if console_output == "simple":
|
|
@@ -573,7 +570,7 @@ class XML2D(FMFile):
|
|
|
573
570
|
elif method.upper() == "RETURN_PROCESS":
|
|
574
571
|
print("Executing simulation ...")
|
|
575
572
|
# execute simulation
|
|
576
|
-
return Popen(run_command, cwd=
|
|
573
|
+
return Popen(run_command, cwd=Path(self._filepath).parent, stdout=stdout)
|
|
577
574
|
|
|
578
575
|
return None
|
|
579
576
|
|
|
@@ -584,7 +581,8 @@ class XML2D(FMFile):
|
|
|
584
581
|
floodmodeller_api.LF2 class object
|
|
585
582
|
"""
|
|
586
583
|
if not self._log_path.exists():
|
|
587
|
-
|
|
584
|
+
msg = "Log file (LF2) not found"
|
|
585
|
+
raise FileNotFoundError(msg)
|
|
588
586
|
|
|
589
587
|
return LF2(self._log_path)
|
|
590
588
|
|