floodmodeller-api 0.4.2__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 (178) 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 -838
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +307 -311
  7. floodmodeller_api/ief.py +647 -646
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +266 -268
  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 -314
  21. floodmodeller_api/logs/lf_helpers.py +354 -346
  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 -154
  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 -330
  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 -94
  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 -72
  131. floodmodeller_api/units/__init__.py +10 -10
  132. floodmodeller_api/units/_base.py +214 -209
  133. floodmodeller_api/units/boundaries.py +467 -469
  134. floodmodeller_api/units/comment.py +52 -55
  135. floodmodeller_api/units/conduits.py +382 -403
  136. floodmodeller_api/units/helpers.py +123 -132
  137. floodmodeller_api/units/iic.py +107 -101
  138. floodmodeller_api/units/losses.py +305 -308
  139. floodmodeller_api/units/sections.py +444 -445
  140. floodmodeller_api/units/structures.py +1690 -1684
  141. floodmodeller_api/units/units.py +93 -102
  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 -177
  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 -107
  151. floodmodeller_api/urban1d/raingauges.py +111 -108
  152. floodmodeller_api/urban1d/subsections.py +92 -93
  153. floodmodeller_api/urban1d/xsections.py +147 -141
  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 -112
  158. floodmodeller_api/version.py +1 -1
  159. floodmodeller_api/xml2d.py +688 -684
  160. floodmodeller_api/xml2d_template.py +37 -37
  161. floodmodeller_api/zzn.py +387 -365
  162. {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
  163. {floodmodeller_api-0.4.2.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.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
  166. floodmodeller_api-0.4.3.dist-info/entry_points.txt +3 -0
  167. floodmodeller_api/libifcoremd.dll +0 -0
  168. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  169. floodmodeller_api/test/test_data/test_output.csv +0 -87
  170. floodmodeller_api/zzn_read.dll +0 -0
  171. floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.bat +0 -2
  172. floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.py +0 -3
  173. floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.bat +0 -2
  174. floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.py +0 -3
  175. floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.bat +0 -2
  176. floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.py +0 -41
  177. floodmodeller_api-0.4.2.dist-info/RECORD +0 -169
  178. {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
@@ -1,445 +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
- 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(
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 = True if row_split[3][0] == "*" else False
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
- else:
246
- return self._raw_block
247
-
248
-
249
- class INTERPOLATE(Unit):
250
- """Class to hold and process INTERPOLATE unit type
251
-
252
- Args:
253
- name (str, optional): Unit name.
254
- comment (str, optional): Comment included in unit.
255
- first_spill (str, optional): Spill label if required.
256
- second_spill (str, optional): Spill label if required.
257
- lat1 (str, optional): First lateral inflow label.
258
- lat2 (str, optional): Second lateral inflow label.
259
- lat3 (str, optional): Third lateral inflow label.
260
- lat4 (str, optional): Fourth lateral inflow label.
261
- dist_to_next (float, optional): Chainage downstream to following section (m).
262
- easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
263
- northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
264
-
265
- Returns:
266
- INTERPOLATE: Flood Modeller INTERPOLATE Unit class object"""
267
-
268
- _unit = "INTERPOLATE"
269
-
270
- def _read(self, block):
271
- """Function to read a given INTERPOLATE WEIR block and store data as class attributes"""
272
-
273
- # Extends label line to be correct length before splitting to pick up blank labels
274
- labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
275
- self.name = labels[0]
276
- self.first_spill = labels[1]
277
- self.second_spill = labels[2]
278
- self.lat1 = labels[3]
279
- self.lat2 = labels[4]
280
- self.lat3 = labels[5]
281
- self.lat4 = labels[6]
282
- self.comment = block[0].replace("INTERPOLATE", "").strip()
283
-
284
- # First parameter line
285
- params1 = split_10_char(f"{block[2]:<30}")
286
- self.dist_to_next = _to_float(params1[0])
287
- self.easting = _to_float(params1[1])
288
- self.northing = _to_float(params1[2])
289
-
290
- def _write(self):
291
- """Function to write a valid INTERPOLATE block"""
292
-
293
- _validate_unit(self)
294
- header = "INTERPOLATE " + self.comment
295
- labels = join_n_char_ljust(
296
- self._label_len,
297
- self.name,
298
- self.first_spill,
299
- self.second_spill,
300
- self.lat1,
301
- self.lat2,
302
- self.lat3,
303
- self.lat4,
304
- )
305
- block = [header, labels]
306
-
307
- # First parameter line
308
- params1 = join_10_char(self.dist_to_next, self.easting, self.northing)
309
- block.append(params1)
310
-
311
- return block
312
-
313
- def _create_from_blank(
314
- self,
315
- name="new_interp",
316
- comment="",
317
- first_spill="",
318
- second_spill="",
319
- lat1="",
320
- lat2="",
321
- lat3="",
322
- lat4="",
323
- dist_to_next=0,
324
- easting=0,
325
- northing=0,
326
- ):
327
- for param, val in {
328
- "name": name,
329
- "comment": comment,
330
- "first_spill": first_spill,
331
- "second_spill": second_spill,
332
- "lat1": lat1,
333
- "lat2": lat2,
334
- "lat3": lat3,
335
- "lat4": lat4,
336
- "dist_to_next": dist_to_next,
337
- "easting": easting,
338
- "northing": northing,
339
- }.items():
340
- setattr(self, param, val)
341
-
342
-
343
- class REPLICATE(Unit):
344
- """Class to hold and process REPLICATE unit type
345
-
346
- Args:
347
- name (str, optional): Unit name.
348
- comment (str, optional): Comment included in unit.
349
- first_spill (str, optional): Spill label if required.
350
- second_spill (str, optional): Spill label if required.
351
- lat1 (str, optional): First lateral inflow label.
352
- lat2 (str, optional): Second lateral inflow label.
353
- lat3 (str, optional): Third lateral inflow label.
354
- lat4 (str, optional): Fourth lateral inflow label.
355
- dist_to_next (float, optional): Chainage downstream to following section (m).
356
- easting (float, optional): Easting coordinate of interpolated section (not used in hydraulic calculations).
357
- northing (float, optional): Northing coordinate of interpolated section (not used in hydraulic calculations).
358
- bed_level_drop (float, optional): Drop in bed level from previous section (m).
359
-
360
- Returns:
361
- REPLICATE: Flood Modeller REPLICATE Unit class object"""
362
-
363
- _unit = "REPLICATE"
364
-
365
- def _read(self, block: list[str]):
366
- """Function to read a given REPLICATE block and store data as class attributes"""
367
-
368
- # Extends label line to be correct length before splitting to pick up blank labels
369
- labels = split_n_char(f"{block[1]:<{7*self._label_len}}", self._label_len)
370
- self.name = labels[0]
371
- self.first_spill = labels[1]
372
- self.second_spill = labels[2]
373
- self.lat1 = labels[3]
374
- self.lat2 = labels[4]
375
- self.lat3 = labels[5]
376
- self.lat4 = labels[6]
377
-
378
- self.comment = block[0].replace("REPLICATE", "").strip()
379
-
380
- # First parameter line
381
- params1 = split_10_char(f"{block[2]:<40}")
382
- self.dist_to_next = _to_float(params1[0])
383
- self.bed_level_drop = _to_float(params1[1])
384
- self.easting = _to_float(params1[2])
385
- self.northing = _to_float(params1[3])
386
-
387
- def _write(self):
388
- """Function to write a valid REPLICATE block"""
389
-
390
- _validate_unit(self)
391
- header = "REPLICATE " + self.comment
392
- labels = join_n_char_ljust(
393
- self._label_len,
394
- self.name,
395
- self.first_spill,
396
- self.second_spill,
397
- self.lat1,
398
- self.lat2,
399
- self.lat3,
400
- self.lat4,
401
- )
402
- block = [header, labels]
403
-
404
- # First parameter line
405
-
406
- params1 = join_10_char(
407
- self.dist_to_next,
408
- f"{self.bed_level_drop:>10.4f}", # allowing 4dp
409
- self.easting,
410
- self.northing,
411
- )
412
- block.append(params1)
413
-
414
- return block
415
-
416
- def _create_from_blank(
417
- self,
418
- name="new_repl",
419
- comment="",
420
- first_spill="",
421
- second_spill="",
422
- lat1="",
423
- lat2="",
424
- lat3="",
425
- lat4="",
426
- dist_to_next=0,
427
- bed_level_drop=0,
428
- easting=0,
429
- northing=0,
430
- ):
431
- for param, val in {
432
- "name": name,
433
- "comment": comment,
434
- "first_spill": first_spill,
435
- "second_spill": second_spill,
436
- "lat1": lat1,
437
- "lat2": lat2,
438
- "lat3": lat3,
439
- "lat4": lat4,
440
- "dist_to_next": dist_to_next,
441
- "bed_level_drop": bed_level_drop,
442
- "easting": easting,
443
- "northing": northing,
444
- }.items():
445
- 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)