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.
@@ -0,0 +1,613 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ import os
5
+
6
+ import numpy as np
7
+ import tilupy.read
8
+
9
+ from tilupy import notations
10
+
11
+ # Dictionnary with results names lookup table, to match code output names
12
+ LOOKUP_NAMES = dict(h="rho",
13
+ hvert="rho",
14
+ ux="u",
15
+ uy="ut",
16
+ u="unorm",
17
+ hu="momentum",
18
+ ek="ek",
19
+ vol="vol",
20
+ ep="ep",
21
+ etot="etot",
22
+ )
23
+ """Dictionary of correspondance, to match code output names."""
24
+
25
+ # Classify results
26
+ AVAILABLE_OUTPUT = ["h", "hvert", "u", "hu", "hu2", "ux", "uy"]
27
+ """All output available for a shaltop simulation.
28
+
29
+ Implemented states:
30
+
31
+ - h : Flow thickness (normal to the surface)
32
+ - hvert : True vertical flow thickness
33
+ - ux : Velocity component in X direction
34
+ - uy : Velocity component in Y direction
35
+
36
+ Output computed from other output:
37
+
38
+ - u : Norm of the velocity (from ux and uy)
39
+ - hu : Momentum flux (from h and u)
40
+ - hu2 : Convective momentum flux (from h and u)
41
+ """
42
+
43
+ READ_FROM_FILE_OUTPUT = ["h_max", "u_max", "hu_max", "hu2_int"]
44
+ """Output read from the file.
45
+
46
+ Read output:
47
+
48
+ - h_max : Maximum value of h integrating all time steps
49
+ - u_max : Maximum value of u integrating all time steps
50
+ - hu_max : Maximum value of hu integrating all time steps
51
+ - hu2_int : Integral of values of hu2 for each time steps
52
+ """
53
+
54
+ FORCES_OUTPUT = []
55
+ """Force-related output variables, including components in x, y, and additional forces.
56
+
57
+ Generated from:
58
+
59
+ - faccx, faccy : acceleration force
60
+ - fcurvx, fcurvy : curvature force
61
+ - ffricx, ffricy : friction force
62
+ - fgravx, fgravy : gravitational force
63
+ - finertx, finerty : inertial force
64
+ - fpressionx, fpressiony : pressure force
65
+
66
+ Additional variables:
67
+
68
+ - shearx, sheary, shearz : shear stresses
69
+ - normalx, normaly, normalz : normal stresses
70
+ - pbottom : basal pressure
71
+ - pcorrdt : pressure correction (time step)
72
+ - pcorrdiv : pressure correction (divergence)
73
+ """
74
+
75
+ for force in ["facc", "fcurv", "ffric", "fgrav", "finert", "fpression"]:
76
+ FORCES_OUTPUT.append(force + "x")
77
+ FORCES_OUTPUT.append(force + "y")
78
+
79
+ FORCES_OUTPUT += ["shearx",
80
+ "sheary",
81
+ "shearz",
82
+ "normalx",
83
+ "normaly",
84
+ "normalz",
85
+ "pbottom",
86
+ "pcorrdt",
87
+ "pcorrdiv"]
88
+
89
+ INTEGRATED_OUTPUT = ["ek", "ep", "etot"]
90
+ """Integrated energy quantities.
91
+
92
+ Implemented energy:
93
+
94
+ - ek : kinetic energy
95
+ - ep : potential energy
96
+ - etot : total energy
97
+ """
98
+
99
+
100
+ def read_params(file: str) -> dict:
101
+ """Read simulation parameters from file.
102
+
103
+ Parameters
104
+ ----------
105
+ file : str
106
+ Path to the parameters file.
107
+
108
+ Returns
109
+ -------
110
+ params : dict
111
+ Dictionnary with parameters
112
+ """
113
+ if file is None:
114
+ params = None
115
+ else:
116
+ params = dict()
117
+ with open(file, "r") as f:
118
+ lines = filter(None, (line.rstrip() for line in f))
119
+ for line in lines:
120
+ (key, val) = line.split(" ")
121
+ try:
122
+ params[key] = float(val)
123
+ except ValueError:
124
+ params[key] = val.rstrip()
125
+ params["nx"] = int(params["nx"])
126
+ params["ny"] = int(params["ny"])
127
+
128
+ return params
129
+
130
+
131
+ def read_file_bin(file: str,
132
+ nx: int,
133
+ ny: int
134
+ ) -> np.ndarray:
135
+ """Read shaltop .bin result file.
136
+
137
+ Parameters
138
+ ----------
139
+ file : str
140
+ Path to the .bin file.
141
+ nx : int
142
+ Number of cells in the X direction.
143
+ ny : int
144
+ Number of cells in the Y direction.
145
+
146
+ Returns
147
+ -------
148
+ numpy.ndarray
149
+ Values in the .bin file.
150
+ """
151
+ data = np.fromfile(file, dtype=np.float32)
152
+ nbim = int(np.size(data) / nx / ny)
153
+ data = np.reshape(data, (nx, ny, nbim), order="F")
154
+ data = np.transpose(np.flip(data, axis=1), (1, 0, 2))
155
+ return data
156
+
157
+
158
+ def read_file_init(file: str, nx: int, ny: int) -> np.ndarray:
159
+ """Read shaltop initial .d files.
160
+
161
+ Parameters
162
+ ----------
163
+ file : str
164
+ Path to the .d file.
165
+ nx : int
166
+ Number of cells in the X direction.
167
+ ny : int
168
+ Number of cells in the Y direction.
169
+
170
+ Returns
171
+ -------
172
+ numpy.ndarray
173
+ Values in the .d file.
174
+ """
175
+ data = np.loadtxt(file)
176
+ data = np.reshape(data, (nx, ny), order="F")
177
+ data = np.transpose(np.flip(data, axis=1), (1, 0))
178
+ return data
179
+
180
+
181
+ class Results(tilupy.read.Results):
182
+ """Results of shaltop simulations.
183
+
184
+ This class is the results class for shaltop. Reading results from shaltop outputs
185
+ are done in this class.
186
+
187
+ This class has all the global and quick attributes of the parent class. The quick
188
+ attributes are only computed if needed and can be deleted to clean memory.
189
+
190
+ In addition to these attributes, there are those necessary for the operation of
191
+ reading the shaltop results.
192
+
193
+ Parameters
194
+ ----------
195
+ folder : str
196
+ Path to the folder containing the simulation files.
197
+ file_params : str
198
+ Name of the simulation parameters file.
199
+
200
+ Attributes
201
+ ----------
202
+ _htype : str
203
+ Always "normal".
204
+ _tforces : list
205
+ Times of output.
206
+ _params : dict
207
+ Dictionary of the simulation parameters.
208
+ """
209
+ def __init__(self, folder=None, file_params=None, **varargs):
210
+ super().__init__()
211
+ self._code = "shaltop"
212
+
213
+ try:
214
+ if "folder_base" in varargs:
215
+ raise UserWarning("Variable name has changed: 'folder_base' -> 'folder'")
216
+ except UserWarning as w:
217
+ print(f"[WARNING] {w}")
218
+ if "folder_base" in varargs :
219
+ folder = varargs["folder_base"]
220
+
221
+ if folder is None:
222
+ folder = os.getcwd()
223
+ self._folder = folder
224
+
225
+ if file_params is None:
226
+ file_params = "params.txt"
227
+ if "." not in file_params:
228
+ file_params = file_params + ".txt"
229
+ file_params = os.path.join(folder, file_params)
230
+ self._params = read_params(file_params)
231
+
232
+ # Folder where results are stored
233
+ if "folder_output" not in self._params:
234
+ self._folder_output = os.path.join(self._folder,
235
+ "data2")
236
+ else:
237
+ self._folder_output = os.path.join(self._folder,
238
+ self._params["folder_output"])
239
+
240
+ self._x, self._y = self.get_axes(**self._params)
241
+ self._nx, self._ny = len(self._x), len(self._y)
242
+ self._dx = self._x[1] - self._x[0]
243
+ self._dy = self._y[1] - self._y[0]
244
+ self._zinit = self.get_zinit()
245
+
246
+ self._htype = "normal"
247
+
248
+ # Get time of outputs
249
+ self._tim = np.loadtxt(os.path.join(self._folder_output, "time_im.d"))
250
+ file_tforces = os.path.join(self._folder_output, "time_forces.d")
251
+ if os.path.isfile(file_tforces):
252
+ self._tforces = np.loadtxt(os.path.join(self._folder_output,
253
+ "time_forces.d"))
254
+ else:
255
+ self._tforces = []
256
+
257
+
258
+ def get_zinit(self, zinit=None) -> np.ndarray:
259
+ """Get zinit, the initial topography.
260
+
261
+ Returns
262
+ -------
263
+ numpy.ndarray
264
+ The initial topography.
265
+ """
266
+ path_zinit = os.path.join(self._folder_output, "z.bin")
267
+ if not os.path.isfile(path_zinit) and "file_z_init" in self._params:
268
+ path_zinit = os.path.join(self._folder, self._params["file_z_init"])
269
+ zinit = read_file_init(path_zinit, self._nx, self._ny)
270
+ else:
271
+ zinit = np.squeeze(read_file_bin(path_zinit, self._nx, self._ny))
272
+ return zinit
273
+
274
+
275
+ def get_axes(self, **varargs) -> tuple[np.ndarray, np.ndarray]:
276
+ """Get X and Y axes.
277
+
278
+ varargs : dict
279
+ All parameters needed to compute the axes :
280
+ :data:`x0`, :data:`y0`, :data:`nx`, :data:`ny`, :data:`per`,
281
+ :data:`pery` and :data:`coord_pos`.
282
+
283
+ Returns
284
+ -------
285
+ tuple[numpy.ndarray, numpy.ndarray]
286
+ Values of X and Y axes.
287
+ """
288
+ if "x0" in varargs:
289
+ x0 = varargs["x0"]
290
+ else:
291
+ x0 = 0
292
+ if "y0" in varargs:
293
+ y0 = varargs["y0"]
294
+ else:
295
+ y0 = 0
296
+
297
+ nx = varargs["nx"]
298
+ ny = varargs["ny"]
299
+ dx = varargs["per"] / nx
300
+ dy = varargs["pery"] / ny
301
+ x = dx * np.arange(nx) + dx / 2
302
+ y = dy * np.arange(ny) + dy / 2
303
+
304
+ try:
305
+ coord_pos = varargs["coord_pos"]
306
+ except KeyError:
307
+ coord_pos = "bottom_left"
308
+
309
+ if coord_pos == "bottom_left":
310
+ x = x + x0
311
+ y = y + y0
312
+ elif coord_pos == "upper_right":
313
+ x = x + x0 - x[-1]
314
+ y = y + y0 - y[-1]
315
+ elif coord_pos == "upper_left":
316
+ x = x + x0
317
+ y = y + y0 - y[-1]
318
+ elif coord_pos == "lower_right":
319
+ x = x + x0 - x[-1]
320
+ y = y + y0
321
+
322
+ return x, y
323
+
324
+
325
+ def get_u(self) -> np.ndarray:
326
+ """Compute velocity norm from results.
327
+
328
+ Returns
329
+ -------
330
+ numpy.ndarray
331
+ Values of flow velocity (norm).
332
+ """
333
+ file = os.path.join(self._folder_output, "u" + ".bin")
334
+ u = read_file_bin(file, self._nx, self._ny)
335
+ file = os.path.join(self._folder_output, "ut" + ".bin")
336
+ ut = read_file_bin(file, self._nx, self._ny)
337
+
338
+ if self._costh is None:
339
+ self._costh = self.compute_costh()
340
+ # print(np.shape(self._zinit))
341
+ # print(self._zinit)
342
+ # print(np.shape(self._y))
343
+ # print(np.shape(self._x))
344
+
345
+ [Fx, Fy] = np.gradient(self._zinit, np.flip(self._y), self._x)
346
+ u = u * self._costh[:, :, np.newaxis]
347
+ ut = ut * self._costh[:, :, np.newaxis]
348
+ d = np.sqrt(u**2 + ut**2 + (Fx[:, :, np.newaxis] * u + Fy[:, :, np.newaxis] * ut) ** 2)
349
+
350
+ return d
351
+
352
+
353
+ def _read_from_file(self,
354
+ name: str,
355
+ operator: str,
356
+ axis: str=None,
357
+ **kwargs
358
+ ) -> tilupy.read.StaticResults2D | tilupy.read.TemporalResults0D:
359
+ """Read output from specific files.
360
+
361
+ Can access to data in :data:`READ_FROM_FILE_OUTPUT`.
362
+
363
+ Parameters
364
+ ----------
365
+ name : str
366
+ Wanted output. Can access to: "u", "hu", "h" and "hu2".
367
+ operator : str
368
+ Wanted operator. Can be "max" for every output except "hu2" or "int" only for "hu2".
369
+ axis : str, optional
370
+ Optional axis. Can be "t". By default None.
371
+
372
+ Returns
373
+ -------
374
+ tilupy.read.StaticResults2D | tilupy.read.TemporalResults0D
375
+ Wanted output.
376
+ """
377
+ res = None
378
+
379
+ if name in ["u", "hu", "h"]:
380
+ if operator in ["max"] and axis in [None, "t"]:
381
+ file = os.path.join(self._folder_output,
382
+ LOOKUP_NAMES[name] + operator + ".bin")
383
+ d = np.squeeze(read_file_bin(file, self._nx, self._ny))
384
+ res = tilupy.read.StaticResults2D("_".join([name, operator]),
385
+ d,
386
+ x=self._x,
387
+ y=self._y,
388
+ z=self._zinit)
389
+
390
+ if (name, operator) == ("hu2", "int"):
391
+ array = np.loadtxt(os.path.join(self._folder_output, "ek.d"))
392
+ d = array[:, 1]
393
+ t = array[:, 0]
394
+ res = tilupy.read.TemporalResults0D(name, d, t)
395
+
396
+ return res
397
+
398
+
399
+ def _extract_output(self,
400
+ name: str,
401
+ **kwargs
402
+ )-> tilupy.read.TemporalResults2D | tilupy.read.TemporalResults0D | tilupy.read.AbstractResults:
403
+ """Result extraction for shaltop files.
404
+
405
+ Parameters
406
+ ----------
407
+ name : str
408
+ Wanted output. Can access to variables in :data:`AVAILABLE_OUTPUT`, :data:`INTEGRATED_OUTPUT`
409
+ and :data:`FORCES_OUTPUT`.
410
+
411
+ Returns
412
+ -------
413
+ tilupy.read.TemporalResults2D | tilupy.read.TemporalResults0D | tilupy.read.AbstractResults
414
+ Wanted output. If no output computed, return an object of :class:`tilupy.read.AbstractResults`.
415
+ """
416
+ # Read thicknesses or velocity components
417
+ d = None
418
+ t = None
419
+ notation = None
420
+
421
+ if name in ["h", "ux", "uy", "hvert"]:
422
+ if self._costh is None:
423
+ self._costh = self.compute_costh()
424
+
425
+ file = os.path.join(self._folder_output,
426
+ LOOKUP_NAMES[name] + ".bin")
427
+
428
+ d = read_file_bin(file, self._nx, self._ny)
429
+
430
+ if name == "hvert":
431
+ d = d / self._costh[:, :, np.newaxis]
432
+ if name in ["ux", "uy"]:
433
+ d = d * self.compute_costh()[:, :, np.newaxis]
434
+ t = self._tim
435
+
436
+ if name == "u":
437
+ d = self.get_u()
438
+ t = self._tim
439
+
440
+ if name in ["hu", "hu2"]:
441
+ fileh = os.path.join(self._folder_output, "rho.bin")
442
+ h = read_file_bin(fileh, self._nx, self._ny)
443
+ u = self.get_u()
444
+ if name == "hu":
445
+ d = h * u
446
+ elif name == "hu2":
447
+ d = h * u**2
448
+ t = self._tim
449
+
450
+ if name in INTEGRATED_OUTPUT:
451
+ array = np.loadtxt(os.path.join(self._folder_output,
452
+ LOOKUP_NAMES[name] + ".d"))
453
+ d = array[:, 1]
454
+ if "density" in self._params:
455
+ density = self._params["density"]
456
+ else:
457
+ density = 1
458
+ d = d * density
459
+ t = array[:, 0]
460
+
461
+ if name in FORCES_OUTPUT:
462
+ file = os.path.join(self._folder_output, name + ".bin")
463
+ d = read_file_bin(file, self._nx, self._ny)
464
+ t = self._tforces
465
+ notation = notations.Notation(name,
466
+ long_name=name,
467
+ unit=notations.Unit(Pa=1, kg=-1, m=3),
468
+ symbol=name)
469
+
470
+ if d is None:
471
+ file = os.path.join(self._folder_output, name)
472
+ if os.path.isfile(file + ".bin"):
473
+ d = read_file_bin(file + ".bin", self._nx, self._ny)
474
+ t = self._tim
475
+ elif os.path.isfile(file + ".d"):
476
+ d = np.loadtxt(file + ".d")
477
+
478
+ if ("h_thresh" in kwargs
479
+ and kwargs["h_thresh"] is not None
480
+ and d.ndim == 3):
481
+ d = tilupy.read.use_thickness_threshold(self,
482
+ d,
483
+ kwargs["h_thresh"])
484
+
485
+ if t is None:
486
+ return tilupy.read.AbstractResults(name, d, notation=notation)
487
+
488
+ else:
489
+ if d.ndim == 3:
490
+ return tilupy.read.TemporalResults2D(name,
491
+ d,
492
+ t,
493
+ notation=notation,
494
+ x=self._x,
495
+ y=self._y,
496
+ z=self._zinit)
497
+ if d.ndim == 1:
498
+ return tilupy.read.TemporalResults0D(name,
499
+ d,
500
+ t,
501
+ notation=notation)
502
+ return None
503
+
504
+ # def get_temporal_output(self, name, d=None, t=None, **varargs):
505
+ # """
506
+ # Read 2D time dependent simulation results.
507
+
508
+ # Parameters
509
+ # ----------
510
+ # name : str
511
+ # Name of output.
512
+ # d : ndarray, optional
513
+ # Data to be read. If None, will be computed.
514
+ # The default is None.
515
+ # t : ndarray, optional
516
+ # Time of results snapshots (1D array), matching last dimension of d.
517
+ # If None, will be computed. The default is None.
518
+ # **varargs : TYPE
519
+ # DESCRIPTION.
520
+
521
+ # """
522
+ # # Read thicknesses or velocity components
523
+ # if name in STATES_OUTPUT:
524
+ # file = os.path.join(
525
+ # self.folder_output, LOOKUP_NAMES[name] + ".bin"
526
+ # )
527
+ # d = read_file_bin(file, self.nx, self.ny)
528
+ # if name == "hvert":
529
+ # d = d / self.costh[:, :, np.newaxis]
530
+ # t = self.tim
531
+
532
+ # # Raed integrated kinetic energy
533
+ # if name in ["hu2_int"]:
534
+ # array = np.loadtxt(
535
+ # os.path.join(self.folder_output, LOOKUP_NAMES[name] + ".d")
536
+ # )
537
+ # d = array[:, 1]
538
+ # t = array[:, 0]
539
+
540
+ # # Compute the velocity
541
+ # if name == "u":
542
+ # d = self.get_u()
543
+ # t = self.tim
544
+
545
+ # if name in ["hu", "hu2"]:
546
+ # fileh = os.path.join(self.folder_output, "rho.bin")
547
+ # h = read_file_bin(fileh, self.nx, self.ny)
548
+ # u = self.get_u()
549
+ # if name == "hu":
550
+ # d = h * u
551
+ # elif name == "hu2":
552
+ # d = h * u**2
553
+ # t = self.tim
554
+
555
+ # if (
556
+ # "h_thresh" in varargs
557
+ # and varargs["h_thresh"] is not None
558
+ # and d.ndim == 3
559
+ # ):
560
+ # d = tilupy.read.use_thickness_threshold(
561
+ # self, d, varargs["h_thresh"]
562
+ # )
563
+
564
+ # return tilupy.read.TemporalResults2D(name, d, t)
565
+
566
+ # def get_static_output(self, name, d=None, from_file=True, **varargs):
567
+ # """
568
+ # Read 2D time dependent simulation results.
569
+
570
+ # Parameters
571
+ # ----------
572
+ # name : str
573
+ # Name of output.
574
+ # d : ndarray, optional
575
+ # Data to be read. Last dimension is for time.
576
+ # If None, will be computed.
577
+ # The default is None.
578
+ # **varargs : TYPE
579
+ # DESCRIPTION.
580
+
581
+ # """
582
+ # if name in tilupy.read.COMPUTED_STATIC_DATA_2D:
583
+ # state, stat = name.split("_")
584
+ # if stat in ["final", "initial"]:
585
+ # hh = self.get_temporal_output(state)
586
+ # if state == "hvert":
587
+ # hh.d = hh.d / self.costh[:, :, np.newaxis]
588
+ # return hh.get_temporal_stat(stat)
589
+ # if from_file:
590
+ # file = os.path.join(
591
+ # self.folder_output, LOOKUP_NAMES[state] + stat + ".bin"
592
+ # )
593
+ # if os.path.isfile(file):
594
+ # d = np.squeeze(read_file_bin(file, self.nx, self.ny))
595
+ # else:
596
+ # print(
597
+ # file
598
+ # + " was not found, "
599
+ # + name
600
+ # + "_"
601
+ # + stat
602
+ # + " computed from temporal output."
603
+ # )
604
+ # from_file = False
605
+ # if not from_file:
606
+ # data = self.get_temporal_output(state)
607
+ # if state == "hvert":
608
+ # hh.d = hh.d / self.costh
609
+ # d = data.get_temporal_stat(stat).d
610
+ # else:
611
+ # raise (NotImplementedError())
612
+
613
+ # return tilupy.read.StaticResults(name, d)