wolfhece 2.2.16__py3-none-any.whl → 2.2.18__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,335 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ from shapely.geometry import Point, LineString
5
+ from typing import Literal
6
+ import pandas as pd
7
+
8
+ from .PyTranslate import _
9
+ from .drawing_obj import Element_To_Draw
10
+ from .PyVertexvectors import Triangulation, vector,Zones, zone
11
+ from .wolf_array import WolfArray, header_wolf
12
+
13
+ class Array_analysis_onepolygon():
14
+ """ Class for values analysis of an array based on a polygon.
15
+
16
+ This class select values insides a polygon and plot statistics of the values.
17
+
18
+ The class is designed to be used with the WolfArray class and the vector class from the PyVertexvectors module.
19
+
20
+ Plots of the values distribution can be generated using seaborn or plotly.
21
+ """
22
+
23
+ def __init__(self, wa:WolfArray, polygon:vector):
24
+
25
+ self._wa = wa
26
+ self._polygon = polygon
27
+
28
+ self._selected_cells = None
29
+ self._values = None
30
+
31
+ def values(self, which:Literal['Mean', 'Std', 'Median', 'Sum', 'Volume', 'Values']) -> pd.DataFrame | float:
32
+ """ Get the values as a pandas DataFrame
33
+
34
+ :param which: Mean, Std, Median, Sum, Volume, Values
35
+ """
36
+
37
+ authrorized = ['Mean', 'Std', 'Median', 'Sum', 'Volume', 'Values']
38
+ if which not in authrorized:
39
+ raise ValueError(f"Invalid value for 'which'. Must be one of {authrorized}.")
40
+
41
+ if self._values is None:
42
+ self.compute_values()
43
+
44
+ if self._values is None:
45
+ raise ValueError("No values computed. Please call compute_values() first.")
46
+
47
+ if which == 'Values':
48
+ return pd.DataFrame(self._values[_(which)], columns=[which])
49
+ else:
50
+ return self._values[which]
51
+
52
+ def select_cells(self, mode:Literal['polygon', 'buffer'] = 'polygon', **kwargs):
53
+ """ Select the cells inside the polygon """
54
+
55
+ if mode == 'polygon':
56
+ if 'polygon' in kwargs:
57
+ self._polygon = kwargs['polygon']
58
+ self._select_cells_polygon(self._polygon)
59
+ else:
60
+ raise ValueError("No polygon provided. Please provide a polygon to select cells.")
61
+ elif mode == 'buffer':
62
+ if 'buffer' in kwargs:
63
+ self._select_cells_buffer(kwargs['buffer'])
64
+ else:
65
+ raise ValueError("No buffer size provided. Please provide a buffer size to select cells.")
66
+ else:
67
+ raise ValueError("Invalid mode. Please use 'polygon' or 'buffer'.")
68
+
69
+ def _select_cells_polygon(self, selection_poly:vector):
70
+ """ Select the cells inside the polygon """
71
+
72
+ self._polygon = selection_poly
73
+ self._selected_cells = self._wa.get_xy_inside_polygon(self._polygon)
74
+
75
+ def _select_cells_buffer(self, buffer_size:float = 0.0):
76
+ """ Select the cells inside the buffer of the polygon """
77
+
78
+ self._polygon = self._polygon.buffer(buffer_size, inplace=False)
79
+ self._selected_cells = self._wa.get_xy_inside_polygon(self._polygon)
80
+
81
+ def compute_values(self):
82
+ """ Get the values of the array inside the polygon """
83
+
84
+ if self._selected_cells is None:
85
+ if self._polygon is None:
86
+ raise ValueError("No polygon provided. Please provide a polygon to select cells.")
87
+
88
+ self._values = self._wa.statistics(self._polygon)
89
+
90
+ def plot_values(self, show:bool = True, bins:int = 100,
91
+ engine:Literal['seaborn', 'plotly'] = 'seaborn'):
92
+ """ Plot a histogram of the values """
93
+
94
+ if engine == 'seaborn':
95
+ return self.plot_values_seaborn(show=show, bins=bins)
96
+ elif engine == 'plotly':
97
+ return self.plot_values_plotly(show=show, bins=bins)
98
+
99
+ def plot_values_seaborn(self, bins:int = 100, show:bool = True):
100
+ """ Plot a histogram of the values """
101
+
102
+ import seaborn as sns
103
+ import matplotlib.pyplot as plt
104
+
105
+ fig, ax = plt.subplots()
106
+ sns.histplot(self.values('Values'), bins=bins,
107
+ kde=True, ax=ax,
108
+ stat="density")
109
+
110
+ # Add mean, std, median values on plot
111
+ mean = self.values('Mean')
112
+ # std = self.values('Std').values[0]
113
+ median = self.values('Median')
114
+
115
+ # test noen and masked value
116
+ if mean is not None and mean is not np.ma.masked:
117
+ ax.axvline(mean, color='r', linestyle='--', label=f'Mean: {mean:.2f}')
118
+ if median is not None and median is not np.ma.masked:
119
+ ax.axvline(median, color='b', linestyle='--', label=f'Median: {median:.2f}')
120
+
121
+ ax.legend()
122
+ ax.set_xlabel('Values')
123
+ ax.set_ylabel('Frequency')
124
+ ax.set_title('Values distribution')
125
+
126
+ if show:
127
+ plt.show()
128
+
129
+ return (fig, ax)
130
+
131
+ def plot_values_plotly(self, bins:int = 100, show:bool = True):
132
+ """ Plot a histogram of the values """
133
+
134
+ import plotly.express as px
135
+
136
+ fig = px.histogram(self.values('Values'), x='Values',
137
+ nbins=bins, title='Values distribution',
138
+ histnorm='probability density')
139
+
140
+ # Add mean, std, median values on plot
141
+ mean = self.values('Mean')
142
+ median = self.values('Median')
143
+
144
+ if mean is not None and mean is not np.ma.masked:
145
+ fig.add_vline(x=mean, line_color='red', line_dash='dash', annotation_text=f'Mean: {mean:.2f}')
146
+ if median is not None and median is not np.ma.masked:
147
+ fig.add_vline(x=median, line_color='blue', line_dash='dash', annotation_text=f'Median: {median:.2f}')
148
+
149
+ fig.update_layout(xaxis_title='Values', yaxis_title='Frequency')
150
+
151
+ if show:
152
+ fig.show(renderer='browser')
153
+
154
+ return fig
155
+
156
+
157
+ class Array_analysis_polygons():
158
+ """ Class for values analysis of an array based on a polygon.
159
+
160
+ This class select values insides a polygon and plot statistics of the values.
161
+
162
+ The class is designed to be used with the WolfArray class and the vector class from the PyVertexvectors module.
163
+
164
+ Plots of the values distribution can be generated using seaborn or plotly.
165
+ """
166
+
167
+ def __init__(self, wa:WolfArray, polygons:zone):
168
+ """ Initialize the class with a WolfArray and a zone of polygons """
169
+
170
+ self._wa = wa
171
+ self._polygons = polygons
172
+
173
+ self._zone = {polygon.myname: Array_analysis_onepolygon(self._wa, polygon) for polygon in self._polygons.myvectors}
174
+
175
+ def __getitem__(self, key):
176
+ """ Get the polygon by name """
177
+ if key in self._zone:
178
+ return self._zone[key]
179
+ else:
180
+ raise KeyError(f"Polygon {key} not found in zone.")
181
+
182
+ def plot_values(self, show:bool = True, bins:int = 100,
183
+ engine:Literal['seaborn', 'plotly'] = 'seaborn'):
184
+ """ Plot a histogram of the values """
185
+
186
+ if engine == 'seaborn':
187
+ return self.plot_values_seaborn(show=show, bins=bins)
188
+ elif engine == 'plotly':
189
+ return self.plot_values_plotly(show=show, bins=bins)
190
+
191
+ def plot_values_seaborn(self, bins:int = 100, show:bool = True):
192
+ """ Plot a histogram of the values """
193
+ return {key: pol.plot_values_seaborn(bins=bins, show=show) for key, pol in self._zone.items()}
194
+
195
+ def plot_values_plotly(self, bins:int = 100, show:bool = True):
196
+ """ Plot a histogram of the values """
197
+
198
+ return {key: pol.plot_values_plotly(bins=bins, show=show) for key, pol in self._zone.items()}
199
+
200
+ class Slope_analysis:
201
+ """ Class for slope analysis of in an array based on a trace vector.
202
+
203
+ This class allows to select cells inside a polygon or a buffer around a trace vector
204
+ and compute the slope of the dike. The slope is computed as the difference in elevation
205
+ between the trace and the cell divided by the distance to the trace.
206
+
207
+ The slope is computed for each cell inside the polygon or buffer and accessed in a Pandas Dataframe.
208
+
209
+ Plots of the slope distribution can be generated using seaborn or plotly.
210
+
211
+ The class is designed to be used with the WolfArray class and the vector class from the PyVertexvectors module.
212
+ """
213
+
214
+ def __init__(self, wa:WolfArray, trace:vector):
215
+
216
+ self._wa = wa
217
+ self._trace = trace
218
+
219
+ self._selection_poly = None
220
+ self._buffer_size = 0.0
221
+
222
+ self._selected_cells = None
223
+ self._slopes = None
224
+
225
+ @property
226
+ def slopes(self) -> pd.DataFrame:
227
+ """ Get the slopes as a pandas DataFrame """
228
+
229
+ if self._slopes is None:
230
+ self.compute_slopes()
231
+
232
+ if self._slopes is None:
233
+ raise ValueError("No slopes computed. Please call compute_slopes() first.")
234
+
235
+ return pd.DataFrame(self._slopes, columns=['Slope [m/m]'])
236
+
237
+ def select_cells(self, mode:Literal['polygon', 'buffer'] = 'polygon', **kwargs):
238
+ """ Select the cells inside the trace """
239
+
240
+ if mode == 'polygon':
241
+ if 'polygon' in kwargs:
242
+ self._selection_poly = kwargs['polygon']
243
+ self._select_cells_polygon(self._selection_poly)
244
+ else:
245
+ raise ValueError("No polygon provided. Please provide a polygon to select cells.")
246
+ elif mode == 'buffer':
247
+ if 'buffer' in kwargs:
248
+ self._buffer_size = kwargs['buffer']
249
+ self._select_cells_buffer(self._buffer_size)
250
+ else:
251
+ raise ValueError("No buffer size provided. Please provide a buffer size to select cells.")
252
+ else:
253
+ raise ValueError("Invalid mode. Please use 'polygon' or 'buffer'.")
254
+
255
+ def _select_cells_buffer(self, buffer_size:float = 0.0):
256
+ """ Select the cells inside the buffer of the trace """
257
+
258
+ self._buffer_size = buffer_size
259
+ self._selection_poly = self._trace.buffer(self._buffer_size, inplace=False)
260
+ self._select_cells_polygon(self._selection_poly)
261
+
262
+ def _select_cells_polygon(self, selection_poly:vector):
263
+ """ Select the cells inside the polygon """
264
+
265
+ self._selection_poly = selection_poly
266
+ self._selected_cells = self._wa.get_xy_inside_polygon(self._selection_poly)
267
+
268
+ def compute_slopes(self):
269
+ """ Get the slope of the dike """
270
+
271
+ if self._selected_cells is None:
272
+ self.select_cells()
273
+ if self._selected_cells is None:
274
+ raise ValueError("No cells selected. Please call select_cells() first.")
275
+
276
+ trace_ls = self._trace.linestring
277
+
278
+ def compute_cell_slope(curxy):
279
+ i, j = self._wa.get_ij_from_xy(curxy[0], curxy[1])
280
+ pt = Point(curxy[0], curxy[1])
281
+ distance_to_trace = trace_ls.distance(pt)
282
+ elevation_on_trace = trace_ls.interpolate(trace_ls.project(pt, normalized=True), normalized=True).z
283
+ if distance_to_trace == 0.0:
284
+ return 0.0
285
+ if elevation_on_trace == -99999.0:
286
+ return 0.0
287
+
288
+ return (elevation_on_trace - self._wa.array[i, j]) / distance_to_trace
289
+
290
+ self._slopes = [compute_cell_slope(curxy) for curxy in self._selected_cells]
291
+
292
+ def plot_slopes(self, show:bool = True, bins:int = 100,
293
+ engine:Literal['seaborn', 'plotly'] = 'seaborn'):
294
+ """ Plot a histogram of the slopes """
295
+
296
+ if engine == 'seaborn':
297
+ return self.plot_slopes_seaborn(show=show, bins=bins)
298
+ elif engine == 'plotly':
299
+ return self.plot_slopes_plotly(show=show, bins=bins)
300
+
301
+ def plot_slopes_seaborn(self, bins:int = 100, show:bool = True):
302
+ """ Plot a histogram of the slopes """
303
+
304
+ import seaborn as sns
305
+ import matplotlib.pyplot as plt
306
+
307
+ fig, ax = plt.subplots()
308
+ sns.histplot(self.slopes, bins=bins,
309
+ kde=True, ax=ax,
310
+ stat="density")
311
+
312
+ ax.set_xlabel('Slope [m/m]')
313
+ ax.set_ylabel('Frequency')
314
+ ax.set_title('Slope distribution')
315
+
316
+ if show:
317
+ plt.show()
318
+
319
+ return (fig, ax)
320
+
321
+ def plot_slopes_plotly(self, bins:int = 100, show:bool = True):
322
+ """ Plot a histogram of the slopes """
323
+
324
+ import plotly.express as px
325
+
326
+ fig = px.histogram(self.slopes, x='Slope [m/m]',
327
+ nbins=bins, title='Slope distribution',
328
+ histnorm='probability density')
329
+
330
+ fig.update_layout(xaxis_title='Slope [m/m]', yaxis_title='Frequency')
331
+
332
+ if show:
333
+ fig.show(renderer='browser')
334
+
335
+ return fig
wolfhece/apps/version.py CHANGED
@@ -5,7 +5,7 @@ class WolfVersion():
5
5
 
