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 operator"); 1054 CeedCheck(op->num_qpts == 0, op->ceed, CEED_ERROR_MINOR, "Number of quadrature points already defined"); 1055 CeedCheck(!op->is_immutable, op->ceed, CEED_ERROR_MAJOR, "Operator cannot be changed after set as immutable"); 1056 op->num_qpts = num_qpts; 1057 return CEED_ERROR_SUCCESS; 1058 } 1059 1060 /** 1061 @brief Set name of CeedOperator for CeedOperatorView output 1062 1063 @param[in,out] op CeedOperator 1064 @param[in] name Name to set, or NULL to remove previously set name 1065 1066 @return An error code: 0 - success, otherwise - failure 1067 1068 @ref User 1069 **/ 1070 int CeedOperatorSetName(CeedOperator op, const char *name) { 1071 char *name_copy; 1072 size_t name_len = name ? strlen(name) : 0; 1073 1074 CeedCall(CeedFree(&op->name)); 1075 if (name_len > 0) { 1076 CeedCall(CeedCalloc(name_len + 1, &name_copy)); 1077 memcpy(name_copy, name, name_len); 1078 op->name = name_copy; 1079 } 1080 1081 return CEED_ERROR_SUCCESS; 1082 } 1083 1084 /** 1085 @brief View a CeedOperator 1086 1087 @param[in] op CeedOperator to view 1088 @param[in] stream Stream to write; typically stdout/stderr or a file 1089 1090 @return Error code: 0 - success, otherwise - failure 1091 1092 @ref User 1093 **/ 1094 int CeedOperatorView(CeedOperator op, FILE *stream) { 1095 bool has_name = op->name; 1096 1097 if (op->is_composite) { 1098 fprintf(stream, "Composite CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1099 1100 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1101 has_name = op->sub_operators[i]->name; 1102 fprintf(stream, " SubOperator %" CeedInt_FMT "%s%s:\n", i, has_name ? " - " : "", has_name ? op->sub_operators[i]->name : ""); 1103 CeedCall(CeedOperatorSingleView(op->sub_operators[i], 1, stream)); 1104 } 1105 } else { 1106 fprintf(stream, "CeedOperator%s%s\n", has_name ? " - " : "", has_name ? op->name : ""); 1107 CeedCall(CeedOperatorSingleView(op, 0, stream)); 1108 } 1109 return CEED_ERROR_SUCCESS; 1110 } 1111 1112 /** 1113 @brief Get the Ceed associated with a CeedOperator 1114 1115 @param[in] op CeedOperator 1116 @param[out] ceed Variable to store Ceed 1117 1118 @return An error code: 0 - success, otherwise - failure 1119 1120 @ref Advanced 1121 **/ 1122 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) { 1123 *ceed = op->ceed; 1124 return CEED_ERROR_SUCCESS; 1125 } 1126 1127 /** 1128 @brief Get the number of elements associated with a CeedOperator 1129 1130 @param[in] op CeedOperator 1131 @param[out] num_elem Variable to store number of elements 1132 1133 @return An error code: 0 - success, otherwise - failure 1134 1135 @ref Advanced 1136 **/ 1137 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) { 1138 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1139 *num_elem = op->num_elem; 1140 return CEED_ERROR_SUCCESS; 1141 } 1142 1143 /** 1144 @brief Get the number of quadrature points associated with a CeedOperator 1145 1146 @param[in] op CeedOperator 1147 @param[out] num_qpts Variable to store vector number of quadrature points 1148 1149 @return An error code: 0 - success, otherwise - failure 1150 1151 @ref Advanced 1152 **/ 1153 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) { 1154 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_MINOR, "Not defined for composite operator"); 1155 *num_qpts = op->num_qpts; 1156 return CEED_ERROR_SUCCESS; 1157 } 1158 1159 /** 1160 @brief Estimate number of FLOPs required to apply CeedOperator on the active vector 1161 1162 @param[in] op CeedOperator to estimate FLOPs for 1163 @param[out] flops Address of variable to hold FLOPs estimate 1164 1165 @ref Backend 1166 **/ 1167 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) { 1168 bool is_composite; 1169 CeedCall(CeedOperatorCheckReady(op)); 1170 1171 *flops = 0; 1172 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1173 if (is_composite) { 1174 CeedInt num_suboperators; 1175 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1176 CeedOperator *sub_operators; 1177 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1178 1179 // FLOPs for each suboperator 1180 for (CeedInt i = 0; i < num_suboperators; i++) { 1181 CeedSize suboperator_flops; 1182 CeedCall(CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops)); 1183 *flops += suboperator_flops; 1184 } 1185 } else { 1186 CeedInt num_input_fields, num_output_fields; 1187 CeedOperatorField *input_fields, *output_fields; 1188 CeedCall(CeedOperatorGetFields(op, &num_input_fields, &input_fields, &num_output_fields, &output_fields)); 1189 CeedInt num_elem = 0; 1190 CeedCall(CeedOperatorGetNumElements(op, &num_elem)); 1191 1192 // Input FLOPs 1193 for (CeedInt i = 0; i < num_input_fields; i++) { 1194 if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1195 CeedSize restr_flops, basis_flops; 1196 1197 CeedCall(CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_rstr, CEED_NOTRANSPOSE, &restr_flops)); 1198 *flops += restr_flops; 1199 CeedCall(CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE, op->qf->input_fields[i]->eval_mode, &basis_flops)); 1200 *flops += basis_flops * num_elem; 1201 } 1202 } 1203 // QF FLOPs 1204 CeedInt num_qpts; 1205 CeedSize qf_flops; 1206 CeedCall(CeedOperatorGetNumQuadraturePoints(op, &num_qpts)); 1207 CeedCall(CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops)); 1208 *flops += num_elem * num_qpts * qf_flops; 1209 // Output FLOPs 1210 for (CeedInt i = 0; i < num_output_fields; i++) { 1211 if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) { 1212 CeedSize restr_flops, basis_flops; 1213 1214 CeedCall(CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_rstr, CEED_TRANSPOSE, &restr_flops)); 1215 *flops += restr_flops; 1216 CeedCall(CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE, op->qf->output_fields[i]->eval_mode, &basis_flops)); 1217 *flops += basis_flops * num_elem; 1218 } 1219 } 1220 } 1221 1222 return CEED_ERROR_SUCCESS; 1223 } 1224 1225 /** 1226 @brief Get CeedQFunction global context for a CeedOperator. 1227 1228 The caller is responsible for destroying `ctx` returned from this function via `CeedQFunctionContextDestroy()`. 1229 1230 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. 1231 This CeedQFunctionContext will be destroyed if `ctx` is the only reference to this CeedQFunctionContext. 1232 1233 @param[in] op CeedOperator 1234 @param[out] ctx Variable to store CeedQFunctionContext 1235 1236 @return An error code: 0 - success, otherwise - failure 1237 1238 @ref Advanced 1239 **/ 1240 int CeedOperatorGetContext(CeedOperator op, CeedQFunctionContext *ctx) { 1241 CeedCheck(!op->is_composite, op->ceed, CEED_ERROR_INCOMPATIBLE, "Cannot retrieve QFunctionContext for composite operator"); 1242 if (op->qf->ctx) CeedCall(CeedQFunctionContextReferenceCopy(op->qf->ctx, ctx)); 1243 else *ctx = NULL; 1244 return CEED_ERROR_SUCCESS; 1245 } 1246 1247 /** 1248 @brief Get label for a registered QFunctionContext field, or `NULL` if no field has been registered with this `field_name`. 1249 1250 Fields are registered via `CeedQFunctionContextRegister*()` functions (eg. `CeedQFunctionContextRegisterDouble()`). 1251 1252 @param[in] op CeedOperator 1253 @param[in] field_name Name of field to retrieve label 1254 @param[out] field_label Variable to field label 1255 1256 @return An error code: 0 - success, otherwise - failure 1257 1258 @ref User 1259 **/ 1260 int CeedOperatorGetContextFieldLabel(CeedOperator op, const char *field_name, CeedContextFieldLabel *field_label) { 1261 bool is_composite; 1262 CeedCall(CeedOperatorIsComposite(op, &is_composite)); 1263 1264 if (is_composite) { 1265 // Composite operator 1266 // -- Check if composite label already created 1267 for (CeedInt i = 0; i < op->num_context_labels; i++) { 1268 if (!strcmp(op->context_labels[i]->name, field_name)) { 1269 *field_label = op->context_labels[i]; 1270 return CEED_ERROR_SUCCESS; 1271 } 1272 } 1273 1274 // -- Create composite label if needed 1275 CeedInt num_sub; 1276 CeedOperator *sub_operators; 1277 CeedContextFieldLabel new_field_label; 1278 1279 CeedCall(CeedCalloc(1, &new_field_label)); 1280 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_sub)); 1281 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1282 CeedCall(CeedCalloc(num_sub, &new_field_label->sub_labels)); 1283 new_field_label->num_sub_labels = num_sub; 1284 1285 bool field_found = false; 1286 for (CeedInt i = 0; i < num_sub; i++) { 1287 if (sub_operators[i]->qf->ctx) { 1288 CeedContextFieldLabel new_field_label_i; 1289 CeedCall(CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name, &new_field_label_i)); 1290 if (new_field_label_i) { 1291 field_found = true; 1292 new_field_label->sub_labels[i] = new_field_label_i; 1293 new_field_label->name = new_field_label_i->name; 1294 new_field_label->description = new_field_label_i->description; 1295 if (new_field_label->type && new_field_label->type != new_field_label_i->type) { 1296 // LCOV_EXCL_START 1297 CeedCall(CeedFree(&new_field_label)); 1298 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field types on sub-operator contexts. %s != %s", 1299 CeedContextFieldTypes[new_field_label->type], CeedContextFieldTypes[new_field_label_i->type]); 1300 // LCOV_EXCL_STOP 1301 } else { 1302 new_field_label->type = new_field_label_i->type; 1303 } 1304 if (new_field_label->num_values != 0 && new_field_label->num_values != new_field_label_i->num_values) { 1305 // LCOV_EXCL_START 1306 CeedCall(CeedFree(&new_field_label)); 1307 return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE, "Incompatible field number of values on sub-operator contexts. %ld != %ld", 1308 new_field_label->num_values, new_field_label_i->num_values); 1309 // LCOV_EXCL_STOP 1310 } else { 1311 new_field_label->num_values = new_field_label_i->num_values; 1312 } 1313 } 1314 } 1315 } 1316 // -- Cleanup if field was found 1317 if (field_found) { 1318 *field_label = new_field_label; 1319 } else { 1320 // LCOV_EXCL_START 1321 CeedCall(CeedFree(&new_field_label->sub_labels)); 1322 CeedCall(CeedFree(&new_field_label)); 1323 *field_label = NULL; 1324 // LCOV_EXCL_STOP 1325 } 1326 } else { 1327 // Single, non-composite operator 1328 if (op->qf->ctx) { 1329 CeedCall(CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label)); 1330 } else { 1331 *field_label = NULL; 1332 } 1333 } 1334 1335 // Set label in operator 1336 if (*field_label) { 1337 (*field_label)->from_op = true; 1338 1339 // Move new composite label to operator 1340 if (op->num_context_labels == 0) { 1341 CeedCall(CeedCalloc(1, &op->context_labels)); 1342 op->max_context_labels = 1; 1343 } else if (op->num_context_labels == op->max_context_labels) { 1344 CeedCall(CeedRealloc(2 * op->num_context_labels, &op->context_labels)); 1345 op->max_context_labels *= 2; 1346 } 1347 op->context_labels[op->num_context_labels] = *field_label; 1348 op->num_context_labels++; 1349 } 1350 1351 return CEED_ERROR_SUCCESS; 1352 } 1353 1354 /** 1355 @brief Set QFunctionContext field holding double precision values. 1356 1357 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1358 1359 @param[in,out] op CeedOperator 1360 @param[in] field_label Label of field to set 1361 @param[in] values Values to set 1362 1363 @return An error code: 0 - success, otherwise - failure 1364 1365 @ref User 1366 **/ 1367 int CeedOperatorSetContextDouble(CeedOperator op, CeedContextFieldLabel field_label, double *values) { 1368 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1369 } 1370 1371 /** 1372 @brief Get QFunctionContext field holding double precision values, read-only. 1373 1374 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1375 1376 @param[in] op CeedOperator 1377 @param[in] field_label Label of field to get 1378 @param[out] num_values Number of values in the field label 1379 @param[out] values Pointer to context values 1380 1381 @return An error code: 0 - success, otherwise - failure 1382 1383 @ref User 1384 **/ 1385 int CeedOperatorGetContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const double **values) { 1386 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, num_values, values); 1387 } 1388 1389 /** 1390 @brief Restore QFunctionContext field holding double precision values, read-only. 1391 1392 @param[in] op CeedOperator 1393 @param[in] field_label Label of field to restore 1394 @param[out] values Pointer to context values 1395 1396 @return An error code: 0 - success, otherwise - failure 1397 1398 @ref User 1399 **/ 1400 int CeedOperatorRestoreContextDoubleRead(CeedOperator op, CeedContextFieldLabel field_label, const double **values) { 1401 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_DOUBLE, values); 1402 } 1403 1404 /** 1405 @brief Set QFunctionContext field holding int32 values. 1406 1407 For composite operators, the values are set in all sub-operator QFunctionContexts that have a matching `field_name`. 1408 1409 @param[in,out] op CeedOperator 1410 @param[in] field_label Label of field to set 1411 @param[in] values Values to set 1412 1413 @return An error code: 0 - success, otherwise - failure 1414 1415 @ref User 1416 **/ 1417 int CeedOperatorSetContextInt32(CeedOperator op, CeedContextFieldLabel field_label, int *values) { 1418 return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1419 } 1420 1421 /** 1422 @brief Get QFunctionContext field holding int32 values, read-only. 1423 1424 For composite operators, the values correspond to the first sub-operator QFunctionContexts that has a matching `field_name`. 1425 1426 @param[in] op CeedOperator 1427 @param[in] field_label Label of field to get 1428 @param[out] num_values Number of int32 values in `values` 1429 @param[out] values Pointer to context values 1430 1431 @return An error code: 0 - success, otherwise - failure 1432 1433 @ref User 1434 **/ 1435 int CeedOperatorGetContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, size_t *num_values, const int **values) { 1436 return CeedOperatorContextGetGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, num_values, values); 1437 } 1438 1439 /** 1440 @brief Restore QFunctionContext field holding int32 values, read-only. 1441 1442 @param[in] op CeedOperator 1443 @param[in] field_label Label of field to get 1444 @param[out] values Pointer to context values 1445 1446 @return An error code: 0 - success, otherwise - failure 1447 1448 @ref User 1449 **/ 1450 int CeedOperatorRestoreContextInt32Read(CeedOperator op, CeedContextFieldLabel field_label, const int **values) { 1451 return CeedOperatorContextRestoreGenericRead(op, field_label, CEED_CONTEXT_FIELD_INT32, values); 1452 } 1453 1454 /** 1455 @brief Apply CeedOperator to a vector 1456 1457 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1458 All inputs and outputs must be specified using CeedOperatorSetField(). 1459 1460 Note: Calling this function asserts that setup is complete and sets the CeedOperator as immutable. 1461 1462 @param[in] op CeedOperator to apply 1463 @param[in] in CeedVector containing input state or @ref CEED_VECTOR_NONE if there are no active inputs 1464 @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 1465 active outputs 1466 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1467 1468 @return An error code: 0 - success, otherwise - failure 1469 1470 @ref User 1471 **/ 1472 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1473 CeedCall(CeedOperatorCheckReady(op)); 1474 1475 if (op->is_composite) { 1476 // Composite Operator 1477 if (op->ApplyComposite) { 1478 CeedCall(op->ApplyComposite(op, in, out, request)); 1479 } else { 1480 CeedInt num_suboperators; 1481 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1482 CeedOperator *sub_operators; 1483 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1484 1485 // Zero all output vectors 1486 if (out != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(out, 0.0)); 1487 for (CeedInt i = 0; i < num_suboperators; i++) { 1488 for (CeedInt j = 0; j < sub_operators[i]->qf->num_output_fields; j++) { 1489 CeedVector vec = sub_operators[i]->output_fields[j]->vec; 1490 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) { 1491 CeedCall(CeedVectorSetValue(vec, 0.0)); 1492 } 1493 } 1494 } 1495 // Apply 1496 for (CeedInt i = 0; i < op->num_suboperators; i++) { 1497 CeedCall(CeedOperatorApplyAdd(op->sub_operators[i], in, out, request)); 1498 } 1499 } 1500 } else { 1501 // Standard Operator 1502 if (op->Apply) { 1503 CeedCall(op->Apply(op, in, out, request)); 1504 } else { 1505 // Zero all output vectors 1506 CeedQFunction qf = op->qf; 1507 for (CeedInt i = 0; i < qf->num_output_fields; i++) { 1508 CeedVector vec = op->output_fields[i]->vec; 1509 if (vec == CEED_VECTOR_ACTIVE) vec = out; 1510 if (vec != CEED_VECTOR_NONE) CeedCall(CeedVectorSetValue(vec, 0.0)); 1511 } 1512 // Apply 1513 if (op->num_elem) CeedCall(op->ApplyAdd(op, in, out, request)); 1514 } 1515 } 1516 return CEED_ERROR_SUCCESS; 1517 } 1518 1519 /** 1520 @brief Apply CeedOperator to a vector and add result to output vector 1521 1522 This computes the action of the operator on the specified (active) input, yielding its (active) output. 1523 All inputs and outputs must be specified using CeedOperatorSetField(). 1524 1525 @param[in] op CeedOperator to apply 1526 @param[in] in CeedVector containing input state or NULL if there are no active inputs 1527 @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 1528 @param[in] request Address of CeedRequest for non-blocking completion, else @ref CEED_REQUEST_IMMEDIATE 1529 1530 @return An error code: 0 - success, otherwise - failure 1531 1532 @ref User 1533 **/ 1534 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out, CeedRequest *request) { 1535 CeedCall(CeedOperatorCheckReady(op)); 1536 1537 if (op->is_composite) { 1538 // Composite Operator 1539 if (op->ApplyAddComposite) { 1540 CeedCall(op->ApplyAddComposite(op, in, out, request)); 1541 } else { 1542 CeedInt num_suboperators; 1543 CeedCall(CeedCompositeOperatorGetNumSub(op, &num_suboperators)); 1544 CeedOperator *sub_operators; 1545 CeedCall(CeedCompositeOperatorGetSubList(op, &sub_operators)); 1546 1547 for (CeedInt i = 0; i < num_suboperators; i++) { 1548 CeedCall(CeedOperatorApplyAdd(sub_operators[i], in, out, request)); 1549 } 1550 } 1551 } else if (op->num_elem) { 1552 // Standard Operator 1553 CeedCall(op->ApplyAdd(op, in, out, request)); 1554 } 1555 return CEED_ERROR_SUCCESS; 1556 } 1557 1558 /** 1559 @brief Destroy a CeedOperator 1560 1561 @param[in,out] op CeedOperator to destroy 1562 1563 @return An error code: 0 - success, otherwise - failure 1564 1565 @ref User 1566 **/ 1567 int CeedOperatorDestroy(CeedOperator *op) { 1568 if (!*op || --(*op)->ref_count > 0) { 1569 *op = NULL; 1570 return CEED_ERROR_SUCCESS; 1571 } 1572 if ((*op)->Destroy) CeedCall((*op)->Destroy(*op)); 1573 CeedCall(CeedDestroy(&(*op)->ceed)); 1574 // Free fields 1575 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1576 if ((*op)->input_fields[i]) { 1577 if ((*op)->input_fields[i]->elem_rstr != CEED_ELEMRESTRICTION_NONE) { 1578 CeedCall(CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_rstr)); 1579 } 1580 if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1581 CeedCall(CeedBasisDestroy(&(*op)->input_fields[i]->basis)); 1582 } 1583 if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->input_fields[i]->vec != CEED_VECTOR_NONE) { 1584 CeedCall(CeedVectorDestroy(&(*op)->input_fields[i]->vec)); 1585 } 1586 CeedCall(CeedFree(&(*op)->input_fields[i]->field_name)); 1587 CeedCall(CeedFree(&(*op)->input_fields[i])); 1588 } 1589 } 1590 for (CeedInt i = 0; i < (*op)->num_fields; i++) { 1591 if ((*op)->output_fields[i]) { 1592 CeedCall(CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_rstr)); 1593 if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) { 1594 CeedCall(CeedBasisDestroy(&(*op)->output_fields[i]->basis)); 1595 } 1596 if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE && (*op)->output_fields[i]->vec != CEED_VECTOR_NONE) { 1597 CeedCall(CeedVectorDestroy(&(*op)->output_fields[i]->vec)); 1598 } 1599 CeedCall(CeedFree(&(*op)->output_fields[i]->field_name)); 1600 CeedCall(CeedFree(&(*op)->output_fields[i])); 1601 } 1602 } 1603 // Destroy sub_operators 1604 for (CeedInt i = 0; i < (*op)->num_suboperators; i++) { 1605 if ((*op)->sub_operators[i]) { 1606 CeedCall(CeedOperatorDestroy(&(*op)->sub_operators[i])); 1607 } 1608 } 1609 CeedCall(CeedQFunctionDestroy(&(*op)->qf)); 1610 CeedCall(CeedQFunctionDestroy(&(*op)->dqf)); 1611 CeedCall(CeedQFunctionDestroy(&(*op)->dqfT)); 1612 // Destroy any composite labels 1613 if ((*op)->is_composite) { 1614 for (CeedInt i = 0; i < (*op)->num_context_labels; i++) { 1615 CeedCall(CeedFree(&(*op)->context_labels[i]->sub_labels)); 1616 CeedCall(CeedFree(&(*op)->context_labels[i])); 1617 } 1618 } 1619 CeedCall(CeedFree(&(*op)->context_labels)); 1620 1621 // Destroy fallback 1622 CeedCall(CeedOperatorDestroy(&(*op)->op_fallback)); 1623 1624 // Destroy assembly data 1625 CeedCall(CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled)); 1626 CeedCall(CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled)); 1627 1628 CeedCall(CeedFree(&(*op)->input_fields)); 1629 CeedCall(CeedFree(&(*op)->output_fields)); 1630 CeedCall(CeedFree(&(*op)->sub_operators)); 1631 CeedCall(CeedFree(&(*op)->name)); 1632 CeedCall(CeedFree(op)); 1633 return CEED_ERROR_SUCCESS; 1634 } 1635 1636 /// @} 1637