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 The number of quadrature points must agree across all points. 620 When using @ref CEED_BASIS_COLLOCATED, the number of quadrature points is determined by the element size of r. 621 622 @param[in,out] op CeedOperator on which to provide the field 623 @param[in] field_name Name of the field (to be matched with the name used by CeedQFunction) 624 @param[in] r CeedElemRestriction 625 @param[in] b CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED if collocated with quadrature points 626 @param[in] v CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE if field is active or @ref CEED_VECTOR_NONE 627 if using @ref CEED_EVAL_WEIGHT in the QFunction 628 629 @return An error code: 0 - success, otherwise - failure 630 631 @ref User 632 **/ 633 int CeedOperatorSetField(CeedOperator op, const char *field_name, CeedElemRestriction r, CeedBasis b, CeedVector v) { 634 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot add field to composite operator."); 635 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 636 CeedCheck(r, op->ceed, CEED_ERROR_INCOMPATIBLE, "ElemRestriction r for field \"%s\" must be non-NULL.", field_name); 637 CeedCheck(b, op->ceed, CEED_ERROR_INCOMPATIBLE, "Basis b for field \"%s\" must be non-NULL.", field_name); 638 CeedCheck(v, op->ceed, CEED_ERROR_INCOMPATIBLE, "Vector v for field \"%s\" must be non-NULL.", field_name); 639 640 CeedInt num_elem = 0; 641 CeedCall(CeedElemRestrictionGetNumElements(r, &num_elem)); 642 CeedCheck(r == CEED_ELEMRESTRICTION_NONE || !op->has_restriction || op->num_elem == num_elem, op->ceed, CEED_ERROR_DIMENSION, 643 "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %" CeedInt_FMT " elements", num_elem, op->num_elem); 644 645 CeedInt num_qpts = 0; 646 if (b == CEED_BASIS_COLLOCATED) CeedCall(CeedElemRestrictionGetElementSize(r, &num_qpts)); 647 else CeedCall(CeedBasisGetNumQuadraturePoints(b, &num_qpts)); 648 CeedCheck(op->num_qpts == 0 || op->num_qpts == num_qpts, op->ceed, CEED_ERROR_DIMENSION, 649 "%s must correspond to the same number of quadrature points as previously added Bases. Found %" CeedInt_FMT 650 " quadrature points but expected %" CeedInt_FMT " quadrature points.", 651 b == CEED_BASIS_COLLOCATED ? "ElemRestriction" : "Basis", num_qpts, op->num_qpts); 652 CeedQFunctionField qf_field; 653 CeedOperatorField *op_field; 654 bool is_input = true; 655 for (CeedInt i = 0; i < op->qf->num_input_fields; i++) { 656 if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) { 657 qf_field = op->qf->input_fields[i]; 658 op_field = &op->input_fields[i]; 659 goto found; 660 } 661 } 662 is_input = false; 663 for (CeedInt i = 0; i < op->qf->num_output_fields; i++) { 664 if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) { 665 qf_field = op->qf->output_fields[i]; 666 op_field = &op->output_fields[i]; 667 goto found; 668 } 669 } 670 // LCOV_EXCL_START 671 return CeedError(op->ceed, CEED_ERROR_INCOMPLETE, "QFunction has no knowledge of field '%s'", field_name); 672 // LCOV_EXCL_STOP 673 found: 674 CeedCall(CeedOperatorCheckField(op->ceed, qf_field, r, b)); 675 CeedCall(CeedCalloc(1, op_field)); 676 677 if (v == CEED_VECTOR_ACTIVE) { 678 CeedSize l_size; 679 CeedCall(CeedElemRestrictionGetLVectorSize(r, &l_size)); 680 if (is_input) { 681 if (op->input_size == -1) op->input_size = l_size; 682 CeedCheck(l_size == op->input_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 683 op->input_size); 684 } else { 685 if (op->output_size == -1) op->output_size = l_size; 686 CeedCheck(l_size == op->output_size, op->ceed, CEED_ERROR_INCOMPATIBLE, "LVector size %td does not match previous size %td", l_size, 687 op->output_size); 688 } 689 } 690 691 CeedCall(CeedVectorReferenceCopy(v, &(*op_field)->vec)); 692 CeedCall(CeedElemRestrictionReferenceCopy(r, &(*op_field)->elem_rstr)); 693 if (r != CEED_ELEMRESTRICTION_NONE) { 694 op->num_elem = num_elem; 695 op->has_restriction = true; // Restriction set, but num_elem may be 0 696 } 697 CeedCall(CeedBasisReferenceCopy(b, &(*op_field)->basis)); 698 if (op->num_qpts == 0) CeedCall(CeedOperatorSetNumQuadraturePoints(op, num_qpts)); 699 700 op->num_fields += 1; 701 CeedCall(CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name)); 702 return CEED_ERROR_SUCCESS; 703 } 704 705 /** 706 @brief Get the CeedOperatorFields of a CeedOperator 707 708 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 709 710 @param[in] op CeedOperator 711 @param[out] num_input_fields Variable to store number of input fields 712 @param[out] input_fields Variable to store input_fields 713 @param[out] num_output_fields Variable to store number of output fields 714 @param[out] output_fields Variable to store output_fields 715 716 @return An error code: 0 - success, otherwise - failure 717 718 @ref Advanced 719 **/ 720 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields, CeedOperatorField **input_fields, CeedInt *num_output_fields, 721 CeedOperatorField **output_fields) { 722 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 723 CeedCall(CeedOperatorCheckReady(op)); 724 725 if (num_input_fields) *num_input_fields = op->qf->num_input_fields; 726 if (input_fields) *input_fields = op->input_fields; 727 if (num_output_fields) *num_output_fields = op->qf->num_output_fields; 728 if (output_fields) *output_fields = op->output_fields; 729 return CEED_ERROR_SUCCESS; 730 } 731 732 /** 733 @brief Get a CeedOperatorField of an CeedOperator from its name 734 735 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 736 737 @param[in] op CeedOperator 738 @param[in] field_name Name of desired CeedOperatorField 739 @param[out] op_field CeedOperatorField corresponding to the name 740 741 @return An error code: 0 - success, otherwise - failure 742 743 @ref Advanced 744 **/ 745 int CeedOperatorGetFieldByName(CeedOperator op, const char *field_name, CeedOperatorField *op_field) { 746 CeedInt num_input_fields, num_output_fields; 747 CeedOperatorField *input_fields, *output_fields; 748 char *name; 749 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 750 751 for (CeedInt i = 0; i < num_input_fields; i++) { 752 CeedCall(CeedOperatorFieldGetName(input_fields[i], &name)); 753 if (!strcmp(name, field_name)) { 754 *op_field = input_fields[i]; 755 return CEED_ERROR_SUCCESS; 756 } 757 } 758 759 for (CeedInt i = 0; i < num_output_fields; i++) { 760 CeedCall(CeedOperatorFieldGetName(output_fields[i], &name)); 761 if (!strcmp(name, field_name)) { 762 *op_field = output_fields[i]; 763 return CEED_ERROR_SUCCESS; 764 } 765 } 766 767 bool has_name = op->name; 768 // LCOV_EXCL_START 769 return CeedError(op->ceed, CEED_ERROR_MINOR, "The field \"%s\" not found in CeedOperator%s%s%s.\n", field_name, has_name ? " \"" : "", 770 has_name ? op->name : "", has_name ? "\"" : ""); 771 // LCOV_EXCL_STOP 772 } 773 774 /** 775 @brief Get the name of a CeedOperatorField 776 777 @param[in] op_field CeedOperatorField 778 @param[out] field_name Variable to store the field name 779 780 @return An error code: 0 - success, otherwise - failure 781 782 @ref Advanced 783 **/ 784 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) { 785 *field_name = (char *)op_field->field_name; 786 return CEED_ERROR_SUCCESS; 787 } 788 789 /** 790 @brief Get the CeedElemRestriction of a CeedOperatorField 791 792 @param[in] op_field CeedOperatorField 793 @param[out] rstr Variable to store CeedElemRestriction 794 795 @return An error code: 0 - success, otherwise - failure 796 797 @ref Advanced 798 **/ 799 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field, CeedElemRestriction *rstr) { 800 *rstr = op_field->elem_rstr; 801 return CEED_ERROR_SUCCESS; 802 } 803 804 /** 805 @brief Get the CeedBasis of a CeedOperatorField 806 807 @param[in] op_field CeedOperatorField 808 @param[out] basis Variable to store CeedBasis 809 810 @return An error code: 0 - success, otherwise - failure 811 812 @ref Advanced 813 **/ 814 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) { 815 *basis = op_field->basis; 816 return CEED_ERROR_SUCCESS; 817 } 818 819 /** 820 @brief Get the CeedVector of a CeedOperatorField 821 822 @param[in] op_field CeedOperatorField 823 @param[out] vec Variable to store CeedVector 824 825 @return An error code: 0 - success, otherwise - failure 826 827 @ref Advanced 828 **/ 829 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) { 830 *vec = op_field->vec; 831 return CEED_ERROR_SUCCESS; 832 } 833 834 /** 835 @brief Add a sub-operator to a composite CeedOperator 836 837 @param[in,out] composite_op Composite CeedOperator 838 @param[in] sub_op Sub-operator CeedOperator 839 840 @return An error code: 0 - success, otherwise - failure 841 842 @ref User 843 */ 844 int CeedCompositeOperatorAddSub(CeedOperator composite_op, CeedOperator sub_op) { 845 CeedCheck(composite_op->is_composite, composite_op->ceed, CEED_ERROR_MINOR, "CeedOperator is not a composite operator"); 846 CeedCheck(composite_op->num_suboperators < CEED_COMPOSITE_MAX, composite_op->ceed, CEED_ERROR_UNSUPPORTED, "Cannot add additional sub-operators"); 847 CeedCheck(!composite_op->is_immutable, composite_op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 848 849 { 850 CeedSize input_size, output_size; 851 CeedCall(CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size)); 852 if (composite_op->input_size == -1) composite_op->input_size = input_size; 853 if (composite_op->output_size == -1) composite_op->output_size = output_size; 854 // Note, a size of -1 means no active vector restriction set, so no incompatibility 855 CeedCheck((input_size == -1 || input_size == composite_op->input_size) && (output_size == -1 || output_size == composite_op->output_size), 856 composite_op->ceed, CEED_ERROR_MAJOR, 857 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 858 "shape (%td, %td)", 859 composite_op->input_size, composite_op->output_size, input_size, output_size); 860 } 861 862 composite_op->sub_operators[composite_op->num_suboperators] = sub_op; 863 CeedCall(CeedOperatorReference(sub_op)); 864 composite_op->num_suboperators++; 865 return CEED_ERROR_SUCCESS; 866 } 867 868 /** 869 @brief Get the number of sub_operators associated with a CeedOperator 870 871 @param[in] op CeedOperator 872 @param[out] num_suboperators Variable to store number of sub_operators 873 874 @return An error code: 0 - success, otherwise - failure 875 876 @ref Backend 877 **/ 878 int CeedCompositeOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) { 879 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 880 *num_suboperators = op->num_suboperators; 881 return CEED_ERROR_SUCCESS; 882 } 883 884 /** 885 @brief Get the list of sub_operators associated with a CeedOperator 886 887 @param op CeedOperator 888 @param[out] sub_operators Variable to store list of sub_operators 889 890 @return An error code: 0 - success, otherwise - failure 891 892 @ref Backend 893 **/ 894 int CeedCompositeOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) { 895 CeedCheck(op->is_composite, op->ceed, CEED_ERROR_MINOR, "Only defined for a composite operator"); 896 *sub_operators = op->sub_operators; 897 return CEED_ERROR_SUCCESS; 898 } 899 900 /** 901 @brief Check if a CeedOperator is ready to be used. 902 903 @param[in] op CeedOperator to check 904 905 @return An error code: 0 - success, otherwise - failure 906 907 @ref User 908 **/ 909 int CeedOperatorCheckReady(CeedOperator op) { 910 Ceed ceed; 911 CeedCall(CeedOperatorGetCeed(op, &ceed)); 912 913 if (op->is_interface_setup) return CEED_ERROR_SUCCESS; 914 915 CeedQFunction qf = op->qf; 916 if (op->is_composite) { 917 if (!op->num_suboperators) { 918 // Empty operator setup 919 op->input_size = 0; 920 op->output_size = 0; 921 } else { 922 for (CeedInt i = 0; i < op->num_suboperators; i++) { 923 CeedCall(CeedOperatorCheckReady(op->sub_operators[i])); 924 } 925 // Sub-operators could be modified after adding to composite operator 926 // Need to verify no lvec incompatibility from any changes 927 CeedSize input_size, output_size; 928 CeedCall(CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size)); 929 } 930 } else { 931 CeedCheck(op->num_fields > 0, ceed, CEED_ERROR_INCOMPLETE, "No operator fields set"); 932 CeedCheck(op->num_fields == qf->num_input_fields + qf->num_output_fields, ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set"); 933 CeedCheck(op->has_restriction, ceed, CEED_ERROR_INCOMPLETE, "At least one restriction required"); 934 CeedCheck(op->num_qpts > 0, ceed, CEED_ERROR_INCOMPLETE, 935 "At least one non-collocated basis is required or the number of quadrature points must be set"); 936 } 937 938 // Flag as immutable and ready 939 op->is_interface_setup = true; 940 if (op->qf && op->qf != CEED_QFUNCTION_NONE) op->qf->is_immutable = true; 941 if (op->dqf && op->dqf != CEED_QFUNCTION_NONE) op->dqf->is_immutable = true; 942 if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE) op->dqfT->is_immutable = true; 943 return CEED_ERROR_SUCCESS; 944 } 945 946 /** 947 @brief Get vector lengths for the active input and/or output vectors of a CeedOperator. 948 949 Note: Lengths of -1 indicate that the CeedOperator does not have an active input and/or output. 950 951 @param[in] op CeedOperator 952 @param[out] input_size Variable to store active input vector length, or NULL 953 @param[out] output_size Variable to store active output vector length, or NULL 954 955 @return An error code: 0 - success, otherwise - failure 956 957 @ref User 958 **/ 959 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size, CeedSize *output_size) { 960 bool is_composite; 961 962 if (input_size) *input_size = op->input_size; 963 if (output_size) *output_size = op->output_size; 964 965 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 966 if (is_composite && (op->input_size == -1 || op->output_size == -1)) { 967 for (CeedInt i = 0; i < op->num_suboperators; i++) { 968 CeedSize sub_input_size, sub_output_size; 969 CeedCall(CeedOperatorGetActiveVectorLengths(op->sub_operators[i], &sub_input_size, &sub_output_size)); 970 if (op->input_size == -1) op->input_size = sub_input_size; 971 if (op->output_size == -1) op->output_size = sub_output_size; 972 // Note, a size of -1 means no active vector restriction set, so no incompatibility 973 CeedCheck((sub_input_size == -1 || sub_input_size == op->input_size) && (sub_output_size == -1 || sub_output_size == op->output_size), op->ceed, 974 CEED_ERROR_MAJOR, 975 "Sub-operators must have compatible dimensions; composite operator of shape (%td, %td) not compatible with sub-operator of " 976 "shape (%td, %td)", 977 op->input_size, op->output_size, input_size, output_size); 978 } 979 } 980 981 return CEED_ERROR_SUCCESS; 982 } 983 984 /** 985 @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions. 986 987 When `reuse_assembly_data = false` (default), the CeedQFunction associated with this CeedOperator is re-assembled every time a 988 `CeedOperatorLinearAssemble*` function is called. 989 When `reuse_assembly_data = true`, the CeedQFunction associated with this CeedOperator is reused between calls to 990 `CeedOperatorSetQFunctionAssemblyDataUpdated`. 991 992 @param[in] op CeedOperator 993 @param[in] reuse_assembly_data Boolean flag setting assembly data reuse 994 995 @return An error code: 0 - success, otherwise - failure 996 997 @ref Advanced 998 **/ 999 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op, bool reuse_assembly_data) { 1000 bool is_composite; 1001 1002 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1003 if (is_composite) { 1004 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1005 CeedCall(CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i], reuse_assembly_data)); 1006 } 1007 } else { 1008 CeedCall(CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data)); 1009 } 1010 1011 return CEED_ERROR_SUCCESS; 1012 } 1013 1014 /** 1015 @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly. 1016 1017 @param[in] op CeedOperator 1018 @param[in] needs_data_update Boolean flag setting assembly data reuse 1019 1020 @return An error code: 0 - success, otherwise - failure 1021 1022 @ref Advanced 1023 **/ 1024 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op, bool needs_data_update) { 1025 bool is_composite; 1026 1027 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1028 if (is_composite) { 1029 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1030 CeedCall(CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i], needs_data_update)); 1031 } 1032 } else { 1033 CeedCall(CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled, needs_data_update)); 1034 } 1035 1036 return CEED_ERROR_SUCCESS; 1037 } 1038 1039 /** 1040 @brief Set the number of quadrature points associated with a CeedOperator. 1041 1042 This should be used when creating a CeedOperator where every field has a collocated basis. 1043 This function cannot be used for composite CeedOperators. 1044 1045 @param[in,out] op CeedOperator 1046 @param[in] num_qpts Number of quadrature points to set 1047 1048 @return An error code: 0 - success, otherwise - failure 1049 1050 @ref Advanced 1051 **/ 1052 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) { 1053 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite CeedOperator"); 1054 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 1055 if (op->num_qpts > 0) { 1056 CeedWarn( 1057 "CeedOperatorSetNumQuadraturePoints will be removed from the libCEED interface in the next release.\n" 1058 "This function is reduntant and you can safely remove any calls to this function without replacing them."); 1059 CeedCheck(num_qpts == op->num_qpts, op->ceed, CEED_ERROR_DIMENSION, "Different number of quadrature points already defined for the CeedOperator"); 1060 } 1061 op->num_qpts = num_qpts; 1062 return CEED_ERROR_SUCCESS; 1063 } 1064 1065 /** 1066 @brief Set name of CeedOperator for CeedOperatorView output 1067 1068 @param[in,out] op CeedOperator 1069 @param[in] name Name to set, or NULL to remove previously set name 1070 1071 @return An error code: 0 - success, otherwise - failure 1072 1073 @ref User 1074 **/ 1075 int CeedOperatorSetName(CeedOperator op, const char *name) { 1076 char *name_copy; 1077 size_t name_len = name ? strlen(name) : 0; 1078 1079 CeedCall(CeedFree(&op->name)); 1080 if (name_len > 0) { 1081 CeedCall(CeedCalloc(name_len + 1, &name_copy)); 1082 memcpy(name_copy, name, name_len); 1083 op->name = name_copy; 1084 } 1085 1086 return CEED_ERROR_SUCCESS; 1087 } 1088 1089 /** 1090 @brief View a CeedOperator 1091 1092 @param[in] op CeedOperator to view 1093 @param[in] stream Stream to write; typically stdout/stderr or a file 1094 1095 @return Error code: 0 - success, otherwise - failure 1096 1097 @ref User 1098 **/ 1099 int CeedOperatorView(CeedOperator op, FILE *stream) { 1100 bool has_name = op->name; 1101 1102 if (op->is_composite) { 1103 fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1104 1105 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1106 has_name = op->sub_operators[i]->name; 1107 fprintf(stream, " SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : ""); 1108 CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream)); 1109 } 1110 } else { 1111 fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1112 CeedCall(CeedOperatorSingleView(op, 0, stream)); 1113 } 1114 return CEED_ERROR_SUCCESS; 1115 } 1116 1117 /** 1118 @brief Get the Ceed associated with a CeedOperator 1119 1120 @param[in] op CeedOperator 1121 @param[out] ceed Variable to store Ceed 1122 1123 @return An error code: 0 - success, otherwise - failure 1124 1125 @ref Advanced 1126 **/ 1127 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) { 1128 *ceed = op->ceed; 1129 return CEED_ERROR_SUCCESS; 1130 } 1131 1132 /** 1133 @brief Get the number of elements associated with a CeedOperator 1134 1135 @param[in] op CeedOperator 1136 @param[out] num_elem Variable to store number of elements 1137 1138 @return An error code: 0 - success, otherwise - failure 1139 1140 @ref Advanced 1141 **/ 1142 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) { 1143 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1144 *num_elem = op->num_elem; 1145 return CEED_ERROR_SUCCESS; 1146 } 1147 1148 /** 1149 @brief Get the number of quadrature points associated with a CeedOperator 1150 1151 @param[in] op CeedOperator 1152 @param[out] num_qpts Variable to store vector number of quadrature points 1153 1154 @return An error code: 0 - success, otherwise - failure 1155 1156 @ref Advanced 1157 **/ 1158 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) { 1159 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1160 *num_qpts = op->num_qpts; 1161 return CEED_ERROR_SUCCESS; 1162 } 1163 1164 /** 1165 @brief Estimate number of FLOPs required to apply CeedOperator on the active vector 1166 1167 @param[in] op CeedOperator to estimate FLOPs for 1168 @param[out] flops Address of variable to hold FLOPs estimate 1169 1170 @ref Backend 1171 **/ 1172 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) { 1173 bool is_composite; 1174 CeedCall(CeedOperatorCheckReady(op)); 1175 1176 *flops = 0; 1177 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1178 if (is_composite) { 1179 CeedInt num_suboperators; 1180 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1181 CeedOperator *sub_operators; 1182 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1183 1184 // FLOPs for each suboperator 1185 for (CeedInt i = 0; i < num_suboperators; i++) { 1186 CeedSize suboperator_flops; 1187 CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops)); 1188 *flops += suboperator_flops; 1189 } 1190 } else { 1191 CeedInt num_input_fields, num_output_fields; 1192 CeedOperatorField *input_fields, *output_fields; 1193 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 1194 CeedInt num_elem = 0; 1195 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1196 1197 // Input FLOPs 1198 for (CeedInt i = 0; i < num_input_fields; i++) { 1199 if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1200 CeedSize restr_flops, basis_flops; 1201 1202 CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops)); 1203 *flops += restr_flops; 1204 CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops)); 1205 *flops += basis_flops * num_elem; 1206 } 1207 } 1208 // QF FLOPs 1209 CeedInt num_qpts; 1210 CeedSize qf_flops; 1211 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 1212 CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops)); 1213 *flops += num_elem * num_qpts * qf_flops; 1214 // Output FLOPs 1215 for (CeedInt i = 0; i < num_output_fields; i++) { 1216 if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1217 CeedSize restr_flops, basis_flops; 1218 1219 CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops)); 1220 *flops += restr_flops; 1221 CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops)); 1222 *flops += basis_flops * num_elem; 1223 } 1224 } 1225 } 1226 1227 return CEED_ERROR_SUCCESS; 1228 } 1229 1230 /** 1231 @brief Get CeedQFunction global context for a CeedOperator. 1232 1233 The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`. 1234 1235 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. 1236 This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext. 1237 1238 @param[in] op CeedOperator 1239 @param[out] ctx Variable to store CeedQFunctionContext 1240 1241 @return An error code: 0 - success, otherwise - failure 1242 1243 @ref Advanced 1244 **/ 1245 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) { 1246 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator"); 1247 if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx)); 1248 else *ctx = NULL; 1249 return CEED_ERROR_SUCCESS; 1250 } 1251 1252 /** 1253 @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`. 1254 1255 Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`). 1256 1257 @param[in] op CeedOperator 1258 @param[in] field_name Name of field to retrieve label 1259 @param[out] field_label Variable to field label 1260 1261 @return An error code: 0 - success, otherwise - failure 1262 1263 @ref User 1264 **/ 1265 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) { 1266 bool is_composite; 1267 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1268 1269 if (is_composite) { 1270 // Composite operator 1271 // -- Check if composite label already created 1272 for (CeedInt i = 0; i < op->num_context_labels; i++) { 1273 if (!strcmp(op->context_labels[i]->name, field_name)) { 1274 *field_label = op->context_labels[i]; 1275 return CEED_ERROR_SUCCESS; 1276 } 1277 } 1278 1279 // -- Create composite label if needed 1280 CeedInt num_sub; 1281 CeedOperator *sub_operators; 1282 CeedContextFieldLabel new_field_label; 1283 1284 CeedCall(CeedCalloc(1, &new_field_label)); 1285 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 1286 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1287 CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels)); 1288 new_field_label->num_sub_labels = num_sub; 1289 1290 bool field_found = false; 1291 for (CeedInt i = 0; i < num_sub; i++) { 1292 if (sub_operators[i]->qf->ctx) { 1293 CeedContextFieldLabel new_field_label_i; 1294 CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i)); 1295 if (new_field_label_i) { 1296 field_found = true; 1297 new_field_label->sub_labels[i] = new_field_label_i; 1298 new_field_label->name = new_field_label_i->name; 1299 new_field_label->description = new_field_label_i->description; 1300 if (new_field_label->type && new_field_label->type != new_field_label_i->type) { 1301 // LCOV_EXCL_START 1302 CeedCall(CeedFree(&new_field_label)); 1303 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s", 1304 CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]); 1305 // LCOV_EXCL_STOP 1306 } else { 1307 new_field_label->type = new_field_label_i->type; 1308 } 1309 if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) { 1310 // LCOV_EXCL_START 1311 CeedCall(CeedFree(&new_field_label)); 1312 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld", 1313 new_field_label->num_values, new_field_label_i->num_values); 1314 // LCOV_EXCL_STOP 1315 } else { 1316 new_field_label->num_values = new_field_label_i->num_values; 1317 } 1318 } 1319 } 1320 } 1321 // -- Cleanup if field was found 1322 if (field_found) { 1323 *field_label = new_field_label; 1324 } else { 1325 // LCOV_EXCL_START 1326 CeedCall(CeedFree(&new_field_label->sub_labels)); 1327 CeedCall(CeedFree(&new_field_label)); 1328 *field_label = NULL; 1329 // LCOV_EXCL_STOP 1330 } 1331 } else { 1332 // Single, non-composite operator 1333 if (op->qf->ctx) { 1334 CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label)); 1335 } else { 1336 *field_label = NULL; 1337 } 1338 } 1339 1340 // Set label in operator 1341 if (*field_label) { 1342 (*field_label)->from_op = true; 1343 1344 // Move new composite label to operator 1345 if (op->num_context_labels == 0) { 1346 CeedCall(CeedCalloc(1, &op->context_labels)); 1347 op->max_context_labels = 1; 1348 } else if (op->num_context_labels == op->max_context_labels) { 1349 CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels)); 1350 op->max_context_labels *= 2; 1351 } 1352 op->context_labels[op->num_context_labels] = *field_label; 1353 op->num_context_labels++; 1354 } 1355 1356 return CEED_ERROR_SUCCESS; 1357 } 1358 1359 /** 1360 @brief Set QFunctionContext field holding double precision values. 1361 1362 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1363 1364 @param[in,out] op CeedOperator 1365 @param[in] field_label Label of field to set 1366 @param[in] values Values to set 1367 1368 @return An error code: 0 - success, otherwise - failure 1369 1370 @ref User 1371 **/ 1372 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) { 1373 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1374 } 1375 1376 /** 1377 @brief Get QFunctionContext field holding double precision values, read-only. 1378 1379 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1380 1381 @param[in] op CeedOperator 1382 @param[in] field_label Label of field to get 1383 @param[out] num_values Number of values in the field label 1384 @param[out] values Pointer to context values 1385 1386 @return An error code: 0 - success, otherwise - failure 1387 1388 @ref User 1389 **/ 1390 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) { 1391 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values); 1392 } 1393 1394 /** 1395 @brief Restore QFunctionContext field holding double precision values, read-only. 1396 1397 @param[in] op CeedOperator 1398 @param[in] field_label Label of field to restore 1399 @param[out] values Pointer to context values 1400 1401 @return An error code: 0 - success, otherwise - failure 1402 1403 @ref User 1404 **/ 1405 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) { 1406 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1407 } 1408 1409 /** 1410 @brief Set QFunctionContext field holding int32 values. 1411 1412 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1413 1414 @param[in,out] op CeedOperator 1415 @param[in] field_label Label of field to set 1416 @param[in] values Values to set 1417 1418 @return An error code: 0 - success, otherwise - failure 1419 1420 @ref User 1421 **/ 1422 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) { 1423 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1424 } 1425 1426 /** 1427 @brief Get QFunctionContext field holding int32 values, read-only. 1428 1429 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1430 1431 @param[in] op CeedOperator 1432 @param[in] field_label Label of field to get 1433 @param[out] num_values Number of int32 values in `values` 1434 @param[out] values Pointer to context values 1435 1436 @return An error code: 0 - success, otherwise - failure 1437 1438 @ref User 1439 **/ 1440 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) { 1441 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values); 1442 } 1443 1444 /** 1445 @brief Restore QFunctionContext field holding int32 values, read-only. 1446 1447 @param[in] op CeedOperator 1448 @param[in] field_label Label of field to get 1449 @param[out] values Pointer to context values 1450 1451 @return An error code: 0 - success, otherwise - failure 1452 1453 @ref User 1454 **/ 1455 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) { 1456 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1457 } 1458 1459 /** 1460 @brief Apply CeedOperator to a vector 1461 1462 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1463 All inputs and outputs must be specified using CeedOperatorSetField(). 1464 1465 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1466 1467 @param[in] op CeedOperator to apply 1468 @param[in] in CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs 1469 @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 1470 active outputs 1471 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1472 1473 @return An error code: 0 - success, otherwise - failure 1474 1475 @ref User 1476 **/ 1477 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1478 CeedCall(CeedOperatorCheckReady(op)); 1479 1480 if (op->is_composite) { 1481 // Composite Operator 1482 if (op->ApplyComposite) { 1483 CeedCall(op->ApplyComposite(op, in, out, request)); 1484 } else { 1485 CeedInt num_suboperators; 1486 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1487 CeedOperator *sub_operators; 1488 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1489 1490 // Zero all output vectors 1491 if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0)); 1492 for (CeedInt i = 0; i < num_suboperators; i++) { 1493 for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) { 1494 CeedVector vec = sub_operators[i]->output_fields[j]->vec; 1495 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) { 1496 CeedCall(CeedVectorSetValue(vec, 0.0)); 1497 } 1498 } 1499 } 1500 // Apply 1501 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1502 CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request)); 1503 } 1504 } 1505 } else { 1506 // Standard Operator 1507 if (op->Apply) { 1508 CeedCall(op->Apply(op, in, out, request)); 1509 } else { 1510 // Zero all output vectors 1511 CeedQFunction qf = op->qf; 1512 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 1513 CeedVector vec = op->output_fields[i]->vec; 1514 if (vec == CEED_VECTOR_ACTIVE) vec = out; 1515 if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0)); 1516 } 1517 // Apply 1518 if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request)); 1519 } 1520 } 1521 return CEED_ERROR_SUCCESS; 1522 } 1523 1524 /** 1525 @brief Apply CeedOperator to a vector and add result to output vector 1526 1527 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1528 All inputs and outputs must be specified using CeedOperatorSetField(). 1529 1530 @param[in] op CeedOperator to apply 1531 @param[in] in CeedVector containing input state or NULL if there are no active inputs 1532 @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 1533 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1534 1535 @return An error code: 0 - success, otherwise - failure 1536 1537 @ref User 1538 **/ 1539 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1540 CeedCall(CeedOperatorCheckReady(op)); 1541 1542 if (op->is_composite) { 1543 // Composite Operator 1544 if (op->ApplyAddComposite) { 1545 CeedCall(op->ApplyAddComposite(op, in, out, request)); 1546 } else { 1547 CeedInt num_suboperators; 1548 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1549 CeedOperator *sub_operators; 1550 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1551 1552 for (CeedInt i = 0; i < num_suboperators; i++) { 1553 CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request)); 1554 } 1555 } 1556 } else if (op->num_elem) { 1557 // Standard Operator 1558 CeedCall(op->ApplyAdd(op, in, out, request)); 1559 } 1560 return CEED_ERROR_SUCCESS; 1561 } 1562 1563 /** 1564 @brief Destroy a CeedOperator 1565 1566 @param[in,out] op CeedOperator to destroy 1567 1568 @return An error code: 0 - success, otherwise - failure 1569 1570 @ref User 1571 **/ 1572 int CeedOperatorDestroy(CeedOperator *op) { 1573 if (!*op || --(*op)->ref_count > 0) { 1574 *op = NULL; 1575 return CEED_ERROR_SUCCESS; 1576 } 1577 if ((*op)->Destroy) CeedCall((*op)->Destroy(*op)); 1578 CeedCall(CeedDestroy(&(*op)->ceed)); 1579 // Free fields 1580 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1581 if ((*op)->input_fields[i]) { 1582 if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) { 1583 CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr)); 1584 } 1585 if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1586 CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis)); 1587 } 1588 if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) { 1589 CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec)); 1590 } 1591 CeedCall(CeedFree(&(*op)->input_fields[i]->field_name)); 1592 CeedCall(CeedFree(&(*op)->input_fields[i])); 1593 } 1594 } 1595 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1596 if ((*op)->output_fields[i]) { 1597 CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr)); 1598 if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1599 CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis)); 1600 } 1601 if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) { 1602 CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec)); 1603 } 1604 CeedCall(CeedFree(&(*op)->output_fields[i]->field_name)); 1605 CeedCall(CeedFree(&(*op)->output_fields[i])); 1606 } 1607 } 1608 // Destroy sub_operators 1609 for (CeedInt i = 0; i < (*op)->num_suboperators; i++) { 1610 if ((*op)->sub_operators[i]) { 1611 CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i])); 1612 } 1613 } 1614 CeedCall(CeedQFunctionDestroy(&(*op)->qf)); 1615 CeedCall(CeedQFunctionDestroy(&(*op)->dqf)); 1616 CeedCall(CeedQFunctionDestroy(&(*op)->dqfT)); 1617 // Destroy any composite labels 1618 if ((*op)->is_composite) { 1619 for (CeedInt i = 0; i < (*op)->num_context_labels; i++) { 1620 CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels)); 1621 CeedCall(CeedFree(&(*op)->context_labels[i])); 1622 } 1623 } 1624 CeedCall(CeedFree(&(*op)->context_labels)); 1625 1626 // Destroy fallback 1627 CeedCall(CeedOperatorDestroy(&(*op)->op_fallback)); 1628 1629 // Destroy assembly data 1630 CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled)); 1631 CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled)); 1632 1633 CeedCall(CeedFree(&(*op)->input_fields)); 1634 CeedCall(CeedFree(&(*op)->output_fields)); 1635 CeedCall(CeedFree(&(*op)->sub_operators)); 1636 CeedCall(CeedFree(&(*op)->name)); 1637 CeedCall(CeedFree(op)); 1638 return CEED_ERROR_SUCCESS; 1639 } 1640 1641 /// @} 1642