6
6
  self.major = 2
7
7
  self.minor = 2
8
- self.patch = 16
8
+ self.patch = 18
9
9
 
10
10
  def __str__(self):
11
11
 
wolfhece/assets/mesh.py CHANGED
@@ -16,7 +16,7 @@ class Mesh2D(header_wolf):
16
16
  self.shape = src_header.shape
17
17
  self._factor = None
18
18
 
19
- def plot_cells(self, ax:Axes=None, color='black', **kwargs):
19
+ def plot_cells(self, ax:Axes=None, transpose:bool= False, color='black', **kwargs):
20
20
  """ Plot the grid of the mesh.
21
21
  """
22
22
 
@@ -27,13 +27,28 @@ class Mesh2D(header_wolf):
27
27
 
28
28
  [xmin, xmax], [ymin, ymax] = self.get_bounds()
29
29
 
30
- for y in np.linspace(ymin, ymax, endpoint=True, num=self.nby + 1):
31
- ax.plot([xmin, xmax], [y, y], color=color, **kwargs)
30
+ if transpose:
31
+
32
+ # plot the grid of the mesh in a transposed way
33
+ for x in np.linspace(xmin, xmax, endpoint=True, num=self.nbx + 1):
34
+ ax.plot([ymin, ymax], [x, x], color=color, **kwargs)
32
35
 
