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 <stdbool.h> 12 #include <stdio.h> 13 #include <string.h> 14 15 /// @file 16 /// Implementation of CeedOperator interfaces 17 18 /// ---------------------------------------------------------------------------- 19 /// CeedOperator Library Internal Functions 20 /// ---------------------------------------------------------------------------- 21 /// @addtogroup CeedOperatorDeveloper 22 /// @{ 23 24 /** 25 @brief Check if a CeedOperator Field matches the QFunction Field 26 27 @param[in] ceed Ceed object for error handling 28 @param[in] qf_field QFunction Field matching Operator Field 29 @param[in] r Operator Field ElemRestriction 30 @param[in] b Operator Field Basis 31 32 @return An error code: 0 - success, otherwise - failure 33 34 @ref Developer 35 **/ 36 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field, CeedElemRestriction r, CeedBasis b) { 37 CeedEvalMode eval_mode = qf_field->eval_mode; 38 CeedInt dim = 1, num_comp = 1, q_comp = 1, restr_num_comp = 1, size = qf_field->size; 39 40 // Restriction 41 CeedCheck((r == CEED_ELEMRESTRICTION_NONE) == (eval_mode == CEED_EVAL_WEIGHT), ceed, CEED_ERROR_INCOMPATIBLE, 42 "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT must be used together."); 43 if (r != CEED_ELEMRESTRICTION_NONE) { 44 CeedCall(CeedElemRestrictionGetNumComponents(r, &restr_num_comp)); 45 } 46 // Basis 47 CeedCheck((b == CEED_BASIS_COLLOCATED) == (eval_mode == CEED_EVAL_NONE), ceed, CEED_ERROR_INCOMPATIBLE, 48 "CEED_BASIS_COLLOCATED and CEED_EVAL_NONE must be used together."); 49 if (b != CEED_BASIS_COLLOCATED) { 50 CeedCall(CeedBasisGetDimension(b, &dim)); 51 CeedCall(CeedBasisGetNumComponents(b, &num_comp)); 52 CeedCall(CeedBasisGetNumQuadratureComponents(b, eval_mode, &q_comp)); 53 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || restr_num_comp == num_comp, ceed, CEED_ERROR_DIMENSION, 54 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components, but Basis has %" CeedInt_FMT 55 " components", 56 qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp, num_comp); 57 } 58 // Field size 59 switch (eval_mode) { 60 case CEED_EVAL_NONE: 61 CeedCheck(size == restr_num_comp, ceed, CEED_ERROR_DIMENSION, 62 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has %" CeedInt_FMT " components", qf_field->field_name, 63 qf_field->size, CeedEvalModes[qf_field->eval_mode], restr_num_comp); 64 break; 65 case CEED_EVAL_INTERP: 66 case CEED_EVAL_GRAD: 67 case CEED_EVAL_DIV: 68 case CEED_EVAL_CURL: 69 CeedCheck(size == num_comp * q_comp, ceed, CEED_ERROR_DIMENSION, 70 "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction/Basis has %" CeedInt_FMT " components", qf_field->field_name, 71 qf_field->size, CeedEvalModes[qf_field->eval_mode], num_comp * q_comp); 72 break; 73 case CEED_EVAL_WEIGHT: 74 // No additional checks required 75 break; 76 } 77 return CEED_ERROR_SUCCESS; 78 } 79 80 /** 81 @brief View a field of a CeedOperator 82 83 @param[in] field Operator field to view 84 @param[in] qf_field QFunction field (carries field name) 85 @param[in] field_number Number of field being viewed 86 @param[in] sub true indicates sub-operator, which increases indentation; false for top-level operator 87 @param[in] input true for an input field; false for output field 88 @param[in] stream Stream to view to, e.g., stdout 89 90 @return An error code: 0 - success, otherwise - failure 91 92 @ref Utility 93 **/ 94 static int CeedOperatorFieldView(CeedOperatorField field, CeedQFunctionField qf_field, CeedInt field_number, bool sub, bool input, FILE *stream) { 95 const char *pre = sub ? " " : ""; 96 const char *in_out = input ? "Input" : "Output"; 97 98 fprintf(stream, 99 "%s %s field %" CeedInt_FMT 100 ":\n" 101 "%s Name: \"%s\"\n", 102 pre, in_out, field_number, pre, qf_field->field_name); 103 104 fprintf(stream, "%s Size: %" CeedInt_FMT "\n", pre, qf_field->size); 105 106 fprintf(stream, "%s EvalMode: %s\n", pre, CeedEvalModes[qf_field->eval_mode]); 107 108 if (field->basis == CEED_BASIS_COLLOCATED) fprintf(stream, "%s Collocated basis\n", pre); 109 110 if (field->vec == CEED_VECTOR_ACTIVE) fprintf(stream, "%s Active vector\n", pre); 111 else if (field->vec == CEED_VECTOR_NONE) fprintf(stream, "%s No vector\n", pre); 112 113 return CEED_ERROR_SUCCESS; 114 } 115 116 /** 117 @brief View a single CeedOperator 118 119 @param[in] op CeedOperator to view 120 @param[in] sub Boolean flag for sub-operator 121 @param[in] stream Stream to write; typically stdout/stderr or a file 122 123 @return Error code: 0 - success, otherwise - failure 124 125 @ref Utility 126 **/ 127 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) { 128 const char *pre = sub ? " " : ""; 129 130 CeedInt num_elem, num_qpts; 131 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 132 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 133 134 CeedInt total_fields = 0; 135 CeedCall(CeedOperatorGetNumArgs(op, &total_fields)); 136 fprintf(stream, "%s %" CeedInt_FMT " elements with %" CeedInt_FMT " quadrature points each\n", pre, num_elem, num_qpts); 137 138 fprintf(stream, "%s %" CeedInt_FMT " field%s\n", pre, total_fields, total_fields > 1 ? "s" : ""); 139 140 fprintf(stream, "%s %" CeedInt_FMT " input field%s:\n", pre, op->qf->num_input_fields, op->qf->num_input_fields > 1 ? "s" : ""); 141 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 142 CeedCall(CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i], i, sub, 1, stream)); 143 } 144 145 fprintf(stream, "%s %" CeedInt_FMT " output field%s:\n", pre, op->qf->num_output_fields, op->qf->num_output_fields > 1 ? "s" : ""); 146 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 147 CeedCall(CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i], i, sub, 0, stream)); 148 } 149 return CEED_ERROR_SUCCESS; 150 } 151 152 /** 153 @brief Find the active vector basis for a non-composite CeedOperator 154 155 @param[in] op CeedOperator to find active basis for 156 @param[out] active_basis Basis for active input vector or NULL for composite operator 157 158 @return An error code: 0 - success, otherwise - failure 159 160 @ref Developer 161 **/ 162 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) { 163 Ceed ceed; 164 CeedCall(CeedOperatorGetCeed(op, &ceed)); 165 166 *active_basis = NULL; 167 if (op->is_composite) return CEED_ERROR_SUCCESS; 168 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 169 if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 170 CeedCheck(!*active_basis || *active_basis == op->input_fields[i]->basis, ceed, CEED_ERROR_MINOR, "Multiple active CeedBases found"); 171 *active_basis = op->input_fields[i]->basis; 172 } 173 } 174 175 CeedCheck(*active_basis, ceed, CEED_ERROR_INCOMPLETE, "No active CeedBasis found"); 176 return CEED_ERROR_SUCCESS; 177 } 178 179 /** 180 @brief Find the active vector ElemRestriction for a non-composite CeedOperator 181 182 @param[in] op CeedOperator to find active basis for 183 @param[out] active_rstr ElemRestriction for active input vector or NULL for composite operator 184 185 @return An error code: 0 - success, otherwise - failure 186 187 @ref Utility 188 **/ 189 int CeedOperatorGetActiveElemRestriction(CeedOperator op, CeedElemRestriction *active_rstr) { 190 Ceed ceed; 191 CeedCall(CeedOperatorGetCeed(op, &ceed)); 192 193 *active_rstr = NULL; 194 if (op->is_composite) return CEED_ERROR_SUCCESS; 195 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 196 if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 197 CeedCheck(!*active_rstr || *active_rstr == op->input_fields[i]->elem_rstr, ceed, CEED_ERROR_MINOR, 198 "Multiple active CeedElemRestrictions found"); 199 *active_rstr = op->input_fields[i]->elem_rstr; 200 } 201 } 202 203 CeedCheck(*active_rstr, ceed, CEED_ERROR_INCOMPLETE, "No active CeedElemRestriction found"); 204 return CEED_ERROR_SUCCESS; 205 } 206 207 /** 208 @brief Set QFunctionContext field values of the specified type. 209 210 For composite operators, the value is set in all sub-operator QFunctionContexts that have a matching `field_name`. 211 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 212 any field of a matching type. 213 214 @param[in,out] op CeedOperator 215 @param[in] field_label Label of field to set 216 @param[in] field_type Type of field to set 217 @param[in] values Values to set 218 219 @return An error code: 0 - success, otherwise - failure 220 221 @ref User 222 **/ 223 static int CeedOperatorContextSetGeneric(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) { 224 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 225 226 // Check if field_label and op correspond 227 if (field_label->from_op) { 228 CeedInt index = -1; 229 230 for (CeedInt i = 0; i < op->num_context_labels; i++) { 231 if (op->context_labels[i] == field_label) index = i; 232 } 233 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 234 } 235 236 bool is_composite = false; 237 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 238 if (is_composite) { 239 CeedInt num_sub; 240 CeedOperator *sub_operators; 241 242 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 243 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 244 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 245 "Composite operator modified after ContextFieldLabel created"); 246 247 for (CeedInt i = 0; i < num_sub; i++) { 248 // Try every sub-operator, ok if some sub-operators do not have field 249 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 250 CeedCall(CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values)); 251 } 252 } 253 } else { 254 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 255 CeedCall(CeedQFunctionContextSetGeneric(op->qf->ctx, field_label, field_type, values)); 256 } 257 258 return CEED_ERROR_SUCCESS; 259 } 260 261 /** 262 @brief Get QFunctionContext field values of the specified type, read-only. 263 264 For composite operators, the values retrieved are for the first sub-operator QFunctionContext that have a matching `field_name`. 265 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 266 any field of a matching type. 267 268 @param[in,out] op CeedOperator 269 @param[in] field_label Label of field to set 270 @param[in] field_type Type of field to set 271 @param[out] num_values Number of values of type `field_type` in array `values` 272 @param[out] values Values in the label 273 274 @return An error code: 0 - success, otherwise - failure 275 276 @ref User 277 **/ 278 static int CeedOperatorContextGetGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, size_t *num_values, 279 void *values) { 280 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 281 282 *(void **)values = NULL; 283 *num_values = 0; 284 285 // Check if field_label and op correspond 286 if (field_label->from_op) { 287 CeedInt index = -1; 288 289 for (CeedInt i = 0; i < op->num_context_labels; i++) { 290 if (op->context_labels[i] == field_label) index = i; 291 } 292 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 293 } 294 295 bool is_composite = false; 296 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 297 if (is_composite) { 298 CeedInt num_sub; 299 CeedOperator *sub_operators; 300 301 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 302 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 303 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 304 "Composite operator modified after ContextFieldLabel created"); 305 306 for (CeedInt i = 0; i < num_sub; i++) { 307 // Try every sub-operator, ok if some sub-operators do not have field 308 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 309 CeedCall(CeedQFunctionContextGetGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, num_values, values)); 310 return CEED_ERROR_SUCCESS; 311 } 312 } 313 } else { 314 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 315 CeedCall(CeedQFunctionContextGetGenericRead(op->qf->ctx, field_label, field_type, num_values, values)); 316 } 317 318 return CEED_ERROR_SUCCESS; 319 } 320 321 /** 322 @brief Restore QFunctionContext field values of the specified type, read-only. 323 324 For composite operators, the values restored are for the first sub-operator QFunctionContext that have a matching `field_name`. 325 A non-zero error code is returned for single operators that do not have a matching field of the same type or composite operators that do not have 326 any field of a matching type. 327 328 @param[in,out] op CeedOperator 329 @param[in] field_label Label of field to set 330 @param[in] field_type Type of field to set 331 @param[in] values Values array to restore 332 333 @return An error code: 0 - success, otherwise - failure 334 335 @ref User 336 **/ 337 static int CeedOperatorContextRestoreGenericRead(CeedOperator op, CeedContextFieldLabel field_label, CeedContextFieldType field_type, void *values) { 338 CeedCheck(field_label, op->ceed, CEED_ERROR_UNSUPPORTED, "Invalid field label"); 339 340 // Check if field_label and op correspond 341 if (field_label->from_op) { 342 CeedInt index = -1; 343 344 for (CeedInt i = 0; i < op->num_context_labels; i++) { 345 if (op->context_labels[i] == field_label) index = i; 346 } 347 CeedCheck(index != -1, op->ceed, CEED_ERROR_UNSUPPORTED, "ContextFieldLabel does not correspond to the operator"); 348 } 349 350 bool is_composite = false; 351 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 352 if (is_composite) { 353 CeedInt num_sub; 354 CeedOperator *sub_operators; 355 356 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 357 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 358 CeedCheck(num_sub == field_label->num_sub_labels, op->ceed, CEED_ERROR_UNSUPPORTED, 359 "Composite operator modified after ContextFieldLabel created"); 360 361 for (CeedInt i = 0; i < num_sub; i++) { 362 // Try every sub-operator, ok if some sub-operators do not have field 363 if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) { 364 CeedCall(CeedQFunctionContextRestoreGenericRead(sub_operators[i]->qf->ctx, field_label->sub_labels[i], field_type, values)); 365 return CEED_ERROR_SUCCESS; 366 } 367 } 368 } else { 369 CeedCheck(op->qf->ctx, op->ceed, CEED_ERROR_UNSUPPORTED, "QFunction does not have context data"); 370 CeedCall(CeedQFunctionContextRestoreGenericRead(op->qf->ctx, field_label, field_type, values)); 371 } 372 373 return CEED_ERROR_SUCCESS; 374 } 375 376 /// @} 377 378 /// ---------------------------------------------------------------------------- 379 /// CeedOperator Backend API 380 /// ---------------------------------------------------------------------------- 381 /// @addtogroup CeedOperatorBackend 382 /// @{ 383 384 /** 385 @brief Get the number of arguments associated with a CeedOperator 386 387 @param[in] op CeedOperator 388 @param[out] num_args Variable to store vector number of arguments 389 390 @return An error code: 0 - success, otherwise - failure 391 392 @ref Backend 393 **/ 394 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) { 395 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operators"); 396 *num_args = op->num_fields; 397 return CEED_ERROR_SUCCESS; 398 } 399 400 /** 401 @brief Get the setup status of a CeedOperator 402 403 @param[in] op CeedOperator 404 @param[out] is_setup_done Variable to store setup status 405 406 @return An error code: 0 - success, otherwise - failure 407 408 @ref Backend 409 **/ 410 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) { 411 *is_setup_done = op->is_backend_setup; 412 return CEED_ERROR_SUCCESS; 413 } 414 415 /** 416 @brief Get the QFunction associated with a CeedOperator 417 418 @param[in] op CeedOperator 419 @param[out] qf Variable to store QFunction 420 421 @return An error code: 0 - success, otherwise - failure 422 423 @ref Backend 424 **/ 425 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) { 426 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 427 *qf = op->qf; 428 return CEED_ERROR_SUCCESS; 429 } 430 431 /** 432 @brief Get a boolean value indicating if the CeedOperator is composite 433 434 @param[in] op CeedOperator 435 @param[out] is_composite Variable to store composite status 436 437 @return An error code: 0 - success, otherwise - failure 438 439 @ref Backend 440 **/ 441 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) { 442 *is_composite = op->is_composite; 443 return CEED_ERROR_SUCCESS; 444 } 445 446 /** 447 @brief Get the backend data of a CeedOperator 448 449 @param[in] op CeedOperator 450 @param[out] data Variable to store data 451 452 @return An error code: 0 - success, otherwise - failure 453 454 @ref Backend 455 **/ 456 int CeedOperatorGetData(CeedOperator op, void *data) { 457 *(void **)data = op->data; 458 return CEED_ERROR_SUCCESS; 459 } 460 461 /** 462 @brief Set the backend data of a CeedOperator 463 464 @param[in,out] op CeedOperator 465 @param[in] data Data to set 466 467 @return An error code: 0 - success, otherwise - failure 468 469 @ref Backend 470 **/ 471 int CeedOperatorSetData(CeedOperator op, void *data) { 472 op->data = data; 473 return CEED_ERROR_SUCCESS; 474 } 475 476 /** 477 @brief Increment the reference counter for a CeedOperator 478 479 @param[in,out] op CeedOperator to increment the reference counter 480 481 @return An error code: 0 - success, otherwise - failure 482 483 @ref Backend 484 **/ 485 int CeedOperatorReference(CeedOperator op) { 486 op->ref_count++; 487 return CEED_ERROR_SUCCESS; 488 } 489 490 /** 491 @brief Set the setup flag of a CeedOperator to True 492 493 @param[in,out] op CeedOperator 494 495 @return An error code: 0 - success, otherwise - failure 496 497 @ref Backend 498 **/ 499 int CeedOperatorSetSetupDone(CeedOperator op) { 500 op->is_backend_setup = true; 501 return CEED_ERROR_SUCCESS; 502 } 503 504 /// @} 505 506 /// ---------------------------------------------------------------------------- 507 /// CeedOperator Public API 508 /// ---------------------------------------------------------------------------- 509 /// @addtogroup CeedOperatorUser 510 /// @{ 511 512 /** 513 @brief Create a CeedOperator and associate a CeedQFunction. 514 515 A CeedBasis and CeedElemRestriction can be associated with CeedQFunction fields with \ref CeedOperatorSetField. 516 517 @param[in] ceed Ceed object where the CeedOperator will be created 518 @param[in] qf QFunction defining the action of the operator at quadrature points 519 @param[in] dqf QFunction defining the action of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE) 520 @param[in] dqfT QFunction defining the action of the transpose of the Jacobian of @a qf (or @ref CEED_QFUNCTION_NONE) 521 @param[out] op Address of the variable where the newly created CeedOperator will be stored 522 523 @return An error code: 0 - success, otherwise - failure 524 525 @ref User 526 */ 527 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, CeedQFunction dqfT, CeedOperator *op) { 528 if (!ceed->OperatorCreate) { 529 Ceed delegate; 530 531 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator")); 532 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support OperatorCreate"); 533 CeedCall(CeedOperatorCreate(delegate, qf, dqf, dqfT, op)); 534 return CEED_ERROR_SUCCESS; 535 } 536 537 CeedCheck(qf && qf != CEED_QFUNCTION_NONE, ceed, CEED_ERROR_MINOR, "Operator must have a valid QFunction."); 538 539 CeedCall(CeedCalloc(1, op)); 540 CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed)); 541 (*op)->ref_count = 1; 542 (*op)->input_size = -1; 543 (*op)->output_size = -1; 544 CeedCall(CeedQFunctionReferenceCopy(qf, &(*op)->qf)); 545 if (dqf && dqf != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqf, &(*op)->dqf)); 546 if (dqfT && dqfT != CEED_QFUNCTION_NONE) CeedCall(CeedQFunctionReferenceCopy(dqfT, &(*op)->dqfT)); 547 CeedCall(CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled)); 548 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields)); 549 CeedCall(CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields)); 550 CeedCall(ceed->OperatorCreate(*op)); 551 return CEED_ERROR_SUCCESS; 552 } 553 554 /** 555 @brief Create an operator that composes the action of several operators 556 557 @param[in] ceed Ceed object where the CeedOperator will be created 558 @param[out] op Address of the variable where the newly created Composite CeedOperator will be stored 559 560 @return An error code: 0 - success, otherwise - failure 561 562 @ref User 563 */ 564 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) { 565 if (!ceed->CompositeOperatorCreate) { 566 Ceed delegate; 567 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Operator")); 568 569 if (delegate) { 570 CeedCall(CeedCompositeOperatorCreate(delegate, op)); 571 return CEED_ERROR_SUCCESS; 572 } 573 } 574 575 CeedCall(CeedCalloc(1, op)); 576 CeedCall(CeedReferenceCopy(ceed, &(*op)->ceed)); 577 (*op)->ref_count = 1; 578 (*op)->is_composite = true; 579 CeedCall(CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators)); 580 (*op)->input_size = -1; 581 (*op)->output_size = -1; 582 583 if (ceed->CompositeOperatorCreate) CeedCall(ceed->CompositeOperatorCreate(*op)); 584 return CEED_ERROR_SUCCESS; 585 } 586 587 /** 588 @brief Copy the pointer to a CeedOperator. 589 590 Both pointers should be destroyed with `CeedOperatorDestroy()`. 591 592 Note: If the value of `op_copy` passed to this function is non-NULL, then it is assumed that `op_copy` is a pointer to a CeedOperator. 593 This CeedOperator will be destroyed if `op_copy` is the only reference to this CeedOperator. 594 595 @param[in] op CeedOperator to copy reference to 596 @param[in,out] op_copy Variable to store copied reference 597 598 @return An error code: 0 - success, otherwise - failure 599 600 @ref User 601 **/ 602 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) { 603 CeedCall(CeedOperatorReference(op)); 604 CeedCall(CeedOperatorDestroy(op_copy)); 605 *op_copy = op; 606 return CEED_ERROR_SUCCESS; 607 } 608 609 /** 610 @brief Provide a field to a CeedOperator for use by its CeedQFunction. 611 612 This function is used to specify both active and passive fields to a CeedOperator. 613 For passive fields, a vector @arg v must be provided. 614 Passive fields can inputs or outputs (updated in-place when operator is applied). 615 616 Active fields must be specified using this function, but their data (in a CeedVector) is passed in CeedOperatorApply(). 617 There can be at most one active input CeedVector and at most one active output CeedVector passed to CeedOperatorApply(). 618 619 @param[in,out] op CeedOperator on which to provide the field 620 @param[in] field_name Name of the field (to be matched with the name used by CeedQFunction) 621 @param[in] r CeedElemRestriction 622 @param[in] b CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points 623 @param[in] v CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE 624 if using @ref CEED_EVAL_WEIGHT in the QFunction 625 626 @return An error code: 0 - success, otherwise - failure 627 628 @ref User 629 **/ 630 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) { 631 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator."); 632 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 633 CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name); 634 CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name); 635 CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name); 636 637 CeedInt num_elem = 0; 638 CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem)); 639 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION, 640 "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem); 641 642 CeedInt num_qpts = 0; 643 CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts)); 644 CeedCheck(b == CEED_BASIS_COLLOCATED || !op->num_qpts || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION, 645 "Basis with %" CeedInt_FMT " quadrature points incompatible with prior %" CeedInt_FMT " points", num_qpts, op->num_qpts); 646 CeedQFunctionField qf_field; 647 CeedOperatorField *op_field; 648 bool is_input = true; 649 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 650 if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) { 651 qf_field = op->qf->input_fields[i]; 652 op_field = &op->input_fields[i]; 653 goto found; 654 } 655 } 656 is_input = false; 657 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 658 if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) { 659 qf_field = op->qf->output_fields[i]; 660 op_field = &op->output_fields[i]; 661 goto found; 662 } 663 } 664 // LCOV_EXCL_START 665 return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name); 666 // LCOV_EXCL_STOP 667 found: 668 CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b)); 669 CeedCall(CeedCalloc(1, op_field)); 670 671 if (v == CEED_VECTOR_ACTIVE) { 672 CeedSize l_size; 673 CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size)); 674 if (is_input) { 675 if (op->input_size == -1) op->input_size = l_size; 676 CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 677 op->input_size); 678 } else { 679 if (op->output_size == -1) op->output_size = l_size; 680 CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 681 op->output_size); 682 } 683 } 684 685 CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec)); 686 CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr)); 687 if (r != CEED_ELEMRESTRICTION_NONE) { 688 op->num_elem = num_elem; 689 op->has_restriction = true; // Restriction set, but num_elem may be 0 690 } 691 CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis)); 692 if (!op->num_qpts && b != CEED_BASIS_COLLOCATED) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts)); 693 694 op->num_fields += 1; 695 CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name)); 696 return CEED_ERROR_SUCCESS; 697 } 698 699 /** 700 @brief Get the CeedOperatorFields of a CeedOperator 701 702 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 703 704 @param[in] op CeedOperator 705 @param[out] num_input_fields Variable to store number of input fields 706 @param[out] input_fields Variable to store input_fields 707 @param[out] num_output_fields Variable to store number of output fields 708 @param[out] output_fields Variable to store output_fields 709 710 @return An error code: 0 - success, otherwise - failure 711 712 @ref Advanced 713 **/ 714 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields, 715 CeedOperatorField **output_fields) { 716 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 717 CeedCall(CeedOperatorCheckReady(op)); 718 719 if (num_input_fields) *num_input_fields = op->qf->num_input_fields; 720 if (input_fields) *input_fields = op->input_fields; 721 if (num_output_fields) *num_output_fields = op->qf->num_output_fields; 722 if (output_fields) *output_fields = op->output_fields; 723 return CEED_ERROR_SUCCESS; 724 } 725 726 /** 727 @brief Get a CeedOperatorField of an CeedOperator from its name 728 729 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 730 731 @param[in] op CeedOperator 732 @param[in] field_name Name of desired CeedOperatorField 733 @param[out] op_field CeedOperatorField corresponding to the name 734 735 @return An error code: 0 - success, otherwise - failure 736 737 @ref Advanced 738 **/ 739 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) { 740 CeedInt num_input_fields, num_output_fields; 741 CeedOperatorField *input_fields, *output_fields; 742 char *name; 743 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 744 745 for (CeedInt i = 0; i < num_input_fields; i++) { 746 CeedCall(CeedOperatorFieldGetName(input_fields[i], &name)); 747 if (!strcmp(name, field_name)) { 748 *op_field = input_fields[i]; 749 return CEED_ERROR_SUCCESS; 750 } 751 } 752 753 for (CeedInt i = 0; i < num_output_fields; i++) { 754 CeedCall(CeedOperatorFieldGetName(output_fields[i], &name)); 755 if (!strcmp(name, field_name)) { 756 *op_field = output_fields[i]; 757 return CEED_ERROR_SUCCESS; 758 } 759 } 760 761 bool has_name = op->name; 762 // LCOV_EXCL_START 763 return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "", 764 has_name ? op->name : "", has_name ? "\"" : ""); 765 // LCOV_EXCL_STOP 766 } 767 768 /** 769 @brief Get the name of a CeedOperatorField 770 771 @param[in] op_field CeedOperatorField 772 @param[out] field_name Variable to store the field name 773 774 @return An error code: 0 - success, otherwise - failure 775 776 @ref Advanced 777 **/ 778 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) { 779 *field_name = (char *)op_field->field_name; 780 return CEED_ERROR_SUCCESS; 781 } 782 783 /** 784 @brief Get the CeedElemRestriction of a CeedOperatorField 785 786 @param[in] op_field CeedOperatorField 787 @param[out] rstr Variable to store CeedElemRestriction 788 789 @return An error code: 0 - success, otherwise - failure 790 791 @ref Advanced 792 **/ 793 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) { 794 *rstr = op_field->elem_rstr; 795 return CEED_ERROR_SUCCESS; 796 } 797 798 /** 799 @brief Get the CeedBasis of a CeedOperatorField 800 801 @param[in] op_field CeedOperatorField 802 @param[out] basis Variable to store CeedBasis 803 804 @return An error code: 0 - success, otherwise - failure 805 806 @ref Advanced 807 **/ 808 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) { 809 *basis = op_field->basis; 810 return CEED_ERROR_SUCCESS; 811 } 812 813 /** 814 @brief Get the CeedVector of a CeedOperatorField 815 816 @param[in] op_field CeedOperatorField 817 @param[out] vec Variable to store CeedVector 818 819 @return An error code: 0 - success, otherwise - failure 820 821 @ref Advanced 822 **/ 823 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) { 824 *vec = op_field->vec; 825 return CEED_ERROR_SUCCESS; 826 } 827 828 /** 829 @brief Add a sub-operator to a composite CeedOperator 830 831 @param[in,out] composite_op Composite CeedOperator 832 @param[in] sub_op Sub-operator CeedOperator 833 834 @return An error code: 0 - success, otherwise - failure 835 836 @ref User 837 */ 838 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) { 839 CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator"); 840 CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators"); 841 CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 842 843 { 844 CeedSize input_size, output_size; 845 CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size)); 846 if (composite_op->input_size == -1) composite_op->input_size = input_size; 847 if (composite_op->output_size == -1) composite_op->output_size = output_size; 848 // Note, a size of -1 means no active vector restriction set, so no incompatibility 849 CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size), 850 composite_op->ceed, CEED_ERROR_MAJOR, 851 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 852 "shape (%td, %td)", 853 composite_op->input_size, composite_op->output_size, input_size, output_size); 854 } 855 856 composite_op->sub_operators[composite_op->num_suboperators] = sub_op; 857 CeedCall(CeedOperatorReference(sub_op)); 858 composite_op->num_suboperators++; 859 return CEED_ERROR_SUCCESS; 860 } 861 862 /** 863 @brief Get the number of sub_operators associated with a CeedOperator 864 865 @param[in] op CeedOperator 866 @param[out] num_suboperators Variable to store number of sub_operators 867 868 @return An error code: 0 - success, otherwise - failure 869 870 @ref Backend 871 **/ 872 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) { 873 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 874 *num_suboperators = op->num_suboperators; 875 return CEED_ERROR_SUCCESS; 876 } 877 878 /** 879 @brief Get the list of sub_operators associated with a CeedOperator 880 881 @param op CeedOperator 882 @param[out] sub_operators Variable to store list of sub_operators 883 884 @return An error code: 0 - success, otherwise - failure 885 886 @ref Backend 887 **/ 888 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) { 889 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 890 *sub_operators = op->sub_operators; 891 return CEED_ERROR_SUCCESS; 892 } 893 894 /** 895 @brief Check if a CeedOperator is ready to be used. 896 897 @param[in] op CeedOperator to check 898 899 @return An error code: 0 - success, otherwise - failure 900 901 @ref User 902 **/ 903 int CeedOperatorCheckReady(CeedOperator op) { 904 Ceed ceed; 905 CeedCall(CeedOperatorGetCeed(op, &ceed)); 906 907 if (op->is_interface_setup) return CEED_ERROR_SUCCESS; 908 909 CeedQFunction qf = op->qf; 910 if (op->is_composite) { 911 if (!op->num_suboperators) { 912 // Empty operator setup 913 op->input_size = 0; 914 op->output_size = 0; 915 } else { 916 for (CeedInt i = 0; i < op->num_suboperators; i++) { 917 CeedCall(CeedOperatorCheckReady(op->sub_operators[i])); 918 } 919 // Sub-operators could be modified after adding to composite operator 920 // Need to verify no lvec incompatibility from any changes 921 CeedSize input_size, output_size; 922 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 923 } 924 } else { 925 CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set"); 926 CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set"); 927 CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required"); 928 CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE, 929 "At least one non-collocated basis is required or the number of quadrature points must be set"); 930 } 931 932 // Flag as immutable and ready 933 op->is_interface_setup = true; 934 if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true; 935 if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true; 936 if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true; 937 return CEED_ERROR_SUCCESS; 938 } 939 940 /** 941 @brief Get vector lengths for the active input and/or output vectors of a CeedOperator. 942 943 Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output. 944 945 @param[in] op CeedOperator 946 @param[out] input_size Variable to store active input vector length, or NULL 947 @param[out] output_size Variable to store active output vector length, or NULL 948 949 @return An error code: 0 - success, otherwise - failure 950 951 @ref User 952 **/ 953 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) { 954 bool is_composite; 955 956 if (input_size) *input_size = op->input_size; 957 if (output_size) *output_size = op->output_size; 958 959 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 960 if (is_composite && (op->input_size == -1 || op->output_size == -1)) { 961 for (CeedInt i = 0; i < op->num_suboperators; i++) { 962 CeedSize sub_input_size, sub_output_size; 963 CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size)); 964 if (op->input_size == -1) op->input_size = sub_input_size; 965 if (op->output_size == -1) op->output_size = sub_output_size; 966 // Note, a size of -1 means no active vector restriction set, so no incompatibility 967 CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed, 968 CEED_ERROR_MAJOR, 969 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 970 "shape (%td, %td)", 971 op->input_size, op->output_size, input_size, output_size); 972 } 973 } 974 975 return CEED_ERROR_SUCCESS; 976 } 977 978 /** 979 @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions. 980 981 When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a 982 `CeedOperatorLinearAssemble*` function is called. 983 When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to 984 `CeedOperatorSetQFunctionAssemblyDataUpdated`. 985 986 @param[in] op CeedOperator 987 @param[in] reuse_assembly_data Boolean flag setting assembly data reuse 988 989 @return An error code: 0 - success, otherwise - failure 990 991 @ref Advanced 992 **/ 993 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) { 994 bool is_composite; 995 996 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 997 if (is_composite) { 998 for (CeedInt i = 0; i < op->num_suboperators; i++) { 999 CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data)); 1000 } 1001 } else { 1002 CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data)); 1003 } 1004 1005 return CEED_ERROR_SUCCESS; 1006 } 1007 1008 /** 1009 @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly. 1010 1011 @param[in] op CeedOperator 1012 @param[in] needs_data_update Boolean flag setting assembly data reuse 1013 1014 @return An error code: 0 - success, otherwise - failure 1015 1016 @ref Advanced 1017 **/ 1018 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) { 1019 bool is_composite; 1020 1021 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1022 if (is_composite) { 1023 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1024 CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update)); 1025 } 1026 } else { 1027 CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update)); 1028 } 1029 1030 return CEED_ERROR_SUCCESS; 1031 } 1032 1033 /** 1034 @brief Set the number of quadrature points associated with a CeedOperator. 1035 1036 This should be used when creating a CeedOperator where every field has a collocated basis. 1037 This function cannot be used for composite CeedOperators. 1038 1039 @param[in,out] op CeedOperator 1040 @param[in] num_qpts Number of quadrature points to set 1041 1042 @return An error code: 0 - success, otherwise - failure 1043 1044 @ref Advanced 1045 **/ 1046 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) { 1047 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1048 CeedCheck(op->num_qpts == 0, op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined"); 1049 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 1050 op->num_qpts = num_qpts; 1051 return CEED_ERROR_SUCCESS; 1052 } 1053 1054 /** 1055 @brief Set name of CeedOperator for CeedOperatorView output 1056 1057 @param[in,out] op CeedOperator 1058 @param[in] name Name to set, or NULL to remove previously set name 1059 1060 @return An error code: 0 - success, otherwise - failure 1061 1062 @ref User 1063 **/ 1064 int CeedOperatorSetName(CeedOperator op, const char *name) { 1065 char *name_copy; 1066 size_t name_len = name ? strlen(name) : 0; 1067 1068 CeedCall(CeedFree(&op->name)); 1069 if (name_len > 0) { 1070 CeedCall(CeedCalloc(name_len + 1, &name_copy)); 1071 memcpy(name_copy, name, name_len); 1072 op->name = name_copy; 1073 } 1074 1075 return CEED_ERROR_SUCCESS; 1076 } 1077 1078 /** 1079 @brief View a CeedOperator 1080 1081 @param[in] op CeedOperator to view 1082 @param[in] stream Stream to write; typically stdout/stderr or a file 1083 1084 @return Error code: 0 - success, otherwise - failure 1085 1086 @ref User 1087 **/ 1088 int CeedOperatorView(CeedOperator op, FILE *stream) { 1089 bool has_name = op->name; 1090 1091 if (op->is_composite) { 1092 fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1093 1094 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1095 has_name = op->sub_operators[i]->name; 1096 fprintf(stream, " SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : ""); 1097 CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream)); 1098 } 1099 } else { 1100 fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1101 CeedCall(CeedOperatorSingleView(op, 0, stream)); 1102 } 1103 return CEED_ERROR_SUCCESS; 1104 } 1105 1106 /** 1107 @brief Get the Ceed associated with a CeedOperator 1108 1109 @param[in] op CeedOperator 1110 @param[out] ceed Variable to store Ceed 1111 1112 @return An error code: 0 - success, otherwise - failure 1113 1114 @ref Advanced 1115 **/ 1116 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) { 1117 *ceed = op->ceed; 1118 return CEED_ERROR_SUCCESS; 1119 } 1120 1121 /** 1122 @brief Get the number of elements associated with a CeedOperator 1123 1124 @param[in] op CeedOperator 1125 @param[out] num_elem Variable to store number of elements 1126 1127 @return An error code: 0 - success, otherwise - failure 1128 1129 @ref Advanced 1130 **/ 1131 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) { 1132 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1133 *num_elem = op->num_elem; 1134 return CEED_ERROR_SUCCESS; 1135 } 1136 1137 /** 1138 @brief Get the number of quadrature points associated with a CeedOperator 1139 1140 @param[in] op CeedOperator 1141 @param[out] num_qpts Variable to store vector number of quadrature points 1142 1143 @return An error code: 0 - success, otherwise - failure 1144 1145 @ref Advanced 1146 **/ 1147 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) { 1148 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1149 *num_qpts = op->num_qpts; 1150 return CEED_ERROR_SUCCESS; 1151 } 1152 1153 /** 1154 @brief Estimate number of FLOPs required to apply CeedOperator on the active vector 1155 1156 @param[in] op CeedOperator to estimate FLOPs for 1157 @param[out] flops Address of variable to hold FLOPs estimate 1158 1159 @ref Backend 1160 **/ 1161 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) { 1162 bool is_composite; 1163 CeedCall(CeedOperatorCheckReady(op)); 1164 1165 *flops = 0; 1166 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1167 if (is_composite) { 1168 CeedInt num_suboperators; 1169 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1170 CeedOperator *sub_operators; 1171 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1172 1173 // FLOPs for each suboperator 1174 for (CeedInt i = 0; i < num_suboperators; i++) { 1175 CeedSize suboperator_flops; 1176 CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops)); 1177 *flops += suboperator_flops; 1178 } 1179 } else { 1180 CeedInt num_input_fields, num_output_fields; 1181 CeedOperatorField *input_fields, *output_fields; 1182 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 1183 CeedInt num_elem = 0; 1184 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1185 1186 // Input FLOPs 1187 for (CeedInt i = 0; i < num_input_fields; i++) { 1188 if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1189 CeedSize restr_flops, basis_flops; 1190 1191 CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops)); 1192 *flops += restr_flops; 1193 CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops)); 1194 *flops += basis_flops * num_elem; 1195 } 1196 } 1197 // QF FLOPs 1198 CeedInt num_qpts; 1199 CeedSize qf_flops; 1200 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 1201 CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops)); 1202 *flops += num_elem * num_qpts * qf_flops; 1203 // Output FLOPs 1204 for (CeedInt i = 0; i < num_output_fields; i++) { 1205 if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1206 CeedSize restr_flops, basis_flops; 1207 1208 CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops)); 1209 *flops += restr_flops; 1210 CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops)); 1211 *flops += basis_flops * num_elem; 1212 } 1213 } 1214 } 1215 1216 return CEED_ERROR_SUCCESS; 1217 } 1218 1219 /** 1220 @brief Get CeedQFunction global context for a CeedOperator. 1221 1222 The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`. 1223 1224 Note: If the value of `ctx` passed into this function is non-NULL, then it is assumed that `ctx` is a pointer to a CeedQFunctionContext. 1225 This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext. 1226 1227 @param[in] op CeedOperator 1228 @param[out] ctx Variable to store CeedQFunctionContext 1229 1230 @return An error code: 0 - success, otherwise - failure 1231 1232 @ref Advanced 1233 **/ 1234 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) { 1235 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator"); 1236 if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx)); 1237 else *ctx = NULL; 1238 return CEED_ERROR_SUCCESS; 1239 } 1240 1241 /** 1242 @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`. 1243 1244 Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`). 1245 1246 @param[in] op CeedOperator 1247 @param[in] field_name Name of field to retrieve label 1248 @param[out] field_label Variable to field label 1249 1250 @return An error code: 0 - success, otherwise - failure 1251 1252 @ref User 1253 **/ 1254 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) { 1255 bool is_composite; 1256 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1257 1258 if (is_composite) { 1259 // Check if composite label already created 1260 for (CeedInt i = 0; i < op->num_context_labels; i++) { 1261 if (!strcmp(op->context_labels[i]->name, field_name)) { 1262 *field_label = op->context_labels[i]; 1263 return CEED_ERROR_SUCCESS; 1264 } 1265 } 1266 1267 // Create composite label if needed 1268 CeedInt num_sub; 1269 CeedOperator *sub_operators; 1270 CeedContextFieldLabel new_field_label; 1271 1272 CeedCall(CeedCalloc(1, &new_field_label)); 1273 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 1274 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1275 CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels)); 1276 new_field_label->num_sub_labels = num_sub; 1277 1278 bool label_found = false; 1279 for (CeedInt i = 0; i < num_sub; i++) { 1280 if (sub_operators[i]->qf->ctx) { 1281 CeedContextFieldLabel new_field_label_i; 1282 CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i)); 1283 if (new_field_label_i) { 1284 label_found = true; 1285 new_field_label->sub_labels[i] = new_field_label_i; 1286 new_field_label->name = new_field_label_i->name; 1287 new_field_label->description = new_field_label_i->description; 1288 if (new_field_label->type && new_field_label->type != new_field_label_i->type) { 1289 // LCOV_EXCL_START 1290 CeedCall(CeedFree(&new_field_label)); 1291 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s", 1292 CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]); 1293 // LCOV_EXCL_STOP 1294 } else { 1295 new_field_label->type = new_field_label_i->type; 1296 } 1297 if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) { 1298 // LCOV_EXCL_START 1299 CeedCall(CeedFree(&new_field_label)); 1300 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld", 1301 new_field_label->num_values, new_field_label_i->num_values); 1302 // LCOV_EXCL_STOP 1303 } else { 1304 new_field_label->num_values = new_field_label_i->num_values; 1305 } 1306 } 1307 } 1308 } 1309 if (!label_found) { 1310 // LCOV_EXCL_START 1311 CeedCall(CeedFree(&new_field_label->sub_labels)); 1312 CeedCall(CeedFree(&new_field_label)); 1313 *field_label = NULL; 1314 // LCOV_EXCL_STOP 1315 } else { 1316 *field_label = new_field_label; 1317 } 1318 } else { 1319 CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label)); 1320 } 1321 1322 // Set label in operator 1323 if (*field_label) { 1324 (*field_label)->from_op = true; 1325 1326 // Move new composite label to operator 1327 if (op->num_context_labels == 0) { 1328 CeedCall(CeedCalloc(1, &op->context_labels)); 1329 op->max_context_labels = 1; 1330 } else if (op->num_context_labels == op->max_context_labels) { 1331 CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels)); 1332 op->max_context_labels *= 2; 1333 } 1334 op->context_labels[op->num_context_labels] = *field_label; 1335 op->num_context_labels++; 1336 } 1337 1338 return CEED_ERROR_SUCCESS; 1339 } 1340 1341 /** 1342 @brief Set QFunctionContext field holding double precision values. 1343 1344 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1345 1346 @param[in,out] op CeedOperator 1347 @param[in] field_label Label of field to set 1348 @param[in] values Values to set 1349 1350 @return An error code: 0 - success, otherwise - failure 1351 1352 @ref User 1353 **/ 1354 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) { 1355 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1356 } 1357 1358 /** 1359 @brief Get QFunctionContext field holding double precision values, read-only. 1360 1361 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1362 1363 @param[in] op CeedOperator 1364 @param[in] field_label Label of field to get 1365 @param[out] num_values Number of values in the field label 1366 @param[out] values Pointer to context values 1367 1368 @return An error code: 0 - success, otherwise - failure 1369 1370 @ref User 1371 **/ 1372 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) { 1373 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values); 1374 } 1375 1376 /** 1377 @brief Restore QFunctionContext field holding double precision values, read-only. 1378 1379 @param[in] op CeedOperator 1380 @param[in] field_label Label of field to restore 1381 @param[out] values Pointer to context values 1382 1383 @return An error code: 0 - success, otherwise - failure 1384 1385 @ref User 1386 **/ 1387 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) { 1388 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1389 } 1390 1391 /** 1392 @brief Set QFunctionContext field holding int32 values. 1393 1394 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1395 1396 @param[in,out] op CeedOperator 1397 @param[in] field_label Label of field to set 1398 @param[in] values Values to set 1399 1400 @return An error code: 0 - success, otherwise - failure 1401 1402 @ref User 1403 **/ 1404 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) { 1405 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1406 } 1407 1408 /** 1409 @brief Get QFunctionContext field holding int32 values, read-only. 1410 1411 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1412 1413 @param[in] op CeedOperator 1414 @param[in] field_label Label of field to get 1415 @param[out] num_values Number of int32 values in `values` 1416 @param[out] values Pointer to context values 1417 1418 @return An error code: 0 - success, otherwise - failure 1419 1420 @ref User 1421 **/ 1422 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) { 1423 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values); 1424 } 1425 1426 /** 1427 @brief Restore QFunctionContext field holding int32 values, read-only. 1428 1429 @param[in] op CeedOperator 1430 @param[in] field_label Label of field to get 1431 @param[out] values Pointer to context values 1432 1433 @return An error code: 0 - success, otherwise - failure 1434 1435 @ref User 1436 **/ 1437 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) { 1438 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1439 } 1440 1441 /** 1442 @brief Apply CeedOperator to a vector 1443 1444 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1445 All inputs and outputs must be specified using CeedOperatorSetField(). 1446 1447 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1448 1449 @param[in] op CeedOperator to apply 1450 @param[in] in CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs 1451 @param[out] out CeedVector to store result of applying operator (must be distinct from @a in) or @ref CEED_VECTOR_NONE if there are no 1452 active outputs 1453 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1454 1455 @return An error code: 0 - success, otherwise - failure 1456 1457 @ref User 1458 **/ 1459 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1460 CeedCall(CeedOperatorCheckReady(op)); 1461 1462 if (op->is_composite) { 1463 // Composite Operator 1464 if (op->ApplyComposite) { 1465 CeedCall(op->ApplyComposite(op, in, out, request)); 1466 } else { 1467 CeedInt num_suboperators; 1468 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1469 CeedOperator *sub_operators; 1470 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1471 1472 // Zero all output vectors 1473 if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0)); 1474 for (CeedInt i = 0; i < num_suboperators; i++) { 1475 for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) { 1476 CeedVector vec = sub_operators[i]->output_fields[j]->vec; 1477 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) { 1478 CeedCall(CeedVectorSetValue(vec, 0.0)); 1479 } 1480 } 1481 } 1482 // Apply 1483 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1484 CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request)); 1485 } 1486 } 1487 } else { 1488 // Standard Operator 1489 if (op->Apply) { 1490 CeedCall(op->Apply(op, in, out, request)); 1491 } else { 1492 // Zero all output vectors 1493 CeedQFunction qf = op->qf; 1494 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 1495 CeedVector vec = op->output_fields[i]->vec; 1496 if (vec == CEED_VECTOR_ACTIVE) vec = out; 1497 if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0)); 1498 } 1499 // Apply 1500 if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request)); 1501 } 1502 } 1503 return CEED_ERROR_SUCCESS; 1504 } 1505 1506 /** 1507 @brief Apply CeedOperator to a vector and add result to output vector 1508 1509 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1510 All inputs and outputs must be specified using CeedOperatorSetField(). 1511 1512 @param[in] op CeedOperator to apply 1513 @param[in] in CeedVector containing input state or NULL if there are no active inputs 1514 @param[out] out CeedVector to sum in result of applying operator (must be distinct from @a in) or NULL if there are no active outputs 1515 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1516 1517 @return An error code: 0 - success, otherwise - failure 1518 1519 @ref User 1520 **/ 1521 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1522 CeedCall(CeedOperatorCheckReady(op)); 1523 1524 if (op->is_composite) { 1525 // Composite Operator 1526 if (op->ApplyAddComposite) { 1527 CeedCall(op->ApplyAddComposite(op, in, out, request)); 1528 } else { 1529 CeedInt num_suboperators; 1530 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1531 CeedOperator *sub_operators; 1532 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1533 1534 for (CeedInt i = 0; i < num_suboperators; i++) { 1535 CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request)); 1536 } 1537 } 1538 } else if (op->num_elem) { 1539 // Standard Operator 1540 CeedCall(op->ApplyAdd(op, in, out, request)); 1541 } 1542 return CEED_ERROR_SUCCESS; 1543 } 1544 1545 /** 1546 @brief Destroy a CeedOperator 1547 1548 @param[in,out] op CeedOperator to destroy 1549 1550 @return An error code: 0 - success, otherwise - failure 1551 1552 @ref User 1553 **/ 1554 int CeedOperatorDestroy(CeedOperator *op) { 1555 if (!*op || --(*op)->ref_count > 0) { 1556 *op = NULL; 1557 return CEED_ERROR_SUCCESS; 1558 } 1559 if ((*op)->Destroy) CeedCall((*op)->Destroy(*op)); 1560 CeedCall(CeedDestroy(&(*op)->ceed)); 1561 // Free fields 1562 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1563 if ((*op)->input_fields[i]) { 1564 if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) { 1565 CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr)); 1566 } 1567 if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1568 CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis)); 1569 } 1570 if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) { 1571 CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec)); 1572 } 1573 CeedCall(CeedFree(&(*op)->input_fields[i]->field_name)); 1574 CeedCall(CeedFree(&(*op)->input_fields[i])); 1575 } 1576 } 1577 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1578 if ((*op)->output_fields[i]) { 1579 CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr)); 1580 if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1581 CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis)); 1582 } 1583 if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) { 1584 CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec)); 1585 } 1586 CeedCall(CeedFree(&(*op)->output_fields[i]->field_name)); 1587 CeedCall(CeedFree(&(*op)->output_fields[i])); 1588 } 1589 } 1590 // Destroy sub_operators 1591 for (CeedInt i = 0; i < (*op)->num_suboperators; i++) { 1592 if ((*op)->sub_operators[i]) { 1593 CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i])); 1594 } 1595 } 1596 CeedCall(CeedQFunctionDestroy(&(*op)->qf)); 1597 CeedCall(CeedQFunctionDestroy(&(*op)->dqf)); 1598 CeedCall(CeedQFunctionDestroy(&(*op)->dqfT)); 1599 // Destroy any composite labels 1600 if ((*op)->is_composite) { 1601 for (CeedInt i = 0; i < (*op)->num_context_labels; i++) { 1602 CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels)); 1603 CeedCall(CeedFree(&(*op)->context_labels[i])); 1604 } 1605 } 1606 CeedCall(CeedFree(&(*op)->context_labels)); 1607 1608 // Destroy fallback 1609 CeedCall(CeedOperatorDestroy(&(*op)->op_fallback)); 1610 1611 // Destroy assembly data 1612 CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled)); 1613 CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled)); 1614 1615 CeedCall(CeedFree(&(*op)->input_fields)); 1616 CeedCall(CeedFree(&(*op)->output_fields)); 1617 CeedCall(CeedFree(&(*op)->sub_operators)); 1618 CeedCall(CeedFree(&(*op)->name)); 1619 CeedCall(CeedFree(op)); 1620 return CEED_ERROR_SUCCESS; 1621 } 1622 1623 /// @} 1624