1*0b96b02dSJeremy L Thompson // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors. 2*0b96b02dSJeremy L Thompson // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3*0b96b02dSJeremy L Thompson // 4*0b96b02dSJeremy L Thompson // SPDX-License-Identifier: BSD-2-Clause 5*0b96b02dSJeremy L Thompson // 6*0b96b02dSJeremy L Thompson // This file is part of CEED: http://github.com/ceed 7*0b96b02dSJeremy L Thompson 8*0b96b02dSJeremy L Thompson #include <ceed/types.h> 9*0b96b02dSJeremy L Thompson 10*0b96b02dSJeremy L Thompson /// A structure used to pass additional data to f_build_mass_diff 11*0b96b02dSJeremy L Thompson struct BuildContext { 12*0b96b02dSJeremy L Thompson CeedInt dim, space_dim; 13*0b96b02dSJeremy L Thompson }; 14*0b96b02dSJeremy L Thompson 15*0b96b02dSJeremy L Thompson /// libCEED Q-function for building quadrature data for a mass + diffusion operator 16*0b96b02dSJeremy L Thompson CEED_QFUNCTION(build_mass_diff)(void *ctx, const CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 17*0b96b02dSJeremy L Thompson struct BuildContext *build_data = (struct BuildContext *)ctx; 18*0b96b02dSJeremy L Thompson // in[0] is Jacobians with shape [dim, nc=dim, Q] 19*0b96b02dSJeremy L Thompson // in[1] is quadrature weights, size (Q) 20*0b96b02dSJeremy L Thompson // 21*0b96b02dSJeremy L Thompson // At every quadrature point, compute w/det(J).adj(J).adj(J)^T and store 22*0b96b02dSJeremy L Thompson // the symmetric part of the result. 23*0b96b02dSJeremy L Thompson const CeedScalar *J = in[0], *w = in[1]; 24*0b96b02dSJeremy L Thompson CeedScalar *q_data = out[0]; 25*0b96b02dSJeremy L Thompson 26*0b96b02dSJeremy L Thompson switch (build_data->dim + 10 * build_data->space_dim) { 27*0b96b02dSJeremy L Thompson case 11: 28*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 29*0b96b02dSJeremy L Thompson // Mass 30*0b96b02dSJeremy L Thompson q_data[i + Q * 0] = w[i] * J[i]; 31*0b96b02dSJeremy L Thompson // Diffusion 32*0b96b02dSJeremy L Thompson q_data[i + Q * 1] = w[i] / J[i]; 33*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 34*0b96b02dSJeremy L Thompson break; 35*0b96b02dSJeremy L Thompson case 22: 36*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 37*0b96b02dSJeremy L Thompson // J: 0 2 q_data: 1 3 adj(J): J22 -J12 38*0b96b02dSJeremy L Thompson // 1 3 3 2 -J21 J11 39*0b96b02dSJeremy L Thompson const CeedScalar J11 = J[i + Q * 0]; 40*0b96b02dSJeremy L Thompson const CeedScalar J21 = J[i + Q * 1]; 41*0b96b02dSJeremy L Thompson const CeedScalar J12 = J[i + Q * 2]; 42*0b96b02dSJeremy L Thompson const CeedScalar J22 = J[i + Q * 3]; 43*0b96b02dSJeremy L Thompson const CeedScalar qw = w[i] / (J11 * J22 - J21 * J12); 44*0b96b02dSJeremy L Thompson 45*0b96b02dSJeremy L Thompson // Mass 46*0b96b02dSJeremy L Thompson q_data[i + Q * 0] = w[i] * (J11 * J22 - J21 * J12); 47*0b96b02dSJeremy L Thompson // Diffusion 48*0b96b02dSJeremy L Thompson q_data[i + Q * 1] = qw * (J12 * J12 + J22 * J22); 49*0b96b02dSJeremy L Thompson q_data[i + Q * 2] = qw * (J11 * J11 + J21 * J21); 50*0b96b02dSJeremy L Thompson q_data[i + Q * 3] = -qw * (J11 * J12 + J21 * J22); 51*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 52*0b96b02dSJeremy L Thompson break; 53*0b96b02dSJeremy L Thompson case 33: 54*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 55*0b96b02dSJeremy L Thompson // Compute the adjoint 56*0b96b02dSJeremy L Thompson CeedScalar A[3][3]; 57*0b96b02dSJeremy L Thompson for (CeedInt j = 0; j < 3; j++) { 58*0b96b02dSJeremy L Thompson for (CeedInt k = 0; k < 3; k++) { 59*0b96b02dSJeremy L Thompson // Equivalent code with J as a VLA and no mod operations: 60*0b96b02dSJeremy L Thompson // A[k][j] = J[j+1][k+1]*J[j+2][k+2] - J[j+1][k+2]*J[j+2][k+1] 61*0b96b02dSJeremy L Thompson A[k][j] = J[i + Q * ((j + 1) % 3 + 3 * ((k + 1) % 3))] * J[i + Q * ((j + 2) % 3 + 3 * ((k + 2) % 3))] - 62*0b96b02dSJeremy L Thompson J[i + Q * ((j + 1) % 3 + 3 * ((k + 2) % 3))] * J[i + Q * ((j + 2) % 3 + 3 * ((k + 1) % 3))]; 63*0b96b02dSJeremy L Thompson } 64*0b96b02dSJeremy L Thompson } 65*0b96b02dSJeremy L Thompson 66*0b96b02dSJeremy L Thompson // Compute quadrature weight / det(J) 67*0b96b02dSJeremy L Thompson const CeedScalar qw = w[i] / (J[i + Q * 0] * A[0][0] + J[i + Q * 1] * A[0][1] + J[i + Q * 2] * A[0][2]); 68*0b96b02dSJeremy L Thompson 69*0b96b02dSJeremy L Thompson // Mass 70*0b96b02dSJeremy L Thompson q_data[i + Q * 0] = w[i] * (J[i + Q * 0] * A[0][0] + J[i + Q * 1] * A[0][1] + J[i + Q * 2] * A[0][2]); 71*0b96b02dSJeremy L Thompson // Diffusion 72*0b96b02dSJeremy L Thompson // Stored in Voigt convention 73*0b96b02dSJeremy L Thompson // 1 6 5 74*0b96b02dSJeremy L Thompson // 6 2 4 75*0b96b02dSJeremy L Thompson // 5 4 3 76*0b96b02dSJeremy L Thompson q_data[i + Q * 1] = qw * (A[0][0] * A[0][0] + A[0][1] * A[0][1] + A[0][2] * A[0][2]); 77*0b96b02dSJeremy L Thompson q_data[i + Q * 2] = qw * (A[1][0] * A[1][0] + A[1][1] * A[1][1] + A[1][2] * A[1][2]); 78*0b96b02dSJeremy L Thompson q_data[i + Q * 3] = qw * (A[2][0] * A[2][0] + A[2][1] * A[2][1] + A[2][2] * A[2][2]); 79*0b96b02dSJeremy L Thompson q_data[i + Q * 4] = qw * (A[1][0] * A[2][0] + A[1][1] * A[2][1] + A[1][2] * A[2][2]); 80*0b96b02dSJeremy L Thompson q_data[i + Q * 5] = qw * (A[0][0] * A[2][0] + A[0][1] * A[2][1] + A[0][2] * A[2][2]); 81*0b96b02dSJeremy L Thompson q_data[i + Q * 6] = qw * (A[0][0] * A[1][0] + A[0][1] * A[1][1] + A[0][2] * A[1][2]); 82*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 83*0b96b02dSJeremy L Thompson break; 84*0b96b02dSJeremy L Thompson } 85*0b96b02dSJeremy L Thompson return CEED_ERROR_SUCCESS; 86*0b96b02dSJeremy L Thompson } 87*0b96b02dSJeremy L Thompson 88*0b96b02dSJeremy L Thompson /// libCEED Q-function for applying a mass + diffusion operator 89*0b96b02dSJeremy L Thompson CEED_QFUNCTION(apply_mass_diff)(void *ctx, const CeedInt Q, const CeedScalar *const *in, CeedScalar *const *out) { 90*0b96b02dSJeremy L Thompson struct BuildContext *build_data = (struct BuildContext *)ctx; 91*0b96b02dSJeremy L Thompson // in[0], out[0] have shape [1, nc=1, Q] 92*0b96b02dSJeremy L Thompson // in[1], out[1] have shape [dim, nc=1, Q] 93*0b96b02dSJeremy L Thompson const CeedScalar *u = in[0], *ug = in[1], *q_data = in[2]; 94*0b96b02dSJeremy L Thompson CeedScalar *v = out[0], *vg = out[1]; 95*0b96b02dSJeremy L Thompson 96*0b96b02dSJeremy L Thompson switch (build_data->dim) { 97*0b96b02dSJeremy L Thompson case 1: 98*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 99*0b96b02dSJeremy L Thompson // Mass 100*0b96b02dSJeremy L Thompson v[i] = q_data[i + Q * 0] * u[i]; 101*0b96b02dSJeremy L Thompson // Diffusion 102*0b96b02dSJeremy L Thompson vg[i] = ug[i] * q_data[i + Q * 1]; 103*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 104*0b96b02dSJeremy L Thompson break; 105*0b96b02dSJeremy L Thompson case 2: 106*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 107*0b96b02dSJeremy L Thompson // Mass 108*0b96b02dSJeremy L Thompson v[i] = q_data[i + Q * 0] * u[i]; 109*0b96b02dSJeremy L Thompson 110*0b96b02dSJeremy L Thompson // Diffusion 111*0b96b02dSJeremy L Thompson // Read spatial derivatives of u 112*0b96b02dSJeremy L Thompson const CeedScalar du[2] = {ug[i + Q * 0], ug[i + Q * 1]}; 113*0b96b02dSJeremy L Thompson 114*0b96b02dSJeremy L Thompson // Read q_data (dXdxdXdx_T symmetric matrix) 115*0b96b02dSJeremy L Thompson // Stored in Voigt convention 116*0b96b02dSJeremy L Thompson // 1 3 117*0b96b02dSJeremy L Thompson // 3 2 118*0b96b02dSJeremy L Thompson const CeedScalar dXdxdXdx_T[2][2] = { 119*0b96b02dSJeremy L Thompson {q_data[i + 1 * Q], q_data[i + 3 * Q]}, 120*0b96b02dSJeremy L Thompson {q_data[i + 3 * Q], q_data[i + 2 * Q]} 121*0b96b02dSJeremy L Thompson }; 122*0b96b02dSJeremy L Thompson // j = direction of vg 123*0b96b02dSJeremy L Thompson for (int j = 0; j < 2; j++) vg[i + j * Q] = (du[0] * dXdxdXdx_T[0][j] + du[1] * dXdxdXdx_T[1][j]); 124*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 125*0b96b02dSJeremy L Thompson break; 126*0b96b02dSJeremy L Thompson case 3: 127*0b96b02dSJeremy L Thompson CeedPragmaSIMD for (CeedInt i = 0; i < Q; i++) { 128*0b96b02dSJeremy L Thompson // Mass 129*0b96b02dSJeremy L Thompson v[i] = q_data[i + Q * 0] * u[i]; 130*0b96b02dSJeremy L Thompson 131*0b96b02dSJeremy L Thompson // Diffusion 132*0b96b02dSJeremy L Thompson // Read spatial derivatives of u 133*0b96b02dSJeremy L Thompson const CeedScalar du[3] = {ug[i + Q * 0], ug[i + Q * 1], ug[i + Q * 2]}; 134*0b96b02dSJeremy L Thompson 135*0b96b02dSJeremy L Thompson // Read q_data (dXdxdXdx_T symmetric matrix) 136*0b96b02dSJeremy L Thompson // Stored in Voigt convention 137*0b96b02dSJeremy L Thompson // 0 5 4 138*0b96b02dSJeremy L Thompson // 5 1 3 139*0b96b02dSJeremy L Thompson // 4 3 2 140*0b96b02dSJeremy L Thompson const CeedScalar dXdxdXdx_T[3][3] = { 141*0b96b02dSJeremy L Thompson {q_data[i + 1 * Q], q_data[i + 6 * Q], q_data[i + 5 * Q]}, 142*0b96b02dSJeremy L Thompson {q_data[i + 6 * Q], q_data[i + 2 * Q], q_data[i + 4 * Q]}, 143*0b96b02dSJeremy L Thompson {q_data[i + 5 * Q], q_data[i + 4 * Q], q_data[i + 3 * Q]} 144*0b96b02dSJeremy L Thompson }; 145*0b96b02dSJeremy L Thompson // j = direction of vg 146*0b96b02dSJeremy L Thompson for (int j = 0; j < 3; j++) vg[i + j * Q] = (du[0] * dXdxdXdx_T[0][j] + du[1] * dXdxdXdx_T[1][j] + du[2] * dXdxdXdx_T[2][j]); 147*0b96b02dSJeremy L Thompson } // End of Quadrature Point Loop 148*0b96b02dSJeremy L Thompson break; 149*0b96b02dSJeremy L Thompson } 150*0b96b02dSJeremy L Thompson return CEED_ERROR_SUCCESS; 151*0b96b02dSJeremy L Thompson } 152