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.
@@ -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
@@ -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
- for curdir in dirs:
607
- if Path(curdir).is_dir():
608
- self.grids.append(xyz_laz_grid(join(dir_grids,curdir)))
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
  """
@@ -203,6 +203,7 @@ class Calculator(wx.Frame):
203
203
  while id in ids:
204
204
  id += '_'
205
205
 
206
+ res.set_nullvalue_in_mask()
206
207
  self._mapviewer.add_object('array', newobj=res, id = id)
207
208
  self.command = ''
208
209
 
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, poly:vector)->list:
1012
+ def find_inside_poly(self, vec:vector)->list:
1013
1013
  """
1014
1014
  Find bridges inside polygon/vector
1015
1015
  """
1016
- polyls = poly.asshapely_pol()
1016
+ polyls = vec.polygon
1017
1017
  bridges = list(self.myelts.values())
1018
1018
 
1019
1019
  mysel=[]
@@ -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.asshapely_pol().centroid
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.asshapely_pol().centroid
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)