33
- for x in np.linspace(xmin, xmax, endpoint=True, num=self.nbx + 1):
34
- ax.plot([x, x], [ymin, ymax], color=color, **kwargs)
36
+ for y in np.linspace(ymin, ymax, endpoint=True, num=self.nby + 1):
37
+ ax.plot([y, y], [xmin, xmax], color=color, **kwargs)
38
+
39
+ self.set_aspect_labels_matrice(ax=ax, **kwargs)
40
+
41
+ else:
42
+
43
+ # plot the grid of the mesh
44
+ for y in np.linspace(ymin, ymax, endpoint=True, num=self.nby + 1):
45
+ ax.plot([xmin, xmax], [y, y], color=color, **kwargs)
46
+
47
+ for x in np.linspace(xmin, xmax, endpoint=True, num=self.nbx + 1):
48
+ ax.plot([x, x], [ymin, ymax], color=color, **kwargs)
49
+
50
+ self.set_aspect_labels(ax=ax, **kwargs)
35
51
 
36
- self.set_aspect_labels(ax=ax, **kwargs)
37
52
  return fig, ax
38
53
 
39
54
  def plot_center_cells(self, ax:Axes=None, color='black', linestyle='--', **kwargs):
@@ -77,6 +92,34 @@ class Mesh2D(header_wolf):
77
92
 
