zoomy-core 0.1.14__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.
Files changed (57) hide show
  1. decorators/decorators.py +25 -0
  2. fvm/__init__.py +0 -0
  3. fvm/flux.py +52 -0
  4. fvm/nonconservative_flux.py +97 -0
  5. fvm/ode.py +55 -0
  6. fvm/solver_numpy.py +297 -0
  7. fvm/timestepping.py +13 -0
  8. mesh/__init__.py +0 -0
  9. mesh/mesh.py +1239 -0
  10. mesh/mesh_extrude.py +168 -0
  11. mesh/mesh_util.py +487 -0
  12. misc/__init__.py +0 -0
  13. misc/custom_types.py +6 -0
  14. misc/interpolation.py +140 -0
  15. misc/io.py +448 -0
  16. misc/logger_config.py +18 -0
  17. misc/misc.py +218 -0
  18. model/__init__.py +0 -0
  19. model/analysis.py +147 -0
  20. model/basefunction.py +113 -0
  21. model/basemodel.py +513 -0
  22. model/boundary_conditions.py +193 -0
  23. model/initial_conditions.py +171 -0
  24. model/model.py +65 -0
  25. model/models/GN.py +70 -0
  26. model/models/advection.py +53 -0
  27. model/models/basisfunctions.py +181 -0
  28. model/models/basismatrices.py +381 -0
  29. model/models/coupled_constrained.py +60 -0
  30. model/models/poisson.py +41 -0
  31. model/models/shallow_moments.py +757 -0
  32. model/models/shallow_moments_sediment.py +378 -0
  33. model/models/shallow_moments_topo.py +423 -0
  34. model/models/shallow_moments_variants.py +1509 -0
  35. model/models/shallow_water.py +266 -0
  36. model/models/shallow_water_topo.py +111 -0
  37. model/models/shear_shallow_flow.py +594 -0
  38. model/models/sme_turbulent.py +613 -0
  39. model/models/vam.py +455 -0
  40. postprocessing/__init__.py +0 -0
  41. postprocessing/plotting.py +244 -0
  42. postprocessing/postprocessing.py +75 -0
  43. preprocessing/__init__.py +0 -0
  44. preprocessing/openfoam_moments.py +453 -0
  45. transformation/__init__.py +0 -0
  46. transformation/helpers.py +25 -0
  47. transformation/to_amrex.py +241 -0
  48. transformation/to_c.py +185 -0
  49. transformation/to_jax.py +14 -0
  50. transformation/to_numpy.py +118 -0
  51. transformation/to_openfoam.py +258 -0
  52. transformation/to_ufl.py +67 -0
  53. zoomy_core-0.1.14.dist-info/METADATA +52 -0
  54. zoomy_core-0.1.14.dist-info/RECORD +57 -0
  55. zoomy_core-0.1.14.dist-info/WHEEL +5 -0
  56. zoomy_core-0.1.14.dist-info/licenses/LICENSE +674 -0
  57. zoomy_core-0.1.14.dist-info/top_level.txt +8 -0
