floodmodeller-api 0.4.2.post1__py3-none-any.whl → 0.4.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. floodmodeller_api/__init__.py +8 -9
  2. floodmodeller_api/_base.py +169 -176
  3. floodmodeller_api/backup.py +273 -273
  4. floodmodeller_api/dat.py +889 -831
  5. floodmodeller_api/diff.py +136 -119
  6. floodmodeller_api/ied.py +302 -306
  7. floodmodeller_api/ief.py +553 -637
  8. floodmodeller_api/ief_flags.py +253 -253
  9. floodmodeller_api/inp.py +260 -266
  10. floodmodeller_api/libs/libifcoremd.dll +0 -0
  11. floodmodeller_api/libs/libifcoremt.so.5 +0 -0
  12. floodmodeller_api/libs/libifport.so.5 +0 -0
  13. floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
  14. floodmodeller_api/libs/libintlc.so.5 +0 -0
  15. floodmodeller_api/libs/libmmd.dll +0 -0
  16. floodmodeller_api/libs/libsvml.so +0 -0
  17. floodmodeller_api/libs/libzzn_read.so +0 -0
  18. floodmodeller_api/libs/zzn_read.dll +0 -0
  19. floodmodeller_api/logs/__init__.py +2 -2
  20. floodmodeller_api/logs/lf.py +364 -312
  21. floodmodeller_api/logs/lf_helpers.py +354 -352
  22. floodmodeller_api/logs/lf_params.py +643 -529
  23. floodmodeller_api/mapping.py +84 -0
  24. floodmodeller_api/test/__init__.py +4 -4
  25. floodmodeller_api/test/conftest.py +16 -8
  26. floodmodeller_api/test/test_backup.py +117 -117
  27. floodmodeller_api/test/test_conveyance.py +107 -0
  28. floodmodeller_api/test/test_dat.py +222 -92
  29. floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
  30. floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
  31. floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
  32. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
  33. floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
  34. floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
  35. floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
  36. floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
  37. floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
  38. floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
  39. floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
  40. floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
  41. floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
  42. floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
  43. floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
  44. floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
  45. floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
  46. floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
  47. floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
  48. floodmodeller_api/test/test_data/EX1.DAT +321 -321
  49. floodmodeller_api/test/test_data/EX1.ext +107 -107
  50. floodmodeller_api/test/test_data/EX1.feb +320 -320
  51. floodmodeller_api/test/test_data/EX1.gxy +107 -107
  52. floodmodeller_api/test/test_data/EX17.DAT +421 -422
  53. floodmodeller_api/test/test_data/EX17.ext +213 -213
  54. floodmodeller_api/test/test_data/EX17.feb +422 -422
  55. floodmodeller_api/test/test_data/EX18.DAT +375 -375
  56. floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
  57. floodmodeller_api/test/test_data/EX2.DAT +302 -302
  58. floodmodeller_api/test/test_data/EX3.DAT +926 -926
  59. floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
  60. floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
  61. floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
  62. floodmodeller_api/test/test_data/EX6.ext +532 -532
  63. floodmodeller_api/test/test_data/EX6.feb +2084 -2084
  64. floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
  65. floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
  66. floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
  67. floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
  68. floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
  69. floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
  70. floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
  71. floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
  72. floodmodeller_api/test/test_data/blockage.dat +50 -50
  73. floodmodeller_api/test/test_data/blockage.ext +45 -45
  74. floodmodeller_api/test/test_data/blockage.feb +9 -9
  75. floodmodeller_api/test/test_data/blockage.gxy +71 -71
  76. floodmodeller_api/test/test_data/conveyance_test.dat +165 -0
  77. floodmodeller_api/test/test_data/conveyance_test.feb +116 -0
  78. floodmodeller_api/test/test_data/conveyance_test.gxy +85 -0
  79. floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
  80. floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
  81. floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
  82. floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
  83. floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
  84. floodmodeller_api/test/test_data/ex3.ief +20 -20
  85. floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
  86. floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
  87. floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
  88. floodmodeller_api/test/test_data/example1.inp +329 -329
  89. floodmodeller_api/test/test_data/example2.inp +158 -158
  90. floodmodeller_api/test/test_data/example3.inp +297 -297
  91. floodmodeller_api/test/test_data/example4.inp +388 -388
  92. floodmodeller_api/test/test_data/example5.inp +147 -147
  93. floodmodeller_api/test/test_data/example6.inp +154 -154
  94. floodmodeller_api/test/test_data/expected_conveyance.csv +60 -0
  95. floodmodeller_api/test/test_data/jump.dat +176 -176
  96. floodmodeller_api/test/test_data/network.dat +1374 -1374
  97. floodmodeller_api/test/test_data/network.ext +45 -45
  98. floodmodeller_api/test/test_data/network.exy +1 -1
  99. floodmodeller_api/test/test_data/network.feb +45 -45
  100. floodmodeller_api/test/test_data/network.ied +45 -45
  101. floodmodeller_api/test/test_data/network.ief +20 -20
  102. floodmodeller_api/test/test_data/network.inp +147 -147
  103. floodmodeller_api/test/test_data/network.pxy +57 -57
  104. floodmodeller_api/test/test_data/network.zzd +122 -122
  105. floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
  106. floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
  107. floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
  108. floodmodeller_api/test/test_data/rnweir.dat +9 -9
  109. floodmodeller_api/test/test_data/rnweir.ext +45 -45
  110. floodmodeller_api/test/test_data/rnweir.feb +9 -9
  111. floodmodeller_api/test/test_data/rnweir.gxy +45 -45
  112. floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
  113. floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
  114. floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
  115. floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
  116. floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
  117. floodmodeller_api/test/test_data/unit checks.dat +16 -16
  118. floodmodeller_api/test/test_ied.py +29 -29
  119. floodmodeller_api/test/test_ief.py +136 -24
  120. floodmodeller_api/test/test_inp.py +47 -48
  121. floodmodeller_api/test/test_json.py +114 -0
  122. floodmodeller_api/test/test_logs_lf.py +102 -51
  123. floodmodeller_api/test/test_tool.py +165 -152
  124. floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
  125. floodmodeller_api/test/test_xml2d.py +151 -156
  126. floodmodeller_api/test/test_zzn.py +36 -34
  127. floodmodeller_api/to_from_json.py +230 -0
  128. floodmodeller_api/tool.py +332 -329
  129. floodmodeller_api/toolbox/__init__.py +5 -5
  130. floodmodeller_api/toolbox/example_tool.py +45 -45
  131. floodmodeller_api/toolbox/model_build/__init__.py +2 -2
  132. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -98
  133. floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
  134. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
  135. floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -76
  136. floodmodeller_api/units/__init__.py +10 -10
  137. floodmodeller_api/units/_base.py +214 -212
  138. floodmodeller_api/units/boundaries.py +467 -467
  139. floodmodeller_api/units/comment.py +52 -55
  140. floodmodeller_api/units/conduits.py +382 -402
  141. floodmodeller_api/units/conveyance.py +301 -0
  142. floodmodeller_api/units/helpers.py +123 -131
  143. floodmodeller_api/units/iic.py +107 -101
  144. floodmodeller_api/units/losses.py +305 -306
  145. floodmodeller_api/units/sections.py +465 -446
  146. floodmodeller_api/units/structures.py +1690 -1683
  147. floodmodeller_api/units/units.py +93 -104
  148. floodmodeller_api/units/unsupported.py +44 -44
  149. floodmodeller_api/units/variables.py +87 -89
  150. floodmodeller_api/urban1d/__init__.py +11 -11
  151. floodmodeller_api/urban1d/_base.py +188 -179
  152. floodmodeller_api/urban1d/conduits.py +93 -85
  153. floodmodeller_api/urban1d/general_parameters.py +58 -58
  154. floodmodeller_api/urban1d/junctions.py +81 -79
  155. floodmodeller_api/urban1d/losses.py +81 -74
  156. floodmodeller_api/urban1d/outfalls.py +114 -110
  157. floodmodeller_api/urban1d/raingauges.py +111 -111
  158. floodmodeller_api/urban1d/subsections.py +92 -98
  159. floodmodeller_api/urban1d/xsections.py +147 -144
  160. floodmodeller_api/util.py +119 -21
  161. floodmodeller_api/validation/parameters.py +660 -660
  162. floodmodeller_api/validation/urban_parameters.py +388 -404
  163. floodmodeller_api/validation/validation.py +110 -108
  164. floodmodeller_api/version.py +1 -1
  165. floodmodeller_api/xml2d.py +632 -673
  166. floodmodeller_api/xml2d_template.py +37 -37
  167. floodmodeller_api/zzn.py +414 -363
  168. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/LICENSE.txt +13 -13
  169. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/METADATA +85 -82
  170. floodmodeller_api-0.4.4.dist-info/RECORD +185 -0
  171. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/WHEEL +1 -1
  172. floodmodeller_api/libifcoremd.dll +0 -0
  173. floodmodeller_api/test/test_data/EX3.bmp +0 -0
  174. floodmodeller_api/test/test_data/test_output.csv +0 -87
  175. floodmodeller_api/zzn_read.dll +0 -0
  176. floodmodeller_api-0.4.2.post1.dist-info/RECORD +0 -164
  177. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/entry_points.txt +0 -0
  178. {floodmodeller_api-0.4.2.post1.dist-info → floodmodeller_api-0.4.4.dist-info}/top_level.txt +0 -0
@@ -1,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_conduit = self._dat.next(current_conduit)
37
- if hasattr(next_conduit, "subtype"):
38
- if next_conduit.subtype == "OUTLET":
39
- outlet = next_conduit.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()