MeshFields
GPU accelerated mesh-based fields
MeshField_Element.hpp
1 #ifndef MESHFIELD_ELEMENT_H
2 #define MESHFIELD_ELEMENT_H
3 
4 #include <Kokkos_Core.hpp>
5 #include <MeshField_Defines.hpp>
6 #include <MeshField_Fail.hpp>
7 #include <MeshField_For.hpp>
8 #include <MeshField_Shape.hpp>
9 #include <MeshField_Utility.hpp> // getLastValue
10 #include <iostream>
11 #include <type_traits> // has_static_size helper
12 
13 namespace {
14 Kokkos::View<MeshField::LO *> getOffsets(MeshField::LO numItems,
15  MeshField::LO numEntriesPerItem) {
16  Kokkos::View<MeshField::LO *> offsets("offsets", numItems + 1);
17  auto first = Kokkos::subview(offsets, 0);
18  Kokkos::deep_copy(first, 0); // write 0 to the first item
19  Kokkos::parallel_for(
20  numItems, KOKKOS_LAMBDA(const int i) {
21  offsets(i + 1) = (i + 1) * numEntriesPerItem;
22  });
23  return offsets;
24 }
25 
26 // chatgpt prompt 2/20/2025:
27 // c++ static assert that checks that a type
28 // provides a function named size
29 template <typename T> class has_size_method {
30 private:
31  template <typename U>
32  static auto test(int) -> decltype(std::declval<U>().size(), std::true_type());
33  template <typename> static std::false_type test(...);
34 
35 public:
36  static constexpr bool value = decltype(test<T>(0))::value;
37 };
38 
39 template <typename T> class has_static_rank {
40 private:
41  template <typename U>
42  static auto test(int) -> decltype(U::rank(), std::true_type());
43  template <typename> static std::false_type test(...);
44 
45 public:
46  static constexpr bool value = decltype(test<T>(0))::value;
47 };
48 
49 template <typename T> class has_extent_method {
50 private:
51  template <typename U>
52  static auto
53  test(int) -> decltype(std::declval<U>().extent(std::declval<std::size_t>()),
54  std::true_type());
55  template <typename> static std::false_type test(...);
56 
57 public:
58  static constexpr bool value = decltype(test<T>(0))::value;
59 };
60 
61 // FIXME - addTensorProduct(...) likely needs performance improvements
63 template <typename VecA, typename VecB, typename Matrix>
64 KOKKOS_INLINE_FUNCTION auto addTensorProduct(VecA const &a, VecB const &b,
65  Matrix &A) {
66  static_assert(has_size_method<VecA>::value,
67  "VecA must have a size() method.");
68  static_assert(has_size_method<VecB>::value,
69  "VecB must have a size() method.");
70  static_assert(has_static_rank<VecA>::value,
71  "VecA must have a static rank() method.");
72  static_assert(has_static_rank<VecB>::value,
73  "VecB must have a static rank() method.");
74  static_assert(
75  std::is_same_v<typename VecA::value_type, typename VecB::value_type>);
76  static_assert(VecA::rank() == 1);
77  static_assert(VecB::rank() == 1);
78  static_assert(has_extent_method<Matrix>::value,
79  "Matrix must have an extent(size_t) method.");
80  const auto M = a.size();
81  const auto N = b.size();
82  for (std::size_t i = 0; i < M; ++i) {
83  for (std::size_t j = 0; j < N; ++j) {
84  A(i, j) += b[j] * a[i];
85  }
86  }
87 }
88 } // namespace
89 
90 namespace MeshField {
91 
92 
93 //
94 
105  LO node;
106 
112 
116  LO entity;
117 
121  Mesh_Topology topo;
122 };
123 
147 template <typename FieldAccessor, typename ShapeType,
148  typename ElementDofHolderAccessor>
149 struct FieldElement {
150  const size_t numMeshEnts;
151  const FieldAccessor field;
152  ShapeType shapeFn;
153  ElementDofHolderAccessor elm2dof;
154 
155  static const size_t MeshEntDim = ShapeType::meshEntDim;
156  FieldElement(size_t numMeshEntsIn, const FieldAccessor &fieldIn,
157  const ShapeType shapeFnIn,
158  const ElementDofHolderAccessor elm2dofIn)
159  : numMeshEnts(numMeshEntsIn), field(fieldIn), shapeFn(shapeFnIn),
160  elm2dof(elm2dofIn) {}
161  /* general template for baseType which simply sets type
162  */
163  template <typename T> struct baseType {
164  using type = T;
165  };
166  /* template specialization to recursively strip type to get base type
167  * Example: int[5][6] => int[6] => int
168  */
169  template <typename T, size_t N> struct baseType<T[N]> {
170  using type = typename baseType<T>::type;
171  };
172  using ValArray =
173  Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
174  FieldAccessor::numComp>;
175  static const size_t NumComponents = FieldAccessor::numComp;
176 
192  KOKKOS_INLINE_FUNCTION ValArray
193  getValue(int ent, Kokkos::Array<Real, MeshEntDim> localCoord) const {
194  assert(ent >= 0);
195  assert(static_cast<size_t>(ent) < numMeshEnts);
196  ValArray c;
197  const auto shapeValues = shapeFn.getValues(localCoord);
198  for (size_t ci = 0; ci < NumComponents; ++ci)
199  c[ci] = 0;
200  for (auto topo : elm2dof.getTopology()) { // element topology
201  for (size_t ni = 0; ni < shapeFn.numNodes; ++ni) {
202  for (size_t ci = 0; ci < NumComponents; ++ci) {
203  auto map = elm2dof(ni, ci, ent, topo);
204  const auto fval =
205  field(map.entity, map.node, map.component, map.topo);
206  c[ci] += fval * shapeValues[ni];
207  }
208  }
209  }
210  return c;
211  }
212 
213  using NodeArray =
214  Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
215  ShapeType::meshEntDim * ShapeType::numNodes>;
216  KOKKOS_INLINE_FUNCTION NodeArray getNodeValues(int ent) const {
217  NodeArray c;
218  for (auto topo : elm2dof.getTopology()) { // element topology
219  for (size_t ni = 0; ni < ShapeType::numNodes; ++ni) {
220  for (size_t d = 0; d < ShapeType::meshEntDim; ++d) {
221  auto map = elm2dof(ni, d, ent, topo);
222  const auto fval =
223  field(map.entity, map.node, map.component, map.topo);
224  c[ni * ShapeType::meshEntDim + d] = fval;
225  }
226  }
227  }
228  return c;
229  }
230 
243  KOKKOS_INLINE_FUNCTION Real getJacobian1d(int ent) const {
244  assert(ent >= 0);
245  assert(static_cast<size_t>(ent) < numMeshEnts);
246  Vector1 ignored;
247  const auto nodalGradients = shapeFn.getLocalGradients(ignored);
248  const auto nodeValues = getNodeValues(ent);
249  auto g = nodalGradients[0] * nodeValues[0];
250  for (size_t i = 1; i < shapeFn.numNodes; ++i) {
251  g = g + nodalGradients[i] * nodeValues[i];
252  }
253  return g;
254  }
255 
260  template <typename Matrices>
261  Kokkos::View<Real *> getJacobianDeterminants(Matrices const &J) const {
262  static_assert(has_static_rank<Matrices>::value,
263  "Matrices must have a static rank() method.");
264  static_assert(has_extent_method<Matrices>::value,
265  "Matrices must have an extent(size_t) method.");
266  static_assert(Matrices::rank() == 3); // array of rank two matrices
267  if (J.extent(1) != J.extent(2)) {
268  fail("getJacobianDeterminant only supports square matrices. "
269  "The given matrices have dimension %lu x %lu \n",
270  J.extent(1), J.extent(2));
271  }
272  const auto dimension = J.extent(1);
273  if (dimension > 3 || dimension < 1) {
274  fail("getJacobianDeterminant: invalid dimension of input matrix. "
275  "The given matrices have dimension %lu x %lu \n",
276  J.extent(0), J.extent(1));
277  }
278  if (dimension == 3) {
279  /* det(J) is also the triple product of the
280  "tangent vectors" in 3D, the volume of their
281  parallelpiped, which is the differential volume
282  of the coordinate field */
283  Kokkos::View<Real *> determinants("3dJacobianDeterminants", J.extent(0));
284  Kokkos::parallel_for(
285  J.extent(0), KOKKOS_LAMBDA(const int i) {
286  const auto Ji = Kokkos::subview(J, i, Kokkos::ALL(), Kokkos::ALL());
287  const auto cofactorSum =
288  Ji(0, 0) * (Ji(1, 1) * Ji(2, 2) - Ji(1, 2) * Ji(2, 1)) -
289  Ji(0, 1) * (Ji(1, 0) * Ji(2, 2) - Ji(1, 2) * Ji(2, 0)) +
290  Ji(0, 2) * (Ji(1, 0) * Ji(2, 1) - Ji(1, 1) * Ji(2, 0));
291  determinants(i) = cofactorSum;
292  });
293  return determinants;
294  }
295  if (dimension == 2) {
296  if (J.extent(1) != 2) {
297  fail("getJacobianDeterminant only supports 2x2 matrices in 2d. "
298  "The given matrices have dimension %lu x %lu \n",
299  J.extent(1), J.extent(2));
300  }
301  /* |\frac{\partial x}{\partial s}\times
302  \frac{\partial x}{\partial t}|,
303  the area spanned by the tangent vectors
304  at this point, surface integral. */
305  Kokkos::View<Real *> determinants("2dJacobianDeterminants", J.extent(0));
306  // compute the cross product of the 2x2 jacobian matrix
307  Kokkos::parallel_for(
308  J.extent(0), KOKKOS_LAMBDA(const int i) {
309  // TODO use nested parallel for?
310  auto Ji = Kokkos::subview(J, i, Kokkos::ALL(), Kokkos::ALL());
311  const auto cross = Ji(0, 0) * Ji(1, 1) - Ji(1, 0) * Ji(0, 1);
312  determinants(i) = cross;
313  });
314  return determinants;
315  }
316  if (dimension == 1) {
317  fail("getJacobianDeterminant doesn't yet support 1d. "
318  "The given matrices have dimension %lu x %lu \n",
319  J.extent(0), J.extent(1));
320  }
321  // assuming at this point dimension=1
322  /* \|\vec{x}_{,\xi}\| the length
323  of the tangent vector at this point.
324  line integral:
325  ds = sqrt(dx^2 + dy^2 + dz^2) */
326  return Kokkos::View<Real *>("foo", J.extent(0));
327  }
328 
329  Kokkos::View<Real ***> getJacobiansStaticLength(Kokkos::View<Real **> localCoords) {
330  if (Debug) {
331  LO numErrors = 0;
332  Kokkos::parallel_reduce(
333  "checkCoords", localCoords.extent(0),
334  KOKKOS_LAMBDA(const int &ent, LO &lerrors) {
335  Real sum = 0;
336  LO isError = 0;
337  for (size_t i = 0; i < localCoords.extent(1); i++) {
338  if (localCoords(ent, i) < 0)
339  isError++;
340  sum += localCoords(ent, i);
341  }
342  if (Kokkos::fabs(sum - 1) > MachinePrecision)
343  isError++;
344  lerrors += isError;
345  },
346  numErrors);
347  if (numErrors) {
348  fail("One or more of the parametric coordinates passed "
349  "to evaluate(...) were invalid\n");
350  }
351  }
352  if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) {
353  fail("getJacobians only currently supports 1d, 2d, and 3d meshes. Input "
354  "mesh "
355  "has %zu dimensions.\n",
356  numMeshEnts);
357  }
358 
359  if constexpr (MeshEntDim == 1) {
360  const auto numPts = localCoords.extent(0);
361  Kokkos::View<Real ***> res("result", numPts * numMeshEnts, 1, 1);
362  auto jacobianFunc = KOKKOS_CLASS_LAMBDA(const int ent, const int pt) {
363  const auto val = getJacobian1d(ent);
364  res(pt, 0, 0) = val;
365  };
366  MeshField::parallel_for<typename FieldAccessor::Ctrlr, true>({0, 0},
367  {numMeshEnts, numPts}, jacobianFunc, "1dJacobian");
368  return res;
369  } else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) {
370  const auto numPts = localCoords.extent(0);
371  Kokkos::View<Real ***> res("result", numPts * numMeshEnts, MeshEntDim,
372  MeshEntDim);
373  Kokkos::deep_copy(res, 0.0);
374  auto jacobianFunc = KOKKOS_CLASS_LAMBDA(const int ent, const int pt) {
375  const auto vals = getNodeValues(ent);
376  Kokkos::Array<Real, MeshEntDim> xi;
377  for (size_t d = 0; d < MeshEntDim; d++)
378  xi[d] = localCoords(pt, d);
379  const auto grad = shapeFn.getLocalGradients(xi);
380  auto A = Kokkos::subview(res, ent * numPts + pt, Kokkos::ALL(),
381  Kokkos::ALL());
382  Real localA[MeshEntDim][MeshEntDim] = {};
383  for (size_t node = 0; node < ShapeType::numNodes; node++) {
384  for (size_t i = 0; i < MeshEntDim; ++i) {
385  for (size_t j = 0; j < MeshEntDim; ++j) {
386  localA[j][i] += vals[node * MeshEntDim + i] *
387  grad[node * MeshEntDim + j];
388  }
389  }
390  }
391  for (size_t i = 0; i < MeshEntDim; ++i) {
392  for (size_t j = 0; j < MeshEntDim; ++j) {
393  A(i, j) = localA[i][j];
394  }
395  }
396  };
397  MeshField::parallel_for<typename FieldAccessor::Ctrlr, true>({0, 0},
398  {numMeshEnts, numPts}, jacobianFunc, "2d3dJacobian");
399  return res;
400  }
401  }
402 
418  Kokkos::View<Real ***> getJacobians(Kokkos::View<Real **> localCoords,
419  Kokkos::View<LO *> offsets) const {
420  if (Debug) {
421  // check input parametric coords are positive and sum to one
422  // TODO move this to helper function
423  LO numErrors = 0;
424  Kokkos::parallel_reduce(
425  "checkCoords", numMeshEnts,
426  KOKKOS_LAMBDA(const int &ent, LO &lerrors) {
427  Real sum = 0;
428  LO isError = 0;
429  for (size_t i = 0; i < localCoords.extent(1); i++) {
430  if (localCoords(ent, i) < 0)
431  isError++;
432  sum += localCoords(ent, i);
433  }
434  if (sum > 1.0)
435  isError++;
436  lerrors += isError;
437  },
438  numErrors);
439  if (numErrors) {
440  fail("One or more of the parametric coordinates passed "
441  "to evaluate(...) were invalid\n");
442  }
443  }
444  if (localCoords.extent(0) < numMeshEnts) {
445  fail("The size of dimension 0 of the local coordinates input array "
446  "must be at least %zu.\n",
447  numMeshEnts);
448  }
449  if (localCoords.extent(1) != MeshEntDim) {
450  fail("Dimension 1 of the input array of local coordinates "
451  "must have size = %zu.\n",
452  MeshEntDim);
453  }
454  if (offsets.size() != numMeshEnts + 1) {
455  fail("The input array of offsets must have size = %zu\n",
456  numMeshEnts + 1);
457  }
458  if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) {
459  fail("getJacobians only currently supports 1d, 2d, and 3d meshes. Input "
460  "mesh "
461  "has %zu dimensions.\n",
462  numMeshEnts);
463  }
464  if constexpr (MeshEntDim == 1) {
465  const auto numPts = MeshFieldUtil::getLastValue(offsets);
466  Kokkos::View<Real ***> res("result", numPts, 1, 1);
467  Kokkos::parallel_for(
468  numMeshEnts, KOKKOS_CLASS_LAMBDA(const int ent) {
469  // TODO use nested parallel for?
470  for (auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
471  const auto val = getJacobian1d(ent);
472  res(pt, 0, 0) = val;
473  }
474  });
475  return res;
476  } else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) {
477  const auto numPts = MeshFieldUtil::getLastValue(offsets);
478  // one matrix per point
479  Kokkos::View<Real ***> res("result", numPts, MeshEntDim, MeshEntDim);
480  Kokkos::deep_copy(res, 0.0); // initialize all entries to zero
481  Kokkos::parallel_for(
482  numMeshEnts, KOKKOS_CLASS_LAMBDA(const int ent) {
483  const auto vals = getNodeValues(ent);
484  assert(vals.size() == MeshEntDim * ShapeType::numNodes);
485  for (auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
486  auto A = Kokkos::subview(res, pt, Kokkos::ALL(), Kokkos::ALL());
487  Real localA[MeshEntDim][MeshEntDim] = {};
488  Kokkos::Array<Real, MeshEntDim> xi;
489  for (size_t d = 0; d < MeshEntDim; d++)
490  xi[d] = localCoords(pt, d);
491  const auto grad = shapeFn.getLocalGradients(xi);
492  for (size_t node = 0; node < ShapeType::numNodes; node++) {
493  for (size_t i = 0; i < MeshEntDim; ++i) {
494  for (size_t j = 0; j < MeshEntDim; ++j) {
495  localA[j][i] += vals[node * MeshEntDim + i] *
496  grad[node * MeshEntDim + j];
497  }
498  }
499  }
500  for (size_t i = 0; i < MeshEntDim; ++i) {
501  for (size_t j = 0; j < MeshEntDim; ++j) {
502  A(i, j) = localA[i][j];
503  }
504  }
505  }
506  });
507 
508  return res;
509  }
510  }
511 };
512 
513 template <typename FieldElement>
514 Kokkos::View<Real *[FieldElement::NumComponents]>
515 evaluateStaticLength(FieldElement &fes, Kokkos::View<Real **> localCoords) {
516  if (Debug) {
517  LO numErrors = 0;
518  Kokkos::parallel_reduce(
519  "checkCoords", localCoords.extent(0),
520  KOKKOS_LAMBDA(const int &ent, LO &lerrors) {
521  Real sum = 0;
522  LO isError = 0;
523  for (size_t i = 0; i < localCoords.extent(1); i++) {
524  if (localCoords(ent, i) < 0)
525  isError++;
526  sum += localCoords(ent, i);
527  }
528  if (Kokkos::fabs(sum - 1) > MachinePrecision)
529  isError++;
530  lerrors += isError;
531  },
532  numErrors);
533  if (numErrors) {
534  fail("One or more of the parametric coordinates passed "
535  "to evaluate(...) were invalid\n");
536  }
537  }
538  constexpr const auto numComponents = FieldElement::ValArray::size();
539  const auto numPts = localCoords.extent(0);
540  Kokkos::View<Real *[numComponents]> res("result", numPts * fes.numMeshEnts);
541  auto evaluateFunc = KOKKOS_LAMBDA(const int ent, const int pt) {
542  Kokkos::Array<Real, FieldElement::MeshEntDim> lc;
543  for (size_t i = 0; i < localCoords.extent(1); ++i)
544  lc[i] = localCoords(pt, i);
545  const auto val = fes.getValue(ent, lc);
546  for (size_t i = 0; i < numComponents; ++i) {
547  res(ent * numPts + pt, i) = val[i];
548  }
549  };
550  MeshField::parallel_for<typename decltype(fes.field)::Ctrlr, true>({0, 0}, {fes.numMeshEnts, numPts}, evaluateFunc,
551  "evaluate");
552  return res;
553 }
554 
573 template <typename FieldElement>
574 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
575  FieldElement &fes, Kokkos::View<Real **> localCoords,
576  Kokkos::View<LO *> offsets) {
577  if (Debug) {
578  // check input parametric coords are positive and sum to one
579  LO numErrors = 0;
580  Kokkos::parallel_reduce(
581  "checkCoords", fes.numMeshEnts,
582  KOKKOS_LAMBDA(const int &ent, LO &lerrors) {
583  Real sum = 0;
584  LO isError = 0;
585  for (size_t i = 0; i < localCoords.extent(1); i++) {
586  if (localCoords(ent, i) < 0)
587  isError++;
588  sum += localCoords(ent, i);
589  }
590  if (sum > 1.0)
591  isError++;
592  lerrors += isError;
593  },
594  numErrors);
595  if (numErrors) {
596  fail("One or more of the parametric coordinates passed "
597  "to evaluate(...) were invalid\n");
598  }
599  }
600 
601  if (localCoords.extent(1) != fes.MeshEntDim) {
602  fail("Dimension 1 of the input array of local coordinates "
603  "must have size = %zu.\n",
604  fes.MeshEntDim);
605  }
606  if (offsets.size() != fes.numMeshEnts + 1) {
607  fail("The input array of offsets must have size = %zu\n",
608  fes.numMeshEnts + 1);
609  }
610  LO numLocalCoords;
611  Kokkos::deep_copy(numLocalCoords,
612  Kokkos::subview(offsets, offsets.size() - 1));
613  if (localCoords.extent(0) != static_cast<size_t>(numLocalCoords)) {
614  fail("The size of dimension 0 of the local coordinates input array (%zu) "
615  "does not match the last entry of the offsets array (%d).\n",
616  localCoords.extent(0), numLocalCoords);
617  }
618 
619  const auto numPts = MeshFieldUtil::getLastValue(offsets);
620  Kokkos::View<Real *[FieldElement::NumComponents]> res("result", numPts);
621  Kokkos::parallel_for(
622  fes.numMeshEnts, KOKKOS_LAMBDA(const int ent) {
623  Kokkos::Array<Real, FieldElement::MeshEntDim> lc;
624  // TODO use nested parallel for?
625  for (auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
626  for (size_t i = 0; i < localCoords.extent(1); i++) // better way?
627  lc[i] = localCoords(pt, i);
628  const auto val = fes.getValue(ent, lc);
629  for (size_t i = 0; i < FieldElement::NumComponents; i++)
630  res(pt, i) = val[i];
631  }
632  });
633  return res;
634 }
635 
644 template <typename FieldElement>
645 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
646  FieldElement &fes, Kokkos::View<Real **> localCoords) {
647  const auto numPtsPerElement = 1;
648  return evaluate(fes, localCoords, numPtsPerElement);
649 }
650 
662 template <typename FieldElement>
663 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
664  FieldElement &fes, Kokkos::View<Real **> localCoords,
665  size_t numPtsPerElement) {
666  const auto offsets = getOffsets(fes.numMeshEnts, numPtsPerElement);
667  return evaluate(fes, localCoords, offsets);
668 }
669 
684 template <typename FieldElement>
685 Kokkos::View<Real ***> getJacobians(FieldElement &fes,
686  Kokkos::View<Real **> localCoords,
687  size_t numPtsPerElement) {
688  const auto offsets = getOffsets(fes.numMeshEnts, numPtsPerElement);
689  return fes.getJacobians(localCoords, offsets);
690 }
691 
702 template <typename FieldElement, typename Matrices>
703 Kokkos::View<Real *> getJacobianDeterminants(FieldElement &fes, Matrices J) {
704  return fes.getJacobianDeterminants(J);
705 }
706 
713  static constexpr size_t NumTopologies = Pyramid + 1;
714  Kokkos::Array<LO, NumTopologies + 1> offset;
715  Kokkos::View<LO *> owner;
716 
718  KOKKOS_INLINE_FUNCTION LO operator()(Mesh_Topology topo, LO entity) const {
719  return owner(offset[topo] + entity);
720  }
721 };
722 
728 template <typename FieldElement>
729 DofHolderOwningEntities getDofHolderOwningEntities(FieldElement const &fes) {
730  constexpr size_t numNodes = decltype(fes.shapeFn)::numNodes;
731  constexpr size_t numTopologies = DofHolderOwningEntities::NumTopologies;
732  const auto &meshInfo = fes.field.meshInfo;
733  const LO numElements = fes.numMeshEnts;
734  DofHolderOwningEntities owners;
735  owners.offset[0] = 0;
736  for (size_t t = 0; t < numTopologies; ++t)
737  owners.offset[t + 1] =
738  owners.offset[t] + meshInfo.numEntities(static_cast<Mesh_Topology>(t));
739  owners.owner = Kokkos::View<LO *>(
740  Kokkos::view_alloc(Kokkos::WithoutInitializing, "dofHolderOwner"),
741  owners.offset[numTopologies]);
742  Kokkos::deep_copy(owners.owner, numElements);
743  const auto offset = owners.offset;
744  const auto owner = owners.owner;
745  Kokkos::parallel_for(
746  "getDofHolderOwningEntities", numElements, KOKKOS_LAMBDA(const int ent) {
747  for (auto topo : fes.elm2dof.getTopology()) {
748  for (size_t n = 0; n < numNodes; ++n) {
749  const auto map = fes.elm2dof(n, 0, ent, topo);
750  Kokkos::atomic_min(&owner(offset[map.topo] + map.entity), ent);
751  }
752  }
753  });
754  return owners;
755 }
756 
762 template <typename FieldElement, typename Source>
763 void setDofByInterpolation(FieldElement &target, Source const &source) {
764  constexpr size_t numNodes = decltype(target.shapeFn)::numNodes;
765  constexpr size_t meshEntDim = FieldElement::MeshEntDim;
766  constexpr size_t numComp = FieldElement::NumComponents;
767  // we get a list of owners so that we only perform evaluation once
768  // per shared dof
769  const auto owners = getDofHolderOwningEntities(target);
770  Kokkos::parallel_for(
771  "interpolate", target.numMeshEnts, KOKKOS_LAMBDA(const int ent) {
772  const auto nodeCoords = target.shapeFn.getNodeParametricCoords();
773  for (auto topo : target.elm2dof.getTopology()) {
774  for (size_t n = 0; n < numNodes; ++n) {
775  const auto holder = target.elm2dof(n, 0, ent, topo);
776  if (owners(holder.topo, holder.entity) != ent)
777  continue;
778  Kokkos::Array<Real, meshEntDim> xi;
779  for (size_t d = 0; d < meshEntDim; ++d)
780  xi[d] = nodeCoords[n * meshEntDim + d];
781  const auto val = source(ent, xi);
782  for (size_t c = 0; c < numComp; ++c) {
783  const auto map = target.elm2dof(n, c, ent, topo);
784  target.field(map.entity, map.node, map.component, map.topo) =
785  val[c];
786  }
787  }
788  }
789  });
790 }
791 
792 } // namespace MeshField
793 #endif
Shape function definitions for finite element analysis.
Kokkos::Array< Real, 1 > Vector1
1D parametric coordinate vector
For each dof holder of a field, the element that owns it; see getDofHolderOwningEntities.
KOKKOS_INLINE_FUNCTION LO operator()(Mesh_Topology topo, LO entity) const
the index of the element that owns the given dof holder
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...
Kokkos::View< Real * > getJacobianDeterminants(Matrices const &J) const
Kokkos::View< Real *** > getJacobians(Kokkos::View< Real ** > localCoords, Kokkos::View< LO * > offsets) const
Given an array of parametric coordinates 'localCoords', one per mesh element, compute the jacobian wi...
KOKKOS_INLINE_FUNCTION Real getJacobian1d(int ent) const
compute the Jacobian of an edge
KOKKOS_INLINE_FUNCTION ValArray getValue(int ent, Kokkos::Array< Real, MeshEntDim > localCoord) const
evaluate the field in the specified element at the specified parametric/local/area coordinate