floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.4__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 +8 -9
- floodmodeller_api/_base.py +169 -176
- floodmodeller_api/backup.py +273 -273
- floodmodeller_api/dat.py +889 -831
- floodmodeller_api/diff.py +136 -119
- floodmodeller_api/ied.py +302 -306
- floodmodeller_api/ief.py +553 -637
- floodmodeller_api/ief_flags.py +253 -253
- floodmodeller_api/inp.py +260 -266
- floodmodeller_api/libs/libifcoremd.dll +0 -0
- floodmodeller_api/libs/libifcoremt.so.5 +0 -0
- floodmodeller_api/libs/libifport.so.5 +0 -0
- floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
- floodmodeller_api/libs/libintlc.so.5 +0 -0
- floodmodeller_api/libs/libmmd.dll +0 -0
- floodmodeller_api/libs/libsvml.so +0 -0
- floodmodeller_api/libs/libzzn_read.so +0 -0
- floodmodeller_api/libs/zzn_read.dll +0 -0
- floodmodeller_api/logs/__init__.py +2 -2
- floodmodeller_api/logs/lf.py +364 -312
- floodmodeller_api/logs/lf_helpers.py +354 -352
- floodmodeller_api/logs/lf_params.py +643 -529
- floodmodeller_api/mapping.py +84 -0
- floodmodeller_api/test/__init__.py +4 -4
- floodmodeller_api/test/conftest.py +16 -8
- floodmodeller_api/test/test_backup.py +117 -117
- floodmodeller_api/test/test_conveyance.py +107 -0
- floodmodeller_api/test/test_dat.py +222 -92
- floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
- floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
- floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
- floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
- floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
- floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
- floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
- floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
- floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
- floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
- floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
- floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
- floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
- floodmodeller_api/test/test_data/EX1.DAT +321 -321
- floodmodeller_api/test/test_data/EX1.ext +107 -107
- floodmodeller_api/test/test_data/EX1.feb +320 -320
- floodmodeller_api/test/test_data/EX1.gxy +107 -107
- floodmodeller_api/test/test_data/EX17.DAT +421 -422
- floodmodeller_api/test/test_data/EX17.ext +213 -213
- floodmodeller_api/test/test_data/EX17.feb +422 -422
- floodmodeller_api/test/test_data/EX18.DAT +375 -375
- floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
- floodmodeller_api/test/test_data/EX2.DAT +302 -302
- floodmodeller_api/test/test_data/EX3.DAT +926 -926
- floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
- floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
- floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
- floodmodeller_api/test/test_data/EX6.ext +532 -532
- floodmodeller_api/test/test_data/EX6.feb +2084 -2084
- floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
- floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
- floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
- floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
- floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
- floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
- floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
- floodmodeller_api/test/test_data/blockage.dat +50 -50
- floodmodeller_api/test/test_data/blockage.ext +45 -45
- floodmodeller_api/test/test_data/blockage.feb +9 -9
- floodmodeller_api/test/test_data/blockage.gxy +71 -71
- floodmodeller_api/test/test_data/conveyance_test.dat +165 -0
- floodmodeller_api/test/test_data/conveyance_test.feb +116 -0
- floodmodeller_api/test/test_data/conveyance_test.gxy +85 -0
- floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
- floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
- floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
- floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
- floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
- floodmodeller_api/test/test_data/ex3.ief +20 -20
- floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
- floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
- floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
- floodmodeller_api/test/test_data/example1.inp +329 -329
- floodmodeller_api/test/test_data/example2.inp +158 -158
- floodmodeller_api/test/test_data/example3.inp +297 -297
- floodmodeller_api/test/test_data/example4.inp +388 -388
- floodmodeller_api/test/test_data/example5.inp +147 -147
- floodmodeller_api/test/test_data/example6.inp +154 -154
- floodmodeller_api/test/test_data/expected_conveyance.csv +60 -0
- floodmodeller_api/test/test_data/jump.dat +176 -176
- floodmodeller_api/test/test_data/network.dat +1374 -1374
- floodmodeller_api/test/test_data/network.ext +45 -45
- floodmodeller_api/test/test_data/network.exy +1 -1
- floodmodeller_api/test/test_data/network.feb +45 -45
- floodmodeller_api/test/test_data/network.ied +45 -45
- floodmodeller_api/test/test_data/network.ief +20 -20
- floodmodeller_api/test/test_data/network.inp +147 -147
- floodmodeller_api/test/test_data/network.pxy +57 -57
- floodmodeller_api/test/test_data/network.zzd +122 -122
- floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
- floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
- floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
- floodmodeller_api/test/test_data/rnweir.dat +9 -9
- floodmodeller_api/test/test_data/rnweir.ext +45 -45
- floodmodeller_api/test/test_data/rnweir.feb +9 -9
- floodmodeller_api/test/test_data/rnweir.gxy +45 -45
- floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
- floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
- floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
- floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
- floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
- floodmodeller_api/test/test_data/unit checks.dat +16 -16
- floodmodeller_api/test/test_ied.py +29 -29
- floodmodeller_api/test/test_ief.py +136 -24
- floodmodeller_api/test/test_inp.py +47 -48
- floodmodeller_api/test/test_json.py +114 -0
- floodmodeller_api/test/test_logs_lf.py +102 -51
- floodmodeller_api/test/test_tool.py +165 -152
- floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
- floodmodeller_api/test/test_xml2d.py +151 -156
- floodmodeller_api/test/test_zzn.py +36 -34
- floodmodeller_api/to_from_json.py +230 -0
- floodmodeller_api/tool.py +332 -329
- floodmodeller_api/toolbox/__init__.py +5 -5
- floodmodeller_api/toolbox/example_tool.py +45 -45
- floodmodeller_api/toolbox/model_build/__init__.py +2 -2
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
- floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
- floodmodeller_api/units/__init__.py +10 -10
- floodmodeller_api/units/_base.py +214 -212
- floodmodeller_api/units/boundaries.py +467 -467
- floodmodeller_api/units/comment.py +52 -55
- floodmodeller_api/units/conduits.py +382 -402
- floodmodeller_api/units/conveyance.py +301 -0
- floodmodeller_api/units/helpers.py +123 -131
- floodmodeller_api/units/iic.py +107 -101
- floodmodeller_api/units/losses.py +305 -306
- floodmodeller_api/units/sections.py +465 -446
- floodmodeller_api/units/structures.py +1690 -1683
- floodmodeller_api/units/units.py +93 -104
- floodmodeller_api/units/unsupported.py +44 -44
- floodmodeller_api/units/variables.py +87 -89
- floodmodeller_api/urban1d/__init__.py +11 -11
- floodmodeller_api/urban1d/_base.py +188 -179
- floodmodeller_api/urban1d/conduits.py +93 -85
- floodmodeller_api/urban1d/general_parameters.py +58 -58
- floodmodeller_api/urban1d/junctions.py +81 -79
- floodmodeller_api/urban1d/losses.py +81 -74
- floodmodeller_api/urban1d/outfalls.py +114 -110
- floodmodeller_api/urban1d/raingauges.py +111 -111
- floodmodeller_api/urban1d/subsections.py +92 -98
- floodmodeller_api/urban1d/xsections.py +147 -144
- floodmodeller_api/util.py +119 -21
- floodmodeller_api/validation/parameters.py +660 -660
- floodmodeller_api/validation/urban_parameters.py +388 -404
- floodmodeller_api/validation/validation.py +110 -108
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +632 -673
- floodmodeller_api/xml2d_template.py +37 -37
- floodmodeller_api/zzn.py +414 -363
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/LICENSE.txt +13 -13
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/METADATA +85 -82
- floodmodeller_api-0.4.4.dist-info/RECORD +185 -0
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/WHEEL +1 -1
- floodmodeller_api/libifcoremd.dll +0 -0
- floodmodeller_api/test/test_data/EX3.bmp +0 -0
- floodmodeller_api/test/test_data/test_output.csv +0 -87
- floodmodeller_api/zzn_read.dll +0 -0
- floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/entry_points.txt +0 -0
- {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from shapely import LineString, MultiLineString, Polygon, intersection
|
|
8
|
+
|
|
9
|
+
MINIMUM_PERIMETER_THRESHOLD = 1e-8
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def calculate_cross_section_conveyance(
|
|
13
|
+
x: np.ndarray,
|
|
14
|
+
y: np.ndarray,
|
|
15
|
+
n: np.ndarray,
|
|
16
|
+
rpl: np.ndarray,
|
|
17
|
+
panel_markers: np.ndarray,
|
|
18
|
+
) -> pd.Series:
|
|
19
|
+
"""
|
|
20
|
+
Calculate the conveyance of a cross-section by summing the conveyance
|
|
21
|
+
across all panels defined by panel markers.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
x (np.ndarray): The x-coordinates of the cross-section.
|
|
25
|
+
y (np.ndarray): The y-coordinates of the cross-section.
|
|
26
|
+
n (np.ndarray): Manning's n values for each segment.
|
|
27
|
+
rpl (np.ndarray): Relative Path Length values for each segment.
|
|
28
|
+
panel_markers (np.ndarray): Boolean array indicating the start of each panel.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
pd.Series: A pandas Series containing the conveyance values indexed by water levels.
|
|
32
|
+
|
|
33
|
+
Example:
|
|
34
|
+
.. code-block:: python
|
|
35
|
+
|
|
36
|
+
x = np.array([0, 1, 2, 3, 4])
|
|
37
|
+
y = np.array([1, 2, 1, 2, 1])
|
|
38
|
+
n = np.array([0.03, 0.03, 0.03, 0.03, 0.03])
|
|
39
|
+
rpl = np.array([1., 1., 1., 1., 1.])
|
|
40
|
+
panel_markers = np.array([True, False, True, False, True])
|
|
41
|
+
result = calculate_cross_section_conveyance(x, y, n, rpl, panel_markers)
|
|
42
|
+
print(result)
|
|
43
|
+
"""
|
|
44
|
+
# Create a set of water levels to calculate conveyance at,
|
|
45
|
+
# currently using 50mm minimum increments plus WLs at every data point
|
|
46
|
+
wls = insert_intermediate_wls(np.unique(y), threshold=0.05)
|
|
47
|
+
|
|
48
|
+
# Panel markers are forced true to the bounds to make the process work
|
|
49
|
+
panel_markers = np.array([True, *panel_markers[1:-1], True])
|
|
50
|
+
panel_indices = np.where(panel_markers)[0]
|
|
51
|
+
conveyance_by_panel = []
|
|
52
|
+
for panel_start, panel_end in zip(panel_indices[:-1], panel_indices[1:] + 1):
|
|
53
|
+
panel_x = x[panel_start:panel_end]
|
|
54
|
+
panel_y = y[panel_start:panel_end]
|
|
55
|
+
panel_n = n[panel_start:panel_end]
|
|
56
|
+
# RPL value is only valid for the start of a panel, and set to 1 if it's zero
|
|
57
|
+
panel_rpl = (
|
|
58
|
+
1.0
|
|
59
|
+
if (panel_start == 0 and not panel_markers[0]) or rpl[panel_start] == 0
|
|
60
|
+
else float(rpl[panel_start])
|
|
61
|
+
)
|
|
62
|
+
conveyance_by_panel.append(
|
|
63
|
+
calculate_conveyance_by_panel(panel_x, panel_y, panel_n, panel_rpl, wls),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Sum conveyance across panels
|
|
67
|
+
conveyance_values = [sum(values) for values in zip(*conveyance_by_panel)]
|
|
68
|
+
|
|
69
|
+
return pd.Series(data=conveyance_values, index=wls)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def calculate_conveyance_by_panel(
|
|
73
|
+
x: np.ndarray,
|
|
74
|
+
y: np.ndarray,
|
|
75
|
+
n: np.ndarray,
|
|
76
|
+
rpl: float,
|
|
77
|
+
wls: np.ndarray,
|
|
78
|
+
) -> list[float]:
|
|
79
|
+
"""
|
|
80
|
+
Calculate the conveyance for a single panel of a cross-section at specified water levels.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
x (np.ndarray): The x-coordinates of the panel.
|
|
84
|
+
y (np.ndarray): The y-coordinates of the panel.
|
|
85
|
+
n (np.ndarray): Manning's n values for each segment in the panel.
|
|
86
|
+
rpl (float): Relative Path Length for each segment in the panel.
|
|
87
|
+
wls (np.ndarray): The water levels at which to calculate conveyance.
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
list[float]: A list of conveyance values for each water level.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
max_y = np.max(wls) + 1
|
|
94
|
+
min_y = np.min(wls) - 1
|
|
95
|
+
|
|
96
|
+
# insert additional start/end points to represent the glass wall sides
|
|
97
|
+
x = np.array([x[0], *x, x[-1]])
|
|
98
|
+
y = np.array([max_y, *y, max_y])
|
|
99
|
+
n = np.array([0, *n, 0])
|
|
100
|
+
|
|
101
|
+
# Define a polygon for the channel including artificial sides and top
|
|
102
|
+
channel_polygon = Polygon(zip(x, y))
|
|
103
|
+
start, end = x[0] - 0.1, x[-1] + 0.1 # Useful points enclosing the x bounds with small buffer
|
|
104
|
+
|
|
105
|
+
# Define linestring geometries representing glass walls, so they can be subtracted later
|
|
106
|
+
glass_walls = (
|
|
107
|
+
LineString(zip([x[0], x[1]], [y[0], y[1]])), # left
|
|
108
|
+
LineString(zip([x[-2], x[-1]], [y[-2], y[-1]])), # right
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# Remove glass wall sections from coords
|
|
112
|
+
x, y, n = x[1:-1], y[1:-1], n[1:-1]
|
|
113
|
+
|
|
114
|
+
conveyance_values = []
|
|
115
|
+
for wl in wls:
|
|
116
|
+
if wl <= np.min(y):
|
|
117
|
+
# no channel capacity (essentially zero depth) so no need to calculate
|
|
118
|
+
conveyance_values.append(0.0)
|
|
119
|
+
continue
|
|
120
|
+
|
|
121
|
+
# Some geometries to represent the channel at a given water level
|
|
122
|
+
water_surface = Polygon(zip([start, start, end, end], [wl, min_y, min_y, wl]))
|
|
123
|
+
water_plane = intersection(channel_polygon, LineString(zip([start, end], [wl, wl])))
|
|
124
|
+
wetted_polygon = intersection(channel_polygon, water_surface)
|
|
125
|
+
|
|
126
|
+
multiple_parts = wetted_polygon.geom_type in ["GeometryCollection", "MultiPolygon"]
|
|
127
|
+
parts = wetted_polygon.geoms if multiple_parts else [wetted_polygon]
|
|
128
|
+
|
|
129
|
+
conveyance = 0.0
|
|
130
|
+
|
|
131
|
+
# 'parts' here refers to when a water level results in 2 separate channel sections,
|
|
132
|
+
# e.g. where the cross section has a 'peak' part way through
|
|
133
|
+
for part in parts:
|
|
134
|
+
conveyance += calculate_conveyance_part(part, water_plane, glass_walls, x, n, rpl)
|
|
135
|
+
conveyance_values.append(conveyance)
|
|
136
|
+
|
|
137
|
+
return conveyance_values
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def calculate_conveyance_part( # noqa: PLR0913
|
|
141
|
+
wetted_polygon: Polygon,
|
|
142
|
+
water_plane: LineString,
|
|
143
|
+
glass_walls: tuple[LineString, LineString],
|
|
144
|
+
x: np.ndarray,
|
|
145
|
+
n: np.ndarray,
|
|
146
|
+
rpl: float,
|
|
147
|
+
) -> float:
|
|
148
|
+
"""
|
|
149
|
+
Calculate the conveyance for a part of the wetted area.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
wetted_polygon (Polygon): The polygon representing the wetted area.
|
|
153
|
+
water_plane (LineString): The line representing the water plane.
|
|
154
|
+
glass_wall_left (LineString): The left boundary of the channel.
|
|
155
|
+
glass_wall_right (LineString): The right boundary of the channel.
|
|
156
|
+
x (np.ndarray): 1D array of channel chainage
|
|
157
|
+
n (np.ndarray): 1D array of channel mannings
|
|
158
|
+
rpl (float): Relative path length of panel
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
float: The conveyance value for the wetted part.
|
|
162
|
+
"""
|
|
163
|
+
water_plane_clip: LineString = intersection(water_plane, wetted_polygon)
|
|
164
|
+
glass_wall_left_clip: LineString = intersection(glass_walls[0], wetted_polygon)
|
|
165
|
+
glass_wall_right_clip: LineString = intersection(glass_walls[1], wetted_polygon)
|
|
166
|
+
|
|
167
|
+
# wetted perimeter should only account for actual section of channel, so we need to remove any
|
|
168
|
+
# length related to the water surface and any glass walls due to panel
|
|
169
|
+
perimeter_loss = (
|
|
170
|
+
water_plane_clip.length + glass_wall_left_clip.length + glass_wall_right_clip.length
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
wetted_perimeter = wetted_polygon.boundary.length - perimeter_loss
|
|
174
|
+
if wetted_perimeter < MINIMUM_PERIMETER_THRESHOLD:
|
|
175
|
+
# Would occur if water level is above lowest point on section, but intersects a near-zero
|
|
176
|
+
# perimeter, e.g. touching the bottom of an elevated side channel
|
|
177
|
+
return 0.0
|
|
178
|
+
|
|
179
|
+
area = wetted_polygon.area
|
|
180
|
+
|
|
181
|
+
wetted_polyline: LineString = (
|
|
182
|
+
wetted_polygon.exterior.difference(water_plane_clip)
|
|
183
|
+
.difference(glass_wall_left_clip)
|
|
184
|
+
.difference(glass_wall_right_clip)
|
|
185
|
+
)
|
|
186
|
+
weighted_mannings = calculate_weighted_mannings(x, n, rpl, wetted_polyline)
|
|
187
|
+
|
|
188
|
+
# apply conveyance equation
|
|
189
|
+
return (area ** (5 / 3) / wetted_perimeter ** (2 / 3)) * (wetted_perimeter / weighted_mannings)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def insert_intermediate_wls(arr: np.ndarray, threshold: float):
|
|
193
|
+
"""
|
|
194
|
+
Insert intermediate water levels into an array based on a threshold.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
arr (np.ndarray): The array of original water levels.
|
|
198
|
+
threshold (float): The maximum allowed gap between water levels.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
np.ndarray: The array with intermediate water levels inserted.
|
|
202
|
+
"""
|
|
203
|
+
# Calculate gaps between consecutive elements
|
|
204
|
+
gaps = np.diff(arr)
|
|
205
|
+
|
|
206
|
+
# Calculate the number of points needed for each gap
|
|
207
|
+
num_points = (gaps // threshold).astype(int)
|
|
208
|
+
|
|
209
|
+
# Prepare lists to hold the new points and results
|
|
210
|
+
new_points = []
|
|
211
|
+
|
|
212
|
+
for i, start in enumerate(arr[:-1]):
|
|
213
|
+
end = arr[i + 1]
|
|
214
|
+
if num_points[i] > 0:
|
|
215
|
+
points = np.linspace(start, end, num_points[i] + 2)[1:-1]
|
|
216
|
+
new_points.extend(points)
|
|
217
|
+
new_points.append(end)
|
|
218
|
+
|
|
219
|
+
# Combine the original starting point with the new points
|
|
220
|
+
return np.array([arr[0]] + new_points)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def calculate_weighted_mannings(
|
|
224
|
+
x: np.ndarray,
|
|
225
|
+
n: np.ndarray,
|
|
226
|
+
rpl: float,
|
|
227
|
+
wetted_polyline: LineString,
|
|
228
|
+
) -> float:
|
|
229
|
+
"""Calculate the weighted Manning's n value for a wetted polyline."""
|
|
230
|
+
|
|
231
|
+
# We want the polyline to be split into each individual segment
|
|
232
|
+
segments = line_to_segments(wetted_polyline)
|
|
233
|
+
weighted_mannings = 0
|
|
234
|
+
for segment in segments:
|
|
235
|
+
mannings_value = get_mannings_by_segment_x_coords(
|
|
236
|
+
x,
|
|
237
|
+
n,
|
|
238
|
+
segment.coords[0][0],
|
|
239
|
+
segment.coords[1][0],
|
|
240
|
+
)
|
|
241
|
+
weighted_mannings += mannings_value * segment.length * np.sqrt(rpl)
|
|
242
|
+
|
|
243
|
+
return weighted_mannings
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def line_to_segments(line: LineString | MultiLineString) -> list[LineString]:
|
|
247
|
+
"""Convert a LineString or MultiLineString into a list of LineString segments."""
|
|
248
|
+
if isinstance(line, LineString):
|
|
249
|
+
segments = []
|
|
250
|
+
for start, end in zip(line.coords[:-1], line.coords[1:]):
|
|
251
|
+
points = sorted([start, end], key=lambda x: x[0])
|
|
252
|
+
segments.append(LineString(points))
|
|
253
|
+
return segments
|
|
254
|
+
if isinstance(line, MultiLineString):
|
|
255
|
+
segments = []
|
|
256
|
+
for linestring in line.geoms:
|
|
257
|
+
segments.extend(line_to_segments(linestring))
|
|
258
|
+
return segments
|
|
259
|
+
raise TypeError("Input must be a LineString or MultiLineString")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def get_mannings_by_segment_x_coords(
|
|
263
|
+
x: np.ndarray,
|
|
264
|
+
n: np.ndarray,
|
|
265
|
+
start_x: float,
|
|
266
|
+
end_x: float,
|
|
267
|
+
) -> float:
|
|
268
|
+
"""Get the Manning's n or RPL value for a segment based on its start x-coordinate."""
|
|
269
|
+
|
|
270
|
+
# This method doesn't handle cases where we have multiple manning's values at a vertical section
|
|
271
|
+
# and will always just take the first at any verticle, but it is probably quite rare for this
|
|
272
|
+
# not to be the case
|
|
273
|
+
if start_x == end_x:
|
|
274
|
+
# Vertical segment take first x match
|
|
275
|
+
index = np.searchsorted(x, start_x) - (start_x not in x)
|
|
276
|
+
else:
|
|
277
|
+
# Otherwise non-vertical segment, take last match
|
|
278
|
+
index = np.searchsorted(x, start_x, side="right") - 1
|
|
279
|
+
|
|
280
|
+
return n[index]
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
@lru_cache
|
|
284
|
+
def calculate_cross_section_conveyance_chached(
|
|
285
|
+
x: tuple[float],
|
|
286
|
+
y: tuple[float],
|
|
287
|
+
n: tuple[float],
|
|
288
|
+
rpl: tuple[float],
|
|
289
|
+
panel_markers: tuple[float],
|
|
290
|
+
) -> pd.Series:
|
|
291
|
+
"""Dummy function to allow for caching of the conveyance function as numpy arrays are not
|
|
292
|
+
hashable
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
return calculate_cross_section_conveyance(
|
|
296
|
+
np.array(x),
|
|
297
|
+
np.array(y),
|
|
298
|
+
np.array(n),
|
|
299
|
+
np.array(rpl),
|
|
300
|
+
np.array(panel_markers),
|
|
301
|
+
)
|
|
@@ -1,131 +1,123 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Flood Modeller Python API
|
|
3
|
-
Copyright (C)
|
|
4
|
-
|
|
5
|
-
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
|
-
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
7
|
-
|
|
8
|
-
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
-
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
10
|
-
|
|
11
|
-
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
|
|
12
|
-
|
|
13
|
-
If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
|
|
14
|
-
address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
from
|
|
18
|
-
|
|
19
|
-
# Helper Functions
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
else:
|
|
125
|
-
row_split = [_to_float(itm) for itm in row_split]
|
|
126
|
-
|
|
127
|
-
row_list = []
|
|
128
|
-
for var in row_split:
|
|
129
|
-
row_list.append(var)
|
|
130
|
-
data_list.append(row_list)
|
|
131
|
-
return data_list
|
|
1
|
+
"""
|
|
2
|
+
Flood Modeller Python API
|
|
3
|
+
Copyright (C) 2024 Jacobs U.K. Limited
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
|
+
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
7
|
+
|
|
8
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
10
|
+
|
|
11
|
+
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
|
|
12
|
+
|
|
13
|
+
If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
|
|
14
|
+
address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
# Helper Functions
|
|
20
|
+
NOTATION_THRESHOLD = 10
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def split_10_char(line):
|
|
24
|
+
return [line[i : i + 10].strip() for i in range(0, len(line), 10)]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def split_12_char(line):
|
|
28
|
+
return [line[i : i + 12].strip() for i in range(0, len(line), 12)]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def split_n_char(line, n):
|
|
32
|
+
return [line[i : i + n].strip() for i in range(0, len(line), n)]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def join_10_char(*itms, dp=3):
|
|
36
|
+
"""Joins a set of values with a 10 character buffer and right-justified"""
|
|
37
|
+
string = ""
|
|
38
|
+
for itm in itms:
|
|
39
|
+
if isinstance(itm, float):
|
|
40
|
+
# save to 3 dp
|
|
41
|
+
# Use scientific notation if number greater than NOTATION_THRESHOLD characters
|
|
42
|
+
itm = f"{itm:.{dp}e}" if len(f"{itm:.{dp}f}") > NOTATION_THRESHOLD else f"{itm:.{dp}f}"
|
|
43
|
+
itm = str(itm)
|
|
44
|
+
itm = itm[:10]
|
|
45
|
+
string += f"{itm:>10}"
|
|
46
|
+
return string
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def join_12_char_ljust(*itms, dp=3):
|
|
50
|
+
"""Joins a set of values with a 12 character buffer and left-justified"""
|
|
51
|
+
string = ""
|
|
52
|
+
for itm in itms:
|
|
53
|
+
if isinstance(itm, float):
|
|
54
|
+
# save to 3 dp
|
|
55
|
+
# Use scientific notation if number greater than 10 characters
|
|
56
|
+
itm = f"{itm:.{dp}e}" if len(f"{itm:.{dp}f}") > NOTATION_THRESHOLD else f"{itm:.{dp}f}"
|
|
57
|
+
itm = str(itm)
|
|
58
|
+
itm = itm[:12]
|
|
59
|
+
string += f"{itm:<12}"
|
|
60
|
+
return string
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def join_n_char_ljust(n, *itms, dp=3):
|
|
64
|
+
"""Joins a set of values with a n character buffer and left-justified"""
|
|
65
|
+
string = ""
|
|
66
|
+
for itm in itms:
|
|
67
|
+
if isinstance(itm, float):
|
|
68
|
+
# save to 3 dp
|
|
69
|
+
# Use scientific notation if number greater than 10 characters
|
|
70
|
+
itm = f"{itm:.{dp}e}" if len(f"{itm:.{dp}f}") > NOTATION_THRESHOLD else f"{itm:.{dp}f}"
|
|
71
|
+
itm = str(itm)
|
|
72
|
+
itm = itm[:n]
|
|
73
|
+
string += f"{itm:<{n}}"
|
|
74
|
+
return string
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _to_float(itm, default=0.0):
|
|
78
|
+
try:
|
|
79
|
+
return float(itm)
|
|
80
|
+
except ValueError:
|
|
81
|
+
return default
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _to_int(itm, default=0):
|
|
85
|
+
try:
|
|
86
|
+
return int(itm)
|
|
87
|
+
except ValueError:
|
|
88
|
+
return default
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _to_str(itm, default, check_float=False):
|
|
92
|
+
if check_float:
|
|
93
|
+
try:
|
|
94
|
+
return float(itm)
|
|
95
|
+
except ValueError:
|
|
96
|
+
pass
|
|
97
|
+
if itm == "":
|
|
98
|
+
return default
|
|
99
|
+
return itm
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _to_data_list(block: list[str], num_cols: int | None = None, date_col: int | None = None):
|
|
103
|
+
if num_cols is not None:
|
|
104
|
+
num_cols += 1 if date_col is not None else 0
|
|
105
|
+
data_list = []
|
|
106
|
+
for row in block:
|
|
107
|
+
row_split = split_10_char(row) if num_cols is None else split_10_char(row)[:num_cols]
|
|
108
|
+
if date_col is not None:
|
|
109
|
+
date_time = " ".join(row_split[date_col : date_col + 2])
|
|
110
|
+
row_split = [
|
|
111
|
+
_to_float(itm)
|
|
112
|
+
for idx, itm in enumerate(row_split)
|
|
113
|
+
if idx not in (date_col, date_col + 1)
|
|
114
|
+
]
|
|
115
|
+
row_split.insert(date_col, date_time)
|
|
116
|
+
else:
|
|
117
|
+
row_split = [_to_float(itm) for itm in row_split]
|
|
118
|
+
|
|
119
|
+
row_list = []
|
|
120
|
+
for var in row_split:
|
|
121
|
+
row_list.append(var)
|
|
122
|
+
data_list.append(row_list)
|
|
123
|
+
return data_list
|