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.
Files changed (178) hide show
  1. floodmodeller_api/__init__.py +8 -9
  2. floodmodeller_api/_base.py +169 -176
  3. floodmodeller_api/backup.py +273 -273
  4. floodmodeller_api/dat.py +889 -831
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +302 -306
  7. floodmodeller_api/ief.py +553 -637
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +260 -266
  10. floodmodeller_api/libs/libifcoremd.dll +0 -0
  11. floodmodeller_api/libs/libifcoremt.so.5 +0 -0
  12. floodmodeller_api/libs/libifport.so.5 +0 -0
  13. floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
  14. floodmodeller_api/libs/libintlc.so.5 +0 -0
  15. floodmodeller_api/libs/libmmd.dll +0 -0
  16. floodmodeller_api/libs/libsvml.so +0 -0
  17. floodmodeller_api/libs/libzzn_read.so +0 -0
  18. floodmodeller_api/libs/zzn_read.dll +0 -0
  19. floodmodeller_api/logs/__init__.py +2 -2
  20. floodmodeller_api/logs/lf.py +364 -312
  21. floodmodeller_api/logs/lf_helpers.py +354 -352
  22. floodmodeller_api/logs/lf_params.py +643 -529
  23. floodmodeller_api/mapping.py +84 -0
  24. floodmodeller_api/test/__init__.py +4 -4
  25. floodmodeller_api/test/conftest.py +16 -8
  26. floodmodeller_api/test/test_backup.py +117 -117
  27. floodmodeller_api/test/test_conveyance.py +107 -0
  28. floodmodeller_api/test/test_dat.py +222 -92
  29. floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
  30. floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
  31. floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
  32. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
  33. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
  34. floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
  35. floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
  36. floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
  37. floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
  38. floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
  39. floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
  40. floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
  41. floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
  42. floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
  43. floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
  44. floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
  45. floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
  46. floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
  47. floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
  48. floodmodeller_api/test/test_data/EX1.DAT +321 -321
  49. floodmodeller_api/test/test_data/EX1.ext +107 -107
  50. floodmodeller_api/test/test_data/EX1.feb +320 -320
  51. floodmodeller_api/test/test_data/EX1.gxy +107 -107
  52. floodmodeller_api/test/test_data/EX17.DAT +421 -422
  53. floodmodeller_api/test/test_data/EX17.ext +213 -213
  54. floodmodeller_api/test/test_data/EX17.feb +422 -422
  55. floodmodeller_api/test/test_data/EX18.DAT +375 -375
  56. floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
  57. floodmodeller_api/test/test_data/EX2.DAT +302 -302
  58. floodmodeller_api/test/test_data/EX3.DAT +926 -926
  59. floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
  60. floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
  61. floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
  62. floodmodeller_api/test/test_data/EX6.ext +532 -532
  63. floodmodeller_api/test/test_data/EX6.feb +2084 -2084
  64. floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
  65. floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
  66. floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
  67. floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
  68. floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
  69. floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
  70. floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
  71. floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
  72. floodmodeller_api/test/test_data/blockage.dat +50 -50
  73. floodmodeller_api/test/test_data/blockage.ext +45 -45
  74. floodmodeller_api/test/test_data/blockage.feb +9 -9
  75. floodmodeller_api/test/test_data/blockage.gxy +71 -71
  76. floodmodeller_api/test/test_data/conveyance_test.dat +165 -0
  77. floodmodeller_api/test/test_data/conveyance_test.feb +116 -0
  78. floodmodeller_api/test/test_data/conveyance_test.gxy +85 -0
  79. floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
  80. floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
  81. floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
  82. floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
  83. floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
  84. floodmodeller_api/test/test_data/ex3.ief +20 -20
  85. floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
  86. floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
  87. floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
  88. floodmodeller_api/test/test_data/example1.inp +329 -329
  89. floodmodeller_api/test/test_data/example2.inp +158 -158
  90. floodmodeller_api/test/test_data/example3.inp +297 -297
  91. floodmodeller_api/test/test_data/example4.inp +388 -388
  92. floodmodeller_api/test/test_data/example5.inp +147 -147
  93. floodmodeller_api/test/test_data/example6.inp +154 -154
  94. floodmodeller_api/test/test_data/expected_conveyance.csv +60 -0
  95. floodmodeller_api/test/test_data/jump.dat +176 -176
  96. floodmodeller_api/test/test_data/network.dat +1374 -1374
  97. floodmodeller_api/test/test_data/network.ext +45 -45
  98. floodmodeller_api/test/test_data/network.exy +1 -1
  99. floodmodeller_api/test/test_data/network.feb +45 -45
  100. floodmodeller_api/test/test_data/network.ied +45 -45
  101. floodmodeller_api/test/test_data/network.ief +20 -20
  102. floodmodeller_api/test/test_data/network.inp +147 -147
  103. floodmodeller_api/test/test_data/network.pxy +57 -57
  104. floodmodeller_api/test/test_data/network.zzd +122 -122
  105. floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
  106. floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
  107. floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
  108. floodmodeller_api/test/test_data/rnweir.dat +9 -9
  109. floodmodeller_api/test/test_data/rnweir.ext +45 -45
  110. floodmodeller_api/test/test_data/rnweir.feb +9 -9
  111. floodmodeller_api/test/test_data/rnweir.gxy +45 -45
  112. floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
  113. floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
  114. floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
  115. floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
  116. floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
  117. floodmodeller_api/test/test_data/unit checks.dat +16 -16
  118. floodmodeller_api/test/test_ied.py +29 -29
  119. floodmodeller_api/test/test_ief.py +136 -24
  120. floodmodeller_api/test/test_inp.py +47 -48
  121. floodmodeller_api/test/test_json.py +114 -0
  122. floodmodeller_api/test/test_logs_lf.py +102 -51
  123. floodmodeller_api/test/test_tool.py +165 -152
  124. floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
  125. floodmodeller_api/test/test_xml2d.py +151 -156
  126. floodmodeller_api/test/test_zzn.py +36 -34
  127. floodmodeller_api/to_from_json.py +230 -0
  128. floodmodeller_api/tool.py +332 -329
  129. floodmodeller_api/toolbox/__init__.py +5 -5
  130. floodmodeller_api/toolbox/example_tool.py +45 -45
  131. floodmodeller_api/toolbox/model_build/__init__.py +2 -2
  132. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
  133. floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
  134. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
  135. floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
  136. floodmodeller_api/units/__init__.py +10 -10
  137. floodmodeller_api/units/_base.py +214 -212
  138. floodmodeller_api/units/boundaries.py +467 -467
  139. floodmodeller_api/units/comment.py +52 -55
  140. floodmodeller_api/units/conduits.py +382 -402
  141. floodmodeller_api/units/conveyance.py +301 -0
  142. floodmodeller_api/units/helpers.py +123 -131
  143. floodmodeller_api/units/iic.py +107 -101
  144. floodmodeller_api/units/losses.py +305 -306
  145. floodmodeller_api/units/sections.py +465 -446
  146. floodmodeller_api/units/structures.py +1690 -1683
  147. floodmodeller_api/units/units.py +93 -104
  148. floodmodeller_api/units/unsupported.py +44 -44
  149. floodmodeller_api/units/variables.py +87 -89
  150. floodmodeller_api/urban1d/__init__.py +11 -11
  151. floodmodeller_api/urban1d/_base.py +188 -179
  152. floodmodeller_api/urban1d/conduits.py +93 -85
  153. floodmodeller_api/urban1d/general_parameters.py +58 -58
  154. floodmodeller_api/urban1d/junctions.py +81 -79
  155. floodmodeller_api/urban1d/losses.py +81 -74
  156. floodmodeller_api/urban1d/outfalls.py +114 -110
  157. floodmodeller_api/urban1d/raingauges.py +111 -111
  158. floodmodeller_api/urban1d/subsections.py +92 -98
  159. floodmodeller_api/urban1d/xsections.py +147 -144
  160. floodmodeller_api/util.py +119 -21
  161. floodmodeller_api/validation/parameters.py +660 -660
  162. floodmodeller_api/validation/urban_parameters.py +388 -404
  163. floodmodeller_api/validation/validation.py +110 -108
  164. floodmodeller_api/version.py +1 -1
  165. floodmodeller_api/xml2d.py +632 -673
  166. floodmodeller_api/xml2d_template.py +37 -37
  167. floodmodeller_api/zzn.py +414 -363
  168. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/LICENSE.txt +13 -13
  169. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/METADATA +85 -82
  170. floodmodeller_api-0.4.4.dist-info/RECORD +185 -0
  171. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/WHEEL +1 -1
  172. floodmodeller_api/libifcoremd.dll +0 -0
  173. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  174. floodmodeller_api/test/test_data/test_output.csv +0 -87
  175. floodmodeller_api/zzn_read.dll +0 -0
  176. floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
  177. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/entry_points.txt +0 -0
  178. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/top_level.txt +0 -0
