1 // Copyright (c) 2017-2022, 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-impl.h> 9 #include <ceed.h> 10 #include <ceed/backend.h> 11 #include <assert.h> 12 #include <math.h> 13 #include <stdbool.h> 14 #include <stdio.h> 15 #include <string.h> 16 17 /// @file 18 /// Implementation of CeedOperator preconditioning interfaces 19 20 /// ---------------------------------------------------------------------------- 21 /// CeedOperator Library Internal Preconditioning Functions 22 /// ---------------------------------------------------------------------------- 23 /// @addtogroup CeedOperatorDeveloper 24 /// @{ 25 26 /** 27 @brief Duplicate a CeedQFunction with a reference Ceed to fallback for advanced CeedOperator functionality 28 29 @param[in] fallback_ceed Ceed on which to create fallback CeedQFunction 30 @param[in] qf CeedQFunction to create fallback for 31 @param[out] qf_fallback fallback CeedQFunction 32 33 @return An error code: 0 - success, otherwise - failure 34 35 @ref Developer 36 **/ 37 static int CeedQFunctionCreateFallback(Ceed fallback_ceed, CeedQFunction qf, CeedQFunction *qf_fallback) { 38 char *source_path_with_name = NULL; 39 40 // Check if NULL qf passed in 41 if (!qf) return CEED_ERROR_SUCCESS; 42 43 CeedDebug256(qf->ceed, 1, "---------- CeedOperator Fallback ----------\n"); 44 CeedDebug(qf->ceed, "Creating fallback CeedQFunction\n"); 45 46 if (qf->source_path) { 47 size_t path_len = strlen(qf->source_path), name_len = strlen(qf->kernel_name); 48 CeedCall(CeedCalloc(path_len + name_len + 2, &source_path_with_name)); 49 memcpy(source_path_with_name, qf->source_path, path_len); 50 memcpy(&source_path_with_name[path_len], ":", 1); 51 memcpy(&source_path_with_name[path_len + 1], qf->kernel_name, name_len); 52 } else { 53 CeedCall(CeedCalloc(1, &source_path_with_name)); 54 } 55 56 CeedCall(CeedQFunctionCreateInterior(fallback_ceed, qf->vec_length, qf->function, source_path_with_name, qf_fallback)); 57 { 58 CeedQFunctionContext ctx; 59 60 CeedCall(CeedQFunctionGetContext(qf, &ctx)); 61 CeedCall(CeedQFunctionSetContext(*qf_fallback, ctx)); 62 } 63 for (CeedInt i = 0; i < qf->num_input_fields; i++) { 64 CeedCall(CeedQFunctionAddInput(*qf_fallback, qf->input_fields[i]->field_name, qf->input_fields[i]->size, qf->input_fields[i]->eval_mode)); 65 } 66 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 67 CeedCall(CeedQFunctionAddOutput(*qf_fallback, qf->output_fields[i]->field_name, qf->output_fields[i]->size, qf->output_fields[i]->eval_mode)); 68 } 69 CeedCall(CeedFree(&source_path_with_name)); 70 return CEED_ERROR_SUCCESS; 71 } 72 73 /** 74 @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced CeedOperator functionality 75 76 @param[in,out] op CeedOperator to create fallback for 77 78 @return An error code: 0 - success, otherwise - failure 79 80 @ref Developer 81 **/ 82 static int CeedOperatorCreateFallback(CeedOperator op) { 83 Ceed ceed_fallback; 84 bool is_composite; 85 CeedOperator op_fallback; 86 87 // Check not already created 88 if (op->op_fallback) return CEED_ERROR_SUCCESS; 89 90 // Fallback Ceed 91 CeedCall(CeedGetOperatorFallbackCeed(op->ceed, &ceed_fallback)); 92 if (!ceed_fallback) return CEED_ERROR_SUCCESS; 93 94 CeedDebug256(op->ceed, 1, "---------- CeedOperator Fallback ----------\n"); 95 CeedDebug(op->ceed, "Creating fallback CeedOperator\n"); 96 97 // Clone Op 98 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 99 if (is_composite) { 100 CeedInt num_suboperators; 101 CeedOperator *sub_operators; 102 103 CeedCall(CeedCompositeOperatorCreate(ceed_fallback, &op_fallback)); 104 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 105 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 106 for (CeedInt i = 0; i < num_suboperators; i++) { 107 CeedOperator op_sub_fallback; 108 109 CeedCall(CeedOperatorGetFallback(sub_operators[i], &op_sub_fallback)); 110 CeedCall(CeedCompositeOperatorAddSub(op_fallback, op_sub_fallback)); 111 } 112 } else { 113 CeedQFunction qf_fallback = NULL, dqf_fallback = NULL, dqfT_fallback = NULL; 114 115 CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->qf, &qf_fallback)); 116 CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqf, &dqf_fallback)); 117 CeedCall(CeedQFunctionCreateFallback(ceed_fallback, op->dqfT, &dqfT_fallback)); 118 CeedCall(CeedOperatorCreate(ceed_fallback, qf_fallback, dqf_fallback, dqfT_fallback, &op_fallback)); 119 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 120 CeedCall(CeedOperatorSetField(op_fallback, op->input_fields[i]->field_name, op->input_fields[i]->elem_rstr, op->input_fields[i]->basis, 121 op->input_fields[i]->vec)); 122 } 123 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 124 CeedCall(CeedOperatorSetField(op_fallback, op->output_fields[i]->field_name, op->output_fields[i]->elem_rstr, op->output_fields[i]->basis, 125 op->output_fields[i]->vec)); 126 } 127 CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op->qf_assembled, &op_fallback->qf_assembled)); 128 if (op_fallback->num_qpts == 0) CeedCall(CeedOperatorSetNumQuadraturePoints(op_fallback, op->num_qpts)); 129 // Cleanup 130 CeedCall(CeedQFunctionDestroy(&qf_fallback)); 131 CeedCall(CeedQFunctionDestroy(&dqf_fallback)); 132 CeedCall(CeedQFunctionDestroy(&dqfT_fallback)); 133 } 134 CeedCall(CeedOperatorSetName(op_fallback, op->name)); 135 CeedCall(CeedOperatorCheckReady(op_fallback)); 136 // Note: No ref-counting here so we don't get caught in a reference loop. 137 // The op holds the only reference to op_fallback and is responsible for deleting itself and op_fallback. 138 op->op_fallback = op_fallback; 139 op_fallback->op_fallback_parent = op; 140 return CEED_ERROR_SUCCESS; 141 } 142 143 /** 144 @brief Retrieve fallback CeedOperator with a reference Ceed for advanced CeedOperator functionality 145 146 @param[in] op CeedOperator to retrieve fallback for 147 @param[out] op_fallback Fallback CeedOperator 148 149 @return An error code: 0 - success, otherwise - failure 150 151 @ref Developer 152 **/ 153 int CeedOperatorGetFallback(CeedOperator op, CeedOperator *op_fallback) { 154 // Create if needed 155 if (!op->op_fallback) CeedCall(CeedOperatorCreateFallback(op)); 156 if (op->op_fallback) { 157 bool is_debug; 158 159 CeedCall(CeedIsDebug(op->ceed, &is_debug)); 160 if (is_debug) { 161 Ceed ceed, ceed_fallback; 162 const char *resource, *resource_fallback; 163 164 CeedCall(CeedOperatorGetCeed(op, &ceed)); 165 CeedCall(CeedGetOperatorFallbackCeed(ceed, &ceed_fallback)); 166 CeedCall(CeedGetResource(ceed, &resource)); 167 CeedCall(CeedGetResource(ceed_fallback, &resource_fallback)); 168 169 CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n"); 170 CeedDebug(ceed, "Falling back from %s operator at address %ld to %s operator at address %ld\n", resource, op, resource_fallback, 171 op->op_fallback); 172 } 173 } 174 *op_fallback = op->op_fallback; 175 return CEED_ERROR_SUCCESS; 176 } 177 178 /** 179 @brief Get the parent CeedOperator for a fallback CeedOperator 180 181 @param[in] op CeedOperator context 182 @param[out] parent Variable to store parent CeedOperator context 183 184 @return An error code: 0 - success, otherwise - failure 185 186 @ref Developer 187 **/ 188 int CeedOperatorGetFallbackParent(CeedOperator op, CeedOperator *parent) { 189 *parent = op->op_fallback_parent ? op->op_fallback_parent : NULL; 190 return CEED_ERROR_SUCCESS; 191 } 192 193 /** 194 @brief Get the Ceed context of the parent CeedOperator for a fallback CeedOperator 195 196 @param[in] op CeedOperator context 197 @param[out] parent Variable to store parent Ceed context 198 199 @return An error code: 0 - success, otherwise - failure 200 201 @ref Developer 202 **/ 203 int CeedOperatorGetFallbackParentCeed(CeedOperator op, Ceed *parent) { 204 *parent = op->op_fallback_parent ? op->op_fallback_parent->ceed : op->ceed; 205 return CEED_ERROR_SUCCESS; 206 } 207 208 /** 209 @brief Select correct basis matrix pointer based on CeedEvalMode 210 211 @param[in] basis CeedBasis from which to get the basis matrix 212 @param[in] eval_mode Current basis evaluation mode 213 @param[in] identity Pointer to identity matrix 214 @param[out] basis_ptr Basis pointer to set 215 216 @ref Developer 217 **/ 218 static inline int CeedOperatorGetBasisPointer(CeedBasis basis, CeedEvalMode eval_mode, const CeedScalar *identity, const CeedScalar **basis_ptr) { 219 switch (eval_mode) { 220 case CEED_EVAL_NONE: 221 *basis_ptr = identity; 222 break; 223 case CEED_EVAL_INTERP: 224 CeedCall(CeedBasisGetInterp(basis, basis_ptr)); 225 break; 226 case CEED_EVAL_GRAD: 227 CeedCall(CeedBasisGetGrad(basis, basis_ptr)); 228 break; 229 case CEED_EVAL_DIV: 230 CeedCall(CeedBasisGetDiv(basis, basis_ptr)); 231 break; 232 case CEED_EVAL_CURL: 233 CeedCall(CeedBasisGetCurl(basis, basis_ptr)); 234 break; 235 case CEED_EVAL_WEIGHT: 236 break; // Caught by QF Assembly 237 } 238 assert(*basis_ptr != NULL); 239 return CEED_ERROR_SUCCESS; 240 } 241 242 /** 243 @brief Create point block restriction for active operator field 244 245 @param[in] rstr Original CeedElemRestriction for active field 246 @param[out] pointblock_rstr Address of the variable where the newly created CeedElemRestriction will be stored 247 248 @return An error code: 0 - success, otherwise - failure 249 250 @ref Developer 251 **/ 252 static int CeedOperatorCreateActivePointBlockRestriction(CeedElemRestriction rstr, CeedElemRestriction *pointblock_rstr) { 253 Ceed ceed; 254 CeedInt num_elem, num_comp, shift, elem_size, comp_stride, *pointblock_offsets; 255 CeedSize l_size; 256 const CeedInt *offsets; 257 258 CeedCall(CeedElemRestrictionGetCeed(rstr, &ceed)); 259 CeedCall(CeedElemRestrictionGetOffsets(rstr, CEED_MEM_HOST, &offsets)); 260 261 // Expand offsets 262 CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem)); 263 CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp)); 264 CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size)); 265 CeedCall(CeedElemRestrictionGetCompStride(rstr, &comp_stride)); 266 CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size)); 267 shift = num_comp; 268 if (comp_stride != 1) shift *= num_comp; 269 CeedCall(CeedCalloc(num_elem * elem_size, &pointblock_offsets)); 270 for (CeedInt i = 0; i < num_elem * elem_size; i++) { 271 pointblock_offsets[i] = offsets[i] * shift; 272 } 273 274 // Create new restriction 275 CeedCall(CeedElemRestrictionCreate(ceed, num_elem, elem_size, num_comp * num_comp, 1, l_size * num_comp, CEED_MEM_HOST, CEED_OWN_POINTER, 276 pointblock_offsets, pointblock_rstr)); 277 278 // Cleanup 279 CeedCall(CeedElemRestrictionRestoreOffsets(rstr, &offsets)); 280 return CEED_ERROR_SUCCESS; 281 } 282 283 /** 284 @brief Core logic for assembling operator diagonal or point block diagonal 285 286 @param[in] op CeedOperator to assemble point block diagonal 287 @param[in] request Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE 288 @param[in] is_pointblock Boolean flag to assemble diagonal or point block diagonal 289 @param[out] assembled CeedVector to store assembled diagonal 290 291 @return An error code: 0 - success, otherwise - failure 292 293 @ref Developer 294 **/ 295 static inline int CeedSingleOperatorAssembleAddDiagonal_Core(CeedOperator op, CeedRequest *request, const bool is_pointblock, CeedVector assembled) { 296 Ceed ceed; 297 CeedInt num_input_fields, num_output_fields; 298 CeedInt layout[3]; 299 const CeedScalar *assembled_qf_array; 300 CeedVector assembled_qf = NULL; 301 CeedElemRestriction assembled_elem_rstr = NULL; 302 CeedQFunction qf; 303 304 CeedCall(CeedOperatorGetCeed(op, &ceed)); 305 306 // Assemble QFunction 307 CeedCall(CeedOperatorGetQFunction(op, &qf)); 308 CeedCall(CeedQFunctionGetNumArgs(qf, &num_input_fields, &num_output_fields)); 309 CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &assembled_elem_rstr, request)); 310 CeedCall(CeedElemRestrictionGetELayout(assembled_elem_rstr, &layout)); 311 CeedCall(CeedElemRestrictionDestroy(&assembled_elem_rstr)); 312 CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array)); 313 314 // Get assembly data 315 const CeedEvalMode **eval_modes_in, **eval_modes_out; 316 CeedInt *num_eval_modes_in, *num_eval_modes_out, num_active_bases; 317 CeedSize **eval_mode_offsets_in, **eval_mode_offsets_out, num_output_components; 318 CeedElemRestriction *active_elem_rstrs; 319 CeedBasis *active_bases; 320 CeedOperatorAssemblyData data; 321 322 CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data)); 323 CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases, &num_eval_modes_in, &eval_modes_in, &eval_mode_offsets_in, 324 &num_eval_modes_out, &eval_modes_out, &eval_mode_offsets_out, &num_output_components)); 325 CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &active_bases, NULL, NULL)); 326 CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, NULL, &active_elem_rstrs)); 327 328 // Loop over all active bases 329 for (CeedInt b = 0; b < num_active_bases; b++) { 330 bool has_eval_none = false; 331 CeedInt num_elem, num_nodes, num_qpts, num_components; 332 CeedScalar *elem_diag_array, *identity = NULL; 333 CeedVector elem_diag; 334 CeedElemRestriction diag_elem_rstr; 335 336 // Assemble point block diagonal restriction, if needed 337 if (is_pointblock) { 338 CeedCall(CeedOperatorCreateActivePointBlockRestriction(active_elem_rstrs[b], &diag_elem_rstr)); 339 } else { 340 CeedCall(CeedElemRestrictionCreateUnsignedCopy(active_elem_rstrs[b], &diag_elem_rstr)); 341 } 342 343 // Create diagonal vector 344 CeedCall(CeedElemRestrictionCreateVector(diag_elem_rstr, NULL, &elem_diag)); 345 346 // Assemble element operator diagonals 347 CeedCall(CeedVectorSetValue(elem_diag, 0.0)); 348 CeedCall(CeedVectorGetArray(elem_diag, CEED_MEM_HOST, &elem_diag_array)); 349 CeedCall(CeedElemRestrictionGetNumElements(diag_elem_rstr, &num_elem)); 350 CeedCall(CeedBasisGetNumNodes(active_bases[b], &num_nodes)); 351 CeedCall(CeedBasisGetNumComponents(active_bases[b], &num_components)); 352 CeedCall(CeedBasisGetNumQuadraturePoints(active_bases[b], &num_qpts)); 353 354 // Construct identity matrix for basis if required 355 for (CeedInt i = 0; i < num_eval_modes_in[b]; i++) { 356 has_eval_none = has_eval_none || (eval_modes_in[b][i] == CEED_EVAL_NONE); 357 } 358 for (CeedInt i = 0; i < num_eval_modes_out[b]; i++) { 359 has_eval_none = has_eval_none || (eval_modes_out[b][i] == CEED_EVAL_NONE); 360 } 361 if (has_eval_none) { 362 CeedCall(CeedCalloc(num_qpts * num_nodes, &identity)); 363 for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) identity[i * num_nodes + i] = 1.0; 364 } 365 366 // Compute the diagonal of B^T D B 367 // Each element 368 for (CeedSize e = 0; e < num_elem; e++) { 369 // Each basis eval mode pair 370 CeedInt d_out = 0, q_comp_out; 371 CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE; 372 373 for (CeedInt e_out = 0; e_out < num_eval_modes_out[b]; e_out++) { 374 CeedInt d_in = 0, q_comp_in; 375 const CeedScalar *B_t = NULL; 376 CeedEvalMode eval_mode_in_prev = CEED_EVAL_NONE; 377 378 CeedOperatorGetBasisPointer(active_bases[b], eval_modes_out[b][e_out], identity, &B_t); 379 CeedCall(CeedBasisGetNumQuadratureComponents(active_bases[b], eval_modes_out[b][e_out], &q_comp_out)); 380 if (q_comp_out > 1) { 381 if (e_out == 0 || eval_modes_out[b][e_out] != eval_mode_out_prev) d_out = 0; 382 else B_t = &B_t[(++d_out) * num_qpts * num_nodes]; 383 } 384 eval_mode_out_prev = eval_modes_out[b][e_out]; 385 386 for (CeedInt e_in = 0; e_in < num_eval_modes_in[b]; e_in++) { 387 const CeedScalar *B = NULL; 388 389 CeedOperatorGetBasisPointer(active_bases[b], eval_modes_in[b][e_in], identity, &B); 390 CeedCall(CeedBasisGetNumQuadratureComponents(active_bases[b], eval_modes_in[b][e_in], &q_comp_in)); 391 if (q_comp_in > 1) { 392 if (e_in == 0 || eval_modes_in[b][e_in] != eval_mode_in_prev) d_in = 0; 393 else B = &B[(++d_in) * num_qpts * num_nodes]; 394 } 395 eval_mode_in_prev = eval_modes_in[b][e_in]; 396 397 // Each component 398 for (CeedInt c_out = 0; c_out < num_components; c_out++) { 399 // Each qpt/node pair 400 for (CeedInt q = 0; q < num_qpts; q++) { 401 if (is_pointblock) { 402 // Point Block Diagonal 403 for (CeedInt c_in = 0; c_in < num_components; c_in++) { 404 const CeedSize c_offset = (eval_mode_offsets_in[b][e_in] + c_in) * num_output_components + eval_mode_offsets_out[b][e_out] + c_out; 405 const CeedScalar qf_value = assembled_qf_array[q * layout[0] + c_offset * layout[1] + e * layout[2]]; 406 407 for (CeedInt n = 0; n < num_nodes; n++) { 408 elem_diag_array[((e * num_components + c_out) * num_components + c_in) * num_nodes + n] += 409 B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n]; 410 } 411 } 412 } else { 413 // Diagonal Only 414 const CeedInt c_offset = (eval_mode_offsets_in[b][e_in] + c_out) * num_output_components + eval_mode_offsets_out[b][e_out] + c_out; 415 const CeedScalar qf_value = assembled_qf_array[q * layout[0] + c_offset * layout[1] + e * layout[2]]; 416 417 for (CeedInt n = 0; n < num_nodes; n++) { 418 elem_diag_array[(e * num_components + c_out) * num_nodes + n] += B_t[q * num_nodes + n] * qf_value * B[q * num_nodes + n]; 419 } 420 } 421 } 422 } 423 } 424 } 425 } 426 CeedCall(CeedVectorRestoreArray(elem_diag, &elem_diag_array)); 427 428 // Assemble local operator diagonal 429 CeedCall(CeedElemRestrictionApply(diag_elem_rstr, CEED_TRANSPOSE, elem_diag, assembled, request)); 430 431 // Cleanup 432 CeedCall(CeedElemRestrictionDestroy(&diag_elem_rstr)); 433 CeedCall(CeedVectorDestroy(&elem_diag)); 434 CeedCall(CeedFree(&identity)); 435 } 436 CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array)); 437 CeedCall(CeedVectorDestroy(&assembled_qf)); 438 return CEED_ERROR_SUCCESS; 439 } 440 441 /** 442 @brief Core logic for assembling composite operator diagonal 443 444 @param[in] op CeedOperator to assemble point block diagonal 445 @param[in] request Address of CeedRequest for non-blocking completion, else CEED_REQUEST_IMMEDIATE 446 @param[in] is_pointblock Boolean flag to assemble diagonal or point block diagonal 447 @param[out] assembled CeedVector to store assembled diagonal 448 449 @return An error code: 0 - success, otherwise - failure 450 451 @ref Developer 452 **/ 453 static inline int CeedCompositeOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedRequest *request, const bool is_pointblock, 454 CeedVector assembled) { 455 CeedInt num_sub; 456 CeedOperator *suboperators; 457 458 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 459 CeedCall(CeedCompositeOperatorGetSubList(op, &suboperators)); 460 for (CeedInt i = 0; i < num_sub; i++) { 461 if (is_pointblock) { 462 CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(suboperators[i], assembled, request)); 463 } else { 464 CeedCall(CeedOperatorLinearAssembleAddDiagonal(suboperators[i], assembled, request)); 465 } 466 } 467 return CEED_ERROR_SUCCESS; 468 } 469 470 /** 471 @brief Build nonzero pattern for non-composite operator 472 473 Users should generally use CeedOperatorLinearAssembleSymbolic() 474 475 @param[in] op CeedOperator to assemble nonzero pattern 476 @param[in] offset Offset for number of entries 477 @param[out] rows Row number for each entry 478 @param[out] cols Column number for each entry 479 480 @return An error code: 0 - success, otherwise - failure 481 482 @ref Developer 483 **/ 484 static int CeedSingleOperatorAssembleSymbolic(CeedOperator op, CeedInt offset, CeedInt *rows, CeedInt *cols) { 485 Ceed ceed; 486 bool is_composite; 487 CeedInt num_elem, elem_size, num_comp, layout_er[3], local_num_entries; 488 CeedSize num_nodes, count = 0; 489 CeedScalar *array; 490 const CeedScalar *elem_dof_a; 491 CeedVector index_vec, elem_dof; 492 CeedElemRestriction active_rstr, index_elem_rstr; 493 494 CeedCall(CeedOperatorGetCeed(op, &ceed)); 495 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 496 CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported"); 497 498 CeedCall(CeedOperatorGetActiveVectorLengths(op, &num_nodes, NULL)); 499 CeedCall(CeedOperatorGetActiveElemRestriction(op, &active_rstr)); 500 CeedCall(CeedElemRestrictionCreateUnorientedCopy(active_rstr, &index_elem_rstr)); 501 CeedCall(CeedElemRestrictionGetNumElements(index_elem_rstr, &num_elem)); 502 CeedCall(CeedElemRestrictionGetElementSize(index_elem_rstr, &elem_size)); 503 CeedCall(CeedElemRestrictionGetNumComponents(index_elem_rstr, &num_comp)); 504 CeedCall(CeedElemRestrictionGetELayout(index_elem_rstr, &layout_er)); 505 local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem; 506 507 // Determine elem_dof relation 508 CeedCall(CeedVectorCreate(ceed, num_nodes, &index_vec)); 509 CeedCall(CeedVectorGetArrayWrite(index_vec, CEED_MEM_HOST, &array)); 510 for (CeedInt i = 0; i < num_nodes; i++) array[i] = i; 511 CeedCall(CeedVectorRestoreArray(index_vec, &array)); 512 CeedCall(CeedVectorCreate(ceed, num_elem * elem_size * num_comp, &elem_dof)); 513 CeedCall(CeedVectorSetValue(elem_dof, 0.0)); 514 CeedCall(CeedElemRestrictionApply(index_elem_rstr, CEED_NOTRANSPOSE, index_vec, elem_dof, CEED_REQUEST_IMMEDIATE)); 515 CeedCall(CeedVectorGetArrayRead(elem_dof, CEED_MEM_HOST, &elem_dof_a)); 516 CeedCall(CeedVectorDestroy(&index_vec)); 517 518 // Determine i, j locations for element matrices 519 for (CeedInt e = 0; e < num_elem; e++) { 520 for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) { 521 for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) { 522 for (CeedInt i = 0; i < elem_size; i++) { 523 for (CeedInt j = 0; j < elem_size; j++) { 524 const CeedInt elem_dof_index_row = i * layout_er[0] + (comp_out)*layout_er[1] + e * layout_er[2]; 525 const CeedInt elem_dof_index_col = j * layout_er[0] + comp_in * layout_er[1] + e * layout_er[2]; 526 const CeedInt row = elem_dof_a[elem_dof_index_row]; 527 const CeedInt col = elem_dof_a[elem_dof_index_col]; 528 529 rows[offset + count] = row; 530 cols[offset + count] = col; 531 count++; 532 } 533 } 534 } 535 } 536 } 537 CeedCheck(count == local_num_entries, ceed, CEED_ERROR_MAJOR, "Error computing assembled entries"); 538 CeedCall(CeedVectorRestoreArrayRead(elem_dof, &elem_dof_a)); 539 CeedCall(CeedVectorDestroy(&elem_dof)); 540 CeedCall(CeedElemRestrictionDestroy(&index_elem_rstr)); 541 return CEED_ERROR_SUCCESS; 542 } 543 544 /** 545 @brief Assemble nonzero entries for non-composite operator 546 547 Users should generally use CeedOperatorLinearAssemble() 548 549 @param[in] op CeedOperator to assemble 550 @param[in] offset Offset for number of entries 551 @param[out] values Values to assemble into matrix 552 553 @return An error code: 0 - success, otherwise - failure 554 555 @ref Developer 556 **/ 557 static int CeedSingleOperatorAssemble(CeedOperator op, CeedInt offset, CeedVector values) { 558 Ceed ceed; 559 bool is_composite; 560 561 CeedCall(CeedOperatorGetCeed(op, &ceed)); 562 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 563 564 CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported"); 565 566 // Early exit for empty operator 567 { 568 CeedInt num_elem = 0; 569 570 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 571 if (num_elem == 0) return CEED_ERROR_SUCCESS; 572 } 573 574 if (op->LinearAssembleSingle) { 575 // Backend version 576 CeedCall(op->LinearAssembleSingle(op, offset, values)); 577 return CEED_ERROR_SUCCESS; 578 } else { 579 // Operator fallback 580 CeedOperator op_fallback; 581 582 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 583 if (op_fallback) { 584 CeedCall(CeedSingleOperatorAssemble(op_fallback, offset, values)); 585 return CEED_ERROR_SUCCESS; 586 } 587 } 588 589 // Assemble QFunction 590 const bool *orients = NULL; 591 const CeedInt8 *curl_orients = NULL; 592 CeedInt *num_eval_modes_in, *num_eval_modes_out, num_active_bases, num_input_fields, num_output_fields, num_elem, elem_size, num_qpts, num_comp, 593 local_num_entries, layout_qf[3]; 594 const CeedScalar *assembled_qf_array; 595 CeedVector assembled_qf = NULL; 596 CeedRestrictionType rstr_type; 597 CeedElemRestriction rstr_q = NULL, active_rstr; 598 const CeedEvalMode **eval_modes_in, **eval_modes_out; 599 CeedBasis *bases, basis_in; 600 CeedQFunction qf; 601 CeedOperatorAssemblyData data; 602 CeedOperatorField *input_fields, *output_fields; 603 604 CeedCall(CeedOperatorGetQFunction(op, &qf)); 605 CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled_qf, &rstr_q, CEED_REQUEST_IMMEDIATE)); 606 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 607 608 // Get assembly data 609 CeedCall(CeedOperatorGetOperatorAssemblyData(op, &data)); 610 CeedCall(CeedOperatorAssemblyDataGetEvalModes(data, &num_active_bases, &num_eval_modes_in, &eval_modes_in, NULL, &num_eval_modes_out, 611 &eval_modes_out, NULL, NULL)); 612 CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, &bases, NULL, NULL)); 613 basis_in = bases[0]; 614 615 CeedCheck(num_active_bases == 1, ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator with multiple active bases"); 616 CeedCheck(num_eval_modes_in[0] > 0 && num_eval_modes_out[0] > 0, ceed, CEED_ERROR_UNSUPPORTED, "Cannot assemble operator with out inputs/outputs"); 617 618 CeedCall(CeedOperatorGetActiveElemRestriction(op, &active_rstr)); 619 CeedCall(CeedElemRestrictionGetNumElements(active_rstr, &num_elem)); 620 CeedCall(CeedElemRestrictionGetElementSize(active_rstr, &elem_size)); 621 CeedCall(CeedElemRestrictionGetNumComponents(active_rstr, &num_comp)); 622 CeedCall(CeedBasisGetNumQuadraturePoints(basis_in, &num_qpts)); 623 local_num_entries = elem_size * num_comp * elem_size * num_comp * num_elem; 624 625 CeedCall(CeedElemRestrictionGetType(active_rstr, &rstr_type)); 626 if (rstr_type == CEED_RESTRICTION_ORIENTED) { 627 CeedCall(CeedElemRestrictionGetOrientations(active_rstr, CEED_MEM_HOST, &orients)); 628 } else if (rstr_type == CEED_RESTRICTION_CURL_ORIENTED) { 629 CeedCall(CeedElemRestrictionGetCurlOrientations(active_rstr, CEED_MEM_HOST, &curl_orients)); 630 } 631 632 // Loop over elements and put in data structure 633 CeedCall(CeedVectorGetArrayRead(assembled_qf, CEED_MEM_HOST, &assembled_qf_array)); 634 CeedCall(CeedElemRestrictionGetELayout(rstr_q, &layout_qf)); 635 CeedCall(CeedElemRestrictionDestroy(&rstr_q)); 636 637 // We store B_mat_in, B_mat_out, BTD, elem_mat in row-major order 638 CeedSize count = 0; 639 CeedScalar *vals, BTD_mat[elem_size * num_qpts * num_eval_modes_in[0]], elem_mat[elem_size * elem_size]; 640 const CeedScalar **B_mats_in, **B_mats_out; 641 CeedCall(CeedOperatorAssemblyDataGetBases(data, NULL, NULL, &B_mats_in, &B_mats_out)); 642 const CeedScalar *B_mat_in = B_mats_in[0], *B_mat_out = B_mats_out[0]; 643 644 CeedCall(CeedVectorGetArray(values, CEED_MEM_HOST, &vals)); 645 for (CeedSize e = 0; e < num_elem; e++) { 646 for (CeedInt comp_in = 0; comp_in < num_comp; comp_in++) { 647 for (CeedInt comp_out = 0; comp_out < num_comp; comp_out++) { 648 // Compute B^T*D 649 for (CeedSize n = 0; n < elem_size; n++) { 650 for (CeedSize q = 0; q < num_qpts; q++) { 651 for (CeedInt e_in = 0; e_in < num_eval_modes_in[0]; e_in++) { 652 const CeedSize btd_index = n * (num_qpts * num_eval_modes_in[0]) + (num_eval_modes_in[0] * q + e_in); 653 CeedScalar sum = 0.0; 654 655 for (CeedInt e_out = 0; e_out < num_eval_modes_out[0]; e_out++) { 656 const CeedSize b_out_index = (num_eval_modes_out[0] * q + e_out) * elem_size + n; 657 const CeedSize eval_mode_index = ((e_in * num_comp + comp_in) * num_eval_modes_out[0] + e_out) * num_comp + comp_out; 658 const CeedSize qf_index = q * layout_qf[0] + eval_mode_index * layout_qf[1] + e * layout_qf[2]; 659 660 sum += B_mat_out[b_out_index] * assembled_qf_array[qf_index]; 661 } 662 BTD_mat[btd_index] = sum; 663 } 664 } 665 } 666 667 // Form element matrix itself (for each block component) 668 CeedCall(CeedMatrixMatrixMultiply(ceed, BTD_mat, B_mat_in, elem_mat, elem_size, elem_size, num_qpts * num_eval_modes_in[0])); 669 670 // Transform the element matrix if required 671 if (orients) { 672 const bool *elem_orients = &orients[e * elem_size]; 673 674 for (CeedInt i = 0; i < elem_size; i++) { 675 for (CeedInt j = 0; j < elem_size; j++) { 676 elem_mat[i * elem_size + j] *= elem_orients[i] ? -1.0 : 1.0; 677 elem_mat[i * elem_size + j] *= elem_orients[j] ? -1.0 : 1.0; 678 } 679 } 680 } else if (curl_orients) { 681 const CeedInt8 *elem_curl_orients = &curl_orients[e * 3 * elem_size]; 682 CeedScalar o_elem_mat[elem_size * elem_size]; 683 684 // T^T*(B^T*D*B) 685 for (CeedInt i = 0; i < elem_size; i++) { 686 for (CeedInt j = 0; j < elem_size; j++) { 687 o_elem_mat[i * elem_size + j] = elem_mat[i * elem_size + j] * elem_curl_orients[3 * i + 1] + 688 (i > 0 ? elem_mat[(i - 1) * elem_size + j] * elem_curl_orients[3 * i - 1] : 0.0) + 689 (i < elem_size - 1 ? elem_mat[(i + 1) * elem_size + j] * elem_curl_orients[3 * i + 3] : 0.0); 690 } 691 } 692 // T^T*(B^T*D*B)*T 693 for (CeedInt i = 0; i < elem_size; i++) { 694 for (CeedInt j = 0; j < elem_size; j++) { 695 elem_mat[i * elem_size + j] = o_elem_mat[i * elem_size + j] * elem_curl_orients[3 * j + 1] + 696 (j > 0 ? o_elem_mat[i * elem_size + j - 1] * elem_curl_orients[3 * j - 1] : 0.0) + 697 (j < elem_size - 1 ? o_elem_mat[i * elem_size + j + 1] * elem_curl_orients[3 * j + 3] : 0.0); 698 } 699 } 700 } 701 702 // Put element matrix in coordinate data structure 703 for (CeedInt i = 0; i < elem_size; i++) { 704 for (CeedInt j = 0; j < elem_size; j++) { 705 vals[offset + count] = elem_mat[i * elem_size + j]; 706 count++; 707 } 708 } 709 } 710 } 711 } 712 CeedCheck(count == local_num_entries, ceed, CEED_ERROR_MAJOR, "Error computing entries"); 713 CeedCall(CeedVectorRestoreArray(values, &vals)); 714 715 CeedCall(CeedVectorRestoreArrayRead(assembled_qf, &assembled_qf_array)); 716 CeedCall(CeedVectorDestroy(&assembled_qf)); 717 718 if (rstr_type == CEED_RESTRICTION_ORIENTED) { 719 CeedCall(CeedElemRestrictionRestoreOrientations(active_rstr, &orients)); 720 } else if (rstr_type == CEED_RESTRICTION_CURL_ORIENTED) { 721 CeedCall(CeedElemRestrictionRestoreCurlOrientations(active_rstr, &curl_orients)); 722 } 723 return CEED_ERROR_SUCCESS; 724 } 725 726 /** 727 @brief Count number of entries for assembled CeedOperator 728 729 @param[in] op CeedOperator to assemble 730 @param[out] num_entries Number of entries in assembled representation 731 732 @return An error code: 0 - success, otherwise - failure 733 734 @ref Utility 735 **/ 736 static int CeedSingleOperatorAssemblyCountEntries(CeedOperator op, CeedSize *num_entries) { 737 bool is_composite; 738 CeedInt num_elem, elem_size, num_comp; 739 CeedElemRestriction rstr; 740 741 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 742 CeedCheck(!is_composite, op->ceed, CEED_ERROR_UNSUPPORTED, "Composite operator not supported"); 743 CeedCall(CeedOperatorGetActiveElemRestriction(op, &rstr)); 744 CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem)); 745 CeedCall(CeedElemRestrictionGetElementSize(rstr, &elem_size)); 746 CeedCall(CeedElemRestrictionGetNumComponents(rstr, &num_comp)); 747 *num_entries = (CeedSize)elem_size * num_comp * elem_size * num_comp * num_elem; 748 return CEED_ERROR_SUCCESS; 749 } 750 751 /** 752 @brief Common code for creating a multigrid coarse operator and level transfer operators for a CeedOperator 753 754 @param[in] op_fine Fine grid operator 755 @param[in] p_mult_fine L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators 756 @param[in] rstr_coarse Coarse grid restriction 757 @param[in] basis_coarse Coarse grid active vector basis 758 @param[in] basis_c_to_f Basis for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators 759 @param[out] op_coarse Coarse grid operator 760 @param[out] op_prolong Coarse to fine operator, or NULL 761 @param[out] op_rstrict Fine to coarse operator, or NULL 762 763 @return An error code: 0 - success, otherwise - failure 764 765 @ref Developer 766 **/ 767 static int CeedSingleOperatorMultigridLevel(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse, 768 CeedBasis basis_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_rstrict) { 769 bool is_composite; 770 Ceed ceed; 771 CeedInt num_comp; 772 CeedVector mult_vec = NULL; 773 CeedElemRestriction rstr_p_mult_fine = NULL, rstr_fine = NULL; 774 775 CeedCall(CeedOperatorGetCeed(op_fine, &ceed)); 776 777 // Check for composite operator 778 CeedCall(CeedOperatorIsComposite(op_fine, &is_composite)); 779 CeedCheck(!is_composite, ceed, CEED_ERROR_UNSUPPORTED, "Automatic multigrid setup for composite operators not supported"); 780 781 // Coarse Grid 782 CeedCall(CeedOperatorCreate(ceed, op_fine->qf, op_fine->dqf, op_fine->dqfT, op_coarse)); 783 // -- Clone input fields 784 for (CeedInt i = 0; i < op_fine->qf->num_input_fields; i++) { 785 if (op_fine->input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 786 rstr_fine = op_fine->input_fields[i]->elem_rstr; 787 CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE)); 788 } else { 789 CeedCall(CeedOperatorSetField(*op_coarse, op_fine->input_fields[i]->field_name, op_fine->input_fields[i]->elem_rstr, 790 op_fine->input_fields[i]->basis, op_fine->input_fields[i]->vec)); 791 } 792 } 793 // -- Clone output fields 794 for (CeedInt i = 0; i < op_fine->qf->num_output_fields; i++) { 795 if (op_fine->output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 796 CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, rstr_coarse, basis_coarse, CEED_VECTOR_ACTIVE)); 797 } else { 798 CeedCall(CeedOperatorSetField(*op_coarse, op_fine->output_fields[i]->field_name, op_fine->output_fields[i]->elem_rstr, 799 op_fine->output_fields[i]->basis, op_fine->output_fields[i]->vec)); 800 } 801 } 802 // -- Clone QFunctionAssemblyData 803 CeedCall(CeedQFunctionAssemblyDataReferenceCopy(op_fine->qf_assembled, &(*op_coarse)->qf_assembled)); 804 805 // Multiplicity vector 806 if (op_rstrict || op_prolong) { 807 CeedVector mult_e_vec; 808 CeedRestrictionType rstr_type; 809 810 CeedCall(CeedElemRestrictionGetType(rstr_fine, &rstr_type)); 811 CeedCheck(rstr_type != CEED_RESTRICTION_CURL_ORIENTED, ceed, CEED_ERROR_UNSUPPORTED, 812 "Element restrictions created with CeedElemRestrictionCreateCurlOriented are not supported"); 813 CeedCheck(p_mult_fine, ceed, CEED_ERROR_INCOMPATIBLE, "Prolongation or restriction operator creation requires fine grid multiplicity vector"); 814 CeedCall(CeedElemRestrictionCreateUnsignedCopy(rstr_fine, &rstr_p_mult_fine)); 815 CeedCall(CeedElemRestrictionCreateVector(rstr_fine, &mult_vec, &mult_e_vec)); 816 CeedCall(CeedVectorSetValue(mult_e_vec, 0.0)); 817 CeedCall(CeedElemRestrictionApply(rstr_p_mult_fine, CEED_NOTRANSPOSE, p_mult_fine, mult_e_vec, CEED_REQUEST_IMMEDIATE)); 818 CeedCall(CeedVectorSetValue(mult_vec, 0.0)); 819 CeedCall(CeedElemRestrictionApply(rstr_p_mult_fine, CEED_TRANSPOSE, mult_e_vec, mult_vec, CEED_REQUEST_IMMEDIATE)); 820 CeedCall(CeedVectorDestroy(&mult_e_vec)); 821 CeedCall(CeedVectorReciprocal(mult_vec)); 822 } 823 824 // Clone name 825 bool has_name = op_fine->name; 826 size_t name_len = op_fine->name ? strlen(op_fine->name) : 0; 827 CeedCall(CeedOperatorSetName(*op_coarse, op_fine->name)); 828 829 // Check that coarse to fine basis is provided if prolong/rstrict operators are requested 830 CeedCheck(basis_c_to_f || (!op_rstrict && !op_prolong), ceed, CEED_ERROR_INCOMPATIBLE, 831 "Prolongation or restriction operator creation requires coarse-to-fine basis"); 832 833 // Restriction/Prolongation Operators 834 CeedCall(CeedBasisGetNumComponents(basis_coarse, &num_comp)); 835 836 // Restriction 837 if (op_rstrict) { 838 CeedInt *num_comp_r_data; 839 CeedQFunctionContext ctx_r; 840 CeedQFunction qf_rstrict; 841 842 CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_rstrict)); 843 CeedCall(CeedCalloc(1, &num_comp_r_data)); 844 num_comp_r_data[0] = num_comp; 845 CeedCall(CeedQFunctionContextCreate(ceed, &ctx_r)); 846 CeedCall(CeedQFunctionContextSetData(ctx_r, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_r_data), num_comp_r_data)); 847 CeedCall(CeedQFunctionSetContext(qf_rstrict, ctx_r)); 848 CeedCall(CeedQFunctionContextDestroy(&ctx_r)); 849 CeedCall(CeedQFunctionAddInput(qf_rstrict, "input", num_comp, CEED_EVAL_NONE)); 850 CeedCall(CeedQFunctionAddInput(qf_rstrict, "scale", num_comp, CEED_EVAL_NONE)); 851 CeedCall(CeedQFunctionAddOutput(qf_rstrict, "output", num_comp, CEED_EVAL_INTERP)); 852 CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_rstrict, num_comp)); 853 854 CeedCall(CeedOperatorCreate(ceed, qf_rstrict, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_rstrict)); 855 CeedCall(CeedOperatorSetField(*op_rstrict, "input", rstr_fine, CEED_BASIS_NONE, CEED_VECTOR_ACTIVE)); 856 CeedCall(CeedOperatorSetField(*op_rstrict, "scale", rstr_p_mult_fine, CEED_BASIS_NONE, mult_vec)); 857 CeedCall(CeedOperatorSetField(*op_rstrict, "output", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE)); 858 859 // Set name 860 char *restriction_name; 861 862 CeedCall(CeedCalloc(17 + name_len, &restriction_name)); 863 sprintf(restriction_name, "restriction%s%s", has_name ? " for " : "", has_name ? op_fine->name : ""); 864 CeedCall(CeedOperatorSetName(*op_rstrict, restriction_name)); 865 CeedCall(CeedFree(&restriction_name)); 866 867 // Check 868 CeedCall(CeedOperatorCheckReady(*op_rstrict)); 869 870 // Cleanup 871 CeedCall(CeedQFunctionDestroy(&qf_rstrict)); 872 } 873 874 // Prolongation 875 if (op_prolong) { 876 CeedInt *num_comp_p_data; 877 CeedQFunctionContext ctx_p; 878 CeedQFunction qf_prolong; 879 880 CeedCall(CeedQFunctionCreateInteriorByName(ceed, "Scale", &qf_prolong)); 881 CeedCall(CeedCalloc(1, &num_comp_p_data)); 882 num_comp_p_data[0] = num_comp; 883 CeedCall(CeedQFunctionContextCreate(ceed, &ctx_p)); 884 CeedCall(CeedQFunctionContextSetData(ctx_p, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_p_data), num_comp_p_data)); 885 CeedCall(CeedQFunctionSetContext(qf_prolong, ctx_p)); 886 CeedCall(CeedQFunctionContextDestroy(&ctx_p)); 887 CeedCall(CeedQFunctionAddInput(qf_prolong, "input", num_comp, CEED_EVAL_INTERP)); 888 CeedCall(CeedQFunctionAddInput(qf_prolong, "scale", num_comp, CEED_EVAL_NONE)); 889 CeedCall(CeedQFunctionAddOutput(qf_prolong, "output", num_comp, CEED_EVAL_NONE)); 890 CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_prolong, num_comp)); 891 892 CeedCall(CeedOperatorCreate(ceed, qf_prolong, CEED_QFUNCTION_NONE, CEED_QFUNCTION_NONE, op_prolong)); 893 CeedCall(CeedOperatorSetField(*op_prolong, "input", rstr_coarse, basis_c_to_f, CEED_VECTOR_ACTIVE)); 894 CeedCall(CeedOperatorSetField(*op_prolong, "scale", rstr_p_mult_fine, CEED_BASIS_NONE, mult_vec)); 895 CeedCall(CeedOperatorSetField(*op_prolong, "output", rstr_fine, CEED_BASIS_NONE, CEED_VECTOR_ACTIVE)); 896 897 // Set name 898 char *prolongation_name; 899 900 CeedCall(CeedCalloc(18 + name_len, &prolongation_name)); 901 sprintf(prolongation_name, "prolongation%s%s", has_name ? " for " : "", has_name ? op_fine->name : ""); 902 CeedCall(CeedOperatorSetName(*op_prolong, prolongation_name)); 903 CeedCall(CeedFree(&prolongation_name)); 904 905 // Check 906 CeedCall(CeedOperatorCheckReady(*op_prolong)); 907 908 // Cleanup 909 CeedCall(CeedQFunctionDestroy(&qf_prolong)); 910 } 911 912 // Check 913 CeedCall(CeedOperatorCheckReady(*op_coarse)); 914 915 // Cleanup 916 CeedCall(CeedVectorDestroy(&mult_vec)); 917 CeedCall(CeedElemRestrictionDestroy(&rstr_p_mult_fine)); 918 CeedCall(CeedBasisDestroy(&basis_c_to_f)); 919 return CEED_ERROR_SUCCESS; 920 } 921 922 /** 923 @brief Build 1D mass matrix and Laplacian with perturbation 924 925 @param[in] interp_1d Interpolation matrix in one dimension 926 @param[in] grad_1d Gradient matrix in one dimension 927 @param[in] q_weight_1d Quadrature weights in one dimension 928 @param[in] P_1d Number of basis nodes in one dimension 929 @param[in] Q_1d Number of quadrature points in one dimension 930 @param[in] dim Dimension of basis 931 @param[out] mass Assembled mass matrix in one dimension 932 @param[out] laplace Assembled perturbed Laplacian in one dimension 933 934 @return An error code: 0 - success, otherwise - failure 935 936 @ref Developer 937 **/ 938 CeedPragmaOptimizeOff 939 static int CeedBuildMassLaplace(const CeedScalar *interp_1d, const CeedScalar *grad_1d, const CeedScalar *q_weight_1d, CeedInt P_1d, CeedInt Q_1d, 940 CeedInt dim, CeedScalar *mass, CeedScalar *laplace) { 941 for (CeedInt i = 0; i < P_1d; i++) { 942 for (CeedInt j = 0; j < P_1d; j++) { 943 CeedScalar sum = 0.0; 944 for (CeedInt k = 0; k < Q_1d; k++) sum += interp_1d[k * P_1d + i] * q_weight_1d[k] * interp_1d[k * P_1d + j]; 945 mass[i + j * P_1d] = sum; 946 } 947 } 948 // -- Laplacian 949 for (CeedInt i = 0; i < P_1d; i++) { 950 for (CeedInt j = 0; j < P_1d; j++) { 951 CeedScalar sum = 0.0; 952 953 for (CeedInt k = 0; k < Q_1d; k++) sum += grad_1d[k * P_1d + i] * q_weight_1d[k] * grad_1d[k * P_1d + j]; 954 laplace[i + j * P_1d] = sum; 955 } 956 } 957 CeedScalar perturbation = dim > 2 ? 1e-6 : 1e-4; 958 for (CeedInt i = 0; i < P_1d; i++) laplace[i + P_1d * i] += perturbation; 959 return CEED_ERROR_SUCCESS; 960 } 961 CeedPragmaOptimizeOn 962 963 /// @} 964 965 /// ---------------------------------------------------------------------------- 966 /// CeedOperator Backend API 967 /// ---------------------------------------------------------------------------- 968 /// @addtogroup CeedOperatorBackend 969 /// @{ 970 971 /** 972 @brief Create object holding CeedQFunction assembly data for CeedOperator 973 974 @param[in] ceed A Ceed object where the CeedQFunctionAssemblyData will be created 975 @param[out] data Address of the variable where the newly created CeedQFunctionAssemblyData will be stored 976 977 @return An error code: 0 - success, otherwise - failure 978 979 @ref Backend 980 **/ 981 int CeedQFunctionAssemblyDataCreate(Ceed ceed, CeedQFunctionAssemblyData *data) { 982 CeedCall(CeedCalloc(1, data)); 983 (*data)->ref_count = 1; 984 (*data)->ceed = ceed; 985 CeedCall(CeedReference(ceed)); 986 return CEED_ERROR_SUCCESS; 987 } 988 989 /** 990 @brief Increment the reference counter for a CeedQFunctionAssemblyData 991 992 @param[in,out] data CeedQFunctionAssemblyData to increment the reference counter 993 994 @return An error code: 0 - success, otherwise - failure 995 996 @ref Backend 997 **/ 998 int CeedQFunctionAssemblyDataReference(CeedQFunctionAssemblyData data) { 999 data->ref_count++; 1000 return CEED_ERROR_SUCCESS; 1001 } 1002 1003 /** 1004 @brief Set re-use of CeedQFunctionAssemblyData 1005 1006 @param[in,out] data CeedQFunctionAssemblyData to mark for reuse 1007 @param[in] reuse_data Boolean flag indicating data re-use 1008 1009 @return An error code: 0 - success, otherwise - failure 1010 1011 @ref Backend 1012 **/ 1013 int CeedQFunctionAssemblyDataSetReuse(CeedQFunctionAssemblyData data, bool reuse_data) { 1014 data->reuse_data = reuse_data; 1015 data->needs_data_update = true; 1016 return CEED_ERROR_SUCCESS; 1017 } 1018 1019 /** 1020 @brief Mark QFunctionAssemblyData as stale 1021 1022 @param[in,out] data CeedQFunctionAssemblyData to mark as stale 1023 @param[in] needs_data_update Boolean flag indicating if update is needed or completed 1024 1025 @return An error code: 0 - success, otherwise - failure 1026 1027 @ref Backend 1028 **/ 1029 int CeedQFunctionAssemblyDataSetUpdateNeeded(CeedQFunctionAssemblyData data, bool needs_data_update) { 1030 data->needs_data_update = needs_data_update; 1031 return CEED_ERROR_SUCCESS; 1032 } 1033 1034 /** 1035 @brief Determine if QFunctionAssemblyData needs update 1036 1037 @param[in] data CeedQFunctionAssemblyData to mark as stale 1038 @param[out] is_update_needed Boolean flag indicating if re-assembly is required 1039 1040 @return An error code: 0 - success, otherwise - failure 1041 1042 @ref Backend 1043 **/ 1044 int CeedQFunctionAssemblyDataIsUpdateNeeded(CeedQFunctionAssemblyData data, bool *is_update_needed) { 1045 *is_update_needed = !data->reuse_data || data->needs_data_update; 1046 return CEED_ERROR_SUCCESS; 1047 } 1048 1049 /** 1050 @brief Copy the pointer to a CeedQFunctionAssemblyData. 1051 1052 Both pointers should be destroyed with `CeedCeedQFunctionAssemblyDataDestroy()`. 1053 1054 Note: If the value of `data_copy` passed to this function is non-NULL, then it is assumed that `*data_copy` is a pointer to a 1055 CeedQFunctionAssemblyData. This CeedQFunctionAssemblyData will be destroyed if `data_copy` is the only reference to this 1056 CeedQFunctionAssemblyData. 1057 1058 @param[in] data CeedQFunctionAssemblyData to copy reference to 1059 @param[in,out] data_copy Variable to store copied reference 1060 1061 @return An error code: 0 - success, otherwise - failure 1062 1063 @ref Backend 1064 **/ 1065 int CeedQFunctionAssemblyDataReferenceCopy(CeedQFunctionAssemblyData data, CeedQFunctionAssemblyData *data_copy) { 1066 CeedCall(CeedQFunctionAssemblyDataReference(data)); 1067 CeedCall(CeedQFunctionAssemblyDataDestroy(data_copy)); 1068 *data_copy = data; 1069 return CEED_ERROR_SUCCESS; 1070 } 1071 1072 /** 1073 @brief Get setup status for internal objects for CeedQFunctionAssemblyData 1074 1075 @param[in] data CeedQFunctionAssemblyData to retrieve status 1076 @param[out] is_setup Boolean flag for setup status 1077 1078 @return An error code: 0 - success, otherwise - failure 1079 1080 @ref Backend 1081 **/ 1082 int CeedQFunctionAssemblyDataIsSetup(CeedQFunctionAssemblyData data, bool *is_setup) { 1083 *is_setup = data->is_setup; 1084 return CEED_ERROR_SUCCESS; 1085 } 1086 1087 /** 1088 @brief Set internal objects for CeedQFunctionAssemblyData 1089 1090 @param[in,out] data CeedQFunctionAssemblyData to set objects 1091 @param[in] vec CeedVector to store assembled CeedQFunction at quadrature points 1092 @param[in] rstr CeedElemRestriction for CeedVector containing assembled CeedQFunction 1093 1094 @return An error code: 0 - success, otherwise - failure 1095 1096 @ref Backend 1097 **/ 1098 int CeedQFunctionAssemblyDataSetObjects(CeedQFunctionAssemblyData data, CeedVector vec, CeedElemRestriction rstr) { 1099 CeedCall(CeedVectorReferenceCopy(vec, &data->vec)); 1100 CeedCall(CeedElemRestrictionReferenceCopy(rstr, &data->rstr)); 1101 1102 data->is_setup = true; 1103 return CEED_ERROR_SUCCESS; 1104 } 1105 1106 int CeedQFunctionAssemblyDataGetObjects(CeedQFunctionAssemblyData data, CeedVector *vec, CeedElemRestriction *rstr) { 1107 CeedCheck(data->is_setup, data->ceed, CEED_ERROR_INCOMPLETE, "Internal objects not set; must call CeedQFunctionAssemblyDataSetObjects first."); 1108 1109 CeedCall(CeedVectorReferenceCopy(data->vec, vec)); 1110 CeedCall(CeedElemRestrictionReferenceCopy(data->rstr, rstr)); 1111 return CEED_ERROR_SUCCESS; 1112 } 1113 1114 /** 1115 @brief Destroy CeedQFunctionAssemblyData 1116 1117 @param[in,out] data CeedQFunctionAssemblyData to destroy 1118 1119 @return An error code: 0 - success, otherwise - failure 1120 1121 @ref Backend 1122 **/ 1123 int CeedQFunctionAssemblyDataDestroy(CeedQFunctionAssemblyData *data) { 1124 if (!*data || --(*data)->ref_count > 0) { 1125 *data = NULL; 1126 return CEED_ERROR_SUCCESS; 1127 } 1128 CeedCall(CeedDestroy(&(*data)->ceed)); 1129 CeedCall(CeedVectorDestroy(&(*data)->vec)); 1130 CeedCall(CeedElemRestrictionDestroy(&(*data)->rstr)); 1131 1132 CeedCall(CeedFree(data)); 1133 return CEED_ERROR_SUCCESS; 1134 } 1135 1136 /** 1137 @brief Get CeedOperatorAssemblyData 1138 1139 @param[in] op CeedOperator to assemble 1140 @param[out] data CeedQFunctionAssemblyData 1141 1142 @return An error code: 0 - success, otherwise - failure 1143 1144 @ref Backend 1145 **/ 1146 int CeedOperatorGetOperatorAssemblyData(CeedOperator op, CeedOperatorAssemblyData *data) { 1147 if (!op->op_assembled) { 1148 CeedOperatorAssemblyData data; 1149 1150 CeedCall(CeedOperatorAssemblyDataCreate(op->ceed, op, &data)); 1151 op->op_assembled = data; 1152 } 1153 *data = op->op_assembled; 1154 return CEED_ERROR_SUCCESS; 1155 } 1156 1157 /** 1158 @brief Create object holding CeedOperator assembly data. 1159 1160 The CeedOperatorAssemblyData holds an array with references to every active CeedBasis used in the CeedOperator. 1161 An array with references to the corresponding active CeedElemRestrictions is also stored. 1162 For each active CeedBasis, the CeedOperatorAssemblyData holds an array of all input and output CeedEvalModes for this CeedBasis. 1163 The CeedOperatorAssemblyData holds an array of offsets for indexing into the assembled CeedQFunction arrays to the row representing each 1164 CeedEvalMode. 1165 The number of input columns across all active bases for the assembled CeedQFunction is also stored. 1166 Lastly, the CeedOperatorAssembly data holds assembled matrices representing the full action of the CeedBasis for all CeedEvalModes. 1167 1168 @param[in] ceed Ceed object where the CeedOperatorAssemblyData will be created 1169 @param[in] op CeedOperator to be assembled 1170 @param[out] data Address of the variable where the newly created CeedOperatorAssemblyData will be stored 1171 1172 @return An error code: 0 - success, otherwise - failure 1173 1174 @ref Backend 1175 **/ 1176 int CeedOperatorAssemblyDataCreate(Ceed ceed, CeedOperator op, CeedOperatorAssemblyData *data) { 1177 CeedInt num_active_bases = 0, num_input_fields, *num_eval_modes_in = NULL, *num_eval_modes_out = NULL, offset = 0, num_output_fields; 1178 CeedSize **eval_mode_offsets_in = NULL, **eval_mode_offsets_out = NULL; 1179 CeedEvalMode **eval_modes_in = NULL, **eval_modes_out = NULL; 1180 CeedQFunctionField *qf_fields; 1181 CeedQFunction qf; 1182 CeedOperatorField *op_fields; 1183 bool is_composite; 1184 1185 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1186 CeedCheck(!is_composite, ceed, CEED_ERROR_INCOMPATIBLE, "Can only create CeedOperator assembly data for non-composite operators."); 1187 1188 // Allocate 1189 CeedCall(CeedCalloc(1, data)); 1190 (*data)->ceed = ceed; 1191 CeedCall(CeedReference(ceed)); 1192 1193 // Build OperatorAssembly data 1194 CeedCall(CeedOperatorGetQFunction(op, &qf)); 1195 CeedCall(CeedQFunctionGetFields(qf, &num_input_fields, &qf_fields, NULL, NULL)); 1196 CeedCall(CeedOperatorGetFields(op, NULL, &op_fields, NULL, NULL)); 1197 1198 // Determine active input basis 1199 for (CeedInt i = 0; i < num_input_fields; i++) { 1200 CeedVector vec; 1201 1202 CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec)); 1203 if (vec == CEED_VECTOR_ACTIVE) { 1204 CeedInt index = -1, num_comp, q_comp; 1205 CeedEvalMode eval_mode; 1206 CeedBasis basis_in = NULL; 1207 1208 CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_in)); 1209 CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode)); 1210 CeedCall(CeedBasisGetNumComponents(basis_in, &num_comp)); 1211 CeedCall(CeedBasisGetNumQuadratureComponents(basis_in, eval_mode, &q_comp)); 1212 for (CeedInt i = 0; i < num_active_bases; i++) { 1213 if ((*data)->active_bases[i] == basis_in) index = i; 1214 } 1215 if (index == -1) { 1216 CeedElemRestriction elem_rstr_in; 1217 1218 index = num_active_bases; 1219 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_bases)); 1220 (*data)->active_bases[num_active_bases] = NULL; 1221 CeedCall(CeedBasisReferenceCopy(basis_in, &(*data)->active_bases[num_active_bases])); 1222 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_elem_rstrs)); 1223 (*data)->active_elem_rstrs[num_active_bases] = NULL; 1224 CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_in)); 1225 CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_in, &(*data)->active_elem_rstrs[num_active_bases])); 1226 CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_in)); 1227 CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_out)); 1228 num_eval_modes_in[index] = 0; 1229 num_eval_modes_out[index] = 0; 1230 CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_in)); 1231 CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_out)); 1232 eval_modes_in[index] = NULL; 1233 eval_modes_out[index] = NULL; 1234 CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_in)); 1235 CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_out)); 1236 eval_mode_offsets_in[index] = NULL; 1237 eval_mode_offsets_out[index] = NULL; 1238 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_in)); 1239 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_out)); 1240 (*data)->assembled_bases_in[index] = NULL; 1241 (*data)->assembled_bases_out[index] = NULL; 1242 num_active_bases++; 1243 } 1244 if (eval_mode != CEED_EVAL_WEIGHT) { 1245 // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly 1246 CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_modes_in[index])); 1247 CeedCall(CeedRealloc(num_eval_modes_in[index] + q_comp, &eval_mode_offsets_in[index])); 1248 for (CeedInt d = 0; d < q_comp; d++) { 1249 eval_modes_in[index][num_eval_modes_in[index] + d] = eval_mode; 1250 eval_mode_offsets_in[index][num_eval_modes_in[index] + d] = offset; 1251 offset += num_comp; 1252 } 1253 num_eval_modes_in[index] += q_comp; 1254 } 1255 } 1256 } 1257 1258 // Determine active output basis 1259 CeedCall(CeedQFunctionGetFields(qf, NULL, NULL, &num_output_fields, &qf_fields)); 1260 CeedCall(CeedOperatorGetFields(op, NULL, NULL, NULL, &op_fields)); 1261 offset = 0; 1262 for (CeedInt i = 0; i < num_output_fields; i++) { 1263 CeedVector vec; 1264 1265 CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec)); 1266 if (vec == CEED_VECTOR_ACTIVE) { 1267 CeedInt index = -1, num_comp, q_comp; 1268 CeedEvalMode eval_mode; 1269 CeedBasis basis_out = NULL; 1270 1271 CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis_out)); 1272 CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode)); 1273 CeedCall(CeedBasisGetNumComponents(basis_out, &num_comp)); 1274 CeedCall(CeedBasisGetNumQuadratureComponents(basis_out, eval_mode, &q_comp)); 1275 for (CeedInt i = 0; i < num_active_bases; i++) { 1276 if ((*data)->active_bases[i] == basis_out) index = i; 1277 } 1278 if (index == -1) { 1279 CeedElemRestriction elem_rstr_out; 1280 1281 index = num_active_bases; 1282 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_bases)); 1283 (*data)->active_bases[num_active_bases] = NULL; 1284 CeedCall(CeedBasisReferenceCopy(basis_out, &(*data)->active_bases[num_active_bases])); 1285 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->active_elem_rstrs)); 1286 (*data)->active_elem_rstrs[num_active_bases] = NULL; 1287 CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &elem_rstr_out)); 1288 CeedCall(CeedElemRestrictionReferenceCopy(elem_rstr_out, &(*data)->active_elem_rstrs[num_active_bases])); 1289 CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_in)); 1290 CeedCall(CeedRealloc(num_active_bases + 1, &num_eval_modes_out)); 1291 num_eval_modes_in[index] = 0; 1292 num_eval_modes_out[index] = 0; 1293 CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_in)); 1294 CeedCall(CeedRealloc(num_active_bases + 1, &eval_modes_out)); 1295 eval_modes_in[index] = NULL; 1296 eval_modes_out[index] = NULL; 1297 CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_in)); 1298 CeedCall(CeedRealloc(num_active_bases + 1, &eval_mode_offsets_out)); 1299 eval_mode_offsets_in[index] = NULL; 1300 eval_mode_offsets_out[index] = NULL; 1301 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_in)); 1302 CeedCall(CeedRealloc(num_active_bases + 1, &(*data)->assembled_bases_out)); 1303 (*data)->assembled_bases_in[index] = NULL; 1304 (*data)->assembled_bases_out[index] = NULL; 1305 num_active_bases++; 1306 } 1307 if (eval_mode != CEED_EVAL_WEIGHT) { 1308 // q_comp = 1 if CEED_EVAL_NONE, CEED_EVAL_WEIGHT caught by QF Assembly 1309 CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_modes_out[index])); 1310 CeedCall(CeedRealloc(num_eval_modes_out[index] + q_comp, &eval_mode_offsets_out[index])); 1311 for (CeedInt d = 0; d < q_comp; d++) { 1312 eval_modes_out[index][num_eval_modes_out[index] + d] = eval_mode; 1313 eval_mode_offsets_out[index][num_eval_modes_out[index] + d] = offset; 1314 offset += num_comp; 1315 } 1316 num_eval_modes_out[index] += q_comp; 1317 } 1318 } 1319 } 1320 (*data)->num_eval_modes_in = num_eval_modes_in; 1321 (*data)->eval_modes_in = eval_modes_in; 1322 (*data)->eval_mode_offsets_in = eval_mode_offsets_in; 1323 (*data)->num_output_components = offset; 1324 (*data)->num_eval_modes_out = num_eval_modes_out; 1325 (*data)->eval_modes_out = eval_modes_out; 1326 (*data)->eval_mode_offsets_out = eval_mode_offsets_out; 1327 (*data)->num_active_bases = num_active_bases; 1328 return CEED_ERROR_SUCCESS; 1329 } 1330 1331 /** 1332 @brief Get CeedOperator CeedEvalModes for assembly. 1333 1334 Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object. 1335 1336 @param[in] data CeedOperatorAssemblyData 1337 @param[out] num_active_bases Total number of active bases 1338 @param[out] num_eval_modes_in Pointer to hold array of numbers of input CeedEvalModes, or NULL. 1339 `eval_modes_in[0]` holds an array of eval modes for the first active basis. 1340 @param[out] eval_modes_in Pointer to hold arrays of input CeedEvalModes, or NULL. 1341 @param[out] eval_mode_offsets_in Pointer to hold arrays of input offsets at each quadrature point. 1342 @param[out] num_eval_modes_out Pointer to hold array of numbers of output CeedEvalModes, or NULL 1343 @param[out] eval_modes_out Pointer to hold arrays of output CeedEvalModes, or NULL. 1344 @param[out] eval_mode_offsets_out Pointer to hold arrays of output offsets at each quadrature point 1345 @param[out] num_output_components The number of columns in the assembled CeedQFunction matrix for each quadrature point, 1346 including contributions of all active bases 1347 1348 @return An error code: 0 - success, otherwise - failure 1349 1350 1351 @ref Backend 1352 **/ 1353 int CeedOperatorAssemblyDataGetEvalModes(CeedOperatorAssemblyData data, CeedInt *num_active_bases, CeedInt **num_eval_modes_in, 1354 const CeedEvalMode ***eval_modes_in, CeedSize ***eval_mode_offsets_in, CeedInt **num_eval_modes_out, 1355 const CeedEvalMode ***eval_modes_out, CeedSize ***eval_mode_offsets_out, CeedSize *num_output_components) { 1356 if (num_active_bases) *num_active_bases = data->num_active_bases; 1357 if (num_eval_modes_in) *num_eval_modes_in = data->num_eval_modes_in; 1358 if (eval_modes_in) *eval_modes_in = (const CeedEvalMode **)data->eval_modes_in; 1359 if (eval_mode_offsets_in) *eval_mode_offsets_in = data->eval_mode_offsets_in; 1360 if (num_eval_modes_out) *num_eval_modes_out = data->num_eval_modes_out; 1361 if (eval_modes_out) *eval_modes_out = (const CeedEvalMode **)data->eval_modes_out; 1362 if (eval_mode_offsets_out) *eval_mode_offsets_out = data->eval_mode_offsets_out; 1363 if (num_output_components) *num_output_components = data->num_output_components; 1364 return CEED_ERROR_SUCCESS; 1365 } 1366 1367 /** 1368 @brief Get CeedOperator CeedBasis data for assembly. 1369 1370 Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object. 1371 1372 @param[in] data CeedOperatorAssemblyData 1373 @param[out] num_active_bases Number of active bases, or NULL 1374 @param[out] active_bases Pointer to hold active CeedBasis, or NULL 1375 @param[out] assembled_bases_in Pointer to hold assembled active input B, or NULL 1376 @param[out] assembled_bases_out Pointer to hold assembled active output B, or NULL 1377 1378 @return An error code: 0 - success, otherwise - failure 1379 1380 @ref Backend 1381 **/ 1382 int CeedOperatorAssemblyDataGetBases(CeedOperatorAssemblyData data, CeedInt *num_active_bases, CeedBasis **active_bases, 1383 const CeedScalar ***assembled_bases_in, const CeedScalar ***assembled_bases_out) { 1384 // Assemble B_in, B_out if needed 1385 if (assembled_bases_in && !data->assembled_bases_in[0]) { 1386 CeedInt num_qpts; 1387 1388 CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases[0], &num_qpts)); 1389 for (CeedInt b = 0; b < data->num_active_bases; b++) { 1390 bool has_eval_none = false; 1391 CeedInt num_nodes; 1392 CeedScalar *B_in = NULL, *identity = NULL; 1393 1394 CeedCall(CeedBasisGetNumNodes(data->active_bases[b], &num_nodes)); 1395 CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_in[b], &B_in)); 1396 1397 for (CeedInt i = 0; i < data->num_eval_modes_in[b]; i++) { 1398 has_eval_none = has_eval_none || (data->eval_modes_in[b][i] == CEED_EVAL_NONE); 1399 } 1400 if (has_eval_none) { 1401 CeedCall(CeedCalloc(num_qpts * num_nodes, &identity)); 1402 for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) { 1403 identity[i * num_nodes + i] = 1.0; 1404 } 1405 } 1406 1407 for (CeedInt q = 0; q < num_qpts; q++) { 1408 for (CeedInt n = 0; n < num_nodes; n++) { 1409 CeedInt d_in = 0, q_comp_in; 1410 CeedEvalMode eval_mode_in_prev = CEED_EVAL_NONE; 1411 1412 for (CeedInt e_in = 0; e_in < data->num_eval_modes_in[b]; e_in++) { 1413 const CeedInt qq = data->num_eval_modes_in[b] * q; 1414 const CeedScalar *B = NULL; 1415 1416 CeedOperatorGetBasisPointer(data->active_bases[b], data->eval_modes_in[b][e_in], identity, &B); 1417 CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases[b], data->eval_modes_in[b][e_in], &q_comp_in)); 1418 if (q_comp_in > 1) { 1419 if (e_in == 0 || data->eval_modes_in[b][e_in] != eval_mode_in_prev) d_in = 0; 1420 else B = &B[(++d_in) * num_qpts * num_nodes]; 1421 } 1422 eval_mode_in_prev = data->eval_modes_in[b][e_in]; 1423 B_in[(qq + e_in) * num_nodes + n] = B[q * num_nodes + n]; 1424 } 1425 } 1426 } 1427 if (identity) CeedCall(CeedFree(&identity)); 1428 data->assembled_bases_in[b] = B_in; 1429 } 1430 } 1431 1432 if (assembled_bases_out && !data->assembled_bases_out[0]) { 1433 CeedInt num_qpts; 1434 1435 CeedCall(CeedBasisGetNumQuadraturePoints(data->active_bases[0], &num_qpts)); 1436 for (CeedInt b = 0; b < data->num_active_bases; b++) { 1437 bool has_eval_none = false; 1438 CeedInt num_nodes; 1439 CeedScalar *B_out = NULL, *identity = NULL; 1440 1441 CeedCall(CeedBasisGetNumNodes(data->active_bases[b], &num_nodes)); 1442 CeedCall(CeedCalloc(num_qpts * num_nodes * data->num_eval_modes_out[b], &B_out)); 1443 1444 for (CeedInt i = 0; i < data->num_eval_modes_out[b]; i++) { 1445 has_eval_none = has_eval_none || (data->eval_modes_out[b][i] == CEED_EVAL_NONE); 1446 } 1447 if (has_eval_none) { 1448 CeedCall(CeedCalloc(num_qpts * num_nodes, &identity)); 1449 for (CeedInt i = 0; i < (num_nodes < num_qpts ? num_nodes : num_qpts); i++) { 1450 identity[i * num_nodes + i] = 1.0; 1451 } 1452 } 1453 1454 for (CeedInt q = 0; q < num_qpts; q++) { 1455 for (CeedInt n = 0; n < num_nodes; n++) { 1456 CeedInt d_out = 0, q_comp_out; 1457 CeedEvalMode eval_mode_out_prev = CEED_EVAL_NONE; 1458 1459 for (CeedInt e_out = 0; e_out < data->num_eval_modes_out[b]; e_out++) { 1460 const CeedInt qq = data->num_eval_modes_out[b] * q; 1461 const CeedScalar *B = NULL; 1462 1463 CeedOperatorGetBasisPointer(data->active_bases[b], data->eval_modes_out[b][e_out], identity, &B); 1464 CeedCall(CeedBasisGetNumQuadratureComponents(data->active_bases[b], data->eval_modes_out[b][e_out], &q_comp_out)); 1465 if (q_comp_out > 1) { 1466 if (e_out == 0 || data->eval_modes_out[b][e_out] != eval_mode_out_prev) d_out = 0; 1467 else B = &B[(++d_out) * num_qpts * num_nodes]; 1468 } 1469 eval_mode_out_prev = data->eval_modes_out[b][e_out]; 1470 B_out[(qq + e_out) * num_nodes + n] = B[q * num_nodes + n]; 1471 } 1472 } 1473 } 1474 if (identity) CeedCall(CeedFree(&identity)); 1475 data->assembled_bases_out[b] = B_out; 1476 } 1477 } 1478 1479 // Pass out assembled data 1480 if (active_bases) *active_bases = data->active_bases; 1481 if (assembled_bases_in) *assembled_bases_in = (const CeedScalar **)data->assembled_bases_in; 1482 if (assembled_bases_out) *assembled_bases_out = (const CeedScalar **)data->assembled_bases_out; 1483 return CEED_ERROR_SUCCESS; 1484 } 1485 1486 /** 1487 @brief Get CeedOperator CeedBasis data for assembly. 1488 1489 Note: See CeedOperatorAssemblyDataCreate for a full description of the data stored in this object. 1490 1491 @param[in] data CeedOperatorAssemblyData 1492 @param[out] num_active_elem_rstrs Number of active element restrictions, or NULL 1493 @param[out] active_elem_rstrs Pointer to hold active CeedElemRestrictions, or NULL 1494 1495 @return An error code: 0 - success, otherwise - failure 1496 1497 @ref Backend 1498 **/ 1499 int CeedOperatorAssemblyDataGetElemRestrictions(CeedOperatorAssemblyData data, CeedInt *num_active_elem_rstrs, 1500 CeedElemRestriction **active_elem_rstrs) { 1501 if (num_active_elem_rstrs) *num_active_elem_rstrs = data->num_active_bases; 1502 if (active_elem_rstrs) *active_elem_rstrs = data->active_elem_rstrs; 1503 return CEED_ERROR_SUCCESS; 1504 } 1505 1506 /** 1507 @brief Destroy CeedOperatorAssemblyData 1508 1509 @param[in,out] data CeedOperatorAssemblyData to destroy 1510 1511 @return An error code: 0 - success, otherwise - failure 1512 1513 @ref Backend 1514 **/ 1515 int CeedOperatorAssemblyDataDestroy(CeedOperatorAssemblyData *data) { 1516 if (!*data) { 1517 *data = NULL; 1518 return CEED_ERROR_SUCCESS; 1519 } 1520 CeedCall(CeedDestroy(&(*data)->ceed)); 1521 for (CeedInt b = 0; b < (*data)->num_active_bases; b++) { 1522 CeedCall(CeedBasisDestroy(&(*data)->active_bases[b])); 1523 CeedCall(CeedElemRestrictionDestroy(&(*data)->active_elem_rstrs[b])); 1524 CeedCall(CeedFree(&(*data)->eval_modes_in[b])); 1525 CeedCall(CeedFree(&(*data)->eval_modes_out[b])); 1526 CeedCall(CeedFree(&(*data)->eval_mode_offsets_in[b])); 1527 CeedCall(CeedFree(&(*data)->eval_mode_offsets_out[b])); 1528 CeedCall(CeedFree(&(*data)->assembled_bases_in[b])); 1529 CeedCall(CeedFree(&(*data)->assembled_bases_out[b])); 1530 } 1531 CeedCall(CeedFree(&(*data)->active_bases)); 1532 CeedCall(CeedFree(&(*data)->active_elem_rstrs)); 1533 CeedCall(CeedFree(&(*data)->num_eval_modes_in)); 1534 CeedCall(CeedFree(&(*data)->num_eval_modes_out)); 1535 CeedCall(CeedFree(&(*data)->eval_modes_in)); 1536 CeedCall(CeedFree(&(*data)->eval_modes_out)); 1537 CeedCall(CeedFree(&(*data)->eval_mode_offsets_in)); 1538 CeedCall(CeedFree(&(*data)->eval_mode_offsets_out)); 1539 CeedCall(CeedFree(&(*data)->assembled_bases_in)); 1540 CeedCall(CeedFree(&(*data)->assembled_bases_out)); 1541 1542 CeedCall(CeedFree(data)); 1543 return CEED_ERROR_SUCCESS; 1544 } 1545 1546 /// @} 1547 1548 /// ---------------------------------------------------------------------------- 1549 /// CeedOperator Public API 1550 /// ---------------------------------------------------------------------------- 1551 /// @addtogroup CeedOperatorUser 1552 /// @{ 1553 1554 /** 1555 @brief Assemble a linear CeedQFunction associated with a CeedOperator 1556 1557 This returns a CeedVector containing a matrix at each quadrature point providing the action of the CeedQFunction associated with the CeedOperator. 1558 The vector `assembled` is of shape `[num_elements, num_input_fields, num_output_fields, num_quad_points]` and contains column-major matrices 1559 representing the action of the CeedQFunction for a corresponding quadrature point on an element. 1560 1561 Inputs and outputs are in the order provided by the user when adding CeedOperator fields. 1562 For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and 'v', provided in that order, would result in an assembled QFunction 1563 that consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v]. 1564 1565 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1566 1567 @param[in] op CeedOperator to assemble CeedQFunction 1568 @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points 1569 @param[out] rstr CeedElemRestriction for CeedVector containing assembled CeedQFunction 1570 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1571 1572 @return An error code: 0 - success, otherwise - failure 1573 1574 @ref User 1575 **/ 1576 int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) { 1577 CeedCall(CeedOperatorCheckReady(op)); 1578 1579 if (op->LinearAssembleQFunction) { 1580 // Backend version 1581 CeedCall(op->LinearAssembleQFunction(op, assembled, rstr, request)); 1582 } else { 1583 // Operator fallback 1584 CeedOperator op_fallback; 1585 1586 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 1587 if (op_fallback) CeedCall(CeedOperatorLinearAssembleQFunction(op_fallback, assembled, rstr, request)); 1588 else return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunction"); 1589 } 1590 return CEED_ERROR_SUCCESS; 1591 } 1592 1593 /** 1594 @brief Assemble CeedQFunction and store result internally. 1595 1596 Return copied references of stored data to the caller. 1597 Caller is responsible for ownership and destruction of the copied references. 1598 See also @ref CeedOperatorLinearAssembleQFunction 1599 1600 Note: If the value of `assembled` or `rstr` passed to this function are non-NULL, then it is assumed that they hold valid pointers. 1601 These objects will be destroyed if `*assembled` or `*rstr` is the only reference to the object. 1602 1603 @param[in] op CeedOperator to assemble CeedQFunction 1604 @param[out] assembled CeedVector to store assembled CeedQFunction at quadrature points 1605 @param[out] rstr CeedElemRestriction for CeedVector containing assembledCeedQFunction 1606 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1607 1608 @return An error code: 0 - success, otherwise - failure 1609 1610 @ref User 1611 **/ 1612 int CeedOperatorLinearAssembleQFunctionBuildOrUpdate(CeedOperator op, CeedVector *assembled, CeedElemRestriction *rstr, CeedRequest *request) { 1613 int (*LinearAssembleQFunctionUpdate)(CeedOperator, CeedVector, CeedElemRestriction, CeedRequest *) = NULL; 1614 CeedOperator op_assemble = NULL; 1615 CeedOperator op_fallback_parent = NULL; 1616 1617 CeedCall(CeedOperatorCheckReady(op)); 1618 1619 // Determine if fallback parent or operator has implementation 1620 CeedCall(CeedOperatorGetFallbackParent(op, &op_fallback_parent)); 1621 if (op_fallback_parent && op_fallback_parent->LinearAssembleQFunctionUpdate) { 1622 // -- Backend version for op fallback parent is faster, if it exists 1623 LinearAssembleQFunctionUpdate = op_fallback_parent->LinearAssembleQFunctionUpdate; 1624 op_assemble = op_fallback_parent; 1625 } else if (op->LinearAssembleQFunctionUpdate) { 1626 // -- Backend version for op 1627 LinearAssembleQFunctionUpdate = op->LinearAssembleQFunctionUpdate; 1628 op_assemble = op; 1629 } 1630 1631 // Assemble QFunction 1632 if (LinearAssembleQFunctionUpdate) { 1633 // Backend or fallback parent version 1634 bool qf_assembled_is_setup; 1635 CeedVector assembled_vec = NULL; 1636 CeedElemRestriction assembled_rstr = NULL; 1637 1638 CeedCall(CeedQFunctionAssemblyDataIsSetup(op->qf_assembled, &qf_assembled_is_setup)); 1639 if (qf_assembled_is_setup) { 1640 bool update_needed; 1641 1642 CeedCall(CeedQFunctionAssemblyDataGetObjects(op->qf_assembled, &assembled_vec, &assembled_rstr)); 1643 CeedCall(CeedQFunctionAssemblyDataIsUpdateNeeded(op->qf_assembled, &update_needed)); 1644 if (update_needed) CeedCall(LinearAssembleQFunctionUpdate(op_assemble, assembled_vec, assembled_rstr, request)); 1645 } else { 1646 CeedCall(CeedOperatorLinearAssembleQFunction(op_assemble, &assembled_vec, &assembled_rstr, request)); 1647 CeedCall(CeedQFunctionAssemblyDataSetObjects(op->qf_assembled, assembled_vec, assembled_rstr)); 1648 } 1649 CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, false)); 1650 1651 // Copy reference from internally held copy 1652 CeedCall(CeedVectorReferenceCopy(assembled_vec, assembled)); 1653 CeedCall(CeedElemRestrictionReferenceCopy(assembled_rstr, rstr)); 1654 CeedCall(CeedVectorDestroy(&assembled_vec)); 1655 CeedCall(CeedElemRestrictionDestroy(&assembled_rstr)); 1656 } else { 1657 // Operator fallback 1658 CeedOperator op_fallback; 1659 1660 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 1661 if (op_fallback) CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op_fallback, assembled, rstr, request)); 1662 else return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedOperatorLinearAssembleQFunctionUpdate"); 1663 } 1664 return CEED_ERROR_SUCCESS; 1665 } 1666 1667 /** 1668 @brief Assemble the diagonal of a square linear CeedOperator 1669 1670 This overwrites a CeedVector with the diagonal of a linear CeedOperator. 1671 1672 Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported. 1673 1674 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1675 1676 @param[in] op CeedOperator to assemble CeedQFunction 1677 @param[out] assembled CeedVector to store assembled CeedOperator diagonal 1678 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1679 1680 @return An error code: 0 - success, otherwise - failure 1681 1682 @ref User 1683 **/ 1684 int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) { 1685 bool is_composite; 1686 CeedSize input_size = 0, output_size = 0; 1687 1688 CeedCall(CeedOperatorCheckReady(op)); 1689 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1690 1691 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 1692 CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square"); 1693 1694 // Early exit for empty operator 1695 if (!is_composite) { 1696 CeedInt num_elem = 0; 1697 1698 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1699 if (num_elem == 0) return CEED_ERROR_SUCCESS; 1700 } 1701 1702 if (op->LinearAssembleDiagonal) { 1703 // Backend version 1704 CeedCall(op->LinearAssembleDiagonal(op, assembled, request)); 1705 return CEED_ERROR_SUCCESS; 1706 } else if (op->LinearAssembleAddDiagonal) { 1707 // Backend version with zeroing first 1708 CeedCall(CeedVectorSetValue(assembled, 0.0)); 1709 CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request)); 1710 return CEED_ERROR_SUCCESS; 1711 } else { 1712 // Operator fallback 1713 CeedOperator op_fallback; 1714 1715 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 1716 if (op_fallback) { 1717 CeedCall(CeedOperatorLinearAssembleDiagonal(op_fallback, assembled, request)); 1718 return CEED_ERROR_SUCCESS; 1719 } 1720 } 1721 // Default interface implementation 1722 CeedCall(CeedVectorSetValue(assembled, 0.0)); 1723 CeedCall(CeedOperatorLinearAssembleAddDiagonal(op, assembled, request)); 1724 return CEED_ERROR_SUCCESS; 1725 } 1726 1727 /** 1728 @brief Assemble the diagonal of a square linear CeedOperator 1729 1730 This sums into a CeedVector the diagonal of a linear CeedOperator. 1731 1732 Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported. 1733 1734 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1735 1736 @param[in] op CeedOperator to assemble CeedQFunction 1737 @param[out] assembled CeedVector to store assembled CeedOperator diagonal 1738 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1739 1740 @return An error code: 0 - success, otherwise - failure 1741 1742 @ref User 1743 **/ 1744 int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) { 1745 bool is_composite; 1746 CeedSize input_size = 0, output_size = 0; 1747 1748 CeedCall(CeedOperatorCheckReady(op)); 1749 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1750 1751 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 1752 CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square"); 1753 1754 // Early exit for empty operator 1755 if (!is_composite) { 1756 CeedInt num_elem = 0; 1757 1758 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1759 if (num_elem == 0) return CEED_ERROR_SUCCESS; 1760 } 1761 1762 if (op->LinearAssembleAddDiagonal) { 1763 // Backend version 1764 CeedCall(op->LinearAssembleAddDiagonal(op, assembled, request)); 1765 return CEED_ERROR_SUCCESS; 1766 } else { 1767 // Operator fallback 1768 CeedOperator op_fallback; 1769 1770 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 1771 if (op_fallback) { 1772 CeedCall(CeedOperatorLinearAssembleAddDiagonal(op_fallback, assembled, request)); 1773 return CEED_ERROR_SUCCESS; 1774 } 1775 } 1776 // Default interface implementation 1777 if (is_composite) { 1778 CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, false, assembled)); 1779 } else { 1780 CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, false, assembled)); 1781 } 1782 return CEED_ERROR_SUCCESS; 1783 } 1784 1785 /** 1786 @brief Fully assemble the point-block diagonal pattern of a linear operator. 1787 1788 Expected to be used in conjunction with CeedOperatorLinearAssemblePointBlockDiagonal(). 1789 1790 The assembly routines use coordinate format, with `num_entries` tuples of the form (i, j, value) which indicate that value should be added to the 1791 matrix in entry (i, j). 1792 Note that the (i, j) pairs are unique. 1793 This function returns the number of entries and their (i, j) locations, while CeedOperatorLinearAssemblePointBlockDiagonal() provides the values in 1794 the same ordering. 1795 1796 This will generally be slow unless your operator is low-order. 1797 1798 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1799 1800 @param[in] op CeedOperator to assemble 1801 @param[out] num_entries Number of entries in coordinate nonzero pattern 1802 @param[out] rows Row number for each entry 1803 @param[out] cols Column number for each entry 1804 1805 @ref User 1806 **/ 1807 int CeedOperatorLinearAssemblePointBlockDiagonalSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) { 1808 Ceed ceed; 1809 bool is_composite; 1810 CeedInt num_active_components, num_sub_operators; 1811 CeedOperator *sub_operators; 1812 1813 CeedCall(CeedOperatorGetCeed(op, &ceed)); 1814 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1815 1816 CeedSize input_size = 0, output_size = 0; 1817 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 1818 CeedCheck(input_size == output_size, ceed, CEED_ERROR_DIMENSION, "Operator must be square"); 1819 1820 if (is_composite) { 1821 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub_operators)); 1822 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1823 } else { 1824 sub_operators = &op; 1825 num_sub_operators = 1; 1826 } 1827 1828 { // Verify operator can be assembled correctly 1829 CeedInt num_active_elem_rstrs, comp_stride; 1830 CeedOperatorAssemblyData data; 1831 CeedElemRestriction *active_elem_rstrs; 1832 1833 // Get initial values to check against 1834 CeedCall(CeedOperatorGetOperatorAssemblyData(sub_operators[0], &data)); 1835 CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, &num_active_elem_rstrs, &active_elem_rstrs)); 1836 CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstrs[0], &comp_stride)); 1837 CeedCall(CeedElemRestrictionGetNumComponents(active_elem_rstrs[0], &num_active_components)); 1838 1839 for (CeedInt k = 0; k < num_sub_operators; k++) { 1840 CeedCall(CeedOperatorGetOperatorAssemblyData(sub_operators[k], &data)); 1841 1842 // Verify that all active element restrictions have same component stride and number of components 1843 CeedCall(CeedOperatorAssemblyDataGetElemRestrictions(data, &num_active_elem_rstrs, &active_elem_rstrs)); 1844 CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstrs[0], &comp_stride)); 1845 for (CeedInt i = 0; i < num_active_elem_rstrs; i++) { 1846 CeedInt comp_stride_sub; 1847 CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstrs[i], &comp_stride_sub)); 1848 CeedCheck(comp_stride == comp_stride_sub, ceed, CEED_ERROR_DIMENSION, 1849 "Active element restrictions must have the same component stride: %d vs %d", comp_stride, comp_stride_sub); 1850 1851 CeedInt num_active_components_sub; 1852 CeedCall(CeedElemRestrictionGetNumComponents(active_elem_rstrs[i], &num_active_components_sub)); 1853 CeedCheck(num_active_components == num_active_components_sub, ceed, CEED_ERROR_INCOMPATIBLE, 1854 "All suboperators must have the same number of output components"); 1855 } 1856 } 1857 } 1858 1859 *num_entries = input_size * num_active_components; 1860 CeedCall(CeedCalloc(*num_entries, rows)); 1861 CeedCall(CeedCalloc(*num_entries, cols)); 1862 1863 for (CeedInt o = 0; o < num_sub_operators; o++) { 1864 CeedElemRestriction active_elem_rstr, pb_active_elem_rstr; 1865 CeedInt comp_stride, num_elem, elem_size; 1866 const CeedInt *offsets, *pb_offsets; 1867 1868 CeedCall(CeedOperatorGetActiveElemRestriction(sub_operators[o], &active_elem_rstr)); 1869 CeedCall(CeedElemRestrictionGetCompStride(active_elem_rstr, &comp_stride)); 1870 CeedCall(CeedElemRestrictionGetNumElements(active_elem_rstr, &num_elem)); 1871 CeedCall(CeedElemRestrictionGetElementSize(active_elem_rstr, &elem_size)); 1872 CeedCall(CeedElemRestrictionGetOffsets(active_elem_rstr, CEED_MEM_HOST, &offsets)); 1873 1874 CeedCall(CeedOperatorCreateActivePointBlockRestriction(active_elem_rstr, &pb_active_elem_rstr)); 1875 CeedCall(CeedElemRestrictionGetOffsets(pb_active_elem_rstr, CEED_MEM_HOST, &pb_offsets)); 1876 1877 for (CeedSize i = 0; i < num_elem * elem_size; i++) { 1878 for (CeedInt c_out = 0; c_out < num_active_components; c_out++) { 1879 for (CeedInt c_in = 0; c_in < num_active_components; c_in++) { 1880 (*rows)[pb_offsets[i] + c_out * num_active_components + c_in] = offsets[i] + c_out * comp_stride; 1881 (*cols)[pb_offsets[i] + c_out * num_active_components + c_in] = offsets[i] + c_in * comp_stride; 1882 } 1883 } 1884 } 1885 1886 CeedCall(CeedElemRestrictionRestoreOffsets(active_elem_rstr, &offsets)); 1887 CeedCall(CeedElemRestrictionRestoreOffsets(pb_active_elem_rstr, &pb_offsets)); 1888 CeedCall(CeedElemRestrictionDestroy(&pb_active_elem_rstr)); 1889 } 1890 return CEED_ERROR_SUCCESS; 1891 } 1892 1893 /** 1894 @brief Assemble the point block diagonal of a square linear CeedOperator 1895 1896 This overwrites a CeedVector with the point block diagonal of a linear CeedOperator. 1897 1898 Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported. 1899 1900 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1901 1902 @param[in] op CeedOperator to assemble CeedQFunction 1903 @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp 1904 block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out, 1905 component in]. 1906 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1907 1908 @return An error code: 0 - success, otherwise - failure 1909 1910 @ref User 1911 **/ 1912 int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) { 1913 bool is_composite; 1914 CeedSize input_size = 0, output_size = 0; 1915 1916 CeedCall(CeedOperatorCheckReady(op)); 1917 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1918 1919 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 1920 CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square"); 1921 1922 // Early exit for empty operator 1923 if (!is_composite) { 1924 CeedInt num_elem = 0; 1925 1926 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1927 if (num_elem == 0) return CEED_ERROR_SUCCESS; 1928 } 1929 1930 if (op->LinearAssemblePointBlockDiagonal) { 1931 // Backend version 1932 CeedCall(op->LinearAssemblePointBlockDiagonal(op, assembled, request)); 1933 return CEED_ERROR_SUCCESS; 1934 } else if (op->LinearAssembleAddPointBlockDiagonal) { 1935 // Backend version with zeroing first 1936 CeedCall(CeedVectorSetValue(assembled, 0.0)); 1937 CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request)); 1938 return CEED_ERROR_SUCCESS; 1939 } else { 1940 // Operator fallback 1941 CeedOperator op_fallback; 1942 1943 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 1944 if (op_fallback) { 1945 CeedCall(CeedOperatorLinearAssemblePointBlockDiagonal(op_fallback, assembled, request)); 1946 return CEED_ERROR_SUCCESS; 1947 } 1948 } 1949 // Default interface implementation 1950 CeedCall(CeedVectorSetValue(assembled, 0.0)); 1951 CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled, request)); 1952 return CEED_ERROR_SUCCESS; 1953 } 1954 1955 /** 1956 @brief Assemble the point block diagonal of a square linear CeedOperator 1957 1958 This sums into a CeedVector with the point block diagonal of a linear CeedOperator. 1959 1960 Note: Currently only non-composite CeedOperators with a single field and composite CeedOperators with single field sub-operators are supported. 1961 1962 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1963 1964 @param[in] op CeedOperator to assemble CeedQFunction 1965 @param[out] assembled CeedVector to store assembled CeedOperator point block diagonal, provided in row-major form with an @a num_comp * @a num_comp 1966 block at each node. The dimensions of this vector are derived from the active vector for the CeedOperator. The array has shape [nodes, component out, 1967 component in]. 1968 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1969 1970 @return An error code: 0 - success, otherwise - failure 1971 1972 @ref User 1973 **/ 1974 int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op, CeedVector assembled, CeedRequest *request) { 1975 bool is_composite; 1976 CeedSize input_size = 0, output_size = 0; 1977 1978 CeedCall(CeedOperatorCheckReady(op)); 1979 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1980 1981 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 1982 CeedCheck(input_size == output_size, op->ceed, CEED_ERROR_DIMENSION, "Operator must be square"); 1983 1984 // Early exit for empty operator 1985 if (!is_composite) { 1986 CeedInt num_elem = 0; 1987 1988 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1989 if (num_elem == 0) return CEED_ERROR_SUCCESS; 1990 } 1991 1992 if (op->LinearAssembleAddPointBlockDiagonal) { 1993 // Backend version 1994 CeedCall(op->LinearAssembleAddPointBlockDiagonal(op, assembled, request)); 1995 return CEED_ERROR_SUCCESS; 1996 } else { 1997 // Operator fallback 1998 CeedOperator op_fallback; 1999 2000 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 2001 if (op_fallback) { 2002 CeedCall(CeedOperatorLinearAssembleAddPointBlockDiagonal(op_fallback, assembled, request)); 2003 return CEED_ERROR_SUCCESS; 2004 } 2005 } 2006 // Default interface implementation 2007 if (is_composite) { 2008 CeedCall(CeedCompositeOperatorLinearAssembleAddDiagonal(op, request, true, assembled)); 2009 } else { 2010 CeedCall(CeedSingleOperatorAssembleAddDiagonal_Core(op, request, true, assembled)); 2011 } 2012 return CEED_ERROR_SUCCESS; 2013 } 2014 2015 /** 2016 @brief Fully assemble the nonzero pattern of a linear operator. 2017 2018 Expected to be used in conjunction with CeedOperatorLinearAssemble(). 2019 2020 The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the 2021 matrix in entry (i, j). 2022 Note that the (i, j) pairs are not unique and may repeat. 2023 This function returns the number of entries and their (i, j) locations, while CeedOperatorLinearAssemble() provides the values in the same ordering. 2024 2025 This will generally be slow unless your operator is low-order. 2026 2027 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 2028 2029 @param[in] op CeedOperator to assemble 2030 @param[out] num_entries Number of entries in coordinate nonzero pattern 2031 @param[out] rows Row number for each entry 2032 @param[out] cols Column number for each entry 2033 2034 @ref User 2035 **/ 2036 int CeedOperatorLinearAssembleSymbolic(CeedOperator op, CeedSize *num_entries, CeedInt **rows, CeedInt **cols) { 2037 bool is_composite; 2038 CeedInt num_suboperators, offset = 0; 2039 CeedSize single_entries; 2040 CeedOperator *sub_operators; 2041 2042 CeedCall(CeedOperatorCheckReady(op)); 2043 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 2044 2045 if (op->LinearAssembleSymbolic) { 2046 // Backend version 2047 CeedCall(op->LinearAssembleSymbolic(op, num_entries, rows, cols)); 2048 return CEED_ERROR_SUCCESS; 2049 } else { 2050 // Operator fallback 2051 CeedOperator op_fallback; 2052 2053 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 2054 if (op_fallback) { 2055 CeedCall(CeedOperatorLinearAssembleSymbolic(op_fallback, num_entries, rows, cols)); 2056 return CEED_ERROR_SUCCESS; 2057 } 2058 } 2059 2060 // Default interface implementation 2061 2062 // count entries and allocate rows, cols arrays 2063 *num_entries = 0; 2064 if (is_composite) { 2065 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 2066 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 2067 for (CeedInt k = 0; k < num_suboperators; ++k) { 2068 CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries)); 2069 *num_entries += single_entries; 2070 } 2071 } else { 2072 CeedCall(CeedSingleOperatorAssemblyCountEntries(op, &single_entries)); 2073 *num_entries += single_entries; 2074 } 2075 CeedCall(CeedCalloc(*num_entries, rows)); 2076 CeedCall(CeedCalloc(*num_entries, cols)); 2077 2078 // assemble nonzero locations 2079 if (is_composite) { 2080 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 2081 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 2082 for (CeedInt k = 0; k < num_suboperators; ++k) { 2083 CeedCall(CeedSingleOperatorAssembleSymbolic(sub_operators[k], offset, *rows, *cols)); 2084 CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries)); 2085 offset += single_entries; 2086 } 2087 } else { 2088 CeedCall(CeedSingleOperatorAssembleSymbolic(op, offset, *rows, *cols)); 2089 } 2090 return CEED_ERROR_SUCCESS; 2091 } 2092 2093 /** 2094 @brief Fully assemble the nonzero entries of a linear operator. 2095 2096 Expected to be used in conjunction with CeedOperatorLinearAssembleSymbolic(). 2097 2098 The assembly routines use coordinate format, with num_entries tuples of the form (i, j, value) which indicate that value should be added to the 2099 matrix in entry (i, j). 2100 Note that the (i, j) pairs are not unique and may repeat. 2101 This function returns the values of the nonzero entries to be added, their (i, j) locations are provided by CeedOperatorLinearAssembleSymbolic() 2102 2103 This will generally be slow unless your operator is low-order. 2104 2105 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 2106 2107 @param[in] op CeedOperator to assemble 2108 @param[out] values Values to assemble into matrix 2109 2110 @ref User 2111 **/ 2112 int CeedOperatorLinearAssemble(CeedOperator op, CeedVector values) { 2113 bool is_composite; 2114 CeedInt num_suboperators, offset = 0; 2115 CeedSize single_entries = 0; 2116 CeedOperator *sub_operators; 2117 2118 CeedCall(CeedOperatorCheckReady(op)); 2119 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 2120 2121 // Early exit for empty operator 2122 if (!is_composite) { 2123 CeedInt num_elem = 0; 2124 2125 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 2126 if (num_elem == 0) return CEED_ERROR_SUCCESS; 2127 } 2128 2129 if (op->LinearAssemble) { 2130 // Backend version 2131 CeedCall(op->LinearAssemble(op, values)); 2132 return CEED_ERROR_SUCCESS; 2133 } else { 2134 // Operator fallback 2135 CeedOperator op_fallback; 2136 2137 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 2138 if (op_fallback) { 2139 CeedCall(CeedOperatorLinearAssemble(op_fallback, values)); 2140 return CEED_ERROR_SUCCESS; 2141 } 2142 } 2143 2144 // Default interface implementation 2145 CeedCall(CeedVectorSetValue(values, 0.0)); 2146 if (is_composite) { 2147 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 2148 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 2149 for (CeedInt k = 0; k < num_suboperators; k++) { 2150 CeedCall(CeedSingleOperatorAssemble(sub_operators[k], offset, values)); 2151 CeedCall(CeedSingleOperatorAssemblyCountEntries(sub_operators[k], &single_entries)); 2152 offset += single_entries; 2153 } 2154 } else { 2155 CeedCall(CeedSingleOperatorAssemble(op, offset, values)); 2156 } 2157 return CEED_ERROR_SUCCESS; 2158 } 2159 2160 /** 2161 @brief Get the multiplicity of nodes across suboperators in a composite CeedOperator 2162 2163 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 2164 2165 @param[in] op Composite CeedOperator 2166 @param[in] num_skip_indices Number of suboperators to skip 2167 @param[in] skip_indices Array of indices of suboperators to skip 2168 @param[out] mult Vector to store multiplicity (of size l_size) 2169 2170 @return An error code: 0 - success, otherwise - failure 2171 2172 @ref User 2173 **/ 2174 int CeedCompositeOperatorGetMultiplicity(CeedOperator op, CeedInt num_skip_indices, CeedInt *skip_indices, CeedVector mult) { 2175 Ceed ceed; 2176 CeedInt num_suboperators; 2177 CeedSize l_vec_len; 2178 CeedScalar *mult_array; 2179 CeedVector ones_l_vec; 2180 CeedElemRestriction elem_rstr, mult_elem_rstr; 2181 CeedOperator *sub_operators; 2182 2183 CeedCall(CeedOperatorCheckReady(op)); 2184 2185 CeedCall(CeedOperatorGetCeed(op, &ceed)); 2186 2187 // Zero mult vector 2188 CeedCall(CeedVectorSetValue(mult, 0.0)); 2189 2190 // Get suboperators 2191 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 2192 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 2193 if (num_suboperators == 0) return CEED_ERROR_SUCCESS; 2194 2195 // Work vector 2196 CeedCall(CeedVectorGetLength(mult, &l_vec_len)); 2197 CeedCall(CeedVectorCreate(ceed, l_vec_len, &ones_l_vec)); 2198 CeedCall(CeedVectorSetValue(ones_l_vec, 1.0)); 2199 CeedCall(CeedVectorGetArray(mult, CEED_MEM_HOST, &mult_array)); 2200 2201 // Compute multiplicity across suboperators 2202 for (CeedInt i = 0; i < num_suboperators; i++) { 2203 const CeedScalar *sub_mult_array; 2204 CeedVector sub_mult_l_vec, ones_e_vec; 2205 2206 // -- Check for suboperator to skip 2207 for (CeedInt j = 0; j < num_skip_indices; j++) { 2208 if (skip_indices[j] == i) continue; 2209 } 2210 2211 // -- Sub operator multiplicity 2212 CeedCall(CeedOperatorGetActiveElemRestriction(sub_operators[i], &elem_rstr)); 2213 CeedCall(CeedElemRestrictionCreateUnorientedCopy(elem_rstr, &mult_elem_rstr)); 2214 CeedCall(CeedElemRestrictionCreateVector(mult_elem_rstr, &sub_mult_l_vec, &ones_e_vec)); 2215 CeedCall(CeedVectorSetValue(sub_mult_l_vec, 0.0)); 2216 CeedCall(CeedElemRestrictionApply(mult_elem_rstr, CEED_NOTRANSPOSE, ones_l_vec, ones_e_vec, CEED_REQUEST_IMMEDIATE)); 2217 CeedCall(CeedElemRestrictionApply(mult_elem_rstr, CEED_TRANSPOSE, ones_e_vec, sub_mult_l_vec, CEED_REQUEST_IMMEDIATE)); 2218 CeedCall(CeedVectorGetArrayRead(sub_mult_l_vec, CEED_MEM_HOST, &sub_mult_array)); 2219 // ---- Flag every node present in the current suboperator 2220 for (CeedInt j = 0; j < l_vec_len; j++) { 2221 if (sub_mult_array[j] > 0.0) mult_array[j] += 1.0; 2222 } 2223 CeedCall(CeedVectorRestoreArrayRead(sub_mult_l_vec, &sub_mult_array)); 2224 CeedCall(CeedVectorDestroy(&sub_mult_l_vec)); 2225 CeedCall(CeedVectorDestroy(&ones_e_vec)); 2226 CeedCall(CeedElemRestrictionDestroy(&mult_elem_rstr)); 2227 } 2228 CeedCall(CeedVectorRestoreArray(mult, &mult_array)); 2229 CeedCall(CeedVectorDestroy(&ones_l_vec)); 2230 return CEED_ERROR_SUCCESS; 2231 } 2232 2233 /** 2234 @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator, creating the prolongation basis from the fine and coarse 2235 grid interpolation 2236 2237 Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable. 2238 2239 @param[in] op_fine Fine grid operator 2240 @param[in] p_mult_fine L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators 2241 @param[in] rstr_coarse Coarse grid restriction 2242 @param[in] basis_coarse Coarse grid active vector basis 2243 @param[out] op_coarse Coarse grid operator 2244 @param[out] op_prolong Coarse to fine operator, or NULL 2245 @param[out] op_rstrict Fine to coarse operator, or NULL 2246 2247 @return An error code: 0 - success, otherwise - failure 2248 2249 @ref User 2250 **/ 2251 int CeedOperatorMultigridLevelCreate(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse, 2252 CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_rstrict) { 2253 CeedBasis basis_c_to_f = NULL; 2254 2255 CeedCall(CeedOperatorCheckReady(op_fine)); 2256 2257 // Build prolongation matrix, if required 2258 if (op_prolong || op_rstrict) { 2259 CeedBasis basis_fine; 2260 2261 CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine)); 2262 CeedCall(CeedBasisCreateProjection(basis_coarse, basis_fine, &basis_c_to_f)); 2263 } 2264 2265 // Core code 2266 CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_rstrict)); 2267 return CEED_ERROR_SUCCESS; 2268 } 2269 2270 /** 2271 @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a tensor basis for the active basis 2272 2273 Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable. 2274 2275 @param[in] op_fine Fine grid operator 2276 @param[in] p_mult_fine L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators 2277 @param[in] rstr_coarse Coarse grid restriction 2278 @param[in] basis_coarse Coarse grid active vector basis 2279 @param[in] interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators 2280 @param[out] op_coarse Coarse grid operator 2281 @param[out] op_prolong Coarse to fine operator, or NULL 2282 @param[out] op_rstrict Fine to coarse operator, or NULL 2283 2284 @return An error code: 0 - success, otherwise - failure 2285 2286 @ref User 2287 **/ 2288 int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse, 2289 const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, 2290 CeedOperator *op_rstrict) { 2291 Ceed ceed; 2292 CeedInt Q_f, Q_c; 2293 CeedBasis basis_fine, basis_c_to_f = NULL; 2294 2295 CeedCall(CeedOperatorCheckReady(op_fine)); 2296 CeedCall(CeedOperatorGetCeed(op_fine, &ceed)); 2297 2298 // Check for compatible quadrature spaces 2299 CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine)); 2300 CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f)); 2301 CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c)); 2302 CeedCheck(Q_f == Q_c, ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces"); 2303 2304 // Create coarse to fine basis, if required 2305 if (op_prolong || op_rstrict) { 2306 CeedInt dim, num_comp, num_nodes_c, P_1d_f, P_1d_c; 2307 CeedScalar *q_ref, *q_weight, *grad; 2308 2309 // Check if interpolation matrix is provided 2310 CeedCheck(interp_c_to_f, ceed, CEED_ERROR_INCOMPATIBLE, 2311 "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix"); 2312 CeedCall(CeedBasisGetDimension(basis_fine, &dim)); 2313 CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp)); 2314 CeedCall(CeedBasisGetNumNodes1D(basis_fine, &P_1d_f)); 2315 CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c)); 2316 P_1d_c = dim == 1 ? num_nodes_c : dim == 2 ? sqrt(num_nodes_c) : cbrt(num_nodes_c); 2317 CeedCall(CeedCalloc(P_1d_f, &q_ref)); 2318 CeedCall(CeedCalloc(P_1d_f, &q_weight)); 2319 CeedCall(CeedCalloc(P_1d_f * P_1d_c * dim, &grad)); 2320 CeedCall(CeedBasisCreateTensorH1(ceed, dim, num_comp, P_1d_c, P_1d_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f)); 2321 CeedCall(CeedFree(&q_ref)); 2322 CeedCall(CeedFree(&q_weight)); 2323 CeedCall(CeedFree(&grad)); 2324 } 2325 2326 // Core code 2327 CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_rstrict)); 2328 return CEED_ERROR_SUCCESS; 2329 } 2330 2331 /** 2332 @brief Create a multigrid coarse operator and level transfer operators for a CeedOperator with a non-tensor basis for the active vector 2333 2334 Note: Calling this function asserts that setup is complete and sets all four CeedOperators as immutable. 2335 2336 @param[in] op_fine Fine grid operator 2337 @param[in] p_mult_fine L-vector multiplicity in parallel gather/scatter, or NULL if not creating prolongation/restriction operators 2338 @param[in] rstr_coarse Coarse grid restriction 2339 @param[in] basis_coarse Coarse grid active vector basis 2340 @param[in] interp_c_to_f Matrix for coarse to fine interpolation, or NULL if not creating prolongation/restriction operators 2341 @param[out] op_coarse Coarse grid operator 2342 @param[out] op_prolong Coarse to fine operator, or NULL 2343 @param[out] op_rstrict Fine to coarse operator, or NULL 2344 2345 @return An error code: 0 - success, otherwise - failure 2346 2347 @ref User 2348 **/ 2349 int CeedOperatorMultigridLevelCreateH1(CeedOperator op_fine, CeedVector p_mult_fine, CeedElemRestriction rstr_coarse, CeedBasis basis_coarse, 2350 const CeedScalar *interp_c_to_f, CeedOperator *op_coarse, CeedOperator *op_prolong, CeedOperator *op_rstrict) { 2351 Ceed ceed; 2352 CeedInt Q_f, Q_c; 2353 CeedBasis basis_fine, basis_c_to_f = NULL; 2354 2355 CeedCall(CeedOperatorCheckReady(op_fine)); 2356 CeedCall(CeedOperatorGetCeed(op_fine, &ceed)); 2357 2358 // Check for compatible quadrature spaces 2359 CeedCall(CeedOperatorGetActiveBasis(op_fine, &basis_fine)); 2360 CeedCall(CeedBasisGetNumQuadraturePoints(basis_fine, &Q_f)); 2361 CeedCall(CeedBasisGetNumQuadraturePoints(basis_coarse, &Q_c)); 2362 CeedCheck(Q_f == Q_c, ceed, CEED_ERROR_DIMENSION, "Bases must have compatible quadrature spaces"); 2363 2364 // Coarse to fine basis 2365 if (op_prolong || op_rstrict) { 2366 CeedInt dim, num_comp, num_nodes_c, num_nodes_f; 2367 CeedScalar *q_ref, *q_weight, *grad; 2368 CeedElemTopology topo; 2369 2370 // Check if interpolation matrix is provided 2371 CeedCheck(interp_c_to_f, ceed, CEED_ERROR_INCOMPATIBLE, 2372 "Prolongation or restriction operator creation requires coarse-to-fine interpolation matrix"); 2373 CeedCall(CeedBasisGetTopology(basis_fine, &topo)); 2374 CeedCall(CeedBasisGetDimension(basis_fine, &dim)); 2375 CeedCall(CeedBasisGetNumComponents(basis_fine, &num_comp)); 2376 CeedCall(CeedBasisGetNumNodes(basis_fine, &num_nodes_f)); 2377 CeedCall(CeedElemRestrictionGetElementSize(rstr_coarse, &num_nodes_c)); 2378 CeedCall(CeedCalloc(num_nodes_f * dim, &q_ref)); 2379 CeedCall(CeedCalloc(num_nodes_f, &q_weight)); 2380 CeedCall(CeedCalloc(num_nodes_f * num_nodes_c * dim, &grad)); 2381 CeedCall(CeedBasisCreateH1(ceed, topo, num_comp, num_nodes_c, num_nodes_f, interp_c_to_f, grad, q_ref, q_weight, &basis_c_to_f)); 2382 CeedCall(CeedFree(&q_ref)); 2383 CeedCall(CeedFree(&q_weight)); 2384 CeedCall(CeedFree(&grad)); 2385 } 2386 2387 // Core code 2388 CeedCall(CeedSingleOperatorMultigridLevel(op_fine, p_mult_fine, rstr_coarse, basis_coarse, basis_c_to_f, op_coarse, op_prolong, op_rstrict)); 2389 return CEED_ERROR_SUCCESS; 2390 } 2391 2392 /** 2393 @brief Build a FDM based approximate inverse for each element for a CeedOperator 2394 2395 This returns a CeedOperator and CeedVector to apply a Fast Diagonalization Method based approximate inverse. 2396 This function obtains the simultaneous diagonalization for the 1D mass and Laplacian operators, \f$M = V^T V, K = V^T S V\f$. 2397 The assembled QFunction is used to modify the eigenvalues from simultaneous diagonalization and obtain an approximate inverse of the form \f$V^T 2398 \hat S V\f$. 2399 The CeedOperator must be linear and non-composite. 2400 The associated CeedQFunction must therefore also be linear. 2401 2402 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 2403 2404 @param[in] op CeedOperator to create element inverses 2405 @param[out] fdm_inv CeedOperator to apply the action of a FDM based inverse for each element 2406 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 2407 2408 @return An error code: 0 - success, otherwise - failure 2409 2410 @ref User 2411 **/ 2412 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdm_inv, CeedRequest *request) { 2413 Ceed ceed, ceed_parent; 2414 bool interp = false, grad = false, is_tensor_basis = true; 2415 CeedInt num_input_fields, P_1d, Q_1d, num_nodes, num_qpts, dim, num_comp = 1, num_elem = 1; 2416 CeedSize l_size = 1; 2417 CeedScalar *mass, *laplace, *x, *fdm_interp, *lambda, *elem_avg; 2418 const CeedScalar *interp_1d, *grad_1d, *q_weight_1d; 2419 CeedVector q_data; 2420 CeedElemRestriction rstr = NULL, rstr_qd_i; 2421 CeedBasis basis = NULL, fdm_basis; 2422 CeedQFunctionContext ctx_fdm; 2423 CeedQFunctionField *qf_fields; 2424 CeedQFunction qf, qf_fdm; 2425 CeedOperatorField *op_fields; 2426 2427 CeedCall(CeedOperatorCheckReady(op)); 2428 2429 if (op->CreateFDMElementInverse) { 2430 // Backend version 2431 CeedCall(op->CreateFDMElementInverse(op, fdm_inv, request)); 2432 return CEED_ERROR_SUCCESS; 2433 } else { 2434 // Operator fallback 2435 CeedOperator op_fallback; 2436 2437 CeedCall(CeedOperatorGetFallback(op, &op_fallback)); 2438 if (op_fallback) { 2439 CeedCall(CeedOperatorCreateFDMElementInverse(op_fallback, fdm_inv, request)); 2440 return CEED_ERROR_SUCCESS; 2441 } 2442 } 2443 2444 // Default interface implementation 2445 CeedCall(CeedOperatorGetCeed(op, &ceed)); 2446 CeedCall(CeedOperatorGetFallbackParentCeed(op, &ceed_parent)); 2447 CeedCall(CeedOperatorGetQFunction(op, &qf)); 2448 2449 // Determine active input basis 2450 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &op_fields, NULL, NULL)); 2451 CeedCall(CeedQFunctionGetFields(qf, NULL, &qf_fields, NULL, NULL)); 2452 for (CeedInt i = 0; i < num_input_fields; i++) { 2453 CeedVector vec; 2454 2455 CeedCall(CeedOperatorFieldGetVector(op_fields[i], &vec)); 2456 if (vec == CEED_VECTOR_ACTIVE) { 2457 CeedEvalMode eval_mode; 2458 2459 CeedCall(CeedQFunctionFieldGetEvalMode(qf_fields[i], &eval_mode)); 2460 interp = interp || eval_mode == CEED_EVAL_INTERP; 2461 grad = grad || eval_mode == CEED_EVAL_GRAD; 2462 CeedCall(CeedOperatorFieldGetBasis(op_fields[i], &basis)); 2463 CeedCall(CeedOperatorFieldGetElemRestriction(op_fields[i], &rstr)); 2464 } 2465 } 2466 CeedCheck(basis, ceed, CEED_ERROR_BACKEND, "No active field set"); 2467 CeedCall(CeedBasisGetNumNodes1D(basis, &P_1d)); 2468 CeedCall(CeedBasisGetNumNodes(basis, &num_nodes)); 2469 CeedCall(CeedBasisGetNumQuadraturePoints1D(basis, &Q_1d)); 2470 CeedCall(CeedBasisGetNumQuadraturePoints(basis, &num_qpts)); 2471 CeedCall(CeedBasisGetDimension(basis, &dim)); 2472 CeedCall(CeedBasisGetNumComponents(basis, &num_comp)); 2473 CeedCall(CeedElemRestrictionGetNumElements(rstr, &num_elem)); 2474 CeedCall(CeedElemRestrictionGetLVectorSize(rstr, &l_size)); 2475 2476 // Build and diagonalize 1D Mass and Laplacian 2477 CeedCall(CeedBasisIsTensor(basis, &is_tensor_basis)); 2478 CeedCheck(is_tensor_basis, ceed, CEED_ERROR_BACKEND, "FDMElementInverse only supported for tensor bases"); 2479 CeedCall(CeedCalloc(P_1d * P_1d, &mass)); 2480 CeedCall(CeedCalloc(P_1d * P_1d, &laplace)); 2481 CeedCall(CeedCalloc(P_1d * P_1d, &x)); 2482 CeedCall(CeedCalloc(P_1d * P_1d, &fdm_interp)); 2483 CeedCall(CeedCalloc(P_1d, &lambda)); 2484 // -- Build matrices 2485 CeedCall(CeedBasisGetInterp1D(basis, &interp_1d)); 2486 CeedCall(CeedBasisGetGrad1D(basis, &grad_1d)); 2487 CeedCall(CeedBasisGetQWeights(basis, &q_weight_1d)); 2488 CeedCall(CeedBuildMassLaplace(interp_1d, grad_1d, q_weight_1d, P_1d, Q_1d, dim, mass, laplace)); 2489 2490 // -- Diagonalize 2491 CeedCall(CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P_1d)); 2492 CeedCall(CeedFree(&mass)); 2493 CeedCall(CeedFree(&laplace)); 2494 for (CeedInt i = 0; i < P_1d; i++) { 2495 for (CeedInt j = 0; j < P_1d; j++) fdm_interp[i + j * P_1d] = x[j + i * P_1d]; 2496 } 2497 CeedCall(CeedFree(&x)); 2498 2499 { 2500 CeedInt layout[3], num_modes = (interp ? 1 : 0) + (grad ? dim : 0); 2501 CeedScalar max_norm = 0; 2502 const CeedScalar *assembled_array, *q_weight_array; 2503 CeedVector assembled = NULL, q_weight; 2504 CeedElemRestriction rstr_qf = NULL; 2505 2506 // Assemble QFunction 2507 CeedCall(CeedOperatorLinearAssembleQFunctionBuildOrUpdate(op, &assembled, &rstr_qf, request)); 2508 CeedCall(CeedElemRestrictionGetELayout(rstr_qf, &layout)); 2509 CeedCall(CeedElemRestrictionDestroy(&rstr_qf)); 2510 CeedCall(CeedVectorNorm(assembled, CEED_NORM_MAX, &max_norm)); 2511 2512 // Calculate element averages 2513 CeedCall(CeedVectorCreate(ceed_parent, num_qpts, &q_weight)); 2514 CeedCall(CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT, CEED_VECTOR_NONE, q_weight)); 2515 CeedCall(CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembled_array)); 2516 CeedCall(CeedVectorGetArrayRead(q_weight, CEED_MEM_HOST, &q_weight_array)); 2517 CeedCall(CeedCalloc(num_elem, &elem_avg)); 2518 const CeedScalar qf_value_bound = max_norm * 100 * CEED_EPSILON; 2519 2520 for (CeedInt e = 0; e < num_elem; e++) { 2521 CeedInt count = 0; 2522 2523 for (CeedInt q = 0; q < num_qpts; q++) { 2524 for (CeedInt i = 0; i < num_comp * num_comp * num_modes * num_modes; i++) { 2525 if (fabs(assembled_array[q * layout[0] + i * layout[1] + e * layout[2]]) > qf_value_bound) { 2526 elem_avg[e] += assembled_array[q * layout[0] + i * layout[1] + e * layout[2]] / q_weight_array[q]; 2527 count++; 2528 } 2529 } 2530 } 2531 if (count) { 2532 elem_avg[e] /= count; 2533 } else { 2534 elem_avg[e] = 1.0; 2535 } 2536 } 2537 CeedCall(CeedVectorRestoreArrayRead(assembled, &assembled_array)); 2538 CeedCall(CeedVectorDestroy(&assembled)); 2539 CeedCall(CeedVectorRestoreArrayRead(q_weight, &q_weight_array)); 2540 CeedCall(CeedVectorDestroy(&q_weight)); 2541 } 2542 2543 // Build FDM diagonal 2544 { 2545 CeedScalar *q_data_array, *fdm_diagonal; 2546 2547 CeedCall(CeedCalloc(num_comp * num_nodes, &fdm_diagonal)); 2548 const CeedScalar fdm_diagonal_bound = num_nodes * CEED_EPSILON; 2549 for (CeedInt c = 0; c < num_comp; c++) { 2550 for (CeedInt n = 0; n < num_nodes; n++) { 2551 if (interp) fdm_diagonal[c * num_nodes + n] = 1.0; 2552 if (grad) { 2553 for (CeedInt d = 0; d < dim; d++) { 2554 CeedInt i = (n / CeedIntPow(P_1d, d)) % P_1d; 2555 fdm_diagonal[c * num_nodes + n] += lambda[i]; 2556 } 2557 } 2558 if (fabs(fdm_diagonal[c * num_nodes + n]) < fdm_diagonal_bound) fdm_diagonal[c * num_nodes + n] = fdm_diagonal_bound; 2559 } 2560 } 2561 CeedCall(CeedVectorCreate(ceed_parent, num_elem * num_comp * num_nodes, &q_data)); 2562 CeedCall(CeedVectorSetValue(q_data, 0.0)); 2563 CeedCall(CeedVectorGetArrayWrite(q_data, CEED_MEM_HOST, &q_data_array)); 2564 for (CeedInt e = 0; e < num_elem; e++) { 2565 for (CeedInt c = 0; c < num_comp; c++) { 2566 for (CeedInt n = 0; n < num_nodes; n++) 2567 q_data_array[(e * num_comp + c) * num_nodes + n] = 1. / (elem_avg[e] * fdm_diagonal[c * num_nodes + n]); 2568 } 2569 } 2570 CeedCall(CeedFree(&elem_avg)); 2571 CeedCall(CeedFree(&fdm_diagonal)); 2572 CeedCall(CeedVectorRestoreArray(q_data, &q_data_array)); 2573 } 2574 2575 // Setup FDM operator 2576 // -- Basis 2577 { 2578 CeedScalar *grad_dummy, *q_ref_dummy, *q_weight_dummy; 2579 2580 CeedCall(CeedCalloc(P_1d * P_1d, &grad_dummy)); 2581 CeedCall(CeedCalloc(P_1d, &q_ref_dummy)); 2582 CeedCall(CeedCalloc(P_1d, &q_weight_dummy)); 2583 CeedCall(CeedBasisCreateTensorH1(ceed_parent, dim, num_comp, P_1d, P_1d, fdm_interp, grad_dummy, q_ref_dummy, q_weight_dummy, &fdm_basis)); 2584 CeedCall(CeedFree(&fdm_interp)); 2585 CeedCall(CeedFree(&grad_dummy)); 2586 CeedCall(CeedFree(&q_ref_dummy)); 2587 CeedCall(CeedFree(&q_weight_dummy)); 2588 CeedCall(CeedFree(&lambda)); 2589 } 2590 2591 // -- Restriction 2592 { 2593 CeedInt strides[3] = {1, num_nodes, num_nodes * num_comp}; 2594 CeedCall(CeedElemRestrictionCreateStrided(ceed_parent, num_elem, num_nodes, num_comp, num_elem * num_comp * num_nodes, strides, &rstr_qd_i)); 2595 } 2596 2597 // -- QFunction 2598 CeedCall(CeedQFunctionCreateInteriorByName(ceed_parent, "Scale", &qf_fdm)); 2599 CeedCall(CeedQFunctionAddInput(qf_fdm, "input", num_comp, CEED_EVAL_INTERP)); 2600 CeedCall(CeedQFunctionAddInput(qf_fdm, "scale", num_comp, CEED_EVAL_NONE)); 2601 CeedCall(CeedQFunctionAddOutput(qf_fdm, "output", num_comp, CEED_EVAL_INTERP)); 2602 CeedCall(CeedQFunctionSetUserFlopsEstimate(qf_fdm, num_comp)); 2603 2604 // -- QFunction context 2605 { 2606 CeedInt *num_comp_data; 2607 2608 CeedCall(CeedCalloc(1, &num_comp_data)); 2609 num_comp_data[0] = num_comp; 2610 CeedCall(CeedQFunctionContextCreate(ceed, &ctx_fdm)); 2611 CeedCall(CeedQFunctionContextSetData(ctx_fdm, CEED_MEM_HOST, CEED_OWN_POINTER, sizeof(*num_comp_data), num_comp_data)); 2612 } 2613 CeedCall(CeedQFunctionSetContext(qf_fdm, ctx_fdm)); 2614 CeedCall(CeedQFunctionContextDestroy(&ctx_fdm)); 2615 2616 // -- Operator 2617 CeedCall(CeedOperatorCreate(ceed_parent, qf_fdm, NULL, NULL, fdm_inv)); 2618 CeedCall(CeedOperatorSetField(*fdm_inv, "input", rstr, fdm_basis, CEED_VECTOR_ACTIVE)); 2619 CeedCall(CeedOperatorSetField(*fdm_inv, "scale", rstr_qd_i, CEED_BASIS_NONE, q_data)); 2620 CeedCall(CeedOperatorSetField(*fdm_inv, "output", rstr, fdm_basis, CEED_VECTOR_ACTIVE)); 2621 2622 // Cleanup 2623 CeedCall(CeedVectorDestroy(&q_data)); 2624 CeedCall(CeedBasisDestroy(&fdm_basis)); 2625 CeedCall(CeedElemRestrictionDestroy(&rstr_qd_i)); 2626 CeedCall(CeedQFunctionDestroy(&qf_fdm)); 2627 return CEED_ERROR_SUCCESS; 2628 } 2629 2630 /// @} 2631