zoomy-core 0.1.0__py3-none-any.whl → 0.1.2__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.

Potentially problematic release.


This version of zoomy-core might be problematic. Click here for more details.

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