Theseus
Compressible flow solver
Loading...
Searching...
No Matches
dgsem_cache_utilities.hpp
Go to the documentation of this file.
1// Copyright (c) 2025-2026 Board of Trustees of the University of Illinois
2//
3// This file is part of Theseus.
4//
5// SPDX-License-Identifier: BSD-3-Clause
6#pragma once
7#include "mfem.hpp"
9#include "dgsem_cache.hpp"
10#include "ModalBasis.hpp"
11#include "timer.hpp"
12
13namespace Theseus {
14
15 // TODO: Not complete as written, after this, callsites need to set up
16 // Boundary Face data structures with a separate call. Need to fix.
17 template<typename CacheT>
18 void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
19 {
20 GetDiscretizationInfo(fes, cache);
21 {
22 Theseus::ScopedTimer timer("SetupRestrictions");
23 SetupRestrictions(fes, cache);
24 }
25 SetupVolumeMarkers(fes, cache);
26 {
27 Theseus::ScopedTimer timer("SetupGeometricTerms");
28 SetupGeometricTerms(fes, cache);
29 }
30 // AssembleBoundaryFaceGeometryTerms(fes, cache);
31 // TODO: Move these to where the caches are created and validated
32 // MFEM_VERIFY(nfaces == cache.num_interior_faces, "restriction faces != cached interior faces");
33 // MFEM_VERIFY(cache.face_normals.Size() == nfaces*nfp*dim, "normals size mismatch");
34 // MFEM_VERIFY(cache.face_wt_minus.Size() == nfaces*nfp, "w_minus size mismatch");
35 // MFEM_VERIFY(cache.face_wt_plus.Size() == nfaces*nfp, "w_plus size mismatch");
36 }
37
38 template<typename CacheT>
39 void GetDiscretizationInfo(mfem::FiniteElementSpace *fes, CacheT *cache)
40 {
41
42 MFEM_VERIFY(fes, "fes must be set");
43 mfem::Mesh *mesh = fes->GetMesh();
44 MFEM_VERIFY(mesh, "mesh must be set");
45 const int p = fes->GetFE(0)->GetOrder();
46 const int dim = mesh->SpaceDimension();
47 const int Np = p + 1; // num 1d quadrature points
48 const int Np_x = Np;
49 const int Np_y = dim > 1 ? Np : 1;
50 const int Np_z = dim > 2 ? Np : 1;
51 const int ne = fes->GetNE();
52 const int num_dofs_per_eqn_per_element = fes->GetFE(0)->GetDof();
53 const int num_eqns = fes->GetVDim();
54
55 cache->p = p;
56 cache->Np = Np;
57 cache->dim = dim;
58 cache->Np_x = Np_x;
59 cache->Np_y = Np_y;
60 cache->Np_z = Np_z;
61 cache->num_elements = ne;
62 cache->ndof_scalar_el = num_dofs_per_eqn_per_element;
63 cache->num_equations = num_eqns;
64 }
65
66 template<typename CacheT>
67 void SetupRestrictions(mfem::FiniteElementSpace *fes, CacheT *cache)
68 {
69 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace*>(fes);
70 MFEM_VERIFY(pfes, "Restriction setup requires ParFiniteElementSpace");
71 cache->restr_v = fes->GetElementRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC);
72 cache->restr_f = pfes->GetFaceRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC,
73 mfem::FaceType::Interior,
74 mfem::L2FaceValues::DoubleValued);
75 cache->restr_b = pfes->GetFaceRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC,
76 mfem::FaceType::Boundary,
77 mfem::L2FaceValues::SingleValued);
78 }
79
80 // Set up and populate elJac, elMetric, D, Dhat, Dhat2
81 // Face normals, and weights
82 template<typename CacheT>
83 void SetupGeometricTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
84 {
85 const int nelem = cache->num_elements;
86 const int p = cache->p;
87 const int Np = cache->Np;
88 const int dim = cache->dim;
89 const int Np_x = Np;
90 const int Np_y = dim > 1 ? Np : 1;
91 const int Np_z = dim > 2 ? Np : 1;
92 const int neq = cache->num_equations;
93 mfem::Mesh *mesh = fes->GetMesh();
94
95 // Build integration rules
96 const int IntegrationOrder = 2 * Np_x - 3;
97 cache->ir = &cache->GLIntRules.Get(mfem::Geometry::SEGMENT, IntegrationOrder);
98 auto vol_topo = (dim == 1 ? mfem::Geometry::SEGMENT :
99 (dim == 2 ? mfem::Geometry::SQUARE : mfem::Geometry::CUBE));
100 auto face_topo = (dim == 1 ? mfem::Geometry::POINT :
101 (dim == 2 ? mfem::Geometry::SEGMENT : mfem::Geometry::SQUARE));
102
103 cache->ir_face = &cache->GLIntRules.Get(face_topo, IntegrationOrder);
104 cache->ir_vol = &cache->GLIntRules.Get(vol_topo, IntegrationOrder);
105
106 MFEM_ASSERT(cache->ir->GetNPoints() == Np_x, "");
107 MFEM_ASSERT(cache->ir_vol->GetNPoints() == Np_x*Np_y*Np_z, "");
108
109 // Populate element Jacobian determinant and metric terms
110 cache->elJac.SetSize(Np_x*Np_y*Np_z*nelem);
111 cache->elMetric.SetSize(dim*dim*Np_x*Np_y*Np_z*nelem);
112 cache->elQuadratureWeights.SetSize(Np_x*Np_y*Np_z*nelem);
113 cache->elRadius.SetSize(dim > AxisymmetricGeometry::radial_coordinate ?
114 Np_x*Np_y*Np_z*nelem : 0);
115 for (int i = 0; i < nelem; i++)
116 {
117 mfem::ElementTransformation *T = fes->GetElementTransformation(i);
118 assert(T->ElementNo == i);
120 }
121
122 // Set up derivative operators
123 mfem::DenseMatrix D_T, Dhat_T, Dhat2_T;
124 D_T.SetSize(Np_x);
125 Dhat_T.SetSize(Np_x);
126 Dhat2_T.SetSize(Np_x);
127
128 mfem::Vector wBary(Np_x);
129 wBary = 1.0;
130
131 for (int i = 1; i < Np_x; i++)
132 {
133 for (int j = 0; j < i; j++)
134 {
135 wBary(j) *= (cache->ir->IntPoint(j).x - cache->ir->IntPoint(i).x);
136 wBary(i) *= (cache->ir->IntPoint(i).x - cache->ir->IntPoint(j).x);
137 }
138 }
139
140 wBary.Reciprocal();
141 D_T = 0.0;
142 for (int iL = 0; iL < Np_x; iL++)
143 {
144 for (int i = 0; i < Np_x; i++)
145 {
146 if (iL != i)
147 {
148 D_T(i, iL) = wBary(iL) / wBary(i) / (cache->ir->IntPoint(i).x - cache->ir->IntPoint(iL).x);
149 D_T(i, i) -= D_T(i, iL);
150 }
151 }
152 }
153
154 Dhat_T = D_T;
155 Dhat_T(0, 0) += 1.0 / cache->ir->IntPoint(0).weight;
156 Dhat_T(Np - 1, Np - 1) -= 1.0 / cache->ir->IntPoint(Np - 1).weight;
157 Dhat_T.Transpose();
158
159 Dhat2_T = D_T;
160 Dhat2_T *= 2.0;
161 Dhat2_T(0, 0) += 1.0 / cache->ir->IntPoint(0).weight;
162 Dhat2_T(Np - 1, Np - 1) -= 1.0 / cache->ir->IntPoint(Np - 1).weight;
163 Dhat2_T.Transpose();
164 D_T.Transpose();
165
166 // Just copy D_T, Dhat_T, and Dhat2_T
167 cache->D.SetSize(Np_x*Np_x);
168 cache->Dhat.SetSize(Np_x*Np_x);
169 cache->Dhat2.SetSize(Np_x*Np_x);
170 std::memcpy(cache->D.HostWrite(), D_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
171 std::memcpy(cache->Dhat.HostWrite(), Dhat_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
172 std::memcpy(cache->Dhat2.HostWrite(), Dhat2_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
173
174 cache->elWaveSpeed.SetSize(nelem);
175 cache->elWaveSpeed = 0.0;
176 cache->elWaveSpeed.UseDevice();
177 cache->elWaveSpeed.Read();
178
179 cache->elJac.UseDevice();
180 cache->elMetric.UseDevice();
181 cache->elRadius.UseDevice();
182 cache->D.UseDevice();
183 cache->Dhat.UseDevice();
184 cache->Dhat2.UseDevice();
185 cache->elJac.Read();
186 cache->elMetric.Read();
187 cache->elRadius.Read();
188 cache->D.Read();
189 cache->Dhat.Read();
190 cache->Dhat2.Read();
191
192 // Set up data for faces
193 const int nfp = cache->ir_face->GetNPoints();
194 cache->num_face_points = nfp;
195
196 const int nfaces_restr = cache->restr_f->Height() / (nfp * neq * 2);
197 cache->num_interior_faces = nfaces_restr;
198 MFEM_VERIFY(nfaces_restr > 0, "nfaces_restr is 0");
199
201
202 cache->face_normals.UseDevice();
203 cache->face_wt_minus.UseDevice();
204 cache->face_wt_plus.UseDevice();
205 cache->face_radius.UseDevice();
206 cache->face_normals.Read();
207 cache->face_wt_minus.Read();
208 cache->face_wt_plus.Read();
209 cache->face_radius.Read();
210
211 cache->ifWaveSpeed.SetSize(cache->num_interior_faces);
212 cache->ifWaveSpeed = 0.0;
213 cache->ifWaveSpeed.UseDevice();
214 cache->ifWaveSpeed.Read();
215 }
216
217
218 template<typename CacheT>
219 void SetupVolumeMarkers(mfem::FiniteElementSpace *fes, CacheT *cache)
220 {
221 mfem::Mesh *mesh = fes->GetMesh();
222
223 cache->num_attr = mesh->attributes.Size() ? mesh->attributes.Max() : 0;
224 cache->vol_attr_marker.SetSize(cache->num_attr);
225 cache->vol_attr_marker = 1; // process everything
226
227 cache->domain_attr_marker.SetSize(cache->num_attr);
228 cache->domain_attr_marker = 1; // process everything
229
230 // ---- 2) Per-element attribute id array -----------------------------------
231 const int ne = mesh->GetNE();
232 cache->elem_attr.SetSize(ne);
233 for (int e = 0; e < ne; ++e)
234 {
235 const int attr = mesh->GetAttribute(e); // 1-based
236 cache->elem_attr[e] = attr;
237 }
238
239 // Optional host-side sanity check (cheap, catches bad markers early):
240 if (cache->num_attr > 0)
241 {
242 for (int e = 0; e < ne; ++e)
243 {
244 const int a = cache->elem_attr[e];
245 MFEM_VERIFY(a >= 1 && a <= cache->num_attr,
246 "element attribute out of range: attr=" << a
247 << " num_attr=" << cache->num_attr);
248 }
249 }
250
251 cache->elem_attr.UseDevice();
252 cache->vol_attr_marker.UseDevice();
253 cache->domain_attr_marker.UseDevice();
254 cache->elem_attr.Read();
255 cache->vol_attr_marker.Read();
256 cache->domain_attr_marker.Read();
257 }
258
259
260 template <typename CacheT>
261 void BuildBoundaryFacePermutationMap(mfem::ParMesh *pmesh, CacheT *cache)
262 {
263 MFEM_VERIFY(cache->ir_face, "cache->ir_face must exist");
264
265 auto &bnd_faces = pmesh->GetFaceIndices(mfem::FaceType::Boundary);
266 const int nbnd_faces = bnd_faces.Size();
267 const int nfp = cache->ir_face->GetNPoints();
268
269 cache->inv_fp_map_bnd.SetSize(nbnd_faces * nfp);
270
271 for (int fslot = 0; fslot < nbnd_faces; ++fslot)
272 {
273 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
274 {
275 const int fp_perm = cache->fqs_bnd->GetPermutedIndex(fslot, fp_restr);
276 cache->inv_fp_map_bnd[fslot * nfp + fp_perm] = fp_restr;
277 }
278 }
279 }
280
281 template <typename CacheT>
282 void BuildBoundaryFaceToMarkerMap(mfem::ParMesh *pmesh,
283 const std::vector<mfem::Array<int>> &bdr_marker_vector,
284 CacheT *cache)
285 {
286 auto &bnd_faces = pmesh->GetFaceIndices(mfem::FaceType::Boundary);
287 const auto &bnd_face_attr = pmesh->GetBdrFaceAttributes();
288
289 const int nbnd_faces = bnd_faces.Size();
290
291 MFEM_VERIFY(bnd_face_attr.Size() == nbnd_faces,
292 "Expected compact boundary-face attribute array");
293
294 cache->bnd_attr.SetSize(nbnd_faces);
295 cache->bnd_marker_index.SetSize(nbnd_faces);
296
297 for (int fslot = 0; fslot < nbnd_faces; ++fslot)
298 {
299 const int attr = bnd_face_attr[fslot];
300 cache->bnd_attr[fslot] = attr;
301 cache->bnd_marker_index[fslot] = -1;
302
303 if (attr <= 0) { continue; }
304
305 for (int mindex = 0; mindex < (int)bdr_marker_vector.size(); ++mindex)
306 {
307 const auto &marker = bdr_marker_vector[mindex];
308 MFEM_VERIFY(attr-1 < marker.Size(), "boundary attribute out of marker range");
309
310 if (marker[attr-1])
311 {
312 cache->bnd_marker_index[fslot] = mindex;
313 break;
314 }
315 }
316 }
317 }
318
319 inline void BuildBoundaryFaceToBEMap(mfem::ParMesh *pmesh,
320 mfem::Array<int> &face_to_be)
321 {
322 const int nfaces = pmesh->GetNumFaces();
323 face_to_be.SetSize(nfaces);
324 face_to_be = -1;
325
326 for (int be = 0; be < pmesh->GetNBE(); ++be)
327 {
328 const int face_id = pmesh->GetBdrElementFaceIndex(be);
329 // const int face_id = pmesh->GetBdrFace(be);
330 MFEM_VERIFY(face_id >= 0 && face_id < nfaces, "bad boundary face id");
331 MFEM_VERIFY(face_to_be[face_id] < 0, "duplicate boundary element for face");
332 face_to_be[face_id] = be;
333 }
334 }
335
336 template<typename CacheT>
337 void AssembleBoundaryFaceGeometryTerms(mfem::FiniteElementSpace *fes,
338 const std::vector<mfem::Array<int>> &bdr_marker_vector,
339 CacheT *cache)
340 {
341 auto *mesh = fes->GetMesh();
342 auto *pmesh = dynamic_cast<mfem::ParMesh*>(mesh);
343 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace*>(fes);
344
345 MFEM_VERIFY(pmesh, "need ParMesh");
346 MFEM_VERIFY(pfes, "need ParFiniteElementSpace");
347 MFEM_VERIFY(cache, "need cache");
348 MFEM_VERIFY(cache->ir_face, "cache->ir_face must exist");
349 MFEM_VERIFY(cache->ir, "cache->ir must exist");
350
351 cache->fqs_bnd.reset(new mfem::FaceQuadratureSpace(*mesh, *cache->ir_face,
352 mfem::FaceType::Boundary));
353
354 const int dim = mesh->Dimension();
355 const int nfp = cache->ir_face->GetNPoints();
356
357 auto &bnd_faces = pmesh->GetFaceIndices(mfem::FaceType::Boundary);
358 const int nbnd_faces = bnd_faces.Size();
359
360 cache->bndWaveSpeed.SetSize(nbnd_faces*nfp);
361 cache->bndWaveSpeed = 0.0;
362 cache->bndWaveSpeed.Read();
363
364 // 0. Get a restriction-face-to-bnd-element mapping
365 mfem::Array<int> face_to_be;
366 BuildBoundaryFaceToBEMap(pmesh, face_to_be);
367
368 // 1. Permutation map
370
371 // 2. BC mapping
372 BuildBoundaryFaceToMarkerMap(pmesh, bdr_marker_vector, cache);
373
374 // 3. Geometry arrays
375 cache->bnd_normals.SetSize(nbnd_faces * nfp * dim);
376 cache->bnd_wt.SetSize(nbnd_faces * nfp);
377 cache->bnd_xyz.SetSize(nbnd_faces * nfp * dim);
378 cache->bnd_radius.SetSize(dim > AxisymmetricGeometry::radial_coordinate ?
379 nbnd_faces * nfp : 0);
380
381 mfem::real_t *nor_d = cache->bnd_normals.HostWrite();
382 mfem::real_t *wt_d = cache->bnd_wt.HostWrite();
383 mfem::real_t *xyz_d = cache->bnd_xyz.HostWrite();
384 mfem::real_t *rad_d = cache->bnd_radius.Size() > 0 ?
385 cache->bnd_radius.HostWrite() : nullptr;
386
387 const mfem::real_t w0 = cache->ir->IntPoint(0).weight;
388
389 mfem::Vector nor(dim);
390 mfem::Vector phys(dim);
391
392 auto store = [&](int fslot, int fp_restr,
393 const mfem::Vector &nor,
394 const mfem::Vector &phys,
395 mfem::real_t inv_wJ1)
396 {
397 const int base_scl = fslot * nfp + fp_restr;
398 const int base_vec = base_scl * dim;
399
400 for (int d = 0; d < dim; ++d)
401 {
402 nor_d[base_vec + d] = nor(d);
403 xyz_d[base_vec + d] = phys(d);
404 }
405
406 wt_d[base_scl] = inv_wJ1;
407 if (rad_d)
408 {
409 const mfem::real_t radius =
410 AxisymmetricGeometry::Radius(phys.GetData());
411 rad_d[base_scl] = cache->axisymmetric ?
413 radius, "boundary face " + std::to_string(fslot) +
414 ", point " + std::to_string(fp_restr)) : radius;
415 }
416 };
417
418 for (int fslot = 0; fslot < nbnd_faces; ++fslot)
419 {
420 const int face_id = bnd_faces[fslot];
421 const int be_match = face_to_be[face_id];
422 // Map boundary face slot -> boundary element index.
423 // We need boundary-element index for GetBdrFaceTransformations(be).
424 MFEM_VERIFY(be_match >= 0, "Could not find boundary element for boundary face");
425 auto *tr = mesh->GetBdrFaceTransformations(be_match);
426 MFEM_VERIFY(tr, "expected boundary face transformation");
427
428 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
429 {
430 const int fp_geom = cache->inv_fp_map_bnd[fslot * nfp + fp_restr];
431 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
432 tr->SetAllIntPoints(&ip);
433
434 const mfem::real_t J1 = tr->GetElement1Transformation().Weight();
435 if (dim == 1)
436 {
437 nor(0) = (tr->GetElement1IntPoint().x - 0.5) * 2.0;
438 }
439 else
440 {
441 mfem::CalcOrtho(tr->Jacobian(), nor);
442 }
443 tr->Transform(ip, phys);
444 store(fslot, fp_restr, nor, phys, 1.0 / (w0 * J1));
445 }
446 }
447 cache->bnd_xyz.UseDevice();
448 cache->bnd_radius.UseDevice();
449 cache->bnd_xyz.Read();
450 cache->bnd_radius.Read();
451 }
452
453 // Builds element-specific Jac/Metric and stuffs into cache.elJac, cache.elMetric
454 template<typename CacheT>
455 void AssembleElementVolumeGeometricTerms(mfem::ElementTransformation &Tr, CacheT *cache)
456 {
457
458 mfem::real_t *Jinv_h = cache->elJac.HostWrite();
459 mfem::real_t *Met_h = cache->elMetric.HostWrite();
460 mfem::real_t *qWgts_h = cache->elQuadratureWeights.HostWrite();
461 mfem::real_t *radius_h = cache->elRadius.Size() > 0 ?
462 cache->elRadius.HostWrite() : nullptr;
463
464 int dim = cache->dim;
465 mfem::Vector metric1(dim);
466 mfem::Vector physical(dim);
467 const int e = Tr.ElementNo;
468 const int nq = cache->Np_x * cache->Np_y * cache->Np_z;
469
470 for (int q = 0; q < nq; ++q)
471 {
472 const mfem::IntegrationPoint &ip = cache->ir_vol->IntPoint(q);
473 Tr.SetIntPoint(&ip);
474 const mfem::real_t J = Tr.Weight();
475 Jinv_h[e*nq + q] = J;
476 qWgts_h[e*nq + q] = J * ip.weight;
477 if (radius_h)
478 {
479 Tr.Transform(ip, physical);
480 const mfem::real_t radius =
481 AxisymmetricGeometry::Radius(physical.GetData());
482 radius_h[e*nq + q] = cache->axisymmetric ?
484 radius, "element " + std::to_string(e) +
485 ", point " + std::to_string(q)) : radius;
486 }
487 const mfem::DenseMatrix &adj = Tr.AdjugateJacobian();
488 for (int dir = 0; dir < dim; ++dir)
489 {
490 adj.GetRow(dir, metric1); // metric1.Size() == dim
491
492 for (int d = 0; d < dim; ++d)
493 {
494 const int idxM = (((e*nq + q)*dim + dir)*dim + d);
495 Met_h[idxM] = metric1(d);
496 }
497 }
498 }
499 }
500
501 template<typename CacheT>
502 void AssembleInteriorFaceGeometryTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
503 {
504 auto *mesh = fes->GetMesh();
505 auto *pmesh = dynamic_cast<mfem::ParMesh*>(mesh);
506 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace*>(fes);
507 cache->fqs_int.reset(new mfem::FaceQuadratureSpace(*mesh, *cache->ir_face,
508 mfem::FaceType::Interior));
509 MFEM_VERIFY(pfes, "need ParFiniteElementSpace");
510
511 const int dim = mesh->Dimension();
512 const int neq = pfes->GetVDim();
513 const int nfp = cache->ir_face->GetNPoints();
514
515 auto &int_faces = pmesh->GetFaceIndices(mfem::FaceType::Interior);
516 const int ninterior_faces = int_faces.Size();
517
518 cache->inv_fp_map.SetSize(ninterior_faces * nfp);
519 for (int face_slot = 0; face_slot < ninterior_faces; ++face_slot)
520 {
521 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
522 {
523 int fp_perm = cache->fqs_int->GetPermutedIndex(face_slot, fp_restr);
524 cache->inv_fp_map[face_slot*nfp + fp_perm] = fp_restr;
525 }
526 }
527
528 cache->face_normals.SetSize(ninterior_faces * nfp * dim);
529 cache->face_wt_minus.SetSize(ninterior_faces * nfp);
530 cache->face_wt_plus.SetSize(ninterior_faces * nfp);
531 cache->face_radius.SetSize(
533 ninterior_faces * nfp : 0);
534
535 mfem::real_t *nor_d = cache->face_normals.HostWrite();
536 mfem::real_t *inv1_d = cache->face_wt_minus.HostWrite();
537 mfem::real_t *inv2_d = cache->face_wt_plus.HostWrite();
538 mfem::real_t *radius_d = cache->face_radius.Size() > 0 ?
539 cache->face_radius.HostWrite() : nullptr;
540 const mfem::real_t w0 = cache->ir->IntPoint(0).weight;
541
542 auto store = [&](int fslot, int fp, const mfem::Vector &nor,
543 const mfem::Vector &physical,
544 mfem::real_t inv_wJ1, mfem::real_t inv_wJ2)
545 {
546 const int nbase = (fslot * nfp + fp) * dim;
547 for (int d = 0; d < dim; ++d) { nor_d[nbase + d] = nor(d); }
548 inv1_d[fslot * nfp + fp] = inv_wJ1;
549 inv2_d[fslot * nfp + fp] = inv_wJ2;
550 if (radius_d)
551 {
552 const mfem::real_t radius =
553 AxisymmetricGeometry::Radius(physical.GetData());
554 radius_d[fslot*nfp + fp] = cache->axisymmetric ?
556 radius, "interior face " + std::to_string(fslot) +
557 ", point " + std::to_string(fp)) : radius;
558 }
559 };
560
561 mfem::Vector nor(dim);
562 mfem::Vector physical(dim);
563 // The order of faces in GetFaceIndices(FaceType::Interior) *must*
564 // match the order of the faces in the interior face restriction
565 // operator face slots.
566 for (int fslot = 0; fslot < ninterior_faces; ++fslot)
567 {
568 const int face_id = int_faces[fslot];
569 // bool face_is_flipped = false;
570 // for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
571 // {
572 // const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
573 // if (fp_geom != fp_restr){
574 // face_is_flipped = true;
575 // }
576 // }
577 auto *tr = mesh->GetInteriorFaceTransformations(face_id);
578 if (tr){ // Do interior face caching
579 // MFEM_VERIFY(tr, "expected interior face");
580 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
581 {
582 const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
583 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
584 tr->SetAllIntPoints(&ip);
585
586 const mfem::real_t J1 = tr->GetElement1Transformation().Weight();
587 const mfem::real_t J2 = tr->GetElement2Transformation().Weight();
588
589 if (dim == 1) { nor(0) = (tr->GetElement1IntPoint().x - 0.5)*2.0; }
590 else { mfem::CalcOrtho(tr->Jacobian(), nor); }
591 tr->Transform(ip, physical);
592
593 //const mfem::real_t fac = face_is_flipped ? -1.0 : 1.0;
594 const mfem::real_t fac = 1.0;
595 store(fslot, fp_restr, nor, physical,
596 fac/(w0*J1), fac/(w0*J2));
597 }
598 continue;
599 } // Internal face processing
600 {
601 auto *sh_tr = pmesh->GetSharedFaceTransformationsByLocalIndex(face_id, true);
602 MFEM_VERIFY(sh_tr, "expected shared face");
603 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
604 {
605 const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
606 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
607 sh_tr->SetAllIntPoints(&ip);
608
609 const mfem::real_t J1 = sh_tr->GetElement1Transformation().Weight();
610 const mfem::real_t J2 = sh_tr->GetElement2Transformation().Weight();
611
612 if (dim == 1) { nor(0) = (sh_tr->GetElement1IntPoint().x - 0.5)*2.0; }
613 else { mfem::CalcOrtho(sh_tr->Jacobian(), nor); }
614 sh_tr->Transform(ip, physical);
615
616 //const mfem::real_t fac = face_is_flipped ? -1.0 : 1.0;
617 const mfem::real_t fac1 = 1.0;
618 const mfem::real_t fac2 = 0.0;
619 store(fslot, fp_restr, nor, physical,
620 fac1/(w0*J1), fac2/(w0*J2));
621 }
622 } // Shared face processing
623 } // Interior face processing
624 }
625
626 template<typename CacheT>
627 void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
628 {
629 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace *>(fes);
630 auto *pmesh = pfes->GetMesh();
631 const int dim = cache->dim;
632 const int ne = pmesh->GetNE();
633 const int Np_x = cache->Np_x;
634 const int Np_y = cache->Np_y;
635 const int Np_z = cache->Np_z;
636 const mfem::real_t *D = cache->D.HostRead();
637
638 mfem::DenseTensor SubcellMetricXi, SubcellMetricEta, SubcellMetricZeta;
639 SubcellMetricXi.SetSize(dim, Np_z * Np_y * (Np_x + 1), ne);
640 if(dim > 1)
641 SubcellMetricEta.SetSize(dim, Np_z * (Np_y + 1) * Np_x, ne);
642 if(dim == 3)
643 SubcellMetricZeta.SetSize(dim, (Np_z + 1) * Np_y * Np_x, ne);
644
645 for (int el = 0; el < ne; el++)
646 {
647 mfem::ElementTransformation *Tr = pmesh->GetElementTransformation(el);
648 mfem::DenseMatrix &nor_mat_xi = SubcellMetricXi(el);
649
650 mfem::Vector tmp(dim);
651 mfem::Vector nor(dim);
652 mfem::Vector metric1(dim);
653 mfem::Vector metric2(dim);
654
655 for (int k = 0; k < Np_z; k++)
656 {
657 const int pos1 = k * Np_y * Np_x;
658 for (int j = 0; j < Np_y; j++)
659 {
660 const int pos = pos1 + j * Np_x;
661 const mfem::IntegrationPoint &ip1 = cache->ir_vol->IntPoint(pos); // left xi-face
662 Tr->SetIntPoint(&ip1);
663 Tr->AdjugateJacobian().GetRow(0, metric1);
664 for (int i = 0; i < Np_x + 1; i++)
665 {
666 nor = metric1;
667 for (int l = 0; l < i; l++)
668 {
669 const mfem::real_t *Dcol = D + l*Np_x; // D_T.GetColumn(l, D_row);
670 tmp = 0.0;
671 mfem::real_t weight = cache->ir->IntPoint(l).weight;
672 for (int m = 0; m < Np_x; m++)
673 {
674 const mfem::IntegrationPoint &ip2 = cache->ir_vol->IntPoint(pos + m);
675 Tr->SetIntPoint(&ip2);
676 Tr->AdjugateJacobian().GetRow(0, metric2);
677 metric2 *= Dcol[m];
678 tmp += metric2;
679 }
680 tmp *= weight;
681 nor += tmp;
682 }
683 nor_mat_xi.SetCol(pos + i, nor);
684 }
685 }
686 }
687
688 if (dim > 1)
689 {
690 mfem::DenseMatrix &nor_mat_eta = SubcellMetricEta(el);
691 for (int k = 0; k < Np_z; k++)
692 {
693 const int pos1 = k * Np_y * Np_x;
694 for (int i = 0; i < Np_x; i++)
695 {
696 const mfem::IntegrationPoint &ip1 = cache->ir_vol->IntPoint(pos1 + i); // bottom eta-face
697 Tr->SetIntPoint(&ip1);
698 Tr->AdjugateJacobian().GetRow(1, metric1);
699 for (int j = 0; j < Np_y + 1; j++)
700 {
701 nor = metric1;
702 for (int l = 0; l < j; l++)
703 {
704 const mfem::real_t *Dcol = D + l*Np_x; // D_T.GetColumn(l, D_row);
705 tmp = 0.0;
706 mfem::real_t weight = cache->ir->IntPoint(l).weight;
707 for (int m = 0; m < Np_y; m++)
708 {
709 const int pos = pos1 + m * Np_x;
710 const mfem::IntegrationPoint &ip2 = cache->ir_vol->IntPoint(pos + i);
711 Tr->SetIntPoint(&ip2);
712 Tr->AdjugateJacobian().GetRow(1, metric2);
713 metric2 *= Dcol[m];
714 tmp += metric2;
715 }
716 tmp *= weight;
717 nor += tmp;
718 }
719 nor_mat_eta.SetCol(pos1 + j * Np_x + i, nor);
720 }
721 }
722 }
723
724 if (dim > 2)
725 {
726 mfem::DenseMatrix &nor_mat_zeta = SubcellMetricZeta(el);
727 for (int j = 0; j < Np_y; j++)
728 {
729 const int pos1 = j * Np_x;
730 for (int i = 0; i < Np_x; i++)
731 {
732 const mfem::IntegrationPoint &ip1 = cache->ir_vol->IntPoint(pos1 + i); // bottom zeta-face
733 Tr->SetIntPoint(&ip1);
734 Tr->AdjugateJacobian().GetRow(2, metric1);
735 for (int k = 0; k < Np_z + 1; k++)
736 {
737 nor = metric1;
738 for (int l = 0; l < k; l++)
739 {
740 const mfem::real_t *Dcol = D + l*Np_x; // D_T.GetColumn(l, D_row);
741 tmp = 0.0;
742 mfem::real_t weight = cache->ir->IntPoint(l).weight;
743 for (int m = 0; m < Np_z; m++)
744 {
745 const int pos = m * Np_y * Np_x + pos1 + i;
746 const mfem::IntegrationPoint &ip2 = cache->ir_vol->IntPoint(pos);
747 Tr->SetIntPoint(&ip2);
748 Tr->AdjugateJacobian().GetRow(2, metric2);
749 metric2 *= Dcol[m];
750 tmp += metric2;
751 }
752 tmp *= weight;
753 nor += tmp;
754 }
755 nor_mat_zeta.SetCol(k * Np_y * Np_x + pos1 + i, nor);
756 }
757 }
758 }
759 }
760 }
761 }
762 CacheSubcellMetricData(SubcellMetricXi, SubcellMetricEta, SubcellMetricZeta, cache);
763 }
764
765 // Call this *after* element geometric cache is created
766 template <typename CacheT>
767 void CacheSubcellMetricData(const mfem::DenseTensor &SubcellMetricXi,
768 const mfem::DenseTensor &SubcellMetricEta,
769 const mfem::DenseTensor &SubcellMetricZeta,
770 CacheT *cache)
771 {
772 const int ne = cache->num_elements;
773 const int dim = cache->dim;
774
775 // Num quadrature points per direction
776 const int Np_x = cache->Np_x;
777 const int Np_y = cache->Np_y;
778 const int Np_z = cache->Np_z;
779
780 // Num points per metric component per element
781 const int n_metric_xi = (Np_x + 1) * Np_y * Np_z;
782 const int n_metric_eta = Np_x * (Np_y + 1) * Np_z;
783 const int n_metric_zeta = Np_x * Np_y * (Np_z + 1);
784
785 // Num metric values per element
786 const int nq_metric_xi = n_metric_xi * dim;
787 const int nq_metric_eta = n_metric_eta * dim;
788 const int nq_metric_zeta = n_metric_zeta * dim;
789
790 cache->subcellWeights.SetSize(Np_x);
791 mfem::real_t *wgts = cache->subcellWeights.HostWrite();
792 for (int i = 0;i < Np_x;i++){
793 wgts[i] = cache->ir->IntPoint(i).weight;
794 }
795
796 cache->subcellMetricXi.SetSize(nq_metric_xi*ne);
797 if (dim > 1)
798 cache->subcellMetricEta.SetSize(nq_metric_eta*ne);
799 if (dim > 2)
800 cache->subcellMetricZeta.SetSize(nq_metric_zeta*ne);
801
802 mfem::real_t *xi_h = cache->subcellMetricXi.HostWrite();
803 mfem::real_t *eta_h = (dim > 1 ? cache->subcellMetricEta.HostWrite() : nullptr);
804 mfem::real_t *zeta_h = (dim > 2 ? cache->subcellMetricZeta.HostWrite() : nullptr);
805
806 for (int el = 0; el < ne; ++el){
807 const mfem::DenseMatrix &Mxi = SubcellMetricXi(el);
808 MFEM_VERIFY(Mxi.Height() == dim && Mxi.Width() == n_metric_xi,
809 "SubcellMetricXi has unexpected shape.");
810 for (int id = 0; id < n_metric_xi; ++id){
811 for (int d = 0; d < dim; ++d){
812 xi_h [((el * n_metric_xi + id) * dim) + d] = Mxi(d, id);
813 }
814 }
815 if (dim > 1) {
816 const mfem::DenseMatrix &Meta = SubcellMetricEta(el);
817 MFEM_VERIFY(Meta.Height() == dim && Meta.Width() == n_metric_eta,
818 "SubcellMetricEta has unexpected shape.");
819 for (int id = 0; id < n_metric_eta; ++id)
820 {
821 for (int d = 0; d < dim; ++d)
822 {
823 eta_h[((el * n_metric_eta + id) * dim) + d] = Meta(d, id);
824 }
825 }
826 }
827 if (dim == 3)
828 {
829 const mfem::DenseMatrix &Mzeta = SubcellMetricZeta(el);
830 MFEM_VERIFY(Mzeta.Height() == dim && Mzeta.Width() == n_metric_zeta,
831 "SubcellMetricZeta has unexpected shape.");
832 for (int id = 0; id < n_metric_zeta; ++id)
833 {
834 for (int d = 0; d < dim; ++d)
835 {
836 zeta_h[((el * n_metric_zeta + id) * dim) + d] = Mzeta(d, id);
837 }
838 }
839 }
840 }
841 }
842
843 template<typename CacheT, typename DeviceCacheT>
844 void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
845 {
846 // Fixed data items
847 // - Discretization parameters:
848 device_cache.ndof_scalar_el = cache.ndof_scalar_el;
849 device_cache.num_attr = cache.num_attr;
850 device_cache.attr_marker_d = cache.vol_attr_marker.Read();
851 device_cache.elem_attr_d = cache.elem_attr.Read();
852 device_cache.num_face_points = cache.num_face_points;
853 device_cache.p = cache.p;
854 device_cache.dim = cache.dim;
855 device_cache.Np = cache.Np;
856 device_cache.Np_x = cache.Np_x;
857 device_cache.Np_y = cache.Np_y;
858 device_cache.Np_z = cache.Np_z;
859 device_cache.num_elements = cache.num_elements;
860 device_cache.num_equations = cache.num_equations;
861 device_cache.axisymmetric = cache.axisymmetric;
862
863 // - Volume element data
864 device_cache.elJac_d = cache.elJac.Read();
865 device_cache.elMetric_d = cache.elMetric.Read();
866 device_cache.D_d = cache.D.Read();
867 device_cache.Dhat_d = cache.Dhat.Read();
868 device_cache.Dhat2_d = cache.Dhat2.Read();
869 device_cache.elQWgts_d = cache.elQuadratureWeights.Read();
870 device_cache.elRadius_d = cache.elRadius.Read();
871
872 // - Interior faces (including remote)
873 device_cache.nor_d = cache.face_normals.Read();
874 device_cache.fw_minus_d = cache.face_wt_minus.Read();
875 device_cache.fw_plus_d = cache.face_wt_plus.Read();
876 device_cache.face_radius_d = cache.face_radius.Read();
877
878 // - Boundary faces
879 device_cache.bnd_nor_d = cache.bnd_normals.Read();
880 device_cache.bnd_wt_d = cache.bnd_wt.Read();
881 device_cache.bnd_radius_d = cache.bnd_radius.Read();
882 device_cache.bnd_marker_index_d = cache.bnd_marker_index.Read();
883 device_cache.bnd_marker_to_bc_descr_d = cache.bnd_marker_to_bc_descr.Read();
884 device_cache.bc_scalar_d = cache.bc_scalar_data.Read();
885 device_cache.bc_vector_d = cache.bc_vector_data.Read();
886 device_cache.bc_descr_d = cache.bc_descriptors.Read();
887
888 // Updated every step by the compute device
889 device_cache.elWaveSpeed_d = cache.elWaveSpeed.ReadWrite();
890 device_cache.ifWaveSpeed_d = cache.ifWaveSpeed.ReadWrite();
891 device_cache.bndWaveSpeed_d = cache.bndWaveSpeed.ReadWrite();
892
893 // POD gas model
894 device_cache.gas = cache.gas.to_device(cache);
895 device_cache.iflux = cache.iflux;
896
897#ifdef SUBCELL_FV_BLENDING
898 device_cache.subcell_metric_xi_d = cache.subcellMetricXi.Read();
899 device_cache.subcell_metric_eta_d = cache.subcellMetricEta.Read();
900 device_cache.subcell_metric_zeta_d = cache.subcellMetricZeta.Read();
901 device_cache.subcell_weights_d = cache.subcellWeights.Read();
902#endif
903
904 }
905
906 template<typename CacheT>
907 bool AxisBoundaryGeometryIsValid(const CacheT &cache)
908 {
909 const int points_per_face = cache.num_face_points;
910 const int boundary_faces = cache.bnd_marker_index.Size();
911 const int *marker_index = cache.bnd_marker_index.HostRead();
912 const Theseus::BCDescriptor *descriptors =
913 cache.bc_descriptors.HostRead();
914 const mfem::real_t *radius = cache.bnd_radius.HostRead();
915 for (int face = 0; face < boundary_faces; ++face)
916 {
917 const int descriptor_index = marker_index[face];
918 if (descriptor_index < 0 ||
919 descriptor_index >= cache.bc_descriptors.Size() ||
920 descriptors[descriptor_index].type !=
922 {
923 continue;
924 }
925 if (cache.bnd_radius.Size() != boundary_faces*points_per_face)
926 {
927 return false;
928 }
929 for (int point = 0; point < points_per_face; ++point)
930 {
931 const mfem::real_t point_radius =
932 radius[face*points_per_face + point];
934 {
935 return false;
936 }
937 }
938 }
939 return true;
940 }
941
942 template<typename CacheT>
943 void ValidateAxisBoundaryGeometry(const CacheT &cache)
944 {
945 MFEM_VERIFY(AxisBoundaryGeometryIsValid(cache),
946 "an axis boundary quadrature point is not on r=0");
947 }
948
949 template<typename CacheT>
951 Prandtl::ModalBasis &modalBasis)
952 {
953 // std::shared_ptr<Prandtl::ModalBasis> modalBasis;
954 // mfem::Vector rho_p, modes, modesM1, modesM2;
955 // mfem::Array2D<int> ubdegs;
956 // mfem::Array<int> ubdegs_row;
957 int dim = c.dim;
958 int order = c.p;
959 int ndofs = c.ndof_scalar_el;
960 int ne = c.num_elements;
961 const mfem::Array2D<int> ubdegs(modalBasis.GetPolyDegs());
962
963 c.modal.SetSize(ndofs * ndofs);
964 c.keep_M1.SetSize(ndofs);
965 c.keep_M2.SetSize(ndofs);
966 c.eta.SetSize(ne);
967
968 c.modal.UseDevice();
969 c.keep_M1.UseDevice();
970 c.keep_M2.UseDevice();
971 c.eta.UseDevice();
972
973 auto *modal_h = c.modal.HostWrite();
974 auto *m1_h = c.keep_M1.HostWrite();
975 auto *m2_h = c.keep_M2.HostWrite();
976
977 mfem::Vector nodal(ndofs), modes(ndofs);
978
979 for (int q = 0; q < ndofs; ++q)
980 {
981 nodal = 0.0;
982 nodal(q) = 1.0;
983
984 modalBasis.ComputeModes(nodal, modes);
985
986 for (int m = 0; m < ndofs; ++m)
987 {
988 modal_h[m * ndofs + q] = modes(m);
989 }
990 }
991
992 mfem::Array<int> row;
993 for (int m = 0; m < ndofs; ++m)
994 {
995 ubdegs.GetRow(m, row);
996
997 bool keep1 = true;
998 bool keep2 = true;
999
1000 for (int d = 0; d < dim; ++d)
1001 {
1002 if (row[d] > order - 2)
1003 {
1004 keep2 = false;
1005 }
1006
1007 if (row[d] > order - 1)
1008 {
1009 keep1 = false;
1010 }
1011 }
1012
1013 m1_h[m] = keep1 ? 1.0 : 0.0;
1014 m2_h[m] = keep2 ? 1.0 : 0.0;
1015 }
1016
1017 c.modal_d = c.modal.Read();
1018 c.keep_M1_d = c.keep_M1.Read();
1019 c.keep_M2_d = c.keep_M2.Read();
1020 c.eta_d = c.eta.ReadWrite();
1021
1022 }
1023
1024 template<typename CacheT>
1025 void OutputCacheContents(const CacheT &cache)
1026 {
1027 std::cout << "Cache Contents:" << std::endl
1028 << "p = " << cache.p << std::endl
1029 << "dim = " << cache.dim << std::endl
1030 << "num_elements = " << cache.num_elements << std::endl
1031 << "Np,Np_x,Np_y,Np_z = " << cache.Np << "," << cache.Np_x
1032 << "," << cache.Np_y << "," << cache.Np_z << std::endl
1033 << "num_face_points = " << cache.num_face_points << std::endl
1034 << "num_attr = " << cache.num_attr << std::endl
1035 << "ndof_scalar_el = " << cache.ndof_scalar_el << std::endl
1036 << "num_interior_faces = " << cache.num_interior_faces << std::endl;
1037 MFEM_VERIFY(cache.ir, "IR is not set");
1038 MFEM_VERIFY(cache.ir_face, "Face IR not set");
1039 MFEM_VERIFY(cache.ir_vol, "Volume IR not set");
1040 MFEM_VERIFY(cache.restr_v, "Volume Restriction not set");
1041 MFEM_VERIFY(cache.restr_f, "Facial Restriction not set");
1042 MFEM_VERIFY(cache.ndof_scalar_el == cache.Np_x*cache.Np_y*cache.Np_z,
1043 "Element dof count not equal to num quadrature points.");
1044 int ds_size = cache.elem_attr.Size();
1045 MFEM_VERIFY(ds_size > 0, "Elem attr not set");
1046
1047 ds_size = cache.elWaveSpeed.Size();
1048 MFEM_VERIFY(ds_size == cache.num_elements, "Element wavespeeds missized.");
1049 ds_size = cache.bndWaveSpeed.Size();
1050 ds_size = cache.elJac.Size();
1051 MFEM_VERIFY(ds_size > 0, "Element Jacobians not set");
1052 ds_size = cache.elMetric.Size();
1053 MFEM_VERIFY(ds_size > 0, "Element Metrics not set");
1054 ds_size = cache.D.Size();
1055 MFEM_VERIFY(ds_size > 0, "Deriv operator not set");
1056 ds_size = cache.Dhat2.Size();
1057 MFEM_VERIFY(ds_size > 0, "Dhat2 operator not set");
1058 ds_size = cache.face_normals.Size();
1059 MFEM_VERIFY(ds_size == cache.num_face_points*cache.num_interior_faces*cache.dim,
1060 "Inapropriately sized face normals");
1061 ds_size = cache.face_wt_minus.Size();
1062 ds_size = cache.face_wt_plus.Size();
1063 MFEM_VERIFY(ds_size > 0, "Face weights not set.");
1064 }
1065
1066}
Definition ModalBasis.hpp:14
mfem::Array2D< int > GetPolyDegs()
Definition ModalBasis.cpp:37
void ComputeModes(const mfem::Vector &nodes)
Definition ModalBasis.cpp:142
Definition timer.hpp:17
Definition AxisymmetricGeometry.hpp:15
void GetDiscretizationInfo(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:39
void SetupVolumeMarkers(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:219
bool AxisBoundaryGeometryIsValid(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:907
void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
Definition dgsem_cache_utilities.hpp:844
void BuildPerssonDeviceCache(CacheT &c, Prandtl::ModalBasis &modalBasis)
Definition dgsem_cache_utilities.hpp:950
void AssembleBoundaryFaceGeometryTerms(mfem::FiniteElementSpace *fes, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:337
void CacheSubcellMetricData(const mfem::DenseTensor &SubcellMetricXi, const mfem::DenseTensor &SubcellMetricEta, const mfem::DenseTensor &SubcellMetricZeta, CacheT *cache)
Definition dgsem_cache_utilities.hpp:767
void AssembleInteriorFaceGeometryTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:502
void BuildBoundaryFacePermutationMap(mfem::ParMesh *pmesh, CacheT *cache)
Definition dgsem_cache_utilities.hpp:261
void SetupGeometricTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:83
void SetupRestrictions(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:67
void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:18
void OutputCacheContents(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:1025
void BuildBoundaryFaceToMarkerMap(mfem::ParMesh *pmesh, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:282
void ValidateAxisBoundaryGeometry(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:943
void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:627
void BuildBoundaryFaceToBEMap(mfem::ParMesh *pmesh, mfem::Array< int > &face_to_be)
Definition dgsem_cache_utilities.hpp:319
void AssembleElementVolumeGeometricTerms(mfem::ElementTransformation &Tr, CacheT *cache)
Definition dgsem_cache_utilities.hpp:455
static mfem::real_t ValidateRadius(mfem::real_t radius, const std::string &location)
Definition AxisymmetricGeometry.hpp:43
static constexpr mfem::real_t radius_tolerance
Definition AxisymmetricGeometry.hpp:22
static constexpr int radial_coordinate
Definition AxisymmetricGeometry.hpp:20
static MFEM_HOST_DEVICE mfem::real_t Radius(const mfem::real_t *physical)
Definition AxisymmetricGeometry.hpp:32
Definition bc_cache_utilities.hpp:35
int type
Definition bc_cache_utilities.hpp:36