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>
8 #include <MeshField_Utility.hpp>
10 #include <type_traits>
13 Kokkos::View<MeshField::LO *> getOffsets(MeshField::LO numItems,
14 MeshField::LO numEntriesPerItem) {
15 Kokkos::View<MeshField::LO *> offsets(
"offsets", numItems + 1);
16 auto first = Kokkos::subview(offsets, 0);
17 Kokkos::deep_copy(first, 0);
19 numItems, KOKKOS_LAMBDA(
const int i) {
20 offsets(i + 1) = (i + 1) * numEntriesPerItem;
28 template <
typename T>
class has_size_method {
31 static auto test(
int) -> decltype(std::declval<U>().size(), std::true_type());
32 template <
typename>
static std::false_type test(...);
35 static constexpr
bool value = decltype(test<T>(0))::value;
38 template <
typename T>
class has_static_rank {
41 static auto test(
int) -> decltype(U::rank(), std::true_type());
42 template <
typename>
static std::false_type test(...);
45 static constexpr
bool value = decltype(test<T>(0))::value;
48 template <
typename T>
class has_extent_method {
52 -> decltype(std::declval<U>().extent(std::declval<std::size_t>()),
54 template <
typename>
static std::false_type test(...);
57 static constexpr
bool value = decltype(test<T>(0))::value;
62 template <
typename VecA,
typename VecB,
typename Matrix>
63 KOKKOS_INLINE_FUNCTION
auto addTensorProduct(VecA
const &a, VecB
const &b,
65 static_assert(has_size_method<VecA>::value,
66 "VecA must have a size() method.");
67 static_assert(has_size_method<VecB>::value,
68 "VecB must have a size() method.");
69 static_assert(has_static_rank<VecA>::value,
70 "VecA must have a static rank() method.");
71 static_assert(has_static_rank<VecB>::value,
72 "VecB must have a static rank() method.");
74 std::is_same_v<typename VecA::value_type, typename VecB::value_type>);
75 static_assert(VecA::rank() == 1);
76 static_assert(VecB::rank() == 1);
77 static_assert(has_extent_method<Matrix>::value,
78 "Matrix must have an extent(size_t) method.");
79 const auto M = a.size();
80 const auto N = b.size();
81 for (std::size_t i = 0; i < M; ++i) {
82 for (std::size_t j = 0; j < N; ++j) {
83 A(i, j) += b[j] * a[i];
146 template <
typename FieldAccessor,
typename ShapeType,
147 typename ElementDofHolderAccessor>
149 const size_t numMeshEnts;
150 const FieldAccessor field;
152 ElementDofHolderAccessor elm2dof;
154 static const size_t MeshEntDim = ShapeType::meshEntDim;
155 FieldElement(
size_t numMeshEntsIn,
const FieldAccessor &fieldIn,
156 const ShapeType shapeFnIn,
157 const ElementDofHolderAccessor elm2dofIn)
158 : numMeshEnts(numMeshEntsIn), field(fieldIn), shapeFn(shapeFnIn),
159 elm2dof(elm2dofIn) {}
162 template <
typename T>
struct baseType {
168 template <
typename T,
size_t N>
struct baseType<T[N]> {
169 using type =
typename baseType<T>::type;
172 Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
173 FieldAccessor::numComp>;
174 static const size_t NumComponents = FieldAccessor::numComp;
191 KOKKOS_INLINE_FUNCTION ValArray
192 getValue(
int ent, Kokkos::Array<Real, MeshEntDim> localCoord)
const {
194 assert(
static_cast<size_t>(ent) < numMeshEnts);
196 const auto shapeValues = shapeFn.getValues(localCoord);
197 for (
size_t ci = 0; ci < NumComponents; ++ci)
199 for (
auto topo : elm2dof.getTopology()) {
200 for (
size_t ni = 0; ni < shapeFn.numNodes; ++ni) {
201 for (
size_t ci = 0; ci < NumComponents; ++ci) {
202 auto map = elm2dof(ni, ci, ent, topo);
204 field(map.entity, map.node, map.component, map.topo);
205 c[ci] += fval * shapeValues[ni];
213 Kokkos::Array<typename baseType<typename FieldAccessor::BaseType>::type,
214 ShapeType::meshEntDim * ShapeType::numNodes>;
215 KOKKOS_INLINE_FUNCTION NodeArray getNodeValues(
int ent)
const {
217 for (
auto topo : elm2dof.getTopology()) {
218 for (
size_t ni = 0; ni < ShapeType::numNodes; ++ni) {
219 for (
size_t d = 0; d < ShapeType::meshEntDim; ++d) {
220 auto map = elm2dof(ni, d, ent, topo);
222 field(map.entity, map.node, map.component, map.topo);
223 c[ni * ShapeType::meshEntDim + d] = fval;
244 assert(
static_cast<size_t>(ent) < numMeshEnts);
246 const auto nodalGradients = shapeFn.getLocalGradients(ignored);
247 const auto nodeValues = getNodeValues(ent);
248 auto g = nodalGradients[0] * nodeValues[0];
249 for (
size_t i = 1; i < shapeFn.numNodes; ++i) {
250 g = g + nodalGradients[i] * nodeValues[i];
259 template <
typename Matrices>
261 static_assert(has_static_rank<Matrices>::value,
262 "Matrices must have a static rank() method.");
263 static_assert(has_extent_method<Matrices>::value,
264 "Matrices must have an extent(size_t) method.");
265 static_assert(Matrices::rank() == 3);
266 if (J.extent(1) != J.extent(2)) {
267 fail(
"getJacobianDeterminant only supports square matrices. "
268 "The given matrices have dimension %lu x %lu \n",
269 J.extent(1), J.extent(2));
271 const auto dimension = J.extent(1);
272 if (dimension > 3 || dimension < 1) {
273 fail(
"getJacobianDeterminant: invalid dimension of input matrix. "
274 "The given matrices have dimension %lu x %lu \n",
275 J.extent(0), J.extent(1));
277 if (dimension == 3) {
282 Kokkos::View<Real *> determinants(
"3dJacobianDeterminants", J.extent(0));
283 Kokkos::parallel_for(
284 J.extent(0), KOKKOS_LAMBDA(
const int i) {
285 const auto Ji = Kokkos::subview(J, i, Kokkos::ALL(), Kokkos::ALL());
286 const auto cofactorSum =
287 Ji(0, 0) * (Ji(1, 1) * Ji(2, 2) - Ji(1, 2) * Ji(2, 1)) -
288 Ji(0, 1) * (Ji(1, 0) * Ji(2, 2) - Ji(1, 2) * Ji(2, 0)) +
289 Ji(0, 2) * (Ji(1, 0) * Ji(2, 1) - Ji(1, 1) * Ji(2, 0));
290 determinants(i) = cofactorSum;
294 if (dimension == 2) {
295 if (J.extent(1) != 2) {
296 fail(
"getJacobianDeterminant only supports 2x2 matrices in 2d. "
297 "The given matrices have dimension %lu x %lu \n",
298 J.extent(1), J.extent(2));
304 Kokkos::View<Real *> determinants(
"2dJacobianDeterminants", J.extent(0));
306 Kokkos::parallel_for(
307 J.extent(0), KOKKOS_LAMBDA(
const int i) {
309 auto Ji = Kokkos::subview(J, i, Kokkos::ALL(), Kokkos::ALL());
310 const auto cross = Ji(0, 0) * Ji(1, 1) - Ji(1, 0) * Ji(0, 1);
311 const auto magnitude = Kokkos::fabs(cross);
312 determinants(i) = magnitude;
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));
345 Kokkos::View<LO *> offsets)
const {
350 Kokkos::parallel_reduce(
351 "checkCoords", numMeshEnts,
352 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
355 for (
size_t i = 0; i < localCoords.extent(1); i++) {
356 if (localCoords(ent, i) < 0)
358 sum += localCoords(ent, i);
366 fail(
"One or more of the parametric coordinates passed "
367 "to evaluate(...) were invalid\n");
370 if (localCoords.extent(0) < numMeshEnts) {
371 fail(
"The size of dimension 0 of the local coordinates input array "
372 "must be at least %zu.\n",
375 if (localCoords.extent(1) != MeshEntDim) {
376 fail(
"Dimension 1 of the input array of local coordinates "
377 "must have size = %zu.\n",
380 if (offsets.size() != numMeshEnts + 1) {
381 fail(
"The input array of offsets must have size = %zu\n",
384 if (MeshEntDim != 1 && MeshEntDim != 2 && MeshEntDim != 3) {
385 fail(
"getJacobians only currently supports 1d, 2d, and 3d meshes. Input "
387 "has %zu dimensions.\n",
390 if constexpr (MeshEntDim == 1) {
391 const auto numPts = MeshFieldUtil::getLastValue(offsets);
392 Kokkos::View<Real ***> res(
"result", numPts, 1, 1);
393 Kokkos::parallel_for(
394 numMeshEnts, KOKKOS_CLASS_LAMBDA(
const int ent) {
396 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
402 }
else if constexpr (MeshEntDim == 2 || MeshEntDim == 3) {
403 const auto numPts = MeshFieldUtil::getLastValue(offsets);
405 Kokkos::View<Real ***> res(
"result", numPts, MeshEntDim, MeshEntDim);
406 Kokkos::deep_copy(res, 0.0);
409 Kokkos::View<Real * [ShapeType::numNodes][MeshEntDim]> nodeCoords(
410 "nodeCoords", numPts);
411 Kokkos::View<Real * [ShapeType::numNodes][MeshEntDim]> nodalGradients(
412 "nodalGradients", numPts);
413 Kokkos::parallel_for(
414 numMeshEnts, KOKKOS_CLASS_LAMBDA(
const int ent) {
415 const auto vals = getNodeValues(ent);
416 assert(vals.size() == MeshEntDim * ShapeType::numNodes);
417 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
418 Kokkos::Array<Real, MeshEntDim> xi;
419 for (
size_t d = 0; d < MeshEntDim; d++)
420 xi[d] = localCoords(pt, d);
421 const auto grad = shapeFn.getLocalGradients(xi);
422 for (
size_t node = 0; node < ShapeType::numNodes; node++) {
423 for (
size_t d = 0; d < MeshEntDim; d++) {
424 nodeCoords(pt, node, d) = vals[node * MeshEntDim + d];
425 nodalGradients(pt, node, d) = grad[node * MeshEntDim + d];
431 Kokkos::parallel_for(
432 numMeshEnts, KOKKOS_LAMBDA(
const int ent) {
434 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
435 auto A = Kokkos::subview(res, pt, Kokkos::ALL(), Kokkos::ALL());
436 for (
size_t node = 0; node < ShapeType::numNodes; node++) {
438 Kokkos::subview(nodalGradients, pt, node, Kokkos::ALL());
439 auto b = Kokkos::subview(nodeCoords, pt, node, Kokkos::ALL());
440 addTensorProduct(a, b, A);
467 template <
typename FieldElement>
468 Kokkos::View<Real *[FieldElement::NumComponents]>
469 evaluate(FieldElement &fes, Kokkos::View<Real **> localCoords,
470 Kokkos::View<LO *> offsets) {
474 Kokkos::parallel_reduce(
475 "checkCoords", fes.numMeshEnts,
476 KOKKOS_LAMBDA(
const int &ent, LO &lerrors) {
479 for (
size_t i = 0; i < localCoords.extent(1); i++) {
480 if (localCoords(ent, i) < 0)
482 sum += localCoords(ent, i);
490 fail(
"One or more of the parametric coordinates passed "
491 "to evaluate(...) were invalid\n");
495 if (localCoords.extent(1) != fes.MeshEntDim) {
496 fail(
"Dimension 1 of the input array of local coordinates "
497 "must have size = %zu.\n",
500 if (offsets.size() != fes.numMeshEnts + 1) {
501 fail(
"The input array of offsets must have size = %zu\n",
502 fes.numMeshEnts + 1);
505 Kokkos::deep_copy(numLocalCoords,
506 Kokkos::subview(offsets, offsets.size() - 1));
507 if (localCoords.extent(0) !=
static_cast<size_t>(numLocalCoords)) {
508 fail(
"The size of dimension 0 of the local coordinates input array (%zu) "
509 "does not match the last entry of the offsets array (%d).\n",
510 localCoords.extent(0), numLocalCoords);
513 constexpr
const auto numComponents = FieldElement::ValArray::size();
514 const auto numPts = MeshFieldUtil::getLastValue(offsets);
515 Kokkos::View<Real *[numComponents]> res(
"result", numPts);
516 Kokkos::parallel_for(
517 fes.numMeshEnts, KOKKOS_LAMBDA(
const int ent) {
518 Kokkos::Array<Real, FieldElement::MeshEntDim> lc;
520 for (
auto pt = offsets(ent); pt < offsets(ent + 1); pt++) {
521 for (
size_t i = 0; i < localCoords.extent(1); i++)
522 lc[i] = localCoords(pt, i);
523 const auto val = fes.getValue(ent, lc);
524 for (
size_t i = 0; i < numComponents; i++)
539 template <
typename FieldElement>
540 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
541 FieldElement &fes, Kokkos::View<Real **> localCoords) {
542 const auto numPtsPerElement = 1;
543 return evaluate(fes, localCoords, numPtsPerElement);
557 template <
typename FieldElement>
558 Kokkos::View<Real *[FieldElement::NumComponents]> evaluate(
559 FieldElement &fes, Kokkos::View<Real **> localCoords,
560 size_t numPtsPerElement) {
561 const auto offsets = getOffsets(fes.numMeshEnts, numPtsPerElement);
562 return evaluate(fes, localCoords, offsets);
579 template <
typename FieldElement>
580 Kokkos::View<Real ***> getJacobians(FieldElement &fes,
581 Kokkos::View<Real **> localCoords,
582 size_t numPtsPerElement) {
583 const auto offsets = getOffsets(fes.numMeshEnts, numPtsPerElement);
584 return fes.getJacobians(localCoords, offsets);
597 template <
typename FieldElement,
typename Matrices>
598 Kokkos::View<Real *> getJacobianDeterminants(FieldElement &fes, Matrices J) {
599 return fes.getJacobianDeterminants(J);
Shape function definitions for finite element analysis.
Kokkos::Array< Real, 1 > Vector1
1D parametric coordinate vector
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