15aed82e4SJeremy L Thompson // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors. 213fa47b2SJames Wright // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 313fa47b2SJames Wright // 413fa47b2SJames Wright // SPDX-License-Identifier: BSD-2-Clause 513fa47b2SJames Wright // 613fa47b2SJames Wright // This file is part of CEED: http://github.com/ceed 7509d4af6SJeremy L Thompson #pragma once 813fa47b2SJames Wright 9*c0b5abf0SJeremy L Thompson #include <ceed/types.h> 10*c0b5abf0SJeremy L Thompson #ifndef CEED_RUNNING_JIT_PASS 11c9c2c079SJeremy L Thompson #include <math.h> 12*c0b5abf0SJeremy L Thompson #endif 1313fa47b2SJames Wright 1413fa47b2SJames Wright #ifndef M_PI 1513fa47b2SJames Wright #define M_PI 3.14159265358979323846 1613fa47b2SJames Wright #endif 1713fa47b2SJames Wright 1813fa47b2SJames Wright CEED_QFUNCTION_HELPER CeedScalar Max(CeedScalar a, CeedScalar b) { return a < b ? b : a; } 1913fa47b2SJames Wright CEED_QFUNCTION_HELPER CeedScalar Min(CeedScalar a, CeedScalar b) { return a < b ? a : b; } 2013fa47b2SJames Wright 21dc9b5c4aSJames Wright CEED_QFUNCTION_HELPER void SwapScalar(CeedScalar *a, CeedScalar *b) { 22dc9b5c4aSJames Wright CeedScalar temp = *a; 23dc9b5c4aSJames Wright *a = *b; 24dc9b5c4aSJames Wright *b = temp; 25dc9b5c4aSJames Wright } 26dc9b5c4aSJames Wright 2713fa47b2SJames Wright CEED_QFUNCTION_HELPER CeedScalar Square(CeedScalar x) { return x * x; } 2813fa47b2SJames Wright CEED_QFUNCTION_HELPER CeedScalar Cube(CeedScalar x) { return x * x * x; } 2913fa47b2SJames Wright 30530ad8c4SKenneth E. Jansen // @brief Scale vector of length N by scalar alpha 31530ad8c4SKenneth E. Jansen CEED_QFUNCTION_HELPER void ScaleN(CeedScalar *u, const CeedScalar alpha, const CeedInt N) { 32a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) u[i] *= alpha; 33a455f92dSJames Wright } 34a455f92dSJames Wright 35a455f92dSJames Wright // @brief Set vector of length N to a value alpha 36a455f92dSJames Wright CEED_QFUNCTION_HELPER void SetValueN(CeedScalar *u, const CeedScalar alpha, const CeedInt N) { 37a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) u[i] = alpha; 38a455f92dSJames Wright } 39a455f92dSJames Wright 40a455f92dSJames Wright // @brief Copy N elements from x to y 41a455f92dSJames Wright CEED_QFUNCTION_HELPER void CopyN(const CeedScalar *x, CeedScalar *y, const CeedInt N) { CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) y[i] = x[i]; } 42a455f92dSJames Wright 43a455f92dSJames Wright // @brief Copy 3x3 matrix from A to B 44a455f92dSJames Wright CEED_QFUNCTION_HELPER void CopyMat3(const CeedScalar A[3][3], CeedScalar B[3][3]) { CopyN((const CeedScalar *)A, (CeedScalar *)B, 9); } 45a455f92dSJames Wright 46a455f92dSJames Wright // @brief Dot product of vectors with N elements 47a455f92dSJames Wright CEED_QFUNCTION_HELPER CeedScalar DotN(const CeedScalar *u, const CeedScalar *v, const CeedInt N) { 48a455f92dSJames Wright CeedScalar output = 0; 49a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) output += u[i] * v[i]; 50a455f92dSJames Wright return output; 51530ad8c4SKenneth E. Jansen } 52530ad8c4SKenneth E. Jansen 5313fa47b2SJames Wright // @brief Dot product of 3 element vectors 54be91e165SJames Wright CEED_QFUNCTION_HELPER CeedScalar Dot3(const CeedScalar *u, const CeedScalar *v) { return u[0] * v[0] + u[1] * v[1] + u[2] * v[2]; } 5513fa47b2SJames Wright 56a455f92dSJames Wright // @brief Cross product of vectors with 3 elements 57a455f92dSJames Wright CEED_QFUNCTION_HELPER void Cross3(const CeedScalar u[3], const CeedScalar v[3], CeedScalar w[3]) { 58a455f92dSJames Wright w[0] = (u[1] * v[2]) - (u[2] * v[1]); 59a455f92dSJames Wright w[1] = (u[2] * v[0]) - (u[0] * v[2]); 60a455f92dSJames Wright w[2] = (u[0] * v[1]) - (u[1] * v[0]); 61a455f92dSJames Wright } 62a455f92dSJames Wright 63a455f92dSJames Wright // @brief Curl of vector given its gradient 64a455f92dSJames Wright CEED_QFUNCTION_HELPER void Curl3(const CeedScalar gradient[3][3], CeedScalar v[3]) { 65a455f92dSJames Wright v[0] = gradient[2][1] - gradient[1][2]; 66a455f92dSJames Wright v[1] = gradient[0][2] - gradient[2][0]; 67a455f92dSJames Wright v[2] = gradient[1][0] - gradient[0][1]; 68a455f92dSJames Wright } 69a455f92dSJames Wright 70a455f92dSJames Wright // @brief Matrix vector product, b = Ax + b. A is NxM, x is M, b is N 71a455f92dSJames Wright CEED_QFUNCTION_HELPER void MatVecNM(const CeedScalar *A, const CeedScalar *x, const CeedInt N, const CeedInt M, const CeedTransposeMode transpose_A, 72a455f92dSJames Wright CeedScalar *b) { 73a455f92dSJames Wright switch (transpose_A) { 74a455f92dSJames Wright case CEED_NOTRANSPOSE: 75a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) b[i] += DotN(&A[i * M], x, M); 76a455f92dSJames Wright break; 77a455f92dSJames Wright case CEED_TRANSPOSE: 78a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < M; i++) { CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) b[i] += A[j * M + i] * x[j]; } 79a455f92dSJames Wright break; 80a455f92dSJames Wright } 81a455f92dSJames Wright } 82a455f92dSJames Wright 83a455f92dSJames Wright // @brief 3x3 Matrix vector product b = Ax + b. 84a455f92dSJames Wright CEED_QFUNCTION_HELPER void MatVec3(const CeedScalar A[3][3], const CeedScalar x[3], const CeedTransposeMode transpose_A, CeedScalar b[3]) { 85a455f92dSJames Wright MatVecNM((const CeedScalar *)A, (const CeedScalar *)x, 3, 3, transpose_A, (CeedScalar *)b); 86a455f92dSJames Wright } 87a455f92dSJames Wright 88a455f92dSJames Wright // @brief Matrix-Matrix product, B = DA + B, where D is diagonal. 89a455f92dSJames Wright // @details A is NxM, D is diagonal NxN, represented by a vector of length N, and B is NxM. Optionally, A may be transposed. 90a455f92dSJames Wright CEED_QFUNCTION_HELPER void MatDiagNM(const CeedScalar *A, const CeedScalar *D, const CeedInt N, const CeedInt M, const CeedTransposeMode transpose_A, 91a455f92dSJames Wright CeedScalar *B) { 92a455f92dSJames Wright switch (transpose_A) { 93a455f92dSJames Wright case CEED_NOTRANSPOSE: 94a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) { CeedPragmaSIMD for (CeedInt j = 0; j < M; j++) B[i * M + j] += D[i] * A[i * M + j]; } 95a455f92dSJames Wright break; 96a455f92dSJames Wright case CEED_TRANSPOSE: 97a455f92dSJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < M; i++) { CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) B[i * N + j] += D[i] * A[j * M + i]; } 98a455f92dSJames Wright break; 99a455f92dSJames Wright } 100a455f92dSJames Wright } 101a455f92dSJames Wright 102a455f92dSJames Wright // @brief 3x3 Matrix-Matrix product, B = DA + B, where D is diagonal. 103a455f92dSJames Wright // @details Optionally, A may be transposed. 104a455f92dSJames Wright CEED_QFUNCTION_HELPER void MatDiag3(const CeedScalar A[3][3], const CeedScalar D[3], const CeedTransposeMode transpose_A, CeedScalar B[3][3]) { 105a455f92dSJames Wright MatDiagNM((const CeedScalar *)A, (const CeedScalar *)D, 3, 3, transpose_A, (CeedScalar *)B); 106a455f92dSJames Wright } 10798d9a7e6SJames Wright // @brief NxN Matrix-Matrix product, C = AB + C 10898d9a7e6SJames Wright CEED_QFUNCTION_HELPER void MatMatN(const CeedScalar *A, const CeedScalar *B, const CeedInt N, const CeedTransposeMode transpose_A, 10998d9a7e6SJames Wright const CeedTransposeMode transpose_B, CeedScalar *C) { 110a455f92dSJames Wright switch (transpose_A) { 111a455f92dSJames Wright case CEED_NOTRANSPOSE: 112a455f92dSJames Wright switch (transpose_B) { 113a455f92dSJames Wright case CEED_NOTRANSPOSE: 11498d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) { 11598d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) { 11698d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt k = 0; k < N; k++) C[i * N + j] += A[i * N + k] * B[k * N + j]; 11798d9a7e6SJames Wright } 118a455f92dSJames Wright } 119a455f92dSJames Wright break; 120a455f92dSJames Wright case CEED_TRANSPOSE: 12198d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) { 12298d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) { 12398d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt k = 0; k < N; k++) C[i * N + j] += A[i * N + k] * B[j * N + k]; 12498d9a7e6SJames Wright } 125a455f92dSJames Wright } 126a455f92dSJames Wright break; 127a455f92dSJames Wright } 128a455f92dSJames Wright break; 129a455f92dSJames Wright case CEED_TRANSPOSE: 130a455f92dSJames Wright switch (transpose_B) { 131a455f92dSJames Wright case CEED_NOTRANSPOSE: 13298d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) { 13398d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) { 13498d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt k = 0; k < N; k++) C[i * N + j] += A[k * N + i] * B[k * N + j]; 13598d9a7e6SJames Wright } 136a455f92dSJames Wright } 137a455f92dSJames Wright break; 138a455f92dSJames Wright case CEED_TRANSPOSE: 13998d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt i = 0; i < N; i++) { 14098d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt j = 0; j < N; j++) { 14198d9a7e6SJames Wright CeedPragmaSIMD for (CeedInt k = 0; k < N; k++) C[i * N + j] += A[k * N + i] * B[j * N + k]; 14298d9a7e6SJames Wright } 143a455f92dSJames Wright } 144a455f92dSJames Wright break; 145a455f92dSJames Wright } 146a455f92dSJames Wright break; 147a455f92dSJames Wright } 148a455f92dSJames Wright } 149a455f92dSJames Wright 15098d9a7e6SJames Wright // @brief 3x3 Matrix-Matrix product, C = AB + C 15198d9a7e6SJames Wright CEED_QFUNCTION_HELPER void MatMat3(const CeedScalar A[3][3], const CeedScalar B[3][3], const CeedTransposeMode transpose_A, 15298d9a7e6SJames Wright const CeedTransposeMode transpose_B, CeedScalar C[3][3]) { 15398d9a7e6SJames Wright MatMatN((const CeedScalar *)A, (const CeedScalar *)B, 3, transpose_A, transpose_B, (CeedScalar *)C); 15498d9a7e6SJames Wright } 15598d9a7e6SJames Wright 15613fa47b2SJames Wright // @brief Unpack Kelvin-Mandel notation symmetric tensor into full tensor 15713fa47b2SJames Wright CEED_QFUNCTION_HELPER void KMUnpack(const CeedScalar v[6], CeedScalar A[3][3]) { 15813fa47b2SJames Wright const CeedScalar weight = 1 / sqrt(2.); 15913fa47b2SJames Wright A[0][0] = v[0]; 16013fa47b2SJames Wright A[1][1] = v[1]; 16113fa47b2SJames Wright A[2][2] = v[2]; 16213fa47b2SJames Wright A[2][1] = A[1][2] = weight * v[3]; 16313fa47b2SJames Wright A[2][0] = A[0][2] = weight * v[4]; 16413fa47b2SJames Wright A[1][0] = A[0][1] = weight * v[5]; 16513fa47b2SJames Wright } 16613fa47b2SJames Wright 167a455f92dSJames Wright // @brief Pack full tensor into Kelvin-Mandel notation symmetric tensor 168a455f92dSJames Wright CEED_QFUNCTION_HELPER void KMPack(const CeedScalar A[3][3], CeedScalar v[6]) { 169a455f92dSJames Wright const CeedScalar weight = sqrt(2.); 170a455f92dSJames Wright v[0] = A[0][0]; 171a455f92dSJames Wright v[1] = A[1][1]; 172a455f92dSJames Wright v[2] = A[2][2]; 173a455f92dSJames Wright v[3] = A[2][1] * weight; 174a455f92dSJames Wright v[4] = A[2][0] * weight; 175a455f92dSJames Wright v[5] = A[1][0] * weight; 176a455f92dSJames Wright } 177a455f92dSJames Wright 178a455f92dSJames Wright // @brief Calculate metric tensor from mapping, g_{ij} = xi_{k,i} xi_{k,j} = dXdx^T dXdx 179a455f92dSJames Wright CEED_QFUNCTION_HELPER void KMMetricTensor(const CeedScalar dXdx[3][3], CeedScalar km_g_ij[6]) { 180a455f92dSJames Wright CeedScalar g_ij[3][3] = {{0.}}; 181a455f92dSJames Wright MatMat3(dXdx, dXdx, CEED_TRANSPOSE, CEED_NOTRANSPOSE, g_ij); 182a455f92dSJames Wright KMPack(g_ij, km_g_ij); 183a455f92dSJames Wright } 184a455f92dSJames Wright 185530ad8c4SKenneth E. Jansen // @brief Linear ramp evaluation 186530ad8c4SKenneth E. Jansen CEED_QFUNCTION_HELPER CeedScalar LinearRampCoefficient(CeedScalar amplitude, CeedScalar length, CeedScalar start, CeedScalar x) { 187530ad8c4SKenneth E. Jansen if (x < start) { 188530ad8c4SKenneth E. Jansen return amplitude; 189530ad8c4SKenneth E. Jansen } else if (x < start + length) { 190530ad8c4SKenneth E. Jansen return amplitude * ((x - start) * (-1 / length) + 1); 191530ad8c4SKenneth E. Jansen } else { 192530ad8c4SKenneth E. Jansen return 0; 193530ad8c4SKenneth E. Jansen } 194530ad8c4SKenneth E. Jansen } 195530ad8c4SKenneth E. Jansen 196f3e15844SJames Wright /** 197f3e15844SJames Wright @brief Pack stored values at quadrature point 198f3e15844SJames Wright 199f3e15844SJames Wright @param[in] Q Number of quadrature points 200f3e15844SJames Wright @param[in] i Current quadrature point 201f3e15844SJames Wright @param[in] start Starting index to store components 202f3e15844SJames Wright @param[in] num_comp Number of components to store 2034e5897fcSJames Wright @param[in] values_at_qpnt Local values for quadrature point i 204f3e15844SJames Wright @param[out] stored Stored values 205f3e15844SJames Wright 206f3e15844SJames Wright @return An error code: 0 - success, otherwise - failure 207f3e15844SJames Wright **/ 2084e5897fcSJames Wright CEED_QFUNCTION_HELPER int StoredValuesPack(CeedInt Q, CeedInt i, CeedInt start, CeedInt num_comp, const CeedScalar *values_at_qpnt, 2094e5897fcSJames Wright CeedScalar *stored) { 2104e5897fcSJames Wright for (CeedInt j = 0; j < num_comp; j++) stored[(start + j) * Q + i] = values_at_qpnt[j]; 211f3e15844SJames Wright 212f3e15844SJames Wright return CEED_ERROR_SUCCESS; 213f3e15844SJames Wright } 214f3e15844SJames Wright 215f3e15844SJames Wright /** 216f3e15844SJames Wright @brief Unpack stored values at quadrature point 217f3e15844SJames Wright 218f3e15844SJames Wright @param[in] Q Number of quadrature points 219f3e15844SJames Wright @param[in] i Current quadrature point 220f3e15844SJames Wright @param[in] start Starting index to store components 221f3e15844SJames Wright @param[in] num_comp Number of components to store 222f3e15844SJames Wright @param[in] stored Stored values 2234e5897fcSJames Wright @param[out] values_at_qpnt Local values for quadrature point i 224f3e15844SJames Wright 225f3e15844SJames Wright @return An error code: 0 - success, otherwise - failure 226f3e15844SJames Wright **/ 2274e5897fcSJames Wright CEED_QFUNCTION_HELPER int StoredValuesUnpack(CeedInt Q, CeedInt i, CeedInt start, CeedInt num_comp, const CeedScalar *stored, 2284e5897fcSJames Wright CeedScalar *values_at_qpnt) { 2294e5897fcSJames Wright for (CeedInt j = 0; j < num_comp; j++) values_at_qpnt[j] = stored[(start + j) * Q + i]; 230f3e15844SJames Wright 231f3e15844SJames Wright return CEED_ERROR_SUCCESS; 232f3e15844SJames Wright } 233f3e15844SJames Wright 234f3e15844SJames Wright /** 235f3e15844SJames Wright @brief Unpack 3D element q_data at quadrature point 236f3e15844SJames Wright 237f3e15844SJames Wright @param[in] Q Number of quadrature points 238f3e15844SJames Wright @param[in] i Current quadrature point 239f3e15844SJames Wright @param[in] q_data Pointer to q_data (generated by `setupgeo.h:Setup`) 240f3e15844SJames Wright @param[out] wdetJ Quadrature weight times determinant of the mapping Jacobian 241f3e15844SJames Wright @param[out] dXdx Inverse of the mapping Jacobian (shape [3][3]) 242f3e15844SJames Wright 243f3e15844SJames Wright @return An error code: 0 - success, otherwise - failure 244f3e15844SJames Wright **/ 245f3e15844SJames Wright CEED_QFUNCTION_HELPER int QdataUnpack_3D(CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar dXdx[3][3]) { 246f3e15844SJames Wright StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ); 247f3e15844SJames Wright StoredValuesUnpack(Q, i, 1, 9, q_data, (CeedScalar *)dXdx); 248f3e15844SJames Wright return CEED_ERROR_SUCCESS; 249f3e15844SJames Wright } 250f3e15844SJames Wright 251f3e15844SJames Wright /** 252f3e15844SJames Wright @brief Unpack boundary element q_data for 3D problem at quadrature point 253f3e15844SJames Wright 254f3e15844SJames Wright @param[in] Q Number of quadrature points 255f3e15844SJames Wright @param[in] i Current quadrature point 2561394d07eSJames Wright @param[in] q_data Pointer to q_data (generated by `setupgeo.h:SetupBoundary`) 257f3e15844SJames Wright @param[out] wdetJ Quadrature weight times determinant of the mapping Jacobian, or `NULL` 258f3e15844SJames Wright @param[out] dXdx Inverse of the mapping Jacobian (shape [2][3]), or `NULL` 259f3e15844SJames Wright @param[out] normal Components of the normal vector (shape [3]), or `NULL` 260f3e15844SJames Wright 261f3e15844SJames Wright @return An error code: 0 - success, otherwise - failure 262f3e15844SJames Wright **/ 263f3e15844SJames Wright CEED_QFUNCTION_HELPER int QdataBoundaryUnpack_3D(CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar dXdx[2][3], 264f3e15844SJames Wright CeedScalar normal[3]) { 265f3e15844SJames Wright if (wdetJ) StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ); 266f3e15844SJames Wright if (normal) StoredValuesUnpack(Q, i, 1, 3, q_data, normal); 267f3e15844SJames Wright if (dXdx) StoredValuesUnpack(Q, i, 4, 6, q_data, (CeedScalar *)dXdx); 268f3e15844SJames Wright return CEED_ERROR_SUCCESS; 269f3e15844SJames Wright } 270f3e15844SJames Wright 271bf415d3fSJames Wright /** 272bf415d3fSJames Wright @brief Unpack 2D element q_data at quadrature point 273bf415d3fSJames Wright 274bf415d3fSJames Wright @param[in] Q Number of quadrature points 275bf415d3fSJames Wright @param[in] i Current quadrature point 276bf415d3fSJames Wright @param[in] q_data Pointer to q_data (generated by `setupgeo.h:Setup`) 277bf415d3fSJames Wright @param[out] wdetJ Quadrature weight times determinant of the mapping Jacobian 278bf415d3fSJames Wright @param[out] dXdx Inverse of the mapping Jacobian (shape [2][2]) 279bf415d3fSJames Wright 280bf415d3fSJames Wright @return An error code: 0 - success, otherwise - failure 281bf415d3fSJames Wright **/ 282bf415d3fSJames Wright CEED_QFUNCTION_HELPER int QdataUnpack_2D(CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar dXdx[2][2]) { 283bf415d3fSJames Wright StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ); 284bf415d3fSJames Wright StoredValuesUnpack(Q, i, 1, 4, q_data, (CeedScalar *)dXdx); 285bf415d3fSJames Wright return CEED_ERROR_SUCCESS; 286bf415d3fSJames Wright } 287bf415d3fSJames Wright 2881394d07eSJames Wright /** 2891394d07eSJames Wright @brief Unpack boundary element q_data for 2D problem at quadrature point 2901394d07eSJames Wright 2911394d07eSJames Wright @param[in] Q Number of quadrature points 2921394d07eSJames Wright @param[in] i Current quadrature point 2931394d07eSJames Wright @param[in] q_data Pointer to q_data (generated by `setupgeo.h:SetupBoundary2d`) 2941394d07eSJames Wright @param[out] wdetJ Quadrature weight times determinant of the mapping Jacobian, or `NULL` 2951394d07eSJames Wright @param[out] normal Components of the normal vector (shape [2]), or `NULL` 2961394d07eSJames Wright 2971394d07eSJames Wright @return An error code: 0 - success, otherwise - failure 2981394d07eSJames Wright **/ 2991394d07eSJames Wright CEED_QFUNCTION_HELPER int QdataBoundaryUnpack_2D(CeedInt Q, CeedInt i, const CeedScalar *q_data, CeedScalar *wdetJ, CeedScalar normal[2]) { 3001394d07eSJames Wright if (wdetJ) StoredValuesUnpack(Q, i, 0, 1, q_data, wdetJ); 3011394d07eSJames Wright if (normal) StoredValuesUnpack(Q, i, 1, 2, q_data, normal); 3021394d07eSJames Wright return CEED_ERROR_SUCCESS; 3031394d07eSJames Wright } 304