1 #ifndef MESHFIELD_ELEMENT_H
2 #define MESHFIELD_ELEMENT_H
4 #include <Kokkos_Core.hpp>
5 #include <MeshField_Defines.hpp>
6 #include <MeshField_Fail.hpp>
7 #include <MeshField_For.hpp>
9 #include <MeshField_Utility.hpp>
11 #include <type_traits>
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);
20 numItems, KOKKOS_LAMBDA(
const int i) {
21 offsets(i + 1) = (i + 1) * numEntriesPerItem;
29 template <
typename T>
class has_size_method {
32 static auto test(
int) -> decltype(std::declval<U>().size(), std::true_type());
33 template <
typename>
static std::false_type test(...);
36 static constexpr
bool value = decltype(test<T>(0))::value;
39 template <
typename T>
class has_static_rank {
42 static auto test(
int) -> decltype(U::rank(), std::true_type());
43 template <
typename>
static std::false_type test(...);
46 static constexpr
bool value = decltype(test<T>(0))::value;
49 template <
typename T>
class has_extent_method {
53 test(
int) -> decltype(std::declval<U>().extent(std::declval<std::size_t>()),
55 template <
typename>
static std::false_type test(...);
58 static constexpr
bool value = decltype(test<T>(0))::value;
63 template <
typename VecA,
typename VecB,
typename Matrix>
64 KOKKOS_INLINE_FUNCTION
auto addTensorProduct(VecA
const &a, VecB
const &b,
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.");
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];
147 template <
typename FieldAccessor,
typename ShapeType,
148 typename ElementDofHolderAccessor>
150 const size_t numMeshEnts;
151 const FieldAccessor field;
153 ElementDofHolderAccessor elm2dof;
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) {}
163 template <
typename T>
struct baseType {
169 template <
typename T,
size_t N>
struct baseType<T[N]> {
170 using type =
typename baseType<T>::type;
173 Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
174 FieldAccessor::numComp>;
175 static const size_t NumComponents = FieldAccessor::numComp;
192 KOKKOS_INLINE_FUNCTION ValArray
193 getValue(
int ent, Kokkos::Array<Real, MeshEntDim> localCoord)
const {
195 assert(
static_cast<size_t>(ent) < numMeshEnts);
197 const auto shapeValues = shapeFn.getValues(localCoord);
198 for (
size_t ci = 0; ci < NumComponents; ++ci)
200 for (
auto topo : elm2dof.getTopology()) {
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);
205 field(map.entity, map.node, map.component, map.topo);
206 c[ci] += fval * shapeValues[ni];
214 Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
215 ShapeType::meshEntDim * ShapeType::numNodes>;
216 KOKKOS_INLINE_FUNCTION NodeArray getNodeValues(
int ent)
const {
218 for (
auto topo : elm2dof.getTopology()) {
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);
223 field(map.entity, map.node, map.component, map.topo);
224 c[ni * ShapeType::meshEntDim + d] = fval;
245 assert(
static_cast<size_t>(ent) < numMeshEnts);
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];
260 template <
typename Matrices>
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);
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));
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));
278 if (dimension == 3) {
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;
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));
305 Kokkos::View<Real *> determinants(
"2dJacobianDeterminants", J.extent(0));
307 Kokkos::parallel_for(
308 J.extent(0), KOKKOS_LAMBDA(
const int i) {
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;
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));
326 return Kokkos::View<Real *>(
"foo", J.extent(0));
329 Kokkos::View<Real ***> getJacobiansStaticLength(Kokkos::View<Real **> localCoords) {
332 Kokkos::parallel_reduce(
333 "checkCoords", localCoords.extent(0),
334 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
337 for (
size_t i = 0; i < localCoords.extent(1); i++) {
338 if (localCoords(ent, i) < 0)
340 sum += localCoords(ent, i);
342 if (Kokkos::fabs(sum - 1) > MachinePrecision)
348 fail(
"One or more of the parametric coordinates passed "
349 "to evaluate(...) were invalid\n");
352 if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) {
353 fail(
"getJacobians only currently supports 1d, 2d, and 3d meshes. Input "
355 "has %zu dimensions.\n",
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) {
366 MeshField::parallel_for<typename FieldAccessor::Ctrlr, true>({0, 0},
367 {numMeshEnts, numPts}, jacobianFunc,
"1dJacobian");
369 }
else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) {
370 const auto numPts = localCoords.extent(0);
371 Kokkos::View<Real ***> res(
"result", numPts * numMeshEnts, 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(),
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];
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];
397 MeshField::parallel_for<typename FieldAccessor::Ctrlr, true>({0, 0},
398 {numMeshEnts, numPts}, jacobianFunc,
"2d3dJacobian");
419 Kokkos::View<LO *> offsets)
const {
424 Kokkos::parallel_reduce(
425 "checkCoords", numMeshEnts,
426 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
429 for (
size_t i = 0; i < localCoords.extent(1); i++) {
430 if (localCoords(ent, i) < 0)
432 sum += localCoords(ent, i);
440 fail(
"One or more of the parametric coordinates passed "
441 "to evaluate(...) were invalid\n");
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",
449 if (localCoords.extent(1) != MeshEntDim) {
450 fail(
"Dimension 1 of the input array of local coordinates "
451 "must have size = %zu.\n",
454 if (offsets.size() != numMeshEnts + 1) {
455 fail(
"The input array of offsets must have size = %zu\n",
458 if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) {
459 fail(
"getJacobians only currently supports 1d, 2d, and 3d meshes. Input "
461 "has %zu dimensions.\n",
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) {
470 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
476 }
else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) {
477 const auto numPts = MeshFieldUtil::getLastValue(offsets);
479 Kokkos::View<Real ***> res(
"result", numPts, MeshEntDim, MeshEntDim);
480 Kokkos::deep_copy(res, 0.0);
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];
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];
513 template <
typename FieldElement>
514 Kokkos::View<Real *[FieldElement::NumComponents]>
515 evaluateStaticLength(FieldElement &fes, Kokkos::View<Real **> localCoords) {
518 Kokkos::parallel_reduce(
519 "checkCoords", localCoords.extent(0),
520 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
523 for (
size_t i = 0; i < localCoords.extent(1); i++) {
524 if (localCoords(ent, i) < 0)
526 sum += localCoords(ent, i);
528 if (Kokkos::fabs(sum - 1) > MachinePrecision)
534 fail(
"One or more of the parametric coordinates passed "
535 "to evaluate(...) were invalid\n");
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];
550 MeshField::parallel_for<
typename decltype(fes.field)::Ctrlr, true>({0, 0}, {fes.numMeshEnts, numPts}, evaluateFunc,
573 template <
typename FieldElement>
574 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
575 FieldElement &fes, Kokkos::View<Real **> localCoords,
576 Kokkos::View<LO *> offsets) {
580 Kokkos::parallel_reduce(
581 "checkCoords", fes.numMeshEnts,
582 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
585 for (
size_t i = 0; i < localCoords.extent(1); i++) {
586 if (localCoords(ent, i) < 0)
588 sum += localCoords(ent, i);
596 fail(
"One or more of the parametric coordinates passed "
597 "to evaluate(...) were invalid\n");
601 if (localCoords.extent(1) != fes.MeshEntDim) {
602 fail(
"Dimension 1 of the input array of local coordinates "
603 "must have size = %zu.\n",
606 if (offsets.size() != fes.numMeshEnts + 1) {
607 fail(
"The input array of offsets must have size = %zu\n",
608 fes.numMeshEnts + 1);
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);
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;
625 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
626 for (
size_t i = 0; i < localCoords.extent(1); i++)
627 lc[i] = localCoords(pt, i);
628 const auto val = fes.getValue(ent, lc);
629 for (
size_t i = 0; i < FieldElement::NumComponents; i++)
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);
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);
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);
702 template <
typename FieldElement,
typename Matrices>
703 Kokkos::View<Real *> getJacobianDeterminants(FieldElement &fes, Matrices J) {
704 return fes.getJacobianDeterminants(J);
713 static constexpr
size_t NumTopologies = Pyramid + 1;
714 Kokkos::Array<LO, NumTopologies + 1> offset;
715 Kokkos::View<LO *> owner;
718 KOKKOS_INLINE_FUNCTION LO
operator()(Mesh_Topology topo, LO entity)
const {
719 return owner(offset[topo] + entity);
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);
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;
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)
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) =
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