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"
10#include "dgsem_cache.hpp"
11#include "ModalBasis.hpp"
12#include "timer.hpp"
13
14namespace Theseus {
15
16 // TODO: Not complete as written, after this, callsites need to set up
17 // Boundary Face data structures with a separate call. Need to fix.
18 template<typename CacheT>
19 void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
20 {
21 GetDiscretizationInfo(fes, cache);
22 {
23 Theseus::ScopedTimer timer("SetupRestrictions");
24 SetupRestrictions(fes, cache);
25 }
26 SetupVolumeMarkers(fes, cache);
27 {
28 Theseus::ScopedTimer timer("SetupGeometricTerms");
29 SetupGeometricTerms(fes, cache);
30 }
31 // AssembleBoundaryFaceGeometryTerms(fes, cache);
32 // TODO: Move these to where the caches are created and validated
33 // MFEM_VERIFY(nfaces == cache.num_interior_faces, "restriction faces != cached interior faces");
34 // MFEM_VERIFY(cache.face_normals.Size() == nfaces*nfp*dim, "normals size mismatch");
35 // MFEM_VERIFY(cache.face_wt_minus.Size() == nfaces*nfp, "w_minus size mismatch");
36 // MFEM_VERIFY(cache.face_wt_plus.Size() == nfaces*nfp, "w_plus size mismatch");
37 }
38
39 template<typename CacheT>
40 void GetDiscretizationInfo(mfem::FiniteElementSpace *fes, CacheT *cache)
41 {
42
43 MFEM_VERIFY(fes, "fes must be set");
44 mfem::Mesh *mesh = fes->GetMesh();
45 MFEM_VERIFY(mesh, "mesh must be set");
46 const int p = fes->GetFE(0)->GetOrder();
47 const int dim = mesh->SpaceDimension();
48 const int Np = p + 1; // num 1d quadrature points
49 const int Np_x = Np;
50 const int Np_y = dim > 1 ? Np : 1;
51 const int Np_z = dim > 2 ? Np : 1;
52 const int ne = fes->GetNE();
53 const int num_dofs_per_eqn_per_element = fes->GetFE(0)->GetDof();
54 const int num_eqns = fes->GetVDim();
55
56 cache->p = p;
57 cache->Np = Np;
58 cache->dim = dim;
59 cache->Np_x = Np_x;
60 cache->Np_y = Np_y;
61 cache->Np_z = Np_z;
62 cache->num_elements = ne;
63 cache->ndof_scalar_el = num_dofs_per_eqn_per_element;
64 cache->num_equations = num_eqns;
65 }
66
67 template<typename CacheT>
68 void SetupRestrictions(mfem::FiniteElementSpace *fes, CacheT *cache)
69 {
70 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace*>(fes);
71 MFEM_VERIFY(pfes, "Restriction setup requires ParFiniteElementSpace");
72 cache->restr_v = fes->GetElementRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC);
73 cache->restr_f = pfes->GetFaceRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC,
74 mfem::FaceType::Interior,
75 mfem::L2FaceValues::DoubleValued);
76 cache->restr_b = pfes->GetFaceRestriction(mfem::ElementDofOrdering::LEXICOGRAPHIC,
77 mfem::FaceType::Boundary,
78 mfem::L2FaceValues::SingleValued);
79 }
80
81 // Set up and populate elJac, elMetric, D, Dhat, Dhat2
82 // Face normals, and weights
83 template<typename CacheT>
84 void SetupGeometricTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
85 {
86 const int nelem = cache->num_elements;
87 const int p = cache->p;
88 const int Np = cache->Np;
89 const int dim = cache->dim;
90 const int Np_x = Np;
91 const int Np_y = dim > 1 ? Np : 1;
92 const int Np_z = dim > 2 ? Np : 1;
93 const int neq = cache->num_equations;
94 mfem::Mesh *mesh = fes->GetMesh();
95
96 // Build integration rules
97 const int IntegrationOrder = 2 * Np_x - 3;
98 cache->ir = &cache->GLIntRules.Get(mfem::Geometry::SEGMENT, IntegrationOrder);
99 auto vol_topo = (dim == 1 ? mfem::Geometry::SEGMENT :
100 (dim == 2 ? mfem::Geometry::SQUARE : mfem::Geometry::CUBE));
101 auto face_topo = (dim == 1 ? mfem::Geometry::POINT :
102 (dim == 2 ? mfem::Geometry::SEGMENT : mfem::Geometry::SQUARE));
103
104 cache->ir_face = &cache->GLIntRules.Get(face_topo, IntegrationOrder);
105 cache->ir_vol = &cache->GLIntRules.Get(vol_topo, IntegrationOrder);
106
107 MFEM_ASSERT(cache->ir->GetNPoints() == Np_x, "");
108 MFEM_ASSERT(cache->ir_vol->GetNPoints() == Np_x*Np_y*Np_z, "");
109
110 // Populate element Jacobian determinant and metric terms
111 cache->elJac.SetSize(Np_x*Np_y*Np_z*nelem);
112 cache->elMetric.SetSize(dim*dim*Np_x*Np_y*Np_z*nelem);
113 cache->elQuadratureWeights.SetSize(Np_x*Np_y*Np_z*nelem);
114 cache->elRadius.SetSize(dim > AxisymmetricGeometry::radial_coordinate ?
115 Np_x*Np_y*Np_z*nelem : 0);
116 for (int i = 0; i < nelem; i++)
117 {
118 mfem::ElementTransformation *T = fes->GetElementTransformation(i);
119 assert(T->ElementNo == i);
121 }
122
123 // Set up derivative operators
124 mfem::DenseMatrix D_T, Dhat_T, Dhat2_T;
125 D_T.SetSize(Np_x);
126 Dhat_T.SetSize(Np_x);
127 Dhat2_T.SetSize(Np_x);
128
129 mfem::Vector wBary(Np_x);
130 wBary = 1.0;
131
132 for (int i = 1; i < Np_x; i++)
133 {
134 for (int j = 0; j < i; j++)
135 {
136 wBary(j) *= (cache->ir->IntPoint(j).x - cache->ir->IntPoint(i).x);
137 wBary(i) *= (cache->ir->IntPoint(i).x - cache->ir->IntPoint(j).x);
138 }
139 }
140
141 wBary.Reciprocal();
142 D_T = 0.0;
143 for (int iL = 0; iL < Np_x; iL++)
144 {
145 for (int i = 0; i < Np_x; i++)
146 {
147 if (iL != i)
148 {
149 D_T(i, iL) = wBary(iL) / wBary(i) / (cache->ir->IntPoint(i).x - cache->ir->IntPoint(iL).x);
150 D_T(i, i) -= D_T(i, iL);
151 }
152 }
153 }
154
155 Dhat_T = D_T;
156 Dhat_T(0, 0) += 1.0 / cache->ir->IntPoint(0).weight;
157 Dhat_T(Np - 1, Np - 1) -= 1.0 / cache->ir->IntPoint(Np - 1).weight;
158 Dhat_T.Transpose();
159
160 Dhat2_T = D_T;
161 Dhat2_T *= 2.0;
162 Dhat2_T(0, 0) += 1.0 / cache->ir->IntPoint(0).weight;
163 Dhat2_T(Np - 1, Np - 1) -= 1.0 / cache->ir->IntPoint(Np - 1).weight;
164
165 cache->stabilityAdvectionScale = ReferenceAdvectionSpectralScale(p);
166 cache->stabilityDiffusionScale = ReferenceBR1DiffusionSpectralScale(p);
167 const mfem::real_t endpoint_lift =
168 1.0 / cache->ir->IntPoint(0).weight;
169 cache->stabilitySurfaceScale =
170 cache->stabilityAdvectionScale / endpoint_lift;
171
172 Dhat2_T.Transpose();
173 D_T.Transpose();
174
175 // Just copy D_T, Dhat_T, and Dhat2_T
176 cache->D.SetSize(Np_x*Np_x);
177 cache->Dhat.SetSize(Np_x*Np_x);
178 cache->Dhat2.SetSize(Np_x*Np_x);
179 std::memcpy(cache->D.HostWrite(), D_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
180 std::memcpy(cache->Dhat.HostWrite(), Dhat_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
181 std::memcpy(cache->Dhat2.HostWrite(), Dhat2_T.Data(), sizeof(mfem::real_t)*Np_x*Np_x);
182
183 cache->elJac.UseDevice();
184 cache->elMetric.UseDevice();
185 cache->elRadius.UseDevice();
186 cache->D.UseDevice();
187 cache->Dhat.UseDevice();
188 cache->Dhat2.UseDevice();
189 cache->elJac.Read();
190 cache->elMetric.Read();
191 cache->elRadius.Read();
192 cache->D.Read();
193 cache->Dhat.Read();
194 cache->Dhat2.Read();
195
196 // Set up data for faces
197 const int nfp = cache->ir_face->GetNPoints();
198 cache->num_face_points = nfp;
199
200 const int nfaces_restr = cache->restr_f->Height() / (nfp * neq * 2);
201 cache->num_interior_faces = nfaces_restr;
202 MFEM_VERIFY(nfaces_restr > 0, "nfaces_restr is 0");
203
205
206 cache->face_normals.UseDevice();
207 cache->face_wt_minus.UseDevice();
208 cache->face_wt_plus.UseDevice();
209 cache->face_radius.UseDevice();
210 cache->face_normals.Read();
211 cache->face_wt_minus.Read();
212 cache->face_wt_plus.Read();
213 cache->face_radius.Read();
214
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 // 0. Get a restriction-face-to-bnd-element mapping
361 mfem::Array<int> face_to_be;
362 BuildBoundaryFaceToBEMap(pmesh, face_to_be);
363
364 // 1. Permutation map
366
367 // 2. BC mapping
368 BuildBoundaryFaceToMarkerMap(pmesh, bdr_marker_vector, cache);
369
370 // 3. Geometry arrays
371 cache->bnd_normals.SetSize(nbnd_faces * nfp * dim);
372 cache->bnd_wt.SetSize(nbnd_faces * nfp);
373 cache->bnd_xyz.SetSize(nbnd_faces * nfp * dim);
374 cache->bnd_radius.SetSize(dim > AxisymmetricGeometry::radial_coordinate ?
375 nbnd_faces * nfp : 0);
376
377 mfem::real_t *nor_d = cache->bnd_normals.HostWrite();
378 mfem::real_t *wt_d = cache->bnd_wt.HostWrite();
379 mfem::real_t *xyz_d = cache->bnd_xyz.HostWrite();
380 mfem::real_t *rad_d = cache->bnd_radius.Size() > 0 ?
381 cache->bnd_radius.HostWrite() : nullptr;
382
383 const mfem::real_t w0 = cache->ir->IntPoint(0).weight;
384
385 mfem::Vector nor(dim);
386 mfem::Vector phys(dim);
387
388 auto store = [&](int fslot, int fp_restr,
389 const mfem::Vector &nor,
390 const mfem::Vector &phys,
391 mfem::real_t inv_wJ1)
392 {
393 const int base_scl = fslot * nfp + fp_restr;
394 const int base_vec = base_scl * dim;
395
396 for (int d = 0; d < dim; ++d)
397 {
398 nor_d[base_vec + d] = nor(d);
399 xyz_d[base_vec + d] = phys(d);
400 }
401
402 wt_d[base_scl] = inv_wJ1;
403 if (rad_d)
404 {
405 const mfem::real_t radius =
406 AxisymmetricGeometry::Radius(phys.GetData());
407 rad_d[base_scl] = cache->axisymmetric ?
409 radius, "boundary face " + std::to_string(fslot) +
410 ", point " + std::to_string(fp_restr)) : radius;
411 }
412 };
413
414 for (int fslot = 0; fslot < nbnd_faces; ++fslot)
415 {
416 const int face_id = bnd_faces[fslot];
417 const int be_match = face_to_be[face_id];
418 // Map boundary face slot -> boundary element index.
419 // We need boundary-element index for GetBdrFaceTransformations(be).
420 MFEM_VERIFY(be_match >= 0, "Could not find boundary element for boundary face");
421 auto *tr = mesh->GetBdrFaceTransformations(be_match);
422 MFEM_VERIFY(tr, "expected boundary face transformation");
423
424 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
425 {
426 const int fp_geom = cache->inv_fp_map_bnd[fslot * nfp + fp_restr];
427 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
428 tr->SetAllIntPoints(&ip);
429
430 const mfem::real_t J1 = tr->GetElement1Transformation().Weight();
431 if (dim == 1)
432 {
433 nor(0) = (tr->GetElement1IntPoint().x - 0.5) * 2.0;
434 }
435 else
436 {
437 mfem::CalcOrtho(tr->Jacobian(), nor);
438 }
439 tr->Transform(ip, phys);
440 store(fslot, fp_restr, nor, phys, 1.0 / (w0 * J1));
441 }
442 }
443 cache->bnd_xyz.UseDevice();
444 cache->bnd_radius.UseDevice();
445 cache->bnd_xyz.Read();
446 cache->bnd_radius.Read();
447 }
448
449 // Builds element-specific Jac/Metric and stuffs into cache.elJac, cache.elMetric
450 template<typename CacheT>
451 void AssembleElementVolumeGeometricTerms(mfem::ElementTransformation &Tr, CacheT *cache)
452 {
453
454 mfem::real_t *Jinv_h = cache->elJac.HostWrite();
455 mfem::real_t *Met_h = cache->elMetric.HostWrite();
456 mfem::real_t *qWgts_h = cache->elQuadratureWeights.HostWrite();
457 mfem::real_t *radius_h = cache->elRadius.Size() > 0 ?
458 cache->elRadius.HostWrite() : nullptr;
459
460 int dim = cache->dim;
461 mfem::Vector metric1(dim);
462 mfem::Vector physical(dim);
463 const int e = Tr.ElementNo;
464 const int nq = cache->Np_x * cache->Np_y * cache->Np_z;
465
466 for (int q = 0; q < nq; ++q)
467 {
468 const mfem::IntegrationPoint &ip = cache->ir_vol->IntPoint(q);
469 Tr.SetIntPoint(&ip);
470 const mfem::real_t J = Tr.Weight();
471 Jinv_h[e*nq + q] = J;
472 qWgts_h[e*nq + q] = J * ip.weight;
473 if (radius_h)
474 {
475 Tr.Transform(ip, physical);
476 const mfem::real_t radius =
477 AxisymmetricGeometry::Radius(physical.GetData());
478 radius_h[e*nq + q] = cache->axisymmetric ?
480 radius, "element " + std::to_string(e) +
481 ", point " + std::to_string(q)) : radius;
482 }
483 const mfem::DenseMatrix &adj = Tr.AdjugateJacobian();
484 for (int dir = 0; dir < dim; ++dir)
485 {
486 adj.GetRow(dir, metric1); // metric1.Size() == dim
487
488 for (int d = 0; d < dim; ++d)
489 {
490 const int idxM = (((e*nq + q)*dim + dir)*dim + d);
491 Met_h[idxM] = metric1(d);
492 }
493 }
494 }
495 }
496
497 template<typename CacheT>
498 void AssembleInteriorFaceGeometryTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
499 {
500 auto *mesh = fes->GetMesh();
501 auto *pmesh = dynamic_cast<mfem::ParMesh*>(mesh);
502 auto *pfes = dynamic_cast<mfem::ParFiniteElementSpace*>(fes);
503 cache->fqs_int.reset(new mfem::FaceQuadratureSpace(*mesh, *cache->ir_face,
504 mfem::FaceType::Interior));
505 MFEM_VERIFY(pfes, "need ParFiniteElementSpace");
506
507 const int dim = mesh->Dimension();
508 const int neq = pfes->GetVDim();
509 const int nfp = cache->ir_face->GetNPoints();
510
511 auto &int_faces = pmesh->GetFaceIndices(mfem::FaceType::Interior);
512 const int ninterior_faces = int_faces.Size();
513
514 cache->inv_fp_map.SetSize(ninterior_faces * nfp);
515 for (int face_slot = 0; face_slot < ninterior_faces; ++face_slot)
516 {
517 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
518 {
519 int fp_perm = cache->fqs_int->GetPermutedIndex(face_slot, fp_restr);
520 cache->inv_fp_map[face_slot*nfp + fp_perm] = fp_restr;
521 }
522 }
523
524 cache->face_normals.SetSize(ninterior_faces * nfp * dim);
525 cache->face_wt_minus.SetSize(ninterior_faces * nfp);
526 cache->face_wt_plus.SetSize(ninterior_faces * nfp);
527 cache->face_radius.SetSize(
529 ninterior_faces * nfp : 0);
530
531 mfem::real_t *nor_d = cache->face_normals.HostWrite();
532 mfem::real_t *inv1_d = cache->face_wt_minus.HostWrite();
533 mfem::real_t *inv2_d = cache->face_wt_plus.HostWrite();
534 mfem::real_t *radius_d = cache->face_radius.Size() > 0 ?
535 cache->face_radius.HostWrite() : nullptr;
536 const mfem::real_t w0 = cache->ir->IntPoint(0).weight;
537
538 auto store = [&](int fslot, int fp, const mfem::Vector &nor,
539 const mfem::Vector &physical,
540 mfem::real_t inv_wJ1, mfem::real_t inv_wJ2)
541 {
542 const int nbase = (fslot * nfp + fp) * dim;
543 for (int d = 0; d < dim; ++d) { nor_d[nbase + d] = nor(d); }
544 inv1_d[fslot * nfp + fp] = inv_wJ1;
545 inv2_d[fslot * nfp + fp] = inv_wJ2;
546 if (radius_d)
547 {
548 const mfem::real_t radius =
549 AxisymmetricGeometry::Radius(physical.GetData());
550 radius_d[fslot*nfp + fp] = cache->axisymmetric ?
552 radius, "interior face " + std::to_string(fslot) +
553 ", point " + std::to_string(fp)) : radius;
554 }
555 };
556
557 mfem::Vector nor(dim);
558 mfem::Vector physical(dim);
559 // The order of faces in GetFaceIndices(FaceType::Interior) *must*
560 // match the order of the faces in the interior face restriction
561 // operator face slots.
562 for (int fslot = 0; fslot < ninterior_faces; ++fslot)
563 {
564 const int face_id = int_faces[fslot];
565 // bool face_is_flipped = false;
566 // for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
567 // {
568 // const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
569 // if (fp_geom != fp_restr){
570 // face_is_flipped = true;
571 // }
572 // }
573 auto *tr = mesh->GetInteriorFaceTransformations(face_id);
574 if (tr){ // Do interior face caching
575 // MFEM_VERIFY(tr, "expected interior face");
576 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
577 {
578 const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
579 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
580 tr->SetAllIntPoints(&ip);
581
582 const mfem::real_t J1 = tr->GetElement1Transformation().Weight();
583 const mfem::real_t J2 = tr->GetElement2Transformation().Weight();
584
585 if (dim == 1) { nor(0) = (tr->GetElement1IntPoint().x - 0.5)*2.0; }
586 else { mfem::CalcOrtho(tr->Jacobian(), nor); }
587 tr->Transform(ip, physical);
588
589 //const mfem::real_t fac = face_is_flipped ? -1.0 : 1.0;
590 const mfem::real_t fac = 1.0;
591 store(fslot, fp_restr, nor, physical,
592 fac/(w0*J1), fac/(w0*J2));
593 }
594 continue;
595 } // Internal face processing
596 {
597 auto *sh_tr = pmesh->GetSharedFaceTransformationsByLocalIndex(face_id, true);
598 MFEM_VERIFY(sh_tr, "expected shared face");
599 for (int fp_restr = 0; fp_restr < nfp; ++fp_restr)
600 {
601 const int fp_geom = cache->MapFp(fslot, fp_restr);// <-- critical
602 const mfem::IntegrationPoint &ip = cache->ir_face->IntPoint(fp_geom);
603 sh_tr->SetAllIntPoints(&ip);
604
605 const mfem::real_t J1 = sh_tr->GetElement1Transformation().Weight();
606 const mfem::real_t J2 = sh_tr->GetElement2Transformation().Weight();
607
608 if (dim == 1) { nor(0) = (sh_tr->GetElement1IntPoint().x - 0.5)*2.0; }
609 else { mfem::CalcOrtho(sh_tr->Jacobian(), nor); }
610 sh_tr->Transform(ip, physical);
611
612 //const mfem::real_t fac = face_is_flipped ? -1.0 : 1.0;
613 const mfem::real_t fac1 = 1.0;
614 const mfem::real_t fac2 = 0.0;
615 store(fslot, fp_restr, nor, physical,
616 fac1/(w0*J1), fac2/(w0*J2));
617 }
618 } // Shared face processing
619 } // Interior face processing
620 }
621
622 template<typename CacheT>
623 void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
624 {
625 MFEM_VERIFY(fes != nullptr, "A finite element space is required.");
626 const int dim = cache->dim;
627 const int ne = cache->num_elements;
628 const int Np_x = cache->Np_x;
629 const int Np_y = cache->Np_y;
630 const int Np_z = cache->Np_z;
631 const int nq = cache->ndof_scalar_el;
632 const int n_metric_xi = (Np_x + 1) * Np_y * Np_z;
633 const int n_metric_eta = Np_x * (Np_y + 1) * Np_z;
634 const int n_metric_zeta = Np_x * Np_y * (Np_z + 1);
635
636 cache->subcellWeights.SetSize(Np_x);
637 mfem::real_t *wgts = cache->subcellWeights.HostWrite();
638 for (int i = 0;i < Np_x;i++){
639 wgts[i] = cache->ir->IntPoint(i).weight;
640 }
641
642 cache->subcellMetricXi.SetSize(n_metric_xi*dim*ne);
643 if (dim > 1)
644 cache->subcellMetricEta.SetSize(n_metric_eta*dim*ne);
645 if (dim > 2)
646 cache->subcellMetricZeta.SetSize(n_metric_zeta*dim*ne);
647
648 const mfem::real_t *metric = cache->elMetric.Read();
649 const mfem::real_t *D = cache->D.Read();
650 const mfem::real_t *weights = cache->subcellWeights.Read();
651 mfem::real_t *xi = cache->subcellMetricXi.Write();
652
653 const int xi_work = ne*Np_z*Np_y*(Np_x - 1)*dim;
654 mfem::forall(xi_work, [=] MFEM_HOST_DEVICE (int tid) {
655 const int d = tid % dim;
656 int item = tid / dim;
657 const int i = item % (Np_x - 1) + 1;
658 item /= Np_x - 1;
659 const int j = item % Np_y;
660 item /= Np_y;
661 const int k = item % Np_z;
662 const int el = item / Np_z;
663 const int line = k*Np_y*Np_x + j*Np_x;
664 mfem::real_t normal = metric[((el*nq + line)*dim)*dim + d];
665 for (int l = 0; l < i; ++l)
666 {
667 mfem::real_t sum = 0.0;
668 for (int m = 0; m < Np_x; ++m)
669 {
670 mfem::real_t contribution =
671 metric[((el*nq + line + m)*dim)*dim + d];
672 contribution *= D[l*Np_x + m];
673 sum += contribution;
674 }
675 sum *= weights[l];
676 normal += sum;
677 }
678 xi[((el*n_metric_xi + line + i)*dim) + d] = normal;
679 });
680
681 if (dim > 1)
682 {
683 mfem::real_t *eta = cache->subcellMetricEta.Write();
684 const int eta_work = ne*Np_z*Np_x*(Np_y - 1)*dim;
685 mfem::forall(eta_work, [=] MFEM_HOST_DEVICE (int tid) {
686 const int d = tid % dim;
687 int item = tid / dim;
688 const int j = item % (Np_y - 1) + 1;
689 item /= Np_y - 1;
690 const int i = item % Np_x;
691 item /= Np_x;
692 const int k = item % Np_z;
693 const int el = item / Np_z;
694 const int line = k*Np_y*Np_x + i;
695 mfem::real_t normal = metric[((el*nq + line)*dim + 1)*dim + d];
696 for (int l = 0; l < j; ++l)
697 {
698 mfem::real_t sum = 0.0;
699 for (int m = 0; m < Np_y; ++m)
700 {
701 mfem::real_t contribution =
702 metric[((el*nq + line + m*Np_x)*dim + 1)*dim + d];
703 contribution *= D[l*Np_x + m];
704 sum += contribution;
705 }
706 sum *= weights[l];
707 normal += sum;
708 }
709 eta[((el*n_metric_eta + k*Np_y*Np_x + j*Np_x + i)*dim) + d] = normal;
710 });
711 }
712
713 if (dim > 2)
714 {
715 mfem::real_t *zeta = cache->subcellMetricZeta.Write();
716 const int zeta_work = ne*Np_y*Np_x*(Np_z - 1)*dim;
717 mfem::forall(zeta_work, [=] MFEM_HOST_DEVICE (int tid) {
718 const int d = tid % dim;
719 int item = tid / dim;
720 const int k = item % (Np_z - 1) + 1;
721 item /= Np_z - 1;
722 const int i = item % Np_x;
723 item /= Np_x;
724 const int j = item % Np_y;
725 const int el = item / Np_y;
726 const int line = j*Np_x + i;
727 mfem::real_t normal = metric[((el*nq + line)*dim + 2)*dim + d];
728 for (int l = 0; l < k; ++l)
729 {
730 mfem::real_t sum = 0.0;
731 for (int m = 0; m < Np_z; ++m)
732 {
733 mfem::real_t contribution = metric[
734 ((el*nq + line + m*Np_y*Np_x)*dim + 2)*dim + d];
735 contribution *= D[l*Np_x + m];
736 sum += contribution;
737 }
738 sum *= weights[l];
739 normal += sum;
740 }
741 zeta[((el*n_metric_zeta + k*Np_y*Np_x + line)*dim) + d] = normal;
742 });
743 }
744 }
745
746 template<typename CacheT, typename DeviceCacheT>
747 void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
748 {
749 // Fixed data items
750 // - Discretization parameters:
751 device_cache.ndof_scalar_el = cache.ndof_scalar_el;
752 device_cache.num_attr = cache.num_attr;
753 device_cache.attr_marker_d = cache.vol_attr_marker.Read();
754 device_cache.elem_attr_d = cache.elem_attr.Read();
755 device_cache.num_face_points = cache.num_face_points;
756 device_cache.p = cache.p;
757 device_cache.dim = cache.dim;
758 device_cache.Np = cache.Np;
759 device_cache.Np_x = cache.Np_x;
760 device_cache.Np_y = cache.Np_y;
761 device_cache.Np_z = cache.Np_z;
762 device_cache.num_elements = cache.num_elements;
763 device_cache.num_equations = cache.num_equations;
764 device_cache.axisymmetric = cache.axisymmetric;
765
766 // - Volume element data
767 device_cache.elJac_d = cache.elJac.Read();
768 device_cache.elMetric_d = cache.elMetric.Read();
769 device_cache.D_d = cache.D.Read();
770 device_cache.Dhat_d = cache.Dhat.Read();
771 device_cache.Dhat2_d = cache.Dhat2.Read();
772 device_cache.elQWgts_d = cache.elQuadratureWeights.Read();
773 device_cache.elRadius_d = cache.elRadius.Read();
774
775 // - Interior faces (including remote)
776 device_cache.nor_d = cache.face_normals.Read();
777 device_cache.fw_minus_d = cache.face_wt_minus.Read();
778 device_cache.fw_plus_d = cache.face_wt_plus.Read();
779 device_cache.face_radius_d = cache.face_radius.Read();
780
781 // - Boundary faces
782 device_cache.bnd_nor_d = cache.bnd_normals.Read();
783 device_cache.bnd_wt_d = cache.bnd_wt.Read();
784 device_cache.bnd_radius_d = cache.bnd_radius.Read();
785 device_cache.bnd_marker_index_d = cache.bnd_marker_index.Read();
786 device_cache.bnd_marker_to_bc_descr_d = cache.bnd_marker_to_bc_descr.Read();
787 device_cache.bc_scalar_d = cache.bc_scalar_data.Read();
788 device_cache.bc_vector_d = cache.bc_vector_data.Read();
789 device_cache.bc_descr_d = cache.bc_descriptors.Read();
790
791 // POD gas model
792 device_cache.gas = cache.gas.to_device(cache);
793 device_cache.iflux = cache.iflux;
794
795#ifdef SUBCELL_FV_BLENDING
796 device_cache.subcell_metric_xi_d = cache.subcellMetricXi.Read();
797 device_cache.subcell_metric_eta_d = cache.subcellMetricEta.Read();
798 device_cache.subcell_metric_zeta_d = cache.subcellMetricZeta.Read();
799 device_cache.subcell_weights_d = cache.subcellWeights.Read();
800#endif
801
802 }
803
804 template<typename CacheT>
805 bool AxisBoundaryGeometryIsValid(const CacheT &cache)
806 {
807 const int points_per_face = cache.num_face_points;
808 const int boundary_faces = cache.bnd_marker_index.Size();
809 const int *marker_index = cache.bnd_marker_index.HostRead();
810 const Theseus::BCDescriptor *descriptors =
811 cache.bc_descriptors.HostRead();
812 const mfem::real_t *radius = cache.bnd_radius.HostRead();
813 for (int face = 0; face < boundary_faces; ++face)
814 {
815 const int descriptor_index = marker_index[face];
816 if (descriptor_index < 0 ||
817 descriptor_index >= cache.bc_descriptors.Size() ||
818 descriptors[descriptor_index].type !=
820 {
821 continue;
822 }
823 if (cache.bnd_radius.Size() != boundary_faces*points_per_face)
824 {
825 return false;
826 }
827 for (int point = 0; point < points_per_face; ++point)
828 {
829 const mfem::real_t point_radius =
830 radius[face*points_per_face + point];
832 {
833 return false;
834 }
835 }
836 }
837 return true;
838 }
839
840 template<typename CacheT>
841 void ValidateAxisBoundaryGeometry(const CacheT &cache)
842 {
843 MFEM_VERIFY(AxisBoundaryGeometryIsValid(cache),
844 "an axis boundary quadrature point is not on r=0");
845 }
846
847 template<typename CacheT>
849 Prandtl::ModalBasis &modalBasis)
850 {
851 // std::shared_ptr<Prandtl::ModalBasis> modalBasis;
852 // mfem::Vector rho_p, modes, modesM1, modesM2;
853 // mfem::Array2D<int> ubdegs;
854 // mfem::Array<int> ubdegs_row;
855 int dim = c.dim;
856 int order = c.p;
857 int ndofs = c.ndof_scalar_el;
858 int ne = c.num_elements;
859 const mfem::Array2D<int> ubdegs(modalBasis.GetPolyDegs());
860
861 c.modal.SetSize(ndofs * ndofs);
862 c.keep_M1.SetSize(ndofs);
863 c.keep_M2.SetSize(ndofs);
864 c.eta.SetSize(ne);
865
866 c.modal.UseDevice();
867 c.keep_M1.UseDevice();
868 c.keep_M2.UseDevice();
869 c.eta.UseDevice();
870
871 auto *modal_h = c.modal.HostWrite();
872 auto *m1_h = c.keep_M1.HostWrite();
873 auto *m2_h = c.keep_M2.HostWrite();
874
875 mfem::Vector nodal(ndofs), modes(ndofs);
876
877 for (int q = 0; q < ndofs; ++q)
878 {
879 nodal = 0.0;
880 nodal(q) = 1.0;
881
882 modalBasis.ComputeModes(nodal, modes);
883
884 for (int m = 0; m < ndofs; ++m)
885 {
886 // Store the transform transposed. CheckIndicatorSmoothness assigns
887 // adjacent modal rows to adjacent accelerator threads, so this layout
888 // turns their coefficient reads into contiguous transactions.
889 modal_h[q * ndofs + m] = modes(m);
890 }
891 }
892
893 mfem::Array<int> row;
894 for (int m = 0; m < ndofs; ++m)
895 {
896 ubdegs.GetRow(m, row);
897
898 bool keep1 = true;
899 bool keep2 = true;
900
901 for (int d = 0; d < dim; ++d)
902 {
903 if (row[d] > order - 2)
904 {
905 keep2 = false;
906 }
907
908 if (row[d] > order - 1)
909 {
910 keep1 = false;
911 }
912 }
913
914 m1_h[m] = keep1 ? 1.0 : 0.0;
915 m2_h[m] = keep2 ? 1.0 : 0.0;
916 }
917
918 c.modal_d = c.modal.Read();
919 c.keep_M1_d = c.keep_M1.Read();
920 c.keep_M2_d = c.keep_M2.Read();
921 c.eta_d = c.eta.ReadWrite();
922
923 }
924
925 template<typename CacheT>
926 void OutputCacheContents(const CacheT &cache)
927 {
928 std::cout << "Cache Contents:" << std::endl
929 << "p = " << cache.p << std::endl
930 << "dim = " << cache.dim << std::endl
931 << "num_elements = " << cache.num_elements << std::endl
932 << "Np,Np_x,Np_y,Np_z = " << cache.Np << "," << cache.Np_x
933 << "," << cache.Np_y << "," << cache.Np_z << std::endl
934 << "num_face_points = " << cache.num_face_points << std::endl
935 << "num_attr = " << cache.num_attr << std::endl
936 << "ndof_scalar_el = " << cache.ndof_scalar_el << std::endl
937 << "num_interior_faces = " << cache.num_interior_faces << std::endl;
938 MFEM_VERIFY(cache.ir, "IR is not set");
939 MFEM_VERIFY(cache.ir_face, "Face IR not set");
940 MFEM_VERIFY(cache.ir_vol, "Volume IR not set");
941 MFEM_VERIFY(cache.restr_v, "Volume Restriction not set");
942 MFEM_VERIFY(cache.restr_f, "Facial Restriction not set");
943 MFEM_VERIFY(cache.ndof_scalar_el == cache.Np_x*cache.Np_y*cache.Np_z,
944 "Element dof count not equal to num quadrature points.");
945 int ds_size = cache.elem_attr.Size();
946 MFEM_VERIFY(ds_size > 0, "Elem attr not set");
947
948 ds_size = cache.elJac.Size();
949 MFEM_VERIFY(ds_size > 0, "Element Jacobians not set");
950 ds_size = cache.elMetric.Size();
951 MFEM_VERIFY(ds_size > 0, "Element Metrics not set");
952 ds_size = cache.D.Size();
953 MFEM_VERIFY(ds_size > 0, "Deriv operator not set");
954 ds_size = cache.Dhat2.Size();
955 MFEM_VERIFY(ds_size > 0, "Dhat2 operator not set");
956 ds_size = cache.face_normals.Size();
957 MFEM_VERIFY(ds_size == cache.num_face_points*cache.num_interior_faces*cache.dim,
958 "Inapropriately sized face normals");
959 ds_size = cache.face_wt_minus.Size();
960 ds_size = cache.face_wt_plus.Size();
961 MFEM_VERIFY(ds_size > 0, "Face weights not set.");
962 }
963
964}
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:22
Definition AxisymmetricGeometry.hpp:15
void GetDiscretizationInfo(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:40
void SetupVolumeMarkers(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:219
bool AxisBoundaryGeometryIsValid(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:805
void GetDeviceCache(CacheT &cache, DeviceCacheT &device_cache)
Definition dgsem_cache_utilities.hpp:747
void BuildPerssonDeviceCache(CacheT &c, Prandtl::ModalBasis &modalBasis)
Definition dgsem_cache_utilities.hpp:848
void AssembleBoundaryFaceGeometryTerms(mfem::FiniteElementSpace *fes, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:337
void AssembleInteriorFaceGeometryTerms(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:498
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:84
void SetupRestrictions(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:68
void GetOperatorCache(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:19
void OutputCacheContents(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:926
void BuildBoundaryFaceToMarkerMap(mfem::ParMesh *pmesh, const std::vector< mfem::Array< int > > &bdr_marker_vector, CacheT *cache)
Definition dgsem_cache_utilities.hpp:282
mfem::real_t ReferenceAdvectionSpectralScale(const int order)
Definition StabilityEstimate.hpp:44
void ValidateAxisBoundaryGeometry(const CacheT &cache)
Definition dgsem_cache_utilities.hpp:841
void ComputeSubcellMetrics(mfem::FiniteElementSpace *fes, CacheT *cache)
Definition dgsem_cache_utilities.hpp:623
void BuildBoundaryFaceToBEMap(mfem::ParMesh *pmesh, mfem::Array< int > &face_to_be)
Definition dgsem_cache_utilities.hpp:319
mfem::real_t ReferenceBR1DiffusionSpectralScale(const int order)
Definition StabilityEstimate.hpp:63
void AssembleElementVolumeGeometricTerms(mfem::ElementTransformation &Tr, CacheT *cache)
Definition dgsem_cache_utilities.hpp:451
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