tilupy 2.0.0__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.
- tilupy/__init__.py +23 -0
- tilupy/analytic_sol.py +2403 -0
- tilupy/benchmark.py +1563 -0
- tilupy/calibration.py +134 -0
- tilupy/cmd.py +177 -0
- tilupy/compare.py +195 -0
- tilupy/download_data.py +68 -0
- tilupy/initdata.py +207 -0
- tilupy/initsimus.py +50 -0
- tilupy/make_mass.py +111 -0
- tilupy/make_topo.py +468 -0
- tilupy/models/__init__.py +0 -0
- tilupy/models/lave2D/__init__.py +0 -0
- tilupy/models/lave2D/initsimus.py +665 -0
- tilupy/models/lave2D/read.py +264 -0
- tilupy/models/ravaflow/__init__.py +0 -0
- tilupy/models/ravaflow/initsimus.py +192 -0
- tilupy/models/ravaflow/read.py +273 -0
- tilupy/models/saval2D/__init__.py +0 -0
- tilupy/models/saval2D/read.py +298 -0
- tilupy/models/shaltop/__init__.py +0 -0
- tilupy/models/shaltop/initsimus.py +375 -0
- tilupy/models/shaltop/read.py +613 -0
- tilupy/notations.py +866 -0
- tilupy/plot.py +234 -0
- tilupy/raster.py +199 -0
- tilupy/read.py +2588 -0
- tilupy/utils.py +656 -0
- tilupy-2.0.0.dist-info/METADATA +876 -0
- tilupy-2.0.0.dist-info/RECORD +34 -0
- tilupy-2.0.0.dist-info/WHEEL +5 -0
- tilupy-2.0.0.dist-info/entry_points.txt +3 -0
- tilupy-2.0.0.dist-info/licenses/LICENSE +516 -0
- tilupy-2.0.0.dist-info/top_level.txt +1 -0
tilupy/read.py
ADDED
|
@@ -0,0 +1,2588 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
from typing import Callable
|
|
7
|
+
|
|
8
|
+
import matplotlib
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import importlib
|
|
14
|
+
import warnings
|
|
15
|
+
|
|
16
|
+
import pytopomap.plot as plt_fn
|
|
17
|
+
import tilupy.notations as notations
|
|
18
|
+
import tilupy.utils as utils
|
|
19
|
+
import tilupy.plot as plt_tlp
|
|
20
|
+
import tilupy.raster
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
ALLOWED_MODELS = ["shaltop",
|
|
24
|
+
"lave2D",
|
|
25
|
+
"saval2D"]
|
|
26
|
+
"""Allowed models for result reading."""
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
RAW_STATES = ["hvert", "h", "ux", "uy"]
|
|
30
|
+
"""Raw states at the output of a model.
|
|
31
|
+
|
|
32
|
+
Implemented states :
|
|
33
|
+
|
|
34
|
+
- hvert : Fluid thickness taken vertically
|
|
35
|
+
- h : Fluid thickness taken normal to topography
|
|
36
|
+
- ux : X-component of fluid velocity
|
|
37
|
+
- uy : Y-component of fluid velocity
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
TEMPORAL_DATA_0D = ["ek", "volume"]
|
|
41
|
+
"""Time-varying 0D data.
|
|
42
|
+
|
|
43
|
+
Implemented 0D temporal data :
|
|
44
|
+
|
|
45
|
+
- ek : kinetic energy
|
|
46
|
+
- volume : Fluid volume
|
|
47
|
+
|
|
48
|
+
Also combine all the assembly possibilities between :data:`TEMPORAL_DATA_2D` and :data:`NP_OPERATORS` (or :data:`OTHER_OPERATORS`), at each point xy following this format:
|
|
49
|
+
|
|
50
|
+
`[TEMPORAL_DATA_2D]_[NP/OTHER_OPERATORS]_xy`
|
|
51
|
+
|
|
52
|
+
For instance with h :
|
|
53
|
+
- h_max_xy: Maximum value of h over the entire surface for each time step.
|
|
54
|
+
- h_min_xy: Minimal value of h over the entire surface for each time step.
|
|
55
|
+
- h_mean_xy: Mean value of h over the entire surface for each time step.
|
|
56
|
+
- h_std_xy: Standard deviation of h over the entire surface for each time step.
|
|
57
|
+
- h_sum_xy: Sum of each value of h at each point of the surface for each time step.
|
|
58
|
+
- h_int_xy: Integrated value of h at each point of the surface for each time step.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
TEMPORAL_DATA_1D = []
|
|
62
|
+
"""Time-varying 1D data.
|
|
63
|
+
|
|
64
|
+
Combine all the assembly possibilities between :data:`TEMPORAL_DATA_2D` and :data:`NP_OPERATORS` (or :data:`OTHER_OPERATORS`) and with an axis like this:
|
|
65
|
+
|
|
66
|
+
`[TEMPORAL_DATA_2D]_[NP/OTHER_OPERATORS]_[x/y]`
|
|
67
|
+
|
|
68
|
+
For instance with h :
|
|
69
|
+
- h_max_x: For each Y coordinate, maximum value of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hmax(y).
|
|
70
|
+
- h_max_y: For each X coordinate, maximum value of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hmax(x).
|
|
71
|
+
- h_min_x: For each Y coordinate, minimum value of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hmin(y).
|
|
72
|
+
- h_min_y: For each X coordinate, minimum value of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hmin(x).
|
|
73
|
+
- h_mean_x: For each Y coordinate, mean value of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hmean(y).
|
|
74
|
+
- h_mean_y: For each X coordinate, mean value of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hmean(x).
|
|
75
|
+
- h_std_x: For each Y coordinate, standard deviation of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hstd(y).
|
|
76
|
+
- h_std_y: For each X coordinate, standard deviation of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hstd(x).
|
|
77
|
+
- h_sum_x: For each Y coordinate, sum of each value of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hsum(y).
|
|
78
|
+
- h_sum_y: For each X coordinate, sum of each value of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hsum(x).
|
|
79
|
+
- h_int_x: For each Y coordinate, integrate each value of h integrating the values of all points on the X axis (along the fixed Y axis) and integrating all time steps, giving hint(y).
|
|
80
|
+
- h_int_y: For each X coordinate, integrate each value of h integrating the values of all points on the Y axis (along the fixed X axis) and integrating all time steps, giving hint(x).
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
TEMPORAL_DATA_2D = ["hvert", "h", "u", "ux", "uy", "hu", "hu2"]
|
|
84
|
+
"""Time-varying 2D data.
|
|
85
|
+
|
|
86
|
+
Implemented 2D temporal data :
|
|
87
|
+
|
|
88
|
+
- hvert : Fluid height taken vertically
|
|
89
|
+
- h : Fluid height taken normal to topography
|
|
90
|
+
- u : Fluid velocity
|
|
91
|
+
- ux : X-component of fluid velocity
|
|
92
|
+
- uy : Y-component of fluid velocity
|
|
93
|
+
- hu : Momentum flux
|
|
94
|
+
- hu2 : Convective momentum flux
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
STATIC_DATA_0D = []
|
|
98
|
+
"""Static 0D data."""
|
|
99
|
+
|
|
100
|
+
STATIC_DATA_1D = []
|
|
101
|
+
"""Static 1D data."""
|
|
102
|
+
|
|
103
|
+
STATIC_DATA_2D = []
|
|
104
|
+
"""Static 2D data.
|
|
105
|
+
|
|
106
|
+
Combine all the assembly possibilities between :data:`TEMPORAL_DATA_2D` and :data:`NP_OPERATORS` (or :data:`OTHER_OPERATORS`) like this:
|
|
107
|
+
|
|
108
|
+
`[TEMPORAL_DATA_2D]_[NP/OTHER_OPERATORS]`
|
|
109
|
+
|
|
110
|
+
For instance with h :
|
|
111
|
+
- h_max : Maximum value of h at each point on the map, integrating all the time steps.
|
|
112
|
+
- h_min : Minimum value of h at each point on the map, integrating all the time steps.
|
|
113
|
+
- h_mean : Mean value of h at each point on the map, integrating all the time steps.
|
|
114
|
+
- h_std : Standard deviation of h at each point on the map, integrating all the time steps.
|
|
115
|
+
- h_sum : Sum of h at each point on the map, integrating all the time steps.
|
|
116
|
+
- h_final : Value of h at each point on the map, for the last time step.
|
|
117
|
+
- h_init : Value of h at each point on the map, for the first time step.
|
|
118
|
+
- h_int : Integrated value of h at each point on the map, integrating all the time steps.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
TOPO_DATA_2D = ["z", "zinit", "costh"]
|
|
122
|
+
"""Data related to topography.
|
|
123
|
+
|
|
124
|
+
Implemented topographic data :
|
|
125
|
+
|
|
126
|
+
- z : Elevation value of topography
|
|
127
|
+
- zinit : Initial elevation value of topography (same as z if the topography doesn't change during the flow)
|
|
128
|
+
- costh : Cosine of the angle between the vertical and the normal to the relief. Factor to transform vertical height (hvert) into normal height (h).
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
NP_OPERATORS = ["max", "min", "mean", "std", "sum"]
|
|
132
|
+
"""Statistical operators.
|
|
133
|
+
|
|
134
|
+
Implemented operators :
|
|
135
|
+
|
|
136
|
+
- max : Maximum
|
|
137
|
+
- min : Minimum
|
|
138
|
+
- mean : Mean
|
|
139
|
+
- std : Standard deviation
|
|
140
|
+
- sum : Sum
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
OTHER_OPERATORS = ["final", "init", "int"]
|
|
144
|
+
"""Other operators.
|
|
145
|
+
|
|
146
|
+
Implemented operators :
|
|
147
|
+
|
|
148
|
+
- final : Final value
|
|
149
|
+
- init : Initial value
|
|
150
|
+
- int : Integrated value
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
TIME_OPERATORS = ["final", "init", "int"]
|
|
154
|
+
"""Time-related operators.
|
|
155
|
+
|
|
156
|
+
Implemented operators :
|
|
157
|
+
|
|
158
|
+
- final : Final value
|
|
159
|
+
- init : Initial value
|
|
160
|
+
- int : Integrated value
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
for stat in NP_OPERATORS + OTHER_OPERATORS:
|
|
164
|
+
for name in TEMPORAL_DATA_2D:
|
|
165
|
+
STATIC_DATA_2D.append(name + "_" + stat)
|
|
166
|
+
|
|
167
|
+
for stat in NP_OPERATORS + ["int"]:
|
|
168
|
+
for name in TEMPORAL_DATA_2D:
|
|
169
|
+
TEMPORAL_DATA_0D.append(name + "_" + stat + "_xy")
|
|
170
|
+
|
|
171
|
+
for stat in NP_OPERATORS + ["int"]:
|
|
172
|
+
for name in TEMPORAL_DATA_2D:
|
|
173
|
+
for axis in ["x", "y"]:
|
|
174
|
+
TEMPORAL_DATA_1D.append(name + "_" + stat + "_" + axis)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
TEMPORAL_DATA = TEMPORAL_DATA_0D + TEMPORAL_DATA_1D + TEMPORAL_DATA_2D
|
|
178
|
+
"""Assembling all temporal data.
|
|
179
|
+
|
|
180
|
+
:data:`TEMPORAL_DATA_0D` + :data:`TEMPORAL_DATA_1D` + :data:`TEMPORAL_DATA_2D`
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
STATIC_DATA = STATIC_DATA_0D + STATIC_DATA_1D + STATIC_DATA_2D
|
|
184
|
+
"""Assembling all static data.
|
|
185
|
+
|
|
186
|
+
:data:`STATIC_DATA_0D` + :data:`STATIC_DATA_1D` + :data:`STATIC_DATA_2D`
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
DATA_NAMES = TEMPORAL_DATA_0D + TEMPORAL_DATA_1D + TEMPORAL_DATA_2D + STATIC_DATA_0D + STATIC_DATA_1D + STATIC_DATA_2D
|
|
190
|
+
"""Assembling all data.
|
|
191
|
+
|
|
192
|
+
:data:`TEMPORAL_DATA` + :data:`STATIC_DATA`
|
|
193
|
+
"""
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class AbstractResults:
|
|
197
|
+
"""Abstract class for :class:`tilupy.read.TemporalResults` and :class:`tilupy.read.StaticResults`.
|
|
198
|
+
|
|
199
|
+
Parameters
|
|
200
|
+
----------
|
|
201
|
+
name : str
|
|
202
|
+
Name of the property.
|
|
203
|
+
d : numpy.ndarray
|
|
204
|
+
Values of the property.
|
|
205
|
+
notation : dict, optional
|
|
206
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
207
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
208
|
+
kwargs
|
|
209
|
+
|
|
210
|
+
Attributes
|
|
211
|
+
----------
|
|
212
|
+
_name : str
|
|
213
|
+
Name of the property.
|
|
214
|
+
_d : numpy.ndarray
|
|
215
|
+
Values of the property.
|
|
216
|
+
_notation : tilupy.notations.Notation
|
|
217
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
218
|
+
"""
|
|
219
|
+
def __init__(self, name: str,
|
|
220
|
+
d: np.ndarray,
|
|
221
|
+
notation: dict = None,
|
|
222
|
+
**kwargs):
|
|
223
|
+
self._name = name
|
|
224
|
+
self._d = d
|
|
225
|
+
if isinstance(notation, dict):
|
|
226
|
+
self._notation = notations.Notation(**notation)
|
|
227
|
+
elif notation is None:
|
|
228
|
+
self._notation = notations.get_notation(name)
|
|
229
|
+
else:
|
|
230
|
+
self._notation = notation
|
|
231
|
+
self.__dict__.update(kwargs)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def d(self) -> np.ndarray:
|
|
236
|
+
"""Get data values.
|
|
237
|
+
|
|
238
|
+
Returns
|
|
239
|
+
-------
|
|
240
|
+
np.ndarray
|
|
241
|
+
Attribute :attr:`_d`.
|
|
242
|
+
"""
|
|
243
|
+
return self._d
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def name(self) -> str:
|
|
248
|
+
"""Get data name.
|
|
249
|
+
|
|
250
|
+
Returns
|
|
251
|
+
-------
|
|
252
|
+
str
|
|
253
|
+
Attribute :attr:`_name`.
|
|
254
|
+
"""
|
|
255
|
+
return self._name
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def notation(self) -> tilupy.notations.Notation:
|
|
260
|
+
"""Get data notation.
|
|
261
|
+
|
|
262
|
+
Returns
|
|
263
|
+
-------
|
|
264
|
+
tilupy.notations.Notation
|
|
265
|
+
Attribute :attr:`_notation`.
|
|
266
|
+
"""
|
|
267
|
+
return self._notation
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class TemporalResults(AbstractResults):
|
|
271
|
+
"""Abstract class for time dependent result of simulation.
|
|
272
|
+
|
|
273
|
+
Parameters
|
|
274
|
+
----------
|
|
275
|
+
name : str
|
|
276
|
+
Name of the property.
|
|
277
|
+
d : numpy.ndarray
|
|
278
|
+
Values of the property.
|
|
279
|
+
t : numpy.ndarray
|
|
280
|
+
Time steps, must match the last dimension of :data:`d`.
|
|
281
|
+
notation : dict, optional
|
|
282
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
283
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
284
|
+
|
|
285
|
+
Attributes
|
|
286
|
+
----------
|
|
287
|
+
_name : str
|
|
288
|
+
Name of the property.
|
|
289
|
+
_d : numpy.ndarray
|
|
290
|
+
Values of the property.
|
|
291
|
+
_notation : tilupy.notations.Notation
|
|
292
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
293
|
+
_t : numpy.ndarray
|
|
294
|
+
Time steps.
|
|
295
|
+
"""
|
|
296
|
+
def __init__(self, name: str, d: np.ndarray, t: np.ndarray, notation: dict=None):
|
|
297
|
+
super().__init__(name, d, notation=notation)
|
|
298
|
+
# 1d array with times, matching last dimension of self.d
|
|
299
|
+
self._t = t
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def get_temporal_stat(self, stat: str) -> tilupy.read.StaticResults2D | tilupy.read.StaticResults1D:
|
|
303
|
+
"""Statistical analysis along temporal dimension.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
stat : str
|
|
308
|
+
Statistical operator to apply. Must be implemented in :data:`NP_OPERATORS` or in
|
|
309
|
+
:data:`OTHER_OPERATORS`.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
tilupy.read.StaticResults2D or tilupy.read.StaticResults1D
|
|
314
|
+
Static result object depending on the dimensionality of the data.
|
|
315
|
+
"""
|
|
316
|
+
if stat in NP_OPERATORS:
|
|
317
|
+
dnew = getattr(np, stat)(self._d, axis=-1)
|
|
318
|
+
elif stat == "final":
|
|
319
|
+
dnew = self._d[..., -1]
|
|
320
|
+
elif stat == "init":
|
|
321
|
+
dnew = self._d[..., 0]
|
|
322
|
+
elif stat == "int":
|
|
323
|
+
dnew = np.trapezoid(self._d, x=self._t)
|
|
324
|
+
|
|
325
|
+
notation = notations.add_operator(self._notation, stat, axis="t")
|
|
326
|
+
|
|
327
|
+
if dnew.ndim == 2:
|
|
328
|
+
return StaticResults2D(self._name + "_" + stat,
|
|
329
|
+
dnew,
|
|
330
|
+
notation=notation,
|
|
331
|
+
x=self.x,
|
|
332
|
+
y=self.y,
|
|
333
|
+
z=self.z,
|
|
334
|
+
)
|
|
335
|
+
elif dnew.ndim == 1:
|
|
336
|
+
return StaticResults1D(self._name + "_" + stat,
|
|
337
|
+
dnew,
|
|
338
|
+
notation=notation,
|
|
339
|
+
coords=self._coords,
|
|
340
|
+
coords_name=self._coords_name,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
@abstractmethod
|
|
345
|
+
def get_spatial_stat(self, stat, axis):
|
|
346
|
+
"""Abstract method for statistical analysis along spatial dimension.
|
|
347
|
+
|
|
348
|
+
Parameters
|
|
349
|
+
----------
|
|
350
|
+
stat : str
|
|
351
|
+
Statistical operator to apply. Must be implemented in :data:`NP_OPERATORS` or in
|
|
352
|
+
:data:`OTHER_OPERATORS`.
|
|
353
|
+
axis : str
|
|
354
|
+
Axis where to do the analysis.
|
|
355
|
+
"""
|
|
356
|
+
pass
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
@abstractmethod
|
|
360
|
+
def plot(*arg, **kwargs):
|
|
361
|
+
"""Abstract method to plot the temporal evolution of the results."""
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
@abstractmethod
|
|
366
|
+
def save(*arg, **kwargs):
|
|
367
|
+
"""Abstract method to save the temporal results."""
|
|
368
|
+
pass
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@abstractmethod
|
|
372
|
+
def extract_from_time_step(*arg, **kwargs):
|
|
373
|
+
"""Abstract method to extract data from specific time steps of a TemporalResults."""
|
|
374
|
+
pass
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def t(self) -> np.ndarray:
|
|
379
|
+
"""Get times.
|
|
380
|
+
|
|
381
|
+
Returns
|
|
382
|
+
-------
|
|
383
|
+
numpy.ndarray
|
|
384
|
+
Attribute :attr:`_t`
|
|
385
|
+
"""
|
|
386
|
+
return self._t
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class TemporalResults0D(TemporalResults):
|
|
390
|
+
"""
|
|
391
|
+
Class for simulation results described where the data is one or multiple scalar functions of time.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
name : str
|
|
396
|
+
Name of the property.
|
|
397
|
+
d : numpy.ndarray
|
|
398
|
+
Values of the property. The last dimension correspond to time.
|
|
399
|
+
It can be a one dimensionnal Nt array, or a two dimensionnal [Nd, Nt] array,
|
|
400
|
+
where Nt is the legnth of :data:`t`, and Nd correspond to the number of scalar values
|
|
401
|
+
of interest (e.g. X and Y coordinates of the center of mass / front).
|
|
402
|
+
t : numpy.ndarray
|
|
403
|
+
Time steps, must match the last dimension of :data:`d` (size Nt).
|
|
404
|
+
scalar_names : list[str]
|
|
405
|
+
List of length Nd containing the names of the scalar fields (one
|
|
406
|
+
name per row of d)
|
|
407
|
+
notation : dict, optional
|
|
408
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
409
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
410
|
+
|
|
411
|
+
Attributes
|
|
412
|
+
----------
|
|
413
|
+
_name : str
|
|
414
|
+
Name of the property.
|
|
415
|
+
_d : numpy.ndarray
|
|
416
|
+
Values of the property.
|
|
417
|
+
_notation : tilupy.notations.Notation
|
|
418
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
419
|
+
_t : numpy.ndarray
|
|
420
|
+
Time steps.
|
|
421
|
+
_scalar_names : list[str]
|
|
422
|
+
List of names of the scalar fields.
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
def __init__(self, name: str,
|
|
426
|
+
d: np.ndarray,
|
|
427
|
+
t: np.ndarray,
|
|
428
|
+
scalar_names: list[str]=None,
|
|
429
|
+
notation: dict=None):
|
|
430
|
+
super().__init__(name, d, t, notation=notation)
|
|
431
|
+
self._scalar_names = scalar_names
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def plot(self,
|
|
435
|
+
ax: matplotlib.axes._axes.Axes=None,
|
|
436
|
+
figsize: tuple[float]=None,
|
|
437
|
+
**kwargs
|
|
438
|
+
) -> matplotlib.axes._axes.Axes:
|
|
439
|
+
"""Plot the temporal evolution of the 0D results.
|
|
440
|
+
|
|
441
|
+
Parameters
|
|
442
|
+
----------
|
|
443
|
+
ax : matplotlib.axes._axes.Axes, optional
|
|
444
|
+
Existing matplotlib window, if None create one. By default None.
|
|
445
|
+
figsize : tuple[float], optional
|
|
446
|
+
Size of the figure, by default None.
|
|
447
|
+
|
|
448
|
+
Returns
|
|
449
|
+
-------
|
|
450
|
+
matplotlib.axes._axes.Axes
|
|
451
|
+
The created plot.
|
|
452
|
+
"""
|
|
453
|
+
if ax is None:
|
|
454
|
+
fig, ax = plt.subplots(1, 1, figsize=figsize, layout="constrained")
|
|
455
|
+
|
|
456
|
+
if isinstance(self._d, np.ndarray):
|
|
457
|
+
data = self._d.T
|
|
458
|
+
else:
|
|
459
|
+
data = self._d
|
|
460
|
+
|
|
461
|
+
if "color" not in kwargs and self._scalar_names is None:
|
|
462
|
+
color = "black"
|
|
463
|
+
kwargs["color"] = color
|
|
464
|
+
|
|
465
|
+
ax.plot(self._t, data, label=self._scalar_names, **kwargs) # Remove label=self._scalar_names
|
|
466
|
+
|
|
467
|
+
ax.grid(True, alpha=0.3)
|
|
468
|
+
ax.set_xlim(left=min(self._t), right=max(self._t))
|
|
469
|
+
ax.set_xlabel(notations.get_label("t"))
|
|
470
|
+
ax.set_ylabel(notations.get_label(self._notation))
|
|
471
|
+
|
|
472
|
+
return ax
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def save(self):
|
|
476
|
+
"""Save the temporal 0D results.
|
|
477
|
+
|
|
478
|
+
Raises
|
|
479
|
+
------
|
|
480
|
+
NotImplementedError
|
|
481
|
+
Not implemented yet.
|
|
482
|
+
"""
|
|
483
|
+
raise NotImplementedError("Saving method for :class:`tilupy.read.TemporalResults0D` not implemented yet")
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def get_spatial_stat(self, *arg, **kwargs):
|
|
487
|
+
"""Statistical analysis along spatial dimension for 0D results.
|
|
488
|
+
|
|
489
|
+
Raises
|
|
490
|
+
------
|
|
491
|
+
NotImplementedError
|
|
492
|
+
Not implemented because irrelevant.
|
|
493
|
+
"""
|
|
494
|
+
raise NotImplementedError("Spatial integration of :class:`tilupy.read.Spatialresults0D` is not implemented because non relevant")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def extract_from_time_step(self,
|
|
498
|
+
time_steps: float | list[float],
|
|
499
|
+
) -> tilupy.read.StaticResults0D | tilupy.read.TemporalResults0D:
|
|
500
|
+
"""Extract data from specific time steps.
|
|
501
|
+
|
|
502
|
+
Parameters
|
|
503
|
+
----------
|
|
504
|
+
time_steps : float | list[float]
|
|
505
|
+
Value of time steps to extract data.
|
|
506
|
+
|
|
507
|
+
Returns
|
|
508
|
+
-------
|
|
509
|
+
tilupy.read.StaticResults0D | tilupy.read.TemporalResults0D
|
|
510
|
+
Extracted data, the type depends on the time step.
|
|
511
|
+
"""
|
|
512
|
+
if isinstance(time_steps, float) or isinstance(time_steps, int):
|
|
513
|
+
t_index = np.argmin(np.abs(self._t - time_steps))
|
|
514
|
+
|
|
515
|
+
return StaticResults0D(name=self._name,
|
|
516
|
+
d=self._d[t_index],
|
|
517
|
+
notation=self._notation)
|
|
518
|
+
|
|
519
|
+
elif isinstance(time_steps, list):
|
|
520
|
+
time_steps = np.array(time_steps)
|
|
521
|
+
|
|
522
|
+
if isinstance(time_steps, np.ndarray):
|
|
523
|
+
if isinstance(self._t, list):
|
|
524
|
+
self._t = np.array(self._t)
|
|
525
|
+
t_distances = np.abs(self._t[None, :] - time_steps[:, None])
|
|
526
|
+
t_indexes = np.argmin(t_distances, axis=1)
|
|
527
|
+
|
|
528
|
+
return TemporalResults0D(name=self._name,
|
|
529
|
+
d=self._d[t_indexes],
|
|
530
|
+
t=self._t[t_indexes],
|
|
531
|
+
notation=self._notation)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
@property
|
|
535
|
+
def scalar_names(self) -> list[str]:
|
|
536
|
+
"""Get list of names of the scalar fields.
|
|
537
|
+
|
|
538
|
+
Returns
|
|
539
|
+
-------
|
|
540
|
+
list[str]
|
|
541
|
+
Attribute :attr:`_scalar_names`.
|
|
542
|
+
"""
|
|
543
|
+
return self._scalar_names
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class TemporalResults1D(TemporalResults):
|
|
547
|
+
"""
|
|
548
|
+
Class for simulation results described by one dimension for space and one dimension for time.
|
|
549
|
+
|
|
550
|
+
Parameters
|
|
551
|
+
----------
|
|
552
|
+
name : str
|
|
553
|
+
Name of the property.
|
|
554
|
+
d : numpy.ndarray
|
|
555
|
+
Values of the property. The last dimension correspond to time.
|
|
556
|
+
It can be a one dimensionnal Nt array, or a two dimensionnal [Nd, Nt] array,
|
|
557
|
+
where Nt is the legnth of :data:`t`, and Nd correspond to the number of scalar values
|
|
558
|
+
of interest (e.g. X and Y coordinates of the center of mass / front).
|
|
559
|
+
t : numpy.ndarray
|
|
560
|
+
Time steps, must match the last dimension of :data:`d` (size Nt).
|
|
561
|
+
coords: numpy.ndarray
|
|
562
|
+
Spatial coordinates.
|
|
563
|
+
coords_name: str
|
|
564
|
+
Spatial coordinates name.
|
|
565
|
+
notation : dict, optional
|
|
566
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
567
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
568
|
+
|
|
569
|
+
Attributes
|
|
570
|
+
----------
|
|
571
|
+
_name : str
|
|
572
|
+
Name of the property.
|
|
573
|
+
_d : numpy.ndarray
|
|
574
|
+
Values of the property.
|
|
575
|
+
_notation : tilupy.notations.Notation
|
|
576
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
577
|
+
_t : numpy.ndarray
|
|
578
|
+
Time steps.
|
|
579
|
+
_coords: numpy.ndarray
|
|
580
|
+
Spatial coordinates.
|
|
581
|
+
_coords_name: str
|
|
582
|
+
Spatial coordinates name.
|
|
583
|
+
"""
|
|
584
|
+
def __init__(self, name: str,
|
|
585
|
+
d: np.ndarray,
|
|
586
|
+
t: np.ndarray,
|
|
587
|
+
coords: np.ndarray=None,
|
|
588
|
+
coords_name: str=None,
|
|
589
|
+
notation: dict=None):
|
|
590
|
+
super().__init__(name, d, t, notation=notation)
|
|
591
|
+
# x and y arrays
|
|
592
|
+
self._coords = coords
|
|
593
|
+
self._coords_name = coords_name
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def plot(self,
|
|
597
|
+
coords = None,
|
|
598
|
+
plot_type: str = "simple",
|
|
599
|
+
figsize: tuple[float] = None,
|
|
600
|
+
ax: matplotlib.axes._axes.Axes = None,
|
|
601
|
+
linestyles: list[str] = None,
|
|
602
|
+
cmap: str = 'viridis',
|
|
603
|
+
highlighted_curve: bool = False,
|
|
604
|
+
**kwargs
|
|
605
|
+
) -> matplotlib.axes._axes.Axes:
|
|
606
|
+
"""Plot the temporal evolution of the 1D results.
|
|
607
|
+
|
|
608
|
+
Parameters
|
|
609
|
+
----------
|
|
610
|
+
coords : numpy.ndarray, optional
|
|
611
|
+
Specified coordinates, if None uses the coordinates implemented when creating the instance (:attr:`_coords`).
|
|
612
|
+
By default None.
|
|
613
|
+
plot_type: str, optional
|
|
614
|
+
Wanted plot :
|
|
615
|
+
|
|
616
|
+
- "simple" : Every curve in the same graph
|
|
617
|
+
- "multiples" : Every curve in separate graph
|
|
618
|
+
- "'shotgather" : Shotgather graph
|
|
619
|
+
|
|
620
|
+
By default "simple".
|
|
621
|
+
ax : matplotlib.axes._axes.Axes, optional
|
|
622
|
+
Existing matplotlib window, if None create one. By default None
|
|
623
|
+
linestyles : list[str], optional
|
|
624
|
+
Custom linestyles for each time step. If None, colors and styles are auto-assigned.
|
|
625
|
+
Used only for "simple". By default None.
|
|
626
|
+
cmap : str, optional
|
|
627
|
+
Color map for the ploted curves. Used only for "simple". By default "viridis".
|
|
628
|
+
hightlighted_curved : bool, optional
|
|
629
|
+
Option to display all time steps on each graph of the multiples and
|
|
630
|
+
highlight the curve corresponding to the time step of the subgraph. Used only for "multiples".
|
|
631
|
+
By default False.
|
|
632
|
+
kwargs: dict, optional
|
|
633
|
+
Additional arguments for plot functions.
|
|
634
|
+
|
|
635
|
+
Returns
|
|
636
|
+
-------
|
|
637
|
+
matplotlib.axes._axes.Axes
|
|
638
|
+
The created plot.
|
|
639
|
+
|
|
640
|
+
Raises
|
|
641
|
+
------
|
|
642
|
+
TypeError
|
|
643
|
+
If missing coordinates.
|
|
644
|
+
"""
|
|
645
|
+
if coords is None:
|
|
646
|
+
coords = self._coords
|
|
647
|
+
if coords is None:
|
|
648
|
+
raise TypeError("coords data missing")
|
|
649
|
+
|
|
650
|
+
if ax is None and plot_type != "multiples":
|
|
651
|
+
fig, ax = plt.subplots(1, 1, figsize=figsize, layout="constrained")
|
|
652
|
+
|
|
653
|
+
if plot_type == "shotgather":
|
|
654
|
+
xlabel = notations.get_label(self._coords_name, with_unit=True)
|
|
655
|
+
|
|
656
|
+
if "colorbar_kwargs" not in kwargs:
|
|
657
|
+
kwargs["colorbar_kwargs"] = dict()
|
|
658
|
+
if "label" not in kwargs["colorbar_kwargs"]:
|
|
659
|
+
clabel = notations.get_label(self._notation)
|
|
660
|
+
kwargs["colorbar_kwargs"]["label"] = clabel
|
|
661
|
+
|
|
662
|
+
ax = plt_tlp.plot_shotgather(self._coords,
|
|
663
|
+
self._t,
|
|
664
|
+
self._d,
|
|
665
|
+
xlabel=xlabel,
|
|
666
|
+
ylabel=notations.get_label("t"),
|
|
667
|
+
**kwargs)
|
|
668
|
+
|
|
669
|
+
if plot_type == "simple":
|
|
670
|
+
if linestyles is None or len(linestyles)!=(len(self._t)):
|
|
671
|
+
norm = plt.Normalize(vmin=0, vmax=len(self._t)-1)
|
|
672
|
+
cmap = plt.get_cmap(cmap)
|
|
673
|
+
|
|
674
|
+
for i in range(self._d.shape[1]):
|
|
675
|
+
if linestyles is None or len(linestyles)!=(len(self._t)):
|
|
676
|
+
color = cmap(norm(i)) if self._t[i] != 0 else "red"
|
|
677
|
+
l_style = "-" if self._t[i] != 0 else (0, (1, 4))
|
|
678
|
+
else:
|
|
679
|
+
color = "black" if self._t[i] != 0 else "red"
|
|
680
|
+
l_style = linestyles[i] if self._t[i] != 0 else (0, (1, 4))
|
|
681
|
+
|
|
682
|
+
ax.plot(self._coords, self._d[:, i], label=f"t={self._t[i]}s", color=color, linestyle=l_style, **kwargs)
|
|
683
|
+
|
|
684
|
+
ax.grid(True, alpha=0.3)
|
|
685
|
+
ax.set_xlim(left=min(self._coords), right=max(self._coords))
|
|
686
|
+
|
|
687
|
+
ax.set_xlabel(notations.get_label(self._coords_name))
|
|
688
|
+
ax.set_ylabel(notations.get_label(self._notation))
|
|
689
|
+
|
|
690
|
+
if plot_type == "multiples":
|
|
691
|
+
cols_nb = 3
|
|
692
|
+
if len(self._t) < 3:
|
|
693
|
+
cols_nb = len(self._t)
|
|
694
|
+
|
|
695
|
+
row_nb = len(self._t) // 3
|
|
696
|
+
if len(self._t) % 3 != 0:
|
|
697
|
+
row_nb += 1
|
|
698
|
+
|
|
699
|
+
fig, axes = plt.subplots(nrows=row_nb,
|
|
700
|
+
ncols=cols_nb,
|
|
701
|
+
figsize=figsize,
|
|
702
|
+
layout="constrained",
|
|
703
|
+
sharex=True,
|
|
704
|
+
sharey=True)
|
|
705
|
+
axes = axes.flatten()
|
|
706
|
+
|
|
707
|
+
for i in range(self._d.shape[1]):
|
|
708
|
+
if highlighted_curve:
|
|
709
|
+
for j in range(self._d.shape[1]):
|
|
710
|
+
if i == j:
|
|
711
|
+
axes[i].plot(self._coords, self._d[:, j], color="black", linewidth=1.5, **kwargs)
|
|
712
|
+
else:
|
|
713
|
+
axes[i].plot(self._coords, self._d[:, j], color="gray", alpha=0.5, linewidth=0.5, **kwargs)
|
|
714
|
+
else:
|
|
715
|
+
axes[i].plot(self._coords, self._d[:, i], color="black", **kwargs)
|
|
716
|
+
|
|
717
|
+
axes[i].grid(True, alpha=0.3)
|
|
718
|
+
axes[i].set_xlim(left=min(self._coords), right=max(self._coords))
|
|
719
|
+
|
|
720
|
+
axes[i].set_xlabel(notations.get_label(self._coords_name))
|
|
721
|
+
axes[i].set_ylabel(notations.get_label(self._notation))
|
|
722
|
+
|
|
723
|
+
axes[i].set_title(f"t={self._t[i]}s", loc='left')
|
|
724
|
+
|
|
725
|
+
for i in range(len(self._t), len(axes)):
|
|
726
|
+
fig.delaxes(axes[i])
|
|
727
|
+
|
|
728
|
+
return axes
|
|
729
|
+
|
|
730
|
+
return ax
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def save(self):
|
|
734
|
+
"""Save the temporal 1D results.
|
|
735
|
+
|
|
736
|
+
Raises
|
|
737
|
+
------
|
|
738
|
+
NotImplementedError
|
|
739
|
+
Not implemented yet.
|
|
740
|
+
"""
|
|
741
|
+
raise NotImplementedError("Saving method for TemporalResults1D not implemented yet")
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
def get_spatial_stat(self, stat, **kwargs) -> tilupy.read.TemporalResults0D:
|
|
745
|
+
"""Statistical analysis along spatial dimension for 1D results.
|
|
746
|
+
|
|
747
|
+
Parameters
|
|
748
|
+
----------
|
|
749
|
+
stat : str
|
|
750
|
+
Statistical operator to apply. Must be implemented in :data:`NP_OPERATORS` or in
|
|
751
|
+
:data:`OTHER_OPERATORS`.
|
|
752
|
+
|
|
753
|
+
Returns
|
|
754
|
+
-------
|
|
755
|
+
tilupy.read.TemporalResults0D
|
|
756
|
+
Instance of :class:`tilupy.read.TemporalResults0D`.
|
|
757
|
+
"""
|
|
758
|
+
if stat in NP_OPERATORS:
|
|
759
|
+
dnew = getattr(np, stat)(self._d, axis=0)
|
|
760
|
+
elif stat == "int":
|
|
761
|
+
dd = self._coords[1] - self._coords[0]
|
|
762
|
+
dnew = np.sum(self._d, axis=0) * dd
|
|
763
|
+
notation = notations.add_operator(self._notation, stat, axis=self._coords_name)
|
|
764
|
+
|
|
765
|
+
return TemporalResults0D(self._name + "_" + stat,
|
|
766
|
+
dnew,
|
|
767
|
+
self._t,
|
|
768
|
+
notation=notation)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def extract_from_time_step(self,
|
|
772
|
+
time_steps: float | list[float],
|
|
773
|
+
) -> tilupy.read.StaticResults1D | tilupy.read.TemporalResults1D:
|
|
774
|
+
"""Extract data from specific time steps.
|
|
775
|
+
|
|
776
|
+
Parameters
|
|
777
|
+
----------
|
|
778
|
+
time_steps : float | list[float]
|
|
779
|
+
Value of time steps to extract data.
|
|
780
|
+
|
|
781
|
+
Returns
|
|
782
|
+
-------
|
|
783
|
+
tilupy.read.StaticResults1D | tilupy.read.TemporalResults1D
|
|
784
|
+
Extracted data, the type depends on the time step.
|
|
785
|
+
"""
|
|
786
|
+
if isinstance(time_steps, float) or isinstance(time_steps, int):
|
|
787
|
+
t_index = np.argmin(np.abs(self._t - time_steps))
|
|
788
|
+
|
|
789
|
+
return StaticResults1D(name=self._name,
|
|
790
|
+
d=self._d[:, t_index],
|
|
791
|
+
coords=self._coords,
|
|
792
|
+
coords_name=self._coords_name,
|
|
793
|
+
notation=self._notation)
|
|
794
|
+
|
|
795
|
+
elif isinstance(time_steps, list):
|
|
796
|
+
time_steps = np.array(time_steps)
|
|
797
|
+
|
|
798
|
+
if isinstance(time_steps, np.ndarray):
|
|
799
|
+
if isinstance(self._t, list):
|
|
800
|
+
self._t = np.array(self._t)
|
|
801
|
+
t_distances = np.abs(self._t[None, :] - time_steps[:, None])
|
|
802
|
+
t_indexes = np.argmin(t_distances, axis=1)
|
|
803
|
+
|
|
804
|
+
return TemporalResults1D(name=self._name,
|
|
805
|
+
d=self._d[:, t_indexes],
|
|
806
|
+
t=self._t[t_indexes],
|
|
807
|
+
coords=self._coords,
|
|
808
|
+
coords_name=self._coords_name,
|
|
809
|
+
notation=self._notation)
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
@property
|
|
813
|
+
def coords(self) -> np.ndarray:
|
|
814
|
+
"""Get spatial coordinates.
|
|
815
|
+
|
|
816
|
+
Returns
|
|
817
|
+
-------
|
|
818
|
+
numpy.ndarray
|
|
819
|
+
Attribute :attr:`_coords`
|
|
820
|
+
"""
|
|
821
|
+
return self._coords
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
@property
|
|
825
|
+
def coords_name(self) -> str:
|
|
826
|
+
"""Get spatial coordinates name.
|
|
827
|
+
|
|
828
|
+
Returns
|
|
829
|
+
-------
|
|
830
|
+
str
|
|
831
|
+
Attribute :attr:`_coords_name`
|
|
832
|
+
"""
|
|
833
|
+
return self._coords_name
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
class TemporalResults2D(TemporalResults):
|
|
837
|
+
"""
|
|
838
|
+
Class for simulation results described by a two dimensional space and one dimension for time.
|
|
839
|
+
|
|
840
|
+
Parameters
|
|
841
|
+
----------
|
|
842
|
+
name : str
|
|
843
|
+
Name of the property.
|
|
844
|
+
d : numpy.ndarray
|
|
845
|
+
Values of the property. The last dimension correspond to time.
|
|
846
|
+
It can be a one dimensionnal Nt array, or a two dimensionnal [Nd, Nt] array,
|
|
847
|
+
where Nt is the legnth of :data:`t`, and Nd correspond to the number of scalar values
|
|
848
|
+
of interest (e.g. X and Y coordinates of the center of mass / front).
|
|
849
|
+
t : numpy.ndarray
|
|
850
|
+
Time steps, must match the last dimension of :data:`d` (size Nt).
|
|
851
|
+
x : numpy.ndarray
|
|
852
|
+
X coordinate values.
|
|
853
|
+
y : numpy.ndarray
|
|
854
|
+
X coordinate values.
|
|
855
|
+
z : numpy.ndarray
|
|
856
|
+
Elevation values of the surface.
|
|
857
|
+
notation : dict, optional
|
|
858
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notation.Notation`.
|
|
859
|
+
If None use the function :func:`tilupy.notation.get_notation`. By default None.
|
|
860
|
+
|
|
861
|
+
Attributes
|
|
862
|
+
----------
|
|
863
|
+
_name : str
|
|
864
|
+
Name of the property.
|
|
865
|
+
_d : numpy.ndarray
|
|
866
|
+
Values of the property.
|
|
867
|
+
_notation : tilupy.notations.Notation
|
|
868
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
869
|
+
_t : numpy.ndarray
|
|
870
|
+
Time steps.
|
|
871
|
+
_x : numpy.ndarray
|
|
872
|
+
X coordinate values.
|
|
873
|
+
_y : numpy.ndarray
|
|
874
|
+
X coordinate values.
|
|
875
|
+
_z : numpy.ndarray
|
|
876
|
+
Elevation values of the surface.
|
|
877
|
+
"""
|
|
878
|
+
def __init__(self,
|
|
879
|
+
name: str,
|
|
880
|
+
d: np.ndarray,
|
|
881
|
+
t: np.ndarray,
|
|
882
|
+
x: np.ndarray=None,
|
|
883
|
+
y: np.ndarray=None,
|
|
884
|
+
z: np.ndarray=None,
|
|
885
|
+
notation: dict=None):
|
|
886
|
+
super().__init__(name, d, t, notation=notation)
|
|
887
|
+
# x and y arrays
|
|
888
|
+
self._x = x
|
|
889
|
+
self._y = y
|
|
890
|
+
# topography
|
|
891
|
+
self._z = z
|
|
892
|
+
|
|
893
|
+
|
|
894
|
+
def plot(self,
|
|
895
|
+
x: np.ndarray = None,
|
|
896
|
+
y: np.ndarray = None,
|
|
897
|
+
z: np.ndarray = None,
|
|
898
|
+
plot_multiples: bool = False,
|
|
899
|
+
file_name: str = None,
|
|
900
|
+
folder_out: str = None,
|
|
901
|
+
figsize: tuple[float] = None,
|
|
902
|
+
dpi: int = None,
|
|
903
|
+
fmt: str = "png",
|
|
904
|
+
sup_plt_fn = None,
|
|
905
|
+
sup_plt_fn_args = None,
|
|
906
|
+
**kwargs
|
|
907
|
+
) -> None:
|
|
908
|
+
"""Plot the temporal evolution of the 2D results using :func:`pytopomap.plot.plot_maps`.
|
|
909
|
+
|
|
910
|
+
Parameters
|
|
911
|
+
----------
|
|
912
|
+
x : numpy.ndarray, optional
|
|
913
|
+
X coordinate values, if None use :attr:`_x`. By default None.
|
|
914
|
+
y : numpy.ndarray, optional
|
|
915
|
+
Y coordinate values, if None use :attr:`_y`. By default None.
|
|
916
|
+
z : numpy.ndarray, optional
|
|
917
|
+
Elevation values, if None use :attr:`_z`. By default None.
|
|
918
|
+
file_name : str, optional
|
|
919
|
+
Base name for the output image files, by default None.
|
|
920
|
+
folder_out : str, optional
|
|
921
|
+
Path to the output folder. If not provides, figures are not saved. By default None.
|
|
922
|
+
figsize : tuple[float], optional
|
|
923
|
+
Size of the figure, by default None.
|
|
924
|
+
dpi : int, optional
|
|
925
|
+
Resolution for saved figures. Only used if :data:`folder_out` is set. By default None.
|
|
926
|
+
fmt : str, optional
|
|
927
|
+
File format for saving figures, by default "png".
|
|
928
|
+
sup_plt_fn : callable, optional
|
|
929
|
+
A custom function to apply additional plotting on the axes, by default None.
|
|
930
|
+
sup_plt_fn_args : dict, optional
|
|
931
|
+
Arguments to pass to :data:`sup_plt_fn`, by default None.
|
|
932
|
+
|
|
933
|
+
Raises
|
|
934
|
+
------
|
|
935
|
+
TypeError
|
|
936
|
+
If no value for x, y.
|
|
937
|
+
"""
|
|
938
|
+
if file_name is None:
|
|
939
|
+
file_name = self._name
|
|
940
|
+
|
|
941
|
+
if x is None:
|
|
942
|
+
x = self._x
|
|
943
|
+
if y is None:
|
|
944
|
+
y = self._y
|
|
945
|
+
if z is None:
|
|
946
|
+
z = self._z
|
|
947
|
+
|
|
948
|
+
if x is None or y is None:
|
|
949
|
+
raise TypeError("x, y or z data missing")
|
|
950
|
+
|
|
951
|
+
if z is None:
|
|
952
|
+
warnings.warn("No topography given.")
|
|
953
|
+
|
|
954
|
+
if "colorbar_kwargs" not in kwargs:
|
|
955
|
+
kwargs["colorbar_kwargs"] = dict()
|
|
956
|
+
if "label" not in kwargs["colorbar_kwargs"]:
|
|
957
|
+
clabel = notations.get_label(self._notation)
|
|
958
|
+
kwargs["colorbar_kwargs"]["label"] = clabel
|
|
959
|
+
|
|
960
|
+
if plot_multiples:
|
|
961
|
+
if "vmin" not in kwargs:
|
|
962
|
+
kwargs["vmin"] = np.min(self._d)
|
|
963
|
+
if "vmax" not in kwargs:
|
|
964
|
+
kwargs["vmax"] = np.max(self._d)
|
|
965
|
+
|
|
966
|
+
cols_nb = 3
|
|
967
|
+
if len(self._t) < 3:
|
|
968
|
+
cols_nb = len(self._t)
|
|
969
|
+
|
|
970
|
+
row_nb = len(self._t) // 3
|
|
971
|
+
if len(self._t) % 3 != 0:
|
|
972
|
+
row_nb += 1
|
|
973
|
+
|
|
974
|
+
fig, axes = plt.subplots(nrows=row_nb,
|
|
975
|
+
ncols=cols_nb,
|
|
976
|
+
figsize=figsize,
|
|
977
|
+
layout="constrained",
|
|
978
|
+
sharex=True,
|
|
979
|
+
sharey=True)
|
|
980
|
+
axes = axes.flatten()
|
|
981
|
+
|
|
982
|
+
for i in range(len(self._t)):
|
|
983
|
+
plt_fn.plot_data_on_topo(x=x,
|
|
984
|
+
y=y,
|
|
985
|
+
z=z,
|
|
986
|
+
data=self._d[:, :, i],
|
|
987
|
+
axe=axes[i],
|
|
988
|
+
plot_colorbar=False,
|
|
989
|
+
**kwargs)
|
|
990
|
+
|
|
991
|
+
axes[i].set_title(f"t={self._t[i]}s", loc='left')
|
|
992
|
+
|
|
993
|
+
for i in range(len(self._t), len(axes)):
|
|
994
|
+
fig.delaxes(axes[i])
|
|
995
|
+
|
|
996
|
+
max_val, idx = 0, 0
|
|
997
|
+
for i in range(len(self._t)):
|
|
998
|
+
max_val_t = np.max(axes[i].images[1].get_array())
|
|
999
|
+
if max_val_t > max_val:
|
|
1000
|
+
max_val = max_val_t
|
|
1001
|
+
idx = i
|
|
1002
|
+
mappable = axes[idx].images[1]
|
|
1003
|
+
fig.colorbar(mappable, ax=axes, orientation='vertical', **kwargs["colorbar_kwargs"])
|
|
1004
|
+
|
|
1005
|
+
return axes
|
|
1006
|
+
|
|
1007
|
+
plt_fn.plot_maps(x,
|
|
1008
|
+
y,
|
|
1009
|
+
z,
|
|
1010
|
+
self._d,
|
|
1011
|
+
self._t,
|
|
1012
|
+
file_name=file_name,
|
|
1013
|
+
folder_out=folder_out,
|
|
1014
|
+
figsize=figsize,
|
|
1015
|
+
dpi=dpi,
|
|
1016
|
+
fmt=fmt,
|
|
1017
|
+
sup_plt_fn=sup_plt_fn,
|
|
1018
|
+
sup_plt_fn_args=sup_plt_fn_args,
|
|
1019
|
+
**kwargs
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
return None
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
def save(self,
|
|
1026
|
+
folder: str=None,
|
|
1027
|
+
file_name: str=None,
|
|
1028
|
+
fmt: str="asc",
|
|
1029
|
+
time: str | int=None,
|
|
1030
|
+
x: np.ndarray=None,
|
|
1031
|
+
y: np.ndarray=None,
|
|
1032
|
+
**kwargs
|
|
1033
|
+
) -> None:
|
|
1034
|
+
"""Save the temporal 2D results.
|
|
1035
|
+
|
|
1036
|
+
Parameters
|
|
1037
|
+
----------
|
|
1038
|
+
folder : str, optional
|
|
1039
|
+
Path to the output folder, if None create a folder with :attr:`_name`. By default None.
|
|
1040
|
+
file_name : str, optional
|
|
1041
|
+
Base name for the output image files, if None use :attr:`_name`. By default None.
|
|
1042
|
+
fmt : str, optional
|
|
1043
|
+
File format for saving result, by default "asc".
|
|
1044
|
+
time : str | int, optional
|
|
1045
|
+
Time instants to save the results.
|
|
1046
|
+
|
|
1047
|
+
- If time is string, must be "initial" or "final".
|
|
1048
|
+
- If time is int, used as index in :attr:`_t`.
|
|
1049
|
+
- If None use every instant in :attr:`_t`.
|
|
1050
|
+
|
|
1051
|
+
By default None.
|
|
1052
|
+
x : np.ndarray, optional
|
|
1053
|
+
X coordinate values, if None use :attr:`_x`. By default None.
|
|
1054
|
+
y : np.ndarray, optional
|
|
1055
|
+
Y coordinate values, if None use :attr:`_y`. By default None.
|
|
1056
|
+
|
|
1057
|
+
Returns
|
|
1058
|
+
-------
|
|
1059
|
+
None
|
|
1060
|
+
|
|
1061
|
+
Raises
|
|
1062
|
+
------
|
|
1063
|
+
ValueError
|
|
1064
|
+
If no value for x, y.
|
|
1065
|
+
"""
|
|
1066
|
+
if x is None:
|
|
1067
|
+
x = self._x
|
|
1068
|
+
if y is None:
|
|
1069
|
+
y = self._y
|
|
1070
|
+
if x is None or y is None:
|
|
1071
|
+
raise ValueError("x et y arrays must not be None")
|
|
1072
|
+
|
|
1073
|
+
if file_name is None:
|
|
1074
|
+
file_name = self._name
|
|
1075
|
+
|
|
1076
|
+
if folder is not None:
|
|
1077
|
+
file_name = os.path.join(folder, file_name)
|
|
1078
|
+
|
|
1079
|
+
if time is not None:
|
|
1080
|
+
if isinstance(time, str):
|
|
1081
|
+
if time == "final":
|
|
1082
|
+
inds = [self._d.shape[2] - 1]
|
|
1083
|
+
elif time == "initial":
|
|
1084
|
+
inds = [0]
|
|
1085
|
+
else:
|
|
1086
|
+
inds = [np.argmin(time - np.abs(np.array(self._t) - time))]
|
|
1087
|
+
else:
|
|
1088
|
+
inds = range(len(self._t))
|
|
1089
|
+
|
|
1090
|
+
for i in inds:
|
|
1091
|
+
file_out = file_name + "_{:04d}.".format(i) + fmt
|
|
1092
|
+
tilupy.raster.write_raster(x, y, self._d[:, :, i], file_out, fmt=fmt, **kwargs)
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def get_spatial_stat(self,
|
|
1096
|
+
stat: str,
|
|
1097
|
+
axis: str | int | tuple[int]=None
|
|
1098
|
+
) -> tilupy.read.TemporalResults0D | tilupy.read.TemporalResults1D:
|
|
1099
|
+
"""Statistical analysis along spatial dimension for 2D results.
|
|
1100
|
+
|
|
1101
|
+
Parameters
|
|
1102
|
+
----------
|
|
1103
|
+
stat : str
|
|
1104
|
+
Statistical operator to apply. Must be implemented in :data:`NP_OPERATORS`.
|
|
1105
|
+
axis : tuple[int]
|
|
1106
|
+
Axis where to do the analysis:
|
|
1107
|
+
|
|
1108
|
+
- If axis is string, replace 'x' by 1, 'y' by 0 and 'xy' by (0, 1).
|
|
1109
|
+
- If axis is int, only use 0 or 1.
|
|
1110
|
+
- If None use (0, 1). By default None.
|
|
1111
|
+
|
|
1112
|
+
Returns
|
|
1113
|
+
-------
|
|
1114
|
+
tilupy.read.TemporalResults0D or tilupy.read.TemporalResults1D
|
|
1115
|
+
Instance of :class:`tilupy.read.TemporalResults0D` or :class:`tilupy.read.TemporalResults1D`.
|
|
1116
|
+
"""
|
|
1117
|
+
if axis is None:
|
|
1118
|
+
axis = (0, 1)
|
|
1119
|
+
|
|
1120
|
+
if isinstance(axis, str):
|
|
1121
|
+
axis_str = axis
|
|
1122
|
+
if axis == "x":
|
|
1123
|
+
axis = 1
|
|
1124
|
+
elif axis == "y":
|
|
1125
|
+
axis = 0
|
|
1126
|
+
elif axis == "xy":
|
|
1127
|
+
axis = (0, 1)
|
|
1128
|
+
else:
|
|
1129
|
+
if axis == 1:
|
|
1130
|
+
axis_str = "x"
|
|
1131
|
+
elif axis == 0:
|
|
1132
|
+
axis_str = "y"
|
|
1133
|
+
elif axis == (0, 1):
|
|
1134
|
+
axis_str = "xy"
|
|
1135
|
+
|
|
1136
|
+
if stat in NP_OPERATORS:
|
|
1137
|
+
dnew = getattr(np, stat)(self._d, axis=axis)
|
|
1138
|
+
elif stat == "int":
|
|
1139
|
+
dnew = np.sum(self._d, axis=axis)
|
|
1140
|
+
if axis == 1:
|
|
1141
|
+
dd = self._x[1] - self._x[0]
|
|
1142
|
+
elif axis == 0:
|
|
1143
|
+
dd = self._y[1] - self._y[0]
|
|
1144
|
+
elif axis == (0, 1):
|
|
1145
|
+
dd = (self._x[1] - self._x[0]) * (self._y[1] - self._y[0])
|
|
1146
|
+
dnew = dnew * dd
|
|
1147
|
+
|
|
1148
|
+
if axis == 1:
|
|
1149
|
+
# Needed to get correct orinetation as d[0, 0] is the upper corner
|
|
1150
|
+
# of the data, with coordinates x[0], y[-1]
|
|
1151
|
+
dnew = np.flip(dnew, axis=0)
|
|
1152
|
+
|
|
1153
|
+
new_name = self._name + "_" + stat + "_" + axis_str
|
|
1154
|
+
notation = notations.add_operator(self._notation, stat, axis=axis_str)
|
|
1155
|
+
|
|
1156
|
+
if axis == (0, 1):
|
|
1157
|
+
return TemporalResults0D(new_name,
|
|
1158
|
+
dnew,
|
|
1159
|
+
self._t,
|
|
1160
|
+
notation=notation)
|
|
1161
|
+
else:
|
|
1162
|
+
if axis == 0:
|
|
1163
|
+
coords = self._x
|
|
1164
|
+
coords_name = "x"
|
|
1165
|
+
else:
|
|
1166
|
+
coords = self._y
|
|
1167
|
+
coords_name = "y"
|
|
1168
|
+
return TemporalResults1D(new_name,
|
|
1169
|
+
dnew,
|
|
1170
|
+
self._t,
|
|
1171
|
+
coords,
|
|
1172
|
+
coords_name=coords_name,
|
|
1173
|
+
notation=notation)
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def extract_from_time_step(self,
|
|
1177
|
+
time_steps: float | list[float],
|
|
1178
|
+
) -> tilupy.read.StaticResults2D | tilupy.read.TemporalResults2D:
|
|
1179
|
+
"""Extract data from specific time steps.
|
|
1180
|
+
|
|
1181
|
+
Parameters
|
|
1182
|
+
----------
|
|
1183
|
+
time_steps : float | list[float]
|
|
1184
|
+
Value of time steps to extract data.
|
|
1185
|
+
|
|
1186
|
+
Returns
|
|
1187
|
+
-------
|
|
1188
|
+
tilupy.read.StaticResults2D | tilupy.read.TemporalResults2D
|
|
1189
|
+
Extracted data, the type depends on the time step.
|
|
1190
|
+
"""
|
|
1191
|
+
if isinstance(time_steps, float) or isinstance(time_steps, int):
|
|
1192
|
+
t_index = np.argmin(np.abs(self._t - time_steps))
|
|
1193
|
+
|
|
1194
|
+
return StaticResults2D(name=self._name,
|
|
1195
|
+
d=self._d[:, :, t_index],
|
|
1196
|
+
x=self._x,
|
|
1197
|
+
y=self._y,
|
|
1198
|
+
z=self._z,
|
|
1199
|
+
notation=self._notation)
|
|
1200
|
+
|
|
1201
|
+
elif isinstance(time_steps, list):
|
|
1202
|
+
time_steps = np.array(time_steps)
|
|
1203
|
+
|
|
1204
|
+
if isinstance(time_steps, np.ndarray):
|
|
1205
|
+
if isinstance(self._t, list):
|
|
1206
|
+
self._t = np.array(self._t)
|
|
1207
|
+
t_distances = np.abs(self._t[None, :] - time_steps[:, None])
|
|
1208
|
+
t_indexes = np.argmin(t_distances, axis=1)
|
|
1209
|
+
|
|
1210
|
+
return TemporalResults2D(name=self._name,
|
|
1211
|
+
d=self._d[:, :, t_indexes],
|
|
1212
|
+
t=self._t[t_indexes],
|
|
1213
|
+
x=self._x,
|
|
1214
|
+
y=self._y,
|
|
1215
|
+
z=self._z,
|
|
1216
|
+
notation=self._notation)
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
@property
|
|
1220
|
+
def x(self) -> np.ndarray:
|
|
1221
|
+
"""Get X coordinates.
|
|
1222
|
+
|
|
1223
|
+
Returns
|
|
1224
|
+
-------
|
|
1225
|
+
numpy.ndarray
|
|
1226
|
+
Attribute :attr:`_x`.
|
|
1227
|
+
"""
|
|
1228
|
+
return self._x
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
@property
|
|
1232
|
+
def y(self) -> np.ndarray:
|
|
1233
|
+
"""Get Y coordinates.
|
|
1234
|
+
|
|
1235
|
+
Returns
|
|
1236
|
+
-------
|
|
1237
|
+
numpy.ndarray
|
|
1238
|
+
Attribute :attr:`_y`.
|
|
1239
|
+
"""
|
|
1240
|
+
return self._y
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
@property
|
|
1244
|
+
def z(self) -> np.ndarray:
|
|
1245
|
+
"""Get elevations values.
|
|
1246
|
+
|
|
1247
|
+
Returns
|
|
1248
|
+
-------
|
|
1249
|
+
numpy.ndarray
|
|
1250
|
+
Attribute :attr:`_z`.
|
|
1251
|
+
"""
|
|
1252
|
+
return self._z
|
|
1253
|
+
|
|
1254
|
+
|
|
1255
|
+
class StaticResults(AbstractResults):
|
|
1256
|
+
"""Abstract class for result of simulation without time dependence.
|
|
1257
|
+
|
|
1258
|
+
Parameters
|
|
1259
|
+
----------
|
|
1260
|
+
name : str
|
|
1261
|
+
Name of the property.
|
|
1262
|
+
d : numpy.ndarray
|
|
1263
|
+
Values of the property.
|
|
1264
|
+
notation : dict, optional
|
|
1265
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
1266
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
1267
|
+
|
|
1268
|
+
Attributes
|
|
1269
|
+
----------
|
|
1270
|
+
_name : str
|
|
1271
|
+
Name of the property.
|
|
1272
|
+
_d : numpy.ndarray
|
|
1273
|
+
Values of the property.
|
|
1274
|
+
_notation : tilupy.notations.Notation
|
|
1275
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
1276
|
+
"""
|
|
1277
|
+
def __init__(self, name: str, d: np.ndarray, notation: dict=None):
|
|
1278
|
+
super().__init__(name, d, notation=notation)
|
|
1279
|
+
# x and y arrays
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
@abstractmethod
|
|
1283
|
+
def plot(self):
|
|
1284
|
+
"""Abstract method to plot the results."""
|
|
1285
|
+
pass
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
@abstractmethod
|
|
1289
|
+
def save(self):
|
|
1290
|
+
"""Abstract method to save the results."""
|
|
1291
|
+
pass
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
class StaticResults0D(StaticResults):
|
|
1295
|
+
"""
|
|
1296
|
+
Class for simulation results described where the data is one or multiple scalar.
|
|
1297
|
+
|
|
1298
|
+
Parameters
|
|
1299
|
+
----------
|
|
1300
|
+
name : str
|
|
1301
|
+
Name of the property.
|
|
1302
|
+
d : numpy.ndarray
|
|
1303
|
+
Values of the property.
|
|
1304
|
+
notation : dict, optional
|
|
1305
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
1306
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
1307
|
+
|
|
1308
|
+
Attributes
|
|
1309
|
+
----------
|
|
1310
|
+
_name : str
|
|
1311
|
+
Name of the property.
|
|
1312
|
+
_d : numpy.ndarray
|
|
1313
|
+
Values of the property.
|
|
1314
|
+
_notation : tilupy.notations.Notation
|
|
1315
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
1316
|
+
"""
|
|
1317
|
+
def __init__(self, name: str, d: np.ndarray, notation: dict=None):
|
|
1318
|
+
super().__init__(name, d, notation=notation)
|
|
1319
|
+
|
|
1320
|
+
|
|
1321
|
+
class StaticResults1D(StaticResults):
|
|
1322
|
+
"""
|
|
1323
|
+
Class for simulation results described by one dimension for space.
|
|
1324
|
+
|
|
1325
|
+
Parameters
|
|
1326
|
+
----------
|
|
1327
|
+
name : str
|
|
1328
|
+
Name of the property.
|
|
1329
|
+
d : numpy.ndarray
|
|
1330
|
+
Values of the property.
|
|
1331
|
+
coords: numpy.ndarray
|
|
1332
|
+
Spatial coordinates.
|
|
1333
|
+
coords_name: str
|
|
1334
|
+
Spatial coordinates name.
|
|
1335
|
+
notation : dict, optional
|
|
1336
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
1337
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
1338
|
+
|
|
1339
|
+
Attributes
|
|
1340
|
+
----------
|
|
1341
|
+
_name : str
|
|
1342
|
+
Name of the property.
|
|
1343
|
+
_d : numpy.ndarray
|
|
1344
|
+
Values of the property.
|
|
1345
|
+
_notation : tilupy.notations.Notation
|
|
1346
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
1347
|
+
_coords: numpy.ndarray
|
|
1348
|
+
Spatial coordinates.
|
|
1349
|
+
_coords_name: str
|
|
1350
|
+
Spatial coordinates name.
|
|
1351
|
+
"""
|
|
1352
|
+
def __init__(self,
|
|
1353
|
+
name: str,
|
|
1354
|
+
d: np.ndarray,
|
|
1355
|
+
coords: np.ndarray=None,
|
|
1356
|
+
coords_name: list[str]=None,
|
|
1357
|
+
notation: dict=None
|
|
1358
|
+
):
|
|
1359
|
+
super().__init__(name, d, notation=notation)
|
|
1360
|
+
# x and y arrays
|
|
1361
|
+
self._coords = coords
|
|
1362
|
+
self._coords_name = coords_name
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
def plot(self,
|
|
1366
|
+
ax: matplotlib.axes._axes.Axes = None,
|
|
1367
|
+
**kwargs
|
|
1368
|
+
) -> matplotlib.axes._axes.Axes:
|
|
1369
|
+
"""Plot the 1D results.
|
|
1370
|
+
|
|
1371
|
+
Parameters
|
|
1372
|
+
----------
|
|
1373
|
+
ax : matplotlib.axes._axes.Axes, optional
|
|
1374
|
+
Existing matplotlib window, if None create one. By default None.
|
|
1375
|
+
|
|
1376
|
+
Returns
|
|
1377
|
+
-------
|
|
1378
|
+
matplotlib.axes._axes.Axes
|
|
1379
|
+
The created plot.
|
|
1380
|
+
"""
|
|
1381
|
+
if ax is None:
|
|
1382
|
+
fig, ax = plt.subplots(1, 1, layout="constrained")
|
|
1383
|
+
|
|
1384
|
+
if isinstance(self._d, np.ndarray):
|
|
1385
|
+
data = self._d.T
|
|
1386
|
+
else:
|
|
1387
|
+
data = self._d
|
|
1388
|
+
|
|
1389
|
+
if "color" not in kwargs:
|
|
1390
|
+
color = "black"
|
|
1391
|
+
kwargs["color"] = color
|
|
1392
|
+
|
|
1393
|
+
ax.plot(self._coords, data, **kwargs)
|
|
1394
|
+
|
|
1395
|
+
ax.grid(True, alpha=0.3)
|
|
1396
|
+
ax.set_xlim(left=min(self._coords), right=max(self._coords))
|
|
1397
|
+
ax.set_xlabel(notations.get_label(self._coords_name))
|
|
1398
|
+
ax.set_ylabel(notations.get_label(self._notation))
|
|
1399
|
+
|
|
1400
|
+
return ax
|
|
1401
|
+
|
|
1402
|
+
|
|
1403
|
+
@property
|
|
1404
|
+
def coords(self) -> np.ndarray:
|
|
1405
|
+
"""Get spatial coordinates.
|
|
1406
|
+
|
|
1407
|
+
Returns
|
|
1408
|
+
-------
|
|
1409
|
+
numpy.ndarray
|
|
1410
|
+
Attribute :attr:`_coords`
|
|
1411
|
+
"""
|
|
1412
|
+
return self._coords
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
@property
|
|
1416
|
+
def coords_name(self) -> str:
|
|
1417
|
+
"""Get spatial coordinates name.
|
|
1418
|
+
|
|
1419
|
+
Returns
|
|
1420
|
+
-------
|
|
1421
|
+
str
|
|
1422
|
+
Attribute :attr:`_coords_name`
|
|
1423
|
+
"""
|
|
1424
|
+
return self._coords_name
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
class StaticResults2D(StaticResults):
|
|
1428
|
+
"""
|
|
1429
|
+
Class for simulation results described by a two dimensional space result.
|
|
1430
|
+
Inherits from StaticResults.
|
|
1431
|
+
|
|
1432
|
+
Parameters
|
|
1433
|
+
----------
|
|
1434
|
+
name : str
|
|
1435
|
+
Name of the property.
|
|
1436
|
+
d : numpy.ndarray
|
|
1437
|
+
Values of the property.
|
|
1438
|
+
x : numpy.ndarray
|
|
1439
|
+
X coordinate values.
|
|
1440
|
+
y : numpy.ndarray
|
|
1441
|
+
X coordinate values.
|
|
1442
|
+
z : numpy.ndarray
|
|
1443
|
+
Elevation values of the surface.
|
|
1444
|
+
notation : dict, optional
|
|
1445
|
+
Dictionnary of argument for creating an instance of the class :class:`tilupy.notations.Notation`.
|
|
1446
|
+
If None use the function :func:`tilupy.notations.get_notation`. By default None.
|
|
1447
|
+
|
|
1448
|
+
Attributes
|
|
1449
|
+
----------
|
|
1450
|
+
_name : str
|
|
1451
|
+
Name of the property.
|
|
1452
|
+
_d : numpy.ndarray
|
|
1453
|
+
Values of the property.
|
|
1454
|
+
_notation : tilupy.notations.Notation
|
|
1455
|
+
Instance of the class :class:`tilupy.notations.Notation`.
|
|
1456
|
+
_x : numpy.ndarray
|
|
1457
|
+
X coordinate values.
|
|
1458
|
+
_y : numpy.ndarray
|
|
1459
|
+
X coordinate values.
|
|
1460
|
+
_z : numpy.ndarray
|
|
1461
|
+
Elevation values of the surface.
|
|
1462
|
+
"""
|
|
1463
|
+
def __init__(self,
|
|
1464
|
+
name: str,
|
|
1465
|
+
d: np.ndarray,
|
|
1466
|
+
x: np.ndarray=None,
|
|
1467
|
+
y: np.ndarray=None,
|
|
1468
|
+
z: np.ndarray=None,
|
|
1469
|
+
notation: dict=None
|
|
1470
|
+
):
|
|
1471
|
+
super().__init__(name, d, notation=notation)
|
|
1472
|
+
# x and y arrays
|
|
1473
|
+
self._x = x
|
|
1474
|
+
self._y = y
|
|
1475
|
+
# topography
|
|
1476
|
+
self._z = z
|
|
1477
|
+
|
|
1478
|
+
|
|
1479
|
+
def plot(self,
|
|
1480
|
+
figsize: tuple[float] = None,
|
|
1481
|
+
x: np.ndarray = None,
|
|
1482
|
+
y: np.ndarray = None,
|
|
1483
|
+
z: np.ndarray = None,
|
|
1484
|
+
sup_plt_fn: Callable = None,
|
|
1485
|
+
sup_plt_fn_args: dict = None,
|
|
1486
|
+
ax: matplotlib.axes._axes.Axes = None,
|
|
1487
|
+
**kwargs
|
|
1488
|
+
) -> matplotlib.axes._axes.Axes:
|
|
1489
|
+
"""Plot the 2D results using :func:`pytopomap.plot.plot_data_on_topo`.
|
|
1490
|
+
|
|
1491
|
+
Parameters
|
|
1492
|
+
----------
|
|
1493
|
+
figsize : tuple[float], optional
|
|
1494
|
+
Size of the figure, by default None
|
|
1495
|
+
x : numpy.ndarray, optional
|
|
1496
|
+
X coordinate values, if None use :attr:`_x`. By default None.
|
|
1497
|
+
y : numpy.ndarray, optional
|
|
1498
|
+
Y coordinate values, if None use :attr:`_y`. By default None.
|
|
1499
|
+
z : numpy.ndarray, optional
|
|
1500
|
+
Elevation values, if None use :attr:`_z`. By default None.
|
|
1501
|
+
sup_plt_fn : callable, optional
|
|
1502
|
+
A custom function to apply additional plotting on the axes, by default None.
|
|
1503
|
+
sup_plt_fn_args : dict, optional
|
|
1504
|
+
Arguments to pass to :data:`sup_plt_fn`, by default None.
|
|
1505
|
+
ax : matplotlib.axes._axes.Axes, optional
|
|
1506
|
+
Existing matplotlib window, if None create one. By default None
|
|
1507
|
+
kwargs
|
|
1508
|
+
Additional arguments to pass to :func:`pytopomap.plot.plot_data_on_topo`.
|
|
1509
|
+
|
|
1510
|
+
Returns
|
|
1511
|
+
-------
|
|
1512
|
+
matplotlib.axes._axes.Axes
|
|
1513
|
+
The created plot.
|
|
1514
|
+
|
|
1515
|
+
Raises
|
|
1516
|
+
------
|
|
1517
|
+
TypeError
|
|
1518
|
+
If no value for x, y.
|
|
1519
|
+
"""
|
|
1520
|
+
if ax is None:
|
|
1521
|
+
fig, ax = plt.subplots(1, 1, figsize=figsize, layout="constrained")
|
|
1522
|
+
|
|
1523
|
+
if x is None:
|
|
1524
|
+
x = self._x
|
|
1525
|
+
if y is None:
|
|
1526
|
+
y = self._y
|
|
1527
|
+
if z is None:
|
|
1528
|
+
z = self._z
|
|
1529
|
+
|
|
1530
|
+
if x is None or y is None or z is None:
|
|
1531
|
+
raise TypeError("x, y or z data missing")
|
|
1532
|
+
|
|
1533
|
+
if "colorbar_kwargs" not in kwargs:
|
|
1534
|
+
kwargs["colorbar_kwargs"] = dict()
|
|
1535
|
+
if "label" not in kwargs["colorbar_kwargs"]:
|
|
1536
|
+
clabel = notations.get_label(self._notation)
|
|
1537
|
+
kwargs["colorbar_kwargs"]["label"] = clabel
|
|
1538
|
+
|
|
1539
|
+
ax = plt_fn.plot_data_on_topo(x,
|
|
1540
|
+
y,
|
|
1541
|
+
z,
|
|
1542
|
+
self._d,
|
|
1543
|
+
axe=ax,
|
|
1544
|
+
figsize=figsize,
|
|
1545
|
+
**kwargs)
|
|
1546
|
+
if sup_plt_fn is not None:
|
|
1547
|
+
if sup_plt_fn_args is None:
|
|
1548
|
+
sup_plt_fn_args = dict()
|
|
1549
|
+
sup_plt_fn(ax, **sup_plt_fn_args)
|
|
1550
|
+
|
|
1551
|
+
return ax
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
def save(self,
|
|
1555
|
+
folder: str=None,
|
|
1556
|
+
file_name: str=None,
|
|
1557
|
+
fmt: str="txt",
|
|
1558
|
+
x: np.ndarray=None,
|
|
1559
|
+
y: np.ndarray=None,
|
|
1560
|
+
**kwargs
|
|
1561
|
+
) -> None:
|
|
1562
|
+
"""Save the 2D results.
|
|
1563
|
+
|
|
1564
|
+
Parameters
|
|
1565
|
+
----------
|
|
1566
|
+
folder : str, optional
|
|
1567
|
+
Path to the output folder, if None create a folder with :attr:`_name`. By default None.
|
|
1568
|
+
file_name : str, optional
|
|
1569
|
+
Base name for the output image files, if None use :attr:`_name`. By default None.
|
|
1570
|
+
fmt : str, optional
|
|
1571
|
+
File format for saving result, by default "txt".
|
|
1572
|
+
x : np.ndarray, optional
|
|
1573
|
+
X coordinate values, if None use :attr:`_x`. By default None.
|
|
1574
|
+
y : np.ndarray, optional
|
|
1575
|
+
Y coordinate values, if None use :attr:`_y`. By default None.
|
|
1576
|
+
|
|
1577
|
+
Raises
|
|
1578
|
+
------
|
|
1579
|
+
ValueError
|
|
1580
|
+
If no value for x, y.
|
|
1581
|
+
"""
|
|
1582
|
+
if x is None:
|
|
1583
|
+
x = self._x
|
|
1584
|
+
if y is None:
|
|
1585
|
+
y = self._y
|
|
1586
|
+
|
|
1587
|
+
if x is None or y is None:
|
|
1588
|
+
raise ValueError("x et y arrays must not be None")
|
|
1589
|
+
|
|
1590
|
+
if file_name is None:
|
|
1591
|
+
file_name = self._name + "." + fmt
|
|
1592
|
+
|
|
1593
|
+
if folder is not None:
|
|
1594
|
+
file_name = os.path.join(folder, file_name)
|
|
1595
|
+
|
|
1596
|
+
tilupy.raster.write_raster(x, y, self._d, file_name, fmt=fmt, **kwargs)
|
|
1597
|
+
|
|
1598
|
+
|
|
1599
|
+
def get_spatial_stat(self,
|
|
1600
|
+
stat: str,
|
|
1601
|
+
axis=None
|
|
1602
|
+
) -> tilupy.read.StaticResults0D | tilupy.read.StaticResults1D:
|
|
1603
|
+
"""Statistical analysis along spatial dimension for 2D results.
|
|
1604
|
+
|
|
1605
|
+
Parameters
|
|
1606
|
+
----------
|
|
1607
|
+
stat : str
|
|
1608
|
+
Statistical operator to apply. Must be implemented in :data:`NP_OPERATORS`.
|
|
1609
|
+
axis : tuple[int]
|
|
1610
|
+
Axis where to do the analysis:
|
|
1611
|
+
|
|
1612
|
+
- If axis is string, replace 'x' by 1, 'y' by 0 and 'xy' by (0, 1).
|
|
1613
|
+
- If axis is int, only use 0 or 1.
|
|
1614
|
+
- If None use (0, 1). By default None.
|
|
1615
|
+
|
|
1616
|
+
Returns
|
|
1617
|
+
-------
|
|
1618
|
+
tilupy.read.StaticResults0D or tilupy.read.StaticResults1D
|
|
1619
|
+
Instance of :class:`tilupy.read.StaticResults0D` or :class:`tilupy.read.StaticResults1D`.
|
|
1620
|
+
|
|
1621
|
+
"""
|
|
1622
|
+
if axis is None:
|
|
1623
|
+
axis = (0, 1)
|
|
1624
|
+
|
|
1625
|
+
if isinstance(axis, str):
|
|
1626
|
+
axis_str = axis
|
|
1627
|
+
if axis == "x":
|
|
1628
|
+
axis = 1
|
|
1629
|
+
elif axis == "y":
|
|
1630
|
+
axis = 0
|
|
1631
|
+
elif axis == "xy":
|
|
1632
|
+
axis = (0, 1)
|
|
1633
|
+
else:
|
|
1634
|
+
if axis == 1:
|
|
1635
|
+
axis_str = "x"
|
|
1636
|
+
elif axis == 0:
|
|
1637
|
+
axis_str = "y"
|
|
1638
|
+
elif axis == (0, 1):
|
|
1639
|
+
axis_str = "xy"
|
|
1640
|
+
|
|
1641
|
+
if stat in NP_OPERATORS:
|
|
1642
|
+
dnew = getattr(np, stat)(self._d, axis=axis)
|
|
1643
|
+
elif stat == "int":
|
|
1644
|
+
dnew = np.sum(self._d, axis=axis)
|
|
1645
|
+
if axis == 1:
|
|
1646
|
+
dd = self._x[1] - self._x[0]
|
|
1647
|
+
elif axis == 0:
|
|
1648
|
+
dd = self._y[1] - self._y[0]
|
|
1649
|
+
elif axis == (0, 1):
|
|
1650
|
+
dd = (self._x[1] - self._x[0]) * (self._y[1] - self._y[0])
|
|
1651
|
+
dnew = dnew * dd
|
|
1652
|
+
|
|
1653
|
+
if axis == 1:
|
|
1654
|
+
# Needed to get correct orinetation as d[0, 0] is the upper corner
|
|
1655
|
+
# of the data, with coordinates x[0], y[-1]
|
|
1656
|
+
dnew = np.flip(dnew, axis=0)
|
|
1657
|
+
|
|
1658
|
+
new_name = self._name + "_" + stat + "_" + axis_str
|
|
1659
|
+
notation = notations.add_operator(self._notation, stat, axis=axis_str)
|
|
1660
|
+
|
|
1661
|
+
if axis == (0, 1):
|
|
1662
|
+
return StaticResults0D(new_name,
|
|
1663
|
+
dnew,
|
|
1664
|
+
notation=notation)
|
|
1665
|
+
else:
|
|
1666
|
+
if axis == 0:
|
|
1667
|
+
coords = self._x
|
|
1668
|
+
coords_name = "x"
|
|
1669
|
+
else:
|
|
1670
|
+
coords = self._y
|
|
1671
|
+
coords_name = "y"
|
|
1672
|
+
return StaticResults1D(new_name,
|
|
1673
|
+
dnew,
|
|
1674
|
+
coords,
|
|
1675
|
+
coords_name=coords_name,
|
|
1676
|
+
notation=notation,)
|
|
1677
|
+
|
|
1678
|
+
|
|
1679
|
+
@property
|
|
1680
|
+
def x(self) -> np.ndarray:
|
|
1681
|
+
"""Get X coordinates.
|
|
1682
|
+
|
|
1683
|
+
Returns
|
|
1684
|
+
-------
|
|
1685
|
+
numpy.ndarray
|
|
1686
|
+
Attribute :attr:`_x`.
|
|
1687
|
+
"""
|
|
1688
|
+
return self._x
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
@property
|
|
1692
|
+
def y(self) -> np.ndarray:
|
|
1693
|
+
"""Get Y coordinates.
|
|
1694
|
+
|
|
1695
|
+
Returns
|
|
1696
|
+
-------
|
|
1697
|
+
numpy.ndarray
|
|
1698
|
+
Attribute :attr:`_y`.
|
|
1699
|
+
"""
|
|
1700
|
+
return self._y
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
@property
|
|
1704
|
+
def z(self) -> np.ndarray:
|
|
1705
|
+
"""Get elevations values.
|
|
1706
|
+
|
|
1707
|
+
Returns
|
|
1708
|
+
-------
|
|
1709
|
+
numpy.ndarray
|
|
1710
|
+
Attribute :attr:`_z`.
|
|
1711
|
+
"""
|
|
1712
|
+
return self._z
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
class Results:
|
|
1716
|
+
"""Results of thin-layer model simulation
|
|
1717
|
+
|
|
1718
|
+
This class is the parent class for all simulation results, whatever the
|
|
1719
|
+
kind of input data. Methods and functions for processing results are given
|
|
1720
|
+
here. Reading results from code specific outputs is done in inhereited
|
|
1721
|
+
classes.
|
|
1722
|
+
|
|
1723
|
+
This class has global attributes used by all child classes and quick access
|
|
1724
|
+
attributes calculated and stored for easier access to the main results of a
|
|
1725
|
+
simulation. The quick attributes are only computed if needed and can be deleted
|
|
1726
|
+
to clean memory.
|
|
1727
|
+
|
|
1728
|
+
Parameters
|
|
1729
|
+
----------
|
|
1730
|
+
args and kwargs :
|
|
1731
|
+
Specific arguments for each models.
|
|
1732
|
+
|
|
1733
|
+
Attributes
|
|
1734
|
+
----------
|
|
1735
|
+
_code : str
|
|
1736
|
+
Name of the code that generated the result.
|
|
1737
|
+
_folder : str
|
|
1738
|
+
Path to find code files (like parameters).
|
|
1739
|
+
_folder_output :
|
|
1740
|
+
Path to find the results of the code.
|
|
1741
|
+
_zinit : numpy.ndarray
|
|
1742
|
+
Surface elevation of the simulation.
|
|
1743
|
+
_tim : list
|
|
1744
|
+
Lists of recorded time steps.
|
|
1745
|
+
_x : numpy.ndarray
|
|
1746
|
+
X-coordinates of the simulation.
|
|
1747
|
+
_y : numpy.ndarray
|
|
1748
|
+
Y-coordinates of the simulation.
|
|
1749
|
+
_dx : float
|
|
1750
|
+
Cell size along X-coordinates.
|
|
1751
|
+
_dy : float
|
|
1752
|
+
Cell size along Y-coordinates.
|
|
1753
|
+
_nx : float
|
|
1754
|
+
Number of cells along X-coordinates.
|
|
1755
|
+
_ny : float
|
|
1756
|
+
Number of cells along Y-coordinates.
|
|
1757
|
+
|
|
1758
|
+
_h : tilupy.read.TemporalResults2D
|
|
1759
|
+
Quick access attributes for fluid height over time.
|
|
1760
|
+
_h_max : tilupy.read.TemporalResults0D
|
|
1761
|
+
Quick access attributes for max fluid hieght over time.
|
|
1762
|
+
_u : tilupy.read.TemporalResults2D
|
|
1763
|
+
Quick access attributes for norm of fluid velocity over time.
|
|
1764
|
+
_u_max : tilupy.read.TemporalResults0D
|
|
1765
|
+
Quick access attributes for max norm of fluid velocity over time.
|
|
1766
|
+
_costh : numpy.ndarray
|
|
1767
|
+
Quick access attributes for value of cos[theta] at any point on the surface.
|
|
1768
|
+
"""
|
|
1769
|
+
def __init__(self, *args, **kwargs):
|
|
1770
|
+
self._h = None
|
|
1771
|
+
self._h_max = None
|
|
1772
|
+
self._u = None
|
|
1773
|
+
self._u_max = None
|
|
1774
|
+
self._costh = None
|
|
1775
|
+
|
|
1776
|
+
self._code = None
|
|
1777
|
+
self._folder = None
|
|
1778
|
+
self._folder_output = None
|
|
1779
|
+
self._z = None
|
|
1780
|
+
self._zinit = None
|
|
1781
|
+
self._tim = None
|
|
1782
|
+
self._x = None
|
|
1783
|
+
self._y = None
|
|
1784
|
+
self._dx = None
|
|
1785
|
+
self._dy = None
|
|
1786
|
+
self._nx = None
|
|
1787
|
+
self._ny = None
|
|
1788
|
+
|
|
1789
|
+
|
|
1790
|
+
def compute_costh(self) -> np.ndarray:
|
|
1791
|
+
"""Get cos(slope) of topography.
|
|
1792
|
+
|
|
1793
|
+
Returns
|
|
1794
|
+
-------
|
|
1795
|
+
numpy.ndarray
|
|
1796
|
+
Value of cos[theta] at any point on the surface.
|
|
1797
|
+
"""
|
|
1798
|
+
[Fx, Fy] = np.gradient(self._zinit, np.flip(self._y), self._x)
|
|
1799
|
+
costh = 1 / np.sqrt(1 + Fx**2 + Fy**2)
|
|
1800
|
+
return costh
|
|
1801
|
+
|
|
1802
|
+
|
|
1803
|
+
def center_of_mass(self, h_thresh: float=None) -> tilupy.read.TemporalResults0D:
|
|
1804
|
+
"""Compute center of mass coordinates depending on time.
|
|
1805
|
+
|
|
1806
|
+
Parameters
|
|
1807
|
+
----------
|
|
1808
|
+
h_thresh : float, optional
|
|
1809
|
+
Value of threshold for the flow height, by default None.
|
|
1810
|
+
|
|
1811
|
+
Returns
|
|
1812
|
+
-------
|
|
1813
|
+
tilupy.read.TemporalResults0D
|
|
1814
|
+
Values of center of mass coordinates.
|
|
1815
|
+
"""
|
|
1816
|
+
dx = self._x[1] - self._x[0]
|
|
1817
|
+
dy = self._y[1] - self._y[0]
|
|
1818
|
+
# Make meshgrid
|
|
1819
|
+
X, Y = np.meshgrid(self._x, np.flip(self._y))
|
|
1820
|
+
|
|
1821
|
+
if self._h is None:
|
|
1822
|
+
self.h
|
|
1823
|
+
|
|
1824
|
+
# Weights for coordinates average (volume in cell / total volume)
|
|
1825
|
+
h2 = self._h.copy()
|
|
1826
|
+
if h_thresh is not None:
|
|
1827
|
+
h2[h2 < h_thresh] = 0
|
|
1828
|
+
if self._costh is None:
|
|
1829
|
+
self._costh = self.compute_costh()
|
|
1830
|
+
w = h2 / self._costh[:, :, np.newaxis] * dx * dy
|
|
1831
|
+
vol = np.nansum(w, axis=(0, 1))
|
|
1832
|
+
w = w / vol[np.newaxis, np.newaxis, :]
|
|
1833
|
+
# Compute center of mass coordinates
|
|
1834
|
+
nt = h2.shape[2]
|
|
1835
|
+
coord = np.zeros((3, nt))
|
|
1836
|
+
tmp = X[:, :, np.newaxis] * w
|
|
1837
|
+
coord[0, :] = np.nansum(tmp, axis=(0, 1))
|
|
1838
|
+
tmp = Y[:, :, np.newaxis] * w
|
|
1839
|
+
coord[1, :] = np.nansum(tmp, axis=(0, 1))
|
|
1840
|
+
tmp = self._zinit[:, :, np.newaxis] * w
|
|
1841
|
+
coord[2, :] = np.nansum(tmp, axis=(0, 1))
|
|
1842
|
+
|
|
1843
|
+
# Make TemporalResults
|
|
1844
|
+
res = TemporalResults0D("centermass",
|
|
1845
|
+
coord,
|
|
1846
|
+
self._tim,
|
|
1847
|
+
scalar_names=["X", "Y", "z"],
|
|
1848
|
+
notation=None)
|
|
1849
|
+
return res
|
|
1850
|
+
|
|
1851
|
+
|
|
1852
|
+
def volume(self, h_thresh: float=None) -> tilupy.read.TemporalResults0D:
|
|
1853
|
+
"""Compute flow volume depending on time.
|
|
1854
|
+
|
|
1855
|
+
Parameters
|
|
1856
|
+
----------
|
|
1857
|
+
h_thresh : float, optional
|
|
1858
|
+
Value of threshold for the flow height, by default None.
|
|
1859
|
+
|
|
1860
|
+
Returns
|
|
1861
|
+
-------
|
|
1862
|
+
tilupy.read.TemporalResults0D
|
|
1863
|
+
Values of flow volumes.
|
|
1864
|
+
"""
|
|
1865
|
+
dx = self._x[1] - self._x[0]
|
|
1866
|
+
dy = self._y[1] - self._y[0]
|
|
1867
|
+
|
|
1868
|
+
if self._h is None:
|
|
1869
|
+
self._h = self.get_output("h").d
|
|
1870
|
+
h2 = self._h.copy()
|
|
1871
|
+
if h_thresh is not None:
|
|
1872
|
+
h2[h2 < h_thresh] = 0
|
|
1873
|
+
if self._costh is None:
|
|
1874
|
+
self._costh = self.compute_costh()
|
|
1875
|
+
w = h2 / self._costh[:, :, np.newaxis] * dx * dy
|
|
1876
|
+
vol = np.nansum(w, axis=(0, 1))
|
|
1877
|
+
res = TemporalResults0D("volume",
|
|
1878
|
+
vol,
|
|
1879
|
+
self._tim,
|
|
1880
|
+
notation=None)
|
|
1881
|
+
return res
|
|
1882
|
+
|
|
1883
|
+
|
|
1884
|
+
def get_output(self,
|
|
1885
|
+
output_name: str,
|
|
1886
|
+
from_file: bool=True,
|
|
1887
|
+
**kwargs
|
|
1888
|
+
) -> tilupy.read.TemporalResults0D | tilupy.read.StaticResults2D | tilupy.read.TemporalResults2D:
|
|
1889
|
+
"""Get all the available outputs for a simulation :
|
|
1890
|
+
- Topographic outputs : "z", "zinit", "costh"
|
|
1891
|
+
- Temporal 2D outputs : "hvert", "h", "u", "ux", "uy", "hu", "hu2"
|
|
1892
|
+
- Other outputs : "centermass", "volume"
|
|
1893
|
+
|
|
1894
|
+
It is possible to add operators to temporal 2D outputs :
|
|
1895
|
+
- "max", "mean", "std", "sum", "min", "final", "init", "int"
|
|
1896
|
+
|
|
1897
|
+
And it is possible to add axis (only if using operators) :
|
|
1898
|
+
- "x", "y", "xy"
|
|
1899
|
+
|
|
1900
|
+
Parameters
|
|
1901
|
+
----------
|
|
1902
|
+
output_name : str
|
|
1903
|
+
Name of the wanted output, composed of the output name and potentially
|
|
1904
|
+
an operator and an axis: :data:`output_operator_axis`.
|
|
1905
|
+
from_file : bool, optional
|
|
1906
|
+
If True, find the output in a specific file. By default True.
|
|
1907
|
+
|
|
1908
|
+
Returns
|
|
1909
|
+
-------
|
|
1910
|
+
tilupy.read.TemporalResults0D, tilupy.read.StaticResults2D or tilupy.read.TemporalResults2D
|
|
1911
|
+
Wanted output.
|
|
1912
|
+
"""
|
|
1913
|
+
# Specific case of center of mass
|
|
1914
|
+
if output_name == "centermass":
|
|
1915
|
+
return self.center_of_mass(**kwargs)
|
|
1916
|
+
|
|
1917
|
+
# Specific case of volume
|
|
1918
|
+
if output_name == "volume":
|
|
1919
|
+
return self.volume(**kwargs)
|
|
1920
|
+
|
|
1921
|
+
strs = output_name.split("_")
|
|
1922
|
+
n_strs = len(strs)
|
|
1923
|
+
|
|
1924
|
+
res = None
|
|
1925
|
+
|
|
1926
|
+
# get topography
|
|
1927
|
+
if output_name in TOPO_DATA_2D:
|
|
1928
|
+
if output_name == "z":
|
|
1929
|
+
output_name = "zinit"
|
|
1930
|
+
output_name = '_' + output_name
|
|
1931
|
+
res = StaticResults2D(output_name,
|
|
1932
|
+
getattr(self, output_name),
|
|
1933
|
+
x=self._x,
|
|
1934
|
+
y=self._y,
|
|
1935
|
+
z=self._z,
|
|
1936
|
+
notation=None)
|
|
1937
|
+
return res
|
|
1938
|
+
|
|
1939
|
+
# If no operator is called, call directly extract_output
|
|
1940
|
+
if n_strs == 1:
|
|
1941
|
+
res = self._extract_output(output_name, **kwargs)
|
|
1942
|
+
return res
|
|
1943
|
+
|
|
1944
|
+
# Otherwise, get name, operator and axis (optional)
|
|
1945
|
+
name = strs[0]
|
|
1946
|
+
operator = strs[1]
|
|
1947
|
+
axis = None
|
|
1948
|
+
if n_strs == 3 :
|
|
1949
|
+
axis = strs[2]
|
|
1950
|
+
|
|
1951
|
+
# If processed output is read directly from file, call the child method
|
|
1952
|
+
# read_from_file.
|
|
1953
|
+
if from_file:
|
|
1954
|
+
try:
|
|
1955
|
+
res = self._read_from_file(name, operator, axis=axis, **kwargs)
|
|
1956
|
+
if res is None:
|
|
1957
|
+
raise UserWarning(f"{output_name} not found with _read_from_file for {self._code}, use get_spatial_stat")
|
|
1958
|
+
elif isinstance(res, str):
|
|
1959
|
+
raise UserWarning(res)
|
|
1960
|
+
except UserWarning as w:
|
|
1961
|
+
print(f"[WARNING] {w}")
|
|
1962
|
+
res = None
|
|
1963
|
+
# res is None in case of function failure
|
|
1964
|
+
|
|
1965
|
+
# If no results could be read from file, output must be
|
|
1966
|
+
# processed by tilupy
|
|
1967
|
+
if res is None:
|
|
1968
|
+
# Get output from name
|
|
1969
|
+
res = self._extract_output(name, x=self._x, y=self._y, **kwargs)
|
|
1970
|
+
if axis is None:
|
|
1971
|
+
# If no axis is given, the operator operates over time by
|
|
1972
|
+
# default
|
|
1973
|
+
res = res.get_temporal_stat(operator)
|
|
1974
|
+
else:
|
|
1975
|
+
if axis == "t":
|
|
1976
|
+
res = res.get_temporal_stat(operator)
|
|
1977
|
+
else:
|
|
1978
|
+
res = res.get_spatial_stat(operator, axis=axis)
|
|
1979
|
+
|
|
1980
|
+
return res
|
|
1981
|
+
|
|
1982
|
+
|
|
1983
|
+
def clear_quick_results(self) -> None:
|
|
1984
|
+
"""Clear memory by erasing quick access attributes: :attr:`_h`, :attr:`_h_max`, :attr:`_u`, :attr:`_u_max`, :attr:`_costh`.
|
|
1985
|
+
"""
|
|
1986
|
+
self._h = None
|
|
1987
|
+
self._h_max = None
|
|
1988
|
+
self._u = None
|
|
1989
|
+
self._u_max = None
|
|
1990
|
+
self._costh = None
|
|
1991
|
+
|
|
1992
|
+
|
|
1993
|
+
def get_profile(self,
|
|
1994
|
+
output: str,
|
|
1995
|
+
extraction_method: str = "axis",
|
|
1996
|
+
**extraction_params
|
|
1997
|
+
) -> tuple[tilupy.read.TemporalResults1D | tilupy.read.StaticResults1D, np.ndarray]:
|
|
1998
|
+
"""Extract a profile from a 2D data.
|
|
1999
|
+
|
|
2000
|
+
Parameters
|
|
2001
|
+
----------
|
|
2002
|
+
output : str
|
|
2003
|
+
Wanted data output.
|
|
2004
|
+
extraction_mode : str, optional
|
|
2005
|
+
Method to extract profiles:
|
|
2006
|
+
|
|
2007
|
+
- "axis": Extracts a profile along an axis.
|
|
2008
|
+
- "coordinates": Extracts a profile along specified coordinates.
|
|
2009
|
+
- "shapefile": Extracts a profile along a shapefile (polylines).
|
|
2010
|
+
|
|
2011
|
+
Be default "axis".
|
|
2012
|
+
extraction_params : dict, optional
|
|
2013
|
+
Different parameters to be entered depending on the extraction method chosen.
|
|
2014
|
+
See :meth:`tilupy.utils.get_profile`.
|
|
2015
|
+
|
|
2016
|
+
Returns
|
|
2017
|
+
-------
|
|
2018
|
+
tuple[tilupy.read.TemporalResults1D | tilupy.read.StaticResults1D, np.ndarray]
|
|
2019
|
+
profile : tilupy.read.TemporalResults1D | tilupy.read.StaticResults1D
|
|
2020
|
+
Extracted profile.
|
|
2021
|
+
data : numpy.ndarray or float or tuple[nympy.ndarray, nympy.ndarray]
|
|
2022
|
+
Specific output depending on :data:`extraction_mode`:
|
|
2023
|
+
|
|
2024
|
+
- If :data:`extraction_mode == "axis"`: float
|
|
2025
|
+
Position of the profile.
|
|
2026
|
+
- If :data:`extraction_mode == "coordinates"`: tuple[numpy.ndarray]
|
|
2027
|
+
X coordinates, Y coordinates and distance values.
|
|
2028
|
+
- If :data:`extraction_mode == "shapefile"`: numpy.ndarray
|
|
2029
|
+
Distance values.
|
|
2030
|
+
|
|
2031
|
+
Raises
|
|
2032
|
+
------
|
|
2033
|
+
ValueError
|
|
2034
|
+
If :data:`output` doesn't generate a 2D data.
|
|
2035
|
+
"""
|
|
2036
|
+
data = self.get_output(output)
|
|
2037
|
+
|
|
2038
|
+
if not isinstance(data, tilupy.read.TemporalResults2D) and not isinstance(data, tilupy.read.StaticResults2D):
|
|
2039
|
+
raise ValueError("Can only extract profile from 2D data.")
|
|
2040
|
+
|
|
2041
|
+
profile, data = utils.get_profile(data, extraction_method, **extraction_params)
|
|
2042
|
+
|
|
2043
|
+
return profile, data
|
|
2044
|
+
|
|
2045
|
+
|
|
2046
|
+
def plot(self,
|
|
2047
|
+
output: str,
|
|
2048
|
+
from_file: bool =True, #get_output
|
|
2049
|
+
h_thresh: float=None, #get_output
|
|
2050
|
+
time_steps: float | list[float] = None,
|
|
2051
|
+
save: bool = False,
|
|
2052
|
+
folder_out: str = None,
|
|
2053
|
+
dpi: int = 150,
|
|
2054
|
+
fmt: str="png",
|
|
2055
|
+
file_suffix: str = None,
|
|
2056
|
+
file_prefix: str = None,
|
|
2057
|
+
display_plot: bool = True,
|
|
2058
|
+
**plot_kwargs
|
|
2059
|
+
) -> matplotlib.axes._axes.Axes:
|
|
2060
|
+
"""Plot output extracted from model's result.
|
|
2061
|
+
|
|
2062
|
+
Parameters
|
|
2063
|
+
----------
|
|
2064
|
+
output : str
|
|
2065
|
+
Wanted output to be plotted. Must be in :data:`DATA_NAMES`.
|
|
2066
|
+
from_file : bool, optional
|
|
2067
|
+
If True, find the output in a specific file. By default True.
|
|
2068
|
+
h_thresh : float, optional
|
|
2069
|
+
Threshold value to be taken into account when extracting output, by default None.
|
|
2070
|
+
time_steps : float or list[float], optional
|
|
2071
|
+
Time steps to show when plotting temporal data. If None shows every time
|
|
2072
|
+
steps recorded. By default None.
|
|
2073
|
+
save : bool, optional
|
|
2074
|
+
If True, save the plot as an image to the computer, by default False.
|
|
2075
|
+
folder_out : str, optional
|
|
2076
|
+
Path to the folder where to save the plot, by default None.
|
|
2077
|
+
dpi : int, optional
|
|
2078
|
+
Resolution for the saved plot, by default 150.
|
|
2079
|
+
fmt : str, optional
|
|
2080
|
+
Format of the saved plot, by default "png".
|
|
2081
|
+
file_suffix : str, optional
|
|
2082
|
+
Suffix to add to the file name when saving, by default None.
|
|
2083
|
+
file_prefix : str, optional
|
|
2084
|
+
Prefix to add to the file name when saving, by default None.
|
|
2085
|
+
display_plot : bool, optional
|
|
2086
|
+
If True, enables the display of the plot; otherwise, it disables the display to save memory.
|
|
2087
|
+
By default True.
|
|
2088
|
+
|
|
2089
|
+
Returns
|
|
2090
|
+
-------
|
|
2091
|
+
matplotlib.axes._axes.Axes
|
|
2092
|
+
Wanted plot.
|
|
2093
|
+
"""
|
|
2094
|
+
if not display_plot:
|
|
2095
|
+
backend = plt.get_backend()
|
|
2096
|
+
plt.close("all")
|
|
2097
|
+
plt.switch_backend("Agg")
|
|
2098
|
+
|
|
2099
|
+
if output in ["z", "zinit", "z_init"]:
|
|
2100
|
+
topo_kwargs = dict()
|
|
2101
|
+
if "topo_kwargs" in plot_kwargs:
|
|
2102
|
+
topo_kwargs = plot_kwargs["topo_kwargs"]
|
|
2103
|
+
axe = plt_fn.plot_topo(self._zinit, self._x, self._y, **topo_kwargs)
|
|
2104
|
+
return axe
|
|
2105
|
+
|
|
2106
|
+
data = self.get_output(output, from_file=from_file, h_thresh=h_thresh)
|
|
2107
|
+
|
|
2108
|
+
add_time_on_plot = False
|
|
2109
|
+
if (isinstance(time_steps, float) or isinstance(time_steps, int)) and isinstance(data, tilupy.read.TemporalResults):
|
|
2110
|
+
t_index = np.argmin(np.abs(self._tim - time_steps))
|
|
2111
|
+
add_time_on_plot = self._tim[t_index]
|
|
2112
|
+
|
|
2113
|
+
if time_steps is not None and isinstance(data, tilupy.read.TemporalResults):
|
|
2114
|
+
data = data.extract_from_time_step(time_steps)
|
|
2115
|
+
|
|
2116
|
+
if save:
|
|
2117
|
+
if folder_out is None:
|
|
2118
|
+
assert (self._folder_output is not None), "folder_output attribute must be set"
|
|
2119
|
+
folder_out = os.path.join(self._folder_output, "plots")
|
|
2120
|
+
os.makedirs(folder_out, exist_ok=True)
|
|
2121
|
+
|
|
2122
|
+
# TODO
|
|
2123
|
+
# Edit Temporal/Static.plot() pour que la sauvegarde soit directement intégrer dans les méthodes plots
|
|
2124
|
+
# Dans les plots, modifier les fonctions pour appeler des méthodes de pytopomap selon le plot voulu
|
|
2125
|
+
# (shotgater, profil, surface, etc...) et créer une fonction globale qui appelle chaque sous méthode
|
|
2126
|
+
# pour créer les graphes et gérer la sauvegarde
|
|
2127
|
+
if folder_out is not None and isinstance(data, TemporalResults2D):
|
|
2128
|
+
# If data is TemporalResults2D then saving is managed directly
|
|
2129
|
+
# by the associated plot method
|
|
2130
|
+
plot_kwargs["folder_out"] = folder_out
|
|
2131
|
+
plot_kwargs["dpi"] = dpi
|
|
2132
|
+
plot_kwargs["fmt"] = fmt
|
|
2133
|
+
# kwargs["file_suffix"] = file_prefix
|
|
2134
|
+
# kwargs["file_prefix"] = file_prefix
|
|
2135
|
+
|
|
2136
|
+
axe = data.plot(**plot_kwargs)
|
|
2137
|
+
|
|
2138
|
+
if add_time_on_plot:
|
|
2139
|
+
axe.set_title(f"t={add_time_on_plot}s", loc="left")
|
|
2140
|
+
|
|
2141
|
+
if folder_out is not None and not isinstance(data, TemporalResults2D):
|
|
2142
|
+
file_name = output
|
|
2143
|
+
if file_suffix is not None:
|
|
2144
|
+
file_name = file_name + "_" + file_suffix
|
|
2145
|
+
if file_prefix is not None:
|
|
2146
|
+
file_name = file_prefix + "_" + file_name
|
|
2147
|
+
file_out = os.path.join(folder_out, file_name + "." + fmt)
|
|
2148
|
+
# axe.figure.tight_layout(pad=0.1)
|
|
2149
|
+
axe.figure.savefig(file_out, dpi=dpi, bbox_inches="tight", pad_inches=0.05)
|
|
2150
|
+
|
|
2151
|
+
if not display_plot:
|
|
2152
|
+
plt.close("all")
|
|
2153
|
+
plt.switch_backend(backend)
|
|
2154
|
+
|
|
2155
|
+
return axe
|
|
2156
|
+
|
|
2157
|
+
|
|
2158
|
+
def plot_profile(self,
|
|
2159
|
+
output: str,
|
|
2160
|
+
from_file: bool=True,
|
|
2161
|
+
extraction_method: str = "axis",
|
|
2162
|
+
extraction_params: dict = None,
|
|
2163
|
+
time_steps: float | list[float] = None,
|
|
2164
|
+
save: bool = False,
|
|
2165
|
+
folder_out: str = None,
|
|
2166
|
+
display_plot: bool = True,
|
|
2167
|
+
**plot_kwargs
|
|
2168
|
+
) -> matplotlib.axes._axes.Axes:
|
|
2169
|
+
"""Plot a 1D output extracted from a 2D output.
|
|
2170
|
+
|
|
2171
|
+
Parameters
|
|
2172
|
+
----------
|
|
2173
|
+
output : str
|
|
2174
|
+
Wanted 2D output to extract the profile from. Must be in :data:`STATIC_DATA_2D`
|
|
2175
|
+
or in :data:`TEMPORAL_DATA_2D`.
|
|
2176
|
+
from_file : bool, optional
|
|
2177
|
+
If True, find the output in a specific file. By default True.
|
|
2178
|
+
extraction_mode : str, optional
|
|
2179
|
+
Method to extract profiles:
|
|
2180
|
+
|
|
2181
|
+
- "axis": Extracts a profile along an axis.
|
|
2182
|
+
- "coordinates": Extracts a profile along specified coordinates.
|
|
2183
|
+
- "shapefile": Extracts a profile along a shapefile (polylines).
|
|
2184
|
+
Be default "axis".
|
|
2185
|
+
extraction_params : dict, optional
|
|
2186
|
+
Different parameters to be entered depending on the extraction method chosen.
|
|
2187
|
+
See :meth:`tilupy.utils.get_profile`.
|
|
2188
|
+
time_steps : float or list[float], optional
|
|
2189
|
+
Time steps to show when plotting temporal data. If None shows every time
|
|
2190
|
+
steps recorded. By default None.
|
|
2191
|
+
save : bool, optional
|
|
2192
|
+
If True, save the plot as an image to the computer, by default False.
|
|
2193
|
+
folder_out : str, optional
|
|
2194
|
+
Path to the folder where to save the plot, by default None.
|
|
2195
|
+
display_plot : bool, optional
|
|
2196
|
+
If True, enables the display of the plot; otherwise, it disables the display to save memory.
|
|
2197
|
+
By default True.
|
|
2198
|
+
|
|
2199
|
+
Returns
|
|
2200
|
+
-------
|
|
2201
|
+
matplotlib.axes._axes.Axes
|
|
2202
|
+
Wanted plot.
|
|
2203
|
+
|
|
2204
|
+
Raises
|
|
2205
|
+
------
|
|
2206
|
+
ValueError
|
|
2207
|
+
If the :data:`output` is not a 2D output.
|
|
2208
|
+
"""
|
|
2209
|
+
if not display_plot:
|
|
2210
|
+
backend = plt.get_backend()
|
|
2211
|
+
plt.close("all")
|
|
2212
|
+
plt.switch_backend("Agg")
|
|
2213
|
+
|
|
2214
|
+
data = self.get_output(output, from_file=from_file)
|
|
2215
|
+
|
|
2216
|
+
if not isinstance(data, tilupy.read.TemporalResults2D) and not isinstance(data, tilupy.read.StaticResults2D):
|
|
2217
|
+
raise ValueError("Can only extract profile from 2D data.")
|
|
2218
|
+
|
|
2219
|
+
extraction_params = {} if extraction_params is None else extraction_params
|
|
2220
|
+
|
|
2221
|
+
profile, _ = utils.get_profile(data, extraction_method, **extraction_params)
|
|
2222
|
+
closest_value = False
|
|
2223
|
+
|
|
2224
|
+
if (isinstance(time_steps, float) or isinstance(time_steps, int)) and isinstance(data, tilupy.read.TemporalResults):
|
|
2225
|
+
t_index = np.argmin(np.abs(self._tim - time_steps))
|
|
2226
|
+
closest_value = self._tim[t_index]
|
|
2227
|
+
|
|
2228
|
+
if time_steps is not None and isinstance(data, tilupy.read.TemporalResults):
|
|
2229
|
+
profile = profile.extract_from_time_step(time_steps)
|
|
2230
|
+
|
|
2231
|
+
axe = profile.plot(**plot_kwargs)
|
|
2232
|
+
|
|
2233
|
+
if closest_value:
|
|
2234
|
+
axe.set_title(f"t={closest_value}s", loc="left")
|
|
2235
|
+
|
|
2236
|
+
if save:
|
|
2237
|
+
if folder_out is None:
|
|
2238
|
+
assert (self._folder_output is not None), "folder_output attribute must be set"
|
|
2239
|
+
folder_out = os.path.join(self._folder_output, "plots")
|
|
2240
|
+
os.makedirs(folder_out, exist_ok=True)
|
|
2241
|
+
|
|
2242
|
+
# TODO
|
|
2243
|
+
# Same as plot() -> add save mode in plot functions in Temporal/StaticResults
|
|
2244
|
+
'''
|
|
2245
|
+
if folder_out is not None and isinstance(data, TemporalResults2D):
|
|
2246
|
+
# If data is TemporalResults2D then saving is managed directly
|
|
2247
|
+
# by the associated plot method
|
|
2248
|
+
kwargs["folder_out"] = folder_out
|
|
2249
|
+
kwargs["dpi"] = dpi
|
|
2250
|
+
kwargs["fmt"] = fmt
|
|
2251
|
+
# kwargs["file_suffix"] = file_prefix
|
|
2252
|
+
# kwargs["file_prefix"] = file_prefix
|
|
2253
|
+
|
|
2254
|
+
|
|
2255
|
+
|
|
2256
|
+
if folder_out is not None and not isinstance(data, TemporalResults2D):
|
|
2257
|
+
file_name = output_name
|
|
2258
|
+
if file_suffix is not None:
|
|
2259
|
+
file_name = file_name + "_" + file_suffix
|
|
2260
|
+
if file_prefix is not None:
|
|
2261
|
+
file_name = file_prefix + "_" + file_name
|
|
2262
|
+
file_out = os.path.join(folder_out, file_name + "." + fmt)
|
|
2263
|
+
# axe.figure.tight_layout(pad=0.1)
|
|
2264
|
+
axe.figure.savefig(file_out, dpi=dpi, bbox_inches="tight", pad_inches=0.05)
|
|
2265
|
+
'''
|
|
2266
|
+
|
|
2267
|
+
if not display_plot:
|
|
2268
|
+
plt.close("all")
|
|
2269
|
+
plt.switch_backend(backend)
|
|
2270
|
+
|
|
2271
|
+
return axe
|
|
2272
|
+
|
|
2273
|
+
|
|
2274
|
+
def save(self,
|
|
2275
|
+
output_name: str,
|
|
2276
|
+
folder_out: str=None,
|
|
2277
|
+
file_name: str=None,
|
|
2278
|
+
fmt: str="txt",
|
|
2279
|
+
from_file: bool=True,
|
|
2280
|
+
**kwargs
|
|
2281
|
+
) -> None:
|
|
2282
|
+
"""Save simulation outputs (processed results or topographic data) to disk.
|
|
2283
|
+
|
|
2284
|
+
Depending on the requested output_name, the method either:
|
|
2285
|
+
|
|
2286
|
+
- Retrieves a result via :meth:`get_output` and calls its own :meth:`save` method,
|
|
2287
|
+
- Or, for static topography data, writes it directly to a raster file.
|
|
2288
|
+
|
|
2289
|
+
Parameters
|
|
2290
|
+
----------
|
|
2291
|
+
output_name : str
|
|
2292
|
+
Name of the variable or processed result to save
|
|
2293
|
+
(e.g., "h", "u_mean_t", "centermass", or topographic data like "zinit").
|
|
2294
|
+
folder_out : str, optional
|
|
2295
|
+
Destination folder for saving files. If None, defaults to
|
|
2296
|
+
:data:`_folder_output/processed`. By default None.
|
|
2297
|
+
file_name : str, optional
|
|
2298
|
+
Base name of the output file (without extension). If None, uses
|
|
2299
|
+
:data:`output_name`. By default None.
|
|
2300
|
+
fmt : str, optional
|
|
2301
|
+
Output file format (e.g., "txt", "npy", "asc"), by default "txt".
|
|
2302
|
+
from_file : bool, optional
|
|
2303
|
+
If True, attempt to read precomputed results from file before computing,
|
|
2304
|
+
by default True.
|
|
2305
|
+
**kwargs : dict
|
|
2306
|
+
Extra arguments passed to the underlying save function. For raster data,
|
|
2307
|
+
forwarded to :func:`tilupy.raster.write_raster`.
|
|
2308
|
+
|
|
2309
|
+
Raises
|
|
2310
|
+
------
|
|
2311
|
+
AssertionError
|
|
2312
|
+
If neither :data:`folder_out` nor :attr:`_folder_output` is defined.
|
|
2313
|
+
"""
|
|
2314
|
+
|
|
2315
|
+
if folder_out is None:
|
|
2316
|
+
assert (self._folder_output is not None), "folder_output attribute must be set"
|
|
2317
|
+
folder_out = os.path.join(self._folder_output, "processed")
|
|
2318
|
+
os.makedirs(folder_out, exist_ok=True)
|
|
2319
|
+
|
|
2320
|
+
if output_name in DATA_NAMES:
|
|
2321
|
+
data = self.get_output(output_name, from_file=from_file)
|
|
2322
|
+
if data.d.ndim > 1:
|
|
2323
|
+
if "x" not in kwargs:
|
|
2324
|
+
kwargs["x"] = self.x
|
|
2325
|
+
if "y" not in kwargs:
|
|
2326
|
+
kwargs["y"] = self.y
|
|
2327
|
+
|
|
2328
|
+
data.save(folder=folder_out, file_name=file_name, fmt=fmt, **kwargs)
|
|
2329
|
+
|
|
2330
|
+
elif output_name in TOPO_DATA_2D:
|
|
2331
|
+
if file_name is None:
|
|
2332
|
+
file_name = output_name
|
|
2333
|
+
name = "_" + output_name
|
|
2334
|
+
file_out = os.path.join(folder_out, file_name)
|
|
2335
|
+
tilupy.raster.write_raster(self._x,
|
|
2336
|
+
self._y,
|
|
2337
|
+
getattr(self, name),
|
|
2338
|
+
file_out,
|
|
2339
|
+
fmt=fmt,
|
|
2340
|
+
**kwargs)
|
|
2341
|
+
|
|
2342
|
+
|
|
2343
|
+
@abstractmethod
|
|
2344
|
+
def _extract_output(self):
|
|
2345
|
+
"""Abstract method to extract output of simulation result files."""
|
|
2346
|
+
pass
|
|
2347
|
+
|
|
2348
|
+
|
|
2349
|
+
@abstractmethod
|
|
2350
|
+
def _read_from_file(self):
|
|
2351
|
+
"""Abstract method for reading output from specific files."""
|
|
2352
|
+
pass
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
@property
|
|
2356
|
+
def zinit(self):
|
|
2357
|
+
"""Get initial topography.
|
|
2358
|
+
|
|
2359
|
+
Returns
|
|
2360
|
+
-------
|
|
2361
|
+
numpy.ndarray
|
|
2362
|
+
Attribute :attr:`_zinit`
|
|
2363
|
+
"""
|
|
2364
|
+
return self._zinit
|
|
2365
|
+
|
|
2366
|
+
|
|
2367
|
+
@property
|
|
2368
|
+
def z(self):
|
|
2369
|
+
"""Get initial topography, alias for zinit.
|
|
2370
|
+
|
|
2371
|
+
Returns
|
|
2372
|
+
-------
|
|
2373
|
+
numpy.ndarray
|
|
2374
|
+
Attribute :attr:`_zinit`
|
|
2375
|
+
"""
|
|
2376
|
+
return self._zinit
|
|
2377
|
+
|
|
2378
|
+
|
|
2379
|
+
@property
|
|
2380
|
+
def x(self):
|
|
2381
|
+
"""Get X-coordinates.
|
|
2382
|
+
|
|
2383
|
+
Returns
|
|
2384
|
+
-------
|
|
2385
|
+
numpy.ndarray
|
|
2386
|
+
Attribute :attr:`_x`
|
|
2387
|
+
"""
|
|
2388
|
+
return self._x
|
|
2389
|
+
|
|
2390
|
+
|
|
2391
|
+
@property
|
|
2392
|
+
def y(self):
|
|
2393
|
+
"""Get Y-coordinates.
|
|
2394
|
+
|
|
2395
|
+
Returns
|
|
2396
|
+
-------
|
|
2397
|
+
numpy.ndarray
|
|
2398
|
+
Attribute :attr:`_y`
|
|
2399
|
+
"""
|
|
2400
|
+
return self._y
|
|
2401
|
+
|
|
2402
|
+
|
|
2403
|
+
@property
|
|
2404
|
+
def dx(self):
|
|
2405
|
+
"""Get cell size along X.
|
|
2406
|
+
|
|
2407
|
+
Returns
|
|
2408
|
+
-------
|
|
2409
|
+
numpy.ndarray
|
|
2410
|
+
Attribute :attr:`_dx`
|
|
2411
|
+
"""
|
|
2412
|
+
return self._dx
|
|
2413
|
+
|
|
2414
|
+
|
|
2415
|
+
@property
|
|
2416
|
+
def dy(self):
|
|
2417
|
+
"""Get cell size along Y.
|
|
2418
|
+
|
|
2419
|
+
Returns
|
|
2420
|
+
-------
|
|
2421
|
+
numpy.ndarray
|
|
2422
|
+
Attribute :attr:`_dy`
|
|
2423
|
+
"""
|
|
2424
|
+
return self._dy
|
|
2425
|
+
|
|
2426
|
+
|
|
2427
|
+
@property
|
|
2428
|
+
def nx(self):
|
|
2429
|
+
"""Get number of cells along X.
|
|
2430
|
+
|
|
2431
|
+
Returns
|
|
2432
|
+
-------
|
|
2433
|
+
numpy.ndarray
|
|
2434
|
+
Attribute :attr:`_nx`
|
|
2435
|
+
"""
|
|
2436
|
+
return self._nx
|
|
2437
|
+
|
|
2438
|
+
|
|
2439
|
+
@property
|
|
2440
|
+
def ny(self):
|
|
2441
|
+
"""Get number of cells along Y.
|
|
2442
|
+
|
|
2443
|
+
Returns
|
|
2444
|
+
-------
|
|
2445
|
+
numpy.ndarray
|
|
2446
|
+
Attribute :attr:`_ny`
|
|
2447
|
+
"""
|
|
2448
|
+
return self._ny
|
|
2449
|
+
|
|
2450
|
+
|
|
2451
|
+
@property
|
|
2452
|
+
def tim(self):
|
|
2453
|
+
"""Get recorded time steps.
|
|
2454
|
+
|
|
2455
|
+
Returns
|
|
2456
|
+
-------
|
|
2457
|
+
numpy.ndarray
|
|
2458
|
+
Attribute :attr:`_tim`
|
|
2459
|
+
"""
|
|
2460
|
+
return self._tim
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
@property
|
|
2464
|
+
def h(self):
|
|
2465
|
+
"""Get flow thickness. Compute it if not stored.
|
|
2466
|
+
|
|
2467
|
+
Returns
|
|
2468
|
+
-------
|
|
2469
|
+
tilupy.read.TemporalResults2D
|
|
2470
|
+
Attribute :attr:`_h`
|
|
2471
|
+
"""
|
|
2472
|
+
if self._h is None:
|
|
2473
|
+
self._h = self.get_output("h").d
|
|
2474
|
+
return self._h
|
|
2475
|
+
|
|
2476
|
+
|
|
2477
|
+
@property
|
|
2478
|
+
def h_max(self):
|
|
2479
|
+
"""Get maximum flow thickness. Compute it if not stored.
|
|
2480
|
+
|
|
2481
|
+
Returns
|
|
2482
|
+
-------
|
|
2483
|
+
tilupy.read.TemporalResults0D
|
|
2484
|
+
Attribute :attr:`_h_max`
|
|
2485
|
+
"""
|
|
2486
|
+
if self._h_max is None:
|
|
2487
|
+
self._h_max = self.get_output("h_max").d
|
|
2488
|
+
return self._h_max
|
|
2489
|
+
|
|
2490
|
+
|
|
2491
|
+
@property
|
|
2492
|
+
def u(self):
|
|
2493
|
+
"""Get flow velocity. Compute it if not stored.
|
|
2494
|
+
|
|
2495
|
+
Returns
|
|
2496
|
+
-------
|
|
2497
|
+
tilupy.read.TemporalResults2D
|
|
2498
|
+
Attribute :attr:`_u`
|
|
2499
|
+
"""
|
|
2500
|
+
if self._u is None:
|
|
2501
|
+
self._u = self.get_output("u").d
|
|
2502
|
+
return self._u
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
@property
|
|
2506
|
+
def u_max(self):
|
|
2507
|
+
"""Get maximum flow velocity. Compute it if not stored.
|
|
2508
|
+
|
|
2509
|
+
Returns
|
|
2510
|
+
-------
|
|
2511
|
+
tilupy.read.TemporalResults0D
|
|
2512
|
+
Attribute :attr:`_u_max`
|
|
2513
|
+
"""
|
|
2514
|
+
if self._u_max is None:
|
|
2515
|
+
self._u_max = self.get_output("u_max").d
|
|
2516
|
+
return self._u_max
|
|
2517
|
+
|
|
2518
|
+
|
|
2519
|
+
@property
|
|
2520
|
+
def costh(self):
|
|
2521
|
+
"""Get cos(slope) of topography. Compute it if not stored.
|
|
2522
|
+
|
|
2523
|
+
Returns
|
|
2524
|
+
-------
|
|
2525
|
+
numpy.ndarray
|
|
2526
|
+
Attribute :attr:`_costh`
|
|
2527
|
+
"""
|
|
2528
|
+
if self._costh is None:
|
|
2529
|
+
self._costh = self.compute_costh()
|
|
2530
|
+
return self._costh
|
|
2531
|
+
|
|
2532
|
+
|
|
2533
|
+
def get_results(code, **kwargs) -> tilupy.read.Results:
|
|
2534
|
+
"""Get simulation results for a given numerical model.
|
|
2535
|
+
|
|
2536
|
+
Dynamically imports the corresponding reader module from
|
|
2537
|
+
`tilupy.models.<code>.read` and instantiates its :class:`tilupy.read.Results` class.
|
|
2538
|
+
|
|
2539
|
+
Parameters
|
|
2540
|
+
----------
|
|
2541
|
+
code : str
|
|
2542
|
+
Short name of the simulation model: must be in :data:`ALLOWED_MODELS`.
|
|
2543
|
+
**kwargs : dict
|
|
2544
|
+
Additional keyword arguments passed to the :class:`tilupy.read.Results` constructor
|
|
2545
|
+
of the imported module.
|
|
2546
|
+
|
|
2547
|
+
Returns
|
|
2548
|
+
-------
|
|
2549
|
+
tilupy.read.Results
|
|
2550
|
+
Instance of the :class:`tilupy.read.Results` class containing the simulation outputs.
|
|
2551
|
+
|
|
2552
|
+
Raises
|
|
2553
|
+
------
|
|
2554
|
+
ModuleNotFoundError
|
|
2555
|
+
If the module `tilupy.models.<code>.read` cannot be imported.
|
|
2556
|
+
AttributeError
|
|
2557
|
+
If the module does not define a :class:`tilupy.read.Results` class.
|
|
2558
|
+
"""
|
|
2559
|
+
module = importlib.import_module("tilupy.models." + code + ".read")
|
|
2560
|
+
return module.Results(**kwargs)
|
|
2561
|
+
|
|
2562
|
+
|
|
2563
|
+
def use_thickness_threshold(simu: tilupy.read.Results,
|
|
2564
|
+
array: np.ndarray,
|
|
2565
|
+
h_thresh: float
|
|
2566
|
+
) -> np.ndarray:
|
|
2567
|
+
"""Apply a flow thickness threshold to mask simulation results.
|
|
2568
|
+
|
|
2569
|
+
Values of :data:`array` are set to zero wherever the flow thickness
|
|
2570
|
+
is below the given threshold.
|
|
2571
|
+
|
|
2572
|
+
Parameters
|
|
2573
|
+
----------
|
|
2574
|
+
simu : tilupy.read.Results
|
|
2575
|
+
Simulation result object providing access to thickness data :data:`h`.
|
|
2576
|
+
array : numpy.ndarray
|
|
2577
|
+
Array of values to be masked (must be consistent in shape with thickness).
|
|
2578
|
+
h_thresh : float
|
|
2579
|
+
Thickness threshold. Cells with thickness < :data:`h_thresh` are set to zero.
|
|
2580
|
+
|
|
2581
|
+
Returns
|
|
2582
|
+
-------
|
|
2583
|
+
numpy.ndarray
|
|
2584
|
+
Thresholded array, with values set to zero where flow thickness is too low.
|
|
2585
|
+
"""
|
|
2586
|
+
thickness = simu.get_output("h")
|
|
2587
|
+
array[thickness.d < h_thresh] = 0
|
|
2588
|
+
return array
|