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
mesh/mesh.py ADDED
@@ -0,0 +1,1239 @@
1
+ import os
2
+
3
+ try:
4
+ import h5py
5
+
6
+ _HAVE_H5PY = True
7
+ except ImportError:
8
+ _HAVE_H5PY = False
9
+
10
+
11
+ try:
12
+ from petsc4py import PETSc
13
+
14
+ _HAVE_PETSC = True
15
+ except ImportError:
16
+ _HAVE_PETSC = False
17
+
18
+
19
+ try:
20
+ import meshio
21
+
22
+ _HAVE_MESHIO = True
23
+ except:
24
+ _HAVE_MESHIO = False
25
+ from copy import deepcopy
26
+ from itertools import product
27
+ from typing import Union
28
+
29
+ import numpy as np
30
+ from attr import define
31
+
32
+ import zoomy_core.mesh.mesh_extrude as extrude
33
+ import zoomy_core.mesh.mesh_util as mesh_util
34
+ from zoomy_core.mesh.mesh_util import compute_subvolume, get_extruded_mesh_type
35
+ from zoomy_core.misc.custom_types import CArray, FArray, IArray
36
+ from zoomy_core.model.boundary_conditions import Periodic
37
+ from zoomy_core.misc import misc as misc
38
+
39
+
40
+ # petsc4py.init(sys.argv)
41
+
42
+
43
+ def build_monomial_indices(degree: int, dim: int):
44
+ """
45
+ Build the full set of monomial multi-indices for a given degree and dimension.
46
+
47
+ Parameters:
48
+ - degree (int): Maximum total polynomial degree
49
+ - dim (int): Number of dimensions
50
+
51
+ Returns:
52
+ - List of tuples: each tuple is a multi-index (α₁, ..., α_dim)
53
+ """
54
+ mon_indices = []
55
+ for powers in product(range(degree + 1), repeat=dim):
56
+ if sum(powers) <= degree:
57
+ if sum(powers) == 0:
58
+ continue
59
+ mon_indices.append(powers)
60
+ return mon_indices
61
+
62
+
63
+ def scale_lsq_derivative(mon_indices):
64
+ from math import factorial
65
+
66
+ import numpy as np
67
+
68
+ scale_factors = np.array(
69
+ [np.prod([factorial(k) for k in mi]) for mi in mon_indices]
70
+ ) # shape (n_monomials,)
71
+ return scale_factors
72
+
73
+
74
+ def find_derivative_indices(full_monomials_arr, requested_derivs_arr):
75
+ """
76
+ Args:
77
+ full_monomials_arr: shape (N, dim)
78
+ requested_derivs_arr: shape (M, dim)
79
+
80
+ Returns:
81
+ indices: shape (M,), where indices[i] gives the index of requested_derivs_arr[i]
82
+ in full_monomials_arr, or -1 if not found
83
+ """
84
+ # Ensure arrays
85
+ full_monomials_arr = np.array(full_monomials_arr)
86
+ requested_derivs_arr = np.array(requested_derivs_arr)
87
+
88
+ # Compare all requested derivatives with all monomials
89
+ matches = np.all(
90
+ full_monomials_arr[:, None, :] == requested_derivs_arr[None, :, :], axis=-1
91
+ ) # (N, M)
92
+
93
+ # Which monomials match each requested derivative?
94
+ found = np.any(matches, axis=0) # (M,)
95
+ indices = np.argmax(matches, axis=0) # (M,) — returns 0 even if not found
96
+
97
+ # Replace unfound indices with -1
98
+ indices = np.where(found, indices, -1)
99
+ return indices
100
+
101
+
102
+ def compute_derivatives(u, mesh, derivatives_multi_index=None):
103
+ A_glob = mesh.lsq_gradQ # shape (n_cells, n_neighbors, n_monomials)
104
+ neighbors = mesh.lsq_neighbors # list of neighbors per cell
105
+ mon_indices = mesh.lsq_monomial_multi_index # shape (n_monomials, dim)
106
+ # scale_factors = scale_lsq_derivative(mon_indices)
107
+ scale_factors = mesh.lsq_scale_factors
108
+
109
+ if derivatives_multi_index is None:
110
+ derivatives_multi_index = mon_indices
111
+ indices = find_derivative_indices(mon_indices, derivatives_multi_index)
112
+
113
+ def reconstruct_cell(A_loc, neighbor_idx, u_i):
114
+ u_neighbors = u[neighbor_idx]
115
+ delta_u = u_neighbors - u_i
116
+ return (scale_factors * (A_loc.T @ delta_u)).T # shape (n_monomials,)
117
+
118
+ out = np.zeros((A_glob.shape[0], len(
119
+ derivatives_multi_index)), dtype=float)
120
+ for i in range(A_glob.shape[0]):
121
+ A_loc = A_glob[i]
122
+ neighbor_idx = neighbors[i]
123
+ u_i = u[i]
124
+ out[i, :] = reconstruct_cell(A_loc, neighbor_idx, u_i)[indices]
125
+
126
+ return out
127
+
128
+
129
+ def get_polynomial_degree(mon_indices):
130
+ return max(sum(m) for m in mon_indices)
131
+
132
+
133
+ def get_required_monomials_count(degree, dim):
134
+ from math import comb
135
+
136
+ return comb(int(degree + dim), int(dim))
137
+
138
+
139
+ def build_vandermonde(cell_diffs, mon_indices):
140
+ n_neighbors, dim = cell_diffs.shape
141
+ n_monomials = len(mon_indices)
142
+ V = np.zeros((n_neighbors, n_monomials))
143
+ for i, powers in enumerate(mon_indices):
144
+ V[:, i] = np.prod(cell_diffs**powers, axis=1)
145
+ return V
146
+
147
+
148
+ def expand_neighbors(neighbors_list, initial_neighbors):
149
+ # Expand to the next ring: union of neighbors of neighbors
150
+ expanded = set(initial_neighbors)
151
+ for n in initial_neighbors:
152
+ expanded.update(neighbors_list[n])
153
+ return list(expanded)
154
+
155
+
156
+ def compute_gaussian_weights(dX, sigma=1.0):
157
+ distances = np.linalg.norm(dX, axis=1)
158
+ weights = np.exp(-((distances / sigma) ** 2))
159
+ return weights
160
+
161
+
162
+ def least_squares_reconstruction_local(
163
+ n_cells, dim, neighbors_list, cell_centers, lsq_degree
164
+ ):
165
+ """
166
+ Build the full set of monomial multi-indices for a given degree and dimension.
167
+
168
+ Parameters:
169
+ - degree (int): Maximum total polynomial degree
170
+ - dim (int): Number of dimensions
171
+
172
+ Returns:
173
+ - List of tuples: each tuple is a multi-index (α₁, ..., α_dim)
174
+ """
175
+ mon_indices = build_monomial_indices(lsq_degree, dim)
176
+ A_glob = []
177
+ neighbors_all = [] # to store expanded neighbors per cell
178
+ degree = get_polynomial_degree(mon_indices)
179
+ required_neighbors = get_required_monomials_count(degree, dim)
180
+
181
+ # First, expand neighborhoods and store them
182
+ for i_c in range(n_cells):
183
+ current_neighbors = list(neighbors_list[i_c])
184
+
185
+ # Expand neighbor rings until we have enough
186
+ while len(current_neighbors) < required_neighbors:
187
+ new_neighbors = expand_neighbors(neighbors_list, current_neighbors)
188
+ current_neighbors = list(set(new_neighbors) - {i_c})
189
+
190
+ neighbors_all.append(current_neighbors)
191
+
192
+ # Compute max neighbors after expansion
193
+ max_neighbors = max(len(nbrs) for nbrs in neighbors_all)
194
+
195
+ A_glob = []
196
+ neighbors_array = np.empty((n_cells, max_neighbors), dtype=int)
197
+
198
+ for i_c in range(n_cells):
199
+ current_neighbors = list(neighbors_all[i_c]) # previously expanded
200
+ n_nbr = len(current_neighbors)
201
+
202
+ # Expand further if still under-resolved
203
+ while n_nbr < max_neighbors:
204
+ extended_neighbors = expand_neighbors(
205
+ neighbors_list, current_neighbors)
206
+ extended_neighbors = list(
207
+ set(extended_neighbors) - {i_c}) # Remove self
208
+ # Keep only new neighbors not already present
209
+ new_neighbors = [
210
+ n for n in extended_neighbors if n not in current_neighbors
211
+ ]
212
+ current_neighbors.extend(new_neighbors)
213
+ n_nbr = len(current_neighbors)
214
+
215
+ # Stop if enough neighbors collected
216
+ if n_nbr >= max_neighbors:
217
+ break
218
+
219
+ # Trim or pad neighbor list to exact max_neighbors
220
+ if len(current_neighbors) >= max_neighbors:
221
+ trimmed_neighbors = current_neighbors[:max_neighbors]
222
+ else:
223
+ padding = [i_c] * (max_neighbors - len(current_neighbors))
224
+ trimmed_neighbors = current_neighbors + padding
225
+
226
+ neighbors_array[i_c, :] = trimmed_neighbors
227
+
228
+ # Build dX matrix for least squares
229
+ dX = np.zeros((max_neighbors, dim), dtype=float)
230
+ for j, neighbor in enumerate(trimmed_neighbors):
231
+ dX[j, :] = cell_centers[neighbor] - cell_centers[i_c]
232
+
233
+ # shape (max_neighbors, n_monomials)
234
+ V = build_vandermonde(dX, mon_indices)
235
+
236
+ # Compute weights
237
+ weights = compute_gaussian_weights(dX) # shape (n_neighbors,)
238
+ W = np.diag(weights) # shape (n_neighbors, n_neighbors)
239
+ VW = W @ V # shape (n_neighbors, n_monomials)
240
+ alpha = dX.min() * 10 ** (-8)
241
+ A_loc = (
242
+ np.linalg.pinv(VW.T @ VW + alpha * np.eye(VW.shape[1])) @ VW.T @ W
243
+ ) # shape (n_monomials, n_neighbors)
244
+ # A_loc = np.linalg.pinv(V).T # shape (n_monomials, max_neighbors)
245
+ A_glob.append(A_loc.T)
246
+
247
+ A_glob = np.array(A_glob) # shape (n_cells, n_monomials, max_neighbors)
248
+ return A_glob, neighbors_array, mon_indices
249
+
250
+
251
+ def get_physical_boundary_labels(filepath):
252
+ if not _HAVE_MESHIO:
253
+ raise RuntimeError(
254
+ "_write_to_vtk_from_vertices_edges requires meshio, which is not available."
255
+ )
256
+ mesh = meshio.read(filepath)
257
+ boundary_dict = {key: value[0] for key, value in mesh.field_data.items()}
258
+ return boundary_dict
259
+
260
+
261
+ def _compute_inradius_generic(cell_center, face_centers, face_normals):
262
+ """
263
+ strategy: find the shortest path from the center to each side (defined by face center and normal)
264
+ use the minimum of all these shortest paths
265
+ For a distorted element, the inradius might be zero. In this case, use minimal distance of center to face_centers
266
+ """
267
+ inradius = np.inf
268
+ for center, normal in zip(face_centers, face_normals):
269
+ distance = np.abs(np.dot(center - cell_center, normal))
270
+ inradius = min(inradius, distance)
271
+ if inradius <= 0:
272
+ inradius = np.array(face_centers - cell_center).min()
273
+ return inradius
274
+
275
+
276
+ def compute_cell_inradius(dm):
277
+ """
278
+ Error in petsc4py? dm.getMinRadius() always returns 0
279
+ strategy: find the shortest path from the center to each side (defined by face center and normal)
280
+ use the minimum of all these shortest paths
281
+ """
282
+ (cStart, cEnd) = dm.getHeightStratum(0)
283
+ (vStart, vEnd) = dm.getDepthStratum(0)
284
+ (eStart, eEnd) = dm.getDepthStratum(1)
285
+ inradius = []
286
+ for c in range(cStart, cEnd):
287
+ _, cell_center, _ = dm.computeCellGeometryFVM(c)
288
+ face_normals = []
289
+ face_centers = []
290
+ faces = dm.getCone(c)
291
+ for f in faces:
292
+ _, center, normal = dm.computeCellGeometryFVM(f)
293
+ face_normals.append(normal)
294
+ face_centers.append(center)
295
+ inradius.append(
296
+ _compute_inradius_generic(cell_center, face_centers, face_normals)
297
+ )
298
+ return np.array(inradius, dtype=float)
299
+
300
+
301
+ def get_mesh_type_from_dm(num_faces_per_cell, dim):
302
+ if dim == 1:
303
+ if num_faces_per_cell == 2:
304
+ return "line"
305
+ else:
306
+ assert False
307
+ elif dim == 2:
308
+ if num_faces_per_cell == 3:
309
+ return "triangle"
310
+ elif num_faces_per_cell == 4:
311
+ return "quad"
312
+ else:
313
+ assert False
314
+ elif dim == 3:
315
+ if num_faces_per_cell == 4:
316
+ return "tetra"
317
+ elif num_faces_per_cell == 6:
318
+ return "edge"
319
+ elif num_faces_per_cell == 8:
320
+ return "hexahedron"
321
+ else:
322
+ assert False
323
+ assert False
324
+
325
+
326
+ def _boundary_dict_to_list(d):
327
+ l = []
328
+ for i, vs in enumerate(d.values()):
329
+ l += vs
330
+ return l
331
+
332
+
333
+ def _boundary_dict_indices(d):
334
+ indices = []
335
+ index = 0
336
+ for values in d.values():
337
+ indices += [index for v in values]
338
+ index += 1
339
+ return np.array(indices, dtype=int)
340
+
341
+
342
+ def _get_neighberhood(dm, cell, cStart=0):
343
+ neighbors = (
344
+ np.array(
345
+ [dm.getSupport(f)[dm.getSupport(f) != cell][0]
346
+ for f in dm.getCone(cell)],
347
+ dtype=int,
348
+ )
349
+ - cStart
350
+ )
351
+ return neighbors
352
+
353
+
354
+ def _fill_neighborhood(dm, neighbors, max_neighbors, cStart=0):
355
+ new_potential_neighbors = []
356
+ for cell in neighbors:
357
+ new_potential_neighbors += list(_get_neighberhood(dm,
358
+ cell, cStart=cStart))
359
+ new_potential_neighbors = np.array(new_potential_neighbors)
360
+ new_neighbors = np.setdiff1d(new_potential_neighbors, neighbors)
361
+ neighbors = np.concatenate((neighbors, new_neighbors))[:max_neighbors]
362
+ if len(neighbors) == max_neighbors:
363
+ return neighbors
364
+ else:
365
+ return _fill_neighborhood(dm, neighbors, max_neighbors, cStart=cStart)
366
+
367
+
368
+ @define(slots=True, frozen=True)
369
+ class Mesh:
370
+ dimension: int
371
+ type: str
372
+ n_cells: int
373
+ n_inner_cells: int
374
+ n_faces: int
375
+ n_vertices: int
376
+ n_boundary_faces: int
377
+ n_faces_per_cell: int
378
+ vertex_coordinates: FArray
379
+ cell_vertices: IArray
380
+ cell_faces: IArray
381
+ cell_volumes: FArray
382
+ cell_centers: FArray
383
+ cell_inradius: FArray
384
+ cell_neighbors: IArray
385
+ boundary_face_cells: IArray
386
+ boundary_face_ghosts: IArray
387
+ boundary_face_function_numbers: IArray
388
+ boundary_face_physical_tags: IArray
389
+ boundary_face_face_indices: IArray
390
+ face_cells: IArray
391
+ # face_cell_face_index: IArray
392
+ face_normals: FArray
393
+ face_volumes: FArray
394
+ face_centers: FArray
395
+ face_subvolumes: FArray
396
+ face_neighbors: IArray
397
+ boundary_conditions_sorted_physical_tags: IArray
398
+ boundary_conditions_sorted_names: CArray
399
+ lsq_gradQ: FArray
400
+ lsq_neighbors: FArray
401
+ lsq_monomial_multi_index: int
402
+ lsq_scale_factors: FArray
403
+ z_ordering: IArray
404
+
405
+ def resolve_periodic_bcs(self, bcs):
406
+ """
407
+ Goal: if 'apply_boundary_condition' is called, the ghost cell value is computed, given an input cell value funtion.
408
+ In case of a periodic BC, this is NOT the adjacent cell. So I need to change the 'boundary_face_cell' for the periodic
409
+ cells to point to the right data location!
410
+ This is why we only overwrite the 'mesh.boundary_face_cell' in the end.
411
+ Furthermore, I CANNOT alter any ordering! However, I need to sort the two boundaries such that the e.g.
412
+ left and right border are mapped correctly, as the boundary cells are not ordered.
413
+ As ghost/inner cells is confusing here, I rather like to use 'from' and 'to', as 'from' data from which bc is copied and 'to' stands for the boundary where it is copied to. As we only change the cells where the data is taken from (from the adjacent to the periodic cell), we only alter the 'boundary_face_cell' in the end.
414
+ """
415
+ dict_physical_name_to_index = {
416
+ v: i for i, v in enumerate(self.boundary_conditions_sorted_names)
417
+ }
418
+ dict_function_index_to_physical_tag = {
419
+ i: v for i, v in enumerate(self.boundary_conditions_sorted_physical_tags)
420
+ }
421
+
422
+ boundary_face_cells_copy = deepcopy(self.boundary_face_cells)
423
+
424
+ for i_bc, bc in enumerate(bcs._boundary_conditions):
425
+ if type(bc) == Periodic:
426
+ from_physical_tag = dict_function_index_to_physical_tag[
427
+ dict_physical_name_to_index[bc.periodic_to_physical_tag]
428
+ ]
429
+ to_physical_tag = dict_function_index_to_physical_tag[
430
+ dict_physical_name_to_index[bc.tag]
431
+ ]
432
+
433
+ mask_face_from = self.boundary_face_physical_tags == from_physical_tag
434
+ mask_face_to = self.boundary_face_physical_tags == to_physical_tag
435
+
436
+ # I want to copy from boundary_face_cells to boundary_face_ghosts!
437
+ from_cells = self.boundary_face_cells[mask_face_from]
438
+ to_cells = self.boundary_face_ghosts[mask_face_to]
439
+
440
+ from_coords = self.cell_centers[:, from_cells]
441
+ to_coords = self.cell_centers[:, to_cells]
442
+
443
+ # sort not dimension by dimension, but most significant dimension to least significant dimension
444
+ # determine significance by max difference
445
+ significance_per_dimension = [
446
+ from_coords[d, :].max() - from_coords[d, :].min()
447
+ for d in range(self.dimension)
448
+ ]
449
+ _significance_per_dimension = [
450
+ to_coords[d, :].max() - to_coords[d, :].min()
451
+ for d in range(self.dimension)
452
+ ]
453
+ # reverse the order of lexsort such that the most important is first IS NOT NEEDED, since lexsort starts sorting by the last entry in the list
454
+ sort_order_significance = np.lexsort(
455
+ [significance_per_dimension])
456
+
457
+ from_cells_sort_order = np.lexsort(
458
+ [from_coords[d, :] for d in sort_order_significance]
459
+ )
460
+ to_cells_sort_order = np.lexsort(
461
+ [to_coords[d, :] for d in sort_order_significance]
462
+ )
463
+
464
+ # generates indices from 0 to number of ghost_cells (total)
465
+ indices = np.array(list(range(mask_face_to.shape[0])))
466
+ # masks away all cells that do not belong to this tag
467
+ indices_to = indices[mask_face_to]
468
+ # sort the indices
469
+ indices_to_sort = indices_to[to_cells_sort_order]
470
+
471
+ indices_from = indices[mask_face_from]
472
+ indices_from_sort = indices_from[from_cells_sort_order]
473
+
474
+ self.boundary_face_cells[indices_to_sort] = boundary_face_cells_copy[
475
+ indices_from_sort
476
+ ]
477
+
478
+ @classmethod
479
+ def create_1d(cls, domain: tuple[float, float], n_inner_cells: int, lsq_degree=1):
480
+ xL = domain[0]
481
+ xR = domain[1]
482
+
483
+ n_cells = n_inner_cells + 2
484
+ n_vertices = n_inner_cells + 1
485
+ dimension = 1
486
+ n_faces_per_cell = 2
487
+ n_faces = n_inner_cells + 1
488
+ n_boundary_faces = 2
489
+ dx = (xR - xL) / n_inner_cells
490
+ vertex_coordinates = np.zeros((n_vertices, 1))
491
+ vertex_coordinates[:, 0] = np.linspace(xL, xR, n_vertices, dtype=float)
492
+ cell_vertices = np.zeros((n_inner_cells, n_faces_per_cell), dtype=int)
493
+ cell_vertices[:, 0] = list(range(0, n_vertices - 1))
494
+ cell_vertices[:, 1] = list(range(1, n_vertices))
495
+ cell_volumes = dx * np.ones(n_cells, dtype=float)
496
+ cell_inradius = dx / 2 * np.ones(n_cells, dtype=float)
497
+
498
+ face_normals = np.zeros((n_faces, dimension), dtype=float)
499
+ face_volumes = np.ones((n_faces), dtype=float)
500
+
501
+ cell_centers = np.zeros((n_cells, 3), dtype=float)
502
+ cell_centers[:n_inner_cells, 0] = np.arange(xL + dx / 2, xR, dx)
503
+ cell_centers[n_inner_cells, 0] = xL - dx / 2
504
+ cell_centers[n_inner_cells + 1, 0] = xR + dx / 2
505
+ cell_neighbors = (n_cells + 1) * \
506
+ np.ones((n_cells, n_faces_per_cell), dtype=int)
507
+
508
+ cell_faces = np.empty((n_inner_cells, n_faces_per_cell), dtype=int)
509
+ cell_faces[:, 0] = list(range(0, n_faces - 1))
510
+ cell_faces[:, 1] = list(range(1, n_faces))
511
+
512
+ # inner cells
513
+ for i_cell in range(0, n_cells):
514
+ cell_neighbors[i_cell, :] = [i_cell - 1, i_cell + 1]
515
+ # left neighbor of 0is the first ghost
516
+ cell_neighbors[0, 0] = n_inner_cells
517
+ # right neighbor of n_inner_cell is the second ghost
518
+ cell_neighbors[n_inner_cells - 1, 1] = n_inner_cells + 1
519
+ # left neighbor of first ghost is empty, but we add the neighbor of the neighbor
520
+ cell_neighbors[n_inner_cells, 0] = 1
521
+ # right neighbor of first ghost is first cell
522
+ cell_neighbors[n_inner_cells, 1] = 0
523
+ # left neighbor of second ghost is last inner
524
+ cell_neighbors[n_inner_cells + 1, 0] = n_inner_cells - 1
525
+ # right neighbor of second ghost is empty, but we add the neighbor of the neighbor
526
+ cell_neighbors[n_inner_cells + 1, 1] = n_inner_cells - 2
527
+
528
+ for i_face in range(0, n_faces):
529
+ face_normals[i_face, 0] = 1.0
530
+
531
+ boundary_face_cells = np.array([0, n_inner_cells - 1], dtype=int)
532
+ boundary_face_ghosts = np.array(
533
+ [n_inner_cells, n_inner_cells + 1], dtype=int)
534
+ boundary_face_function_numbers = np.empty(
535
+ (n_boundary_faces), dtype=int)
536
+ boundary_face_function_numbers[0] = 0
537
+ boundary_face_function_numbers[1] = 1
538
+ boundary_face_physical_tags = np.array([0, 1], dtype=int)
539
+ boundary_face_face_indices = np.array([0, n_faces - 1], dtype=int)
540
+
541
+ face_cells = np.empty((n_faces, 2), dtype=int)
542
+ # face_cell_face_index = (n_faces + 1)*np.ones((n_faces, 2), dtype=int)
543
+ face_cells[1: n_faces - 1, 0] = list(range(0, n_inner_cells - 1))
544
+ face_cells[1: n_faces - 1, 1] = list(range(1, n_inner_cells))
545
+ # face_cell_face_index[1:n_faces-1, 0] = 1
546
+ # face_cell_face_index[1:n_faces-1, 1] = 0
547
+ face_cells[0, 0] = n_inner_cells
548
+ # face_cell_face_index[0, 0] = 0
549
+ face_cells[0, 1] = 0
550
+ # face_cell_face_index[0, 1] = 0
551
+ face_cells[-1, 0] = n_inner_cells - 1
552
+ # face_cell_face_index[-1, 0] = 1
553
+ face_cells[-1, 1] = n_inner_cells + 1
554
+ # face_cell_face_index[-1, 1] = 0
555
+ face_centers = 0.5 * (
556
+ cell_centers[face_cells[:, 0]] + cell_centers[face_cells[:, 1]]
557
+ )
558
+
559
+ face_subvolumes = np.empty((n_faces, 2), dtype=float)
560
+ face_subvolumes[:, 0] = dx / 2
561
+ face_subvolumes[:, 1] = dx / 2
562
+
563
+ boundary_conditions_sorted_physical_tags = np.array([0, 1], dtype=int)
564
+ boundary_conditions_sorted_names = np.array(["left", "right"])
565
+
566
+ lsq_gradQ = np.zeros((n_cells, dimension, n_cells), dtype=float)
567
+ deltaQ = np.zeros((n_cells, n_faces_per_cell, n_cells), dtype=float)
568
+ cell_centers = cell_centers[:, :dimension]
569
+
570
+ polynomial_degree = 1
571
+ n_neighbors = n_faces_per_cell * polynomial_degree
572
+ dim = 1
573
+ lsq_gradQ, lsq_neighbors, lsq_monomial_multi_index = (
574
+ least_squares_reconstruction_local(
575
+ n_cells, dim, cell_neighbors, cell_centers, lsq_degree
576
+ )
577
+ )
578
+ lsq_scale_factors = scale_lsq_derivative(lsq_monomial_multi_index)
579
+
580
+ n_face_neighbors = 2
581
+ face_neighbors = (n_cells + 1) * \
582
+ np.ones((n_faces, n_face_neighbors), dtype=int)
583
+
584
+ for i_f, neighbors in enumerate(face_cells):
585
+ face_neighbors[i_f] = neighbors
586
+
587
+ z_ordering = np.array([-1], dtype=float)
588
+
589
+ # return cls(dimension, 'line', n_cells, n_cells + 1, 2, n_faces_per_element, vertex_coordinates, element_vertices, element_face_areas, element_centers, element_volume, element_inradius, element_face_normals, element_n_neighbors, element_neighbors, element_neighbors_face_index, boundary_face_vertices, boundary_face_corresponding_element, boundary_face_element_face_index, boundary_face_tag, boundary_tag_names)
590
+ return cls(
591
+ dimension,
592
+ "line",
593
+ n_cells,
594
+ n_inner_cells,
595
+ n_faces,
596
+ n_vertices,
597
+ n_boundary_faces,
598
+ n_faces_per_cell,
599
+ vertex_coordinates.T,
600
+ cell_vertices.T,
601
+ cell_faces.T,
602
+ cell_volumes,
603
+ cell_centers.T,
604
+ cell_inradius,
605
+ cell_neighbors,
606
+ boundary_face_cells.T,
607
+ boundary_face_ghosts.T,
608
+ boundary_face_function_numbers,
609
+ boundary_face_physical_tags,
610
+ boundary_face_face_indices.T,
611
+ face_cells.T,
612
+ face_normals.T,
613
+ face_volumes,
614
+ face_centers,
615
+ face_subvolumes,
616
+ face_neighbors,
617
+ boundary_conditions_sorted_physical_tags,
618
+ boundary_conditions_sorted_names,
619
+ lsq_gradQ,
620
+ lsq_neighbors,
621
+ lsq_monomial_multi_index,
622
+ lsq_scale_factors,
623
+ z_ordering,
624
+ )
625
+
626
+ def _compute_ascending_order_structured_axis(self):
627
+ cell_centers = self.cell_centers.T
628
+ dimension = self.dimension
629
+ if dimension == 1:
630
+ order = np.lexsort((cell_centers[:, 0],))
631
+ elif dimension == 2:
632
+ order = np.lexsort(
633
+ (
634
+ cell_centers[:, 0],
635
+ cell_centers[:, 1],
636
+ )
637
+ )
638
+ elif dimension == 3:
639
+ order = np.lexsort(
640
+ (
641
+ cell_centers[:, 0],
642
+ cell_centers[:, 1],
643
+ cell_centers[:, 2],
644
+ )
645
+ )
646
+ else:
647
+ assert False
648
+
649
+ # find number of cells in z (Nz) from first column
650
+ Nz = 1
651
+ while cell_centers[order[Nz], 2] > cell_centers[order[Nz - 1], 2]:
652
+ Nz += 1
653
+ Nz += 1
654
+
655
+ # partition order into [(Nx x Ny) , (Nz)]
656
+ N = int(cell_centers.shape[0] / Nz)
657
+ assert cell_centers.shape[0] == N * Nz
658
+ order = order.reshape((N, Nz))
659
+
660
+ coords_z = order[0]
661
+ for o in order:
662
+ assert o == coords_z
663
+
664
+ return order
665
+
666
+ @classmethod
667
+ def from_gmsh(cls, filepath, allow_z_integration=False, lsq_degree=1):
668
+ if not _HAVE_PETSC:
669
+ raise RuntimeError(
670
+ "Mesh.from_gmsh() requires petsc4py, which is not available."
671
+ )
672
+ dm = PETSc.DMPlex().createFromFile(filepath, comm=PETSc.COMM_WORLD)
673
+ boundary_dict = get_physical_boundary_labels(filepath)
674
+ (cStart, cEnd) = dm.getHeightStratum(0)
675
+ (vStart, vEnd) = dm.getDepthStratum(0)
676
+ # (eStart, eEnd) = dm.getDepthStratum(1)
677
+ (eStart, eEnd) = dm.getHeightStratum(1)
678
+ gdm = dm.clone()
679
+ gdm.constructGhostCells()
680
+ (cgStart, cgEnd) = gdm.getHeightStratum(0)
681
+ (vgStart, vgEnd) = gdm.getDepthStratum(0)
682
+ # (egStart, egEnd) = gdm.getDepthStratum(1)
683
+ (egStart, egEnd) = gdm.getHeightStratum(1)
684
+
685
+ dim = dm.getDimension()
686
+ n_faces_per_cell = len(gdm.getCone(cgStart))
687
+ n_vertices_per_cell = n_faces_per_cell
688
+ if dim > 2:
689
+ transitive_closure_points, transitive_closure_orientation = (
690
+ dm.getTransitiveClosure(cStart, useCone=True)
691
+ )
692
+ n_vertices_per_cell = transitive_closure_points[
693
+ np.logical_and(
694
+ transitive_closure_points >= vStart,
695
+ transitive_closure_points < vEnd,
696
+ )
697
+ ].shape[0]
698
+ n_cells = cgEnd - cgStart
699
+ n_inner_cells = cEnd - cStart
700
+ n_faces = egEnd - egStart
701
+ n_vertices = vEnd - vStart
702
+ cell_vertices = np.zeros(
703
+ (n_inner_cells, n_vertices_per_cell), dtype=int)
704
+ cell_faces = np.zeros((n_inner_cells, n_faces_per_cell), dtype=int)
705
+ cell_centers = np.zeros((n_cells, 3), dtype=float)
706
+ # I create cell_volumes of size n_cells because then I can avoid an if clause in the numerical flux computation. The values will be delted after using apply_boundary_conditions anyways
707
+ cell_volumes = np.ones((n_cells), dtype=float)
708
+ cell_inradius = np.zeros((n_cells), dtype=float)
709
+ cell_inradius[:n_inner_cells] = compute_cell_inradius(dm)
710
+ for i_c, c in enumerate(range(cStart, cEnd)):
711
+ cell_volume, cell_center, cell_normal = dm.computeCellGeometryFVM(
712
+ c)
713
+ transitive_closure_points, transitive_closure_orientation = (
714
+ dm.getTransitiveClosure(c, useCone=True)
715
+ )
716
+ _cell_vertices = transitive_closure_points[
717
+ np.logical_and(
718
+ transitive_closure_points >= vStart,
719
+ transitive_closure_points < vEnd,
720
+ )
721
+ ]
722
+ # _cell_vertices_orientation = transitive_closure_orientation[np.logical_and(transitive_closure_points >= vStart, transitive_closure_points <vEnd)]
723
+ assert _cell_vertices.shape[0] == cell_vertices.shape[1]
724
+ # assert (_cell_vertices_orientation == 0).all()
725
+ cell_vertices[i_c, :] = _cell_vertices - vStart
726
+ cell_centers[i_c, :dim] = cell_center[:dim]
727
+ cell_volumes[i_c] = cell_volume
728
+
729
+ vertex_coordinates = np.array(gdm.getCoordinates()).reshape((-1, dim))
730
+ boundary_face_cells = {k: [] for k in boundary_dict.values()}
731
+ boundary_face_ghosts = {k: [] for k in boundary_dict.values()}
732
+ boundary_face_face_indices = {k: [] for k in boundary_dict.values()}
733
+ boundary_face_physical_tags = {k: [] for k in boundary_dict.values()}
734
+ face_cells = []
735
+ # face_cell_face_index = np.zeros((n_faces, 2), dtype=int)
736
+ face_normals = []
737
+ face_volumes = []
738
+ face_centers = []
739
+ face_subvolumes = []
740
+ allowed_keys = []
741
+ vertex_coordinates = np.array(dm.getCoordinates()).reshape((-1, dim))
742
+
743
+ def get_face_vertices(dim, gdm, vgStart, e):
744
+ if dim == 2:
745
+ return gdm.getCone(e) - vgStart
746
+ elif dim == 3:
747
+ face_vertices = set()
748
+ face_edges = gdm.getCone(e)
749
+ for edge in face_edges:
750
+ face_vertices.update(gdm.getCone(edge))
751
+ return np.array(list(face_vertices), dtype=int) - vgStart
752
+ else:
753
+ assert False
754
+
755
+ for e in range(egStart, egEnd):
756
+ label = gdm.getLabelValue("Face Sets", e)
757
+ face_volume, face_center, face_normal = gdm.computeCellGeometryFVM(
758
+ e)
759
+ face_vertices = get_face_vertices(dim, gdm, vgStart, e)
760
+ face_vertices_coords = vertex_coordinates[face_vertices]
761
+ _face_cells = gdm.getSupport(e)
762
+
763
+ _, _cell_center, _ = gdm.computeCellGeometryFVM(_face_cells[0])
764
+ _face_subvolume = np.zeros(2, dtype=float)
765
+ _face_subvolume[0] = compute_subvolume(
766
+ face_vertices_coords, _cell_center, dim
767
+ )
768
+ if _face_cells[1] < n_inner_cells:
769
+ _, _cell_center, _ = gdm.computeCellGeometryFVM(_face_cells[1])
770
+ _face_subvolume[1] = compute_subvolume(
771
+ face_vertices_coords, _cell_center, dim
772
+ )
773
+
774
+ # 2 cells support an face. Ghost cell is the one with the higher number
775
+ if label > -1:
776
+ allowed_keys.append(label)
777
+ boundary_cell = gdm.getSupport(e).min() - cStart
778
+ boundary_ghost = gdm.getSupport(e).max() - cStart
779
+ boundary_face_cells[label].append(boundary_cell)
780
+ boundary_face_ghosts[label].append(boundary_ghost)
781
+ boundary_face_face_indices[label].append(e - egStart)
782
+ boundary_face_physical_tags[label].append(label)
783
+ # boundary_face_vertices[label].append(gdm.getCone(e)-vStart)
784
+ # for periodic boudnary conditions, I need the ghost cell to have a cell_center. I copy the one from the related inner cell.
785
+ _face_cell = gdm.getSupport(e).min()
786
+ _face_ghost = gdm.getSupport(e).max()
787
+ cell_centers[_face_ghost, :dim] = (
788
+ cell_centers[_face_cell, :dim]
789
+ + 2
790
+ * ((face_center - cell_centers[_face_cell, :dim]) @ face_normal)
791
+ * face_normal
792
+ )
793
+ cell_inradius[_face_ghost] = cell_inradius[_face_cell]
794
+ cell_volumes[_face_ghost] = cell_volumes[_face_cell]
795
+ # subvolumes of the ghost cell are computes wrongly. In this case, copy the value from the inner cell.
796
+ if _face_cells[0] > _face_cells[1]:
797
+ _face_subvolume[0] = _face_subvolume[1]
798
+ else:
799
+ _face_subvolume[1] = _face_subvolume[0]
800
+
801
+
802
+ face_centers.append(face_center)
803
+ _face_cells = gdm.getSupport(e) - cStart
804
+ face_volumes.append(face_volume)
805
+ face_normals.append(face_normal)
806
+ face_cells.append(_face_cells)
807
+ face_subvolumes.append(_face_subvolume)
808
+
809
+ # I only want to iterate over the inner cells, but in the ghosted mesh
810
+ for i_c, c in enumerate(range(cgStart, cgStart + n_inner_cells)):
811
+ faces = gdm.getCone(c) - egStart
812
+ cell_faces[i_c, :] = faces
813
+
814
+ # least squares liner reconstruction
815
+ # Consider 2d, three points, scalar field, then the reconstruction is for a quad mesh
816
+ # DU = [u_{i+1, j} - u_{i,j}, u_{i-1, j} - u_{i,j}, u_{i, j+1} - u_{i,j}, u_{i, j-1} - u_{i,j}] \in \mathbb{R}^{4}
817
+ # DX = [[x_{i+1, j} - x_{i,j}, x_{i-1, j} - x_{i,j}, x_{i, j+1} - x_{i,j}, x_{i, j-1} - x_{i,j}],
818
+ # [y_{i+1, j} - y_{i,j}, y_{i-1, j} - y_{i,j}, y_{i, j+1} - y_{i,j}, y_{i, j-1} - y_{i,j}]] \in \mathbb{R}^{4x2}
819
+ # S = [S_x, S_y] \mathbb{R}^{2}
820
+ # solve DU = DX \dot S via normal equation
821
+ # A = (DX.T \dot DX)^{-1} DX.T \mathbb{R}^{2x4}
822
+ # so the solution is given by S = A \dot DU
823
+ # the vectorized version is more complicated. I have the vectorization (...) in the first dimension of size n_cells
824
+ # However, I do not want to apply the matrix vector product on DU (such that I can get the reconstruction with a single matrix vector product in the solver), but rather on a scalar field q \in \mathbb{R}^{n_cells}. This requires the need of a discretization matrix D \in \mathbb{R}^{n_cells x 4 x n_cells}, such that Dq = DU \in \mathbb{n_cells x 4}, where the first dimension is the dimension of vectorization
825
+
826
+ # cell_neighbors = np.zeros(1)
827
+ lsq_gradQ = np.zeros(1)
828
+ # NON_VECTORIZED CASE
829
+ polynomial_degree = 1
830
+ n_neighbors = n_faces_per_cell * polynomial_degree
831
+ cell_neighbors = (n_cells + 1) * \
832
+ np.ones((n_cells, n_neighbors), dtype=int)
833
+
834
+ for i_c, c in enumerate(range(cgStart, cgEnd)):
835
+ # GET NEIGHBORHOOD
836
+ neighbors = _get_neighberhood(gdm, c, cStart=cgStart)
837
+ assert not (i_c == neighbors).any()
838
+ _n_neighbors = neighbors.shape[0]
839
+ if _n_neighbors == 1:
840
+ neighbors_of_neighbor = _get_neighberhood(
841
+ gdm, neighbors[0] + cgStart, cStart=cgStart
842
+ )
843
+ assert len(neighbors_of_neighbor) == n_faces_per_cell
844
+ neighbors = np.setdiff1d(
845
+ np.union1d(neighbors_of_neighbor, neighbors), [c]
846
+ )
847
+ cell_neighbors[i_c, :] = neighbors
848
+
849
+ lsq_gradQ, lsq_neighbors, lsq_monomial_multi_index = (
850
+ least_squares_reconstruction_local(
851
+ n_cells, dim, cell_neighbors, cell_centers[:, :dim], lsq_degree
852
+ )
853
+ )
854
+ lsq_scale_factors = scale_lsq_derivative(lsq_monomial_multi_index)
855
+
856
+ n_face_neighbors = (2 * (n_faces_per_cell + 1) - 2) * polynomial_degree
857
+ face_neighbors = (n_cells + 1) * \
858
+ np.ones((n_faces, n_face_neighbors), dtype=int)
859
+
860
+ for i_f, f in enumerate(range(egStart, egEnd)):
861
+ # GET NEIGHBORHOOD
862
+ neighbors = _fill_neighborhood(
863
+ gdm, gdm.getSupport(f), n_face_neighbors, cgStart
864
+ )
865
+ face_neighbors[i_f, :] = neighbors
866
+
867
+ face_volumes = np.array(face_volumes, dtype=float)
868
+ _face_centers = np.array(face_centers, dtype=float)
869
+ face_centers = np.zeros((n_faces, 3), dtype=float)
870
+ face_centers[:, :dim] = _face_centers[:, :dim]
871
+ face_normals = np.array(face_normals, dtype=float)
872
+ face_subvolumes = np.array(face_subvolumes, dtype=float)
873
+
874
+ face_cells = np.array(face_cells, dtype=int)
875
+ # face_cell_face_index = np.array(face_cell_face_index, dtype=int)
876
+ boundary_face_function_numbers = _boundary_dict_indices(
877
+ boundary_face_cells)
878
+
879
+ # get rid of empty keys in the boundary_dict (e.g. no surface values in 2d)
880
+ boundary_dict_inverted = {v: k for k, v in boundary_dict.items()}
881
+ boundary_dict_reduced = {
882
+ k: boundary_dict_inverted[k] for k in allowed_keys}
883
+
884
+ # sort the dict by the values
885
+ sorted_keys = np.array(list(boundary_dict_reduced.keys()), dtype=int)
886
+ sorted_keys.sort()
887
+ boundary_dict = {k: boundary_dict_reduced[k] for k in sorted_keys}
888
+ boundary_face_cells = {k: boundary_face_cells[k] for k in sorted_keys}
889
+ boundary_face_ghosts = {
890
+ k: boundary_face_ghosts[k] for k in sorted_keys}
891
+ boundary_face_face_indices = {
892
+ k: boundary_face_face_indices[k] for k in sorted_keys
893
+ }
894
+
895
+ boundary_conditions_sorted_physical_tags = np.array(
896
+ list(boundary_dict.keys()), dtype=int
897
+ )
898
+ boundary_conditions_sorted_names = np.array(
899
+ list(boundary_dict.values()), dtype="str"
900
+ )
901
+ boundary_face_cells = np.array(
902
+ _boundary_dict_to_list(boundary_face_cells), dtype=int
903
+ )
904
+ boundary_face_ghosts = np.array(
905
+ _boundary_dict_to_list(boundary_face_ghosts), dtype=int
906
+ )
907
+ boundary_face_physical_tags = np.array(
908
+ _boundary_dict_to_list(boundary_face_physical_tags), dtype=int
909
+ )
910
+ boundary_face_face_indices = np.array(
911
+ _boundary_dict_to_list(boundary_face_face_indices), dtype=int
912
+ )
913
+ n_boundary_faces = boundary_face_cells.shape[0]
914
+
915
+ mesh_type = get_mesh_type_from_dm(n_faces_per_cell, dim)
916
+
917
+ z_ordering = np.array([-1], dtype=float)
918
+
919
+ obj = cls(
920
+ dim,
921
+ mesh_type,
922
+ n_cells,
923
+ n_inner_cells,
924
+ n_faces,
925
+ n_vertices,
926
+ n_boundary_faces,
927
+ n_faces_per_cell,
928
+ vertex_coordinates.T,
929
+ cell_vertices.T,
930
+ cell_faces.T,
931
+ cell_volumes,
932
+ cell_centers.T,
933
+ cell_inradius,
934
+ cell_neighbors,
935
+ boundary_face_cells.T,
936
+ boundary_face_ghosts.T,
937
+ boundary_face_function_numbers,
938
+ boundary_face_physical_tags,
939
+ boundary_face_face_indices.T,
940
+ face_cells.T,
941
+ face_normals.T,
942
+ face_volumes,
943
+ face_centers,
944
+ face_subvolumes,
945
+ face_neighbors,
946
+ boundary_conditions_sorted_physical_tags,
947
+ boundary_conditions_sorted_names,
948
+ lsq_gradQ,
949
+ lsq_neighbors,
950
+ lsq_monomial_multi_index,
951
+ lsq_scale_factors,
952
+ z_ordering,
953
+ )
954
+
955
+ if allow_z_integration:
956
+ obj._compute_ascending_order_structured_axis()
957
+
958
+ return obj
959
+
960
+ @classmethod
961
+ def extrude_mesh(cls, msh, n_layers=10):
962
+ Z = np.linspace(0, 1, n_layers + 1)
963
+ dimension = msh.dimension + 1
964
+ mesh_type = get_extruded_mesh_type(msh.type)
965
+ n_cells = msh.n_cells * n_layers
966
+ n_inner_cells = msh.n_inner_cells * n_layers
967
+ n_vertices = msh.n_cells * (n_layers + 1)
968
+ n_boundary_faces = msh.n_boundary_faces * n_layers + 2 * msh.n_cells
969
+ n_faces_per_cell = mesh_util._get_faces_per_element(mesh_type)
970
+ n_faces = n_inner_cells * n_faces_per_cell
971
+ vertex_coordinates = extrude.extrude_points(
972
+ msh.vertex_coordinates.T, Z).T
973
+ cell_vertices = extrude.extrude_element_vertices(
974
+ msh.cell_vertices.T, msh.n_vertices, n_layers
975
+ ).T
976
+
977
+ cell_centers = np.empty((n_cells, 3), dtype=float)
978
+ cell_volumes = np.empty((n_cells), dtype=float)
979
+ cell_inradius = np.empty((n_cells), dtype=float)
980
+ cell_face_areas = np.empty((n_cells, n_faces_per_cell), dtype=float)
981
+ cell_face_normals = np.zeros(
982
+ (n_cells, n_faces_per_cell, 3), dtype=float)
983
+ cell_n_neighbors = np.empty((n_cells), dtype=int)
984
+ cell_neighbors = np.empty((n_cells, n_faces_per_cell), dtype=int)
985
+ cell_neighbors_face_index = np.empty(
986
+ (n_cells, n_faces_per_cell), dtype=int)
987
+ for i_elem, elem in enumerate(cell_vertices.T):
988
+ cell_centers[i_elem, :dimension] = mesh_util.center(
989
+ vertex_coordinates.T, elem
990
+ )
991
+
992
+ # truncate normals and positions from 3d to dimendion-d
993
+ vertex_coordinates = vertex_coordinates.T[:, :dimension].T
994
+ cell_centers = cell_centers[:, :]
995
+ # cell_face_normals = cell_face_normals[:, :, :dimension]
996
+
997
+ # TODO
998
+ # empty fields
999
+ cell_faces = np.empty((n_inner_cells, n_faces_per_cell), dtype=int)
1000
+ boundary_face_cells = np.array([0, n_inner_cells - 1], dtype=int)
1001
+ boundary_face_ghosts = np.array(
1002
+ [n_inner_cells, n_inner_cells + 1], dtype=int)
1003
+ boundary_face_function_numbers = np.empty(
1004
+ (n_boundary_faces), dtype=int)
1005
+ boundary_face_physical_tags = np.array([0, 1], dtype=int)
1006
+ boundary_face_face_indices = np.array([0, n_faces - 1], dtype=int)
1007
+ face_cells = np.empty((n_faces, 2), dtype=int)
1008
+ face_subvolumes = np.empty((n_faces, 2), dtype=float)
1009
+ boundary_conditions_sorted_physical_tags = np.array([0, 1], dtype=int)
1010
+ boundary_conditions_sorted_names = np.array(["left", "right"])
1011
+ face_normals = np.zeros((n_faces, 3), dtype=float)
1012
+ face_volumes = np.zeros((n_faces), dtype=float)
1013
+ face_centers = np.zeros((n_faces, 3), dtype=float)
1014
+
1015
+ # hard coded guess
1016
+ n_face_neighbors = 0
1017
+ face_neighbors = (n_cells + 1) * \
1018
+ np.ones((n_faces, n_face_neighbors), dtype=int)
1019
+ lsq_gradQ = np.zeros((n_cells, dimension, 0), dtype=float)
1020
+ lsq_neighbors = np.zeros(1)
1021
+ lsq_monomial_multi_index = np.zeros(1)
1022
+ lsq_scale_factors = np.zeros(1)
1023
+ z_ordering = np.array([-1], dtype=float)
1024
+
1025
+ return cls(
1026
+ msh.dimension + 1,
1027
+ mesh_type,
1028
+ n_cells,
1029
+ n_inner_cells,
1030
+ n_faces,
1031
+ n_vertices,
1032
+ n_boundary_faces,
1033
+ n_faces_per_cell,
1034
+ vertex_coordinates,
1035
+ cell_vertices,
1036
+ cell_faces.T,
1037
+ cell_volumes,
1038
+ cell_centers.T,
1039
+ cell_inradius,
1040
+ cell_neighbors,
1041
+ boundary_face_cells.T,
1042
+ boundary_face_ghosts.T,
1043
+ boundary_face_function_numbers,
1044
+ boundary_face_physical_tags,
1045
+ boundary_face_face_indices.T,
1046
+ face_cells.T,
1047
+ face_normals.T,
1048
+ face_volumes,
1049
+ face_centers,
1050
+ face_subvolumes,
1051
+ face_neighbors,
1052
+ boundary_conditions_sorted_physical_tags,
1053
+ boundary_conditions_sorted_names,
1054
+ lsq_gradQ,
1055
+ lsq_neighbors,
1056
+ lsq_monomial_multi_index,
1057
+ lsq_scale_factors,
1058
+ z_ordering,
1059
+ )
1060
+
1061
+ def write_to_hdf5(self, filepath: str):
1062
+ if not _HAVE_H5PY:
1063
+ raise RuntimeError(
1064
+ "Mesh.write_to_hdf5() requires h5py, which is not available."
1065
+ )
1066
+ main_dir = misc.get_main_directory()
1067
+
1068
+ with h5py.File(os.path.join(main_dir, filepath), "w") as f:
1069
+ mesh = f.create_group("mesh")
1070
+ mesh.create_dataset("dimension", data=self.dimension)
1071
+ mesh.create_dataset("type", data=self.type)
1072
+ mesh.create_dataset("n_cells", data=self.n_cells)
1073
+ mesh.create_dataset("n_inner_cells", data=self.n_inner_cells)
1074
+ mesh.create_dataset("n_faces", data=self.n_faces)
1075
+ mesh.create_dataset("n_vertices", data=self.n_vertices)
1076
+ mesh.create_dataset("n_boundary_faces", data=self.n_boundary_faces)
1077
+ mesh.create_dataset("n_faces_per_cell", data=self.n_faces_per_cell)
1078
+ mesh.create_dataset("vertex_coordinates",
1079
+ data=self.vertex_coordinates)
1080
+ mesh.create_dataset("cell_vertices", data=self.cell_vertices)
1081
+ mesh.create_dataset("cell_faces", data=self.cell_faces)
1082
+ mesh.create_dataset("cell_volumes", data=self.cell_volumes)
1083
+ mesh.create_dataset("cell_centers", data=self.cell_centers)
1084
+ mesh.create_dataset("cell_inradius", data=self.cell_inradius)
1085
+ mesh.create_dataset("cell_neighbors", data=self.cell_neighbors)
1086
+ mesh.create_dataset("boundary_face_cells",
1087
+ data=self.boundary_face_cells)
1088
+ mesh.create_dataset("boundary_face_ghosts",
1089
+ data=self.boundary_face_ghosts)
1090
+ mesh.create_dataset(
1091
+ "boundary_face_function_numbers",
1092
+ data=self.boundary_face_function_numbers,
1093
+ )
1094
+ mesh.create_dataset(
1095
+ "boundary_face_physical_tags", data=self.boundary_face_physical_tags
1096
+ )
1097
+ mesh.create_dataset(
1098
+ "boundary_face_face_indices", data=self.boundary_face_face_indices
1099
+ )
1100
+ mesh.create_dataset("face_cells", data=self.face_cells)
1101
+ # mesh.create_dataset("face_cell_face_index", data=self.face_cell_face_index)
1102
+ mesh.create_dataset("face_normals", data=self.face_normals)
1103
+ mesh.create_dataset("face_volumes", data=self.face_volumes)
1104
+ mesh.create_dataset("face_centers", data=self.face_centers)
1105
+ mesh.create_dataset("face_subvolumes", data=self.face_subvolumes)
1106
+ mesh.create_dataset("face_neighbors", data=self.face_neighbors)
1107
+ mesh.create_dataset(
1108
+ "boundary_conditions_sorted_physical_tags",
1109
+ data=np.array(self.boundary_conditions_sorted_physical_tags),
1110
+ )
1111
+ mesh.create_dataset(
1112
+ "boundary_conditions_sorted_names",
1113
+ data=np.array(
1114
+ self.boundary_conditions_sorted_names, dtype="S"),
1115
+ )
1116
+ mesh.create_dataset("lsq_gradQ", data=np.array(self.lsq_gradQ))
1117
+ mesh.create_dataset(
1118
+ "lsq_neighbors", data=np.array(self.lsq_neighbors))
1119
+ mesh.create_dataset(
1120
+ "lsq_monomial_multi_index", data=(self.lsq_monomial_multi_index)
1121
+ )
1122
+ mesh.create_dataset("lsq_scale_factors",
1123
+ data=(self.lsq_scale_factors))
1124
+ mesh.create_dataset("z_ordering", data=np.array(self.z_ordering))
1125
+
1126
+ @classmethod
1127
+ def from_hdf5(cls, filepath: str):
1128
+ if not _HAVE_H5PY:
1129
+ raise RuntimeError(
1130
+ "Mesh.from_hdf5() requires h5py, which is not available."
1131
+ )
1132
+ with h5py.File(filepath, "r") as file:
1133
+ file = file
1134
+ mesh = cls(
1135
+ file["mesh"]["dimension"][()],
1136
+ (file["mesh"]["type"][()]).decode("utf-8"),
1137
+ file["mesh"]["n_cells"][()],
1138
+ file["mesh"]["n_inner_cells"][()],
1139
+ file["mesh"]["n_faces"][()],
1140
+ file["mesh"]["n_vertices"][()],
1141
+ file["mesh"]["n_boundary_faces"][()],
1142
+ file["mesh"]["n_faces_per_cell"][()],
1143
+ file["mesh"]["vertex_coordinates"][()],
1144
+ file["mesh"]["cell_vertices"][()],
1145
+ file["mesh"]["cell_faces"][()],
1146
+ file["mesh"]["cell_volumes"][()],
1147
+ file["mesh"]["cell_centers"][()],
1148
+ file["mesh"]["cell_inradius"][()],
1149
+ file["mesh"]["cell_neighbors"][()],
1150
+ file["mesh"]["boundary_face_cells"][()],
1151
+ file["mesh"]["boundary_face_ghosts"][()],
1152
+ file["mesh"]["boundary_face_function_numbers"][()],
1153
+ file["mesh"]["boundary_face_physical_tags"][()],
1154
+ file["mesh"]["boundary_face_face_indices"][()],
1155
+ file["mesh"]["face_cells"][()],
1156
+ # file["mesh"]["face_cell_face_index"][()],
1157
+ file["mesh"]["face_normals"][()],
1158
+ file["mesh"]["face_volumes"][()],
1159
+ file["mesh"]["face_centers"][()],
1160
+ file["mesh"]["face_subvolumes"][()],
1161
+ file["mesh"]["face_neighbors"][()],
1162
+ file["mesh"]["boundary_conditions_sorted_physical_tags"][()],
1163
+ np.array(
1164
+ file["mesh"]["boundary_conditions_sorted_names"][()
1165
+ ], dtype="str"
1166
+ ),
1167
+ file["mesh"]["lsq_gradQ"][()],
1168
+ file["mesh"]["lsq_neighbors"][()],
1169
+ file["mesh"]["lsq_monomial_multi_index"][()],
1170
+ file["mesh"]["lsq_scale_factors"][()],
1171
+ file["mesh"]["z_ordering"][()],
1172
+ )
1173
+ return mesh
1174
+
1175
+ def write_to_vtk(
1176
+ self,
1177
+ filepath: str,
1178
+ fields: Union[FArray, None] = None,
1179
+ field_names: Union[list[str], None] = None,
1180
+ point_data: dict = {},
1181
+ ):
1182
+ if not _HAVE_MESHIO:
1183
+ raise RuntimeError(
1184
+ "_write_to_vtk_from_vertices_edges requires meshio, which is not available."
1185
+ )
1186
+ d_fields = {}
1187
+ vertex_coords_3d = np.zeros((3, self.vertex_coordinates.shape[1]))
1188
+ vertex_coords_3d[: self.vertex_coordinates.shape[0], :] = (
1189
+ self.vertex_coordinates
1190
+ )
1191
+ if fields is not None:
1192
+ if field_names is None:
1193
+ field_names = [str(i) for i in range(fields.shape[0])]
1194
+ for i_fields, field in enumerate(fields):
1195
+ d_fields[field_names[i_fields]] = [field]
1196
+ # the brackets around the second argument (cells) indicate that I have only mesh type of mesh element of type self.type, with corresponding vertices.
1197
+ meshout = meshio.Mesh(
1198
+ vertex_coords_3d.T,
1199
+ [
1200
+ (
1201
+ mesh_util.convert_mesh_type_to_meshio_mesh_type(self.type),
1202
+ self.cell_vertices.T,
1203
+ )
1204
+ ],
1205
+ cell_data=d_fields,
1206
+ point_data=point_data,
1207
+ )
1208
+ path, _ = os.path.split(filepath)
1209
+ filepath, file_ext = os.path.splitext(filepath)
1210
+ if not os.path.exists(path) and path != "":
1211
+ os.mkdir(path)
1212
+ meshout.write(filepath + ".vtk", binary=False)
1213
+
1214
+
1215
+ if __name__ == "__main__":
1216
+ path = "/home/ingo/Git/sms/meshes/quad_2d/mesh_coarse.msh"
1217
+ path2 = "/home/ingo/Git/sms/meshes/quad_2d/mesh_fine.msh"
1218
+ path3 = "/home/ingo/Git/sms/meshes/quad_2d/mesh_finest.msh"
1219
+ path4 = "/home/ingo/Git/sms/meshes/triangle_2d/mesh_coarse.msh"
1220
+ labels = get_physical_boundary_labels(path)
1221
+
1222
+ # dm, boundary_dict, ghost_cells_dict = load_gmsh(path)
1223
+
1224
+ mesh = Mesh.from_gmsh(path)
1225
+ assert mesh.cell_faces.max() == mesh.n_faces - 1
1226
+ assert mesh.cell_faces.min() == 0
1227
+ assert mesh.face_cells.max() == mesh.n_cells - 1
1228
+ assert mesh.face_cells.min() == 0
1229
+ assert mesh.cell_vertices.max() == mesh.n_vertices - 1
1230
+ assert mesh.cell_vertices.min() == 0
1231
+
1232
+ mesh.write_to_hdf5("./test.h5")
1233
+ mesh = Mesh.from_hdf5("./test.h5")
1234
+ # mesh.write_to_vtk('./test.vtk')
1235
+ mesh.write_to_vtk(
1236
+ "./test.vtk",
1237
+ fields=np.ones((2, mesh.n_inner_cells), dtype=float),
1238
+ field_names=["A", "B"],
1239
+ )