78
93
  return fig, ax
79
94
 
95
+ def set_ticks_as_matrice(self, ax:Axes=None, Fortran_type:bool = True, **kwargs):
96
+ """ Set the ticks of the axis as the row and column of a matrice """
97
+
98
+ if ax is None:
99
+ fig, ax = plt.subplots()
100
+ else:
101
+ fig = ax.figure
102
+
103
+ [xmin, xmax], [ymin, ymax] = self.get_bounds()
104
+
105
+ if Fortran_type:
106
+ x_ticks = [f'{i}' for i in range(1,self.nbx+1)]
107
+ y_ticks = [f'{i}' for i in range(1,self.nby+1)]
108
+ else:
109
+ x_ticks = [f'{i}' for i in range(self.nbx)]
110
+ y_ticks = [f'{i}' for i in range(self.nby)]
111
+
112
+ ax.set_yticks(np.linspace(xmin+self.dx/2., xmax-self.dx/2., endpoint=True, num=self.nbx))
113
+ ax.set_xticks(np.linspace(ymin+self.dy/2., ymax-self.dy/2., endpoint=True, num=self.nby))
114
+
115
+ x_ticks.reverse()
116
+ ax.set_yticklabels(x_ticks)
117
+ ax.set_xticklabels(y_ticks)
118
+
119
+ self.set_aspect_labels_matrice(ax=ax, **kwargs)
120
+
121
+ return fig, ax
122
+
80
123
  def plot_circle_at_centers(self, ax:Axes=None, color='black', radius:float=None, **kwargs):
