Skip to content

Commit 510d853

Browse files
committed
Add device-callable Gauss quadrature to gpu/ headers
GaussLegendre1D: 1-D Gauss-Legendre rules (1-7 points), device-callable. GaussQuadrature: Full topology dispatcher with n_points/point/weight methods for EDGE, QUAD, HEX (tensor product), TRI (Dunavant), and TET (Keast/Walkington). All methods are LIBMESH_DEVICE_INLINE. fillQuadrature: Host-side convenience wrapper using std::vector.
1 parent 9f1e1a8 commit 510d853

8 files changed

Lines changed: 1405 additions & 2 deletions

File tree

include/Makefile.am

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ nobase_include_HEADERS = \
1313
gpu/kokkos_fe_lagrange_2d.h \
1414
gpu/kokkos_fe_lagrange_3d.h \
1515
gpu/kokkos_fe_monomial.h \
16-
gpu/kokkos_fe_face_map.h
16+
gpu/kokkos_fe_face_map.h \
17+
gpu/kokkos_fe_map.h \
18+
gpu/kokkos_quadrature.h
1719
endif
1820

1921
# special handholding for prefix_config.m4 generated files

include/base/libmesh_common.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
// The library configuration options
3131
#include "libmesh/libmesh_config.h"
3232

33+
// Device compilation support — must be included before assert macros
34+
// so that LIBMESH_DEVICE_ASSERT is available for the Kokkos path.
35+
#include "libmesh/libmesh_device.h"
36+
3337
// Use actual timestamps or constant dummies (to aid ccache)
3438
#ifdef LIBMESH_ENABLE_TIMESTAMPS
3539
# define LIBMESH_TIME __TIME__
@@ -287,7 +291,13 @@ extern bool warned_about_auto_ptr;
287291
#endif
288292

289293
// The libmesh_assert() macro acts like C's assert(), but throws a
290-
// libmesh_error() (including stack trace, etc) instead of just exiting
294+
// libmesh_error() (including stack trace, etc) instead of just exiting.
295+
//
296+
// In .K translation units (LIBMESH_KOKKOS_COMPILATION defined),
297+
// LIBMESH_DEVICE_ASSERT is provided by libmesh_device.h using
298+
// printf + Kokkos::abort() — device-safe across CUDA/HIP/SYCL.
299+
// The assert macros delegate to it so that both host and device
300+
// code in the same file get assertion checking.
291301
#ifdef NDEBUG
292302

293303
#define libmesh_assert_msg(asserted, msg) ((void) 0)
@@ -299,6 +309,18 @@ extern bool warned_about_auto_ptr;
299309
#define libmesh_assert_less_equal_msg(expr1,expr2, msg) ((void) 0)
300310
#define libmesh_assert_greater_equal_msg(expr1,expr2, msg) ((void) 0)
301311

312+
#elif defined(LIBMESH_DEVICE_ASSERT)
313+
314+
// Kokkos compilation: use the device-safe assert from libmesh_device.h.
315+
#define libmesh_assert_msg(asserted, msg) LIBMESH_DEVICE_ASSERT(asserted)
316+
#define libmesh_exceptionless_assert_msg(asserted, msg) LIBMESH_DEVICE_ASSERT(asserted)
317+
#define libmesh_assert_equal_to_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) == (expr2))
318+
#define libmesh_assert_not_equal_to_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) != (expr2))
319+
#define libmesh_assert_less_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) < (expr2))
320+
#define libmesh_assert_greater_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) > (expr2))
321+
#define libmesh_assert_less_equal_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) <= (expr2))
322+
#define libmesh_assert_greater_equal_msg(expr1,expr2, msg) LIBMESH_DEVICE_ASSERT((expr1) >= (expr2))
323+
302324
#else
303325

304326
#define libmesh_assertion_types(expr1,expr2) \

