wolfhece 2.1.126__py3-none-any.whl → 2.1.128__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.
- wolfhece/PyConfig.py +77 -4
- wolfhece/PyDraw.py +765 -13
- wolfhece/PyPalette.py +36 -0
- wolfhece/PyParams.py +2 -2
- wolfhece/PyVertexvectors.py +560 -64
- wolfhece/apps/version.py +1 -1
- wolfhece/coupling/hydrology_2d.py +295 -192
- wolfhece/eikonal.py +505 -0
- wolfhece/hydrology/Catchment.py +48 -48
- wolfhece/hydrology/PyWatershed.py +93 -93
- wolfhece/lagrange_multiplier.py +205 -0
- wolfhece/lazviewer/laz_viewer.py +28 -3
- wolfhece/math_parser/calculator.py +1 -0
- wolfhece/pybridges.py +2 -2
- wolfhece/pypolygons_scen.py +2 -2
- wolfhece/scenario/config_manager.py +12 -12
- wolfhece/wolf_array.py +1048 -42
- wolfhece/wolfresults_2D.py +204 -13
- {wolfhece-2.1.126.dist-info → wolfhece-2.1.128.dist-info}/METADATA +2 -3
- {wolfhece-2.1.126.dist-info → wolfhece-2.1.128.dist-info}/RECORD +23 -21
- {wolfhece-2.1.126.dist-info → wolfhece-2.1.128.dist-info}/WHEEL +1 -1
- {wolfhece-2.1.126.dist-info → wolfhece-2.1.128.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.126.dist-info → wolfhece-2.1.128.dist-info}/top_level.txt +0 -0
@@ -16,6 +16,7 @@ from ..PyTranslate import _
|
|
16
16
|
|
17
17
|
from scipy.spatial import KDTree
|
18
18
|
import pandas as pd
|
19
|
+
from datetime import datetime
|
19
20
|
from pathlib import Path
|
20
21
|
import numpy as np
|
21
22
|
from typing import Literal, Union
|
@@ -27,22 +28,32 @@ from matplotlib.axes import Axes
|
|
27
28
|
|
28
29
|
|
29
30
|
class InjectionType(Enum):
|
30
|
-
GLOBAL = 'Global'
|
31
|
-
PARTIAL = 'Partial'
|
32
|
-
ANTHROPOGENIC = 'Anthropogenic'
|
33
|
-
CONSTANT = 'Constant'
|
34
|
-
VIRTUAL = 'Virtual'
|
31
|
+
GLOBAL = 'Global' # Global hydrograph - sum of upstream watershed and local hydrograph (hydrological model)
|
32
|
+
PARTIAL = 'Partial' # Partial hydrograph - local hydrograph only (hydrological model)
|
33
|
+
ANTHROPOGENIC = 'Anthropogenic' # Output of an anthropogenic module (hydrological model)
|
34
|
+
CONSTANT = 'Constant' # Constant value (user input)
|
35
|
+
VIRTUAL = 'Virtual' # Virtual hydrograph - Combination/Part of hydrographs (computed by the coupling)
|
36
|
+
FORCED_UNSTEADY = 'Forced unsteady' # Imposed unsteady flow (user input)
|
35
37
|
|
38
|
+
BUILDING_TOLERANCE = 0.05 # Epsilon for the buildings (DEM-DTM) [m]
|
36
39
|
|
37
40
|
class Searching_Context():
|
41
|
+
""" Part of the hydrological model adapted for seraching tasks """
|
38
42
|
|
39
|
-
def __init__(self,
|
40
|
-
river_axis:vector,
|
41
|
-
kdtree:KDTree,
|
42
|
-
nodes:Node_Watershed,
|
43
|
-
downstream_reaches:list[int],
|
43
|
+
def __init__(self,
|
44
|
+
river_axis:vector,
|
45
|
+
kdtree:KDTree,
|
46
|
+
nodes:Node_Watershed,
|
47
|
+
downstream_reaches:list[int],
|
44
48
|
up_node:Node_Watershed) -> None:
|
45
|
-
|
49
|
+
"""
|
50
|
+
:param river_axis: river axis -- vector
|
51
|
+
:param kdtree: KDTree of the downstream reaches
|
52
|
+
:param nodes: nodes of the downstream reaches - from Hydrology model
|
53
|
+
:param downstream_reaches: downstream reaches - from Hydrology model
|
54
|
+
:param up_node: up node - from Hydrology model
|
55
|
+
"""
|
56
|
+
|
46
57
|
self.river_axis:vector = river_axis # river axis -- vector
|
47
58
|
self.kdtree:KDTree = kdtree # KDTree of the downstream reaches
|
48
59
|
self.nodes:Node_Watershed = nodes # nodes of the downstream reaches - from Hydrology model
|
@@ -50,7 +61,8 @@ class Searching_Context():
|
|
50
61
|
self.up_node:Node_Watershed = up_node # up node - from Hydrology model
|
51
62
|
|
52
63
|
def __str__(self) -> str:
|
53
|
-
|
64
|
+
""" Return a string representation of the searching context """
|
65
|
+
|
54
66
|
ret = ''
|
55
67
|
|
56
68
|
ret += f" Number of reaches: {len(self.downstream_reaches)}\n"
|
@@ -64,7 +76,15 @@ class Searching_Context():
|
|
64
76
|
class Scaled_Infiltration():
|
65
77
|
|
66
78
|
def __init__(self, idx:int, type:InjectionType, colref:str, factor:float, lagtime:float) -> None:
|
67
|
-
|
79
|
+
""" Constructor of the scaled infiltration
|
80
|
+
|
81
|
+
:param idx: index of the infiltration
|
82
|
+
:param type: type of the infiltration
|
83
|
+
:param colref: reference column
|
84
|
+
:param factor: multiplicator factor [-]
|
85
|
+
:param lagtime: lag time [s]
|
86
|
+
"""
|
87
|
+
|
68
88
|
self.index = idx # index of the infiltration
|
69
89
|
self.type = type # type of the infiltration
|
70
90
|
self.colref = colref # reference column
|
@@ -74,7 +94,7 @@ class Scaled_Infiltration():
|
|
74
94
|
class Coupling_Hydrology_2D():
|
75
95
|
|
76
96
|
def __init__(self) -> None:
|
77
|
-
|
97
|
+
|
78
98
|
|
79
99
|
self._rivers_zones:list[Zones] = []
|
80
100
|
self.rivers:dict[str, vector] = {}
|
@@ -92,7 +112,10 @@ class Coupling_Hydrology_2D():
|
|
92
112
|
self.hydrographs_local:pd.DataFrame = None
|
93
113
|
self._hydrographs_virtual = []
|
94
114
|
|
95
|
-
self._dem:WolfArray = None
|
115
|
+
self._dem:WolfArray = None # Digital Elevation Model
|
116
|
+
self._dtm:WolfArray = None # Digital Terrain Model -- Optional -- if exists, no infiltration authorized on buildings (defined as dem > dtm)
|
117
|
+
self._buildings:WolfArray = None # Buildings -- exists if dtm exists
|
118
|
+
|
96
119
|
self._infil_idx:WolfArray = None
|
97
120
|
self.counter_zone_infil = 0
|
98
121
|
|
@@ -100,7 +123,7 @@ class Coupling_Hydrology_2D():
|
|
100
123
|
# Chaque zone est définie par :
|
101
124
|
# - un type d'infiltration
|
102
125
|
# - un nom d'hydrogramme (ou une valeur constante)
|
103
|
-
# - un facteur podérateur
|
126
|
+
# - un facteur podérateur
|
104
127
|
# - un temps de déphasage
|
105
128
|
|
106
129
|
self.infiltrations:list[Scaled_Infiltration] = []
|
@@ -120,11 +143,25 @@ class Coupling_Hydrology_2D():
|
|
120
143
|
self_along = None
|
121
144
|
self._locales = None
|
122
145
|
|
146
|
+
@property
|
147
|
+
def dateBegin(self) -> datetime:
|
148
|
+
if self._hydrology_model is None:
|
149
|
+
logging.error(_("No hydrology model loaded"))
|
150
|
+
return None
|
151
|
+
return self._hydrology_model.dateBegin
|
152
|
+
|
153
|
+
@property
|
154
|
+
def dateEnd(self) -> datetime:
|
155
|
+
if self._hydrology_model is None:
|
156
|
+
logging.error(_("No hydrology model loaded"))
|
157
|
+
return None
|
158
|
+
return self._hydrology_model.dateEnd
|
159
|
+
|
123
160
|
def __str__(self) -> str:
|
124
161
|
""" Return a string representation of the coupling """
|
125
|
-
|
162
|
+
|
126
163
|
ret =''
|
127
|
-
|
164
|
+
|
128
165
|
ret += _("Rivers: {}\n").format(len(self.rivers))
|
129
166
|
for curriver in self.rivers:
|
130
167
|
ret += f"{curriver}\n"
|
@@ -145,22 +182,22 @@ class Coupling_Hydrology_2D():
|
|
145
182
|
ret += f"{cursearch} : \n{self._searching[cursearch]}\n"
|
146
183
|
|
147
184
|
ret += f"Coupling array: \n{self._infil_idx}\n"
|
148
|
-
|
185
|
+
|
149
186
|
return ret
|
150
187
|
|
151
188
|
@property
|
152
189
|
def number_of_injections(self) -> int:
|
153
|
-
|
190
|
+
|
154
191
|
if self._infil_idx is None:
|
155
192
|
logging.error(_("No infiltration array -- Spread the injections first"))
|
156
193
|
return 0
|
157
194
|
|
158
195
|
return self._infil_idx.array.max()
|
159
|
-
|
196
|
+
|
160
197
|
@property
|
161
198
|
def number_of_nodes_per_zone(self) -> dict[int, int]:
|
162
199
|
""" Return the number of nodes per zone """
|
163
|
-
|
200
|
+
|
164
201
|
if self._infil_idx is None:
|
165
202
|
logging.error(_("No infiltration array -- Spread the injections first"))
|
166
203
|
return {}
|
@@ -168,7 +205,7 @@ class Coupling_Hydrology_2D():
|
|
168
205
|
non_zeros = self._infil_idx.array[np.where(self._infil_idx.array > 0)]
|
169
206
|
|
170
207
|
return {i:np.count_nonzero(non_zeros == i) for i in range(1, self.number_of_injections+1)}
|
171
|
-
|
208
|
+
|
172
209
|
def plot_number_of_nodes_per_zone(self) -> tuple[Figure, Axes]:
|
173
210
|
""" Plot the number of nodes per zone """
|
174
211
|
|
@@ -183,20 +220,20 @@ class Coupling_Hydrology_2D():
|
|
183
220
|
ax.set_ylabel(_("Number of nodes"))
|
184
221
|
|
185
222
|
return fig, ax
|
186
|
-
|
223
|
+
|
187
224
|
@property
|
188
225
|
def along(self) -> list[str, str]:
|
189
226
|
return self._along
|
190
|
-
|
227
|
+
|
191
228
|
@along.setter
|
192
229
|
def along(self, value:list[tuple[str, str]]) -> None:
|
193
230
|
|
194
231
|
for curval in value:
|
195
|
-
|
232
|
+
|
196
233
|
curcol, currivers = curval
|
197
|
-
|
234
|
+
|
198
235
|
assert curcol in self.hydrographs_local.columns, f"Column {curcol} not found in hydrographs"
|
199
|
-
|
236
|
+
|
200
237
|
def check_rivers(currivers):
|
201
238
|
if isinstance(currivers, list):
|
202
239
|
for curriver in currivers:
|
@@ -209,36 +246,46 @@ class Coupling_Hydrology_2D():
|
|
209
246
|
@property
|
210
247
|
def locales(self) -> list[str]:
|
211
248
|
return self._locales
|
212
|
-
|
249
|
+
|
213
250
|
@locales.setter
|
214
|
-
def locales(self, value:list[str]) -> None:
|
251
|
+
def locales(self, value:list[tuple[str, str | pd.Series | float, InjectionType]]) -> None:
|
252
|
+
""" Set the locales injections """
|
215
253
|
|
216
254
|
for curvect, curcol, curtype in value:
|
255
|
+
# Check if the data is correct
|
217
256
|
|
218
|
-
if curtype
|
257
|
+
if curtype not in [InjectionType.CONSTANT, InjectionType.FORCED_UNSTEADY]:
|
219
258
|
assert curvect in self.locale_injections, f"Vector {curvect} not found"
|
220
259
|
assert curcol in list(self.hydrographs_total.columns)+ self._hydrographs_virtual, f"Column {curcol} not found in hydrographs"
|
260
|
+
elif curtype == InjectionType.FORCED_UNSTEADY:
|
261
|
+
assert isinstance(curcol, pd.Series), "Forced unsteady flow should be a Series"
|
262
|
+
# check if the DateFrame has value inside interval of the hydrographs
|
263
|
+
assert curcol.index[0] <= self.dateBegin, "Forced unsteady flow should start before the hydrographs"
|
264
|
+
assert curcol.index[-1] >= self.dateEnd, "Forced unsteady flow should end after the hydrographs"
|
265
|
+
else:
|
266
|
+
assert isinstance(curcol, float), "Constant value should be a float"
|
221
267
|
|
222
268
|
self._locales = value
|
223
269
|
|
224
270
|
@property
|
225
271
|
def watershed(self) -> Watershed:
|
226
272
|
return self._hydrology_model.charact_watrshd
|
227
|
-
|
273
|
+
|
228
274
|
@property
|
229
275
|
def river_system(self) -> RiverSystem:
|
230
276
|
return self.watershed.riversystem
|
231
|
-
|
277
|
+
|
232
278
|
@property
|
233
279
|
def subs_array(self) -> WolfArray:
|
234
280
|
return self.watershed.subs_array
|
235
|
-
|
236
|
-
def set_array_to_coupling(self, array:WolfArray | Path) -> None:
|
237
|
-
""" Set the array to coupling
|
238
|
-
|
281
|
+
|
282
|
+
def set_array_to_coupling(self, array:WolfArray | Path, dtm:WolfArray | Path = None) -> None:
|
283
|
+
""" Set the array to coupling
|
284
|
+
|
239
285
|
:param array: The array to coupling
|
286
|
+
:param dtm: The DTM of the array
|
240
287
|
"""
|
241
|
-
|
288
|
+
|
242
289
|
if isinstance(array, Path):
|
243
290
|
if array.exists():
|
244
291
|
self._dem = WolfArray(array)
|
@@ -246,7 +293,18 @@ class Coupling_Hydrology_2D():
|
|
246
293
|
logging.error(_("File {} not found").format(array))
|
247
294
|
return
|
248
295
|
|
296
|
+
if isinstance(dtm, Path):
|
297
|
+
if dtm.exists():
|
298
|
+
self._dtm = WolfArray(dtm)
|
299
|
+
else:
|
300
|
+
logging.error(_("File {} not found").format(dtm))
|
301
|
+
return
|
302
|
+
|
249
303
|
self._dem = array
|
304
|
+
self._dtm = dtm
|
305
|
+
|
306
|
+
assert self._dem.get_header().is_like(self._dtm.get_header()), "DEM and DTM should have the same header"
|
307
|
+
|
250
308
|
self._create_infiltration_array()
|
251
309
|
|
252
310
|
def _create_infiltration_array(self):
|
@@ -256,34 +314,51 @@ class Coupling_Hydrology_2D():
|
|
256
314
|
logging.error(_("No array to coupling"))
|
257
315
|
return
|
258
316
|
|
317
|
+
if self._dtm is None:
|
318
|
+
logging.info(_("No DTM found -- Infiltration authorized everywhere"))
|
319
|
+
else:
|
320
|
+
logging.info(_("DTM found -- Infiltration authorized only on the ground"))
|
321
|
+
self._buildings = self._dem - self._dtm
|
322
|
+
|
323
|
+
# Buildings are defined as the difference between the DEM and the DTM
|
324
|
+
# If the difference is greater than BUILDING_TOLERANCE, the cell is considered as a building
|
325
|
+
# Otherwise, the cell is considered as a ground
|
326
|
+
|
327
|
+
# If cell is masked in the DTM, cells is considred as ground
|
328
|
+
self._buildings.array[np.logical_and(~self._dem.array.mask,self._dtm.array.mask)] = 0.
|
329
|
+
|
330
|
+
self._buildings.array[self._buildings.array < BUILDING_TOLERANCE] = 0.
|
331
|
+
self._buildings.array[self._buildings.array >= BUILDING_TOLERANCE] = 1.
|
332
|
+
self._buildings.mask_data(0.)
|
333
|
+
|
259
334
|
self._infil_idx = WolfArray(srcheader=self._dem.get_header(), whichtype=WOLF_ARRAY_FULL_INTEGER)
|
260
335
|
self._infil_idx.add_ops_sel()
|
261
336
|
self._infil_idx.array[:,:] = 0
|
262
337
|
|
263
|
-
self.counter_zone_infil = 0
|
264
|
-
|
338
|
+
self.counter_zone_infil = 0
|
339
|
+
|
265
340
|
def add_hydrology_model(self, name:str, filename:str | Path) -> None:
|
266
|
-
""" Add a hydrology model to the coupling
|
267
|
-
|
341
|
+
""" Add a hydrology model to the coupling
|
342
|
+
|
268
343
|
:param filename: The filename of the hydrology model
|
269
344
|
"""
|
270
|
-
|
345
|
+
|
271
346
|
self._hydrology_model = Catchment(name, str(filename), False, True)
|
272
|
-
|
347
|
+
|
273
348
|
def get_anthropogenic_names(self) -> list[str]:
|
274
349
|
""" Print the names of the anthropogenic hydrographs """
|
275
|
-
|
350
|
+
|
276
351
|
if self._hydrology_model is None:
|
277
352
|
logging.error(_("No hydrology model loaded"))
|
278
353
|
return []
|
279
354
|
|
280
|
-
return [" : ".join([cur_anth.name, name])
|
281
|
-
for cur_anth in self._hydrology_model.retentionBasinDict.values()
|
355
|
+
return [" : ".join([cur_anth.name, name])
|
356
|
+
for cur_anth in self._hydrology_model.retentionBasinDict.values()
|
282
357
|
for name in cur_anth.get_outFlow_names()]
|
283
|
-
|
358
|
+
|
284
359
|
def get_names_areas(self) -> list[str]:
|
285
360
|
""" Print the names of the areas """
|
286
|
-
|
361
|
+
|
287
362
|
if self._hydrology_model is None:
|
288
363
|
logging.error(_("No hydrology model loaded"))
|
289
364
|
return []
|
@@ -295,12 +370,12 @@ class Coupling_Hydrology_2D():
|
|
295
370
|
return names, area_subs, area_glob
|
296
371
|
|
297
372
|
def create_hydrographs_local_global(self, unit_discharge:float, total_duration:float, anth_discharge:dict = None) -> None:
|
298
|
-
"""
|
373
|
+
"""
|
299
374
|
Create the hydrographs from the hydrology model .
|
300
|
-
|
375
|
+
|
301
376
|
Global and local hydrographs are created based on a unit discharge and a total duration.
|
302
377
|
|
303
|
-
You can also add anthropogenic hydrographs from a dictionary.
|
378
|
+
You can also add anthropogenic hydrographs from a dictionary.
|
304
379
|
The key is the name of the anthropogenic hydrograph and the value is the discharge.
|
305
380
|
The keys can be obtained with the method get_anthropogenic_names.
|
306
381
|
|
@@ -311,14 +386,14 @@ class Coupling_Hydrology_2D():
|
|
311
386
|
# Extract the column names according to their sorted subbasin indices
|
312
387
|
col_time = "Time [s]"
|
313
388
|
col_subs, area_subs, area_glob = self.get_names_areas()
|
314
|
-
col_anth = [" : ".join([cur_anth.name, name])
|
315
|
-
for cur_anth in self._hydrology_model.retentionBasinDict.values()
|
389
|
+
col_anth = [" : ".join([cur_anth.name, name])
|
390
|
+
for cur_anth in self._hydrology_model.retentionBasinDict.values()
|
316
391
|
for name in cur_anth.get_outFlow_names()]
|
317
|
-
|
392
|
+
|
318
393
|
#Create a dictionnary
|
319
394
|
|
320
395
|
dict_glob = {col_time : [0., total_duration]}
|
321
|
-
|
396
|
+
|
322
397
|
for cur_sub, cur_area in zip(col_subs, area_glob):
|
323
398
|
discharge = cur_area * unit_discharge
|
324
399
|
dict_glob[cur_sub] = [discharge, discharge]
|
@@ -350,23 +425,23 @@ class Coupling_Hydrology_2D():
|
|
350
425
|
if self._hydrology_model is None:
|
351
426
|
logging.error(_("No hydrology model loaded"))
|
352
427
|
return
|
353
|
-
|
428
|
+
|
354
429
|
directory = Path(self._hydrology_model.workingDir) / 'PostProcess'
|
355
430
|
|
356
431
|
if total is not None:
|
357
432
|
self.hydrographs_total.to_csv(directory / total, sep='\t', decimal='.', encoding='latin1')
|
358
433
|
else:
|
359
434
|
self.hydrographs_total.to_csv(directory / 'Hydros_2_simul2D.txt', sep='\t', decimal='.', encoding='latin1')
|
360
|
-
|
435
|
+
|
361
436
|
if partial is not None:
|
362
437
|
self.hydrographs_local.to_csv(directory / partial, sep='\t', decimal='.', encoding='latin1')
|
363
438
|
else:
|
364
439
|
self.hydrographs_local.to_csv(directory / 'HydrosSub_2_simul2D.txt', sep='\t', decimal='.', encoding='latin1')
|
365
440
|
|
366
|
-
|
441
|
+
|
367
442
|
def load_hydrographs(self, directory:str | Path = None, total:str = None, partial:str = None) -> None:
|
368
|
-
""" Load the hydrographs from the hydrology model
|
369
|
-
|
443
|
+
""" Load the hydrographs from the hydrology model
|
444
|
+
|
370
445
|
:param directory: The directory of the hydrology model -- If None, the working directory of the loaded hydrology model is used
|
371
446
|
:param total: The filename of the total hydrographs - If None, the default filename is used
|
372
447
|
:param partial: The filename of the partial hydrographs - If None, the default filename is used
|
@@ -378,7 +453,7 @@ class Coupling_Hydrology_2D():
|
|
378
453
|
if self._hydrology_model is None:
|
379
454
|
logging.error(_("No hydrology model loaded"))
|
380
455
|
return
|
381
|
-
|
456
|
+
|
382
457
|
directory = Path(self._hydrology_model.workingDir) / 'PostProcess'
|
383
458
|
|
384
459
|
if total is not None:
|
@@ -401,7 +476,7 @@ class Coupling_Hydrology_2D():
|
|
401
476
|
self.hydrographs_local = pd.read_csv(partial, sep='\t', decimal='.', header=0, index_col=0, encoding='latin1')
|
402
477
|
else:
|
403
478
|
logging.error(_("File {} not found").format(partial))
|
404
|
-
|
479
|
+
|
405
480
|
else:
|
406
481
|
partial = directory / 'HydrosSub_2_simul2D.txt'
|
407
482
|
if partial.exists():
|
@@ -409,18 +484,18 @@ class Coupling_Hydrology_2D():
|
|
409
484
|
else:
|
410
485
|
logging.error(_("File {} not found").format(partial))
|
411
486
|
|
412
|
-
|
487
|
+
|
413
488
|
def print_hydrographs(self, total:bool = True, partial:bool = True) -> None:
|
414
489
|
""" Print the hydrographs from the hydrology model """
|
415
490
|
|
416
491
|
if total:
|
417
492
|
print(_("Total hydrographs:"))
|
418
493
|
print(self.hydrographs_total.columns)
|
419
|
-
|
494
|
+
|
420
495
|
if partial:
|
421
496
|
print(_("Partial hydrographs:"))
|
422
497
|
print(self.hydrographs_local.columns)
|
423
|
-
|
498
|
+
|
424
499
|
def plot_hydrographs(self, total:bool = True, partial:bool = True) -> tuple[tuple[Figure, Axes],tuple[Figure, Axes]]:
|
425
500
|
""" Plot the hydrographs from the hydrology model """
|
426
501
|
|
@@ -430,9 +505,9 @@ class Coupling_Hydrology_2D():
|
|
430
505
|
ax1.legend(loc='upper center', ncol=8)
|
431
506
|
ax1.set_ylim(0, 1000)
|
432
507
|
fig1.set_size_inches(15, 5)
|
433
|
-
fig1.tight_layout()
|
508
|
+
fig1.tight_layout()
|
434
509
|
else:
|
435
|
-
fig1, ax1 = None, None
|
510
|
+
fig1, ax1 = None, None
|
436
511
|
|
437
512
|
if partial:
|
438
513
|
ax2 = self.hydrographs_local.plot()
|
@@ -440,11 +515,11 @@ class Coupling_Hydrology_2D():
|
|
440
515
|
ax2.legend(loc='upper center', ncol=8)
|
441
516
|
ax2.set_ylim(0, 1000)
|
442
517
|
fig2.set_size_inches(15, 5)
|
443
|
-
fig2.tight_layout()
|
518
|
+
fig2.tight_layout()
|
444
519
|
else:
|
445
|
-
fig2, ax2 = None, None
|
520
|
+
fig2, ax2 = None, None
|
446
521
|
|
447
|
-
return (fig1, ax1), (fig2, ax2)
|
522
|
+
return (fig1, ax1), (fig2, ax2)
|
448
523
|
|
449
524
|
|
450
525
|
def add_virtual_hydrograph(self, name:str, src_hydrograph_name:str, factor:float, lag:float=0.):
|
@@ -453,16 +528,16 @@ class Coupling_Hydrology_2D():
|
|
453
528
|
self._hydrographs_virtual.append((name, src_hydrograph_name, factor, lag))
|
454
529
|
|
455
530
|
def add_river(self, filename:str | Path) -> None:
|
456
|
-
""" Add a river to the hydrology model
|
457
|
-
|
531
|
+
""" Add a river to the hydrology model
|
532
|
+
|
458
533
|
:param filename: The filename of the river
|
459
534
|
"""
|
460
|
-
|
535
|
+
|
461
536
|
self._rivers_zones.append(Zones(filename))
|
462
537
|
|
463
538
|
def reset(self) -> None:
|
464
539
|
""" Reset the hydrology model """
|
465
|
-
|
540
|
+
|
466
541
|
self._rivers_zones = []
|
467
542
|
self.rivers = {}
|
468
543
|
self._locale_injection_zones = None
|
@@ -475,11 +550,11 @@ class Coupling_Hydrology_2D():
|
|
475
550
|
self.reset_injections()
|
476
551
|
|
477
552
|
def add_locale_injections(self, filename:str | Path) -> None:
|
478
|
-
""" Add a local injection to the hydrology model
|
479
|
-
|
553
|
+
""" Add a local injection to the hydrology model
|
554
|
+
|
480
555
|
:param filename: The filename of the local injection
|
481
556
|
"""
|
482
|
-
|
557
|
+
|
483
558
|
self._locale_injection_zones = Zones(filename)
|
484
559
|
|
485
560
|
def find_river_axis(self):
|
@@ -498,19 +573,19 @@ class Coupling_Hydrology_2D():
|
|
498
573
|
logging.warning(_("Vector {} in zone {} is not used -- Ignoring it as a river axis").format(curvector.myname, curzone.myname))
|
499
574
|
|
500
575
|
def _add_injection(self, name:str, vect:vector):
|
501
|
-
""" Add an injection to the hydrology model
|
502
|
-
|
576
|
+
""" Add an injection to the hydrology model
|
577
|
+
|
503
578
|
:param name: The name of the injection
|
504
579
|
:param vect: The vector of the injection
|
505
580
|
"""
|
506
|
-
|
581
|
+
|
507
582
|
self.locale_injections[name] = vect
|
508
583
|
|
509
584
|
def find_injections(self):
|
510
585
|
""" Find the injection points from Zones """
|
511
586
|
|
512
587
|
for curzone in self._locale_injection_zones.myzones:
|
513
|
-
|
588
|
+
|
514
589
|
names = [curvect.myname for curvect in curzone.myvectors]
|
515
590
|
|
516
591
|
if 'injection' in names:
|
@@ -530,8 +605,8 @@ class Coupling_Hydrology_2D():
|
|
530
605
|
self.upstreams[curriver] = (up.x, up.y)
|
531
606
|
|
532
607
|
def _find_upstream(self, curvect:vector) -> wolfvertex:
|
533
|
-
""" Find the upstream of a vector
|
534
|
-
|
608
|
+
""" Find the upstream of a vector
|
609
|
+
|
535
610
|
:param curvect: The river's axis
|
536
611
|
"""
|
537
612
|
|
@@ -552,17 +627,17 @@ class Coupling_Hydrology_2D():
|
|
552
627
|
return vert1
|
553
628
|
|
554
629
|
def prepare_search(self, rivers:list[str] = None):
|
555
|
-
"""
|
630
|
+
"""
|
556
631
|
Prepare the search for the hydrology model.
|
557
632
|
|
558
|
-
The order is important because the reaches will be
|
633
|
+
The order is important because the reaches will be
|
559
634
|
progressively excluded from the search for the next ones.
|
560
635
|
|
561
636
|
So, you have to start with the **main river** and then the **tributaries**.
|
562
|
-
|
637
|
+
|
563
638
|
:param rivers: The list of rivers to prepare
|
564
639
|
"""
|
565
|
-
|
640
|
+
|
566
641
|
excluded = []
|
567
642
|
|
568
643
|
if rivers is None:
|
@@ -580,12 +655,12 @@ class Coupling_Hydrology_2D():
|
|
580
655
|
|
581
656
|
# Récupération de la maille rivière la plus proche
|
582
657
|
dist, node_up = self.river_system.get_nearest_nodes(self.upstreams[curriver])
|
583
|
-
|
658
|
+
|
584
659
|
# Récupération de la liste des biefs en aval
|
585
660
|
downstream = self.river_system.get_downstream_reaches_excluded(node_up, excluded)
|
586
661
|
|
587
662
|
excluded += downstream
|
588
|
-
|
663
|
+
|
589
664
|
# Mis en place d'une structure de recherche rapide
|
590
665
|
nodes, kdtree = self.river_system.get_kdtree_from_reaches(downstream)
|
591
666
|
|
@@ -593,37 +668,37 @@ class Coupling_Hydrology_2D():
|
|
593
668
|
|
594
669
|
def _is_global(self, col_name:str):
|
595
670
|
""" Vérifie si la colonne est un hydrogramme global """
|
596
|
-
|
671
|
+
|
597
672
|
return col_name in self.hydrographs_total.columns
|
598
673
|
|
599
674
|
def _is_partial(self, col_name:str):
|
600
675
|
""" Vérifie si la colonne est un hydrogramme partiel """
|
601
|
-
|
676
|
+
|
602
677
|
return col_name in self.hydrographs_local.columns
|
603
678
|
|
604
679
|
def _is_anthropic(self, col_name:str):
|
605
|
-
"""
|
606
|
-
Vérifie si la colonne est un hydrogramme anthropique
|
680
|
+
"""
|
681
|
+
Vérifie si la colonne est un hydrogramme anthropique
|
607
682
|
(c'est-à-dire une colonne de l'hydrogramme total qui n'est pas un hydrogramme partiel)
|
608
|
-
|
683
|
+
|
609
684
|
"""
|
610
|
-
|
685
|
+
|
611
686
|
return self._is_global(col_name) and not self._is_partial(col_name)
|
612
687
|
|
613
688
|
def _is_virtual(self, col_name:str):
|
614
689
|
""" Vérifie si la colonne est un hydrogramme virtuel """
|
615
|
-
|
690
|
+
|
616
691
|
return col_name in [virtualname for virtualname, src_name, frac, lag in self._hydrographs_virtual]
|
617
692
|
|
618
|
-
def _add_infil(self,
|
619
|
-
type_name:InjectionType,
|
620
|
-
col_name_q:str | float,
|
621
|
-
factor:float,
|
622
|
-
lag:float,
|
693
|
+
def _add_infil(self,
|
694
|
+
type_name:InjectionType,
|
695
|
+
col_name_q:str | float,
|
696
|
+
factor:float,
|
697
|
+
lag:float,
|
623
698
|
index_zone:int = None):
|
624
|
-
"""
|
625
|
-
Ajoute une infiltration à la liste des infiltrations
|
626
|
-
|
699
|
+
"""
|
700
|
+
Ajoute une infiltration à la liste des infiltrations
|
701
|
+
|
627
702
|
:param type_name: nom du type d'infiltration
|
628
703
|
:param col_name: nom de la colonne de l'hydrogramme
|
629
704
|
:param factor: facteur multiplicatif
|
@@ -646,20 +721,20 @@ class Coupling_Hydrology_2D():
|
|
646
721
|
if index_zone is None:
|
647
722
|
self.counter_zone_infil += 1
|
648
723
|
index_zone = self.counter_zone_infil
|
649
|
-
|
724
|
+
|
650
725
|
self.infiltrations.append(Scaled_Infiltration(index_zone, type_name, col_name_q, factor, lag))
|
651
726
|
|
652
727
|
return index_zone
|
653
728
|
|
654
|
-
def _add_local_injecton(self,
|
655
|
-
local_vect:vector,
|
656
|
-
type_name:InjectionType,
|
657
|
-
col_name:str,
|
658
|
-
factor:float,
|
729
|
+
def _add_local_injecton(self,
|
730
|
+
local_vect:vector,
|
731
|
+
type_name:InjectionType,
|
732
|
+
col_name:str,
|
733
|
+
factor:float,
|
659
734
|
lag:float):
|
660
|
-
|
661
|
-
"""
|
662
|
-
Ajoute une injection locale à la liste des infiltrations
|
735
|
+
|
736
|
+
"""
|
737
|
+
Ajoute une injection locale à la liste des infiltrations
|
663
738
|
et remplissage de la matrice d'infiltration
|
664
739
|
|
665
740
|
:param local_vect: vecteur de la zone d'injection
|
@@ -667,7 +742,7 @@ class Coupling_Hydrology_2D():
|
|
667
742
|
:param col_name: nom de la colonne de l'hydrogramme
|
668
743
|
:param factor: facteur multiplicatif
|
669
744
|
:param lag: déphasage
|
670
|
-
|
745
|
+
|
671
746
|
"""
|
672
747
|
|
673
748
|
assert type_name in InjectionType, f"Unknown type {type_name}"
|
@@ -677,7 +752,7 @@ class Coupling_Hydrology_2D():
|
|
677
752
|
|
678
753
|
self.local_infiltrations[local_vect] = self._add_infil(type_name, col_name, factor, lag)
|
679
754
|
|
680
|
-
# Mise à zéro de la sélection dans la matrice d'infiltration
|
755
|
+
# Mise à zéro de la sélection dans la matrice d'infiltration
|
681
756
|
self._infil_idx.SelectionData.reset()
|
682
757
|
# Sélection des mailles à l'intérieur du polygone de la zone d'injection
|
683
758
|
self._infil_idx.SelectionData.select_insidepoly(local_vect)
|
@@ -687,12 +762,23 @@ class Coupling_Hydrology_2D():
|
|
687
762
|
# Conversion des coordonnées en indices de mailles
|
688
763
|
ij = self._infil_idx.get_ij_from_xy_array(xy)
|
689
764
|
|
690
|
-
|
691
|
-
|
692
|
-
|
765
|
+
if self._buildings is not None:
|
766
|
+
# Vérification de la présence de bâtiments dans la zone d'infiltration
|
767
|
+
for i,j in ij:
|
768
|
+
if self._buildings.array[i,j] == 1:
|
769
|
+
logging.warning(f"Building found in infiltration zone {local_vect.myname} -- Maille {i,j} ignored")
|
770
|
+
continue
|
771
|
+
self._infil_idx.array[i,j] = self.local_infiltrations[local_vect]
|
772
|
+
|
773
|
+
assert np.count_nonzero(self._infil_idx.array == self.local_infiltrations[local_vect]) > 0, f"No infiltration in zone {local_vect.parentzone.myname}"
|
774
|
+
|
775
|
+
else:
|
776
|
+
# Affectation de l'indice de la zone d'infiltration
|
777
|
+
for i,j in ij:
|
778
|
+
self._infil_idx.array[i,j] = self.local_infiltrations[local_vect]
|
693
779
|
|
694
|
-
|
695
|
-
|
780
|
+
# Vérification du nombre de mailles affectées
|
781
|
+
assert len(ij) == np.count_nonzero(self._infil_idx.array == self.local_infiltrations[local_vect]), "Bad count for {}".format(type_name)
|
696
782
|
|
697
783
|
else:
|
698
784
|
# Une injection existe déjà dans cette zone
|
@@ -707,8 +793,8 @@ class Coupling_Hydrology_2D():
|
|
707
793
|
col_name:str,
|
708
794
|
factor:float,
|
709
795
|
lag:float):
|
710
|
-
"""
|
711
|
-
Ajoute une injection le long de la rivière
|
796
|
+
"""
|
797
|
+
Ajoute une injection le long de la rivière
|
712
798
|
et remplissage de la matrice d'infiltration
|
713
799
|
|
714
800
|
:param list_part: liste des coordonnées des points de la rivière
|
@@ -727,22 +813,22 @@ class Coupling_Hydrology_2D():
|
|
727
813
|
|
728
814
|
def write_infil_array(self, dirout:Path):
|
729
815
|
""" Sauvegarde de la matrice d'infiltration """
|
730
|
-
|
816
|
+
|
731
817
|
self._infil_idx.mask_data(0)
|
732
818
|
self._infil_idx.nullvalue = 99999
|
733
819
|
self._infil_idx.set_nullvalue_in_mask()
|
734
820
|
self._infil_idx.write_all(dirout / f'infiltration.tif')
|
735
821
|
|
736
822
|
def _get_reaches_in_sub(self, subbasin:SubWatershed, rivers_names:list[str]) -> list[list[int]]:
|
737
|
-
"""
|
738
|
-
Retourne une liste de listes des biefs dans le sous-bassin
|
739
|
-
|
823
|
+
"""
|
824
|
+
Retourne une liste de listes des biefs dans le sous-bassin
|
825
|
+
|
740
826
|
:param rivers: liste des noms des rivières
|
741
827
|
:return: liste des biefs dans le sous-bassin
|
742
828
|
"""
|
743
829
|
|
744
830
|
ret = []
|
745
|
-
|
831
|
+
|
746
832
|
if rivers_names[0] not in self._searching:
|
747
833
|
logging.error(f"River {rivers_names[0]} not found")
|
748
834
|
return ret
|
@@ -750,7 +836,7 @@ class Coupling_Hydrology_2D():
|
|
750
836
|
reaches1 = self._searching[rivers_names[0]].downstream_reaches
|
751
837
|
|
752
838
|
reaches_in_sub = [idx for idx in reaches1 if subbasin.is_reach_in_sub(idx)]
|
753
|
-
|
839
|
+
|
754
840
|
if len(reaches_in_sub) == 0:
|
755
841
|
logging.error(f"No reaches in subbasin for river {rivers_names[0]}")
|
756
842
|
|
@@ -762,7 +848,7 @@ class Coupling_Hydrology_2D():
|
|
762
848
|
for loc in locret:
|
763
849
|
ret.append(loc)
|
764
850
|
else:
|
765
|
-
|
851
|
+
|
766
852
|
if rivers_names[1] not in self._searching:
|
767
853
|
logging.error(f"River {rivers_names[1]} not found")
|
768
854
|
return ret
|
@@ -778,9 +864,9 @@ class Coupling_Hydrology_2D():
|
|
778
864
|
return ret
|
779
865
|
|
780
866
|
def _get_outlet_reaches(self, subbasin:SubWatershed, idx_reaches:list[int]) -> Node_Watershed:
|
781
|
-
"""
|
782
|
-
Retourne le noeud de sortie du sous-bassin
|
783
|
-
|
867
|
+
"""
|
868
|
+
Retourne le noeud de sortie du sous-bassin
|
869
|
+
|
784
870
|
:param reaches: liste des biefs dans le sous-bassin
|
785
871
|
:return: noeud de sortie du sous-bassin
|
786
872
|
"""
|
@@ -800,16 +886,16 @@ class Coupling_Hydrology_2D():
|
|
800
886
|
return newsub1, newsub2
|
801
887
|
|
802
888
|
def _split_hydrographs(self, subbasin:SubWatershed | str, river_names:list[str, list[str]]):
|
803
|
-
"""
|
804
|
-
Séparation de l'hydrogramme partiel en fonction
|
805
|
-
des surfaces drainées par chaque rivère
|
806
|
-
|
889
|
+
"""
|
890
|
+
Séparation de l'hydrogramme partiel en fonction
|
891
|
+
des surfaces drainées par chaque rivère
|
892
|
+
|
807
893
|
On attend au maximum 2 rivières ou 1 rivière et une liste de rivières.
|
808
894
|
|
809
895
|
Les rivières seront traitées 2 par 2 de façon récursive.
|
810
896
|
|
811
897
|
La seconde rivière et l'affluent de la première rivière.
|
812
|
-
|
898
|
+
|
813
899
|
"""
|
814
900
|
|
815
901
|
if isinstance(subbasin, str):
|
@@ -821,7 +907,7 @@ class Coupling_Hydrology_2D():
|
|
821
907
|
fraction2 = sub2.area / subbasin.area
|
822
908
|
|
823
909
|
assert fraction1 + fraction2 == 1., "Bad fractions"
|
824
|
-
|
910
|
+
|
825
911
|
self.add_virtual_hydrograph(sub1.name, subbasin.name, fraction1, 0.)
|
826
912
|
self.add_virtual_hydrograph(sub2.name, subbasin.name, fraction2, 0.)
|
827
913
|
|
@@ -835,18 +921,18 @@ class Coupling_Hydrology_2D():
|
|
835
921
|
added.append((sub2.name, river_names[1]))
|
836
922
|
|
837
923
|
return added
|
838
|
-
|
924
|
+
|
839
925
|
def get_locale_injection_names(self):
|
840
926
|
""" Print the names of the local injections """
|
841
|
-
|
927
|
+
|
842
928
|
return list(self.locale_injections.keys())
|
843
|
-
|
929
|
+
|
844
930
|
def get_along_injection_names(self) -> tuple[list[str], list[str]]:
|
845
|
-
""" Get the names of the along injections
|
846
|
-
|
931
|
+
""" Get the names of the along injections
|
932
|
+
|
847
933
|
:return: The names of the rivers along which the injections are made and the columns of the hydrographs
|
848
934
|
"""
|
849
|
-
|
935
|
+
|
850
936
|
return list(self.lists_part.keys()), list(self.hydrographs_local.columns)
|
851
937
|
|
852
938
|
def reset_injections(self):
|
@@ -858,7 +944,7 @@ class Coupling_Hydrology_2D():
|
|
858
944
|
self._infil_idx.array[:,:] = 0
|
859
945
|
|
860
946
|
self._hydrographs_virtual = []
|
861
|
-
|
947
|
+
|
862
948
|
self.infiltrations = []
|
863
949
|
self.local_infiltrations = {}
|
864
950
|
|
@@ -871,7 +957,7 @@ class Coupling_Hydrology_2D():
|
|
871
957
|
self.injections_along()
|
872
958
|
|
873
959
|
self.create_hydrographs()
|
874
|
-
|
960
|
+
|
875
961
|
def injections_locales(self, couplings:list[tuple[str, str, InjectionType]] = None):
|
876
962
|
""" Ajoute les injections locales """
|
877
963
|
|
@@ -890,9 +976,9 @@ class Coupling_Hydrology_2D():
|
|
890
976
|
|
891
977
|
|
892
978
|
def link_area2nodes(self):
|
893
|
-
"""
|
979
|
+
"""
|
894
980
|
Searching cells in dem associated to the river nodes in the hydrological model.
|
895
|
-
|
981
|
+
|
896
982
|
We use the river axis to select the cells in the dem.
|
897
983
|
|
898
984
|
Then we search the nearest river nodes in the hydrological
|
@@ -901,7 +987,7 @@ class Coupling_Hydrology_2D():
|
|
901
987
|
We create local lists of cells associated to one river node.
|
902
988
|
|
903
989
|
Due to the fact that the river axis is not exactly the same
|
904
|
-
as the river nodes (not the same spatial resolution, rester vs vector),
|
990
|
+
as the river nodes (not the same spatial resolution, rester vs vector),
|
905
991
|
all river nodes in the hydrological model
|
906
992
|
are not necessarely associated to cells in the dem.
|
907
993
|
|
@@ -914,22 +1000,22 @@ class Coupling_Hydrology_2D():
|
|
914
1000
|
nodes = self._searching[key_river].nodes
|
915
1001
|
|
916
1002
|
# Mise à 0 des zones sélectionnées
|
917
|
-
self._dem.SelectionData.reset()
|
1003
|
+
self._dem.SelectionData.reset()
|
918
1004
|
# Sélection des mailles sur l'axe du lit mineur
|
919
1005
|
self._dem.SelectionData.select_underpoly(river_axis)
|
920
1006
|
|
921
1007
|
# Coordonnées XY des mailles sélectionnées
|
922
1008
|
xy_selected = self._dem.SelectionData.myselection
|
923
|
-
|
924
|
-
# Recherche des mailles rivières les plus proches
|
1009
|
+
|
1010
|
+
# Recherche des mailles rivières les plus proches
|
925
1011
|
# dans la modélisation hydrologique
|
926
1012
|
dist, nearest = kdtree.query(xy_selected, k=1)
|
927
1013
|
# Récupération des noeuds correspondants aux index fournis par l'objet KDTree
|
928
1014
|
nearest:list[Node_Watershed] = [nodes[i] for i in nearest]
|
929
|
-
|
1015
|
+
|
930
1016
|
# Surface drainée par les mailles
|
931
1017
|
drained_surface = np.array([cur.uparea for cur in nearest])
|
932
|
-
|
1018
|
+
|
933
1019
|
# Valeurs de BV uniques
|
934
1020
|
unique_area = np.unique(drained_surface)
|
935
1021
|
|
@@ -950,7 +1036,7 @@ class Coupling_Hydrology_2D():
|
|
950
1036
|
if along is None:
|
951
1037
|
logging.error(_("No along injections defined"))
|
952
1038
|
return
|
953
|
-
|
1039
|
+
|
954
1040
|
along = along.copy()
|
955
1041
|
|
956
1042
|
# ## Création de bassins virtuels afin de séparer les hydrogrammes en plusieurs rivières
|
@@ -964,7 +1050,7 @@ class Coupling_Hydrology_2D():
|
|
964
1050
|
|
965
1051
|
# La rivière secondaire peut également être décomposée en plusieurs selon le même principe.
|
966
1052
|
|
967
|
-
# **La procédure de calcul est récursive.**
|
1053
|
+
# **La procédure de calcul est récursive.**
|
968
1054
|
to_split = [cur for cur in along if isinstance(cur[1], list)]
|
969
1055
|
|
970
1056
|
replace = []
|
@@ -975,16 +1061,16 @@ class Coupling_Hydrology_2D():
|
|
975
1061
|
along.remove(cur)
|
976
1062
|
|
977
1063
|
for cur in replace:
|
978
|
-
along.append(cur)
|
1064
|
+
along.append(cur)
|
979
1065
|
|
980
1066
|
for cur in along:
|
981
|
-
self._injection_along(cur)
|
982
|
-
|
1067
|
+
self._injection_along(cur)
|
1068
|
+
|
983
1069
|
def _injection_along(self, name_subwatershed_river:tuple[str, str]):
|
984
1070
|
|
985
|
-
# Nom de colonne et liste de mailles potentielles à utiliser
|
1071
|
+
# Nom de colonne et liste de mailles potentielles à utiliser
|
986
1072
|
# pour la répartition
|
987
|
-
|
1073
|
+
|
988
1074
|
name_subwatershed, river = name_subwatershed_river
|
989
1075
|
|
990
1076
|
list_rivers, used_reaches = self.lists_part[river], self._searching[river].downstream_reaches
|
@@ -1006,14 +1092,14 @@ class Coupling_Hydrology_2D():
|
|
1006
1092
|
local_areas = 0.
|
1007
1093
|
# liste contenant la maille de connection au réseau et la maille d'injection locale
|
1008
1094
|
to_remove:list[tuple[Node_Watershed, Node_Watershed, float]] = []
|
1009
|
-
|
1095
|
+
|
1010
1096
|
for cur_locinj in local_injections:
|
1011
1097
|
|
1012
1098
|
# Recherche du noeud rivière le plus proche de la zone d'injection locale
|
1013
1099
|
dist, node_local_injection = self.river_system.get_nearest_nodes(cur_locinj)
|
1014
1100
|
|
1015
1101
|
if node_local_injection.reach not in used_reaches:
|
1016
|
-
# Recherche de la maille rivière en aval
|
1102
|
+
# Recherche de la maille rivière en aval
|
1017
1103
|
# qui fait partie de la distribution en long
|
1018
1104
|
down = self.river_system.go_downstream_until_reach_found(node_local_injection, used_reaches)
|
1019
1105
|
else:
|
@@ -1022,8 +1108,8 @@ class Coupling_Hydrology_2D():
|
|
1022
1108
|
down = node_local_injection
|
1023
1109
|
while down is not None and down.uparea not in unique_areas:
|
1024
1110
|
down = down.down
|
1025
|
-
|
1026
|
-
|
1111
|
+
|
1112
|
+
|
1027
1113
|
# surface su sous-bassin qui sera injectée localement
|
1028
1114
|
local_area = node_local_injection.uparea - subbasin.get_area_outside_sub_if_exists(node_local_injection, node_local_injection.get_up_reaches_same_sub())
|
1029
1115
|
|
@@ -1043,7 +1129,7 @@ class Coupling_Hydrology_2D():
|
|
1043
1129
|
|
1044
1130
|
# aire drainée à la limite amont du ss-bassin, le long de la distribution en long
|
1045
1131
|
up_node = subbasin.get_up_rivernode_outside_sub(subbasin.outlet, used_reaches)
|
1046
|
-
|
1132
|
+
|
1047
1133
|
if up_node is None:
|
1048
1134
|
starting_node = subbasin.get_list_nodes_river(min(used_reaches))[-1]
|
1049
1135
|
area_min = subbasin.get_area_outside_sub_if_exists(starting_node, starting_node.get_up_reaches_same_sub())
|
@@ -1059,7 +1145,7 @@ class Coupling_Hydrology_2D():
|
|
1059
1145
|
frac_sum=0.
|
1060
1146
|
|
1061
1147
|
def area_to_remove(node:Node_Watershed) -> float:
|
1062
|
-
|
1148
|
+
|
1063
1149
|
uparea = 0.
|
1064
1150
|
|
1065
1151
|
# injections locales
|
@@ -1086,10 +1172,10 @@ class Coupling_Hydrology_2D():
|
|
1086
1172
|
|
1087
1173
|
if fraction_loc > 0.:
|
1088
1174
|
|
1089
|
-
self._add_along_injection(lists[unique_areas[idx]],
|
1090
|
-
InjectionType.PARTIAL if not subbasin._is_virtual else InjectionType.VIRTUAL,
|
1091
|
-
name_subwatershed,
|
1092
|
-
fraction_loc,
|
1175
|
+
self._add_along_injection(lists[unique_areas[idx]],
|
1176
|
+
InjectionType.PARTIAL if not subbasin._is_virtual else InjectionType.VIRTUAL,
|
1177
|
+
name_subwatershed,
|
1178
|
+
fraction_loc,
|
1093
1179
|
lag =0.)
|
1094
1180
|
|
1095
1181
|
frac_sum += fraction_loc
|
@@ -1100,26 +1186,26 @@ class Coupling_Hydrology_2D():
|
|
1100
1186
|
|
1101
1187
|
fraction_loc = delta_loc / delta_area
|
1102
1188
|
|
1103
|
-
self._add_along_injection(lists[unique_areas[-1]],
|
1104
|
-
InjectionType.PARTIAL if not subbasin._is_virtual else InjectionType.VIRTUAL,
|
1105
|
-
name_subwatershed,
|
1106
|
-
fraction_loc,
|
1189
|
+
self._add_along_injection(lists[unique_areas[-1]],
|
1190
|
+
InjectionType.PARTIAL if not subbasin._is_virtual else InjectionType.VIRTUAL,
|
1191
|
+
name_subwatershed,
|
1192
|
+
fraction_loc,
|
1107
1193
|
lag =0.)
|
1108
1194
|
|
1109
1195
|
frac_sum += fraction_loc
|
1110
1196
|
|
1111
1197
|
if frac_sum > 1.001 or frac_sum < 0.999:
|
1112
|
-
logging.error(f"Bad sum of fractions {frac_sum} " + name_subwatershed)
|
1198
|
+
logging.error(f"Bad sum of fractions {frac_sum} " + name_subwatershed)
|
1113
1199
|
|
1114
1200
|
|
1115
1201
|
def create_hydrographs(self):
|
1116
|
-
""" Création des hydrogrammes
|
1117
|
-
|
1202
|
+
""" Création des hydrogrammes
|
1203
|
+
|
1118
1204
|
Les étapes précédentes ont ajouté à la liste "infiltrations" les éléments suivants:
|
1119
1205
|
|
1120
1206
|
- l'index de la zone d'infiltration (1-based)
|
1121
1207
|
- l'hydrogramme de référence
|
1122
|
-
-
|
1208
|
+
- le facteur pondérateur
|
1123
1209
|
- le temps de déphasage
|
1124
1210
|
|
1125
1211
|
Une zone peut contenir plusieurs apports.
|
@@ -1128,7 +1214,7 @@ class Coupling_Hydrology_2D():
|
|
1128
1214
|
|
1129
1215
|
Le fichier final est ordonné comme la matrice d'infiltration.
|
1130
1216
|
|
1131
|
-
Avant de sommer, il faut tout d'abord créer les hydrogrammes associés au BV virtuels (décomposition d'un BV, modélisé comme un tout, en plusieurs rivières distinctes pour la répartition en long)
|
1217
|
+
Avant de sommer, il faut tout d'abord créer les hydrogrammes associés au BV virtuels (décomposition d'un BV, modélisé comme un tout, en plusieurs rivières distinctes pour la répartition en long)
|
1132
1218
|
"""
|
1133
1219
|
|
1134
1220
|
dt = self.hydrographs_total.index[1] - self.hydrographs_total.index[0]
|
@@ -1146,7 +1232,7 @@ class Coupling_Hydrology_2D():
|
|
1146
1232
|
|
1147
1233
|
if src_hydrograph_name in self.hydrographs_local.columns:
|
1148
1234
|
df_virtual[name] = self.hydrographs_local.shift(decal, fill_value=0.)[src_hydrograph_name] * factor
|
1149
|
-
|
1235
|
+
|
1150
1236
|
elif src_hydrograph_name in df_virtual.columns:
|
1151
1237
|
df_virtual[name] = df_virtual.shift(decal, fill_value=0.)[src_hydrograph_name] * factor
|
1152
1238
|
|
@@ -1176,26 +1262,26 @@ class Coupling_Hydrology_2D():
|
|
1176
1262
|
loc_count += 1
|
1177
1263
|
|
1178
1264
|
if type_name == InjectionType.GLOBAL:
|
1179
|
-
|
1265
|
+
|
1180
1266
|
if loc_count == 1:
|
1181
1267
|
df_2d_dict[idx] = self.hydrographs_total.shift(decal, fill_value = 0.)[col_name] * factor
|
1182
1268
|
else:
|
1183
1269
|
df_2d_dict[idx] += self.hydrographs_total.shift(decal, fill_value = 0.)[col_name] * factor
|
1184
|
-
|
1270
|
+
|
1185
1271
|
elif type_name == InjectionType.PARTIAL:
|
1186
|
-
|
1272
|
+
|
1187
1273
|
if loc_count == 1:
|
1188
1274
|
df_2d_dict[idx] = self.hydrographs_local.shift(decal, fill_value = 0.)[col_name] * factor
|
1189
1275
|
else:
|
1190
1276
|
df_2d_dict[idx] += self.hydrographs_local.shift(decal, fill_value = 0.)[col_name] * factor
|
1191
|
-
|
1277
|
+
|
1192
1278
|
elif type_name == InjectionType.ANTHROPOGENIC:
|
1193
1279
|
|
1194
1280
|
if loc_count == 1:
|
1195
1281
|
df_2d_dict[idx] = self.hydrographs_total.shift(decal, fill_value = 0.)[col_name] * factor
|
1196
1282
|
else:
|
1197
1283
|
df_2d_dict[idx] += self.hydrographs_total.shift(decal, fill_value = 0.)[col_name] * factor
|
1198
|
-
|
1284
|
+
|
1199
1285
|
elif type_name == InjectionType.VIRTUAL:
|
1200
1286
|
|
1201
1287
|
if loc_count == 1:
|
@@ -1210,9 +1296,17 @@ class Coupling_Hydrology_2D():
|
|
1210
1296
|
else:
|
1211
1297
|
df_2d_dict[idx] += col_name * factor
|
1212
1298
|
|
1299
|
+
elif type_name == InjectionType.FORCED_UNSTEADY:
|
1300
|
+
|
1301
|
+
col_name:pd.Series
|
1302
|
+
if loc_count == 1:
|
1303
|
+
df_2d_dict[idx] = col_name.loc[self.dateBegin:self.dateEnd].values * factor
|
1304
|
+
else:
|
1305
|
+
df_2d_dict[idx] += col_name.loc[self.dateBegin:self.dateEnd].values * factor
|
1306
|
+
|
1213
1307
|
else:
|
1214
1308
|
logging.error(f"Unknown type {type_name}")
|
1215
|
-
|
1309
|
+
|
1216
1310
|
if loc_count != counter[i-1]:
|
1217
1311
|
logging.error(f"Bad count for {i}")
|
1218
1312
|
|
@@ -1221,9 +1315,18 @@ class Coupling_Hydrology_2D():
|
|
1221
1315
|
|
1222
1316
|
|
1223
1317
|
def save_hydrographs(self, dirout:Path, name:str):
|
1224
|
-
""" Write the hydrographs
|
1318
|
+
""" Write the hydrographs
|
1225
1319
|
|
1226
|
-
|
1320
|
+
:param dirout: The output directory
|
1321
|
+
:param name: The name of the output file (if no suffix .txt, it will be added)
|
1322
|
+
"""
|
1323
|
+
|
1324
|
+
# ensure suffix .txt
|
1325
|
+
if not name.endswith('.txt'):
|
1326
|
+
name += '.txt'
|
1327
|
+
|
1328
|
+
locname = name.replace('.txt', '_infiltration_zones.txt')
|
1329
|
+
with open(dirout / locname, 'w') as f:
|
1227
1330
|
f.write("Zone\tType\tColonne\tFacteur\tLag\n")
|
1228
1331
|
for cur in self.infiltrations:
|
1229
1332
|
idx, type_name, col_name, factor, lag = cur.index, cur.type.value, cur.colref, cur.factor, cur.lagtime
|
@@ -1232,5 +1335,5 @@ class Coupling_Hydrology_2D():
|
|
1232
1335
|
if self.df_2d is None:
|
1233
1336
|
logging.error("No hydrographs created")
|
1234
1337
|
return
|
1235
|
-
|
1236
|
-
self.df_2d.to_csv(dirout / name, sep='\t', decimal='.', encoding='latin1')
|
1338
|
+
|
1339
|
+
self.df_2d.to_csv(dirout / name, sep='\t', decimal='.', encoding='latin1')
|