81
124
  """ Plot circles at the center of the cells.
82
125
  """
@@ -124,6 +167,64 @@ class Mesh2D(header_wolf):
124
167
  self.set_aspect_labels(ax=ax, **kwargs)
125
168
 
126
169
  return fig, ax
170
+
171
+ def plot_memoryposition_at_centers(self, ax:Axes=None,
172
+ transpose=False,
173
+ Fortran_type:bool = True,
174
+ f_contiguous:bool = True,
175
+ **kwargs):
176
+ """ Plot the position of the cells at the center of the cells.
177
+ """
178
+
179
+ if ax is None:
180
+ fig, ax = plt.subplots()
181
+ else:
182
+ fig = ax.figure
183
+
184
+ [xmin, xmax], [ymin, ymax] = self.get_bounds()
185
+
186
+ if transpose:
187
+ k = 0
188
+ if Fortran_type:
189
+ k+=1
190
+
191
+ all_y = list(np.linspace(xmin + self.dx/2., xmax - self.dx/2., endpoint=True, num=self.nbx))
192
+ all_x = list(np.linspace(ymin + self.dy/2., ymax - self.dy/2., endpoint=True, num=self.nby))
193
+
194
+ all_y.reverse()
195
+
196
+ if f_contiguous:
197
+
198
+ for x in all_x:
199
+ for y in all_y:
200
+
201
+ ax.text(x, y, f'{k}', horizontalalignment='center', verticalalignment='center', **kwargs)
202
+ k+=1
203
+ else:
204
+
205
+ for y in all_y:
206
+ for x in all_x:
207
+
208
+ ax.text(x, y, f'{k}', horizontalalignment='center', verticalalignment='center', **kwargs)
209
+ k+=1
210
+
211
+ self.set_aspect_labels_matrice(ax=ax, **kwargs)
212
+
213
+ else:
214
+
215
+ k = 0
216
+ if Fortran_type:
217
+ k+=1
218
+
219
+ for y in np.linspace(ymin + self.dy/2., ymax - self.dy/2., endpoint=True, num=self.nby):
220
+ for x in np.linspace(xmin + self.dx/2., xmax - self.dx/2., endpoint=True, num=self.nbx):
221
+
222
+ ax.text(x, y, f'{k}', horizontalalignment='center', verticalalignment='center', **kwargs)
223
+ k+=1
224
+
225
+ self.set_aspect_labels(ax=ax, **kwargs)
226
+
227
+ return fig, ax
127
228
 
128
229
  def plot_indices_at_bordersX(self, ax:Axes=None, Fortran_type:bool = True, **kwargs):