model/models/vam.py ADDED
@@ -0,0 +1,455 @@
1
+ import numpy as np
2
+ import numpy.polynomial.legendre as L
3
+ import numpy.polynomial.chebyshev as C
4
+ from scipy.optimize import least_squares as lsq
5
+ import sympy
6
+ from sympy import Matrix, sqrt
7
+ from sympy.abc import x
8
+
9
+ from sympy import integrate, diff
10
+ from sympy import legendre
11
+ from sympy import lambdify
12
+
13
+
14
+ from zoomy_core.model.basemodel import (
15
+ register_sympy_attribute,
16
+ eigenvalue_dict_to_matrix,
17
+ )
18
+ from zoomy_core.model.basemodel import Model
19
+ import zoomy_core.model.initial_conditions as IC
20
+
21
+ class VAMHyperbolic(Model):
22
+ def __init__(
23
+ self,
24
+ boundary_conditions,
25
+ initial_conditions,
26
+ dimension=1,
27
+ fields=6,
28
+ aux_variables=['hw2', 'hp0', 'hp1', 'dbdx', 'dhdx', 'dhp0dx', 'dhp1dx'],
29
+ parameters={},
30
+ _default_parameters={"g": 9.81},
31
+ settings={},
32
+ settings_default={},
33
+ ):
34
+ self.variables = register_sympy_attribute(fields, "q")
35
+ self.n_variables = self.variables.length()
36
+ super().__init__(
37
+ dimension=dimension,
38
+ fields=fields,
39
+ aux_variables=aux_variables,
40
+ parameters=parameters,
41
+ _default_parameters=_default_parameters,
42
+ boundary_conditions=boundary_conditions,
43
+ initial_conditions=initial_conditions,
44
+ settings={**settings_default, **settings},
45
+ )
46
+
47
+ def flux(self):
48
+ fx = Matrix([0 for i in range(self.n_variables)])
49
+ hw2 = self.aux_variables.hw2
50
+ h = self.variables[0]
51
+ hu0 = self.variables[1]
52
+ hu1 = self.variables[2]
53
+ hw0 = self.variables[3]
54
+ hw1 = self.variables[4]
55
+ param = self.parameters
56
+
57
+ u0 = hu0 / h
58
+ u1 = hu1 / h
59
+ w0 = hw0 / h
60
+ w1 = hw1 / h
61
+
62
+ fx[0] = hu0
63
+ fx[1] = hu0 * u0 + 1/3 * hu1 * u1 * u1
64
+ fx[2] = hu0 * w0 + 1/3 * hu1 * w1
65
+ fx[3] = 2*hu0 * u1
66
+ fx[4] = hu0 * w1 + u1 * (hw0 + 2/5*hw2)
67
+
68
+ return [fx]
69
+
70
+ def nonconservative_matrix(self):
71
+ nc = Matrix([[0 for i in range(self.n_variables)] for j in range(self.n_variables)])
72
+
73
+ hw2 = self.aux_variables.hw2
74
+ h = self.variables[0]
75
+ hu0 = self.variables[1]
76
+ hu1 = self.variables[2]
77
+ hw0 = self.variables[3]
78
+ hw1 = self.variables[4]
79
+ param = self.parameters
80
+
81
+ u0 = hu0 / h
82
+ u1 = hu1 / h
83
+ w0 = hw0 / h
84
+ w1 = hw1 / h
85
+ w2 = hw2 / h
86
+
87
+ nc[1, 0] = param.g * h
88
+ nc[3, 2] = u0
89
+ nc[4, 2] = - 1/5 * w2 + w0
90
+ nc[1, 5] = param.g * h
91
+ return [-nc]
92
+
93
+ def eigenvalues(self):
94
+ ev = Matrix([0 for i in range(self.n_variables)])
95
+ h = self.variables[0]
96
+ hu0 = self.variables[1]
97
+ hu1 = self.variables[2]
98
+ param = self.parameters
99
+
100
+ u0 = hu0 / h
101
+ u1 = hu1 / h
102
+
103
+ ev[0] = u0
104
+ ev[1] = u0 + 1/sqrt(3) * u1
105
+ ev[2] = u0 - 1/sqrt(3) * u1
106
+ ev[3] = u0 + sqrt(param.g * h + u1**2)
107
+ ev[4] = u0 - sqrt(param.g * h + u1**2)
108
+ ev[5] = 0
109
+
110
+ return ev
111
+
112
+ def source_implicit(self):
113
+ R = Matrix([0 for i in range(self.n_variables)])
114
+ hw2 = self.aux_variables.hw2
115
+ h = self.variables[0]
116
+ hu0 = self.variables[1]
117
+ hu1 = self.variables[2]
118
+ hw0 = self.variables[3]
119
+ hw1 = self.variables[4]
120
+ b = self.variables[5]
121
+ param = self.parameters
122
+
123
+ u0 = hu0 / h
124
+ u1 = hu1 / h
125
+ w0 = hw0 / h
126
+ w1 = hw1 / h
127
+ w2 = hw2 /h
128
+
129
+
130
+ p0 = self.aux_variables.hp0/h
131
+ p1 = self.aux_variables.hp1/h
132
+ dbdx = self.aux_variables.dbdx
133
+ dhdx = self.aux_variables.dhdx
134
+ dhp0dx = self.aux_variables.dhp0dx
135
+ dhp1dx = self.aux_variables.dhp1dx
136
+
137
+ R[0] = 0.
138
+ R[1] = dhp0dx + 2 * p1 * dbdx
139
+ R[2] = -2*p1
140
+ R[3] = dhp1dx - (3*p0 - p1)*dhdx -6*(p0-p1)*dbdx
141
+ R[4] = 6*(p0-p1)
142
+ R[5] = 0.
143
+ return R
144
+
145
+
146
+ class VAMPoisson(Model):
147
+ def __init__(
148
+ self,
149
+ boundary_conditions,
150
+ initial_conditions,
151
+ dimension=1,
152
+ fields=['hp0', 'hp1'],
153
+ aux_variables=['h', 'hu0', 'hu1', 'hw0', 'hw1' ,'b', 'hw2', 'dbdx', 'ddbdxx', 'dhdx', 'ddhdxx', 'du0dx', 'du1dx', 'dhp0dx', 'ddhp0dxx', 'dhp1dx', 'ddhp1dxx', 'dt', 'd4hp0dx4', 'd4hp1dx4'],
154
+ parameters={},
155
+ _default_parameters={"g": 9.81},
156
+ settings={},
157
+ settings_default={},
158
+ ):
159
+ self.variables = register_sympy_attribute(fields, "q")
160
+ self.n_variables = self.variables.length()
161
+ super().__init__(
162
+ dimension=dimension,
163
+ fields=fields,
164
+ aux_variables=aux_variables,
165
+ parameters=parameters,
166
+ _default_parameters=_default_parameters,
167
+ boundary_conditions=boundary_conditions,
168
+ initial_conditions=initial_conditions,
169
+ settings={**settings_default, **settings},
170
+ )
171
+
172
+ def source_implicit(self):
173
+ R = Matrix([0 for i in range(self.n_variables)])
174
+
175
+ h = self.aux_variables.h
176
+ #p0 = self.variables.p0/h
177
+ #p1 = self.variables.p1/h
178
+ dt = self.aux_variables.dt
179
+
180
+ dbdx = self.aux_variables.dbdx
181
+ ddbdxx = self.aux_variables.ddbdxx
182
+ dhdx = self.aux_variables.dhdx
183
+ ddhdxx = self.aux_variables.ddhdxx
184
+ #dp0dx = self.aux_variables.dp0dx
185
+ #ddp0dxx = self.aux_variables.ddp0dxx
186
+ #dp1dx = self.aux_variables.dp1dx
187
+ #ddp1dxx = self.aux_variables.ddp1dxx
188
+ du0dx = self.aux_variables.du0dx
189
+ du1dx = self.aux_variables.du1dx
190
+
191
+ u0 = self.aux_variables.hu0/h
192
+ u1 = self.aux_variables.hu1/h
193
+ w0 = self.aux_variables.hw0/h
194
+ w1 = self.aux_variables.hw1/h
195
+
196
+ hp0 = self.variables.hp0
197
+ hp1 = self.variables.hp1
198
+ dhp0dx = self.aux_variables.dhp0dx
199
+ ddhp0dxx = self.aux_variables.ddhp0dxx
200
+ dhp1dx = self.aux_variables.dhp1dx
201
+ ddhp1dxx = self.aux_variables.ddhp1dxx
202
+ d4hp0dx4 = self.aux_variables.d4hp0dx4
203
+ d4hp1dx4 = self.aux_variables.d4hp1dx4
204
+
205
+
206
+
207
+
208
+ delta = 0.
209
+ #I1 = 0.666666666666667*dt*dp0dx - 2*(-dt*(h*ddp0dxx + p0*dhdx + 2*p1*dbdx) + h*dp1dx)*dbdx/h + 2*(-dt*(-(3*p0 - p1)*dhdx - (6*p0 - 6*p1)*dbdx + h*dp0dx + p1*dhdx) + h*u1)/h + 0.333333333333333*(2*dt*p1 + h*u0)*dhdx/h + (-(-dt*(h*ddp0dxx + p0*dhdx + 2*p1*dbdx) + h*dp1dx)*dhdx/h**2 + (-dt*(h*du1dx + p0*ddhdxx + 2*p1*ddbdxx + 2*dbdx*dp0dx + 2*dhdx*ddp0dxx) + h*dhdx + dp1dx*dhdx)/h)*h + 0.333333333333333*h*du0dx + 0.333333333333333*u0*dhdx + delta * ddp0dxx
210
+ #I2 = -2*(-dt*(6*p0 - 6*p1) + h*w0)/h + 2*(2*dt*p1 + h*u0)*dbdx/h + (2*dt*p1 + h*u0)*dhdx/h + (-(-dt*(h*ddp0dxx + p0*dhdx + 2*p1*dbdx) + h*dp1dx)*dhdx/h**2 + (-dt*(h*du1dx + p0*ddhdxx + 2*p1*ddbdxx + 2*dbdx*dp0dx + 2*dhdx*ddp0dxx) + h*dhdx + dp1dx*dhdx)/h)*h + delta * ddp1dxx
211
+ I1 = 0.666666666666667*dt*dhp1dx/h - 0.666666666666667*dt*hp1*dhdx/h**2 - 2*(-dt*(dhp0dx + 2*hp1*dbdx/h) + h*u0)*dbdx/h + 2*(-dt*(-(3*hp0/h - hp1/h)*dhdx - (6*hp0/h - 6*hp1/h)*dbdx + dhp1dx) + h*w0)/h + 0.333333333333333*(2*dt*hp1/h + h*u1)*dhdx/h + (-(-dt*(dhp0dx + 2*hp1*dbdx/h) + h*u0)*dhdx/h**2 + (-dt*(ddhp0dxx + 2*hp1*ddbdxx/h + 2*dbdx*dhp1dx/h - 2*hp1*dbdx*dhdx/h**2) + h*du0dx + u0*dhdx)/h)*h + 0.333333333333333*h*du1dx + 0.333333333333333*u1*dhdx + delta *ddhp0dxx
212
+ I2 = -2*(-dt*(6*hp0/h - 6*hp1/h) + h*w1)/h + 2*(2*dt*hp1/h + h*u1)*dbdx/h + (2*dt*hp1/h + h*u1)*dhdx/h + (-(-dt*(dhp0dx + 2*hp1*dbdx/h) + h*u0)*dhdx/h**2 + (-dt*(ddhp0dxx + 2*hp1*ddbdxx/h + 2*dbdx*dhp1dx/h - 2*hp1*dbdx*dhdx/h**2) + h*du0dx + u0*dhdx)/h)*h + delta *ddhp1dxx
213
+ R[0] = I1 +I2
214
+ R[1] = -I2 + I1
215
+
216
+ return R
217
+
218
+ def eigenvalues(self):
219
+ ev = Matrix([0 for i in range(self.n_variables)])
220
+ return ev
221
+
222
+
223
+ class VAMPoissonFull(Model):
224
+ def __init__(
225
+ self,
226
+ boundary_conditions,
227
+ initial_conditions,
228
+ dimension=1,
229
+ fields=8,
230
+ aux_variables=['dhdt', 'dhu0dt', 'dhu1dt', 'dhw0dt', 'dhw1dt', 'dhdx', 'dhu0dx', 'dhu1dx', 'dhw0dx', 'dhw1dx', 'dhp0dx', 'dhp1dx', 'dbdx', 'hw2', 'ddp0dxx', 'ddp1dxx', 'du0dx', 'du1dx'],
231
+ parameters={},
232
+ _default_parameters={"g": 1},
233
+ settings={},
234
+ settings_default={},
235
+ ):
236
+ self.variables = register_sympy_attribute(fields, "q")
237
+ self.n_variables = self.variables.length()
238
+ super().__init__(
239
+ dimension=dimension,
240
+ fields=fields,
241
+ aux_variables=aux_variables,
242
+ parameters=parameters,
243
+ _default_parameters=_default_parameters,
244
+ boundary_conditions=boundary_conditions,
245
+ initial_conditions=initial_conditions,
246
+ settings={**settings_default, **settings},
247
+ )
248
+
249
+ def source_implicit(self):
250
+ R = Matrix([0 for i in range(self.n_variables)])
251
+ hw2 = self.aux_variables.hw2
252
+ h = self.variables[0]
253
+ hu0 = self.variables[1]
254
+ hu1 = self.variables[2]
255
+ hw0 = self.variables[3]
256
+ hw1 = self.variables[4]
257
+ b = self.variables[5]
258
+ p0 = self.variables[6]/h
259
+ p1 = self.variables[7]/h
260
+ param = self.parameters
261
+
262
+ u0 = hu0 / h
263
+ u1 = hu1 / h
264
+ w0 = hw0 / h
265
+ w1 = hw1 / h
266
+ w2 = hw2 /h
267
+
268
+
269
+ dhdt = self.aux_variables.dhdt
270
+ dhu0dt = self.aux_variables.dhu0dt
271
+ dhu1dt = self.aux_variables.dhu1dt
272
+ dhw0dt = self.aux_variables.dhw0dt
273
+ dhw1dt = self.aux_variables.dhw1dt
274
+ dhdx = self.aux_variables.dhdx
275
+ dhu0dx = self.aux_variables.dhu0dx
276
+ dhu1dx = self.aux_variables.dhu1dx
277
+ dhw0dx = self.aux_variables.dhw0dx
278
+ dhw1dx = self.aux_variables.dhw1dx
279
+ dhp0dx = self.aux_variables.dhp0dx
280
+ dhp1dx = self.aux_variables.dhp1dx
281
+ dbdx = self.aux_variables.dbdx
282
+ du0dx = self.aux_variables.du0dx
283
+ ddp0dxx = self.aux_variables.ddp0dxx
284
+ ddp1dxx = self.aux_variables.ddp1dxx
285
+
286
+ R[0] = dhdt
287
+ R[1] = dhu0dt + dhp0dx + 2 * p1 * dbdx
288
+ R[2] = dhu1dt -2*p1
289
+ R[3] = dhw0dt + dhp1dx - (3*p0 - p1)*dhdx -6*(p0-p1)*dbdx
290
+ R[4] = 6*(p0-p1)
291
+ R[5] = 0.
292
+ I1 = h*du0dx + 1/3 * dhu1dx + 1/3 * u1 * dhdx + 2*(w0 - u0 * dbdx)
293
+ I2 = h * du0dx + u1*dhdx + 2*(u1 * dbdx - w1)
294
+ R[6] = I1 + I2
295
+ R[7] = I1 - I2
296
+
297
+ delta = 0.0
298
+ R[6] += delta * (ddp0dxx )
299
+ R[7] += delta * (ddp1dxx)
300
+
301
+ return R
302
+
303
+ def eigenvalues(self):
304
+ ev = Matrix([0 for i in range(self.n_variables)])
305
+ return ev
306
+
307
+ class VAMFullImplicit(Model):
308
+ def __init__(
309
+ self,
310
+ boundary_conditions,
311
+ initial_conditions,
312
+ dimension=1,
313
+ fields=8,
314
+ aux_variables=[ 'hw2', 'dhdt', 'dhu0dt', 'dhu1dt', 'dhw0dt', 'dhw1dt', 'dhdx', 'dhu0dx', 'dhu1dx', 'dhw0dx', 'dhw1dx', 'dhp0dx', 'dhp1dx', 'dbdx','ddhp0dxx', 'ddhp1dxx', 'du0dx', 'du1dx'],
315
+ parameters={},
316
+ _default_parameters={"g": 9.81},
317
+ settings={},
318
+ settings_default={},
319
+ ):
320
+ self.variables = register_sympy_attribute(fields, "q")
321
+ self.n_variables = self.variables.length()
322
+ super().__init__(
323
+ dimension=dimension,
324
+ fields=fields,
325
+ aux_variables=aux_variables,
326
+ parameters=parameters,
327
+ _default_parameters=_default_parameters,
328
+ boundary_conditions=boundary_conditions,
329
+ initial_conditions=initial_conditions,
330
+ settings={**settings_default, **settings},
331
+ )
332
+
333
+ def source_implicit(self):
334
+ R = Matrix([0 for i in range(self.n_variables)])
335
+ hw2 = self.aux_variables.hw2
336
+ h = self.variables[0]
337
+ hu0 = self.variables[1]
338
+ hu1 = self.variables[2]
339
+ hw0 = self.variables[3]
340
+ hw1 = self.variables[4]
341
+ b = self.variables[5]
342
+ p0 = self.variables[6]/h
343
+ p1 = self.variables[7]/h
344
+ param = self.parameters
345
+
346
+ u0 = hu0 / h
347
+ u1 = hu1 / h
348
+ w0 = hw0 / h
349
+ w1 = hw1 / h
350
+ w2 = hw2 /h
351
+
352
+
353
+ dhdt = self.aux_variables.dhdt
354
+ dhu0dt = self.aux_variables.dhu0dt
355
+ dhu1dt = self.aux_variables.dhu1dt
356
+ dhw0dt = self.aux_variables.dhw0dt
357
+ dhw1dt = self.aux_variables.dhw1dt
358
+ dhdx = self.aux_variables.dhdx
359
+ dhu0dx = self.aux_variables.dhu0dx
360
+ dhu1dx = self.aux_variables.dhu1dx
361
+ dhw0dx = self.aux_variables.dhw0dx
362
+ dhw1dx = self.aux_variables.dhw1dx
363
+ dhp0dx = self.aux_variables.dhp0dx
364
+ dhp1dx = self.aux_variables.dhp1dx
365
+ dbdx = self.aux_variables.dbdx
366
+ du0dx = self.aux_variables.du0dx
367
+ ddhp0dxx = self.aux_variables.ddhp0dxx
368
+ ddhp1dxx = self.aux_variables.ddhp1dxx
369
+
370
+ R[0] = dhdt
371
+ R[1] = dhu0dt + dhp0dx + 2 * p1 * dbdx
372
+ R[2] = dhu1dt -2*p1
373
+ R[3] = dhw0dt + dhp1dx - (3*p0 - p1)*dhdx -6*(p0-p1)*dbdx
374
+ R[4] = 6*(p0-p1)
375
+ R[5] = 0.
376
+ I1 = h*du0dx + 1/3 * dhu1dx + 1/3 * u1 * dhdx + 2*(w0 - u0 * dbdx)
377
+ I2 = h * du0dx + u1*dhdx + 2*(u1 * dbdx - w1)
378
+ R[6] = I1
379
+ R[7] = I2
380
+
381
+ delta = 0.1
382
+ R[6] += delta * (ddhp0dxx )
383
+ R[7] += delta * (ddhp1dxx)
384
+
385
+ return R
386
+
387
+ def flux(self):
388
+ fx = Matrix([0 for i in range(self.n_variables)])
389
+ hw2 = self.aux_variables.hw2
390
+ h = self.variables[0]
391
+ hu0 = self.variables[1]
392
+ hu1 = self.variables[2]
393
+ hw0 = self.variables[3]
394
+ hw1 = self.variables[4]
395
+ param = self.parameters
396
+
397
+ u0 = hu0 / h
398
+ u1 = hu1 / h
399
+ w0 = hw0 / h
400
+ w1 = hw1 / h
401
+
402
+ fx[0] = hu0
403
+ fx[1] = hu0 * u0 + 1/3 * hu1 * u1 * u1
404
+ fx[2] = hu0 * w0 + 1/3 * hu1 * w1
405
+ fx[3] = 2*hu0 * u1
406
+ fx[4] = hu0 * w1 + u1 * (hw0 + 2/5*hw2)
407
+
408
+ return [fx]
409
+
410
+ def nonconservative_matrix(self):
411
+ nc = Matrix([[0 for i in range(self.n_variables)] for j in range(self.n_variables)])
412
+
413
+ hw2 = self.aux_variables.hw2
414
+ h = self.variables[0]
415
+ hu0 = self.variables[1]
416
+ hu1 = self.variables[2]
417
+ hw0 = self.variables[3]
418
+ hw1 = self.variables[4]
419
+ param = self.parameters
420
+
421
+ u0 = hu0 / h
422
+ u1 = hu1 / h
423
+ w0 = hw0 / h
424
+ w1 = hw1 / h
425
+ w2 = hw2 / h
426
+
427
+ nc[1, 0] = param.g * h
428
+ nc[3, 2] = u0
429
+ nc[4, 2] = - 1/5 * w2 + w0
430
+ nc[1, 5] = param.g * h
431
+ return [-nc]
432
+
433
+ def eigenvalues(self):
434
+ ev = Matrix([0 for i in range(self.n_variables)])
435
+ h = self.variables[0]
436
+ hu0 = self.variables[1]
437
+ hu1 = self.variables[2]
438
+ param = self.parameters
439
+
440
+ u0 = hu0 / h
441
+ u1 = hu1 / h
442
+
443
+ ev[0] = u0
444
+ ev[1] = u0 + 1/sqrt(3) * u1
445
+ ev[2] = u0 - 1/sqrt(3) * u1
446
+ ev[3] = u0 + sqrt(param.g * h + u1**2)
447
+ ev[4] = u0 - sqrt(param.g * h + u1**2)
448
+ ev[5] = 0
449
+ ev[6] = 0
450
+ ev[7] = 0
451
+
452
+ return ev
453
+
454
+
455
+
File without changes
@@ -0,0 +1,244 @@
1
+ import os
2
+ import json
3
+ import meshio
4
+ import numpy as np
5
+ import matplotlib.pyplot as plt
6
+ from matplotlib.collections import PolyCollection
7
+
8
+
9
+ # ---------- I/O ----------
10
+
11
+
12
+ def read_vtk_or_series(path, index=0, verbose=True):
13
+ """Read either .vtk or .vtk.series (relative paths handled)."""
14
+ path = os.path.abspath(path)
15
+
16
+ if path.endswith(".vtk"):
17
+ if verbose:
18
+ print(f"📁 Reading single VTK file: {path}")
19
+ return meshio.read(path)
20
+
21
+ if path.endswith(".vtk.series"):
22
+ with open(path, "r") as f:
23
+ series = json.load(f)
24
+
25
+ entries = series.get("files", [])
26
+ files, times = [], []
27
+ for e in entries:
28
+ fname = e.get("name") or e.get("filename") or e.get("file")
29
+ if fname:
30
+ files.append(fname)
31
+ times.append(e.get("time", None))
32
+
33
+ if not files:
34
+ raise ValueError("No valid file entries found in .vtk.series.")
35
+
36
+ n_files = len(files)
37
+ if verbose:
38
+ print(f"📂 Loaded VTK series: {path}")
39
+ print(f" • Available indices: {n_files}")
40
+ if any(t is not None for t in times):
41
+ print(" • Time values:", [t for t in times if t is not None])
42
+ print(f" • Reading index {index}")
43
+
44
+ if not (0 <= index < n_files):
45
+ raise IndexError(f"Index {index} out of range (0..{n_files - 1})")
46
+
47
+ base_dir = os.path.dirname(path)
48
+ file_path = os.path.join(base_dir, files[index])
49
+ return meshio.read(file_path)
50
+
51
+ raise ValueError("Expected a .vtk or .vtk.series file")
52
+
53
+
54
+ # ---------- Field listing ----------
55
+
56
+
57
+ def list_available_fields(mesh, print_out=True):
58
+ """List all available point and cell fields."""
59
+ fields = {
60
+ "point_data": list(mesh.point_data.keys()),
61
+ "cell_data": list(mesh.cell_data_dict.keys()),
62
+ }
63
+
64
+ if print_out:
65
+ print("\n📊 Available fields:")
66
+ if fields["point_data"]:
67
+ print(" • Point data:")
68
+ for name in fields["point_data"]:
69
+ arr = mesh.point_data[name]
70
+ print(f" - {name} ({arr.shape})")
71
+ else:
72
+ print(" • Point data: none")
73
+
74
+ if fields["cell_data"]:
75
+ print(" • Cell data:")
76
+ for name in fields["cell_data"]:
77
+ entry = mesh.cell_data_dict[name]
78
+ types = ", ".join(entry.keys())
79
+ print(f" - {name} (types: {types})")
80
+ else:
81
+ print(" • Cell data: none")
82
+ print()
83
+
84
+ return fields
85
+
86
+
87
+ # ---------- Cell helpers ----------
88
+
89
+
90
+ def get_cell_block(mesh, types):
91
+ for block in mesh.cells:
92
+ if block.type in types:
93
+ return block
94
+ return None
95
+
96
+
97
+ def get_cell_field_for_block(mesh, field_name, cell_block):
98
+ """Get cell-centered data for the specified block."""
99
+ if cell_block is None:
100
+ return None
101
+ ctype = cell_block.type
102
+ if field_name is not None:
103
+ field = mesh.cell_data_dict.get(field_name)
104
+ if field is None:
105
+ return None
106
+ return field.get(ctype)
107
+ for name, mapping in mesh.cell_data_dict.items():
108
+ if ctype in mapping:
109
+ return mapping[ctype]
110
+ return None
111
+
112
+
113
+ # ---------- 1D Plotting ----------
114
+
115
+
116
+ def plot_1d_mesh(mesh, ax, field_name=None, **_):
117
+ """
118
+ Plot a 1D mesh as a line plot on the given Axes.
119
+ Returns (vmin, vmax) for consistency.
120
+ """
121
+ points = mesh.points
122
+ x = points[:, 0]
123
+
124
+ line_block = get_cell_block(mesh, ("line", "line3"))
125
+ if line_block is not None:
126
+ connectivity = line_block.data
127
+ unique_order = []
128
+ for conn in connectivity:
129
+ for i in conn:
130
+ if i not in unique_order:
131
+ unique_order.append(i)
132
+ x = x[unique_order]
133
+
134
+ y = None
135
+ field_data = get_cell_field_for_block(mesh, field_name, line_block)
136
+ if field_data is not None:
137
+ cell_centers = np.array(
138
+ [np.mean(mesh.points[cell, 0]) for cell in line_block.data]
139
+ )
140
+ y = np.interp(x, cell_centers, field_data)
141
+ elif field_name and field_name in mesh.point_data:
142
+ y = mesh.point_data[field_name]
143
+ elif mesh.point_data:
144
+ field_name, y = next(iter(mesh.point_data.items()))
145
+
146
+ if y is None:
147
+ y = np.zeros_like(x)
148
+ field_name = field_name or "default"
149
+
150
+ ax.plot(x, y, "-o", markersize=3)
151
+ return float(np.min(y)), float(np.max(y))
152
+
153
+
154
+ # ---------- 2D Plotting ----------
155
+
156
+
157
+ def plot_2d_mesh(
158
+ mesh,
159
+ ax,
160
+ field_name=None,
161
+ show_legend=True,
162
+ legend_location="right",
163
+ cmap="viridis",
164
+ ):
165
+ """
166
+ Plot a 2D mesh (unstructured) using meshio connectivity.
167
+ Adds polygons to the provided Axes.
168
+ Returns (vmin, vmax).
169
+ """
170
+ points = mesh.points[:, :2]
171
+ cell_block = get_cell_block(mesh, ("triangle", "quad", "polygon"))
172
+ if cell_block is None:
173
+ raise ValueError("No 2D cells found.")
174
+
175
+ connectivity = cell_block.data
176
+ field_data = get_cell_field_for_block(mesh, field_name, cell_block)
177
+ if field_data is None:
178
+ field_data = np.zeros(len(connectivity))
179
+ field_name = field_name or "default"
180
+
181
+ polygons = [points[cell] for cell in connectivity]
182
+ vmin, vmax = float(field_data.min()), float(field_data.max())
183
+ if vmin == vmax:
184
+ vmin, vmax = vmin - 0.5, vmax + 0.5
185
+
186
+ coll = PolyCollection(
187
+ polygons,
188
+ array=field_data,
189
+ cmap=cmap,
190
+ norm=plt.Normalize(vmin=vmin, vmax=vmax),
191
+ edgecolors="k",
192
+ linewidths=0.2,
193
+ )
194
+ ax.add_collection(coll)
195
+ ax.autoscale()
196
+ ax.set_aspect("equal")
197
+
198
+ if show_legend:
199
+ if legend_location == "bottom":
200
+ cbar = plt.colorbar(
201
+ coll, ax=ax, orientation="horizontal", fraction=0.05, pad=0.05
202
+ )
203
+ else: # right
204
+ cbar = plt.colorbar(coll, ax=ax)
205
+ cbar.set_label(field_name)
206
+
207
+ return vmin, vmax
208
+
209
+
210
+ # ---------- Auto Dispatch ----------
211
+
212
+
213
+ def plot_mesh(
214
+ mesh,
215
+ ax,
216
+ field_name=None,
217
+ show_legend=True,
218
+ legend_location="right",
219
+ ):
220
+ """
221
+ Auto-detect mesh type (1D or 2D) and plot on the provided Axes.
222
+ Returns (vmin, vmax).
223
+ """
224
+ has_2d = get_cell_block(mesh, ("triangle", "quad", "polygon")) is not None
225
+ has_1d = get_cell_block(mesh, ("line", "line3")) is not None
226
+
227
+ if has_2d:
228
+ return plot_2d_mesh(mesh, ax, field_name, show_legend, legend_location)
229
+ elif has_1d:
230
+ return plot_1d_mesh(mesh, ax, field_name)
231
+ else:
232
+ raise ValueError(f"Unsupported mesh types: {[c.type for c in mesh.cells]}")
233
+
234
+
235
+ # # ---------- Example ----------
236
+
237
+ # if __name__ == "__main__":
238
+ # mesh = read_vtk_or_series("outputs/swe/out.vtk.series", index=0)
239
+ # list_available_fields(mesh)
240
+
241
+ # fig, ax = plt.subplots(figsize=(5, 4))
242
+ # vmin, vmax = plot_mesh(mesh, ax, field_name=None, show_legend=True, legend_location="right")
243
+ # print(f"Color range: {vmin:.3g} – {vmax:.3g}")
244
+ # plt.show()