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