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 
35 template <typename ExecutionSpace, size_t dim,
36  template <typename...>
37  typename Controller = MeshField::KokkosController>
38 decltype(MeshField::CreateCoordinateField<ExecutionSpace, Controller, dim>(
40 createCoordinateField(const MeshField::MeshInfo &mesh_info,
41  Omega_h::Reals coords) {
42  const auto meshDim = mesh_info.dim;
43  auto coordFieldWithCtrlr =
44  MeshField::CreateCoordinateField<ExecutionSpace, Controller, dim>(
45  mesh_info);
46  auto coordField = coordFieldWithCtrlr.field;
47  auto setCoordField = KOKKOS_LAMBDA(const int &i) {
48  coordField(i, 0, 0, MeshField::Vertex) = coords[i * meshDim];
49  coordField(i, 0, 1, MeshField::Vertex) = coords[i * meshDim + 1];
50  if constexpr (dim == 3) {
51  coordField(i, 0, 2, MeshField::Vertex) = coords[i * meshDim + 2];
52  }
53  };
54  MeshField::parallel_for(ExecutionSpace(), {0}, {mesh_info.numVtx},
55  setCoordField, "setCoordField");
56  return coordFieldWithCtrlr;
57 }
58 
59 } // anonymous namespace
60 
61 namespace MeshField {
62 
63 namespace Omegah {
64 struct LinearTriangleToVertexField {
65  Omega_h::LOs triVerts;
66  LinearTriangleToVertexField(Omega_h::Mesh &mesh)
67  : triVerts(mesh.ask_elem_verts()) {
68  if (mesh.dim() != 2 && mesh.family() != OMEGA_H_SIMPLEX) {
69  MeshField::fail(
70  "The mesh passed to %s must be 2D and simplex (triangles)\n",
71  __func__);
72  }
73  }
74 
75  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
76  getTopology() {
77  return {MeshField::Triangle};
78  }
79 
80  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
81  operator()(MeshField::LO triNodeIdx, MeshField::LO triCompIdx,
82  MeshField::LO tri, MeshField::Mesh_Topology topo) const {
83  assert(topo == MeshField::Triangle);
84  const auto triDim = 2;
85  const auto vtxDim = 0;
86  const auto ignored = -1;
87  const auto localVtxIdx =
88  (Omega_h::simplex_down_template(triDim, vtxDim, triNodeIdx, ignored) +
89  2) %
90  3;
91  const auto triToVtxDegree = Omega_h::simplex_degree(triDim, vtxDim);
92  const MeshField::LO vtx = triVerts[(tri * triToVtxDegree) + localVtxIdx];
93  return {0, triCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
94  }
95 };
96 struct LinearTetrahedronToVertexField {
97  Omega_h::LOs tetVerts;
98  LinearTetrahedronToVertexField(Omega_h::Mesh &mesh)
99  : tetVerts(mesh.ask_elem_verts()) {
100  if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
101  MeshField::fail(
102  "The mesh passed to %s must be 3D and simplex (tetrahedron)\n",
103  __func__);
104  }
105  }
106  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
107  getTopology() {
108  return {MeshField::Tetrahedron};
109  }
110 
111  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
112  operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
113  MeshField::LO tet, MeshField::Mesh_Topology topo) const {
114  assert(topo == MeshField::Tetrahedron);
115  const auto tetDim = 3;
116  const auto vtxDim = 0;
117  const auto ignored = -1;
118  const auto localVtxIdx =
119  (Omega_h::simplex_down_template(tetDim, vtxDim, tetNodeIdx, ignored) +
120  3) %
121  4;
122  const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
123  const MeshField::LO vtx = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
124  return {0, tetCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
125  }
126 };
129  Omega_h::LOs triVerts;
130  Omega_h::LOs triEdges;
131  QuadraticTriangleToField(Omega_h::Mesh &mesh)
132  : triVerts(mesh.ask_elem_verts()),
133  triEdges(mesh.ask_down(mesh.dim(), 1).ab2b) {
134  if (mesh.dim() != 2 && mesh.family() != OMEGA_H_SIMPLEX) {
135  MeshField::fail(
136  "The mesh passed to %s must be 2D and simplex (triangles)\n",
137  __func__);
138  }
139  }
140 
141  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
142  getTopology() {
143  return {MeshField::Triangle};
144  }
145 
146  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
147  operator()(MeshField::LO triNodeIdx, MeshField::LO triCompIdx,
148  MeshField::LO tri, MeshField::Mesh_Topology topo) const {
149  assert(topo == MeshField::Triangle);
150  // Omega_h has no concept of nodes so we can define the map from
151  // triNodeIdx to the dof holder index
152  const MeshField::LO triNode2DofHolder[6] = {
153  /*vertices*/ 0, 1, 2,
154  /*edges*/ 0, 1, 2};
155  const MeshField::Mesh_Topology triNode2DofHolderTopo[6] = {
156  /*vertices*/
157  MeshField::Vertex, MeshField::Vertex, MeshField::Vertex,
158  /*edges*/
159  MeshField::Edge, MeshField::Edge, MeshField::Edge};
160  const auto dofHolderIdx = triNode2DofHolder[triNodeIdx];
161  const auto dofHolderTopo = triNode2DofHolderTopo[triNodeIdx];
162  // Given the topo index and type find the Omega_h vertex or edge index that
163  // bounds the triangle
164  Omega_h::LO osh_ent;
165  if (dofHolderTopo == MeshField::Vertex) {
166  const auto triDim = 2;
167  const auto vtxDim = 0;
168  const auto ignored = -1;
169  const auto localVtxIdx = (Omega_h::simplex_down_template(
170  triDim, vtxDim, dofHolderIdx, ignored) +
171  2) %
172  3;
173  const auto triToVtxDegree = Omega_h::simplex_degree(triDim, vtxDim);
174  osh_ent = triVerts[(tri * triToVtxDegree) + localVtxIdx];
175  } else if (dofHolderTopo == MeshField::Edge) {
176  const auto triDim = 2;
177  const auto edgeDim = 1;
178  const auto triToEdgeDegree = Omega_h::simplex_degree(triDim, edgeDim);
179  // passing dofHolderIdx as Omega_h_simplex.hpp does not provide
180  // a function that maps a triangle and edge index to a 'canonical' edge
181  // index. This may need to be revisited...
182  osh_ent = triEdges[(tri * triToEdgeDegree) + (dofHolderIdx + 2) % 3];
183  } else {
184  assert(false);
185  }
186  return {0, triCompIdx, osh_ent, dofHolderTopo};
187  }
188 };
190 
192  Omega_h::LOs tetVerts;
193  Omega_h::LOs tetEdges;
194  QuadraticTetrahedronToField(Omega_h::Mesh &mesh)
195  : tetVerts(mesh.ask_elem_verts()),
196  tetEdges(mesh.ask_down(mesh.dim(), 1).ab2b) {
197  if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
198  MeshField::fail(
199  "The mesh passed to %s must be 3D and simplex (tetrahedron)",
200  __func__);
201  }
202  }
203 
204  static constexpr KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
205  getTopology() {
206  return {MeshField::Tetrahedron};
207  }
208 
209  KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
210  operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
211  MeshField::LO tet, MeshField::Mesh_Topology topo) const {
212  assert(topo == MeshField::Tetrahedron);
213  const MeshField::LO tetNode2DofHolder[10] = {0, 1, 2, 3, 3, 4, 5, 0, 1, 2};
214  const MeshField::Mesh_Topology tetNode2DofHolderTopo[10] = {
215  MeshField::Vertex, MeshField::Vertex, MeshField::Vertex,
216  MeshField::Vertex, MeshField::Edge, MeshField::Edge,
217  MeshField::Edge, MeshField::Edge, MeshField::Edge,
218  MeshField::Edge};
219  const auto dofHolderIdx = tetNode2DofHolder[tetNodeIdx];
220  const auto dofHolderTopo = tetNode2DofHolderTopo[tetNodeIdx];
221  Omega_h::LO osh_ent;
222  if (dofHolderTopo == MeshField::Vertex) {
223  const auto tetDim = 3;
224  const auto vtxDim = 0;
225  const auto ignored = -1;
226  // cyclic rotation of the omegah vertex order to map to the meshfields order
227  // defined by the shape functions in MeshField_Shape.hpp
228  const auto localVtxIdx = (Omega_h::simplex_down_template(
229  tetDim, vtxDim, dofHolderIdx, ignored) +
230  3) %
231  4;
232  const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
233  osh_ent = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
234  } else if (dofHolderTopo == MeshField::Edge) {
235  const auto tetDim = 3;
236  const auto edgeDim = 1;
237  const auto tetToEdgeDegree = Omega_h::simplex_degree(tetDim, edgeDim);
238  osh_ent = tetEdges[(tet * tetToEdgeDegree) + dofHolderIdx];
239  } else {
240  assert(false);
241  }
242  return {0, tetCompIdx, osh_ent, dofHolderTopo};
243  }
244 };
245 
247 template <int ShapeOrder> auto getTriangleElement(Omega_h::Mesh &mesh) {
248  static_assert(ShapeOrder == 1 || ShapeOrder == 2);
249  if constexpr (ShapeOrder == 1) {
250  struct result {
252  LinearTriangleToVertexField map;
253  };
254  return result{MeshField::LinearTriangleShape(),
255  LinearTriangleToVertexField(mesh)};
256  } else if constexpr (ShapeOrder == 2) {
257  struct result {
259  QuadraticTriangleToField map;
260  };
261  return result{MeshField::QuadraticTriangleShape(),
262  QuadraticTriangleToField(mesh)};
263  }
264 }
266 template <int ShapeOrder> auto getTetrahedronElement(Omega_h::Mesh &mesh) {
267  static_assert(ShapeOrder == 1 || ShapeOrder == 2);
268  if constexpr (ShapeOrder == 1) {
269  struct result {
271  LinearTetrahedronToVertexField map;
272  };
273  return result{MeshField::LinearTetrahedronShape(),
274  LinearTetrahedronToVertexField(mesh)};
275  } else if constexpr (ShapeOrder == 2) {
276  struct result {
278  QuadraticTetrahedronToField map;
279  };
280  return result{MeshField::QuadraticTetrahedronShape(),
281  QuadraticTetrahedronToField(mesh)};
282  }
283 }
284 
285 } // namespace Omegah
286 
287 template <typename ExecutionSpace, size_t dim,
288  template <typename...> typename Controller =
289  MeshField::KokkosController>
290 class OmegahMeshField {
291 private:
292  Omega_h::Mesh &mesh;
293  const MeshField::MeshInfo meshInfo;
294  using CoordField =
295  decltype(createCoordinateField<ExecutionSpace, dim, Controller>(
296  MeshField::MeshInfo(), Omega_h::Reals()));
297  CoordField coordField;
298 
299 public:
300  OmegahMeshField(Omega_h::Mesh &mesh_in)
301  : mesh(mesh_in), meshInfo(getMeshInfo(mesh)),
302  coordField(createCoordinateField<ExecutionSpace, dim, Controller>(
303  getMeshInfo(mesh_in), mesh_in.coords())) {
304  static_assert(dim == 1 || dim == 2 || dim == 3);
305  }
306 
307  template <typename DataType, size_t order, size_t numComp>
308  // Ordering of field indexing changed to 'entity, node, component'
309  auto CreateLagrangeField() const {
310  return MeshField::CreateLagrangeField<ExecutionSpace, Controller, DataType,
311  order, dim, numComp>(meshInfo);
312  }
313 
314  auto getCoordField() { return coordField; }
315 
316  // FIXME support 2d and 3d and fields with order>1
317  template <typename Field> void writeVtk(Field &field) const {
318  using FieldDataType = typename decltype(field.vtxField)::BaseType;
319  // HACK assumes there is a vertex field.. in the Field Mixin object
320  auto field_view = field.vtxField.serialize();
321  Omega_h::Write<FieldDataType> field_write(field_view);
322  mesh.add_tag(0, "field", 1, Omega_h::read(field_write), false,
323  Omega_h::ArrayType::VectorND);
324  Omega_h::vtk::write_parallel("foo.vtk", &mesh, mesh.dim());
325  }
326 
327  template <typename ViewType = Kokkos::View<MeshField::LO *>>
328  ViewType createOffsets(size_t numTri, size_t numPtsPerElem) const {
329  ViewType offsets("offsets", numTri + 1);
330  Kokkos::parallel_for(
331  "setOffsets", numTri,
332  KOKKOS_LAMBDA(int i) { offsets(i) = i * numPtsPerElem; });
333  Kokkos::deep_copy(Kokkos::subview(offsets, offsets.size() - 1),
334  numTri * numPtsPerElem);
335  return offsets;
336  }
337 
338  // evaluate a field at the specified local coordinate for each triangle
339  template <typename ViewType, typename ShapeField>
340  auto triangleLocalPointEval(const ViewType &localCoords, size_t NumPtsPerElem,
341  const ShapeField &field) const {
342  auto offsets = createOffsets(meshInfo.numTri, NumPtsPerElem);
343  auto eval = triangleLocalPointEval<ViewType, ShapeField>(localCoords,
344  offsets, field);
345  return eval;
346  }
347 
348  // evaluate a field at the specified local coordinates for each triangle
349  template <typename ViewType, typename ShapeField>
350  auto triangleLocalPointEval(const ViewType &localCoords,
351  Kokkos::View<LO *> offsets,
352  const ShapeField &field) const {
353  const auto MeshDim = 2;
354  if (mesh.dim() != MeshDim) {
355  MeshField::fail("input mesh must be 2d\n");
356  }
357  const auto ShapeOrder = ShapeField::Order;
358  if (ShapeOrder != 1 && ShapeOrder != 2) {
359  MeshField::fail("input field order must be 1 or 2\n");
360  }
361 
362  const auto [shp, map] = Omegah::getTriangleElement<ShapeOrder>(mesh);
363 
364  MeshField::FieldElement<ShapeField, decltype(shp), decltype(map)> f(
365  meshInfo.numTri, field, shp, map);
366  auto eval = MeshField::evaluate(f, localCoords, offsets);
367  return eval;
368  }
369 
370  template <typename ViewType, typename ShapeField>
371  auto tetrahedronLocalPointEval(const ViewType &localCoords,
372  size_t NumPtsPerElem,
373  const ShapeField &field) const {
374  auto offsets = createOffsets(meshInfo.numTet, NumPtsPerElem);
375  auto eval = tetrahedronLocalPointEval(localCoords, offsets, field);
376  return eval;
377  }
378 
379  template <typename ViewType, typename ShapeField>
380  auto tetrahedronLocalPointEval(const ViewType &localCoords,
381  Kokkos::View<LO *> offsets,
382  const ShapeField &field) const {
383  const auto MeshDim = 3;
384  if (mesh.dim() != MeshDim) {
385  MeshField::fail("input mesh must be 3d\n");
386  }
387  const auto ShapeOrder = ShapeField::Order;
388  if (ShapeOrder != 1 && ShapeOrder != 2) {
389  MeshField::fail("input field order must be 1 or 2\n");
390  }
391  const auto [shp, map] = Omegah::getTetrahedronElement<ShapeOrder>(mesh);
392  MeshField::FieldElement<ShapeField, decltype(shp), decltype(map)> f(
393  meshInfo.numTet, field, shp, map);
394  auto eval = MeshField::evaluate(f, localCoords, offsets);
395  return eval;
396  }
397 };
398 
399 } // namespace MeshField
400 
401 #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...
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.