Skip to content

Commit fc4ef7d

Browse files
authored
Merge pull request #65 from SCOREC/jk/linearTet
add quadratic and linear tet shape functions
2 parents 05fc74d + af6026b commit fc4ef7d

File tree

5 files changed

+314
-8
lines changed

5 files changed

+314
-8
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ meshfields_add_exe(CountIntegrator test/testCountIntegrator.cpp)
170170
meshfields_add_exe(OmegahElementTests test/testOmegahElement.cpp)
171171
meshfields_add_exe(ExceptionTest test/testExceptions.cpp)
172172
meshfields_add_exe(PointMapping test/testPointMapping.cpp)
173+
meshfields_add_exe(OmegahTetTest test/testOmegahTet.cpp)
173174

174175
if(MeshFields_USE_Cabana)
175176
meshfields_add_exe(ControllerPerformance test/testControllerPerformance.cpp)
@@ -186,6 +187,7 @@ test_func(ElementJacobian2d ./ElementJacobian2d)
186187
test_func(CountIntegrator ./CountIntegrator)
187188
test_func(OmegahElementTests ./OmegahElementTests)
188189
test_func(PointMapping ./PointMapping)
190+
test_func(OmegahTetTest, ./OmegahTetTest)
189191
if(MeshFields_USE_EXCEPTIONS)
190192
# exception caught - no error
191193
test_func(ExceptionTest ./ExceptionTest)

src/MeshField.hpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,37 @@ struct LinearTriangleToVertexField {
8989
return {0, triCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
9090
}
9191
};
92+
struct LinearTetrahedronToVertexField {
93+
Omega_h::LOs tetVerts;
94+
LinearTetrahedronToVertexField(Omega_h::Mesh &mesh)
95+
: tetVerts(mesh.ask_elem_verts()) {
96+
if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
97+
MeshField::fail(
98+
"The mesh passed to %s must be 3D and simplex (tetrahedron)\n",
99+
__func__);
100+
}
101+
}
102+
KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
103+
getTopology() const {
104+
return {MeshField::Tetrahedron};
105+
}
106+
107+
KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
108+
operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
109+
MeshField::LO tet, MeshField::Mesh_Topology topo) const {
110+
assert(topo == MeshField::Tetrahedron);
111+
const auto tetDim = 3;
112+
const auto vtxDim = 0;
113+
const auto ignored = -1;
114+
const auto localVtxIdx =
115+
(Omega_h::simplex_down_template(tetDim, vtxDim, tetNodeIdx, ignored) +
116+
3) %
117+
4;
118+
const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
119+
const MeshField::LO vtx = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
120+
return {0, tetCompIdx, vtx, MeshField::Vertex}; // node, comp, ent, topo
121+
}
122+
};
92123
struct QuadraticTriangleToField {
93124
Omega_h::LOs triVerts;
94125
Omega_h::LOs triEdges;
@@ -151,6 +182,59 @@ struct QuadraticTriangleToField {
151182
}
152183
};
153184

