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,469 +1,467 @@
1
- """
2
- Flood Modeller Python API
3
- Copyright (C) 2023 Jacobs U.K. Limited
4
-
5
- This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
6
- as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
7
-
8
- This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
9
- of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10
-
11
- You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
12
-
13
- If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
14
- address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
15
- """
16
-
17
- import pandas as pd
18
-
19
- from floodmodeller_api.validation import _validate_unit
20
-
21
- from ._base import Unit
22
- from .helpers import (
23
- _to_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
- )
31
-
32
-
33
- class QTBDY(Unit):
34
- """Class to hold and process QTBDY boundary type
35
-
36
- Args:
37
- name (str, optional): Unit name. Defaults to None.
38
- comment (str, optional): Comment included in unit. Defaults to None.
39
- timeoffset (float, optional): Defaults to None.
40
- timeunit (str, optional): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options. Defaults to None.
41
- extendmethod (str, optional): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
42
- interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
43
- flowmultiplier (float, optional): Multiplier applied to all flow values at runtime. Defaults to None.
44
- minflow (Float, optional): Minimum flow value applied to the boundary at runtime. Defaults to None.
45
- data (pandas.Series, optional): Series object with variable ``'Flow'`` and index ``'Time'``. Defaults to None.
46
- allow_override (str): Allow event parameters to be overridden from simulation file: ''/'OVERRIDE' or 'NOOVERRIDE'
47
-
48
- Returns:
49
- QTBDY: Flood Modeller QTBDY Unit class object
50
- """
51
-
52
- _unit = "QTBDY"
53
-
54
- def _create_from_blank(
55
- self,
56
- name="new_qtbdy",
57
- comment="",
58
- timeoffset=0.0,
59
- timeunit="HOURS",
60
- extendmethod="EXTEND",
61
- interpmethod="LINEAR",
62
- flowmultiplier=0.0,
63
- minflow=0.0,
64
- allow_override="OVERRIDE",
65
- _something=0.0,
66
- data=None,
67
- ):
68
- # Initiate new QTBDY
69
-
70
- for param, val in {
71
- "name": name,
72
- "comment": comment,
73
- "timeunit": timeunit,
74
- "extendmethod": extendmethod,
75
- "interpmethod": interpmethod,
76
- "timeoffset": timeoffset,
77
- "timeunit": timeunit,
78
- "flowmultiplier": flowmultiplier,
79
- "minflow": minflow,
80
- "allow_override": allow_override,
81
- "_something": _something,
82
- }.items():
83
- setattr(self, param, val)
84
-
85
- # AL Since this is most likely used when building a model,
86
- # AL it would be nice to have a "name generator" to create
87
- # AL a unique name with each call (ie new_qtbdy_12345 then new_qtbdy_02508)
88
- # JP Yes this is a good idea, although I'm not sure how it would be best implemented
89
- # since any two instances of the class being initialised would be unaware of each other?
90
- # There is always the option to pass a name when constrcuting the class which may be better
91
-
92
- self.data = (
93
- data
94
- if isinstance(data, pd.Series)
95
- else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Flow")
96
- )
97
-
98
- def _read(self, qtbdy_block):
99
- """Function to read a given QTBDY block and store data as class attributes"""
100
- self.name = qtbdy_block[1][: self._label_len].strip()
101
- self.comment = qtbdy_block[0].replace("QTBDY", "").strip()
102
- qtbdy_params = split_10_char(f"{qtbdy_block[2]:<90}")
103
- self.nrows = int(qtbdy_params[0])
104
- self.timeoffset = _to_float(qtbdy_params[1])
105
- self._something = _to_float(qtbdy_params[2])
106
- self.timeunit = _to_str(qtbdy_params[3], "HOURS", check_float=True)
107
- self.extendmethod = _to_str(qtbdy_params[4], "EXTEND")
108
- self.interpmethod = _to_str(qtbdy_params[5], "LINEAR")
109
- self.flowmultiplier = _to_float(qtbdy_params[6])
110
- self.minflow = _to_float(qtbdy_params[7])
111
- self.allow_override = _to_str(qtbdy_params[8], "OVERRIDE") # ''/OVERRIDE or NOOVERRIDE
112
- data_list = (
113
- _to_data_list(qtbdy_block[3:], date_col=1)
114
- if self.timeunit == "DATES"
115
- else _to_data_list(qtbdy_block[3:])
116
- )
117
-
118
- self.data = pd.DataFrame(data_list, columns=["Flow", "Time"])
119
- self.data = self.data.set_index("Time")
120
- self.data = self.data["Flow"] # Convert to series
121
-
122
- def _write(self):
123
- """Function to write a valid QTBDY block"""
124
- _validate_unit(self) # Function to check the params are valid for QTBDY
125
- header = "QTBDY " + self.comment
126
- name = self.name[: self._label_len]
127
- self.nrows = len(self.data)
128
-
129
- qtbdy_params = join_10_char(
130
- self.nrows,
131
- float(self.timeoffset),
132
- float(self._something),
133
- self.timeunit,
134
- self.extendmethod,
135
- self.interpmethod,
136
- float(self.flowmultiplier),
137
- float(self.minflow),
138
- self.allow_override,
139
- )
140
-
141
- if self.timeunit == "DATES":
142
- qtbdy_data = [join_10_char(q) + t for t, q in self.data.items()]
143
- else:
144
- qtbdy_data = [join_10_char(q, t) for t, q in self.data.items()]
145
- qtbdy_block = [header, name, qtbdy_params]
146
- qtbdy_block.extend(qtbdy_data)
147
-
148
- return qtbdy_block
149
-
150
-
151
- class HTBDY(Unit):
152
- """Class to hold and process HTBDY boundary type
153
-
154
- Args:
155
- name (str, optional): Unit name. Defaults to None.
156
- comment (str, optional): Comment included in unit. Defaults to None.
157
- timeunit (str, optional): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options. Defaults to None.
158
- extendmethod (str, optional): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
159
- interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
160
- data (pandas.Series, optional): Series object with columns ``'Time'`` and ``'Stage'``. Defaults to None.
161
-
162
- Returns:
163
- HTBDY: Flood Modeller HTBDY Unit class object
164
- """
165
-
166
- _unit = "HTBDY"
167
-
168
- def _create_from_blank(
169
- self,
170
- name="new_htbdy",
171
- comment="",
172
- timeunit="HOURS",
173
- extendmethod="EXTEND",
174
- interpmethod="LINEAR",
175
- data=None,
176
- ):
177
- # Initiate new HTBDY
178
-
179
- for param, val in {
180
- "name": name,
181
- "comment": comment,
182
- "timeunit": timeunit,
183
- "extendmethod": extendmethod,
184
- "interpmethod": interpmethod,
185
- }.items():
186
- setattr(self, param, val)
187
-
188
- self.data = (
189
- data
190
- if isinstance(data, pd.Series)
191
- else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Stage")
192
- )
193
-
194
- def _read(self, htbdy_block):
195
- """Function to read a given HTBDY block and store data as class attributes"""
196
- self.name = htbdy_block[1][: self._label_len].strip()
197
- self.comment = htbdy_block[0].replace("HTBDY", "").strip()
198
- htbdy_params = split_10_char(f"{htbdy_block[2]:<50}")
199
- self.nrows = int(htbdy_params[0])
200
- self._something = _to_float(htbdy_params[1])
201
- self.timeunit = _to_str(htbdy_params[2], "HOURS", check_float=True)
202
- self.extendmethod = _to_str(htbdy_params[3], "EXTEND")
203
- self.interpmethod = _to_str(htbdy_params[4], "LINEAR")
204
-
205
- data_list = (
206
- _to_data_list(htbdy_block[3:], date_col=1)
207
- if self.timeunit == "DATES"
208
- else _to_data_list(htbdy_block[3:])
209
- )
210
-
211
- self.data = pd.DataFrame(data_list, columns=["Stage", "Time"])
212
- self.data = self.data.set_index("Time")
213
- self.data = self.data["Stage"] # Convert to series
214
-
215
- def _write(self):
216
- """Function to write a valid HTBDY block"""
217
- _validate_unit(self) # Function to check the params are valid for HTBDY
218
- header = "HTBDY " + self.comment
219
- name = self.name
220
- self.nrows = len(self.data)
221
-
222
- htbdy_params = join_10_char(
223
- self.nrows,
224
- self._something,
225
- self.timeunit,
226
- self.extendmethod,
227
- self.interpmethod,
228
- )
229
- if self.timeunit == "DATES":
230
- htbdy_data = [join_10_char(h) + t for t, h in self.data.items()]
231
- else:
232
- htbdy_data = [join_10_char(h, t) for t, h in self.data.items()]
233
- htbdy_block = [header, name, htbdy_params]
234
- htbdy_block.extend(htbdy_data)
235
-
236
- return htbdy_block
237
-
238
-
239
- class QHBDY(Unit):
240
- """Class to hold and process QHBDY boundary type
241
-
242
- Args:
243
- name (str, optional): Unit name. Defaults to None.
244
- comment (str, optional): Comment included in unit. Defaults to None.
245
- interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
246
- data (pandas.Series, optional): Series object with columns ``'Flow'`` and ``'Stage'``. Defaults to None.
247
- Returns:
248
- QHBDY: Flood Modeller QHBDY Unit class object
249
- """
250
-
251
- _unit = "QHBDY"
252
-
253
- def _create_from_blank(self, name="new_qhbdy", comment="", interpmethod="LINEAR", data=None):
254
- # Initiate new QHBDY
255
- for param, val in {
256
- "name": name,
257
- "comment": comment,
258
- "interpmethod": interpmethod,
259
- }.items():
260
- setattr(self, param, val)
261
- self.data = (
262
- data
263
- if isinstance(data, pd.Series)
264
- else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Stage")
265
- )
266
-
267
- def _read(self, qhbdy_block):
268
- """Function to read a given QHBDY block and store data as class attributes"""
269
- self.name = qhbdy_block[1][: self._label_len].strip()
270
- self.comment = qhbdy_block[0].replace("QHBDY", "").strip()
271
- qhbdy_params = split_10_char(f"{qhbdy_block[2]:<30}")
272
- self.nrows = int(qhbdy_params[0])
273
- self.interpmethod = _to_str(qhbdy_params[2], "LINEAR")
274
-
275
- data_list = _to_data_list(qhbdy_block[3:])
276
-
277
- self.data = pd.DataFrame(data_list, columns=["Flow", "Stage"])
278
- self.data = self.data.set_index("Stage")
279
- self.data = self.data["Flow"] # Convert to series
280
-
281
- pass
282
-
283
- def _write(self):
284
- """Function to write a valid QHBDY block"""
285
- _validate_unit(self) # Function to check the params are valid for QHBDY
286
- header = "QHBDY " + self.comment
287
- name = self.name
288
- self.nrows = len(self.data)
289
-
290
- qhbdy_params = join_10_char(self.nrows, 0.000, self.interpmethod)
291
- qhbdy_data = [f"{q:>10.3f}{h:>10.3f}" for h, q in self.data.items()]
292
- qhbdy_block = [header, name, qhbdy_params]
293
- qhbdy_block.extend(qhbdy_data)
294
-
295
- return qhbdy_block
296
-
297
-
298
- class REFHBDY(Unit):
299
- """Class to hold and process REFHBDY boundary type
300
-
301
- Currently REFHBDY Units are read/edit only and cannot be created from scratch, therefore the
302
- parameters below are only accessible upon instantiating a REFHBDY object from an existing
303
- unit.
304
-
305
- Args:
306
- name (str): Unit name.
307
- comment (str): Comment included in unit.
308
- easting (int): Easting (m)
309
- northing (int): Northing (m)
310
- return_period(float): Flood return period (yrs)
311
- time_delay (float): Time delay before start of hydrograph (hrs)
312
- timestep (float): Time interval for unit hydrograph and rainfall profile
313
- sim_type (str): Simulation Type required: 'FULL' (full hydrograph), 'PFONLY' (peak flow) or 'BFONLY' (baseflow)
314
- scale_method (str): Hydrograph scaling method: 'PEAKVALUE' or 'SCALEFACT'
315
- scale_value (float): Scaling value
316
- boundary_type (str): Boundary type: 'HYDROGRAPH' or 'HYETOGRAPH'
317
- scale_type (str): Full generated hydrograph or quick runnof component only: 'FULL' or 'RUNOFF'
318
- minflow (float): Minimum flow value
319
- allow_override (str): Allow event parameters to be overridden from simulation file: ''/'OVERRIDE' or 'NOOVERRIDE'
320
- area (float): Catchment area (sq km)
321
- saar (int): Seasonal average annual rainfall (mm)
322
- urbext (float): Fraction of urbanised catchment area
323
- season (str): Season for design rainfall profile: 'DEFAULT', 'SUMMER' or 'WINTER'
324
- calc_source (str): ReFH calculation source: 'DLL' (recommended) or 'REPORT'
325
- storm_area (float): Rainfall storm area (sq km)
326
- storm_duration (float): Rainfall storm duration (hrs)
327
- rainfall_comment (str): Comment added to rainfall section of unit
328
- arf_method (str): Method for defining ARF: 'USER' or 'DESIGN'
329
- arf (float): Areal reduction factor (only used if ``arf_method`` set to 'USER')
330
- ddf_c (float): DDF Parameter c
331
- ddf_d1 (float): DDF Parameter d1
332
- ddf_d2 (float): DDF Parameter d2
333
- ddf_d3 (float): DDF Parameter d3
334
- ddf_e (float): DDF Parameter e
335
- ddf_f (float): DDF Parameter f
336
-
337
- Returns:
338
- REFHBDY: Flood Modeller REFHBDY Unit class object
339
- """
340
-
341
- _unit = "REFHBDY"
342
-
343
- def _read(self, refhbdy_block):
344
- """Function to read a given REFHBDY block and store data as class attributes"""
345
- # line 1 & 2
346
- # Extract comment and revision number
347
- b = refhbdy_block[0].replace("REFHBDY #revision#", "").strip()
348
- self._revision = _to_int(b[0], 1)
349
- self.comment = b[1:].strip()
350
- self.name = refhbdy_block[1][: self._label_len].strip()
351
-
352
- # line 3
353
- refhbdy_params1 = split_10_char(refhbdy_block[2])
354
- # TODO: work out what this is
355
- self._something = _to_float(refhbdy_params1[0])
356
- self.easting = int(float(refhbdy_params1[1]))
357
- self.northing = int(float(refhbdy_params1[2]))
358
-
359
- # line 4
360
- refhbdy_opts = split_10_char(f"{refhbdy_block[3]:<90}")
361
- self.time_delay = _to_float(refhbdy_opts[0])
362
- # SD / timestep must be odd interval
363
- self.timestep = _to_float(refhbdy_opts[1])
364
- # '' : Full hydrograph, 'pfonly' : peak flow, 'bfonly' : baseflow only
365
- self.sim_type = refhbdy_opts[2]
366
- self.scale_method = _to_str(refhbdy_opts[3], "SCALEFACT") # PEAKVALUE or SCALEFACT
367
- self.scale_value = _to_float(refhbdy_opts[4], 1.0)
368
- self.boundary_type = _to_str(refhbdy_opts[5], "HYDROGRAPH") # HYDROGRAPH or HYETOGRAPH
369
- self.scale_type = _to_str(refhbdy_opts[6], "FULL") # FULL or RUNOFF
370
- self.minflow = _to_float(refhbdy_opts[7])
371
- self.allow_override = refhbdy_opts[8] # ''/OVERRIDE or NOOVERRIDE
372
-
373
- # line 5
374
- refhbdy_params2 = split_10_char(f"{refhbdy_block[4]:<60}")
375
- self.area = _to_float(refhbdy_params2[0])
376
- try:
377
- # Maintain SAAR as integer if already is, else use float
378
- self.saar = int(refhbdy_params2[1])
379
- except ValueError:
380
- self.saar = float(refhbdy_params2[1])
381
- self.urbext = _to_float(refhbdy_params2[2])
382
- self.season = _to_str(refhbdy_params2[3], "DEFAULT") # DEFAULT, SUMMER or WINTER
383
- self.calc_source = _to_str(refhbdy_params2[4], "DLL") # DLL or REPORT
384
- self.use_urban_subdivisions = False if refhbdy_params2[5] == "" else True
385
- if self.use_urban_subdivisions:
386
- # Just keeping this raw for now as unlikely to be used.
387
- self._urban_refh_data = refhbdy_block[5:8]
388
- rainfall_params1, rainfall_params2, rainfall_params3 = refhbdy_block[8:11]
389
- # Keeping rest raw for now to reduce dev time
390
- self._raw_extra_lines = refhbdy_block[11:]
391
- else:
392
- rainfall_params1, rainfall_params2, rainfall_params3 = refhbdy_block[5:8]
393
- # Keeping rest raw for now to reduce dev time
394
- self._raw_extra_lines = refhbdy_block[8:]
395
-
396
- # line 6
397
- rainfall_params1 = split_10_char(rainfall_params1)
398
- self.storm_area = _to_float(rainfall_params1[0])
399
- self.storm_duration = _to_float(rainfall_params1[1])
400
- # TODO: work out what this is
401
- self._something2 = _to_float(rainfall_params1[2])
402
-
403
- # line 7
404
- self.rainfall_comment = rainfall_params2[20:]
405
- rainfall_params2 = split_10_char(rainfall_params2[:20])
406
- self.arf_method = rainfall_params2[1]
407
- self._something3 = rainfall_params2[0] # TODO: work out what this is
408
-
409
- # line 8
410
- rainfall_params3 = split_10_char(rainfall_params3)
411
- self.observed_rainfall_depth = _to_float(rainfall_params3[0])
412
- self.return_period = _to_float(rainfall_params3[1])
413
- self.arf = _to_float(rainfall_params3[2])
414
- self.ddf_c = _to_float(rainfall_params3[3])
415
- self.ddf_d1 = _to_float(rainfall_params3[4])
416
- self.ddf_d2 = _to_float(rainfall_params3[5])
417
- self.ddf_d3 = _to_float(rainfall_params3[6])
418
- self.ddf_e = _to_float(rainfall_params3[7])
419
- self.ddf_f = _to_float(rainfall_params3[8])
420
-
421
- def _write(self):
422
- """Function to write a valid REFHBDY block"""
423
- _validate_unit(self) # Function to check the params are valid for QTBDY
424
- header = f"REFHBDY #revision#{self._revision} {self.comment}"
425
- name = self.name[: self._label_len]
426
-
427
- refhbdy_block = [header, name]
428
- line3 = join_10_char(self._something, self.easting, self.northing)
429
- self.sim_type = (
430
- "" if self.sim_type.upper() == "FULL" else self.sim_type
431
- ) # Allow 'full' as an option
432
- line4 = (
433
- join_10_char(self.time_delay, self.timestep)
434
- + join_n_char_ljust(10, self.sim_type, self.scale_method)
435
- + join_10_char(self.scale_value)
436
- + join_n_char_ljust(10, self.boundary_type)
437
- + join_10_char(self.scale_type, self.minflow, self.allow_override)
438
- )
439
- use_urban_subdivisions = "" if not self.use_urban_subdivisions else "URBANREFH"
440
- line5 = join_10_char(
441
- self.area,
442
- self.saar,
443
- f"{self.urbext:.4f}",
444
- self.season,
445
- self.calc_source,
446
- use_urban_subdivisions,
447
- )
448
- refhbdy_block.extend([line3, line4, line5])
449
-
450
- if self.use_urban_subdivisions:
451
- refhbdy_block.extend(self._urban_refh_data)
452
-
453
- line6 = join_10_char(self.storm_area, self.storm_duration, self._something2)
454
- line7 = join_10_char(self._something3, self.arf_method) + self.rainfall_comment
455
- line8 = join_10_char(
456
- self.observed_rainfall_depth,
457
- self.return_period,
458
- self.arf,
459
- f"{self.ddf_c:.4f}",
460
- f"{self.ddf_d1:.5f}",
461
- f"{self.ddf_d2:.5f}",
462
- f"{self.ddf_d3:.5f}",
463
- f"{self.ddf_e:.5f}",
464
- f"{self.ddf_f:.5f}",
465
- )
466
-
467
- refhbdy_block.extend([line6, line7, line8])
468
- refhbdy_block.extend(self._raw_extra_lines)
469
- return refhbdy_block
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
+ )
31
+
32
+
33
+ class QTBDY(Unit):
34
+ """Class to hold and process QTBDY boundary type
35
+
36
+ Args:
37
+ name (str, optional): Unit name. Defaults to None.
38
+ comment (str, optional): Comment included in unit. Defaults to None.
39
+ timeoffset (float, optional): Defaults to None.
40
+ timeunit (str, optional): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options. Defaults to None.
41
+ extendmethod (str, optional): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
42
+ interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
43
+ flowmultiplier (float, optional): Multiplier applied to all flow values at runtime. Defaults to None.
44
+ minflow (Float, optional): Minimum flow value applied to the boundary at runtime. Defaults to None.
45
+ data (pandas.Series, optional): Series object with variable ``'Flow'`` and index ``'Time'``. Defaults to None.
46
+ allow_override (str): Allow event parameters to be overridden from simulation file: ''/'OVERRIDE' or 'NOOVERRIDE'
47
+
48
+ Returns:
49
+ QTBDY: Flood Modeller QTBDY Unit class object
50
+ """
51
+
52
+ _unit = "QTBDY"
53
+
54
+ def _create_from_blank( # noqa: PLR0913
55
+ self,
56
+ name="new_qtbdy",
57
+ comment="",
58
+ timeoffset=0.0,
59
+ timeunit="HOURS",
60
+ extendmethod="EXTEND",
61
+ interpmethod="LINEAR",
62
+ flowmultiplier=0.0,
63
+ minflow=0.0,
64
+ allow_override="OVERRIDE",
65
+ _something=0.0,
66
+ data=None,
67
+ ):
68
+ # Initiate new QTBDY
69
+
70
+ for param, val in {
71
+ "name": name,
72
+ "comment": comment,
73
+ "timeunit": timeunit,
74
+ "extendmethod": extendmethod,
75
+ "interpmethod": interpmethod,
76
+ "timeoffset": timeoffset,
77
+ "flowmultiplier": flowmultiplier,
78
+ "minflow": minflow,
79
+ "allow_override": allow_override,
80
+ "_something": _something,
81
+ }.items():
82
+ setattr(self, param, val)
83
+
84
+ # AL Since this is most likely used when building a model,
85
+ # AL it would be nice to have a "name generator" to create
86
+ # AL a unique name with each call (ie new_qtbdy_12345 then new_qtbdy_02508)
87
+ # JP Yes this is a good idea, although I'm not sure how it would be best implemented
88
+ # since any two instances of the class being initialised would be unaware of each other?
89
+ # There is always the option to pass a name when constrcuting the class which may be better
90
+
91
+ self.data = (
92
+ data
93
+ if isinstance(data, pd.Series)
94
+ else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Flow")
95
+ )
96
+
97
+ def _read(self, qtbdy_block):
98
+ """Function to read a given QTBDY block and store data as class attributes"""
99
+ self.name = qtbdy_block[1][: self._label_len].strip()
100
+ self.comment = qtbdy_block[0].replace("QTBDY", "").strip()
101
+ qtbdy_params = split_10_char(f"{qtbdy_block[2]:<90}")
102
+ self.nrows = int(qtbdy_params[0])
103
+ self.timeoffset = _to_float(qtbdy_params[1])
104
+ self._something = _to_float(qtbdy_params[2])
105
+ self.timeunit = _to_str(qtbdy_params[3], "HOURS", check_float=True)
106
+ self.extendmethod = _to_str(qtbdy_params[4], "EXTEND")
107
+ self.interpmethod = _to_str(qtbdy_params[5], "LINEAR")
108
+ self.flowmultiplier = _to_float(qtbdy_params[6])
109
+ self.minflow = _to_float(qtbdy_params[7])
110
+ self.allow_override = _to_str(qtbdy_params[8], "OVERRIDE") # ''/OVERRIDE or NOOVERRIDE
111
+ data_list = (
112
+ _to_data_list(qtbdy_block[3:], date_col=1)
113
+ if self.timeunit == "DATES"
114
+ else _to_data_list(qtbdy_block[3:])
115
+ )
116
+
117
+ self.data = pd.DataFrame(data_list, columns=["Flow", "Time"])
118
+ self.data = self.data.set_index("Time")
119
+ self.data = self.data["Flow"] # Convert to series
120
+
121
+ def _write(self):
122
+ """Function to write a valid QTBDY block"""
123
+ _validate_unit(self) # Function to check the params are valid for QTBDY
124
+ header = "QTBDY " + self.comment
125
+ name = self.name[: self._label_len]
126
+ self.nrows = len(self.data)
127
+
128
+ qtbdy_params = join_10_char(
129
+ self.nrows,
130
+ float(self.timeoffset),
131
+ float(self._something),
132
+ self.timeunit,
133
+ self.extendmethod,
134
+ self.interpmethod,
135
+ float(self.flowmultiplier),
136
+ float(self.minflow),
137
+ self.allow_override,
138
+ )
139
+
140
+ if self.timeunit == "DATES":
141
+ qtbdy_data = [join_10_char(q) + t for t, q in self.data.items()]
142
+ else:
143
+ qtbdy_data = [join_10_char(q, t) for t, q in self.data.items()]
144
+ qtbdy_block = [header, name, qtbdy_params]
145
+ qtbdy_block.extend(qtbdy_data)
146
+
147
+ return qtbdy_block
148
+
149
+
150
+ class HTBDY(Unit):
151
+ """Class to hold and process HTBDY boundary type
152
+
153
+ Args:
154
+ name (str, optional): Unit name. Defaults to None.
155
+ comment (str, optional): Comment included in unit. Defaults to None.
156
+ timeunit (str, optional): Unit of time, e.g. 'HOURS', 'MINUTES' or 'SECONDS'. See Flood Modeller documentation for all available options. Defaults to None.
157
+ extendmethod (str, optional): Data extending method: 'EXTEND', 'NOEXTEND' or 'REPEAT'. Defaults to None.
158
+ interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
159
+ data (pandas.Series, optional): Series object with columns ``'Time'`` and ``'Stage'``. Defaults to None.
160
+
161
+ Returns:
162
+ HTBDY: Flood Modeller HTBDY Unit class object
163
+ """
164
+
165
+ _unit = "HTBDY"
166
+
167
+ def _create_from_blank( # noqa: PLR0913
168
+ self,
169
+ name="new_htbdy",
170
+ comment="",
171
+ timeunit="HOURS",
172
+ extendmethod="EXTEND",
173
+ interpmethod="LINEAR",
174
+ data=None,
175
+ ):
176
+ # Initiate new HTBDY
177
+
178
+ for param, val in {
179
+ "name": name,
180
+ "comment": comment,
181
+ "timeunit": timeunit,
182
+ "extendmethod": extendmethod,
183
+ "interpmethod": interpmethod,
184
+ "_something": "",
185
+ }.items():
186
+ setattr(self, param, val)
187
+
188
+ self.data = (
189
+ data
190
+ if isinstance(data, pd.Series)
191
+ else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Stage")
192
+ )
193
+
194
+ def _read(self, htbdy_block):
195
+ """Function to read a given HTBDY block and store data as class attributes"""
196
+ self.name = htbdy_block[1][: self._label_len].strip()
197
+ self.comment = htbdy_block[0].replace("HTBDY", "").strip()
198
+ htbdy_params = split_10_char(f"{htbdy_block[2]:<50}")
199
+ self.nrows = int(htbdy_params[0])
200
+ self._something = _to_str(htbdy_params[1], "")
201
+ self.timeunit = _to_str(htbdy_params[2], "HOURS", check_float=True)
202
+ self.extendmethod = _to_str(htbdy_params[3], "EXTEND")
203
+ self.interpmethod = _to_str(htbdy_params[4], "LINEAR")
204
+
205
+ data_list = (
206
+ _to_data_list(htbdy_block[3:], date_col=1)
207
+ if self.timeunit == "DATES"
208
+ else _to_data_list(htbdy_block[3:])
209
+ )
210
+
211
+ self.data = pd.DataFrame(data_list, columns=["Stage", "Time"])
212
+ self.data = self.data.set_index("Time")
213
+ self.data = self.data["Stage"] # Convert to series
214
+
215
+ def _write(self):
216
+ """Function to write a valid HTBDY block"""
217
+ _validate_unit(self) # Function to check the params are valid for HTBDY
218
+ header = "HTBDY " + self.comment
219
+ name = self.name
220
+ self.nrows = len(self.data)
221
+
222
+ htbdy_params = join_10_char(
223
+ self.nrows,
224
+ self._something,
225
+ self.timeunit,
226
+ self.extendmethod,
227
+ self.interpmethod,
228
+ )
229
+ if self.timeunit == "DATES":
230
+ htbdy_data = [join_10_char(h) + t for t, h in self.data.items()]
231
+ else:
232
+ htbdy_data = [join_10_char(h, t) for t, h in self.data.items()]
233
+ htbdy_block = [header, name, htbdy_params]
234
+ htbdy_block.extend(htbdy_data)
235
+
236
+ return htbdy_block
237
+
238
+
239
+ class QHBDY(Unit):
240
+ """Class to hold and process QHBDY boundary type
241
+
242
+ Args:
243
+ name (str, optional): Unit name. Defaults to None.
244
+ comment (str, optional): Comment included in unit. Defaults to None.
245
+ interpmethod (str, optional): Data interpolation method: 'LINEAR' or 'SPLINE'. Defaults to None.
246
+ data (pandas.Series, optional): Series object with columns ``'Flow'`` and ``'Stage'``. Defaults to None.
247
+ Returns:
248
+ QHBDY: Flood Modeller QHBDY Unit class object
249
+ """
250
+
251
+ _unit = "QHBDY"
252
+
253
+ def _create_from_blank(self, name="new_qhbdy", comment="", interpmethod="LINEAR", data=None):
254
+ # Initiate new QHBDY
255
+ for param, val in {
256
+ "name": name,
257
+ "comment": comment,
258
+ "interpmethod": interpmethod,
259
+ }.items():
260
+ setattr(self, param, val)
261
+ self.data = (
262
+ data
263
+ if isinstance(data, pd.Series)
264
+ else pd.Series([0.0, 0.0], index=[0.0, 0.1], name="Stage")
265
+ )
266
+
267
+ def _read(self, qhbdy_block):
268
+ """Function to read a given QHBDY block and store data as class attributes"""
269
+ self.name = qhbdy_block[1][: self._label_len].strip()
270
+ self.comment = qhbdy_block[0].replace("QHBDY", "").strip()
271
+ qhbdy_params = split_10_char(f"{qhbdy_block[2]:<30}")
272
+ self.nrows = int(qhbdy_params[0])
273
+ self.interpmethod = _to_str(qhbdy_params[2], "LINEAR")
274
+
275
+ data_list = _to_data_list(qhbdy_block[3:])
276
+
277
+ self.data = pd.DataFrame(data_list, columns=["Flow", "Stage"])
278
+ self.data = self.data.set_index("Stage")
279
+ self.data = self.data["Flow"] # Convert to series
280
+
281
+ def _write(self):
282
+ """Function to write a valid QHBDY block"""
283
+ _validate_unit(self) # Function to check the params are valid for QHBDY
284
+ header = "QHBDY " + self.comment
285
+ name = self.name
286
+ self.nrows = len(self.data)
287
+
288
+ qhbdy_params = join_10_char(self.nrows, 0.000, self.interpmethod)
289
+ qhbdy_data = [f"{q:>10.3f}{h:>10.3f}" for h, q in self.data.items()]
290
+ qhbdy_block = [header, name, qhbdy_params]
291
+ qhbdy_block.extend(qhbdy_data)
292
+
293
+ return qhbdy_block
294
+
295
+
296
+ class REFHBDY(Unit):
297
+ """Class to hold and process REFHBDY boundary type
298
+
299
+ Currently REFHBDY Units are read/edit only and cannot be created from scratch, therefore the
300
+ parameters below are only accessible upon instantiating a REFHBDY object from an existing
301
+ unit.
302
+
303
+ Args:
304
+ name (str): Unit name.
305
+ comment (str): Comment included in unit.
306
+ easting (int): Easting (m)
307
+ northing (int): Northing (m)
308
+ return_period(float): Flood return period (yrs)
309
+ time_delay (float): Time delay before start of hydrograph (hrs)
310
+ timestep (float): Time interval for unit hydrograph and rainfall profile
311
+ sim_type (str): Simulation Type required: 'FULL' (full hydrograph), 'PFONLY' (peak flow) or 'BFONLY' (baseflow)
312
+ scale_method (str): Hydrograph scaling method: 'PEAKVALUE' or 'SCALEFACT'
313
+ scale_value (float): Scaling value
314
+ boundary_type (str): Boundary type: 'HYDROGRAPH' or 'HYETOGRAPH'
315
+ scale_type (str): Full generated hydrograph or quick runnof component only: 'FULL' or 'RUNOFF'
316
+ minflow (float): Minimum flow value
317
+ allow_override (str): Allow event parameters to be overridden from simulation file: ''/'OVERRIDE' or 'NOOVERRIDE'
318
+ area (float): Catchment area (sq km)
319
+ saar (int): Seasonal average annual rainfall (mm)
320
+ urbext (float): Fraction of urbanised catchment area
321
+ season (str): Season for design rainfall profile: 'DEFAULT', 'SUMMER' or 'WINTER'
322
+ calc_source (str): ReFH calculation source: 'DLL' (recommended) or 'REPORT'
323
+ storm_area (float): Rainfall storm area (sq km)
324
+ storm_duration (float): Rainfall storm duration (hrs)
325
+ rainfall_comment (str): Comment added to rainfall section of unit
326
+ arf_method (str): Method for defining ARF: 'USER' or 'DESIGN'
327
+ arf (float): Areal reduction factor (only used if ``arf_method`` set to 'USER')
328
+ ddf_c (float): DDF Parameter c
329
+ ddf_d1 (float): DDF Parameter d1
330
+ ddf_d2 (float): DDF Parameter d2
331
+ ddf_d3 (float): DDF Parameter d3
332
+ ddf_e (float): DDF Parameter e
333
+ ddf_f (float): DDF Parameter f
334
+
335
+ Returns:
336
+ REFHBDY: Flood Modeller REFHBDY Unit class object
337
+ """
338
+
339
+ _unit = "REFHBDY"
340
+
341
+ def _read(self, refhbdy_block): # noqa: PLR0915
342
+ """Function to read a given REFHBDY block and store data as class attributes"""
343
+ # line 1 & 2
344
+ # Extract comment and revision number
345
+ b = refhbdy_block[0].replace("REFHBDY #revision#", "").strip()
346
+ self._revision = _to_int(b[0], 1)
347
+ self.comment = b[1:].strip()
348
+ self.name = refhbdy_block[1][: self._label_len].strip()
349
+
350
+ # line 3
351
+ refhbdy_params1 = split_10_char(refhbdy_block[2])
352
+ # TODO: work out what this is
353
+ self._something = _to_float(refhbdy_params1[0])
354
+ self.easting = int(float(refhbdy_params1[1]))
355
+ self.northing = int(float(refhbdy_params1[2]))
356
+
357
+ # line 4
358
+ refhbdy_opts = split_10_char(f"{refhbdy_block[3]:<90}")
359
+ self.time_delay = _to_float(refhbdy_opts[0])
360
+ # SD / timestep must be odd interval
361
+ self.timestep = _to_float(refhbdy_opts[1])
362
+ # '' : Full hydrograph, 'pfonly' : peak flow, 'bfonly' : baseflow only
363
+ self.sim_type = refhbdy_opts[2]
364
+ self.scale_method = _to_str(refhbdy_opts[3], "SCALEFACT") # PEAKVALUE or SCALEFACT
365
+ self.scale_value = _to_float(refhbdy_opts[4], 1.0)
366
+ self.boundary_type = _to_str(refhbdy_opts[5], "HYDROGRAPH") # HYDROGRAPH or HYETOGRAPH
367
+ self.scale_type = _to_str(refhbdy_opts[6], "FULL") # FULL or RUNOFF
368
+ self.minflow = _to_float(refhbdy_opts[7])
369
+ self.allow_override = refhbdy_opts[8] # ''/OVERRIDE or NOOVERRIDE
370
+
371
+ # line 5
372
+ refhbdy_params2 = split_10_char(f"{refhbdy_block[4]:<60}")
373
+ self.area = _to_float(refhbdy_params2[0])
374
+ try:
375
+ # Maintain SAAR as integer if already is, else use float
376
+ self.saar = int(refhbdy_params2[1])
377
+ except ValueError:
378
+ self.saar = float(refhbdy_params2[1])
379
+ self.urbext = _to_float(refhbdy_params2[2])
380
+ self.season = _to_str(refhbdy_params2[3], "DEFAULT") # DEFAULT, SUMMER or WINTER
381
+ self.calc_source = _to_str(refhbdy_params2[4], "DLL") # DLL or REPORT
382
+ self.use_urban_subdivisions = refhbdy_params2[5] != ""
383
+ if self.use_urban_subdivisions:
384
+ # Just keeping this raw for now as unlikely to be used.
385
+ self._urban_refh_data = refhbdy_block[5:8]
386
+ rainfall_params1, rainfall_params2, rainfall_params3 = refhbdy_block[8:11]
387
+ # Keeping rest raw for now to reduce dev time
388
+ self._raw_extra_lines = refhbdy_block[11:]
389
+ else:
390
+ rainfall_params1, rainfall_params2, rainfall_params3 = refhbdy_block[5:8]
391
+ # Keeping rest raw for now to reduce dev time
392
+ self._raw_extra_lines = refhbdy_block[8:]
393
+
394
+ # line 6
395
+ rainfall_params1 = split_10_char(rainfall_params1)
396
+ self.storm_area = _to_float(rainfall_params1[0])
397
+ self.storm_duration = _to_float(rainfall_params1[1])
398
+ # TODO: work out what this is
399
+ self._something2 = _to_float(rainfall_params1[2])
400
+
401
+ # line 7
402
+ self.rainfall_comment = rainfall_params2[20:]
403
+ rainfall_params2 = split_10_char(rainfall_params2[:20])
404
+ self.arf_method = rainfall_params2[1]
405
+ self._something3 = rainfall_params2[0] # TODO: work out what this is
406
+
407
+ # line 8
408
+ rainfall_params3 = split_10_char(rainfall_params3)
409
+ self.observed_rainfall_depth = _to_float(rainfall_params3[0])
410
+ self.return_period = _to_float(rainfall_params3[1])
411
+ self.arf = _to_float(rainfall_params3[2])
412
+ self.ddf_c = _to_float(rainfall_params3[3])
413
+ self.ddf_d1 = _to_float(rainfall_params3[4])
414
+ self.ddf_d2 = _to_float(rainfall_params3[5])
415
+ self.ddf_d3 = _to_float(rainfall_params3[6])
416
+ self.ddf_e = _to_float(rainfall_params3[7])
417
+ self.ddf_f = _to_float(rainfall_params3[8])
418
+
419
+ def _write(self):
420
+ """Function to write a valid REFHBDY block"""
421
+ _validate_unit(self) # Function to check the params are valid for QTBDY
422
+ header = f"REFHBDY #revision#{self._revision} {self.comment}"
423
+ name = self.name[: self._label_len]
424
+
425
+ refhbdy_block = [header, name]
426
+ line3 = join_10_char(self._something, self.easting, self.northing)
427
+ self.sim_type = (
428
+ "" if self.sim_type.upper() == "FULL" else self.sim_type
429
+ ) # Allow 'full' as an option
430
+ line4 = (
431
+ join_10_char(self.time_delay, self.timestep)
432
+ + join_n_char_ljust(10, self.sim_type, self.scale_method)
433
+ + join_10_char(self.scale_value)
434
+ + join_n_char_ljust(10, self.boundary_type)
435
+ + join_10_char(self.scale_type, self.minflow, self.allow_override)
436
+ )
437
+ use_urban_subdivisions = "" if not self.use_urban_subdivisions else "URBANREFH"
438
+ line5 = join_10_char(
439
+ self.area,
440
+ self.saar,
441
+ f"{self.urbext:.4f}",
442
+ self.season,
443
+ self.calc_source,
444
+ use_urban_subdivisions,
445
+ )
446
+ refhbdy_block.extend([line3, line4, line5])
447
+
448
+ if self.use_urban_subdivisions:
449
+ refhbdy_block.extend(self._urban_refh_data)
450
+
451
+ line6 = join_10_char(self.storm_area, self.storm_duration, self._something2)
452
+ line7 = join_10_char(self._something3, self.arf_method) + self.rainfall_comment
453
+ line8 = join_10_char(
454
+ self.observed_rainfall_depth,
455
+ self.return_period,
456
+ self.arf,
457
+ f"{self.ddf_c:.4f}",
458
+ f"{self.ddf_d1:.5f}",
459
+ f"{self.ddf_d2:.5f}",
460
+ f"{self.ddf_d3:.5f}",
461
+ f"{self.ddf_e:.5f}",
462
+ f"{self.ddf_f:.5f}",
463
+ )
464
+
465
+ refhbdy_block.extend([line6, line7, line8])
466
+ refhbdy_block.extend(self._raw_extra_lines)
467
+ return refhbdy_block