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
@@ -0,0 +1,205 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
def junction_q_lagrangemult(q:np.ndarray):
|
4
|
+
""" Compute the junction flow rate q_junction
|
5
|
+
from the flow rates q of the branches.
|
6
|
+
|
7
|
+
:param q: np.ndarray, estimated signed flow rates of the branches
|
8
|
+
"""
|
9
|
+
|
10
|
+
# Number of incoming branches
|
11
|
+
n = q.size
|
12
|
+
|
13
|
+
# Compute the flow rate ensuring mass conservation
|
14
|
+
# using the Lagrange multiplier method
|
15
|
+
#
|
16
|
+
# We minimize the following function:
|
17
|
+
# sum_{i=1}^{n} 1/2 * (qnew_i - q_i) ** 2
|
18
|
+
# subject to the constraint:
|
19
|
+
# sum_{i=1}^{n} qnew_i
|
20
|
+
#
|
21
|
+
# The solution is given by inversing the matrix A
|
22
|
+
# A = [1 1 1 ... 1 0]
|
23
|
+
# [1 0 0 ... 0 1]
|
24
|
+
# [0 1 0 ... 0 1]
|
25
|
+
# [0 0 1 ... 0 1]
|
26
|
+
# ...
|
27
|
+
# [0 0 0 ... 1 1]
|
28
|
+
#
|
29
|
+
# [lambda qnew] = A^(-1) * [0 q1 q2 ... qn]
|
30
|
+
|
31
|
+
# Create the matrix A
|
32
|
+
A = np.zeros((n+1, n+1))
|
33
|
+
A[0, :-1] = 1
|
34
|
+
A[1:, :-1] = np.eye(n)
|
35
|
+
A[1:, -1] = 1
|
36
|
+
|
37
|
+
# Compute the inverse of A
|
38
|
+
A_inv = np.linalg.inv(A)
|
39
|
+
|
40
|
+
# Compute the new flow rates
|
41
|
+
qnew = A_inv @ np.concatenate(([0], q))
|
42
|
+
|
43
|
+
# Return the junction flow rates and the lambda
|
44
|
+
return qnew[:n], qnew[-1]
|
45
|
+
|
46
|
+
def junction_wse_head_lagrangemult(q:np.ndarray,
|
47
|
+
a:np.ndarray,
|
48
|
+
h_width_froma,
|
49
|
+
z:np.ndarray,
|
50
|
+
epsilon:float=1e-6,
|
51
|
+
energy:bool=False):
|
52
|
+
""" Compute the new area at the junction a_junction
|
53
|
+
from the flow rates q of the branches and the area a.
|
54
|
+
|
55
|
+
We ensure a same water elevation (z+h) at the junction in each branch
|
56
|
+
or a same head if energy is True.
|
57
|
+
|
58
|
+
:param q: np.ndarray, estimated signed flow rates of the branches
|
59
|
+
:param a: np.ndarray, estimated area of the branches
|
60
|
+
:param h_l_froma: function converting area to water height and width
|
61
|
+
:param z: np.ndarray, elevation of the branches
|
62
|
+
:param epsilon: float, tolerance of the method
|
63
|
+
:param energy: bool, if True, ensure the same energy at the junction
|
64
|
+
"""
|
65
|
+
|
66
|
+
n = q.size
|
67
|
+
|
68
|
+
# Compute the area ensuring the same water elevation at the junction
|
69
|
+
# using the Lagrange multiplier method
|
70
|
+
#
|
71
|
+
# We minimize the following function:
|
72
|
+
# sum_{i=1}^{n} 1/2 * (anew_i - a_i) ** 2
|
73
|
+
# subject to the constraint:
|
74
|
+
# (Z_i + h_i) - (Z_i+1 + h_i+1) = 0
|
75
|
+
# or
|
76
|
+
# (Z_i + h_i + 1/2 * (q_i / a_i) ^2 / g) - (Z_i+1 + h_i+1 + 1/2 * (q_i+1 / a_i+1) ^2 / g) = 0
|
77
|
+
#
|
78
|
+
# The problem is generally non-linear, we use the Newton-Raphson method to solve it.
|
79
|
+
#
|
80
|
+
# The Jacobian of the system is:
|
81
|
+
# dH_i / da_i = 1 / width_i
|
82
|
+
# dH_i / da_i+1 = -1 / width_i+1
|
83
|
+
# dZ_i / da_i = 0
|
84
|
+
# dZ_i / da_i+1 = 0
|
85
|
+
# if energy is True:
|
86
|
+
# d1/2 * (q_i / a_i) ^2 / g) / da_i = -q_i^2 / (a_i^3 * g)
|
87
|
+
# d1/2 * (q_i+1 / a_i+1) ^2 / g) / da_i+1 = q_i+1^2 / (a_i+1^3 * g)
|
88
|
+
#
|
89
|
+
# The system is solved iteratively using the Newton-Raphson method.
|
90
|
+
|
91
|
+
|
92
|
+
KITERMAX=100
|
93
|
+
GRAVITY = 9.81
|
94
|
+
|
95
|
+
ncompl = 2*n-1
|
96
|
+
|
97
|
+
# !Mise en correspondance deux par deux des altitudes de surface ou des énérgies
|
98
|
+
kiter = 0
|
99
|
+
error = 1.
|
100
|
+
|
101
|
+
while error > epsilon and kiter<KITERMAX:
|
102
|
+
|
103
|
+
A = np.zeros((ncompl, ncompl))
|
104
|
+
A[:n, :n] = np.eye(n)
|
105
|
+
b = np.zeros(ncompl)
|
106
|
+
|
107
|
+
kiter=kiter+1
|
108
|
+
if kiter>100:
|
109
|
+
print('Max iteration in junction')
|
110
|
+
|
111
|
+
h, width = h_width_froma(a)
|
112
|
+
|
113
|
+
# imposition des contraintes
|
114
|
+
if energy:
|
115
|
+
for i in range(n-1):
|
116
|
+
ipos = n + i
|
117
|
+
jpos = i + 1
|
118
|
+
|
119
|
+
# Termes suppl si égalisation d'énergie
|
120
|
+
v1_carre = (q[i] / a[i])**2. / GRAVITY
|
121
|
+
v2_carre = (q[jpos] / a[jpos])**2. / GRAVITY
|
122
|
+
|
123
|
+
A[ipos,i] = 1./width[i] - v1_carre / a[i]
|
124
|
+
A[ipos,jpos] = -1./width[jpos] + v2_carre / a[jpos]
|
125
|
+
A[i,ipos] = -A[ipos,i]
|
126
|
+
A[jpos,ipos] = -A[ipos,jpos]
|
127
|
+
|
128
|
+
b[ipos] = (v2_carre/2.0 + z[jpos] + h[jpos]) - (v1_carre/2.0 + z[i] + h[i])
|
129
|
+
|
130
|
+
else:
|
131
|
+
for i in range(n-1):
|
132
|
+
ipos = n + i
|
133
|
+
jpos = i + 1
|
134
|
+
|
135
|
+
# !dérivée de H vis-à-vis de la section --> largeur courante
|
136
|
+
A[ipos,i] = 1.0/width[i]
|
137
|
+
A[ipos,jpos] = -1.0/width[jpos]
|
138
|
+
A[i,ipos] = -A[ipos,i]
|
139
|
+
A[jpos,ipos] = -A[ipos,jpos]
|
140
|
+
|
141
|
+
# membre de droite --> delta d'altitude de fond
|
142
|
+
b[ipos] = (z[jpos]+h[jpos]) - (z[i]+h[i])
|
143
|
+
|
144
|
+
# résolution du système
|
145
|
+
incr = np.linalg.solve(A, b)
|
146
|
+
|
147
|
+
# mise à jour de l'inconnue de section sur base des incréments
|
148
|
+
a = a + incr[:n]
|
149
|
+
|
150
|
+
# mise à jour des propriétés indirectes
|
151
|
+
h, width = h_width_froma(a)
|
152
|
+
|
153
|
+
# calcul de l'erreur résiduelle
|
154
|
+
error = np.sum(np.abs(incr[:n]))
|
155
|
+
|
156
|
+
return a
|
157
|
+
|
158
|
+
if __name__ == "__main__":
|
159
|
+
# Test the function
|
160
|
+
q = np.array([-1, 2, 3, -4])
|
161
|
+
q_junction, lambda_ = junction_q_lagrangemult(q)
|
162
|
+
print("q_junction:", q_junction)
|
163
|
+
print("lambda:", lambda_)
|
164
|
+
|
165
|
+
# Test the function
|
166
|
+
q = np.array([-1.1, 2.2, 3.001, -3.99])
|
167
|
+
q_junction, lambda_ = junction_q_lagrangemult(q)
|
168
|
+
print("q_junction:", q_junction)
|
169
|
+
print("lambda:", lambda_)
|
170
|
+
|
171
|
+
def h_width_froma(a:np.ndarray):
|
172
|
+
L = 5.
|
173
|
+
h = a / L
|
174
|
+
dl = np.ones(a.shape) * L
|
175
|
+
return h, dl
|
176
|
+
|
177
|
+
a = np.array([1, 2, 3, 4])
|
178
|
+
z = np.array([0, 0, 0, 0])
|
179
|
+
|
180
|
+
a_junction = junction_wse_head_lagrangemult(q, a, h_width_froma, z, energy=False)
|
181
|
+
print("a_junction:", a_junction + z)
|
182
|
+
|
183
|
+
a = a_junction
|
184
|
+
a_junction = junction_wse_head_lagrangemult(q, a, h_width_froma, z, energy=True)
|
185
|
+
print("a_junction:", a_junction)
|
186
|
+
|
187
|
+
L = 5.
|
188
|
+
z = np.array([0, -1, .5, -.5])
|
189
|
+
a = (1. - z) * L
|
190
|
+
|
191
|
+
a_junction = junction_wse_head_lagrangemult(q, a, h_width_froma, z, energy=False)
|
192
|
+
h, width = h_width_froma(a_junction)
|
193
|
+
print("wse:", h + z)
|
194
|
+
|
195
|
+
z = np.array([0, -1, .5, -.5])
|
196
|
+
a = (1. - z)*5.
|
197
|
+
a[0] += 0.1
|
198
|
+
a[1] += 0.1
|
199
|
+
a[2] -= 0.1
|
200
|
+
a[3] -= 0.1
|
201
|
+
|
202
|
+
a_junction = junction_wse_head_lagrangemult(q, a, h_width_froma, z, energy=False)
|
203
|
+
h, width = h_width_froma(a_junction)
|
204
|
+
print("wse:", h + z)
|
205
|
+
pass
|
wolfhece/lazviewer/laz_viewer.py
CHANGED
@@ -571,6 +571,9 @@ class xyz_laz_grids():
|
|
571
571
|
return ret
|
572
572
|
|
573
573
|
def find_files_in_bounds(self, bounds:Union[tuple[tuple[float,float],tuple[float,float]], list[list[float, float],list[float, float]]]):
|
574
|
+
""" Find all files in bounds
|
575
|
+
|
576
|
+
:param bounds: [[xmin,xmax], [ymin,ymax]]"""
|
574
577
|
|
575
578
|
ret = [(cur.mydir, cur.find_files_in_bounds(bounds)) for cur in self.grids]
|
576
579
|
ret = [cur for cur in ret if len(cur[1])>0]
|
@@ -583,6 +586,11 @@ class xyz_laz_grids():
|
|
583
586
|
return ret
|
584
587
|
|
585
588
|
def copy_files_in_bounds(self, bounds:Union[tuple[tuple[float,float],tuple[float,float]], list[list[float, float],list[float, float]]], dirout:str):
|
589
|
+
""" Copy files in bounds to directory
|
590
|
+
|
591
|
+
:param bounds: [[xmin,xmax], [ymin,ymax]]
|
592
|
+
:param dirout: output directory
|
593
|
+
"""
|
586
594
|
|
587
595
|
import shutil
|
588
596
|
|
@@ -601,11 +609,28 @@ class xyz_laz_grids():
|
|
601
609
|
shutil.copy(join(curdir,'gridinfo.txt'), locdir / 'gridinfo.txt')
|
602
610
|
|
603
611
|
def read_dir(self, dir_grids):
|
612
|
+
""" Read all grids in directory and subdirectories """
|
613
|
+
|
614
|
+
import asyncio
|
615
|
+
|
604
616
|
dirs = listdir(dir_grids)
|
605
617
|
|
606
|
-
|
607
|
-
|
608
|
-
|
618
|
+
logging.info(_('Reading grids information -- {} grids'.format(len(dirs))))
|
619
|
+
|
620
|
+
def init_grid(curdir):
|
621
|
+
if (Path(dir_grids) / Path(curdir)).is_dir():
|
622
|
+
return xyz_laz_grid(join(dir_grids, curdir))
|
623
|
+
|
624
|
+
return None
|
625
|
+
|
626
|
+
async def init_grid_async(curdir):
|
627
|
+
return init_grid(curdir)
|
628
|
+
|
629
|
+
loop = asyncio.get_event_loop()
|
630
|
+
tasks = [init_grid_async(curdir) for curdir in dirs]
|
631
|
+
self.grids = list(filter(None, loop.run_until_complete(asyncio.gather(*tasks))))
|
632
|
+
|
633
|
+
logging.info(_('Grids initialized'))
|
609
634
|
|
610
635
|
def scan_around(self, xy:Union[LineString,list[list[float], list[float]]], length_buffer=5.):
|
611
636
|
"""
|
wolfhece/pybridges.py
CHANGED
@@ -1009,11 +1009,11 @@ class Bridges(Element_To_Draw):
|
|
1009
1009
|
|
1010
1010
|
return self.active_elt
|
1011
1011
|
|
1012
|
-
def find_inside_poly(self,
|
1012
|
+
def find_inside_poly(self, vec:vector)->list:
|
1013
1013
|
"""
|
1014
1014
|
Find bridges inside polygon/vector
|
1015
1015
|
"""
|
1016
|
-
polyls =
|
1016
|
+
polyls = vec.polygon
|
1017
1017
|
bridges = list(self.myelts.values())
|
1018
1018
|
|
1019
1019
|
mysel=[]
|
wolfhece/pypolygons_scen.py
CHANGED
@@ -408,7 +408,7 @@ class Polygons_Analyze(Zones):
|
|
408
408
|
self.polygons_meanxy = {}
|
409
409
|
for curvert in self.polygons_zone.myvectors:
|
410
410
|
# Centre du polygone
|
411
|
-
centroid = curvert.
|
411
|
+
centroid = curvert.centroid
|
412
412
|
self.polygons_meanxy[curvert.myname] = (centroid.x, centroid.y)
|
413
413
|
|
414
414
|
for vec in self.polygons_zone.myvectors:
|
@@ -536,7 +536,7 @@ class Polygons_Analyze(Zones):
|
|
536
536
|
|
537
537
|
for curvert in self.polygons_zone.myvectors:
|
538
538
|
# Centre du polygone
|
539
|
-
centroid = curvert.
|
539
|
+
centroid = curvert.centroid
|
540
540
|
self.polygons_curvi[curvert.myname] = poly.project(Point([centroid.x, centroid.y]))
|
541
541
|
|
542
542
|
def find_values_inside_parts(self, linked_arrays:Union[dict,list]):
|
@@ -472,13 +472,13 @@ class Config_Manager_2D_GPU:
|
|
472
472
|
|
473
473
|
logging.info(_('Number of tif files : {}'.format(len(list_tif))))
|
474
474
|
|
475
|
-
standard_files = ['bathymetry.tif',
|
476
|
-
'manning.tif',
|
477
|
-
'infiltration.tif',
|
478
|
-
'h.tif',
|
479
|
-
'qx.tif',
|
480
|
-
'qy.tif',
|
481
|
-
'roof.tif',
|
475
|
+
standard_files = ['bathymetry.tif',
|
476
|
+
'manning.tif',
|
477
|
+
'infiltration.tif',
|
478
|
+
'h.tif',
|
479
|
+
'qx.tif',
|
480
|
+
'qy.tif',
|
481
|
+
'roof.tif',
|
482
482
|
'deck.tif']
|
483
483
|
|
484
484
|
log = ''
|
@@ -1217,7 +1217,7 @@ class Config_Manager_2D_GPU:
|
|
1217
1217
|
logging.error(_("No 'bathymetry.tif' file found in the root directory !"))
|
1218
1218
|
|
1219
1219
|
|
1220
|
-
def combine_bath_roof_deck(self, bathymetry:WolfArray,
|
1220
|
+
def combine_bath_roof_deck(self, bathymetry:WolfArray,
|
1221
1221
|
bridge_roof:WolfArray, bridge_deck:WolfArray,
|
1222
1222
|
threshold:float = .05) -> str:
|
1223
1223
|
""" Verify bathymetry, roof and deck """
|
@@ -1231,7 +1231,7 @@ class Config_Manager_2D_GPU:
|
|
1231
1231
|
if ret != '':
|
1232
1232
|
logging.error(ret)
|
1233
1233
|
return ret
|
1234
|
-
|
1234
|
+
|
1235
1235
|
# si la matrice de toit de pont est plus basse que la bathymétrie, on met à 99999
|
1236
1236
|
# la bathymétrie et le toit de pont.
|
1237
1237
|
# Ainsi, ces maille seront infranchissables.
|
@@ -1257,7 +1257,7 @@ class Config_Manager_2D_GPU:
|
|
1257
1257
|
ret += _(' -- Bathymetry values will be set to max(bath, deck)\n')
|
1258
1258
|
ret += _(' -- These cells will remain impassable until the water level rises above them\n')
|
1259
1259
|
bridge_roof.array.data[mask] = 99999.
|
1260
|
-
bathymetry.array.data[mask] = np.maximum(bridge_deck.array.data[mask], bathymetry.array.data[mask])
|
1260
|
+
bathymetry.array.data[mask] = np.maximum(bridge_deck.array.data[mask], bathymetry.array.data[mask])
|
1261
1261
|
|
1262
1262
|
mask = np.where(bridge_roof.array.data - bathymetry.array.data < threshold)
|
1263
1263
|
if mask[0].shape[0] > 0:
|
@@ -1273,7 +1273,7 @@ class Config_Manager_2D_GPU:
|
|
1273
1273
|
logging.warning(ret)
|
1274
1274
|
|
1275
1275
|
return ret
|
1276
|
-
|
1276
|
+
|
1277
1277
|
def create_simulation(self,
|
1278
1278
|
dir:Path,
|
1279
1279
|
idx_hydros:list[int] = [-1],
|
@@ -1576,7 +1576,7 @@ class Config_Manager_2D_GPU:
|
|
1576
1576
|
for cursim in allsims:
|
1577
1577
|
cursim:Path
|
1578
1578
|
batch += 'cd {}\n'.format(str(cursim.parent))
|
1579
|
-
batch += str(cursim.name) + '\n'
|
1579
|
+
batch += 'call ' + str(cursim.name) + '\n'
|
1580
1580
|
|
1581
1581
|
with open(path, 'w', encoding='utf-8') as f:
|
1582
1582
|
f.write(batch)
|