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