129
230
  """ Plot the indices of the cells at the borders of the cells.
@@ -599,6 +700,27 @@ class Mesh2D(header_wolf):
599
700
  ax.set_xlabel('X (m)')
600
701
  ax.set_ylabel('Y (m)')
601
702
  return fig, ax
703
+
704
+ def set_aspect_labels_matrice(self, ax:Axes=None, **kwargs):
705
+ """ Set the aspect of the plot to be equal.
706
+ """
707
+ if ax is None:
708
+ fig, ax = plt.subplots()
709
+ else:
710
+ fig = ax.figure
711
+
712
+ [xmin, xmax], [ymin, ymax] = self.get_bounds()
713
+
714
+ ax.set_aspect('equal')
715
+ ax.set_ylim(xmin, xmax)
716
+ ax.set_xlim(ymin, ymax)
717
+ ax.set_xlabel('columns')
718
+ ax.set_ylabel('rows')
719
+
720
+ #set x ais on the upper side
721
+ ax.xaxis.set_ticks_position('top')
722
+ ax.xaxis.set_label_position('top')
723
+ return fig, ax
602
724
 
603
725
  def zeros(self):
604
726
  """ Return a 2D array of zeros with the shape of the mesh.
@@ -13,14 +13,13 @@ import logging
13
13
 
14
14
  from ..drawing_obj import Element_To_Draw
15
15
 
16
- # try:
17
- # # Trying to import the hydrometry_hece module from the hydrometry_hece package
18
- # # containing the KEY access to the SPW server
19
- # from ..hydrometry_hece.kiwis_hece import hydrometry_hece as hydrometry
20
- # except:
21
- # # If the hydrometry_hece module is not found, we import the hydrometry module from the hydrometry package
22
- # from .kiwis import hydrometry
23
- from .kiwis import hydrometry
16
+ try:
17
+ # Trying to import the hydrometry_hece module from the hydrometry_hece package
18
+ # containing the KEY access to the SPW server
19
+ from ..hydrometry_hece.kiwis_hece import hydrometry_hece as hydrometry
20
+ except:
21
+ # If the hydrometry_hece module is not found, we import the hydrometry module from the hydrometry package
22
+ from .kiwis import hydrometry
24
23
 
25
24
  from .kiwis_gui import hydrometry_gui
26
25
  from ..PyVertex import cloud_vertices, wolfvertex, Cloud_Styles, getIfromRGB, getRGBfromI
@@ -1,13 +1,22 @@
1
- from . import _add_path
2
- from .viewer.viewer import *
3
- from .points.points import *
4
- from .points.expr import *
5
- from scipy.spatial import kdtree
6
-
7
- try:
8
- from .processing.estimate_normals.estimate_normals import estimate_normals
9
- except Exception as e:
10
- print(e)
11
- print('Could not import estimate_normals')
12
- print('Please installed the VC++ redistributable from https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads')
1
+ import logging
2
+ # Check __onWindows__ variable from wolfhece/__init__.py
3
+ from ..os_check import isWindows
4
+
5
+ if not isWindows():
6
+ # If not on Windows, raise an exception
7
+ logging.info('Not on Windows, ignoring the lazviewer package')
8
+
9
+ else:
10
+ from . import _add_path
11
+ from .viewer.viewer import *
12
+ from .points.points import *
13
+ from .points.expr import *
14
+ from scipy.spatial import kdtree
15
+
16
+ try:
17
+ from .processing.estimate_normals.estimate_normals import estimate_normals
18
+ except Exception as e:
19
+ print(e)
20
+ print('Could not import estimate_normals')
21
+ print('Please installed the VC++ redistributable from https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads')
13
22
 
