1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative. 16 17 #include <ceed-impl.h> 18 #include <ceed-backend.h> 19 #include <string.h> 20 21 /// @file 22 /// Implementation of public CeedOperator interfaces 23 /// 24 /// @addtogroup CeedOperator 25 /// @{ 26 27 /** 28 @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and 29 CeedElemRestriction can be associated with CeedQFunction fields with 30 \ref CeedOperatorSetField. 31 32 @param ceed A Ceed object where the CeedOperator will be created 33 @param qf QFunction defining the action of the operator at quadrature points 34 @param dqf QFunction defining the action of the Jacobian of @a qf (or NULL) 35 @param dqfT QFunction defining the action of the transpose of the Jacobian 36 of @a qf (or NULL) 37 @param[out] op Address of the variable where the newly created 38 CeedOperator will be stored 39 40 @return An error code: 0 - success, otherwise - failure 41 42 @ref Basic 43 */ 44 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf, 45 CeedQFunction dqfT, CeedOperator *op) { 46 int ierr; 47 48 if (!ceed->OperatorCreate) { 49 Ceed delegate; 50 ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr); 51 52 if (!delegate) 53 // LCOV_EXCL_START 54 return CeedError(ceed, 1, "Backend does not support OperatorCreate"); 55 // LCOV_EXCL_STOP 56 57 ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr); 58 return 0; 59 } 60 61 ierr = CeedCalloc(1,op); CeedChk(ierr); 62 (*op)->ceed = ceed; 63 ceed->refcount++; 64 (*op)->refcount = 1; 65 (*op)->qf = qf; 66 qf->refcount++; 67 (*op)->dqf = dqf; 68 if (dqf) dqf->refcount++; 69 (*op)->dqfT = dqfT; 70 if (dqfT) dqfT->refcount++; 71 ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr); 72 ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr); 73 ierr = ceed->OperatorCreate(*op); CeedChk(ierr); 74 return 0; 75 } 76 77 /** 78 @brief Create an operator that composes the action of several operators 79 80 @param ceed A Ceed object where the CeedOperator will be created 81 @param[out] op Address of the variable where the newly created 82 Composite CeedOperator will be stored 83 84 @return An error code: 0 - success, otherwise - failure 85 86 @ref Basic 87 */ 88 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) { 89 int ierr; 90 91 if (!ceed->CompositeOperatorCreate) { 92 Ceed delegate; 93 ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr); 94 95 if (!delegate) 96 // LCOV_EXCL_START 97 return CeedError(ceed, 1, "Backend does not support " 98 "CompositeOperatorCreate"); 99 // LCOV_EXCL_STOP 100 101 ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr); 102 return 0; 103 } 104 105 ierr = CeedCalloc(1,op); CeedChk(ierr); 106 (*op)->ceed = ceed; 107 ceed->refcount++; 108 (*op)->composite = true; 109 ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr); 110 ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr); 111 return 0; 112 } 113 114 /** 115 @brief Provide a field to a CeedOperator for use by its CeedQFunction 116 117 This function is used to specify both active and passive fields to a 118 CeedOperator. For passive fields, a vector @arg v must be provided. Passive 119 fields can inputs or outputs (updated in-place when operator is applied). 120 121 Active fields must be specified using this function, but their data (in a 122 CeedVector) is passed in CeedOperatorApply(). There can be at most one active 123 input and at most one active output. 124 125 @param op CeedOperator on which to provide the field 126 @param fieldname Name of the field (to be matched with the name used by 127 CeedQFunction) 128 @param r CeedElemRestriction 129 @param lmode CeedTransposeMode which specifies the ordering of the 130 components of the l-vector used by this CeedOperatorField, 131 CEED_NOTRANSPOSE indicates the component is the 132 outermost index and CEED_TRANSPOSE indicates the component 133 is the innermost index in ordering of the l-vector 134 @param b CeedBasis in which the field resides or CEED_BASIS_COLLOCATED 135 if collocated with quadrature points 136 @param v CeedVector to be used by CeedOperator or CEED_VECTOR_ACTIVE 137 if field is active or CEED_VECTOR_NONE if using 138 CEED_EVAL_WEIGHT in the QFunction 139 140 @return An error code: 0 - success, otherwise - failure 141 142 @ref Basic 143 **/ 144 int CeedOperatorSetField(CeedOperator op, const char *fieldname, 145 CeedElemRestriction r, CeedTransposeMode lmode, 146 CeedBasis b, CeedVector v) { 147 int ierr; 148 if (op->composite) 149 // LCOV_EXCL_START 150 return CeedError(op->ceed, 1, "Cannot add field to composite operator."); 151 // LCOV_EXCL_STOP 152 if (!r) 153 // LCOV_EXCL_START 154 return CeedError(op->ceed, 1, 155 "ElemRestriction r for field \"%s\" must be non-NULL.", 156 fieldname); 157 // LCOV_EXCL_STOP 158 if (!b) 159 // LCOV_EXCL_START 160 return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.", 161 fieldname); 162 // LCOV_EXCL_STOP 163 if (!v) 164 // LCOV_EXCL_START 165 return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.", 166 fieldname); 167 // LCOV_EXCL_STOP 168 169 CeedInt numelements; 170 ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr); 171 if (op->numelements && op->numelements != numelements) 172 // LCOV_EXCL_START 173 return CeedError(op->ceed, 1, 174 "ElemRestriction with %d elements incompatible with prior " 175 "%d elements", numelements, op->numelements); 176 // LCOV_EXCL_STOP 177 op->numelements = numelements; 178 179 if (b != CEED_BASIS_COLLOCATED) { 180 CeedInt numqpoints; 181 ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr); 182 if (op->numqpoints && op->numqpoints != numqpoints) 183 // LCOV_EXCL_START 184 return CeedError(op->ceed, 1, "Basis with %d quadrature points " 185 "incompatible with prior %d points", numqpoints, 186 op->numqpoints); 187 // LCOV_EXCL_STOP 188 op->numqpoints = numqpoints; 189 } 190 CeedOperatorField *ofield; 191 for (CeedInt i=0; i<op->qf->numinputfields; i++) { 192 if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) { 193 ofield = &op->inputfields[i]; 194 goto found; 195 } 196 } 197 for (CeedInt i=0; i<op->qf->numoutputfields; i++) { 198 if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) { 199 ofield = &op->outputfields[i]; 200 goto found; 201 } 202 } 203 // LCOV_EXCL_START 204 return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'", 205 fieldname); 206 // LCOV_EXCL_STOP 207 found: 208 ierr = CeedCalloc(1, ofield); CeedChk(ierr); 209 (*ofield)->Erestrict = r; 210 r->refcount += 1; 211 (*ofield)->lmode = lmode; 212 (*ofield)->basis = b; 213 if (b != CEED_BASIS_COLLOCATED) 214 b->refcount += 1; 215 (*ofield)->vec = v; 216 if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) 217 v->refcount += 1; 218 op->nfields += 1; 219 return 0; 220 } 221 222 /** 223 @brief Add a sub-operator to a composite CeedOperator 224 225 @param[out] compositeop Address of the composite CeedOperator 226 @param subop Address of the sub-operator CeedOperator 227 228 @return An error code: 0 - success, otherwise - failure 229 230 @ref Basic 231 */ 232 int CeedCompositeOperatorAddSub(CeedOperator compositeop, 233 CeedOperator subop) { 234 if (!compositeop->composite) 235 // LCOV_EXCL_START 236 return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite " 237 "operator"); 238 // LCOV_EXCL_STOP 239 240 if (compositeop->numsub == CEED_COMPOSITE_MAX) 241 // LCOV_EXCL_START 242 return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators"); 243 // LCOV_EXCL_STOP 244 245 compositeop->suboperators[compositeop->numsub] = subop; 246 subop->refcount++; 247 compositeop->numsub++; 248 return 0; 249 } 250 251 /** 252 @brief Assemble a linear CeedQFunction associated with a CeedOperator 253 254 This returns a CeedVector containing a matrix at each quadrature point 255 providing the action of the CeedQFunction associated with the CeedOperator. 256 The vector 'assembled' is of shape 257 [num_elements, num_input_fields, num_output_fields, num_quad_points] 258 and contains column-major matrices representing the action of the 259 CeedQFunction for a corresponding quadrature point on an element. Inputs and 260 outputs are in the order provided by the user when adding CeedOperator fields. 261 For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and 262 'v', provided in that order, would result in an assembled QFunction that 263 consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting 264 on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v]. 265 266 @param op CeedOperator to assemble CeedQFunction 267 @param[out] assembled CeedVector to store assembled Ceed QFunction at 268 quadrature points 269 @param[out] rstr CeedElemRestriction for CeedVector containing assembled 270 CeedQFunction 271 @param request Address of CeedRequest for non-blocking completion, else 272 CEED_REQUEST_IMMEDIATE 273 274 @return An error code: 0 - success, otherwise - failure 275 276 @ref Advanced 277 **/ 278 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled, 279 CeedElemRestriction *rstr, CeedRequest *request) { 280 int ierr; 281 Ceed ceed = op->ceed; 282 CeedQFunction qf = op->qf; 283 284 if (op->composite) { 285 return CeedError(ceed, 1, "Cannot assemble QFunction for composite operator"); 286 } else { 287 if (op->nfields == 0) 288 return CeedError(ceed, 1, "No operator fields set"); 289 if (op->nfields < qf->numinputfields + qf->numoutputfields) 290 return CeedError( ceed, 1, "Not all operator fields set"); 291 if (op->numelements == 0) 292 return CeedError(ceed, 1, "At least one restriction required"); 293 if (op->numqpoints == 0) 294 return CeedError(ceed, 1, "At least one non-collocated basis required"); 295 } 296 ierr = op->AssembleLinearQFunction(op, assembled, rstr, request); 297 CeedChk(ierr); 298 return 0; 299 } 300 301 /** 302 @brief Apply CeedOperator to a vector 303 304 This computes the action of the operator on the specified (active) input, 305 yielding its (active) output. All inputs and outputs must be specified using 306 CeedOperatorSetField(). 307 308 @param op CeedOperator to apply 309 @param[in] in CeedVector containing input state or NULL if there are no 310 active inputs 311 @param[out] out CeedVector to store result of applying operator (must be 312 distinct from @a in) or NULL if there are no active outputs 313 @param request Address of CeedRequest for non-blocking completion, else 314 CEED_REQUEST_IMMEDIATE 315 316 @return An error code: 0 - success, otherwise - failure 317 318 @ref Basic 319 **/ 320 int CeedOperatorApply(CeedOperator op, CeedVector in, 321 CeedVector out, CeedRequest *request) { 322 int ierr; 323 Ceed ceed = op->ceed; 324 CeedQFunction qf = op->qf; 325 326 if (op->composite) { 327 if (!op->numsub) 328 // LCOV_EXCL_START 329 return CeedError(ceed, 1, "No suboperators set"); 330 // LCOV_EXCL_STOP 331 } else { 332 if (op->nfields == 0) 333 // LCOV_EXCL_START 334 return CeedError(ceed, 1, "No operator fields set"); 335 // LCOV_EXCL_STOP 336 if (op->nfields < qf->numinputfields + qf->numoutputfields) 337 // LCOV_EXCL_START 338 return CeedError(ceed, 1, "Not all operator fields set"); 339 // LCOV_EXCL_STOP 340 if (op->numelements == 0) 341 // LCOV_EXCL_START 342 return CeedError(ceed, 1,"At least one restriction required"); 343 // LCOV_EXCL_STOP 344 if (op->numqpoints == 0) 345 // LCOV_EXCL_START 346 return CeedError(ceed, 1,"At least one non-collocated basis required"); 347 // LCOV_EXCL_STOP 348 } 349 ierr = op->Apply(op, in, out, request); CeedChk(ierr); 350 return 0; 351 } 352 353 /** 354 @brief Get the Ceed associated with a CeedOperator 355 356 @param op CeedOperator 357 @param[out] ceed Variable to store Ceed 358 359 @return An error code: 0 - success, otherwise - failure 360 361 @ref Advanced 362 **/ 363 364 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) { 365 *ceed = op->ceed; 366 return 0; 367 } 368 369 /** 370 @brief Get the number of elements associated with a CeedOperator 371 372 @param op CeedOperator 373 @param[out] numelem Variable to store number of elements 374 375 @return An error code: 0 - success, otherwise - failure 376 377 @ref Advanced 378 **/ 379 380 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) { 381 if (op->composite) 382 // LCOV_EXCL_START 383 return CeedError(op->ceed, 1, "Not defined for composite operator"); 384 // LCOV_EXCL_STOP 385 386 *numelem = op->numelements; 387 return 0; 388 } 389 390 /** 391 @brief Get the number of quadrature points associated with a CeedOperator 392 393 @param op CeedOperator 394 @param[out] numqpts Variable to store vector number of quadrature points 395 396 @return An error code: 0 - success, otherwise - failure 397 398 @ref Advanced 399 **/ 400 401 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) { 402 if (op->composite) 403 // LCOV_EXCL_START 404 return CeedError(op->ceed, 1, "Not defined for composite operator"); 405 // LCOV_EXCL_STOP 406 407 *numqpts = op->numqpoints; 408 return 0; 409 } 410 411 /** 412 @brief Get the number of arguments associated with a CeedOperator 413 414 @param op CeedOperator 415 @param[out] numargs Variable to store vector number of arguments 416 417 @return An error code: 0 - success, otherwise - failure 418 419 @ref Advanced 420 **/ 421 422 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) { 423 if (op->composite) 424 // LCOV_EXCL_START 425 return CeedError(op->ceed, 1, "Not defined for composite operators"); 426 // LCOV_EXCL_STOP 427 428 *numargs = op->nfields; 429 return 0; 430 } 431 432 /** 433 @brief Get the setup status of a CeedOperator 434 435 @param op CeedOperator 436 @param[out] setupdone Variable to store setup status 437 438 @return An error code: 0 - success, otherwise - failure 439 440 @ref Advanced 441 **/ 442 443 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) { 444 *setupdone = op->setupdone; 445 return 0; 446 } 447 448 /** 449 @brief Get the QFunction associated with a CeedOperator 450 451 @param op CeedOperator 452 @param[out] qf Variable to store QFunction 453 454 @return An error code: 0 - success, otherwise - failure 455 456 @ref Advanced 457 **/ 458 459 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) { 460 if (op->composite) 461 // LCOV_EXCL_START 462 return CeedError(op->ceed, 1, "Not defined for composite operator"); 463 // LCOV_EXCL_STOP 464 465 *qf = op->qf; 466 return 0; 467 } 468 469 /** 470 @brief Get the number of suboperators associated with a CeedOperator 471 472 @param op CeedOperator 473 @param[out] numsub Variable to store number of suboperators 474 475 @return An error code: 0 - success, otherwise - failure 476 477 @ref Advanced 478 **/ 479 480 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) { 481 if (!op->composite) 482 // LCOV_EXCL_START 483 return CeedError(op->ceed, 1, "Not a composite operator"); 484 // LCOV_EXCL_STOP 485 486 *numsub = op->numsub; 487 return 0; 488 } 489 490 /** 491 @brief Get the list of suboperators associated with a CeedOperator 492 493 @param op CeedOperator 494 @param[out] suboperators Variable to store list of suboperators 495 496 @return An error code: 0 - success, otherwise - failure 497 498 @ref Advanced 499 **/ 500 501 int CeedOperatorGetSubList(CeedOperator op, CeedOperator* *suboperators) { 502 if (!op->composite) 503 // LCOV_EXCL_START 504 return CeedError(op->ceed, 1, "Not a composite operator"); 505 // LCOV_EXCL_STOP 506 507 *suboperators = op->suboperators; 508 return 0; 509 } 510 511 /** 512 @brief Set the backend data of a CeedOperator 513 514 @param[out] op CeedOperator 515 @param data Data to set 516 517 @return An error code: 0 - success, otherwise - failure 518 519 @ref Advanced 520 **/ 521 522 int CeedOperatorSetData(CeedOperator op, void* *data) { 523 op->data = *data; 524 return 0; 525 } 526 527 /** 528 @brief Get the backend data of a CeedOperator 529 530 @param op CeedOperator 531 @param[out] data Variable to store data 532 533 @return An error code: 0 - success, otherwise - failure 534 535 @ref Advanced 536 **/ 537 538 int CeedOperatorGetData(CeedOperator op, void* *data) { 539 *data = op->data; 540 return 0; 541 } 542 543 /** 544 @brief Set the setup flag of a CeedOperator to True 545 546 @param op CeedOperator 547 548 @return An error code: 0 - success, otherwise - failure 549 550 @ref Advanced 551 **/ 552 553 int CeedOperatorSetSetupDone(CeedOperator op) { 554 op->setupdone = 1; 555 return 0; 556 } 557 558 /** 559 @brief Get the CeedOperatorFields of a CeedOperator 560 561 @param op CeedOperator 562 @param[out] inputfields Variable to store inputfields 563 @param[out] outputfields Variable to store outputfields 564 565 @return An error code: 0 - success, otherwise - failure 566 567 @ref Advanced 568 **/ 569 570 int CeedOperatorGetFields(CeedOperator op, 571 CeedOperatorField* *inputfields, 572 CeedOperatorField* *outputfields) { 573 if (op->composite) 574 // LCOV_EXCL_START 575 return CeedError(op->ceed, 1, "Not defined for composite operator"); 576 // LCOV_EXCL_STOP 577 578 if (inputfields) *inputfields = op->inputfields; 579 if (outputfields) *outputfields = op->outputfields; 580 return 0; 581 } 582 583 /** 584 @brief Get the L vector CeedTransposeMode of a CeedOperatorField 585 586 @param opfield CeedOperatorField 587 @param[out] lmode Variable to store CeedTransposeMode 588 589 @return An error code: 0 - success, otherwise - failure 590 591 @ref Advanced 592 **/ 593 594 int CeedOperatorFieldGetLMode(CeedOperatorField opfield, 595 CeedTransposeMode *lmode) { 596 *lmode = opfield->lmode; 597 return 0; 598 } 599 600 /** 601 @brief Get the CeedElemRestriction of a CeedOperatorField 602 603 @param opfield CeedOperatorField 604 @param[out] rstr Variable to store CeedElemRestriction 605 606 @return An error code: 0 - success, otherwise - failure 607 608 @ref Advanced 609 **/ 610 611 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield, 612 CeedElemRestriction *rstr) { 613 *rstr = opfield->Erestrict; 614 return 0; 615 } 616 617 /** 618 @brief Get the CeedBasis of a CeedOperatorField 619 620 @param opfield CeedOperatorField 621 @param[out] basis Variable to store CeedBasis 622 623 @return An error code: 0 - success, otherwise - failure 624 625 @ref Advanced 626 **/ 627 628 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, 629 CeedBasis *basis) { 630 *basis = opfield->basis; 631 return 0; 632 } 633 634 /** 635 @brief Get the CeedVector of a CeedOperatorField 636 637 @param opfield CeedOperatorField 638 @param[out] vec Variable to store CeedVector 639 640 @return An error code: 0 - success, otherwise - failure 641 642 @ref Advanced 643 **/ 644 645 int CeedOperatorFieldGetVector(CeedOperatorField opfield, 646 CeedVector *vec) { 647 *vec = opfield->vec; 648 return 0; 649 } 650 651 /** 652 @brief Destroy a CeedOperator 653 654 @param op CeedOperator to destroy 655 656 @return An error code: 0 - success, otherwise - failure 657 658 @ref Basic 659 **/ 660 int CeedOperatorDestroy(CeedOperator *op) { 661 int ierr; 662 663 if (!*op || --(*op)->refcount > 0) return 0; 664 if ((*op)->Destroy) { 665 ierr = (*op)->Destroy(*op); CeedChk(ierr); 666 } 667 ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr); 668 // Free fields 669 for (int i=0; i<(*op)->nfields; i++) 670 if ((*op)->inputfields[i]) { 671 ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict); 672 CeedChk(ierr); 673 if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) { 674 ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr); 675 } 676 if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE && 677 (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) { 678 ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr); 679 } 680 ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr); 681 } 682 for (int i=0; i<(*op)->nfields; i++) 683 if ((*op)->outputfields[i]) { 684 ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict); 685 CeedChk(ierr); 686 if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) { 687 ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr); 688 } 689 if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE && 690 (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) { 691 ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr); 692 } 693 ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr); 694 } 695 // Destroy suboperators 696 for (int i=0; i<(*op)->numsub; i++) 697 if ((*op)->suboperators[i]) { 698 ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr); 699 } 700 ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr); 701 ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr); 702 ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr); 703 704 ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr); 705 ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr); 706 ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr); 707 ierr = CeedFree(op); CeedChk(ierr); 708 return 0; 709 } 710 711 /// @} 712