|
| 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