@@ -1050,7 +1050,7 @@ class BcManager(wx.Frame):
1050
1050
  """
1051
1051
  nb=0
1052
1052
  for valbc in bc.values():
1053
- if valbc != '99999.0':
1053
+ if float(valbc) != 99999.0:
1054
1054
  nb+=1
1055
1055
  return nb
1056
1056
 
wolfhece/mesh2d/gpu_2d.py CHANGED
@@ -101,9 +101,12 @@ class Sim_2D_GPU():
101
101
  ('qx','Initial discharge along X [m^2/s]',WOLF_ARRAY_FULL_SINGLE),
102
102
  ('qy','Initial discharge along Y [m^2/s]',WOLF_ARRAY_FULL_SINGLE),
103
103
  ('bridge_roof','Bridge/Culvert (roof el.) [m]',WOLF_ARRAY_FULL_SINGLE),
104
+ ('water_surface_elevation','Water surface elevation [m]',WOLF_ARRAY_FULL_SINGLE),
104
105
  ]}
105
106
 
106
- self.files_ic=['Initial water depth [m]','Initial discharge along X [m^2/s]','Initial discharge along Y [m^2/s]']
107
+ self.files_ic=['Initial water depth [m]',
108
+ 'Initial discharge along X [m^2/s]',
109
+ 'Initial discharge along Y [m^2/s]']
107
110
 
108
111
  # Files for the simulation
109
112
  self.files_others={'Generic file':[
@@ -166,7 +169,11 @@ class Sim_2D_GPU():
166
169
  else:
167
170
  return None
168
171
 
169
- def __getitem__(self, key:Literal['nap', 'bathymetry', 'manning', 'infiltration_zones', 'h', 'qx', 'qy', 'bridge_roof']) -> WolfArray:
172
+ def __getitem__(self, key:Literal['nap', 'bathymetry',
173
+ 'manning', 'infiltration_zones',
174
+ 'h', 'qx', 'qy',
175
+ 'bridge_roof',
176
+ 'water_surface_elevation']) -> WolfArray:
170
177
  """ Get an array from the simulation """
171
178
 
172
179
  if self.is_loaded:
@@ -174,12 +181,33 @@ class Sim_2D_GPU():
174
181
  descr = self._get_description_arrays()[self._get_name_arrays().index(key)]
175
182
 
176
183
  if key not in self._cached_arrays:
177
- locarray = WolfArray(srcheader=self.get_header(),
178
- np_source=self.sim.__getattribute__(key),
179
- idx= descr,
180
- nullvalue=self.nullvalues[key],
181
- whichtype=self.files_array['Characteristics'][self._get_name_arrays().index(key)][2],
182
- masknull=False)
184
+ if key in ['water_surface_elevation']:
185
+
186
+ top = self['bathymetry']
187
+ h = self['h']
188
+
189
+ locarray = top + h
190
+ locarray.idx = descr
191
+ locarray.nullvalue = self.nullvalues[key]
192
+
193
+ def wse_write_all():
194
+ top = self['bathymetry']
195
+ new_h = self['water_surface_elevation'] - top
196
+
197
+ # Filter negative values
198
+ new_h.array.data[new_h.array.data < 0.] = 0.
199
+
200
+ new_h.write_all(str(self.dir / "h.npy"))
201
+
202
+ locarray.write_all = wse_write_all
203
+
204
+ else:
205
+ locarray = WolfArray(srcheader=self.get_header(),
206
+ np_source=self.sim.__getattribute__(key),
207
+ idx= descr,
208
+ nullvalue=self.nullvalues[key],
209
+ whichtype=self.files_array['Characteristics'][self._get_name_arrays().index(key)][2],
210
+ masknull=False)
183
211
  locarray.loaded = True
184
212
  locarray.filename = str(self.dir / f"{key}.npy")
185
213
 
@@ -354,7 +382,15 @@ class Sim_2D_GPU():
354
382
  def nullvalues(self) -> dict[str,int]:
355
383
  """ Define null values for the arrays """
356
384
 
357
- return {'nap':0, 'bathymetry':99999., 'manning':0, 'infiltration_zones':0, 'h':0., 'qx':0., 'qy':0., 'bridge_roof':99999.}
385
+ return {'nap':0,
386
+ 'bathymetry':99999.,
387
+ 'manning':0,
388
+ 'infiltration_zones':0,
389
+ 'h':0.,
390
+ 'qx':0.,
391
+ 'qy':0.,
392
+ 'bridge_roof':99999.,
393
+ 'water_surface_elevation':0.}
358
394
 
359
395
  def verify_files(self):
360
396
  """ Verify the files """
@@ -649,4 +685,4 @@ class Sim_2D_GPU():
649
685
  self.sim._infiltration_zones[:,:]= tmp[:,:]
650
686
 
651
687
  tmp = np.load(self.dir / "NAP.npy")
652
- self.sim._nap[:,:]= tmp[:,:]
688
+ self.sim._nap[:,:]= tmp[:,:]