Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
element_restriction.cpp
1 #include "serac/numerics/functional/element_restriction.hpp"
2 
3 #include "mfem.hpp"
4 
5 #include "serac/numerics/functional/geometry.hpp"
6 
7 std::vector<std::vector<int> > lexicographic_permutations(int p)
8 {
9  // p == 0 is admissible for L2 spaces, but lexicographic permutations
10  // aren't needed in that corner case
11  if (p == 0) {
12  return {};
13  }
14 
15  std::vector<std::vector<int> > output(mfem::Geometry::Type::NUM_GEOMETRIES);
16 
17  {
18  auto P = mfem::H1_SegmentElement(p).GetLexicographicOrdering();
19  std::vector<int> native_to_lex(uint32_t(P.Size()));
20  for (int i = 0; i < P.Size(); i++) {
21  native_to_lex[uint32_t(i)] = P[i];
22  }
23  output[mfem::Geometry::Type::SEGMENT] = native_to_lex;
24  }
25 
26  {
27  auto P = mfem::H1_TriangleElement(p).GetLexicographicOrdering();
28  std::vector<int> native_to_lex(uint32_t(P.Size()));
29  for (int i = 0; i < P.Size(); i++) {
30  native_to_lex[uint32_t(i)] = P[i];
31  }
32  output[mfem::Geometry::Type::TRIANGLE] = native_to_lex;
33  }
34 
35  {
36  auto P = mfem::H1_QuadrilateralElement(p).GetLexicographicOrdering();
37  std::vector<int> native_to_lex(uint32_t(P.Size()));
38  for (int i = 0; i < P.Size(); i++) {
39  native_to_lex[uint32_t(i)] = P[i];
40  }
41  output[mfem::Geometry::Type::SQUARE] = native_to_lex;
42  }
43 
44  {
45  auto P = mfem::H1_TetrahedronElement(p).GetLexicographicOrdering();
46  std::vector<int> native_to_lex(uint32_t(P.Size()));
47  for (int i = 0; i < P.Size(); i++) {
48  native_to_lex[uint32_t(i)] = P[i];
49  }
50  output[mfem::Geometry::Type::TETRAHEDRON] = native_to_lex;
51  }
52 
53  {
54  auto P = mfem::H1_HexahedronElement(p).GetLexicographicOrdering();
55  std::vector<int> native_to_lex(uint32_t(P.Size()));
56  for (int i = 0; i < P.Size(); i++) {
57  native_to_lex[uint32_t(i)] = P[i];
58  }
59  output[mfem::Geometry::Type::CUBE] = native_to_lex;
60  }
61 
62  // other geometries are not defined, as they are not currently used
63 
64  return output;
65 }
66 
67 Array2D<int> face_permutations(mfem::Geometry::Type geom, int p)
68 {
69  if (geom == mfem::Geometry::Type::SEGMENT) {
70  Array2D<int> output(2, p + 1);
71  for (int i = 0; i <= p; i++) {
72  output(0, i) = i;
73  output(1, i) = p - i;
74  }
75  return output;
76  }
77 
78  if (geom == mfem::Geometry::Type::TRIANGLE) {
79  // v = {{0, 0}, {1, 0}, {0, 1}};
80  // f = Transpose[{{0, 1, 2}, {1, 0, 2}, {2, 0, 1}, {2, 1, 0}, {1, 2, 0}, {0, 2, 1}} + 1];
81  // p v[[f[[1]]]] + (v[[f[[2]]]] - v[[f[[1]]]]) i + (v[[f[[3]]]] - v[[f[[1]]]]) j
82  //
83  // {{i, j}, {p-i-j, j}, {j, p-i-j}, {i, p-i-j}, {p-i-j, i}, {j, i}}
84  Array2D<int> output(6, (p + 1) * (p + 2) / 2);
85  auto tri_id = [p](int x, int y) { return x + ((3 + 2 * p - y) * y) / 2; };
86  for (int j = 0; j <= p; j++) {
87  for (int i = 0; i <= p - j; i++) {
88  int id = tri_id(i, j);
89  output(0, tri_id(i, j)) = id;
90  output(1, tri_id(p - i - j, j)) = id;
91  output(2, tri_id(j, p - i - j)) = id;
92  output(3, tri_id(i, p - i - j)) = id;
93  output(4, tri_id(p - i - j, i)) = id;
94  output(5, tri_id(j, i)) = id;
95  }
96  }
97  return output;
98  }
99 
100  if (geom == mfem::Geometry::Type::SQUARE) {
101  // v = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
102  // f = Transpose[{{0, 1, 2, 3}, {0, 3, 2, 1}, {1, 2, 3, 0}, {1, 0, 3, 2},
103  // {2, 3, 0, 1}, {2, 1, 0, 3}, {3, 0, 1, 2}, {3, 2, 1, 0}} + 1];
104  // p v[[f[[1]]]] + (v[[f[[2]]]] - v[[f[[1]]]]) i + (v[[f[[4]]]] - v[[f[[1]]]]) j
105  //
106  // {{i,j}, {j,i}, {p-j,i}, {p-i,j}, {p-i, p-j}, {p-j, p-i}, {j, p-i}, {i, p-j}}
107  Array2D<int> output(8, (p + 1) * (p + 1));
108  auto quad_id = [p](int x, int y) { return ((p + 1) * y) + x; };
109  for (int j = 0; j <= p; j++) {
110  for (int i = 0; i <= p; i++) {
111  int id = quad_id(i, j);
112  output(0, quad_id(i, j)) = id;
113  output(1, quad_id(j, i)) = id;
114  output(2, quad_id(p - j, i)) = id;
115  output(3, quad_id(p - i, j)) = id;
116  output(4, quad_id(p - i, p - j)) = id;
117  output(5, quad_id(p - j, p - i)) = id;
118  output(6, quad_id(j, p - i)) = id;
119  output(7, quad_id(i, p - j)) = id;
120  }
121  }
122  return output;
123  }
124 
125  std::cout << "face_permutation(): unsupported geometry type" << std::endl;
126  exit(1);
127 }
128 
129 std::vector<Array2D<int> > geom_local_face_dofs(int p)
130 {
131  // FullSimplify[InterpolatingPolynomial[{
132  // {{0, 2}, (p + 1) + p},
133  // {{0, 1}, p + 1}, {{1, 1}, p + 2},
134  // {{0, 0}, 0}, {{1, 0}, 1}, {{2, 0}, 2}
135  // }, {x, y}]]
136  //
137  // x + 1/2 (3 + 2 p - y) y
138  auto tri_id = [p](int x, int y) { return x + ((3 + 2 * p - y) * y) / 2; };
139 
140  // FullSimplify[InterpolatingPolynomial[{
141  // {{0, 3}, ((p - 1) (p) + (p) (p + 1) + (p + 1) (p + 2))/2},
142  // {{0, 2}, ((p) (p + 1) + (p + 1) (p + 2))/2}, {{1, 2}, p - 1 + ((p) (p + 1) + (p + 1) (p + 2))/2},
143  // {{0, 1}, (p + 1) (p + 2)/2}, {{1, 1}, p + (p + 1) (p + 2)/2}, {{2, 1}, 2 p - 1 + (p + 1) (p + 2)/2},
144  // {{0, 0}, 0}, {{1, 0}, p + 1}, {{2, 0}, 2 p + 1}, {{3, 0}, 3 p}
145  // }, {y, z}]] + x
146  //
147  // x + (z (11 + p (12 + 3 p) - 6 y + z (z - 6 - 3 p)) - 3 y (y - 2 p - 3))/6
148  auto tet_id = [p](int x, int y, int z) {
149  return x + (z * (11 + p * (12 + 3 * p) - 6 * y + z * (z - 6 - 3 * p)) - 3 * y * (y - 2 * p - 3)) / 6;
150  };
151 
152  auto quad_id = [p](int x, int y) { return ((p + 1) * y) + x; };
153 
154  auto hex_id = [p](int x, int y, int z) { return (p + 1) * ((p + 1) * z + y) + x; };
155 
156  std::vector<Array2D<int> > output(mfem::Geometry::Type::NUM_GEOMETRIES);
157 
158  Array2D<int> tris(3, p + 1);
159  for (int k = 0; k <= p; k++) {
160  tris(0, k) = tri_id(k, 0);
161  tris(1, k) = tri_id(p - k, k);
162  tris(2, k) = tri_id(0, p - k);
163  }
164  output[mfem::Geometry::Type::TRIANGLE] = tris;
165 
166  Array2D<int> quads(4, p + 1);
167  for (int k = 0; k <= p; k++) {
168  quads(0, k) = quad_id(k, 0);
169  quads(1, k) = quad_id(p, k);
170  quads(2, k) = quad_id(p - k, p);
171  quads(3, k) = quad_id(0, p - k);
172  }
173  output[mfem::Geometry::Type::SQUARE] = quads;
174 
175  // v = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
176  // f = Transpose[{{1, 2, 3}, {0, 3, 2}, {0, 1, 3}, {0, 2, 1}} + 1];
177  // p v[[f[[1]]]] + (v[[f[[2]]]] - v[[f[[1]]]]) j + (v[[f[[3]]]] - v[[f[[1]]]]) k
178  //
179  // {{p-j-k, j, k}, {0, k, j}, {j, 0, k}, {k, j, 0}}
180  Array2D<int> tets(4, (p + 1) * (p + 2) / 2);
181  for (int k = 0; k <= p; k++) {
182  for (int j = 0; j <= p - k; j++) {
183  int id = tri_id(j, k);
184  tets(0, id) = tet_id(p - j - k, j, k);
185  tets(1, id) = tet_id(0, k, j);
186  tets(2, id) = tet_id(j, 0, k);
187  tets(3, id) = tet_id(k, j, 0);
188  }
189  }
190  output[mfem::Geometry::Type::TETRAHEDRON] = tets;
191 
192  // v = {{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0},
193  // {0, 0, 1}, {1, 0, 1}, {1, 1, 1}, {0, 1, 1}};
194  // f = Transpose[{{3, 2, 1, 0}, {0, 1, 5, 4}, {1, 2, 6, 5},
195  // {2, 3, 7, 6}, {3, 0, 4, 7}, {4, 5, 6, 7}} + 1];
196  // p v[[f[[1]]]] + (v[[f[[2]]]] - v[[f[[1]]]]) j + (v[[f[[4]]]] - v[[f[[1]]]]) k
197  //
198  // {{j, p-k, 0}, {j, 0, k}, {p, j, k}, {p-j, p, k}, {0, p-j, k}, {j, k, p}}
199  Array2D<int> hexes(6, (p + 1) * (p + 1));
200  for (int k = 0; k <= p; k++) {
201  for (int j = 0; j <= p; j++) {
202  int id = quad_id(j, k);
203  hexes(0, id) = hex_id(j, p - k, 0);
204  hexes(1, id) = hex_id(j, 0, k);
205  hexes(2, id) = hex_id(p, j, k);
206  hexes(3, id) = hex_id(p - j, p, k);
207  hexes(4, id) = hex_id(0, p - j, k);
208  hexes(5, id) = hex_id(j, k, p);
209  }
210  }
211  output[mfem::Geometry::Type::CUBE] = hexes;
212 
213  return output;
214 }
215 
216 axom::Array<DoF, 2, axom::MemorySpace::Host> GetElementRestriction(const mfem::FiniteElementSpace* fes,
217  mfem::Geometry::Type geom)
218 {
219  std::vector<DoF> elem_dofs{};
220  mfem::Mesh* mesh = fes->GetMesh();
221 
222  // note: this assumes that all the elements are the same polynomial order
223  int p = fes->GetElementOrder(0);
224  std::vector<std::vector<int> > lex_perm = lexicographic_permutations(p);
225 
226  uint64_t n = 0;
227 
228  for (int elem = 0; elem < fes->GetNE(); elem++) {
229  // discard elements with the wrong geometry
230  if (mesh->GetElementGeometry(elem) != geom) continue;
231 
232  mfem::Array<int> dofs;
233 
234  [[maybe_unused]] auto* dof_transformation = fes->GetElementDofs(elem, dofs);
235 
236  // mfem returns the H1 dofs in "native" order, so we need
237  // to apply the native-to-lexicographic permutation
238  if (isH1(*fes)) {
239  for (int k = 0; k < dofs.Size(); k++) {
240  elem_dofs.push_back({uint64_t(dofs[lex_perm[uint32_t(geom)][uint32_t(k)]])});
241  }
242  }
243 
244  // the dofs mfem returns for Hcurl include information about
245  // dof orientation, but not for triangle faces on 3D elements.
246  // So, we need to manually
247  if (isHcurl(*fes)) {
248  // TODO
249  // TODO
250  // TODO
251  uint64_t sign = 1;
252  uint64_t orientation = 0;
253  for (int k = 0; k < dofs.Size(); k++) {
254  elem_dofs.push_back({uint64_t(dofs[k]), sign, orientation});
255  }
256  }
257 
258  // mfem returns DG dofs in lexicographic order already
259  // so no permutation is required here
260  if (isDG(*fes)) {
261  for (int k = 0; k < dofs.Size(); k++) {
262  elem_dofs.push_back({uint64_t(dofs[k])});
263  }
264  }
265 
266  n++;
267  }
268 
269  if (n == 0) {
270  return axom::Array<DoF, 2, axom::MemorySpace::Host>{};
271  } else {
272  uint64_t dofs_per_elem = elem_dofs.size() / n;
273  axom::Array<DoF, 2, axom::MemorySpace::Host> output(n, dofs_per_elem);
274  std::memcpy(output.data(), elem_dofs.data(), sizeof(DoF) * n * dofs_per_elem);
275  return output;
276  }
277 }
278 
279 axom::Array<DoF, 2, axom::MemorySpace::Host> GetFaceDofs(const mfem::FiniteElementSpace* fes,
280  mfem::Geometry::Type face_geom, FaceType type)
281 {
282  std::vector<DoF> face_dofs;
283  mfem::Mesh* mesh = fes->GetMesh();
284  mfem::Table* face_to_elem = mesh->GetFaceToElementTable();
285 
286  // note: this assumes that all the elements are the same polynomial order
287  int p = fes->GetElementOrder(0);
288  Array2D<int> face_perm = face_permutations(face_geom, p);
289  std::vector<Array2D<int> > local_face_dofs = geom_local_face_dofs(p);
290  std::vector<std::vector<int> > lex_perm = lexicographic_permutations(p);
291 
292  uint64_t n = 0;
293 
294  for (int f = 0; f < fes->GetNF(); f++) {
295  auto faceinfo = mesh->GetFaceInformation(f);
296 
297  // discard faces with the wrong geometry or type
298  if (mesh->GetFaceGeometry(f) != face_geom) continue;
299  if (faceinfo.IsInterior() && type == FaceType::BOUNDARY) continue;
300  if (faceinfo.IsBoundary() && type == FaceType::INTERIOR) continue;
301 
302  // mfem doesn't provide this connectivity info for DG spaces directly,
303  // so we have to get at it indirectly in several steps:
304  if (isDG(*fes)) {
305  // 1. find the element(s) that this face belongs to
306  mfem::Array<int> elem_ids;
307  face_to_elem->GetRow(f, elem_ids);
308 
309  for (auto elem : elem_ids) {
310  // 2a. get the list of faces (and their orientations) that belong to that element ...
311  mfem::Array<int> elem_side_ids, orientations;
312  if (mesh->Dimension() == 2) {
313  mesh->GetElementEdges(elem, elem_side_ids, orientations);
314 
315  // mfem returns {-1, 1} for edge orientations,
316  // but {0, 1, ... , n} for face orientations.
317  // Here, we renumber the edge orientations to
318  // {0 (no permutation), 1 (reversed)} so the values can be
319  // consistently used as indices into a permutation table
320  for (auto& o : orientations) {
321  o = (o == -1) ? 1 : 0;
322  }
323 
324  } else {
325  mesh->GetElementFaces(elem, elem_side_ids, orientations);
326  }
327 
328  // 2b. ... and find `i` such that `elem_side_ids[i] == f`
329  int i;
330  for (i = 0; i < elem_side_ids.Size(); i++) {
331  if (elem_side_ids[i] == f) break;
332  }
333 
334  // 3. get the dofs for the entire element
335  mfem::Array<int> elem_dof_ids;
336  fes->GetElementDofs(elem, elem_dof_ids);
337 
338  mfem::Geometry::Type elem_geom = mesh->GetElementGeometry(elem);
339 
340  // mfem uses different conventions for boundary element orientations in 2D and 3D.
341  // In 2D, mfem's official edge orientations on the boundary will always be a mix of
342  // CW and CCW, so we have to discard mfem's orientation information in order
343  // to get a consistent winding.
344  //
345  // In 3D, mfem does use a consistently CCW winding for boundary faces (I think).
346  int orientation = (mesh->Dimension() == 2 && type == FaceType::BOUNDARY) ? 0 : orientations[i];
347 
348  // 4. extract only the dofs that correspond to side `i`
349  for (auto k : face_perm(orientation)) {
350  face_dofs.push_back(uint64_t(elem_dof_ids[local_face_dofs[uint32_t(elem_geom)](i, k)]));
351  }
352 
353  // boundary faces only belong to 1 element, so we exit early
354  if (type == FaceType::BOUNDARY) break;
355  }
356 
357  // H1 and Hcurl spaces are more straight-forward, since
358  // we can use FiniteElementSpace::GetFaceDofs() directly
359  } else {
360  mfem::Array<int> dofs;
361 
362  fes->GetFaceDofs(f, dofs);
363 
364  if (isHcurl(*fes)) {
365  for (int k = 0; k < dofs.Size(); k++) {
366  face_dofs.push_back(uint64_t(dofs[k]));
367  }
368  } else {
369  for (int k = 0; k < dofs.Size(); k++) {
370  face_dofs.push_back(uint64_t(dofs[lex_perm[uint32_t(face_geom)][uint32_t(k)]]));
371  }
372  }
373  }
374 
375  n++;
376  }
377 
378  delete face_to_elem;
379 
380  if (n == 0) {
381  return axom::Array<DoF, 2, axom::MemorySpace::Host>{};
382  } else {
383  uint64_t dofs_per_face = face_dofs.size() / n;
384  axom::Array<DoF, 2, axom::MemorySpace::Host> output(n, dofs_per_face);
385  std::memcpy(output.data(), face_dofs.data(), sizeof(DoF) * n * dofs_per_face);
386  return output;
387  }
388 }
389 
390 namespace serac {
391 
392 ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type elem_geom)
393 {
394  dof_info = GetElementRestriction(fes, elem_geom);
395 
396  ordering = fes->GetOrdering();
397 
398  lsize = uint64_t(fes->GetVSize());
399  components = uint64_t(fes->GetVDim());
401  num_elements = uint64_t(dof_info.shape()[0]);
402  nodes_per_elem = uint64_t(dof_info.shape()[1]);
404 }
405 
406 ElementRestriction::ElementRestriction(const mfem::FiniteElementSpace* fes, mfem::Geometry::Type face_geom,
407  FaceType type)
408 {
409  dof_info = GetFaceDofs(fes, face_geom, type);
410 
411  ordering = fes->GetOrdering();
412 
413  lsize = uint64_t(fes->GetVSize());
414  components = uint64_t(fes->GetVDim());
416  num_elements = uint64_t(dof_info.shape()[0]);
417  nodes_per_elem = uint64_t(dof_info.shape()[1]);
419 }
420 
421 uint64_t ElementRestriction::ESize() const { return esize; }
422 
423 uint64_t ElementRestriction::LSize() const { return lsize; }
424 
425 DoF ElementRestriction::GetVDof(DoF node, uint64_t component) const
426 {
427  if (ordering == mfem::Ordering::Type::byNODES) {
428  return DoF{component * num_nodes + node.index(), (node.sign() == 1) ? 0ull : 1ull, node.orientation()};
429  } else {
430  return DoF{node.index() * components + component, (node.sign() == 1) ? 0ull : 1ull, node.orientation()};
431  }
432 }
433 
434 void ElementRestriction::GetElementVDofs(int i, std::vector<DoF>& vdofs) const
435 {
436  for (uint64_t c = 0; c < components; c++) {
437  for (uint64_t j = 0; j < nodes_per_elem; j++) {
438  vdofs[c * nodes_per_elem + j] = GetVDof(dof_info(i, j), c);
439  }
440  }
441 }
442 
443 void ElementRestriction::Gather(const mfem::Vector& L_vector, mfem::Vector& E_vector) const
444 {
445  for (uint64_t i = 0; i < num_elements; i++) {
446  for (uint64_t c = 0; c < components; c++) {
447  for (uint64_t j = 0; j < nodes_per_elem; j++) {
448  uint64_t E_id = (i * components + c) * nodes_per_elem + j;
449  uint64_t L_id = GetVDof(dof_info(i, j), c).index();
450  E_vector[int(E_id)] = L_vector[int(L_id)];
451  }
452  }
453  }
454 }
455 
456 void ElementRestriction::ScatterAdd(const mfem::Vector& E_vector, mfem::Vector& L_vector) const
457 {
458  for (uint64_t i = 0; i < num_elements; i++) {
459  for (uint64_t c = 0; c < components; c++) {
460  for (uint64_t j = 0; j < nodes_per_elem; j++) {
461  uint64_t E_id = (i * components + c) * nodes_per_elem + j;
462  uint64_t L_id = GetVDof(dof_info(i, j), c).index();
463  L_vector[int(L_id)] += E_vector[int(E_id)];
464  }
465  }
466  }
467 }
468 
470 
471 BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes)
472 {
473  int dim = fes->GetMesh()->Dimension();
474 
475  if (dim == 2) {
476  for (auto geom : {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}) {
477  restrictions[geom] = ElementRestriction(fes, geom);
478  }
479  }
480 
481  if (dim == 3) {
482  for (auto geom : {mfem::Geometry::TETRAHEDRON, mfem::Geometry::CUBE}) {
483  restrictions[geom] = ElementRestriction(fes, geom);
484  }
485  }
486 }
487 
488 BlockElementRestriction::BlockElementRestriction(const mfem::FiniteElementSpace* fes, FaceType type)
489 {
490  int dim = fes->GetMesh()->Dimension();
491 
492  if (dim == 2) {
493  restrictions[mfem::Geometry::SEGMENT] = ElementRestriction(fes, mfem::Geometry::SEGMENT, type);
494  }
495 
496  if (dim == 3) {
497  for (auto geom : {mfem::Geometry::TRIANGLE, mfem::Geometry::SQUARE}) {
498  restrictions[geom] = ElementRestriction(fes, geom, type);
499  }
500  }
501 }
502 
503 uint64_t BlockElementRestriction::ESize() const { return (*restrictions.begin()).second.ESize(); }
504 
505 uint64_t BlockElementRestriction::LSize() const { return (*restrictions.begin()).second.LSize(); }
506 
507 mfem::Array<int> BlockElementRestriction::bOffsets() const
508 {
509  mfem::Array<int> offsets(mfem::Geometry::NUM_GEOMETRIES + 1);
510 
511  offsets[0] = 0;
512  for (int i = 0; i < mfem::Geometry::NUM_GEOMETRIES; i++) {
513  auto g = mfem::Geometry::Type(i);
514  if (restrictions.count(g) > 0) {
515  offsets[g + 1] = offsets[g] + int(restrictions.at(g).ESize());
516  } else {
517  offsets[g + 1] = offsets[g];
518  }
519  // std::cout << g << " " << offsets[g+1] << std::endl;
520  }
521  return offsets;
522 };
523 
524 void BlockElementRestriction::Gather(const mfem::Vector& L_vector, mfem::BlockVector& E_block_vector) const
525 {
526  for (auto [geom, restriction] : restrictions) {
527  restriction.Gather(L_vector, E_block_vector.GetBlock(geom));
528  }
529 }
530 
531 void BlockElementRestriction::ScatterAdd(const mfem::BlockVector& E_block_vector, mfem::Vector& L_vector) const
532 {
533  for (auto [geom, restriction] : restrictions) {
534  restriction.ScatterAdd(E_block_vector.GetBlock(geom), L_vector);
535  }
536 }
537 
538 } // namespace serac
Accelerator functionality.
Definition: serac.cpp:38
bool isHcurl(const mfem::ParFiniteElementSpace &fes)
return whether or not the underlying function space is Hcurl or not
constexpr SERAC_HOST_DEVICE auto type(const tuple< T... > &values)
a function intended to be used for extracting the ith type from a tuple.
Definition: tuple.hpp:274
a 2D array
a struct of metadata (index, sign, orientation) associated with a degree of freedom
int sign() const
get the sign field of this DoF
uint64_t index() const
get the index field of this DoF
uint64_t orientation() const
get the orientation field of this DoF
BlockElementRestriction()
default ctor leaves this object uninitialized
mfem::Array< int > bOffsets() const
block offsets used when constructing mfem::HypreParVectors
void ScatterAdd(const mfem::BlockVector &E_block_vector, mfem::Vector &L_vector) const
"E->L" in mfem parlance, each element scatter-adds its local vector into the appropriate place in the...
uint64_t ESize() const
the size of the "E-vector" associated with this restriction operator
std::map< mfem::Geometry::Type, ElementRestriction > restrictions
the individual ElementRestriction operators for each element geometry
void Gather(const mfem::Vector &L_vector, mfem::BlockVector &E_block_vector) const
"L->E" in mfem parlance, each element gathers the values that belong to it, and stores them in the "E...
uint64_t LSize() const
the size of the "L-vector" associated with this restriction operator
mfem::Ordering::Type ordering
whether the underlying dofs are arranged "byNodes" or "byVDim"
uint64_t num_nodes
the total number of nodes in the mesh
ElementRestriction()
default ctor leaves this object uninitialized
uint64_t nodes_per_elem
the number of nodes in each element
uint64_t lsize
the size of the "L-vector"
void GetElementVDofs(int i, std::vector< DoF > &dofs) const
Get a list of DoFs for element i
axom::Array< DoF, 2, axom::MemorySpace::Host > dof_info
a 2D array (num_elements-by-nodes_per_elem) holding the dof info extracted from the finite element sp...
void ScatterAdd(const mfem::Vector &E_vector, mfem::Vector &L_vector) const
"E->L" in mfem parlance, each element scatter-adds its local vector into the appropriate place in the...
uint64_t esize
the size of the "E-vector"
uint64_t num_elements
the number of elements of the given geometry
uint64_t components
the number of components at each node
void Gather(const mfem::Vector &L_vector, mfem::Vector &E_vector) const
"L->E" in mfem parlance, each element gathers the values that belong to it, and stores them in the "E...
uint64_t LSize() const
the size of the "L-vector" associated with this restriction operator
uint64_t ESize() const
the size of the "E-vector" associated with this restriction operator
DoF GetVDof(DoF node, uint64_t component) const
get the dof information for a given node / component