include/base/libmesh_device.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,24 @@
2626
// translation units it expands to plain `inline`.
2727
#ifdef LIBMESH_KOKKOS_COMPILATION
2828
# include <Kokkos_Macros.hpp>
29+
# include <Kokkos_Abort.hpp>
2930
# define LIBMESH_DEVICE_INLINE KOKKOS_INLINE_FUNCTION
31+
32+
// Device-safe assert: uses printf (supported on CUDA/HIP) and
33+
// Kokkos::abort() for backend-portable device termination.
34+
// Defined here (not in libmesh_common.h) because Kokkos headers
35+
// are only available in .K translation units.
36+
# ifndef NDEBUG
37+
# define LIBMESH_DEVICE_ASSERT(asserted) \
38+
do { if (!(asserted)) { \
39+
printf("libMesh assert failed: %s, file %s, line %d\n", \
40+
#asserted, __FILE__, __LINE__); \
41+
::Kokkos::abort("libmesh_assert failed"); \
42+
} } while (0)
43+
# else
44+
# define LIBMESH_DEVICE_ASSERT(asserted) ((void) 0)
45+
# endif
46+
3047
#else
3148
# define LIBMESH_DEVICE_INLINE inline
3249
#endif

include/gpu/kokkos_fe_evaluator.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,28 @@ lagrangeTopologyForClassAndOrder(FEElemClass cls, unsigned int order)
104104
// LAGRANGE_MAP is supported; RATIONAL_BERNSTEIN_MAP requires additional
105105
// rational-weight data that is not yet threaded through the device path.
106106

107+
// ── Compile-time topology versions (preferred for GPU) ───────────────────
108+
// Template on FEFamily and ElemType so nvcc only instantiates the specific
109+
// FEEvaluator specialization. No topology switch means no stack pressure.
110+
111+
/// Compile-time map shape evaluation.
112+
template <libMesh::FEFamily family, libMesh::ElemType topo>
113+
LIBMESH_DEVICE_INLINE Real
114+
nativeMapShape(unsigned int i, Real xi, Real eta, Real zeta)
115+
{
116+
return FEEvaluator<family, topo>::shape(i, xi, eta, zeta);
117+
}
118+
119+
/// Compile-time map gradient evaluation.
120+
template <libMesh::FEFamily family, libMesh::ElemType topo>
121+
LIBMESH_DEVICE_INLINE Real3
122+
nativeGradMapShape(unsigned int i, Real xi, Real eta, Real zeta)
123+
{
124+
return FEEvaluator<family, topo>::grad_shape(i, xi, eta, zeta);
125+
}
126+
127+
// ── Runtime topology versions (larger GPU stack usage) ───────────────────
128+
107129
/// Evaluate the i-th geometric map shape function at (xi, eta, zeta).
108130
LIBMESH_DEVICE_INLINE Real
109131
nativeMapShape(libMesh::ElemMappingType mapping_type,

include/gpu/kokkos_fe_map.h

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Kokkos device-compatible physical map evaluation.
2+
//
3+
// All functions are LIBMESH_DEVICE_INLINE — callable from both host and GPU.
4+
//
5+
// Two API levels:
6+
// 1. Template on ElemType (preferred): eliminates the topology switch at
7+
// compile time, producing small inlined functions with no stack pressure.
8+
// 2. Runtime ElemType dispatch: convenient but requires increased CUDA
9+
// stack size due to the large switch in nativeMapShape.
10+
//
11+
// Given node coordinates and a reference-space point, these functions compute:
12+
// - Physical coordinates (xyz)
13+
// - Jacobian matrix (reference -> physical)
14+
// - Determinant and JxW
15+
// - Outward normal (for face integrals)
16+
17+
#pragma once
18+
19+
#include "gpu/kokkos_fe_evaluator.h"
20+
#include "gpu/kokkos_scalar_types.h"
21+
22+
namespace libMesh::Kokkos
23+
{
24+
25+
// =========================================================================
26+
// Compile-time dispatch (preferred for GPU — no switch overhead)
27+
//
28+
// Template on FEFamily and ElemType so nvcc only instantiates the specific
29+
// FEEvaluator specialization. No topology switch means no stack pressure.
30+
// =========================================================================
31+
32+
/// Compute physical coordinate: xyz = sum_i( phi_i(ref) * node_i )
33+
template <libMesh::FEFamily family, libMesh::ElemType topo>
34+
LIBMESH_DEVICE_INLINE Real3
35+
physicalPoint(const Real3 * nodes,
36+
unsigned int n_nodes,
37+
Real xi, Real eta, Real zeta)
38+
{
39+
Real3 xyz(0, 0, 0);
40+
for (unsigned int i = 0; i < n_nodes; ++i)
41+
xyz += nativeMapShape<family, topo>(i, xi, eta, zeta) * nodes[i];
42+
return xyz;
43+
}
44+
45+
/// Compute Jacobian matrix: J_ij = d(x_i)/d(xi_j)
46+
template <libMesh::FEFamily family, libMesh::ElemType topo>
47+
LIBMESH_DEVICE_INLINE Real33
48+
jacobian(const Real3 * nodes,
49+
unsigned int n_nodes,
50+
Real xi, Real eta, Real zeta)
51+
{
52+
Real33 J(0.0);
53+
for (unsigned int k = 0; k < n_nodes; ++k)
54+
J += nativeGradMapShape<family, topo>(k, xi, eta, zeta)
55+
.cartesian_product(nodes[k]);
56+
return J;
57+
}
58+
59+
/// Compute physical point and Jacobian together (single node loop).
60+
template <libMesh::FEFamily family, libMesh::ElemType topo>
61+
LIBMESH_DEVICE_INLINE void
62+
physicalPointAndJacobian(const Real3 * nodes,
63+
unsigned int n_nodes,
64+
Real xi, Real eta, Real zeta,
65+
Real3 & xyz,
66+
Real33 & J)
67+
{
68+
xyz = Real3(0, 0, 0);
69+
J = Real33(0.0);
70+
for (unsigned int k = 0; k < n_nodes; ++k)
71+
{
72+
const Real phi = nativeMapShape<family, topo>(k, xi, eta, zeta);
73+
const Real3 grad = nativeGradMapShape<family, topo>(k, xi, eta, zeta);
74+
xyz += phi * nodes[k];
75+
J += grad.cartesian_product(nodes[k]);
76+
}
77+
}
78+
79+
/// Face Jacobian from side node positions.
80+
template <libMesh::FEFamily family, libMesh::ElemType face_topo>
81+
LIBMESH_DEVICE_INLINE Real33
82+
faceJacobian(const Real3 * face_nodes,
83+
unsigned int n_face_nodes,
84+
Real xi, Real eta, Real zeta)
85+
{
86+
Real33 J(0.0);
87+
for (unsigned int k = 0; k < n_face_nodes; ++k)
88+
J += nativeGradMapShape<family, face_topo>(k, xi, eta, zeta)
89+
.cartesian_product(face_nodes[k]);
90+
return J;
91+
}
92+
93+
// =========================================================================
94+
// Runtime topology dispatch (convenient, but larger GPU stack usage)
95+
// =========================================================================
96+
97+
/// Compute physical coordinate (runtime topology).
98+
LIBMESH_DEVICE_INLINE Real3
99+
physicalPoint(libMesh::ElemMappingType mapping_type,
100+
libMesh::ElemType topo,
101+
const Real3 * nodes,
102+
unsigned int n_nodes,
103+
Real xi, Real eta, Real zeta)
104+
{
105+
Real3 xyz(0, 0, 0);
106+
for (unsigned int i = 0; i < n_nodes; ++i)
107+
xyz += nativeMapShape(mapping_type, topo, i, xi, eta, zeta) * nodes[i];
108+
return xyz;
109+
}
110+
111+
/// Compute Jacobian matrix (runtime topology).
112+
LIBMESH_DEVICE_INLINE Real33
113+
jacobian(libMesh::ElemMappingType mapping_type,
114+
libMesh::ElemType topo,
115+
const Real3 * nodes,
116+
unsigned int n_nodes,
117+
Real xi, Real eta, Real zeta)
118+
{
119+
Real33 J(0.0);
120+
for (unsigned int k = 0; k < n_nodes; ++k)
121+
J += nativeGradMapShape(mapping_type, topo, k, xi, eta, zeta)
122+
.cartesian_product(nodes[k]);
123+
return J;
124+
}
125+
126+
/// Compute physical point and Jacobian together (runtime topology).
127+
LIBMESH_DEVICE_INLINE void
128+
physicalPointAndJacobian(libMesh::ElemMappingType mapping_type,
129+
libMesh::ElemType topo,
130+
const Real3 * nodes,
131+
unsigned int n_nodes,
132+
Real xi, Real eta, Real zeta,
133+
Real3 & xyz,
134+
Real33 & J)
135+
{
136+
xyz = Real3(0, 0, 0);
137+
J = Real33(0.0);
138+
for (unsigned int k = 0; k < n_nodes; ++k)
139+
{
140+
const Real phi = nativeMapShape(mapping_type, topo, k, xi, eta, zeta);
141+
const Real3 grad = nativeGradMapShape(mapping_type, topo, k, xi, eta, zeta);
142+
xyz += phi * nodes[k];
143+
J += grad.cartesian_product(nodes[k]);
144+
}
145+
}
146+
147+
/// Face Jacobian (runtime topology).
148+
LIBMESH_DEVICE_INLINE Real33
149+
faceJacobian(libMesh::ElemMappingType mapping_type,
150+
libMesh::ElemType face_topo,
151+
const Real3 * face_nodes,
152+
unsigned int n_face_nodes,
153+
Real xi, Real eta, Real zeta)
154+
{
155+
Real33 J(0.0);
156+
for (unsigned int k = 0; k < n_face_nodes; ++k)
157+
J += nativeGradMapShape(mapping_type, face_topo, k, xi, eta, zeta)
158+
.cartesian_product(face_nodes[k]);
159+
return J;
160+
}
161+
162+
// =========================================================================
163+
// Geometry helpers (topology-independent)
164+
// =========================================================================
165+
166+
/// Volume element JxW: |det(J)| * quadrature_weight
167+
LIBMESH_DEVICE_INLINE Real
168+
volumeJxW(const Real33 & J, unsigned int dim, Real quad_weight)
169+
{
170+
return J.determinant(dim) * quad_weight;
171+
}
172+
173+
/// Face JxW: surface measure * quadrature_weight
174+
/// 3D: ||J_row0 x J_row1|| * weight
175+
/// 2D: ||J_row0|| * weight
176+
/// 1D: weight (face is a point)
177+
LIBMESH_DEVICE_INLINE Real
178+
faceJxW(const Real33 & J, unsigned int parent_dim, Real quad_weight)
179+
{
180+
if (parent_dim == 3)
181+
return J.row(0).cross(J.row(1)).norm() * quad_weight;
182+
else if (parent_dim == 2)
183+
return J.row(0).norm() * quad_weight;
184+
else
185+
return quad_weight;
186+
}
187+
188+
/// Outward unit normal from the face Jacobian.
189+
LIBMESH_DEVICE_INLINE Real3
190+
faceNormal(const Real33 & J, unsigned int parent_dim)
191+
{
192+
Real3 n(0, 0, 0);
193+
if (parent_dim == 3)
194+
n = J.row(0).cross(J.row(1));
195+
else if (parent_dim == 2)
196+
{
197+
const Real3 t = J.row(0);
198+
n = Real3(-t(1), t(0), 0.0);
199+
}
200+
else
201+
return Real3(1.0, 0.0, 0.0);
202+
203+
const Real len = n.norm();
204+
if (len > 0.0)
205+
n *= 1.0 / len;
206+
return n;
207+
}
208+
209+
} // namespace libMesh::Kokkos

0 commit comments

Comments
 (0)