@@ -1,446 +1,465 @@
1
- """
2
- Flood Modeller Python API
3
- Copyright (C) 2023 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 typing import List
18
-
19
- import pandas as pd
20
-
21
- from floodmodeller_api.validation import _validate_unit
22
-
23
- from ._base import Unit
24
- from .helpers import (
25
- _to_float,
26
- _to_int,
27
- join_10_char,
28
- join_n_char_ljust,
29
- split_10_char,
30
- split_n_char,
31
- )
32
-
33
-
34
- class RIVER(Unit):
35
- """Class to hold and process RIVER unit type. Currently only river units that are 'SECTION' types are supported.
36
- Other river unit types such as Muskingham will be included in a future release.
37
-
38
- Args:
39
- name (str, optional): River section name
40
- comment (str, optional): Comment included in unit
41
- data (pandas.Dataframe): Dataframe object containing all the cross section data as well as all other relevant data.
42
- Columns are ``'X', 'Y', 'Mannings n', 'Panel', 'RPL', 'Marker', 'Easting', 'Northing', 'Deactivation', 'SP. Marker'``
43
- spill1, spill2 (str, optional): Spill label
44
- lat1, lat2, lat3, lat4 (str, optional): Lateral inflow label
45
- dist_to_next (float, optional): Distance to next section in metres
46
- slope (float, optional): Slope used in normal depth calculations
47
- density (float, optional): Density in kg/m3
48
-
49
- Raises:
50
- NotImplementedError: Raised if class is initialised without existing river block (i.e. if attempting to create new River unit).
51
- This will be an option for future releases
52
-
53
- Returns:
54
- RIVER: Flood Modeller RIVER Unit class object
55
-
56
- Methods:
57
- convert_to_muskingham: Not currently supported but planned for future release
58
- """
59
-
60
- _unit = "RIVER"
61
-
62
- def _create_from_blank(
63
- self,
64
- name="new_section",
65
- comment="",
66
- spill1="",
67
- spill2="",
68
- lat1="",
69
- lat2="",
70
- lat3="",
71
- lat4="",
72
- dist_to_next=0,
73
- slope=0.0001,
74
- density=1000.0,
75
- data=None,
76
- ):
77
- # Initiate new SECTION (currently hardcoding this as default)
78
- self._subtype = "SECTION"
79
-
80
- for param, val in {
81
- "name": name,
82
- "comment": comment,
83
- "spill1": spill1,
84
- "spill2": spill2,
85
- "lat1": lat1,
86
- "lat2": lat2,
87
- "lat3": lat3,
88
- "lat4": lat4,
89
- "dist_to_next": dist_to_next,
90
- "slope": slope,
91
- "density": density,
92
- "data": data,
93
- }.items():
94
- setattr(self, param, val)
95
-
96
- self.data = (
97
- data
98
- if isinstance(data, pd.DataFrame)
99
- else pd.DataFrame(
100
- [],
101
- columns=[
102
- "X",
103
- "Y",
104
- "Mannings n",
105
- "Panel",
106
- "RPL",
107
- "Marker",
108
- "Easting",
109
- "Northing",
110
- "Deactivation",
111
- "SP. Marker",
112
- ],
113
- )
114
- )
115
-
116
- def _read(self, riv_block):
117
- """Function to read a given RIVER block and store data as class attributes."""
118
-
119
- self._subtype = riv_block[1].split(" ")[0].strip()
120
- # Only supporting 'SECTION' subtype for now
121
- if self.subtype == "SECTION":
122
- # Extends label line to be correct length before splitting to pick up blank labels
123
- labels = split_n_char(f"{riv_block[2]:<{7*self._label_len}}", self._label_len)
124
- self.name = labels[0]
125
- self.spill1 = labels[1]
126
- self.spill2 = labels[2]
127
- self.lat1 = labels[3]
128
- self.lat2 = labels[4]
129
- self.lat3 = labels[5]
130
- self.lat4 = labels[6]
131
- self.comment = riv_block[0].replace("RIVER", "").strip()
132
-
133
- params = split_10_char(f"{riv_block[3]:<40}")
134
- self.dist_to_next = _to_float(params[0])
135
- self.slope = _to_float(params[2], 0.0001)
136
- self.density = _to_float(params[3], 1000.0)
137
- self.nrows = int(split_10_char(riv_block[4])[0])
138
- data_list = []
139
- for row in riv_block[5:]:
140
- row_split = split_10_char(f"{row:<100}")
141
- x = _to_float(row_split[0]) # chainage
142
- y = _to_float(row_split[1]) # elevation
143
- n = _to_float(row_split[2]) # Mannings
144
- try:
145
- # panel marker
146
- panel = row_split[3][0] == "*"
147
- except IndexError:
148
- panel = False
149
-
150
- try:
151
- # relative path length
152
- rpl = _to_float(row_split[3][1 if panel else 0 :].strip())
153
- except IndexError:
154
- rpl = 0.000
155
- marker = row_split[4] # Marker
156
- easting = _to_float(row_split[5]) # easting
157
- northing = _to_float(row_split[6]) # northing
158
-
159
- deactivation = row_split[7] # deactivation marker
160
- sp_marker = _to_int(row_split[8]) # special marker
161
- data_list.append(
162
- [
163
- x,
164
- y,
165
- n,
166
- panel,
167
- rpl,
168
- marker,
169
- easting,
170
- northing,
171
- deactivation,
172
- sp_marker,
173
- ]
174
- )
175
- self.data = pd.DataFrame(
176
- data_list,
177
- columns=[
178
- "X",
179
- "Y",
180
- "Mannings n",
181
- "Panel",
182
- "RPL",
183
- "Marker",
184
- "Easting",
185
- "Northing",
186
- "Deactivation",
187
- "SP. Marker",
188
- ],
189
- )
190
-
191
- else:
192
- # This else block is triggered for river subtypes which aren't yet supported, and just keeps the 'riv_block' in it's raw state to write back.
193
- print(
194
- f'This River sub-type: "{self.subtype}" is currently unsupported for reading/editing'
195
- )
196
- self._raw_block = riv_block
197
- self.name = riv_block[2][: self._label_len].strip()
198
-
199
- def _write(self):
200
- """Function to write a valid RIVER block"""
201
-
202
- if self.subtype == "SECTION":
203
- # Function to check the params are valid for RIVER SECTION unit
204
- _validate_unit(self)
205
- header = "RIVER " + self.comment
206
- labels = join_n_char_ljust(
207
- self._label_len,
208
- self.name,
209
- self.spill1,
210
- self.spill2,
211
- self.lat1,
212
- self.lat2,
213
- self.lat3,
214
- self.lat4,
215
- )
216
- # Manual so slope can have more sf
217
- params = f'{self.dist_to_next:>10.3f}{"":>10}{self.slope:>10.6f}{self.density:>10.3f}'
218
- self.nrows = len(self.data)
219
- riv_block = [header, self.subtype, labels, params, f"{str(self.nrows):>10}"]
220
-
221
- riv_data = []
222
- for (
223
- _,
224
- x,
225
- y,
226
- n,
227
- panel,
228
- rpl,
229
- marker,
230
- easting,
231
- northing,
232
- deactivation,
233
- sp_marker,
234
- ) in self.data.itertuples():
235
- row = join_10_char(x, y, n)
236
- if panel:
237
- row += "*"
238
- else:
239
- row += " "
240
- row += f"{rpl:>9.3f}{join_10_char(marker, easting, northing, deactivation, str(sp_marker))}"
241
- riv_data.append(row)
242
-
243
- riv_block.extend(riv_data)
244
-
245
- return riv_block
246
-
247
- return self._raw_block
248
-
249
-
250
- class INTERPOLATE(Unit):
251
- """Class to hold and process INTERPOLATE unit type
252
-
253
- Args:
254
- name (str, optional): Unit name.
255
- comment (str, optional): Comment included in unit.
256
- first_spill (str, optional): Spill label if required.
257
- second_spill (str, optional): Spill label if required.
258
- lat1 (str, optional): First lateral inflow label.
259
- lat2 (str, optional): Second lateral inflow label.
260
- lat3 (str, optional): Third lateral inflow label.
261
- lat4 (str, optional): Fourth lateral inflow label.
262
- dist_to_next (float, optional): Chainage downstream to following section (m).
263
- easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
264
- northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
265
-
266
- Returns:
267
- INTERPOLATE: Flood Modeller INTERPOLATE Unit class object"""
268
-
269
- _unit = "INTERPOLATE"
270
-
271
- def _read(self, block):
272
- """Function to read a given INTERPOLATE WEIR block and store data as class attributes"""
273
-
274
- # Extends label line to be correct length before splitting to pick up blank labels
275
- labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
276
- self.name = labels[0]
277
- self.first_spill = labels[1]
278
- self.second_spill = labels[2]
279
- self.lat1 = labels[3]
280
- self.lat2 = labels[4]
281
- self.lat3 = labels[5]
282
- self.lat4 = labels[6]
283
- self.comment = block[0].replace("INTERPOLATE", "").strip()
284
-
285
- # First parameter line
286
- params1 = split_10_char(f"{block[2]:<30}")
287
- self.dist_to_next = _to_float(params1[0])
288
- self.easting = _to_float(params1[1])
289
- self.northing = _to_float(params1[2])
290
-
291
- def _write(self):
292
- """Function to write a valid INTERPOLATE block"""
293
-
294
- _validate_unit(self)
295
- header = "INTERPOLATE " + self.comment
296
- labels = join_n_char_ljust(
297
- self._label_len,
298
- self.name,
299
- self.first_spill,
300
- self.second_spill,
301
- self.lat1,
302
- self.lat2,
303
- self.lat3,
304
- self.lat4,
305
- )
306
- block = [header, labels]
307
-
308
- # First parameter line
309
- params1 = join_10_char(self.dist_to_next, self.easting, self.northing)
310
- block.append(params1)
311
-
312
- return block
313
-
314
- def _create_from_blank(
315
- self,
316
- name="new_interp",
317
- comment="",
318
- first_spill="",
319
- second_spill="",
320
- lat1="",
321
- lat2="",
322
- lat3="",
323
- lat4="",
324
- dist_to_next=0,
325
- easting=0,
326
- northing=0,
327
- ):
328
- for param, val in {
329
- "name": name,
330
- "comment": comment,
331
- "first_spill": first_spill,
332
- "second_spill": second_spill,
333
- "lat1": lat1,
334
- "lat2": lat2,
335
- "lat3": lat3,
336
- "lat4": lat4,
337
- "dist_to_next": dist_to_next,
338
- "easting": easting,
339
- "northing": northing,
340
- }.items():
341
- setattr(self, param, val)
342
-
343
-
344
- class REPLICATE(Unit):
345
- """Class to hold and process REPLICATE unit type
346
-
347
- Args:
348
- name (str, optional): Unit name.
349
- comment (str, optional): Comment included in unit.
350
- first_spill (str, optional): Spill label if required.
351
- second_spill (str, optional): Spill label if required.
352
- lat1 (str, optional): First lateral inflow label.
353
- lat2 (str, optional): Second lateral inflow label.
354
- lat3 (str, optional): Third lateral inflow label.
355
- lat4 (str, optional): Fourth lateral inflow label.
356
- dist_to_next (float, optional): Chainage downstream to following section (m).
357
- easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
358
- northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
359
- bed_level_drop (float, optional): Drop in bed level from previous section (m).
360
-
361
- Returns:
362
- REPLICATE: Flood Modeller REPLICATE Unit class object"""
363
-
364
- _unit = "REPLICATE"
365
-
366
- def _read(self, block: List[str]):
367
- """Function to read a given REPLICATE block and store data as class attributes"""
368
-
369
- # Extends label line to be correct length before splitting to pick up blank labels
370
- labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
371
- self.name = labels[0]
372
- self.first_spill = labels[1]
373
- self.second_spill = labels[2]
374
- self.lat1 = labels[3]
375
- self.lat2 = labels[4]
376
- self.lat3 = labels[5]
377
- self.lat4 = labels[6]
378
-
379
- self.comment = block[0].replace("REPLICATE", "").strip()
380
-
381
- # First parameter line
382
- params1 = split_10_char(f"{block[2]:<40}")
383
- self.dist_to_next = _to_float(params1[0])
384
- self.bed_level_drop = _to_float(params1[1])
385
- self.easting = _to_float(params1[2])
386
- self.northing = _to_float(params1[3])
387
-
388
- def _write(self):
389
- """Function to write a valid REPLICATE block"""
390
-
391
- _validate_unit(self)
392
- header = "REPLICATE " + self.comment
393
- labels = join_n_char_ljust(
394
- self._label_len,
395
- self.name,
396
- self.first_spill,
397
- self.second_spill,
398
- self.lat1,
399
- self.lat2,
400
- self.lat3,
401
- self.lat4,
402
- )
403
- block = [header, labels]
404
-
405
- # First parameter line
406
-
407
- params1 = join_10_char(
408
- self.dist_to_next,
409
- f"{self.bed_level_drop:>10.4f}", # allowing 4dp
410
- self.easting,
411
- self.northing,
412
- )
413
- block.append(params1)
414
-
415
- return block
416
-
417
- def _create_from_blank(
418
- self,
419
- name="new_repl",
420
- comment="",
421
- first_spill="",
422
- second_spill="",
423
- lat1="",
424
- lat2="",
425
- lat3="",
426
- lat4="",
427
- dist_to_next=0,
428
- bed_level_drop=0,
429
- easting=0,
430
- northing=0,
431
- ):
432
- for param, val in {
433
- "name": name,
434
- "comment": comment,
435
- "first_spill": first_spill,
436
- "second_spill": second_spill,
437
- "lat1": lat1,
438
- "lat2": lat2,
439
- "lat3": lat3,
440
- "lat4": lat4,
441
- "dist_to_next": dist_to_next,
442
- "bed_level_drop": bed_level_drop,
443
- "easting": easting,
444
- "northing": northing,
445
- }.items():
446
- setattr(self, param, val)
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
+ import pandas as pd
18
+
19
+ from floodmodeller_api.validation import _validate_unit
20
+
21
+ from ._base import Unit
22
+ from .conveyance import calculate_cross_section_conveyance_chached
23
+ from .helpers import (
24
+ _to_float,
25
+ _to_int,
26
+ join_10_char,
27
+ join_n_char_ljust,
28
+ split_10_char,
29
+ split_n_char,
30
+ )
31
+
32
+
33
+ class RIVER(Unit):
34
+ """Class to hold and process RIVER unit type. Currently only river units that are 'SECTION' types are supported.
35
+ Other river unit types such as Muskingham will be included in a future release.
36
+
37
+ Args:
38
+ name (str, optional): River section name
39
+ comment (str, optional): Comment included in unit
40
+ data (pandas.Dataframe): Dataframe object containing all the cross section data as well as all other relevant data.
41
+ Columns are ``'X', 'Y', 'Mannings n', 'Panel', 'RPL', 'Marker', 'Easting', 'Northing', 'Deactivation', 'SP. Marker'``
42
+ spill1, spill2 (str, optional): Spill label
43
+ lat1, lat2, lat3, lat4 (str, optional): Lateral inflow label
44
+ dist_to_next (float, optional): Distance to next section in metres
45
+ slope (float, optional): Slope used in normal depth calculations
46
+ density (float, optional): Density in kg/m3
47
+
48
+ Raises:
49
+ NotImplementedError: Raised if class is initialised without existing river block (i.e. if attempting to create new River unit).
50
+ This will be an option for future releases
51
+
52
+ Returns:
53
+ RIVER: Flood Modeller RIVER Unit class object
54
+
55
+ Methods:
56
+ convert_to_muskingham: Not currently supported but planned for future release
57
+ """
58
+
59
+ _unit = "RIVER"
60
+
61
+ def _create_from_blank( # noqa: PLR0913
62
+ self,
63
+ name="new_section",
64
+ comment="",
65
+ spill1="",
66
+ spill2="",
67
+ lat1="",
68
+ lat2="",
69
+ lat3="",
70
+ lat4="",
71
+ dist_to_next=0,
72
+ slope=0.0001,
73
+ density=1000.0,
74
+ data=None,
75
+ ):
76
+ # Initiate new SECTION (currently hardcoding this as default)
77
+ self._subtype = "SECTION"
78
+
79
+ for param, val in {
80
+ "name": name,
81
+ "comment": comment,
82
+ "spill1": spill1,
83
+ "spill2": spill2,
84
+ "lat1": lat1,
85
+ "lat2": lat2,
86
+ "lat3": lat3,
87
+ "lat4": lat4,
88
+ "dist_to_next": dist_to_next,
89
+ "slope": slope,
90
+ "density": density,
91
+ "data": data,
92
+ }.items():
93
+ setattr(self, param, val)
94
+
95
+ self.data = (
96
+ data
97
+ if isinstance(data, pd.DataFrame)
98
+ else pd.DataFrame(
99
+ [],
100
+ columns=[
101
+ "X",
102
+ "Y",
103
+ "Mannings n",
104
+ "Panel",
105
+ "RPL",
106
+ "Marker",
107
+ "Easting",
108
+ "Northing",
109
+ "Deactivation",
110
+ "SP. Marker",
111
+ ],
112
+ )
113
+ )
114
+
115
+ def _read(self, riv_block):
116
+ """Function to read a given RIVER block and store data as class attributes."""
117
+
118
+ self._subtype = riv_block[1].split(" ")[0].strip()
119
+ # Only supporting 'SECTION' subtype for now
120
+ if self.subtype == "SECTION":
121
+ # Extends label line to be correct length before splitting to pick up blank labels
122
+ labels = split_n_char(f"{riv_block[2]:<{7*self._label_len}}", self._label_len)
123
+ self.name = labels[0]
124
+ self.spill1 = labels[1]
125
+ self.spill2 = labels[2]
126
+ self.lat1 = labels[3]
127
+ self.lat2 = labels[4]
128
+ self.lat3 = labels[5]
129
+ self.lat4 = labels[6]
130
+ self.comment = riv_block[0].replace("RIVER", "").strip()
131
+
132
+ params = split_10_char(f"{riv_block[3]:<40}")
133
+ self.dist_to_next = _to_float(params[0])
134
+ self.slope = _to_float(params[2], 0.0001)
135
+ self.density = _to_float(params[3], 1000.0)
136
+ self.nrows = int(split_10_char(riv_block[4])[0])
137
+ data_list = []
138
+ for row in riv_block[5:]:
139
+ row_split = split_10_char(f"{row:<100}")
140
+ x = _to_float(row_split[0]) # chainage
141
+ y = _to_float(row_split[1]) # elevation
142
+ n = _to_float(row_split[2]) # Mannings
143
+ try:
144
+ # panel marker
145
+ panel = row_split[3][0] == "*"
146
+ except IndexError:
147
+ panel = False
148
+
149
+ try:
150
+ # relative path length
151
+ rpl = _to_float(row_split[3][1 if panel else 0 :].strip())
152
+ except IndexError:
153
+ rpl = 0.000
154
+ marker = row_split[4] # Marker
155
+ easting = _to_float(row_split[5]) # easting
156
+ northing = _to_float(row_split[6]) # northing
157
+
158
+ deactivation = row_split[7] # deactivation marker
159
+ sp_marker = _to_int(row_split[8]) # special marker
160
+ data_list.append(
161
+ [
162
+ x,
163
+ y,
164
+ n,
165
+ panel,
166
+ rpl,
167
+ marker,
168
+ easting,
169
+ northing,
170
+ deactivation,
171
+ sp_marker,
172
+ ],
173
+ )
174
+ self.data = pd.DataFrame(
175
+ data_list,
176
+ columns=[
177
+ "X",
178
+ "Y",
179
+ "Mannings n",
180
+ "Panel",
181
+ "RPL",
182
+ "Marker",
183
+ "Easting",
184
+ "Northing",
185
+ "Deactivation",
186
+ "SP. Marker",
187
+ ],
188
+ )
189
+
190
+ else:
191
+ # This else block is triggered for river subtypes which aren't yet supported, and just keeps the 'riv_block' in it's raw state to write back.
192
+ print(
193
+ f'This River sub-type: "{self.subtype}" is currently unsupported for reading/editing',
194
+ )
195
+ self._raw_block = riv_block
196
+ self.name = riv_block[2][: self._label_len].strip()
197
+
198
+ def _write(self):
199
+ """Function to write a valid RIVER block"""
200
+
201
+ if self.subtype == "SECTION":
202
+ # Function to check the params are valid for RIVER SECTION unit
203
+ _validate_unit(self)
204
+ header = "RIVER " + self.comment
205
+ labels = join_n_char_ljust(
206
+ self._label_len,
207
+ self.name,
208
+ self.spill1,
209
+ self.spill2,
210
+ self.lat1,
211
+ self.lat2,
212
+ self.lat3,
213
+ self.lat4,
214
+ )
215
+ # Manual so slope can have more sf
216
+ params = f'{self.dist_to_next:>10.3f}{"":>10}{self.slope:>10.6f}{self.density:>10.3f}'
217
+ self.nrows = len(self.data)
218
+ riv_block = [header, self.subtype, labels, params, f"{str(self.nrows):>10}"]
219
+
220
+ riv_data = []
221
+ for (
222
+ _,
223
+ x,
224
+ y,
225
+ n,
226
+ panel,
227
+ rpl,
228
+ marker,
229
+ easting,
230
+ northing,
231
+ deactivation,
232
+ sp_marker,
233
+ ) in self.data.itertuples():
234
+ row = join_10_char(x, y, n)
235
+ if panel:
236
+ row += "*"
237
+ else:
238
+ row += " "
239
+ row += f"{rpl:>9.3f}{join_10_char(marker, easting, northing, deactivation, str(sp_marker))}"
240
+ riv_data.append(row)
241
+
242
+ riv_block.extend(riv_data)
243
+
244
+ return riv_block
245
+
246
+ return self._raw_block
247
+
248
+ @property
249
+ def conveyance(self) -> pd.Series:
250
+ """Calculate and return the conveyance curve of the cross-section.
251
+
252
+ Note:
253
+ This uses the same method as applied in Flood Modeller so will be able to pick out any
254
+ undesirable spikes in conveyance. The only difference compared with Flood Modeller may
255
+ be the number of sampled points.
256
+
257
+ Returns:
258
+ pd.Series: A pandas Series containing the conveyance values indexed by water levels.
259
+ """
260
+ return calculate_cross_section_conveyance_chached(
261
+ x=tuple(self.data.X.values),
262
+ y=tuple(self.data.Y.values),
263
+ n=tuple(self.data["Mannings n"].values),
264
+ rpl=tuple(self.data.RPL.values),
265
+ panel_markers=tuple(self.data.Panel.values),
266
+ )
267
+
268
+
269
+ class INTERPOLATE(Unit):
270
+ """Class to hold and process INTERPOLATE unit type
271
+
272
+ Args:
273
+ name (str, optional): Unit name.
274
+ comment (str, optional): Comment included in unit.
275
+ first_spill (str, optional): Spill label if required.
276
+ second_spill (str, optional): Spill label if required.
277
+ lat1 (str, optional): First lateral inflow label.
278
+ lat2 (str, optional): Second lateral inflow label.
279
+ lat3 (str, optional): Third lateral inflow label.
280
+ lat4 (str, optional): Fourth lateral inflow label.
281
+ dist_to_next (float, optional): Chainage downstream to following section (m).
282
+ easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
283
+ northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
284
+
285
+ Returns:
286
+ INTERPOLATE: Flood Modeller INTERPOLATE Unit class object"""
287
+
288
+ _unit = "INTERPOLATE"
289
+
290
+ def _read(self, block):
291
+ """Function to read a given INTERPOLATE WEIR block and store data as class attributes"""
292
+
293
+ # Extends label line to be correct length before splitting to pick up blank labels
294
+ labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
295
+ self.name = labels[0]
296
+ self.first_spill = labels[1]
297
+ self.second_spill = labels[2]
298
+ self.lat1 = labels[3]
299
+ self.lat2 = labels[4]
300
+ self.lat3 = labels[5]
301
+ self.lat4 = labels[6]
302
+ self.comment = block[0].replace("INTERPOLATE", "").strip()
303
+
304
+ # First parameter line
305
+ params1 = split_10_char(f"{block[2]:<30}")
306
+ self.dist_to_next = _to_float(params1[0])
307
+ self.easting = _to_float(params1[1])
308
+ self.northing = _to_float(params1[2])
309
+
310
+ def _write(self):
311
+ """Function to write a valid INTERPOLATE block"""
312
+
313
+ _validate_unit(self)
314
+ header = "INTERPOLATE " + self.comment
315
+ labels = join_n_char_ljust(
316
+ self._label_len,
317
+ self.name,
318
+ self.first_spill,
319
+ self.second_spill,
320
+ self.lat1,
321
+ self.lat2,
322
+ self.lat3,
323
+ self.lat4,
324
+ )
325
+ block = [header, labels]
326
+
327
+ # First parameter line
328
+ params1 = join_10_char(self.dist_to_next, self.easting, self.northing)
329
+ block.append(params1)
330
+
331
+ return block
332
+
333
+ def _create_from_blank( # noqa: PLR0913
334
+ self,
335
+ name="new_interp",
336
+ comment="",
337
+ first_spill="",
338
+ second_spill="",
339
+ lat1="",
340
+ lat2="",
341
+ lat3="",
342
+ lat4="",
343
+ dist_to_next=0,
344
+ easting=0,
345
+ northing=0,
346
+ ):
347
+ for param, val in {
348
+ "name": name,
349
+ "comment": comment,
350
+ "first_spill": first_spill,
351
+ "second_spill": second_spill,
352
+ "lat1": lat1,
353
+ "lat2": lat2,
354
+ "lat3": lat3,
355
+ "lat4": lat4,
356
+ "dist_to_next": dist_to_next,
357
+ "easting": easting,
358
+ "northing": northing,
359
+ }.items():
360
+ setattr(self, param, val)
361
+
362
+
363
+ class REPLICATE(Unit):
364
+ """Class to hold and process REPLICATE unit type
365
+
366
+ Args:
367
+ name (str, optional): Unit name.
368
+ comment (str, optional): Comment included in unit.
369
+ first_spill (str, optional): Spill label if required.
370
+ second_spill (str, optional): Spill label if required.
371
+ lat1 (str, optional): First lateral inflow label.
372
+ lat2 (str, optional): Second lateral inflow label.
373
+ lat3 (str, optional): Third lateral inflow label.
374
+ lat4 (str, optional): Fourth lateral inflow label.
375
+ dist_to_next (float, optional): Chainage downstream to following section (m).
376
+ easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
377
+ northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
378
+ bed_level_drop (float, optional): Drop in bed level from previous section (m).
379
+
380
+ Returns:
381
+ REPLICATE: Flood Modeller REPLICATE Unit class object"""
382
+
383
+ _unit = "REPLICATE"
384
+
385
+ def _read(self, block: list[str]):
386
+ """Function to read a given REPLICATE block and store data as class attributes"""
387
+
388
+ # Extends label line to be correct length before splitting to pick up blank labels
389
+ labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
390
+ self.name = labels[0]
391
+ self.first_spill = labels[1]
392
+ self.second_spill = labels[2]
393
+ self.lat1 = labels[3]
394
+ self.lat2 = labels[4]
395
+ self.lat3 = labels[5]
396
+ self.lat4 = labels[6]
397
+
398
+ self.comment = block[0].replace("REPLICATE", "").strip()
399
+
400
+ # First parameter line
401
+ params1 = split_10_char(f"{block[2]:<40}")
402
+ self.dist_to_next = _to_float(params1[0])
403
+ self.bed_level_drop = _to_float(params1[1])
404
+ self.easting = _to_float(params1[2])
405
+ self.northing = _to_float(params1[3])
406
+
407
+ def _write(self):
408
+ """Function to write a valid REPLICATE block"""
409
+
410
+ _validate_unit(self)
411
+ header = "REPLICATE " + self.comment
412
+ labels = join_n_char_ljust(
413
+ self._label_len,
414
+ self.name,
415
+ self.first_spill,
416
+ self.second_spill,
417
+ self.lat1,
418
+ self.lat2,
419
+ self.lat3,
420
+ self.lat4,
421
+ )
422
+ block = [header, labels]
423
+
424
+ # First parameter line
425
+
426
+ params1 = join_10_char(
427
+ self.dist_to_next,
428
+ f"{self.bed_level_drop:>10.4f}", # allowing 4dp
429
+ self.easting,
430
+ self.northing,
431
+ )
432
+ block.append(params1)
433
+
434
+ return block
435
+
436
+ def _create_from_blank( # noqa: PLR0913
437
+ self,
438
+ name="new_repl",
439
+ comment="",
440
+ first_spill="",
441
+ second_spill="",
442
+ lat1="",
443
+ lat2="",
444
+ lat3="",
445
+ lat4="",
446
+ dist_to_next=0,
447
+ bed_level_drop=0,
448
+ easting=0,
449
+ northing=0,
450
+ ):
451
+ for param, val in {
452
+ "name": name,
453
+ "comment": comment,
454
+ "first_spill": first_spill,
455
+ "second_spill": second_spill,
456
+ "lat1": lat1,
457
+ "lat2": lat2,
458
+ "lat3": lat3,
459
+ "lat4": lat4,
460
+ "dist_to_next": dist_to_next,
461
+ "bed_level_drop": bed_level_drop,
462
+ "easting": easting,
463
+ "northing": northing,
464
+ }.items():
465
+ setattr(self, param, val)