1 // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors. 2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3 // 4 // SPDX-License-Identifier: BSD-2-Clause 5 // 6 // This file is part of CEED: http://github.com/ceed 7 8 #include <ceed.h> 9 #include <ceed/backend.h> 10 #include <math.h> 11 #include <stdbool.h> 12 #include <string.h> 13 14 #include "ceed-ref.h" 15 16 //------------------------------------------------------------------------------ 17 // Basis Apply 18 //------------------------------------------------------------------------------ 19 static int CeedBasisApplyCore_Ref(CeedBasis basis, bool apply_add, CeedInt num_elem, CeedTransposeMode t_mode, CeedEvalMode eval_mode, CeedVector U, 20 CeedVector V) { 21 Ceed ceed; 22 bool is_tensor_basis, add = apply_add || (t_mode == CEED_TRANSPOSE); 23 CeedInt dim, num_comp, q_comp, num_nodes, num_qpts; 24 const CeedScalar *u; 25 CeedScalar *v; 26 CeedTensorContract contract; 27 CeedBasis_Ref *impl; 28 29 CeedCallBackend(CeedBasisGetCeed(basis, &ceed)); 30 CeedCallBackend(CeedBasisGetData(basis, &impl)); 31 CeedCallBackend(CeedBasisGetDimension(basis, &dim)); 32 CeedCallBackend(CeedBasisGetNumComponents(basis, &num_comp)); 33 CeedCallBackend(CeedBasisGetNumQuadratureComponents(basis, eval_mode, &q_comp)); 34 CeedCallBackend(CeedBasisGetNumNodes(basis, &num_nodes)); 35 CeedCallBackend(CeedBasisGetNumQuadraturePoints(basis, &num_qpts)); 36 CeedCallBackend(CeedBasisGetTensorContract(basis, &contract)); 37 if (U != CEED_VECTOR_NONE) CeedCallBackend(CeedVectorGetArrayRead(U, CEED_MEM_HOST, &u)); 38 else CeedCheck(eval_mode == CEED_EVAL_WEIGHT, ceed, CEED_ERROR_BACKEND, "An input vector is required for this CeedEvalMode"); 39 // Clear v if operating in transpose 40 if (apply_add) CeedCallBackend(CeedVectorGetArray(V, CEED_MEM_HOST, &v)); 41 else CeedCallBackend(CeedVectorGetArrayWrite(V, CEED_MEM_HOST, &v)); 42 43 if (t_mode == CEED_TRANSPOSE && !apply_add) { 44 CeedSize len; 45 46 CeedCallBackend(CeedVectorGetLength(V, &len)); 47 for (CeedInt i = 0; i < len; i++) v[i] = 0.0; 48 } 49 50 CeedCallBackend(CeedBasisIsTensor(basis, &is_tensor_basis)); 51 if (is_tensor_basis) { 52 // Tensor basis 53 CeedInt P_1d, Q_1d; 54 55 CeedCallBackend(CeedBasisGetNumNodes1D(basis, &P_1d)); 56 CeedCallBackend(CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d)); 57 switch (eval_mode) { 58 // Interpolate to/from quadrature points 59 case CEED_EVAL_INTERP: { 60 if (impl->has_collo_interp) { 61 memcpy(v, u, num_elem * num_comp * num_nodes * sizeof(u[0])); 62 } else { 63 CeedInt P = P_1d, Q = Q_1d; 64 65 if (t_mode == CEED_TRANSPOSE) { 66 P = Q_1d; 67 Q = P_1d; 68 } 69 CeedInt pre = num_comp * CeedIntPow(P, dim - 1), post = num_elem; 70 CeedScalar tmp[2][num_elem * num_comp * Q * CeedIntPow(P > Q ? P : Q, dim - 1)]; 71 const CeedScalar *interp_1d; 72 73 CeedCallBackend(CeedBasisGetInterp1D(basis, &interp_1d)); 74 for (CeedInt d = 0; d < dim; d++) { 75 CeedCallBackend(CeedTensorContractApply(contract, pre, P, post, Q, interp_1d, t_mode, add && (d == dim - 1), d == 0 ? u : tmp[d % 2], 76 d == dim - 1 ? v : tmp[(d + 1) % 2])); 77 pre /= P; 78 post *= Q; 79 } 80 } 81 } break; 82 // Evaluate the gradient to/from quadrature points 83 case CEED_EVAL_GRAD: { 84 // In CEED_NOTRANSPOSE mode: 85 // u has shape [dim, num_comp, P^dim, num_elem], row-major layout 86 // v has shape [dim, num_comp, Q^dim, num_elem], row-major layout 87 // In CEED_TRANSPOSE mode, the sizes of u and v are switched. 88 CeedInt P = P_1d, Q = Q_1d; 89 90 if (t_mode == CEED_TRANSPOSE) { 91 P = Q_1d; 92 Q = Q_1d; 93 } 94 CeedInt pre = num_comp * CeedIntPow(P, dim - 1), post = num_elem; 95 const CeedScalar *interp_1d; 96 97 CeedCallBackend(CeedBasisGetInterp1D(basis, &interp_1d)); 98 if (impl->collo_grad_1d) { 99 CeedScalar tmp[2][num_elem * num_comp * Q * CeedIntPow(P > Q ? P : Q, dim - 1)]; 100 CeedScalar interp[num_elem * num_comp * Q * CeedIntPow(P > Q ? P : Q, dim - 1)]; 101 102 // Interpolate to quadrature points (NoTranspose) 103 // or Grad to quadrature points (Transpose) 104 for (CeedInt d = 0; d < dim; d++) { 105 CeedCallBackend(CeedTensorContractApply(contract, pre, P, post, Q, (t_mode == CEED_NOTRANSPOSE ? interp_1d : impl->collo_grad_1d), t_mode, 106 (t_mode == CEED_TRANSPOSE) && (d > 0), 107 (t_mode == CEED_NOTRANSPOSE ? (d == 0 ? u : tmp[d % 2]) : &u[d * num_qpts * num_comp * num_elem]), 108 (t_mode == CEED_NOTRANSPOSE ? (d == dim - 1 ? interp : tmp[(d + 1) % 2]) : interp))); 109 pre /= P; 110 post *= Q; 111 } 112 // Grad to quadrature points (NoTranspose) 113 // or Interpolate to nodes (Transpose) 114 P = Q_1d, Q = Q_1d; 115 if (t_mode == CEED_TRANSPOSE) { 116 P = Q_1d; 117 Q = P_1d; 118 } 119 pre = num_comp * CeedIntPow(P, dim - 1), post = num_elem; 120 for (CeedInt d = 0; d < dim; d++) { 121 CeedCallBackend(CeedTensorContractApply( 122 contract, pre, P, post, Q, (t_mode == CEED_NOTRANSPOSE ? impl->collo_grad_1d : interp_1d), t_mode, 123 (t_mode == CEED_NOTRANSPOSE && apply_add) || (t_mode == CEED_TRANSPOSE && (d == dim - 1)), 124 (t_mode == CEED_NOTRANSPOSE ? interp : (d == 0 ? interp : tmp[d % 2])), 125 (t_mode == CEED_NOTRANSPOSE ? &v[d * num_qpts * num_comp * num_elem] : (d == dim - 1 ? v : tmp[(d + 1) % 2])))); 126 pre /= P; 127 post *= Q; 128 } 129 } else if (impl->has_collo_interp) { // Qpts collocated with nodes 130 const CeedScalar *grad_1d; 131 132 CeedCallBackend(CeedBasisGetGrad1D(basis, &grad_1d)); 133 134 // Dim contractions, identity in other directions 135 CeedInt pre = num_comp * CeedIntPow(P, dim - 1), post = num_elem; 136 137 for (CeedInt d = 0; d < dim; d++) { 138 CeedCallBackend(CeedTensorContractApply(contract, pre, P, post, Q, grad_1d, t_mode, add && (d > 0), 139 t_mode == CEED_NOTRANSPOSE ? u : &u[d * num_comp * num_qpts * num_elem], 140 t_mode == CEED_TRANSPOSE ? v : &v[d * num_comp * num_qpts * num_elem])); 141 pre /= P; 142 post *= Q; 143 } 144 } else { // Underintegration, P > Q 145 const CeedScalar *grad_1d; 146 147 CeedCallBackend(CeedBasisGetGrad1D(basis, &grad_1d)); 148 149 if (t_mode == CEED_TRANSPOSE) { 150 P = Q_1d; 151 Q = P_1d; 152 } 153 CeedScalar tmp[2][num_elem * num_comp * Q * CeedIntPow(P > Q ? P : Q, dim - 1)]; 154 155 // Dim**2 contractions, apply grad when pass == dim 156 for (CeedInt p = 0; p < dim; p++) { 157 CeedInt pre = num_comp * CeedIntPow(P, dim - 1), post = num_elem; 158 159 for (CeedInt d = 0; d < dim; d++) { 160 CeedCallBackend(CeedTensorContractApply( 161 contract, pre, P, post, Q, (p == d) ? grad_1d : interp_1d, t_mode, add && (d == dim - 1), 162 (d == 0 ? (t_mode == CEED_NOTRANSPOSE ? u : &u[p * num_comp * num_qpts * num_elem]) : tmp[d % 2]), 163 (d == dim - 1 ? (t_mode == CEED_TRANSPOSE ? v : &v[p * num_comp * num_qpts * num_elem]) : tmp[(d + 1) % 2]))); 164 pre /= P; 165 post *= Q; 166 } 167 } 168 } 169 } break; 170 // Retrieve interpolation weights 171 case CEED_EVAL_WEIGHT: { 172 CeedInt Q = Q_1d; 173 const CeedScalar *q_weight_1d; 174 175 CeedCheck(t_mode == CEED_NOTRANSPOSE, ceed, CEED_ERROR_BACKEND, "CEED_EVAL_WEIGHT incompatible with CEED_TRANSPOSE"); 176 CeedCallBackend(CeedBasisGetQWeights(basis, &q_weight_1d)); 177 for (CeedInt d = 0; d < dim; d++) { 178 CeedInt pre = CeedIntPow(Q, dim - d - 1), post = CeedIntPow(Q, d); 179 180 for (CeedInt i = 0; i < pre; i++) { 181 for (CeedInt j = 0; j < Q; j++) { 182 for (CeedInt k = 0; k < post; k++) { 183 const CeedScalar w = q_weight_1d[j] * (d == 0 ? 1 : v[((i * Q + j) * post + k) * num_elem]); 184 185 for (CeedInt e = 0; e < num_elem; e++) v[((i * Q + j) * post + k) * num_elem + e] = w; 186 } 187 } 188 } 189 } 190 } break; 191 // LCOV_EXCL_START 192 case CEED_EVAL_DIV: 193 case CEED_EVAL_CURL: 194 return CeedError(ceed, CEED_ERROR_BACKEND, "%s not supported", CeedEvalModes[eval_mode]); 195 case CEED_EVAL_NONE: 196 return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_NONE does not make sense in this context"); 197 // LCOV_EXCL_STOP 198 } 199 } else { 200 // Non-tensor basis 201 CeedInt P = num_nodes, Q = num_qpts; 202 203 switch (eval_mode) { 204 // Interpolate to/from quadrature points 205 case CEED_EVAL_INTERP: { 206 const CeedScalar *interp; 207 208 CeedCallBackend(CeedBasisGetInterp(basis, &interp)); 209 CeedCallBackend(CeedTensorContractStridedApply(contract, num_comp, P, num_elem, q_comp, Q, interp, t_mode, add, u, v)); 210 } break; 211 // Evaluate the gradient to/from quadrature points 212 case CEED_EVAL_GRAD: { 213 const CeedScalar *grad; 214 215 CeedCallBackend(CeedBasisGetGrad(basis, &grad)); 216 CeedCallBackend(CeedTensorContractStridedApply(contract, num_comp, P, num_elem, q_comp, Q, grad, t_mode, add, u, v)); 217 } break; 218 // Evaluate the divergence to/from the quadrature points 219 case CEED_EVAL_DIV: { 220 const CeedScalar *div; 221 222 CeedCallBackend(CeedBasisGetDiv(basis, &div)); 223 CeedCallBackend(CeedTensorContractStridedApply(contract, num_comp, P, num_elem, q_comp, Q, div, t_mode, add, u, v)); 224 } break; 225 // Evaluate the curl to/from the quadrature points 226 case CEED_EVAL_CURL: { 227 const CeedScalar *curl; 228 229 CeedCallBackend(CeedBasisGetCurl(basis, &curl)); 230 CeedCallBackend(CeedTensorContractStridedApply(contract, num_comp, P, num_elem, q_comp, Q, curl, t_mode, add, u, v)); 231 } break; 232 // Retrieve interpolation weights 233 case CEED_EVAL_WEIGHT: { 234 const CeedScalar *q_weight; 235 236 CeedCheck(t_mode == CEED_NOTRANSPOSE, ceed, CEED_ERROR_BACKEND, "CEED_EVAL_WEIGHT incompatible with CEED_TRANSPOSE"); 237 CeedCallBackend(CeedBasisGetQWeights(basis, &q_weight)); 238 for (CeedInt i = 0; i < num_qpts; i++) { 239 for (CeedInt e = 0; e < num_elem; e++) v[i * num_elem + e] = q_weight[i]; 240 } 241 } break; 242 // LCOV_EXCL_START 243 case CEED_EVAL_NONE: 244 return CeedError(ceed, CEED_ERROR_BACKEND, "CEED_EVAL_NONE does not make sense in this context"); 245 // LCOV_EXCL_STOP 246 } 247 } 248 if (U != CEED_VECTOR_NONE) { 249 CeedCallBackend(CeedVectorRestoreArrayRead(U, &u)); 250 } 251 CeedCallBackend(CeedVectorRestoreArray(V, &v)); 252 return CEED_ERROR_SUCCESS; 253 } 254 255 static int CeedBasisApply_Ref(CeedBasis basis, CeedInt num_elem, CeedTransposeMode t_mode, CeedEvalMode eval_mode, CeedVector U, CeedVector V) { 256 CeedCallBackend(CeedBasisApplyCore_Ref(basis, false, num_elem, t_mode, eval_mode, U, V)); 257 return CEED_ERROR_SUCCESS; 258 } 259 260 static int CeedBasisApplyAdd_Ref(CeedBasis basis, CeedInt num_elem, CeedTransposeMode t_mode, CeedEvalMode eval_mode, CeedVector U, CeedVector V) { 261 CeedCallBackend(CeedBasisApplyCore_Ref(basis, true, num_elem, t_mode, eval_mode, U, V)); 262 return CEED_ERROR_SUCCESS; 263 } 264 265 //------------------------------------------------------------------------------ 266 // Basis Destroy Tensor 267 //------------------------------------------------------------------------------ 268 static int CeedBasisDestroyTensor_Ref(CeedBasis basis) { 269 CeedBasis_Ref *impl; 270 271 CeedCallBackend(CeedBasisGetData(basis, &impl)); 272 CeedCallBackend(CeedFree(&impl->collo_grad_1d)); 273 CeedCallBackend(CeedFree(&impl)); 274 return CEED_ERROR_SUCCESS; 275 } 276 277 //------------------------------------------------------------------------------ 278 // Basis Create Tensor 279 //------------------------------------------------------------------------------ 280 int CeedBasisCreateTensorH1_Ref(CeedInt dim, CeedInt P_1d, CeedInt Q_1d, const CeedScalar *interp_1d, const CeedScalar *grad_1d, 281 const CeedScalar *q_ref_1d, const CeedScalar *q_weight_1d, CeedBasis basis) { 282 Ceed ceed, ceed_parent; 283 CeedBasis_Ref *impl; 284 CeedTensorContract contract; 285 286 CeedCallBackend(CeedBasisGetCeed(basis, &ceed)); 287 CeedCallBackend(CeedGetParent(ceed, &ceed_parent)); 288 289 CeedCallBackend(CeedCalloc(1, &impl)); 290 // Check for collocated interp 291 if (Q_1d == P_1d) { 292 bool has_collocated = true; 293 294 for (CeedInt i = 0; i < P_1d; i++) { 295 has_collocated = has_collocated && (fabs(interp_1d[i + P_1d * i] - 1.0) < 1e-14); 296 for (CeedInt j = 0; j < P_1d; j++) { 297 if (j != i) has_collocated = has_collocated && (fabs(interp_1d[j + P_1d * i]) < 1e-14); 298 } 299 } 300 impl->has_collo_interp = has_collocated; 301 } 302 // Calculate collocated grad 303 if (Q_1d >= P_1d && !impl->has_collo_interp) { 304 CeedCallBackend(CeedMalloc(Q_1d * Q_1d, &impl->collo_grad_1d)); 305 CeedCallBackend(CeedBasisGetCollocatedGrad(basis, impl->collo_grad_1d)); 306 } 307 CeedCallBackend(CeedBasisSetData(basis, impl)); 308 309 CeedCallBackend(CeedTensorContractCreate(ceed_parent, &contract)); 310 CeedCallBackend(CeedBasisSetTensorContract(basis, contract)); 311 312 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "Apply", CeedBasisApply_Ref)); 313 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "ApplyAdd", CeedBasisApplyAdd_Ref)); 314 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "Destroy", CeedBasisDestroyTensor_Ref)); 315 return CEED_ERROR_SUCCESS; 316 } 317 318 //------------------------------------------------------------------------------ 319 // Basis Create Non-Tensor H^1 320 //------------------------------------------------------------------------------ 321 int CeedBasisCreateH1_Ref(CeedElemTopology topo, CeedInt dim, CeedInt num_nodes, CeedInt num_qpts, const CeedScalar *interp, const CeedScalar *grad, 322 const CeedScalar *q_ref, const CeedScalar *q_weight, CeedBasis basis) { 323 Ceed ceed, ceed_parent; 324 CeedTensorContract contract; 325 326 CeedCallBackend(CeedBasisGetCeed(basis, &ceed)); 327 CeedCallBackend(CeedGetParent(ceed, &ceed_parent)); 328 329 CeedCallBackend(CeedTensorContractCreate(ceed_parent, &contract)); 330 CeedCallBackend(CeedBasisSetTensorContract(basis, contract)); 331 332 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "Apply", CeedBasisApply_Ref)); 333 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "ApplyAdd", CeedBasisApplyAdd_Ref)); 334 return CEED_ERROR_SUCCESS; 335 } 336 337 //------------------------------------------------------------------------------ 338 // Basis Create Non-Tensor H(div) 339 //------------------------------------------------------------------------------ 340 int CeedBasisCreateHdiv_Ref(CeedElemTopology topo, CeedInt dim, CeedInt num_nodes, CeedInt num_qpts, const CeedScalar *interp, const CeedScalar *div, 341 const CeedScalar *q_ref, const CeedScalar *q_weight, CeedBasis basis) { 342 Ceed ceed, ceed_parent; 343 CeedTensorContract contract; 344 345 CeedCallBackend(CeedBasisGetCeed(basis, &ceed)); 346 CeedCallBackend(CeedGetParent(ceed, &ceed_parent)); 347 348 CeedCallBackend(CeedTensorContractCreate(ceed_parent, &contract)); 349 CeedCallBackend(CeedBasisSetTensorContract(basis, contract)); 350 351 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "Apply", CeedBasisApply_Ref)); 352 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "ApplyAdd", CeedBasisApplyAdd_Ref)); 353 return CEED_ERROR_SUCCESS; 354 } 355 356 //------------------------------------------------------------------------------ 357 // Basis Create Non-Tensor H(curl) 358 //------------------------------------------------------------------------------ 359 int CeedBasisCreateHcurl_Ref(CeedElemTopology topo, CeedInt dim, CeedInt num_nodes, CeedInt num_qpts, const CeedScalar *interp, 360 const CeedScalar *curl, const CeedScalar *q_ref, const CeedScalar *q_weight, CeedBasis basis) { 361 Ceed ceed, ceed_parent; 362 CeedTensorContract contract; 363 364 CeedCallBackend(CeedBasisGetCeed(basis, &ceed)); 365 CeedCallBackend(CeedGetParent(ceed, &ceed_parent)); 366 367 CeedCallBackend(CeedTensorContractCreate(ceed_parent, &contract)); 368 CeedCallBackend(CeedBasisSetTensorContract(basis, contract)); 369 370 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "Apply", CeedBasisApply_Ref)); 371 CeedCallBackend(CeedSetBackendFunction(ceed, "Basis", basis, "ApplyAdd", CeedBasisApplyAdd_Ref)); 372 return CEED_ERROR_SUCCESS; 373 } 374 375 //------------------------------------------------------------------------------ 376