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,289 +1,287 @@
1
- import csv
2
-
3
- from floodmodeller_api import DAT
4
-
5
-
6
- class StructureLogBuilder:
7
- def __init__(self, input_path, output_path) -> None:
8
- self.dat_file_path = input_path
9
- self.csv_output_path = output_path
10
-
11
- def _add_fields(self):
12
- field = [
13
- "Unit Name",
14
- "Unit Type",
15
- "Unit Subtype",
16
- "Comment",
17
- "Friction",
18
- "Dimensions (m)",
19
- "Weir Coefficient",
20
- "Culvert Inlet/Outlet Loss",
21
- ]
22
- self._writer.writerow(field)
23
-
24
- def _conduit_data(self, conduit):
25
- length = 0.0
26
- inlet = ""
27
- outlet = ""
28
- previous = self._dat.prev(conduit)
29
- if hasattr(previous, "subtype"):
30
- if previous.subtype == "INLET":
31
- inlet = previous.ki
32
- current_conduit = conduit
33
- while current_conduit.dist_to_next != 0:
34
- length += current_conduit.dist_to_next
35
- current_conduit = self._dat.next(current_conduit)
36
- next = self._dat.next(current_conduit)
37
- if hasattr(next, "subtype"):
38
- if next.subtype == "OUTLET":
39
- outlet = next.loss_coefficient
40
- return [length, inlet, outlet]
41
-
42
- def _culvert_loss_data(self, inlet, outlet):
43
- culvert_loss = ""
44
- if inlet != "" and outlet != "":
45
- culvert_loss = f"Ki: {inlet}, Ko: {outlet}"
46
- elif inlet != "":
47
- culvert_loss = f"Ki: {inlet}"
48
- elif outlet != "":
49
- culvert_loss = f"Ko: {outlet}"
50
- return culvert_loss
51
-
52
- def _circular_data(self, conduit, length):
53
- dimensions = f"dia: {conduit.diameter:.2f} x l: {length:.2f}"
54
- all_mannings = [
55
- conduit.friction_above_axis,
56
- conduit.friction_below_axis,
57
- ]
58
- mannings_set = set([min(all_mannings), max(all_mannings)])
59
- if len(mannings_set) == 1:
60
- friction = f"Mannings: {mannings_set.pop()}"
61
- else:
62
- friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
63
-
64
- return [friction, dimensions]
65
-
66
- def _sprungarch_data(self, conduit, length):
67
- dimensions = f"(Springing: {conduit.height_crown:.2f}, Crown: {conduit.height_springing:.2f}) x w: {conduit.width:.2f} x l: {length:.2f}"
68
- all_mannings = [
69
- conduit.friction_on_invert,
70
- conduit.friction_on_soffit,
71
- conduit.friction_on_walls,
72
- ]
73
- mannings_set = set([min(all_mannings), max(all_mannings)])
74
- if len(mannings_set) == 1:
75
- friction = f"Mannings: {mannings_set.pop()}"
76
- else:
77
- friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
78
-
79
- return [friction, dimensions]
80
-
81
- def _rectangular_data(self, conduit, length):
82
- dimensions = f"h: {conduit.height:.2f} x w: {conduit.width:.2f} x l: {length:.2f}"
83
- all_mannings = [
84
- conduit.friction_on_invert,
85
- conduit.friction_on_soffit,
86
- conduit.friction_on_walls,
87
- ]
88
- mannings_set = set([min(all_mannings), max(all_mannings)])
89
- if len(mannings_set) == 1:
90
- friction = f"Mannings: {mannings_set.pop()}"
91
- else:
92
- friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
93
-
94
- return [friction, dimensions]
95
-
96
- def _section_data(self, conduit, length):
97
- x_list = conduit.coords.x.tolist()
98
- width = (max(x_list) - min(x_list)) * 2
99
- y_list = conduit.coords.y.tolist()
100
- height = max(y_list) - min(y_list)
101
- # currently this means that height goes to the top of the spike,
102
- # it is only meant to go up to the height of the majority of the area
103
- dimensions = f"h: {height:.2f} x w: {width:.2f} x l: {length:.2f}"
104
- all_cw_frictions = conduit.coords.cw_friction.tolist()
105
- cw_frictions_set = set([min(all_cw_frictions), max(all_cw_frictions)])
106
- if len(cw_frictions_set) == 1:
107
- friction = f"Colebrook-White: {cw_frictions_set.pop()}"
108
- else:
109
- friction = (
110
- f"Colebrook-White: [min: {cw_frictions_set.pop()}, max: {cw_frictions_set.pop()}]"
111
- )
112
-
113
- return [friction, dimensions]
114
-
115
- def _sprung_data(self, conduit, length):
116
- dimensions = f"(Springing: {conduit.height_crown:.2f}, Crown: {conduit.height_springing:.2f}) x w: {conduit.width:.2f} x l: {length:.2f}"
117
- all_mannings = [
118
- conduit.friction_on_invert,
119
- conduit.friction_on_soffit,
120
- conduit.friction_on_walls,
121
- ]
122
- mannings_set = set([min(all_mannings), max(all_mannings)])
123
- if len(mannings_set) == 1:
124
- friction = f"Mannings: {mannings_set.pop()}"
125
- else:
126
- friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
127
-
128
- return [friction, dimensions]
129
-
130
- def _add_conduits(self):
131
- for conduit in self._dat.conduits.values():
132
- if conduit.subtype not in [
133
- "CIRCULAR",
134
- "SPRUNGARCH",
135
- "RECTANGULAR",
136
- "SECTION",
137
- "SPRUNG",
138
- ]:
139
- print(f"Conduit subtype: {conduit.subtype} not currently supported")
140
- self._write(conduit.name, conduit._unit, conduit.subtype)
141
- continue
142
- conduit_data = self._conduit_data(conduit)
143
- length = conduit_data[0]
144
- inlet = conduit_data[1]
145
- outlet = conduit_data[2]
146
-
147
- if length == 0:
148
- continue
149
-
150
- culvert_loss = self._culvert_loss_data(inlet, outlet)
151
- friction = ""
152
- dimensions = ""
153
- if conduit.subtype == "CIRCULAR":
154
- circular_data = self._circular_data(conduit, length)
155
- friction = circular_data[0]
156
- dimensions = circular_data[1]
157
- elif conduit.subtype == "SPRUNGARCH":
158
- sprungarch_data = self._sprungarch_data(conduit, length)
159
- friction = sprungarch_data[0]
160
- dimensions = sprungarch_data[1]
161
- elif conduit.subtype == "RECTANGULAR":
162
- rectangular_data = self._rectangular_data(conduit, length)
163
- friction = rectangular_data[0]
164
- dimensions = rectangular_data[1]
165
- elif conduit.subtype == "SECTION":
166
- section_data = self._section_data(conduit, length)
167
- friction = section_data[0]
168
- dimensions = section_data[1]
169
- elif conduit.subtype == "SPRUNG":
170
- sprung_data = self._sprung_data(conduit, length)
171
- friction = sprung_data[0]
172
- dimensions = sprung_data[1]
173
-
174
- self._write(
175
- conduit.name,
176
- conduit._unit,
177
- conduit.subtype,
178
- conduit.comment,
179
- friction,
180
- dimensions,
181
- "",
182
- culvert_loss,
183
- )
184
-
185
- def _orifice_dimensions(self, structure):
186
- if structure.shape == "RECTANGLE":
187
- height = structure.soffit - structure.invert
188
- width = structure.bore_area / height
189
- dimensions = f"h: {height:.2f} x w: {width:.2f}"
190
- elif structure.shape == "CIRCULAR":
191
- diameter = structure.soffit - structure.invert
192
- dimensions = f"dia: {diameter:.2f}"
193
- return dimensions
194
-
195
- def _spill_data(self, structure):
196
- elevation = min(structure.data.Y.tolist())
197
- x_list = structure.data.X.tolist()
198
- width = max(x_list) - min(x_list)
199
- dimensions = f"Elevation: {elevation:.2f} x w: {width:.2f}"
200
- weir_coefficient = structure.weir_coefficient
201
- return [dimensions, weir_coefficient]
202
-
203
- def _bridge_data(self, structure):
204
- all_mannings = structure.section_data["Mannings n"].tolist()
205
- mannings_set = set([min(all_mannings), max(all_mannings)])
206
- if len(mannings_set) == 1:
207
- friction = f"Mannings: {mannings_set.pop()}"
208
- else:
209
- friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
210
- height = structure.opening_data.values[0][3] - min(structure.section_data.Y.tolist())
211
- width = structure.opening_data.values[0][1] - structure.opening_data.values[0][0]
212
- dimensions = f"h: {height:.2f} x w: {width:.2f}"
213
- return [friction, dimensions]
214
-
215
- def _add_structures(self):
216
- for structure in self._dat.structures.values():
217
- friction = ""
218
- dimensions = ""
219
- weir_coefficient = ""
220
- if structure._unit == "ORIFICE":
221
- dimensions = self._orifice_dimensions(structure)
222
- elif structure._unit == "SPILL":
223
- spill_data = self._spill_data(structure)
224
- dimensions = spill_data[0]
225
- weir_coefficient = spill_data[1]
226
- elif structure._unit == "SLUICE":
227
- dimensions = f"Crest Elevation: {structure.crest_elevation:.2f} x w: {structure.weir_breadth:.2f} x l: {structure.weir_length:.2f}"
228
- elif structure._unit == "RNWEIR":
229
- dimensions = f"Crest Elevation: {structure.weir_elevation:.2f} x w: {structure.weir_breadth:.2f} x l: {structure.weir_length:.2f}"
230
- elif structure._unit == "WEIR":
231
- dimensions = f"Crest Elevation: {structure.weir_elevation:.2f} x w: {structure.weir_breadth:.2f}"
232
- # Need weir coefficient (the velocity attribute??)
233
- elif structure._unit == "BRIDGE":
234
- bridge_data = self._bridge_data(structure)
235
- friction = bridge_data[0]
236
- dimensions = bridge_data[1]
237
- else:
238
- print(f"Structure: {structure._unit} not currently supported in structure log")
239
- self._write(structure.name, structure._unit, structure.subtype)
240
- continue
241
-
242
- self._write(
243
- structure.name,
244
- structure._unit,
245
- structure.subtype,
246
- structure.comment,
247
- friction,
248
- dimensions,
249
- weir_coefficient,
250
- "",
251
- )
252
-
253
- def _write(
254
- self,
255
- name,
256
- unit,
257
- subtype,
258
- comment="",
259
- friction="",
260
- dimensions="",
261
- weir_coefficient="",
262
- culvert_loss="",
263
- ):
264
- self._writer.writerow(
265
- [
266
- name,
267
- unit,
268
- subtype,
269
- comment,
270
- friction,
271
- dimensions,
272
- weir_coefficient,
273
- culvert_loss,
274
- ]
275
- )
276
-
277
- def create(self):
278
- # Read in the .dat file
279
- self._dat = DAT(self.dat_file_path)
280
-
281
- # Create a new .csv file
282
- with open(self.csv_output_path, "w", newline="") as file:
283
- self._writer = csv.writer(file)
284
-
285
- self._add_fields()
286
-
287
- self._add_conduits()
288
-
289
- self._add_structures()
1
+ import csv
2
+
3
+ from floodmodeller_api import DAT
4
+
5
+
6
+ class StructureLogBuilder:
7
+ def __init__(self, input_path, output_path) -> None:
8
+ self.dat_file_path = input_path
9
+ self.csv_output_path = output_path
10
+
11
+ def _add_fields(self):
12
+ field = [
13
+ "Unit Name",
14
+ "Unit Type",
15
+ "Unit Subtype",
16
+ "Comment",
17
+ "Friction",
18
+ "Dimensions (m)",
19
+ "Weir Coefficient",
20
+ "Culvert Inlet/Outlet Loss",
21
+ ]
22
+ self._writer.writerow(field)
23
+
24
+ def _conduit_data(self, conduit):
25
+ length = 0.0
26
+ inlet = ""
27
+ outlet = ""
28
+ previous = self._dat.prev(conduit)
29
+ if hasattr(previous, "subtype") and previous.subtype == "INLET":
30
+ inlet = previous.ki
31
+ current_conduit = conduit
32
+ while current_conduit.dist_to_next != 0:
33
+ length += current_conduit.dist_to_next
34
+ current_conduit = self._dat.next(current_conduit)
35
+ next_conduit = self._dat.next(current_conduit)
36
+ if hasattr(next_conduit, "subtype") and next_conduit.subtype == "OUTLET":
37
+ outlet = next_conduit.loss_coefficient
38
+ return [length, inlet, outlet]
39
+
40
+ def _culvert_loss_data(self, inlet, outlet):
41
+ culvert_loss = ""
42
+ if inlet != "" and outlet != "":
43
+ culvert_loss = f"Ki: {inlet}, Ko: {outlet}"
44
+ elif inlet != "":
45
+ culvert_loss = f"Ki: {inlet}"
46
+ elif outlet != "":
47
+ culvert_loss = f"Ko: {outlet}"
48
+ return culvert_loss
49
+
50
+ def _circular_data(self, conduit, length):
51
+ dimensions = f"dia: {conduit.diameter:.2f} x l: {length:.2f}"
52
+ all_mannings = [
53
+ conduit.friction_above_axis,
54
+ conduit.friction_below_axis,
55
+ ]
56
+ mannings_set = {min(all_mannings), max(all_mannings)}
57
+ if len(mannings_set) == 1:
58
+ friction = f"Mannings: {mannings_set.pop()}"
59
+ else:
60
+ friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
61
+
62
+ return [friction, dimensions]
63
+
64
+ def _sprungarch_data(self, conduit, length):
65
+ dimensions = f"(Springing: {conduit.height_crown:.2f}, Crown: {conduit.height_springing:.2f}) x w: {conduit.width:.2f} x l: {length:.2f}"
66
+ all_mannings = [
67
+ conduit.friction_on_invert,
68
+ conduit.friction_on_soffit,
69
+ conduit.friction_on_walls,
70
+ ]
71
+ mannings_set = {min(all_mannings), max(all_mannings)}
72
+ if len(mannings_set) == 1:
73
+ friction = f"Mannings: {mannings_set.pop()}"
74
+ else:
75
+ friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
76
+
77
+ return [friction, dimensions]
78
+
79
+ def _rectangular_data(self, conduit, length):
80
+ dimensions = f"h: {conduit.height:.2f} x w: {conduit.width:.2f} x l: {length:.2f}"
81
+ all_mannings = [
82
+ conduit.friction_on_invert,
83
+ conduit.friction_on_soffit,
84
+ conduit.friction_on_walls,
85
+ ]
86
+ mannings_set = {min(all_mannings), max(all_mannings)}
87
+ if len(mannings_set) == 1:
88
+ friction = f"Mannings: {mannings_set.pop()}"
89
+ else:
90
+ friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
91
+
92
+ return [friction, dimensions]
93
+
94
+ def _section_data(self, conduit, length):
95
+ x_list = conduit.coords.x.tolist()
96
+ width = (max(x_list) - min(x_list)) * 2
97
+ y_list = conduit.coords.y.tolist()
98
+ height = max(y_list) - min(y_list)
99
+ # currently this means that height goes to the top of the spike,
100
+ # it is only meant to go up to the height of the majority of the area
101
+ dimensions = f"h: {height:.2f} x w: {width:.2f} x l: {length:.2f}"
102
+ all_cw_frictions = conduit.coords.cw_friction.tolist()
103
+ cw_frictions_set = {min(all_cw_frictions), max(all_cw_frictions)}
104
+ if len(cw_frictions_set) == 1:
105
+ friction = f"Colebrook-White: {cw_frictions_set.pop()}"
106
+ else:
107
+ friction = (
108
+ f"Colebrook-White: [min: {cw_frictions_set.pop()}, max: {cw_frictions_set.pop()}]"
109
+ )
110
+
111
+ return [friction, dimensions]
112
+
113
+ def _sprung_data(self, conduit, length):
114
+ dimensions = f"(Springing: {conduit.height_crown:.2f}, Crown: {conduit.height_springing:.2f}) x w: {conduit.width:.2f} x l: {length:.2f}"
115
+ all_mannings = [
116
+ conduit.friction_on_invert,
117
+ conduit.friction_on_soffit,
118
+ conduit.friction_on_walls,
119
+ ]
120
+ mannings_set = {min(all_mannings), max(all_mannings)}
121
+ if len(mannings_set) == 1:
122
+ friction = f"Mannings: {mannings_set.pop()}"
123
+ else:
124
+ friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
125
+
126
+ return [friction, dimensions]
127
+
128
+ def _add_conduits(self):
129
+ for conduit in self._dat.conduits.values():
130
+ if conduit.subtype not in [
131
+ "CIRCULAR",
132
+ "SPRUNGARCH",
133
+ "RECTANGULAR",
134
+ "SECTION",
135
+ "SPRUNG",
136
+ ]:
137
+ print(f"Conduit subtype: {conduit.subtype} not currently supported")
138
+ self._write(conduit.name, conduit._unit, conduit.subtype)
139
+ continue
140
+ conduit_data = self._conduit_data(conduit)
141
+ length = conduit_data[0]
142
+ inlet = conduit_data[1]
143
+ outlet = conduit_data[2]
144
+
145
+ if length == 0:
146
+ continue
147
+
148
+ culvert_loss = self._culvert_loss_data(inlet, outlet)
149
+ friction = ""
150
+ dimensions = ""
151
+ if conduit.subtype == "CIRCULAR":
152
+ circular_data = self._circular_data(conduit, length)
153
+ friction = circular_data[0]
154
+ dimensions = circular_data[1]
155
+ elif conduit.subtype == "SPRUNGARCH":
156
+ sprungarch_data = self._sprungarch_data(conduit, length)
157
+ friction = sprungarch_data[0]
158
+ dimensions = sprungarch_data[1]
159
+ elif conduit.subtype == "RECTANGULAR":
160
+ rectangular_data = self._rectangular_data(conduit, length)
161
+ friction = rectangular_data[0]
162
+ dimensions = rectangular_data[1]
163
+ elif conduit.subtype == "SECTION":
164
+ section_data = self._section_data(conduit, length)
165
+ friction = section_data[0]
166
+ dimensions = section_data[1]
167
+ elif conduit.subtype == "SPRUNG":
168
+ sprung_data = self._sprung_data(conduit, length)
169
+ friction = sprung_data[0]
170
+ dimensions = sprung_data[1]
171
+
172
+ self._write(
173
+ conduit.name,
174
+ conduit._unit,
175
+ conduit.subtype,
176
+ conduit.comment,
177
+ friction,
178
+ dimensions,
179
+ "",
180
+ culvert_loss,
181
+ )
182
+
183
+ def _orifice_dimensions(self, structure):
184
+ if structure.shape == "RECTANGLE":
185
+ height = structure.soffit - structure.invert
186
+ width = structure.bore_area / height
187
+ dimensions = f"h: {height:.2f} x w: {width:.2f}"
188
+ elif structure.shape == "CIRCULAR":
189
+ diameter = structure.soffit - structure.invert
190
+ dimensions = f"dia: {diameter:.2f}"
191
+ return dimensions
192
+
193
+ def _spill_data(self, structure):
194
+ elevation = min(structure.data.Y.tolist())
195
+ x_list = structure.data.X.tolist()
196
+ width = max(x_list) - min(x_list)
197
+ dimensions = f"Elevation: {elevation:.2f} x w: {width:.2f}"
198
+ weir_coefficient = structure.weir_coefficient
199
+ return [dimensions, weir_coefficient]
200
+
201
+ def _bridge_data(self, structure):
202
+ all_mannings = structure.section_data["Mannings n"].tolist()
203
+ mannings_set = {min(all_mannings), max(all_mannings)}
204
+ if len(mannings_set) == 1:
205
+ friction = f"Mannings: {mannings_set.pop()}"
206
+ else:
207
+ friction = f"Mannings: [min: {mannings_set.pop()}, max: {mannings_set.pop()}]"
208
+ height = structure.opening_data.values[0][3] - min(structure.section_data.Y.tolist())
209
+ width = structure.opening_data.values[0][1] - structure.opening_data.values[0][0]
210
+ dimensions = f"h: {height:.2f} x w: {width:.2f}"
211
+ return [friction, dimensions]
212
+
213
+ def _add_structures(self):
214
+ for structure in self._dat.structures.values():
215
+ friction = ""
216
+ dimensions = ""
217
+ weir_coefficient = ""
218
+ if structure._unit == "ORIFICE":
219
+ dimensions = self._orifice_dimensions(structure)
220
+ elif structure._unit == "SPILL":
221
+ spill_data = self._spill_data(structure)
222
+ dimensions = spill_data[0]
223
+ weir_coefficient = spill_data[1]
224
+ elif structure._unit == "SLUICE":
225
+ dimensions = f"Crest Elevation: {structure.crest_elevation:.2f} x w: {structure.weir_breadth:.2f} x l: {structure.weir_length:.2f}"
226
+ elif structure._unit == "RNWEIR":
227
+ dimensions = f"Crest Elevation: {structure.weir_elevation:.2f} x w: {structure.weir_breadth:.2f} x l: {structure.weir_length:.2f}"
228
+ elif structure._unit == "WEIR":
229
+ dimensions = f"Crest Elevation: {structure.weir_elevation:.2f} x w: {structure.weir_breadth:.2f}"
230
+ # Need weir coefficient (the velocity attribute??)
231
+ elif structure._unit == "BRIDGE":
232
+ bridge_data = self._bridge_data(structure)
233
+ friction = bridge_data[0]
234
+ dimensions = bridge_data[1]
235
+ else:
236
+ print(f"Structure: {structure._unit} not currently supported in structure log")
237
+ self._write(structure.name, structure._unit, structure.subtype)
238
+ continue
239
+
240
+ self._write(
241
+ structure.name,
242
+ structure._unit,
243
+ structure.subtype,
244
+ structure.comment,
245
+ friction,
246
+ dimensions,
247
+ weir_coefficient,
248
+ "",
249
+ )
250
+
251
+ def _write( # noqa: PLR0913
252
+ self,
253
+ name,
254
+ unit,
255
+ subtype,
256
+ comment="",
257
+ friction="",
258
+ dimensions="",
259
+ weir_coefficient="",
260
+ culvert_loss="",
261
+ ):
262
+ self._writer.writerow(
263
+ [
264
+ name,
265
+ unit,
266
+ subtype,
267
+ comment,
268
+ friction,
269
+ dimensions,
270
+ weir_coefficient,
271
+ culvert_loss,
272
+ ],
273
+ )
274
+
275
+ def create(self):
276
+ # Read in the .dat file
277
+ self._dat = DAT(self.dat_file_path)
278
+
279
+ # Create a new .csv file
280
+ with open(self.csv_output_path, "w", newline="") as file:
281
+ self._writer = csv.writer(file)
282
+
283
+ self._add_fields()
284
+
285
+ self._add_conduits()
286
+
287
+ self._add_structures()