185+
struct QuadraticTetrahedronToField {
186+
Omega_h::LOs tetVerts;
187+
Omega_h::LOs tetEdges;
188+
QuadraticTetrahedronToField(Omega_h::Mesh &mesh)
189+
: tetVerts(mesh.ask_elem_verts()),
190+
tetEdges(mesh.ask_down(mesh.dim(), 1).ab2b) {
191+
if (mesh.dim() != 3 && mesh.family() != OMEGA_H_SIMPLEX) {
192+
MeshField::fail(
193+
"The mesh passed to %s must be 3D and simplex (tetrahedron)",
194+
__func__);
195+
}
196+
}
197+
198+
KOKKOS_FUNCTION Kokkos::Array<MeshField::Mesh_Topology, 1>
199+
getTopology() const {
200+
return {MeshField::Tetrahedron};
201+
}
202+
203+
KOKKOS_FUNCTION MeshField::ElementToDofHolderMap
204+
operator()(MeshField::LO tetNodeIdx, MeshField::LO tetCompIdx,
205+
MeshField::LO tet, MeshField::Mesh_Topology topo) const {
206+
assert(topo == MeshField::Tetrahedron);
207+
const MeshField::LO tetNode2DofHolder[10] = {0, 1, 2, 3, 3, 4, 5, 0, 2, 1};
208+
const MeshField::Mesh_Topology tetNode2DofHolderTopo[10] = {
209+
MeshField::Vertex, MeshField::Vertex, MeshField::Vertex,
210+
MeshField::Vertex, MeshField::Edge, MeshField::Edge,
211+
MeshField::Edge, MeshField::Edge, MeshField::Edge,
212+
MeshField::Edge};
213+
const auto dofHolderIdx = tetNode2DofHolder[tetNodeIdx];
214+
const auto dofHolderTopo = tetNode2DofHolderTopo[tetNodeIdx];
215+
Omega_h::LO osh_ent;
216+
if (dofHolderTopo == MeshField::Vertex) {
217+
const auto tetDim = 3;
218+
const auto vtxDim = 0;
219+
const auto ignored = -1;
220+
const auto localVtxIdx = (Omega_h::simplex_down_template(
221+
tetDim, vtxDim, dofHolderIdx, ignored) +
222+
3) %
223+
4;
224+
const auto tetToVtxDegree = Omega_h::simplex_degree(tetDim, vtxDim);
225+
osh_ent = tetVerts[(tet * tetToVtxDegree) + localVtxIdx];
226+
} else if (dofHolderTopo == MeshField::Edge) {
227+
const auto tetDim = 3;
228+
const auto edgeDim = 1;
229+
const auto tetToEdgeDegree = Omega_h::simplex_degree(tetDim, edgeDim);
230+
osh_ent = tetEdges[(tet * tetToEdgeDegree) + dofHolderIdx];
231+
} else {
232+
assert(false);
233+
}
234+
return {0, tetCompIdx, osh_ent, dofHolderTopo};
235+
}
236+
};
237+
154238
template <int ShapeOrder> auto getTriangleElement(Omega_h::Mesh &mesh) {
155239
static_assert(ShapeOrder == 1 || ShapeOrder == 2);
156240
if constexpr (ShapeOrder == 1) {
@@ -169,6 +253,24 @@ template <int ShapeOrder> auto getTriangleElement(Omega_h::Mesh &mesh) {
169253
QuadraticTriangleToField(mesh)};
170254
}
171255
}
256+
template <int ShapeOrder> auto getTetrahedronElement(Omega_h::Mesh &mesh) {
257+
static_assert(ShapeOrder == 1 || ShapeOrder == 2);
258+
if constexpr (ShapeOrder == 1) {
259+
struct result {
260+
MeshField::LinearTetrahedronShape shp;
261+
LinearTetrahedronToVertexField map;
262+
};
263+
return result{MeshField::LinearTetrahedronShape(),
264+
LinearTetrahedronToVertexField(mesh)};
265+
} else if constexpr (ShapeOrder == 2) {
266+
struct result {
267+
MeshField::QuadraticTetrahedronShape shp;
268+
QuadraticTetrahedronToField map;
269+
};
270+
return result{MeshField::QuadraticTetrahedronShape(),
271+
QuadraticTetrahedronToField(mesh)};
272+
}
273+
}
172274

173275
} // namespace Omegah
174276

@@ -252,6 +354,32 @@ class OmegahMeshField {
252354
auto eval = MeshField::evaluate(f, localCoords, offsets);
253355
return eval;
254356
}
357+
358+
template <typename ViewType, typename ShapeField>
359+
auto tetrahedronLocalPointEval(ViewType localCoords, size_t NumPtsPerElem,
360+
ShapeField field) {
361+
auto offsets = createOffsets(meshInfo.numTet, NumPtsPerElem);
362+
auto eval = tetrahedronLocalPointEval(localCoords, offsets, field);
363+
return eval;
364+
}
365+
366+
template <typename ViewType, typename ShapeField>
367+
auto tetrahedronLocalPointEval(ViewType localCoords,
368+
Kokkos::View<LO *> offsets, ShapeField field) {
369+
const auto MeshDim = 3;
370+
if (mesh.dim() != MeshDim) {
371+
MeshField::fail("input mesh must be 3d\n");
372+
}
373+
const auto ShapeOrder = ShapeField::Order;
374+
if (ShapeOrder != 1 && ShapeOrder != 2) {
375+
MeshField::fail("input field order must be 1 or 2\n");
376+
}
377+
const auto [shp, map] = Omegah::getTetrahedronElement<ShapeOrder>(mesh);
378+
MeshField::FieldElement<ShapeField, decltype(shp), decltype(map)> f(
379+
meshInfo.numTet, field, shp, map);
380+
auto eval = MeshField::evaluate(f, localCoords, offsets);
381+
return eval;
382+
}
255383
};
256384

257385
} // namespace MeshField

src/MeshField_Shape.hpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,34 @@ struct LinearTriangleCoordinateShape {
105105
}
106106
};
107107

