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,306 +1,305 @@
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
-
18
- import pandas as pd
19
-
20
- from floodmodeller_api.validation import _validate_unit
21
-
22
- from ._base import Unit
23
- from .helpers import (
24
- _to_data_list,
25
- _to_float,
26
- _to_int,
27
- _to_str,
28
- join_10_char,
29
- join_n_char_ljust,
30
- split_10_char,
31
- split_n_char,
32
- )
33
-
34
-
35
- class CULVERT(Unit):
36
- """The CULVERT class supports two culvert sub-types in Flood Modeller: INLET and OUTLET. Each of these sub-types forms
37
- a unique instance of the class which is differentiated by the ``CULVERT.subtype`` attribute. Physical culvert types (e.g. rectangualr, circular etc)
38
- are provided under the CONDUIT class for consistency with Flood Modeller.
39
-
40
- **Common attributes**
41
-
42
- Args:
43
- name (str): Unit name and upstream node label
44
- ds_label (str): Downstream node label
45
- us_remote_label (str): Upstream remote node label
46
- ds_remote_label (str): Downstream remote node label
47
- comment (str): Comment
48
- loss_coefficient (float): Loss coefficient. Outlet (default = 1.0), INLET (Trash screen head loss coefficient, default = 1.5)
49
- headloss_type (str): Keyword TOTAL to denote headloss based on total head, otherwise (keyword STATIC or blank) headloss is based on static head
50
- reverse_flow_mode (str): Reverse Flow Mode; keyword ZERO (for zero headloss in reverse flow) or CALCULATED (for calculated head loss in reverse flow)
51
-
52
- **Inlet Loss Type (``CULVERT.subtype == 'INLET'``)**
53
-
54
- Args:
55
- type_code (str): options are 'Type A', 'Type B','Type C'
56
- k (float): Unsubmerged inlet control loss coefficient
57
- m (float): Exponent of Flow Intensity for inlet control
58
- c (float): Submerged inlet control loss coefficient
59
- y (float): Submerged inlet control adjustment factor
60
- ki (float): Outlet control loss coefficient
61
- screen_width (float): Trash screen width (m)
62
- bar_proportion (float): Proportion of trash screen area occupied by bars (0 to 1.0)
63
- debris_proportion (float): Blockage ratio (proportion of trash screen area occupied by debris) (0 to 1.0)
64
- max_screen_height (float): Max. Trash Screen Height *see Flood Modeller help for further information
65
-
66
- **Outlet Loss Type (``CULVERT.subtype == 'OUTLET'``)**
67
-
68
- No additional attributes required for OUTLET subtype
69
-
70
- Returns:
71
- CULVERT: Flood Modeller CULVERT unit class object
72
- """
73
-
74
- _unit = "CULVERT"
75
-
76
- def _read(self, block):
77
- """Function to read a given CULVERT block and store data as class attributes"""
78
-
79
- # Extract common attributes
80
- self._subtype = block[1].split(" ")[0].strip()
81
- self.comment = block[0].replace("CULVERT", "").strip()
82
- labels = split_n_char(f"{block[2]:<{4*self._label_len}}", self._label_len)
83
- self.name = labels[0]
84
- self.ds_label = labels[1]
85
- self.us_remote_label = labels[2]
86
- self.ds_remote_label = labels[3]
87
-
88
- # Extract subtype specific attributes
89
-
90
- if self.subtype == "INLET":
91
- # Defaults applied are equivellent to those provide if Culvert Wizard cancelled.
92
-
93
- # Read first set of general parameters
94
- params = split_10_char(f"{block[3]:<60}")
95
- self.k = _to_float(params[0], 0.0)
96
- self.m = _to_float(params[1], 0.0)
97
- self.c = _to_float(params[2], 0.0)
98
- self.y = _to_float(params[3], 0.0)
99
- self.ki = _to_float(params[4], 0.0)
100
- self.type_code = _to_str(params[5], "A")
101
-
102
- # Read trash screen and remaining general parameters
103
- params1 = split_10_char(f"{block[4]:<70}")
104
- self.screen_width = _to_float(params1[0], 0.0)
105
- self.bar_proportion = _to_float(params1[1], 0.0)
106
- self.debris_proportion = _to_float(params1[2], 0.0)
107
- self.loss_coefficient = _to_float(params1[3], 0.0)
108
- self.reverse_flow_mode = _to_str(params1[4], "CALCULATED", check_float=True)
109
- self.headloss_type = _to_str(params1[5], "TOTAL")
110
- self.max_screen_height = _to_float(params1[6], 0.0)
111
-
112
- elif self.subtype == "OUTLET":
113
- params = split_10_char(f"{block[3]:<30}")
114
- self.loss_coefficient = _to_float(params[0], 1.0)
115
- self.reverse_flow_mode = _to_str(params[1], "CALCULATED")
116
- self.headloss_type = _to_str(params[2], "TOTAL")
117
-
118
- else:
119
- # This else block is triggered for culvert subtypes which aren't yet supported, and just keeps the '_block' in it's raw state to write back.
120
- print(
121
- f'This Culvert sub-type: "{self.subtype}" is currently unsupported for reading/editing'
122
- )
123
- self._raw_block = block
124
-
125
- # TODO: Create from blank. Not supported currently as CULVERT has multiple subtypes
126
-
127
- def _write(self):
128
- """Function to write a valid CULVERT block"""
129
-
130
- _validate_unit(self)
131
-
132
- header = "CULVERT " + self.comment
133
- labels = join_n_char_ljust(
134
- self._label_len,
135
- self.name,
136
- self.ds_label,
137
- self.us_remote_label,
138
- self.ds_remote_label,
139
- )
140
- c_block = [header, self.subtype, labels]
141
-
142
- if self.subtype == "INLET":
143
- params = join_10_char(self.k, self.m, self.c, self.y, self.ki, self.type_code, dp=4)
144
- params1 = join_10_char(
145
- self.screen_width,
146
- self.bar_proportion,
147
- self.debris_proportion,
148
- self.loss_coefficient,
149
- self.reverse_flow_mode,
150
- self.headloss_type,
151
- self.max_screen_height,
152
- dp=4,
153
- )
154
-
155
- c_block.extend([params, params1])
156
-
157
- return c_block
158
-
159
- if self.subtype == "OUTLET":
160
- params = join_10_char(self.loss_coefficient, self.reverse_flow_mode, self.headloss_type)
161
-
162
- c_block.append(params)
163
- return c_block
164
-
165
- return self._raw_block
166
-
167
-
168
- class BLOCKAGE(Unit):
169
- """Class to hold and process BLOCKAGE unit type.
170
-
171
- Args:
172
- comment (str): Comment included in unit.
173
- name (str): Upstream label name
174
- ds_label (str): Downstream label
175
- us_reference_label (str): Upstream reference label
176
- ds_reference_label (str): Downstream reference label
177
- constriction_label (str): Constriction reference label
178
- inlet_loss (float): Inlet loss coefficient
179
- outlet_loss (float): Outlet loss coefficient
180
- timeoffset (float): Time Datum Adjustment
181
- timeunit_blockage (str): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options.
182
- extendmethod (str): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
183
- data (pandas.Series): Series object with variable ``'blockage'`` and index ``'Time'``. Defaults to None.
184
-
185
-
186
- Returns:
187
- BLOCKAGE: Flood Modeller BLOCAKGE Unit class object
188
- """
189
-
190
- _unit = "BLOCKAGE"
191
-
192
- def _read(self, block):
193
- """Function to read a given BLOCKAGE block and store data as class attributes"""
194
-
195
- # Extract comment and revision number
196
- b = block[0].replace("BLOCKAGE #revision#", " ").strip()
197
- self._revision = _to_int(b[0], 1)
198
- self.comment = b[1:].strip()
199
-
200
- # Extract labels
201
- labels = split_n_char(f"{block[1]:<{5*self._label_len}}", self._label_len)
202
- self.name = labels[0]
203
- self.ds_label = labels[1]
204
- self.us_reference_label = labels[2]
205
- self.ds_reference_label = labels[3]
206
- self.constriction_label = labels[4]
207
-
208
- # Extract inlet and outlet loss coefficients
209
- params = split_10_char(f"{block[2]:<20}")
210
- self.inlet_loss = _to_float(params[0], 1.5)
211
- self.outlet_loss = _to_float(params[1], 1.0)
212
-
213
- # Extract blockage timeseries parameters
214
- params1 = split_10_char(f"{block[3]:<40}")
215
- self.nrows = int(params1[0])
216
- self.timeoffset = _to_float(params1[1])
217
-
218
- self.timeunit = _to_str(params1[2], "HOURS", check_float=True)
219
- if self.timeunit == "DATE":
220
- self.timeunit = "DATES" # Parameter value updated to 'DATES' for consistency with other unit types. 'DATE' and 'DATES' both accepted for blockage unit ONLY
221
-
222
- self.extendmethod = _to_str(params1[3], "NOEXTEND")
223
-
224
- # Extract blockage to timeseries
225
- data_list = (
226
- _to_data_list(block[4:], num_cols=2, date_col=0)
227
- if self.timeunit == "DATES"
228
- else _to_data_list(block[4:], num_cols=2)
229
- ) # Enforced two columns as Flood Modeller saves old parameters when using DATES (also to avoid extra 'HOURS' bug)
230
-
231
- self.data = pd.DataFrame(data_list, columns=["Time", "Blockage"])
232
- self.data = self.data.set_index("Time")
233
- self.data = self.data["Blockage"]
234
-
235
- def _write(self):
236
- """Function to write a valid BLOCKAGE block"""
237
-
238
- _validate_unit(self)
239
-
240
- # Custom validation for blockage percentage
241
- if self.data.max() > 1 or self.data.min() < 0:
242
- raise ValueError(
243
- f"Parameter error with {repr(self)} - blockage percentage must be between 0 and 1"
244
- )
245
-
246
- header = f"BLOCKAGE #revision#{self._revision} {self.comment}"
247
- labels = join_n_char_ljust(
248
- self._label_len,
249
- self.name,
250
- self.ds_label,
251
- self.us_reference_label,
252
- self.ds_reference_label,
253
- self.constriction_label,
254
- )
255
- params = join_10_char(self.inlet_loss, self.outlet_loss)
256
- self.nrows = len(self.data)
257
- params1 = join_10_char(self.nrows, self.timeoffset, self.timeunit, self.extendmethod)
258
-
259
- blockage_block = [header, labels, params, params1]
260
-
261
- if self.timeunit == "DATES":
262
- blockage_data = [f"{t:<20}{join_10_char(b)}" for t, b in self.data.items()]
263
- else:
264
- blockage_data = [join_10_char(t, b) for t, b in self.data.items()]
265
-
266
- blockage_block.extend(blockage_data)
267
-
268
- return blockage_block
269
-
270
- def _create_from_blank(
271
- self,
272
- name="new_blockage",
273
- _revision=1,
274
- comment="",
275
- ds_label="",
276
- us_reference_label="",
277
- ds_reference_label="",
278
- constriction_label="",
279
- inlet_loss=1.5,
280
- outlet_loss=1.0,
281
- timeoffset=0.0,
282
- timeunit="HOURS",
283
- extendmethod="EXTEND",
284
- data=None,
285
- ):
286
- # Initiate new BLOCKAGE unit
287
- for param, val in {
288
- "name": name,
289
- "_revision": _revision,
290
- "comment": comment,
291
- "ds_label": ds_label,
292
- "us_reference_label": us_reference_label,
293
- "ds_reference_label": ds_reference_label,
294
- "constriction_label": constriction_label,
295
- "inlet_loss": inlet_loss,
296
- "outlet_loss": outlet_loss,
297
- "timeoffset": timeoffset,
298
- "timeunit": timeunit,
299
- "extendmethod": extendmethod,
300
- "data": data,
301
- }.items():
302
- setattr(self, param, val)
303
-
304
- self.data = (
305
- data if isinstance(data, pd.Series) else pd.Series([0.0], index=[0.0], name="Blockage")
306
- )
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_data_list,
24
+ _to_float,
25
+ _to_int,
26
+ _to_str,
27
+ join_10_char,
28
+ join_n_char_ljust,
29
+ split_10_char,
30
+ split_n_char,
31
+ )
32
+
33
+
34
+ class CULVERT(Unit):
35
+ """The CULVERT class supports two culvert sub-types in Flood Modeller: INLET and OUTLET. Each of these sub-types forms
36
+ a unique instance of the class which is differentiated by the ``CULVERT.subtype`` attribute. Physical culvert types (e.g. rectangualr, circular etc)
37
+ are provided under the CONDUIT class for consistency with Flood Modeller.
38
+
39
+ **Common attributes**
40
+
41
+ Args:
42
+ name (str): Unit name and upstream node label
43
+ ds_label (str): Downstream node label
44
+ us_remote_label (str): Upstream remote node label
45
+ ds_remote_label (str): Downstream remote node label
46
+ comment (str): Comment
47
+ loss_coefficient (float): Loss coefficient. Outlet (default = 1.0), INLET (Trash screen head loss coefficient, default = 1.5)
48
+ headloss_type (str): Keyword TOTAL to denote headloss based on total head, otherwise (keyword STATIC or blank) headloss is based on static head
49
+ reverse_flow_mode (str): Reverse Flow Mode; keyword ZERO (for zero headloss in reverse flow) or CALCULATED (for calculated head loss in reverse flow)
50
+
51
+ **Inlet Loss Type** (``CULVERT.subtype == 'INLET'``)
52
+
53
+ Args:
54
+ type_code (str): options are 'Type A', 'Type B','Type C'
55
+ k (float): Unsubmerged inlet control loss coefficient
56
+ m (float): Exponent of Flow Intensity for inlet control
57
+ c (float): Submerged inlet control loss coefficient
58
+ y (float): Submerged inlet control adjustment factor
59
+ ki (float): Outlet control loss coefficient
60
+ screen_width (float): Trash screen width (m)
61
+ bar_proportion (float): Proportion of trash screen area occupied by bars (0 to 1.0)
62
+ debris_proportion (float): Blockage ratio (proportion of trash screen area occupied by debris) (0 to 1.0)
63
+ max_screen_height (float): Max. Trash Screen Height (see Flood Modeller help for further information)
64
+
65
+ **Outlet Loss Type** (``CULVERT.subtype == 'OUTLET'``)
66
+
67
+ No additional attributes required for OUTLET subtype
68
+
69
+ Returns:
70
+ CULVERT: Flood Modeller CULVERT unit class object
71
+ """
72
+
73
+ _unit = "CULVERT"
74
+
75
+ def _read(self, block):
76
+ """Function to read a given CULVERT block and store data as class attributes"""
77
+
78
+ # Extract common attributes
79
+ self._subtype = block[1].split(" ")[0].strip()
80
+ self.comment = block[0].replace("CULVERT", "").strip()
81
+ labels = split_n_char(f"{block[2]:<{4*self._label_len}}", self._label_len)
82
+ self.name = labels[0]
83
+ self.ds_label = labels[1]
84
+ self.us_remote_label = labels[2]
85
+ self.ds_remote_label = labels[3]
86
+
87
+ # Extract subtype specific attributes
88
+
89
+ if self.subtype == "INLET":
90
+ # Defaults applied are equivellent to those provide if Culvert Wizard cancelled.
91
+
92
+ # Read first set of general parameters
93
+ params = split_10_char(f"{block[3]:<60}")
94
+ self.k = _to_float(params[0], 0.0)
95
+ self.m = _to_float(params[1], 0.0)
96
+ self.c = _to_float(params[2], 0.0)
97
+ self.y = _to_float(params[3], 0.0)
98
+ self.ki = _to_float(params[4], 0.0)
99
+ self.type_code = _to_str(params[5], "A")
100
+
101
+ # Read trash screen and remaining general parameters
102
+ params1 = split_10_char(f"{block[4]:<70}")
103
+ self.screen_width = _to_float(params1[0], 0.0)
104
+ self.bar_proportion = _to_float(params1[1], 0.0)
105
+ self.debris_proportion = _to_float(params1[2], 0.0)
106
+ self.loss_coefficient = _to_float(params1[3], 0.0)
107
+ self.reverse_flow_mode = _to_str(params1[4], "CALCULATED", check_float=True)
108
+ self.headloss_type = _to_str(params1[5], "TOTAL")
109
+ self.max_screen_height = _to_float(params1[6], 0.0)
110
+
111
+ elif self.subtype == "OUTLET":
112
+ params = split_10_char(f"{block[3]:<30}")
113
+ self.loss_coefficient = _to_float(params[0], 1.0)
114
+ self.reverse_flow_mode = _to_str(params[1], "CALCULATED")
115
+ self.headloss_type = _to_str(params[2], "TOTAL")
116
+
117
+ else:
118
+ # This else block is triggered for culvert subtypes which aren't yet supported, and just keeps the '_block' in it's raw state to write back.
119
+ print(
120
+ f'This Culvert sub-type: "{self.subtype}" is currently unsupported for reading/editing',
121
+ )
122
+ self._raw_block = block
123
+
124
+ # TODO: Create from blank. Not supported currently as CULVERT has multiple subtypes
125
+
126
+ def _write(self):
127
+ """Function to write a valid CULVERT block"""
128
+
129
+ _validate_unit(self)
130
+
131
+ header = "CULVERT " + self.comment
132
+ labels = join_n_char_ljust(
133
+ self._label_len,
134
+ self.name,
135
+ self.ds_label,
136
+ self.us_remote_label,
137
+ self.ds_remote_label,
138
+ )
139
+ c_block = [header, self.subtype, labels]
140
+
141
+ if self.subtype == "INLET":
142
+ params = join_10_char(self.k, self.m, self.c, self.y, self.ki, self.type_code, dp=4)
143
+ params1 = join_10_char(
144
+ self.screen_width,
145
+ self.bar_proportion,
146
+ self.debris_proportion,
147
+ self.loss_coefficient,
148
+ self.reverse_flow_mode,
149
+ self.headloss_type,
150
+ self.max_screen_height,
151
+ dp=4,
152
+ )
153
+
154
+ c_block.extend([params, params1])
155
+
156
+ return c_block
157
+
158
+ if self.subtype == "OUTLET":
159
+ params = join_10_char(self.loss_coefficient, self.reverse_flow_mode, self.headloss_type)
160
+
161
+ c_block.append(params)
162
+ return c_block
163
+
164
+ return self._raw_block
165
+
166
+
167
+ class BLOCKAGE(Unit):
168
+ """Class to hold and process BLOCKAGE unit type.
169
+
170
+ Args:
171
+ comment (str): Comment included in unit.
172
+ name (str): Upstream label name
173
+ ds_label (str): Downstream label
174
+ us_reference_label (str): Upstream reference label
175
+ ds_reference_label (str): Downstream reference label
176
+ constriction_label (str): Constriction reference label
177
+ inlet_loss (float): Inlet loss coefficient
178
+ outlet_loss (float): Outlet loss coefficient
179
+ timeoffset (float): Time Datum Adjustment
180
+ timeunit_blockage (str): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options.
181
+ extendmethod (str): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
182
+ data (pandas.Series): Series object with variable ``'blockage'`` and index ``'Time'``. Defaults to None.
183
+
184
+
185
+ Returns:
186
+ BLOCKAGE: Flood Modeller BLOCAKGE Unit class object
187
+ """
188
+
189
+ _unit = "BLOCKAGE"
190
+
191
+ def _read(self, block):
192
+ """Function to read a given BLOCKAGE block and store data as class attributes"""
193
+
194
+ # Extract comment and revision number
195
+ b = block[0].replace("BLOCKAGE #revision#", " ").strip()
196
+ self._revision = _to_int(b[0], 1)
197
+ self.comment = b[1:].strip()
198
+
199
+ # Extract labels
200
+ labels = split_n_char(f"{block[1]:<{5*self._label_len}}", self._label_len)
201
+ self.name = labels[0]
202
+ self.ds_label = labels[1]
203
+ self.us_reference_label = labels[2]
204
+ self.ds_reference_label = labels[3]
205
+ self.constriction_label = labels[4]
206
+
207
+ # Extract inlet and outlet loss coefficients
208
+ params = split_10_char(f"{block[2]:<20}")
209
+ self.inlet_loss = _to_float(params[0], 1.5)
210
+ self.outlet_loss = _to_float(params[1], 1.0)
211
+
212
+ # Extract blockage timeseries parameters
213
+ params1 = split_10_char(f"{block[3]:<40}")
214
+ self.nrows = int(params1[0])
215
+ self.timeoffset = _to_float(params1[1])
216
+
217
+ self.timeunit = _to_str(params1[2], "HOURS", check_float=True)
218
+ if self.timeunit == "DATE":
219
+ self.timeunit = "DATES" # Parameter value updated to 'DATES' for consistency with other unit types. 'DATE' and 'DATES' both accepted for blockage unit ONLY
220
+
221
+ self.extendmethod = _to_str(params1[3], "NOEXTEND")
222
+
223
+ # Extract blockage to timeseries
224
+ data_list = (
225
+ _to_data_list(block[4:], num_cols=2, date_col=0)
226
+ if self.timeunit == "DATES"
227
+ else _to_data_list(block[4:], num_cols=2)
228
+ ) # Enforced two columns as Flood Modeller saves old parameters when using DATES (also to avoid extra 'HOURS' bug)
229
+
230
+ self.data = pd.DataFrame(data_list, columns=["Time", "Blockage"])
231
+ self.data = self.data.set_index("Time")
232
+ self.data = self.data["Blockage"]
233
+
234
+ def _write(self):
235
+ """Function to write a valid BLOCKAGE block"""
236
+
237
+ _validate_unit(self)
238
+
239
+ # Custom validation for blockage percentage
240
+ if self.data.max() > 1 or self.data.min() < 0:
241
+ raise ValueError(
242
+ f"Parameter error with {repr(self)} - blockage percentage must be between 0 and 1",
243
+ )
244
+
245
+ header = f"BLOCKAGE #revision#{self._revision} {self.comment}"
246
+ labels = join_n_char_ljust(
247
+ self._label_len,
248
+ self.name,
249
+ self.ds_label,
250
+ self.us_reference_label,
251
+ self.ds_reference_label,
252
+ self.constriction_label,
253
+ )
254
+ params = join_10_char(self.inlet_loss, self.outlet_loss)
255
+ self.nrows = len(self.data)
256
+ params1 = join_10_char(self.nrows, self.timeoffset, self.timeunit, self.extendmethod)
257
+
258
+ blockage_block = [header, labels, params, params1]
259
+
260
+ if self.timeunit == "DATES":
261
+ blockage_data = [f"{t:<20}{join_10_char(b)}" for t, b in self.data.items()]
262
+ else:
263
+ blockage_data = [join_10_char(t, b) for t, b in self.data.items()]
264
+
265
+ blockage_block.extend(blockage_data)
266
+
267
+ return blockage_block
268
+
269
+ def _create_from_blank( # noqa: PLR0913
270
+ self,
271
+ name="new_blockage",
272
+ _revision=1,
273
+ comment="",
274
+ ds_label="",
275
+ us_reference_label="",
276
+ ds_reference_label="",
277
+ constriction_label="",
278
+ inlet_loss=1.5,
279
+ outlet_loss=1.0,
280
+ timeoffset=0.0,
281
+ timeunit="HOURS",
282
+ extendmethod="EXTEND",
283
+ data=None,
284
+ ):
285
+ # Initiate new BLOCKAGE unit
286
+ for param, val in {
287
+ "name": name,
288
+ "_revision": _revision,
289
+ "comment": comment,
290
+ "ds_label": ds_label,
291
+ "us_reference_label": us_reference_label,
292
+ "ds_reference_label": ds_reference_label,
293
+ "constriction_label": constriction_label,
294
+ "inlet_loss": inlet_loss,
295
+ "outlet_loss": outlet_loss,
296
+ "timeoffset": timeoffset,
297
+ "timeunit": timeunit,
298
+ "extendmethod": extendmethod,
299
+ "data": data,
300
+ }.items():
301
+ setattr(self, param, val)
302
+
303
+ self.data = (
304
+ data if isinstance(data, pd.Series) else pd.Series([0.0], index=[0.0], name="Blockage")
305
+ )