MeshFields
GPU accelerated mesh-based fields
MeshField.hpp
1 #ifndef MESHFIELD_MESHFIELD_HPP
2 #define MESHFIELD_MESHFIELD_HPP
3 
4 #include "KokkosController.hpp"
5 #include "MeshField_Element.hpp"
6 #include "MeshField_Fail.hpp"
7 #include "MeshField_For.hpp"
8 #include "MeshField_ShapeField.hpp"
9 #include "Omega_h_file.hpp" //move
10 #include "Omega_h_mesh.hpp" //move
11 #include "Omega_h_simplex.hpp" //move
12 
13 namespace {
14 
15 MeshField::MeshInfo getMeshInfo(Omega_h::Mesh &mesh) {
16  MeshField::MeshInfo meshInfo;
17  meshInfo.dim = mesh.dim();
18  meshInfo.numVtx = mesh.nverts();
19  if (mesh.dim() > 1)
20  meshInfo.numEdge = mesh.nedges();
21  if (mesh.family() == OMEGA_H_SIMPLEX) {
22  if (mesh.dim() > 1)
23  meshInfo.numTri = mesh.nfaces();
24  if (mesh.dim() == 3)
25  meshInfo.numTet = mesh.nregions();
26  } else { // hypercube
27  if (mesh.dim() > 1)
28  meshInfo.numQuad = mesh.nfaces();
29  if (mesh.dim() == 3)
30  meshInfo.numHex = mesh.nregions();
31  }
32  return meshInfo;
33 }
34 // we use functors because cannot use auto return type with KOKKOS_LAMBDAS
35 template <int dim, typename Field, typename Coords, typename CoordinateMap>
36 struct SetCoordFieldFunctor
37 {
38  SetCoordFieldFunctor(Field& field, Coords& coords, int meshDim,
39  const CoordinateMap& map) :
40  field_(field), coords_(coords), meshDim_(meshDim), map_(map) {}
41  KOKKOS_INLINE_FUNCTION
42  void operator()(const size_t vtx) const {
43  Kokkos::Array<MeshField::Real, dim> x{};
44  for (size_t d = 0; d < meshDim_; ++d)
45  x[d] = coords_[vtx * meshDim_ + d];
46  const auto mapped = map_(x);
47  for (size_t d = 0; d < meshDim_; ++d)
48  field_(vtx, 0, d, MeshField::Vertex) = mapped[d];
49  }
50  Field field_;
51  Coords coords_;
52  int meshDim_;
53  const CoordinateMap map_;
54 };
55 
56 template <int dim, typename Element, typename CoordinateMap>
57 struct InterpolateElementFunctor
58 {
59  InterpolateElementFunctor(const Element& element, const CoordinateMap& map)
60  : element_(element), map_(map) {}
61 
62  KOKKOS_INLINE_FUNCTION
63  auto operator()(const int ent, Kokkos::Array<MeshField::Real, dim> const& xi) const {
64  return map_(element_.getValue(ent, xi));
65  }
66 
67  const Element element_;
68  const CoordinateMap map_;
69 };
70 
71 } // anonymous namespace
72 
73 namespace MeshField {
74 
75 namespace Omegah {
76 struct LinearTriangleToVertexField {
77  Omega_h::LOs triVerts;
78  LinearTriangleToVertexField(Omega_h::Mesh &mesh)
79  : triVerts(mesh.ask_elem_verts()) {
80  if (mesh.dim() != 2 && mesh.family() != OMEGA_H_SIMPLEX) {
81  MeshField::fail(
82  "The mesh passed to %s must be 2D and simplex (triangles)\n",
83  __func__);
84  }
85  }
86 
87  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
88  getTopology() {
89  return {MeshField::Triangle};
90  }
91 
92  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
93  operator()(MeshField::LO triNodeIdx, MeshField::LO triCompIdx,
94  MeshField::LO tri, MeshField::Mesh_Topology topo) const {
95  assert(topo == MeshField::Triangle);
96  const auto triDim = 2;
97  const auto vtxDim = 0;
98  const auto ignored = -1;
99  const auto localVtxIdx =
100  (Omega_h::simplex_down_template(triDim, vtxDim, triNodeIdx, ignored) +
101  2) %
102  3;
103  const auto triToVtxDegree = Omega_h::simplex_degree(triDim, vtxDim);
104  const MeshField::LO vtx = triVerts[(tri * triToVtxDegree) + localVtxIdx];
105  return {0, triCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
106  }
107 };
108 struct LinearTetrahedronToVertexField {
109  Omega_h::LOs tetVerts;
110  LinearTetrahedronToVertexField(Omega_h::Mesh &mesh)
111  : tetVerts(mesh.ask_elem_verts()) {
112  if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
113  MeshField::fail(
114  "The mesh passed to %s must be 3D and simplex (tetrahedron)\n",
115  __func__);
116  }
117  }
118  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
119  getTopology() {
120  return {MeshField::Tetrahedron};
121  }
122 
123  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
124  operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
125  MeshField::LO tet, MeshField::Mesh_Topology topo) const {
126  assert(topo == MeshField::Tetrahedron);
127  const auto tetDim = 3;
128  const auto vtxDim = 0;
129  const auto ignored = -1;
130  const auto localVtxIdx =
131  (Omega_h::simplex_down_template(tetDim, vtxDim, tetNodeIdx, ignored) +
132  3) %
133  4;
134  const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
135  const MeshField::LO vtx = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
136  return {0, tetCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
137  }
138 };
141  Omega_h::LOs triVerts;
142  Omega_h::LOs triEdges;
143  QuadraticTriangleToField(Omega_h::Mesh &mesh)
144  : triVerts(mesh.ask_elem_verts()),
145  triEdges(mesh.ask_down(mesh.dim(), 1).ab2b) {
146  if (mesh.dim() != 2 && mesh.family() != OMEGA_H_SIMPLEX) {
147  MeshField::fail(
148  "The mesh passed to %s must be 2D and simplex (triangles)\n",
149  __func__);
150  }
151  }
152 
153  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
154  getTopology() {
155  return {MeshField::Triangle};
156  }
157 
158  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
159  operator()(MeshField::LO triNodeIdx, MeshField::LO triCompIdx,
160  MeshField::LO tri, MeshField::Mesh_Topology topo) const {
161  assert(topo == MeshField::Triangle);
162  // Omega_h has no concept of nodes so we can define the map from
163  // triNodeIdx to the dof holder index
164  const MeshField::LO triNode2DofHolder[6] = {
165  /*vertices*/ 0, 1, 2,
166  /*edges*/ 0, 1, 2};
167  const MeshField::Mesh_Topology triNode2DofHolderTopo[6] = {
168  /*vertices*/
169  MeshField::Vertex, MeshField::Vertex, MeshField::Vertex,
170  /*edges*/
171  MeshField::Edge, MeshField::Edge, MeshField::Edge};
172  const auto dofHolderIdx = triNode2DofHolder[triNodeIdx];
173  const auto dofHolderTopo = triNode2DofHolderTopo[triNodeIdx];
174  // Given the topo index and type find the Omega_h vertex or edge index that
175  // bounds the triangle
176  Omega_h::LO osh_ent;
177  if (dofHolderTopo == MeshField::Vertex) {
178  const auto triDim = 2;
179  const auto vtxDim = 0;
180  const auto ignored = -1;
181  const auto localVtxIdx = (Omega_h::simplex_down_template(
182  triDim, vtxDim, dofHolderIdx, ignored) +
183  2) %
184  3;
185  const auto triToVtxDegree = Omega_h::simplex_degree(triDim, vtxDim);
186  osh_ent = triVerts[(tri * triToVtxDegree) + localVtxIdx];
187  } else if (dofHolderTopo == MeshField::Edge) {
188  const auto triDim = 2;
189  const auto edgeDim = 1;
190  const auto triToEdgeDegree = Omega_h::simplex_degree(triDim, edgeDim);
191  // passing dofHolderIdx as Omega_h_simplex.hpp does not provide
192  // a function that maps a triangle and edge index to a 'canonical' edge
193  // index. This may need to be revisited...
194  osh_ent = triEdges[(tri * triToEdgeDegree) + (dofHolderIdx + 2) % 3];
195  } else {
196  assert(false);
197  }
198  return {0, triCompIdx, osh_ent, dofHolderTopo};
199  }
200 };
202 
204  Omega_h::LOs tetVerts;
205  Omega_h::LOs tetEdges;
206  QuadraticTetrahedronToField(Omega_h::Mesh &mesh)
207  : tetVerts(mesh.ask_elem_verts()),
208  tetEdges(mesh.ask_down(mesh.dim(), 1).ab2b) {
209  if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
210  MeshField::fail(
211  "The mesh passed to %s must be 3D and simplex (tetrahedron)",
212  __func__);
213  }
214  }
215 
216  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
217  getTopology() {
218  return {MeshField::Tetrahedron};
219  }
220 
221  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
222  operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
223  MeshField::LO tet, MeshField::Mesh_Topology topo) const {
224  assert(topo == MeshField::Tetrahedron);
225  const MeshField::LO tetNode2DofHolder[10] = {0, 1, 2, 3, 3, 4, 5, 0, 1, 2};
226  const MeshField::Mesh_Topology tetNode2DofHolderTopo[10] = {
227  MeshField::Vertex, MeshField::Vertex, MeshField::Vertex,
228  MeshField::Vertex, MeshField::Edge, MeshField::Edge,
229  MeshField::Edge, MeshField::Edge, MeshField::Edge,
230  MeshField::Edge};
231  const auto dofHolderIdx = tetNode2DofHolder[tetNodeIdx];
232  const auto dofHolderTopo = tetNode2DofHolderTopo[tetNodeIdx];
233  Omega_h::LO osh_ent;
234  if (dofHolderTopo == MeshField::Vertex) {
235  const auto tetDim = 3;
236  const auto vtxDim = 0;
237  const auto ignored = -1;
238  // cyclic rotation of the omegah vertex order to map to the meshfields order
239  // defined by the shape functions in MeshField_Shape.hpp
240  const auto localVtxIdx = (Omega_h::simplex_down_template(
241  tetDim, vtxDim, dofHolderIdx, ignored) +
242  3) %
243  4;
244  const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
245  osh_ent = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
246  } else if (dofHolderTopo == MeshField::Edge) {
247  const auto tetDim = 3;
248  const auto edgeDim = 1;
249  const auto tetToEdgeDegree = Omega_h::simplex_degree(tetDim, edgeDim);
250  osh_ent = tetEdges[(tet * tetToEdgeDegree) + dofHolderIdx];
251  } else {
252  assert(false);
253  }
254  return {0, tetCompIdx, osh_ent, dofHolderTopo};
255  }
256 };
257 
259 template <int ShapeOrder> auto getTriangleElement(Omega_h::Mesh &mesh) {
260  static_assert(ShapeOrder == 1 || ShapeOrder == 2);
261  if constexpr (ShapeOrder == 1) {
262  struct result {
264  LinearTriangleToVertexField map;
265  };
266  return result{MeshField::LinearTriangleShape(),
267  LinearTriangleToVertexField(mesh)};
268  } else if constexpr (ShapeOrder == 2) {
269  struct result {
271  QuadraticTriangleToField map;
272  };
273  return result{MeshField::QuadraticTriangleShape(),
274  QuadraticTriangleToField(mesh)};
275  }
276 }
278 template <int ShapeOrder> auto getTetrahedronElement(Omega_h::Mesh &mesh) {
279  static_assert(ShapeOrder == 1 || ShapeOrder == 2);
280  if constexpr (ShapeOrder == 1) {
281  struct result {
283  LinearTetrahedronToVertexField map;
284  };
285  return result{MeshField::LinearTetrahedronShape(),
286  LinearTetrahedronToVertexField(mesh)};
287  } else if constexpr (ShapeOrder == 2) {
288  struct result {
290  QuadraticTetrahedronToField map;
291  };
292  return result{MeshField::QuadraticTetrahedronShape(),
293  QuadraticTetrahedronToField(mesh)};
294  }
295 }
296 
297 } // namespace Omegah
298 
303  template <typename Point>
304  KOKKOS_INLINE_FUNCTION Point operator()(Point const &x) const {
305  return x;
306  }
307 };
308 
309 template <typename ExecutionSpace, size_t dim,
310  template <typename...> typename Controller =
311  MeshField::KokkosController>
312 class OmegahMeshField {
313 private:
314  Omega_h::Mesh &mesh;
315  const MeshField::MeshInfo meshInfo;
316  using CoordField =
317  decltype(MeshField::CreateCoordinateField<ExecutionSpace, Controller,
318  dim>(MeshField::MeshInfo()));
319  CoordField coordField;
320 
321 public:
322  OmegahMeshField(Omega_h::Mesh &mesh_in)
323  : mesh(mesh_in), meshInfo(getMeshInfo(mesh)),
324  coordField(CreateLagrangeCoordinateField<1>()) {
325  static_assert(dim == 1 || dim == 2 || dim == 3);
326  }
327 
328  template <typename DataType, size_t order, size_t numComp>
329  // Ordering of field indexing changed to 'entity, node, component'
330  auto CreateLagrangeField() const {
331  return MeshField::CreateLagrangeField<ExecutionSpace, Controller, DataType,
332  order, dim, numComp>(meshInfo);
333  }
334 
350  template <size_t order, typename CoordinateMap = IdentityCoordinateMap>
351  auto CreateLagrangeCoordinateField(
352  const CoordinateMap &map = CoordinateMap()) const {
353  auto fieldWithCtrlr = CreateLagrangeField<Real, order, dim>();
354  auto field = fieldWithCtrlr.field;
355  // note order 1 is called in the constructor to initialize the coordinate field
356  // it must be called before higher order coordinate fields are constructed
357  if constexpr (order == 1) {
358  const auto meshDim = meshInfo.dim;
359  const auto coords = mesh.coords();
360  MeshField::parallel_for(
361  ExecutionSpace(), {0}, {meshInfo.numVtx},
362  SetCoordFieldFunctor<dim, decltype(field), decltype(coords),
363  CoordinateMap>(field, coords, meshDim, map),
364  "setCoordField");
365  } else {
366  const auto linear = CreateLagrangeElement(coordField.field);
367  auto target = CreateLagrangeElement(field);
368  MeshField::setDofByInterpolation(
369  target,
370  InterpolateElementFunctor<dim, decltype(linear), CoordinateMap>(
371  linear, map));
372  }
373  return fieldWithCtrlr;
374  }
375 
376  auto getCoordField() { return coordField; }
377 
382  template <typename Field>
383  auto CreateLagrangeElement(const Field &field) const {
384  static_assert(dim == 2 || dim == 3,
385  "CreateLagrangeElement supports triangles and tetrahedra");
386  constexpr int order = Field::Order;
387  if constexpr (dim == 2) {
388  const auto [shp, map] = Omegah::getTriangleElement<order>(mesh);
389  return MeshField::FieldElement(meshInfo.numTri, field, shp, map);
390  } else {
391  const auto [shp, map] = Omegah::getTetrahedronElement<order>(mesh);
392  return MeshField::FieldElement(meshInfo.numTet, field, shp, map);
393  }
394  }
395 
396  // FIXME support 2d and 3d and fields with order>1
397  template <typename Field> void writeVtk(Field &field) const {
398  using FieldDataType = typename decltype(field.vtxField)::BaseType;
399  // HACK assumes there is a vertex field.. in the Field Mixin object
400  auto field_view = field.vtxField.serialize();
401  Omega_h::Write<FieldDataType> field_write(field_view);
402  mesh.add_tag(0, "field", 1, Omega_h::read(field_write), false,
403  Omega_h::ArrayType::VectorND);
404  Omega_h::vtk::write_parallel("foo.vtk", &mesh, mesh.dim());
405  }
406 
407  template <typename ViewType = Kokkos::View<MeshField::LO *>>
408  ViewType createOffsets(size_t numTri, size_t numPtsPerElem) const {
409  ViewType offsets("offsets", numTri + 1);
410  Kokkos::parallel_for(
411  "setOffsets", numTri,
412  KOKKOS_LAMBDA(int i) { offsets(i) = i * numPtsPerElem; });
413  Kokkos::deep_copy(Kokkos::subview(offsets, offsets.size() - 1),
414  numTri * numPtsPerElem);
415  return offsets;
416  }
417 
418  // evaluate a field at the specified local coordinate for each triangle
419  template <typename ViewType, typename ShapeField>
420  auto triangleLocalPointEval(const ViewType &localCoords, size_t NumPtsPerElem,
421  const ShapeField &field) const {
422  auto offsets = createOffsets(meshInfo.numTri, NumPtsPerElem);
423  auto eval = triangleLocalPointEval<ViewType, ShapeField>(localCoords,
424  offsets, field);
425  return eval;
426  }
427 
428  // evaluate a field at the specified local coordinates for each triangle
429  template <typename ViewType, typename ShapeField>
430  auto triangleLocalPointEval(const ViewType &localCoords,
431  Kokkos::View<LO *> offsets,
432  const ShapeField &field) const {
433  const auto MeshDim = 2;
434  if (mesh.dim() != MeshDim) {
435  MeshField::fail("input mesh must be 2d\n");
436  }
437  const auto ShapeOrder = ShapeField::Order;
438  if (ShapeOrder != 1 && ShapeOrder != 2) {
439  MeshField::fail("input field order must be 1 or 2\n");
440  }
441 
442  const auto [shp, map] = Omegah::getTriangleElement<ShapeOrder>(mesh);
443 
444  MeshField::FieldElement<ShapeField, decltype(shp), decltype(map)> f(
445  meshInfo.numTri, field, shp, map);
446  auto eval = MeshField::evaluate(f, localCoords, offsets);
447  return eval;
448  }
449 
450  template <typename ViewType, typename ShapeField>
451  auto tetrahedronLocalPointEval(const ViewType &localCoords,
452  size_t NumPtsPerElem,
453  const ShapeField &field) const {
454  auto offsets = createOffsets(meshInfo.numTet, NumPtsPerElem);
455  auto eval = tetrahedronLocalPointEval(localCoords, offsets, field);
456  return eval;
457  }
458 
459  template <typename ViewType, typename ShapeField>
460  auto tetrahedronLocalPointEval(const ViewType &localCoords,
461  Kokkos::View<LO *> offsets,
462  const ShapeField &field) const {
463  const auto MeshDim = 3;
464  if (mesh.dim() != MeshDim) {
465  MeshField::fail("input mesh must be 3d\n");
466  }
467  const auto ShapeOrder = ShapeField::Order;
468  if (ShapeOrder != 1 && ShapeOrder != 2) {
469  MeshField::fail("input field order must be 1 or 2\n");
470  }
471  const auto [shp, map] = Omegah::getTetrahedronElement<ShapeOrder>(mesh);
472  MeshField::FieldElement<ShapeField, decltype(shp), decltype(map)> f(
473  meshInfo.numTet, field, shp, map);
474  auto eval = MeshField::evaluate(f, localCoords, offsets);
475  return eval;
476  }
477 };
478 
479 } // namespace MeshField
480 
481 #endif
Supports mapping between mesh (i.e., Omega_h) ordering and MeshFields ordering.
Supports the evaluation of a field, and other per-element operations, given the definition of the ele...
the CoordinateMap that leaves the mesh geometry unchanged
Definition: MeshField.hpp:302
Linear (P1) shape functions for 2D triangular elements.
On-process mesh metadata.
Quadratic (P2) shape functions for 3D tetrahedral elements.
Quadratic (P2) shape functions for 2D triangular elements.