108+
struct LinearTetrahedronShape {
109+
static const size_t numNodes = 4;
110+
static const size_t meshEntDim = 3;
111+
constexpr static Mesh_Topology DofHolders[1] = {Vertex};
112+
constexpr static size_t Order = 1;
113+
114+
KOKKOS_INLINE_FUNCTION
115+
Kokkos::Array<Real, numNodes> getValues(Vector4 const &xi) const {
116+
assert(greaterThanOrEqualZero(xi));
117+
assert(sumsToOne(xi));
118+
// clang-format off
119+
return {1 - xi[0] - xi[1] - xi[2],
120+
xi[0], xi[1],
121+
xi[2]};
122+
// clang-format on
123+
}
124+
125+
KOKKOS_INLINE_FUNCTION
126+
Kokkos::Array<Real, meshEntDim * numNodes> getLocalGradients() const {
127+
// clang-format off
128+
return {-1, -1, -1,
129+
1, 0, 0,
130+
0, 1, 0,
131+
0, 0, 1};
132+
// clang-format on
133+
}
134+
};
135+
108136
struct QuadraticTriangleShape {
109137
static const size_t numNodes = 6;
110138
static const size_t meshEntDim = 2;
@@ -151,7 +179,8 @@ struct QuadraticTetrahedronShape {
151179
constexpr static size_t NumDofHolders[2] = {4, 6};
152180
constexpr static size_t DofsPerHolder[2] = {1, 1};
153181
constexpr static size_t Order = 2;
154-
182+
// ordering taken from mfem
183+
// see mfem/mfem fem/fe/fe_fixed_order.cpp @597cba8
155184
KOKKOS_INLINE_FUNCTION
156185
Kokkos::Array<Real, numNodes> getValues(Vector4 const &xi) const {
157186
assert(greaterThanOrEqualZero(xi));
@@ -163,9 +192,9 @@ struct QuadraticTetrahedronShape {
163192
xi[1]*(2*xi[1]-1),
164193
xi[2]*(2*xi[2]-1),
165194
4*xi[0]*xi3,
166-
4*xi[0]*xi[1],
167195
4*xi[1]*xi3,
168196
4*xi[2]*xi3,
197+
4*xi[0]*xi[1],
169198
4*xi[2]*xi[0],
170199
4*xi[1]*xi[2]};
171200
// clang-format on
@@ -183,9 +212,9 @@ struct QuadraticTetrahedronShape {
183212
0,4*xi[1]-1,0,
184213
0,0,4*xi[2]-1,
185214
4*xi3-4*xi[0],-4*xi[0],-4*xi[0],
186-
4*xi[1],4*xi[0],0,
187215
-4*xi[1],4*xi3-4*xi[1],-4*xi[1],
188216
-4*xi[2],-4*xi[2],4*xi3-4*xi[2],
217+
4*xi[1],4*xi[0],0,
189218
4*xi[2],0,4*xi[0],
190219
0,4*xi[2],4*xi[1]};
191220
// clang-format on

src/MeshField_ShapeField.hpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ auto CreateLagrangeField(const MeshInfo &meshInfo) {
172172
static_assert((dim == 1 || dim == 2 || dim == 3),
173173
"CreateLagrangeField only supports 1d, 2d, and 3d meshes\n");
174174
using MemorySpace = typename ExecutionSpace::memory_space;
175-
if constexpr (order == 1 && (dim == 1 || dim == 2)) {
175+
if constexpr (order == 1 && (dim == 1 || dim == 2 || dim == 3)) {
176176
if (meshInfo.numVtx <= 0) {
177177
fail("mesh has no vertices\n");
178178
}
@@ -201,8 +201,12 @@ auto CreateLagrangeField(const MeshInfo &meshInfo) {
201201
#endif
202202
auto vtxField = MeshField::makeField<Ctrlr, 0>(kk_ctrl);
203203
using LA = LinearAccessor<decltype(vtxField)>;
204-
using LinearLagrangeShapeField =
205-
ShapeField<numComp, Ctrlr, LinearTriangleShape, LA>;
204+
// clang-format off
205+
using LinearLagrangeShapeField = std::conditional_t<
206+
dim == 3,
207+
ShapeField<numComp, Ctrlr, LinearTetrahedronShape, LA>,
208+
ShapeField<numComp, Ctrlr, LinearTriangleShape, LA>>;
209+
// clang-format on
206210
LinearLagrangeShapeField llsf(kk_ctrl, meshInfo, {vtxField});
207211
return llsf;
208212
} else if constexpr (order == 2 && (dim == 2 || dim == 3)) {
@@ -242,8 +246,12 @@ auto CreateLagrangeField(const MeshInfo &meshInfo) {
242246
auto vtxField = MeshField::makeField<Ctrlr, 0>(kk_ctrl);
243247
auto edgeField = MeshField::makeField<Ctrlr, 1>(kk_ctrl);
244248
using QA = QuadraticAccessor<decltype(vtxField), decltype(edgeField)>;
245-
using QuadraticLagrangeShapeField =
246-
ShapeField<numComp, Ctrlr, QuadraticTriangleShape, QA>;
249+
// clang-format off
250+
using QuadraticLagrangeShapeField = std::conditional_t<
251+
dim == 3,
252+
ShapeField<numComp, Ctrlr, QuadraticTetrahedronShape, QA>,
253+
ShapeField<numComp, Ctrlr, QuadraticTriangleShape, QA>>;
254+
// clang-format on
247255
QuadraticLagrangeShapeField qlsf(kk_ctrl, meshInfo, {vtxField, edgeField});
248256
return qlsf;
249257
} else {

0 commit comments

Comments
 (0)