xref: /libCEED/interface/ceed-operator.c (revision b36f2af8eb4e779074f4d92faffbc034a6db4539)
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/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed-impl.h>
20 #include <stdbool.h>
21 #include <stdio.h>
22 #include <string.h>
23 
24 /// @file
25 /// Implementation of CeedOperator interfaces
26 
27 /// ----------------------------------------------------------------------------
28 /// CeedOperator Library Internal Functions
29 /// ----------------------------------------------------------------------------
30 /// @addtogroup CeedOperatorDeveloper
31 /// @{
32 
33 /**
34   @brief Check if a CeedOperator Field matches the QFunction Field
35 
36   @param[in] ceed      Ceed object for error handling
37   @param[in] qf_field  QFunction Field matching Operator Field
38   @param[in] r         Operator Field ElemRestriction
39   @param[in] b         Operator Field Basis
40 
41   @return An error code: 0 - success, otherwise - failure
42 
43   @ref Developer
44 **/
45 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field,
46                                   CeedElemRestriction r, CeedBasis b) {
47   int ierr;
48   CeedEvalMode eval_mode = qf_field->eval_mode;
49   CeedInt dim = 1, num_comp = 1, restr_num_comp = 1, size = qf_field->size;
50   // Restriction
51   if (r != CEED_ELEMRESTRICTION_NONE) {
52     if (eval_mode == CEED_EVAL_WEIGHT) {
53       // LCOV_EXCL_START
54       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
55                        "CEED_ELEMRESTRICTION_NONE should be used "
56                        "for a field with eval mode CEED_EVAL_WEIGHT");
57       // LCOV_EXCL_STOP
58     }
59     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
60     CeedChk(ierr);
61   }
62   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
63     // LCOV_EXCL_START
64     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
65                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
66                      "must be used together.");
67     // LCOV_EXCL_STOP
68   }
69   // Basis
70   if (b != CEED_BASIS_COLLOCATED) {
71     if (eval_mode == CEED_EVAL_NONE)
72       // LCOV_EXCL_START
73       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
74                        "Field '%s' configured with CEED_EVAL_NONE must "
75                        "be used with CEED_BASIS_COLLOCATED",
76                        // LCOV_EXCL_STOP
77                        qf_field->field_name);
78     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
79     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
80     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
81       // LCOV_EXCL_START
82       return CeedError(ceed, CEED_ERROR_DIMENSION,
83                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
84                        "has %d components, but Basis has %d components",
85                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
86                        restr_num_comp,
87                        num_comp);
88       // LCOV_EXCL_STOP
89     }
90   }
91   // Field size
92   switch(eval_mode) {
93   case CEED_EVAL_NONE:
94     if (size != restr_num_comp)
95       // LCOV_EXCL_START
96       return CeedError(ceed, CEED_ERROR_DIMENSION,
97                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d components",
98                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
99                        restr_num_comp);
100     // LCOV_EXCL_STOP
101     break;
102   case CEED_EVAL_INTERP:
103     if (size != num_comp)
104       // LCOV_EXCL_START
105       return CeedError(ceed, CEED_ERROR_DIMENSION,
106                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
107                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
108                        num_comp);
109     // LCOV_EXCL_STOP
110     break;
111   case CEED_EVAL_GRAD:
112     if (size != num_comp * dim)
113       // LCOV_EXCL_START
114       return CeedError(ceed, CEED_ERROR_DIMENSION,
115                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
116                        "ElemRestriction/Basis has %d components",
117                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
118                        num_comp);
119     // LCOV_EXCL_STOP
120     break;
121   case CEED_EVAL_WEIGHT:
122     // No additional checks required
123     break;
124   case CEED_EVAL_DIV:
125     // Not implemented
126     break;
127   case CEED_EVAL_CURL:
128     // Not implemented
129     break;
130   }
131   return CEED_ERROR_SUCCESS;
132 }
133 
134 /**
135   @brief View a field of a CeedOperator
136 
137   @param[in] field         Operator field to view
138   @param[in] qf_field      QFunction field (carries field name)
139   @param[in] field_number  Number of field being viewed
140   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
141   @param[in] input         true for an input field; false for output field
142   @param[in] stream        Stream to view to, e.g., stdout
143 
144   @return An error code: 0 - success, otherwise - failure
145 
146   @ref Utility
147 **/
148 static int CeedOperatorFieldView(CeedOperatorField field,
149                                  CeedQFunctionField qf_field,
150                                  CeedInt field_number, bool sub, bool input,
151                                  FILE *stream) {
152   const char *pre = sub ? "  " : "";
153   const char *in_out = input ? "Input" : "Output";
154 
155   fprintf(stream, "%s    %s Field [%d]:\n"
156           "%s      Name: \"%s\"\n",
157           pre, in_out, field_number, pre, qf_field->field_name);
158 
159   if (field->basis == CEED_BASIS_COLLOCATED)
160     fprintf(stream, "%s      Collocated basis\n", pre);
161 
162   if (field->vec == CEED_VECTOR_ACTIVE)
163     fprintf(stream, "%s      Active vector\n", pre);
164   else if (field->vec == CEED_VECTOR_NONE)
165     fprintf(stream, "%s      No vector\n", pre);
166   return CEED_ERROR_SUCCESS;
167 }
168 
169 /**
170   @brief View a single CeedOperator
171 
172   @param[in] op      CeedOperator to view
173   @param[in] sub     Boolean flag for sub-operator
174   @param[in] stream  Stream to write; typically stdout/stderr or a file
175 
176   @return Error code: 0 - success, otherwise - failure
177 
178   @ref Utility
179 **/
180 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
181   int ierr;
182   const char *pre = sub ? "  " : "";
183 
184   CeedInt total_fields = 0;
185   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
186 
187   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
188           total_fields>1 ? "s" : "");
189 
190   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
191           op->qf->num_input_fields>1 ? "s" : "");
192   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
193     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
194                                  i, sub, 1, stream); CeedChk(ierr);
195   }
196 
197   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
198           op->qf->num_output_fields>1 ? "s" : "");
199   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
200     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
201                                  i, sub, 0, stream); CeedChk(ierr);
202   }
203   return CEED_ERROR_SUCCESS;
204 }
205 
206 /**
207   @brief Find the active vector basis for a CeedOperator
208 
209   @param[in] op             CeedOperator to find active basis for
210   @param[out] active_basis  Basis for active input vector
211 
212   @return An error code: 0 - success, otherwise - failure
213 
214   @ ref Developer
215 **/
216 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
217   *active_basis = NULL;
218   for (int i = 0; i < op->qf->num_input_fields; i++)
219     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
220       *active_basis = op->input_fields[i]->basis;
221       break;
222     }
223 
224   if (!*active_basis) {
225     // LCOV_EXCL_START
226     int ierr;
227     Ceed ceed;
228     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
229     return CeedError(ceed, CEED_ERROR_MINOR,
230                      "No active CeedBasis found");
231     // LCOV_EXCL_STOP
232   }
233   return CEED_ERROR_SUCCESS;
234 }
235 
236 /**
237   @brief Find the active vector ElemRestriction for a CeedOperator
238 
239   @param[in] op            CeedOperator to find active basis for
240   @param[out] active_rstr  ElemRestriction for active input vector
241 
242   @return An error code: 0 - success, otherwise - failure
243 
244   @ref Utility
245 **/
246 int CeedOperatorGetActiveElemRestriction(CeedOperator op,
247     CeedElemRestriction *active_rstr) {
248   *active_rstr = NULL;
249   for (int i = 0; i < op->qf->num_input_fields; i++)
250     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
251       *active_rstr = op->input_fields[i]->elem_restr;
252       break;
253     }
254 
255   if (!*active_rstr) {
256     // LCOV_EXCL_START
257     int ierr;
258     Ceed ceed;
259     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
260     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
261                      "No active CeedElemRestriction found");
262     // LCOV_EXCL_STOP
263   }
264   return CEED_ERROR_SUCCESS;
265 }
266 
267 /**
268   @brief Set QFunctionContext field value of the specified type.
269            For composite operators, the value is set in all
270            sub-operator QFunctionContexts that have a matching `field_name`.
271            A non-zero error code is returned for single operators
272            that do not have a matching field of the same type or composite
273            operators that do not have any field of a matching type.
274 
275   @param op         CeedOperator
276   @param field_name Name of field to set
277   @param field_type Type of field to set
278   @param value      Value to set
279 
280   @return An error code: 0 - success, otherwise - failure
281 
282   @ref User
283 **/
284 static int CeedOperatorContextSetGeneric(CeedOperator op,
285     const char *field_name, CeedContextFieldType field_type, void *value) {
286   int ierr;
287   bool is_set = false, is_composite = false;
288 
289   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
290 
291   if (is_composite) {
292     CeedInt num_sub;
293     CeedOperator *sub_operators;
294 
295     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
296     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
297 
298     for (CeedInt i = 0; i < num_sub; i++) {
299       // Try every sub-operator, ok if some sub-operators do not have field
300       if (sub_operators[i]->qf->ctx) {
301         bool is_set_i = false;
302         ierr = CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx, field_name,
303                                               field_type, &is_set_i, value);
304         CeedChk(ierr);
305         is_set = is_set || is_set_i;
306       }
307     }
308   } else {
309     if (!op->qf->ctx)
310       // LCOV_EXCL_START
311       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
312                        "QFunction does not have context data");
313     // LCOV_EXCL_STOP
314 
315     ierr = CeedQFunctionContextSetGeneric(op->qf->ctx, field_name,
316                                           field_type, &is_set, value); CeedChk(ierr);
317   }
318 
319   if (!is_set)
320     // LCOV_EXCL_START
321     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
322                      "QFunctionContext field with name \"%s\" not registered",
323                      field_name);
324   // LCOV_EXCL_STOP
325 
326   return CEED_ERROR_SUCCESS;
327 }
328 
329 /// @}
330 
331 /// ----------------------------------------------------------------------------
332 /// CeedOperator Backend API
333 /// ----------------------------------------------------------------------------
334 /// @addtogroup CeedOperatorBackend
335 /// @{
336 
337 /**
338   @brief Get the number of arguments associated with a CeedOperator
339 
340   @param op             CeedOperator
341   @param[out] num_args  Variable to store vector number of arguments
342 
343   @return An error code: 0 - success, otherwise - failure
344 
345   @ref Backend
346 **/
347 
348 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
349   if (op->is_composite)
350     // LCOV_EXCL_START
351     return CeedError(op->ceed, CEED_ERROR_MINOR,
352                      "Not defined for composite operators");
353   // LCOV_EXCL_STOP
354 
355   *num_args = op->num_fields;
356   return CEED_ERROR_SUCCESS;
357 }
358 
359 /**
360   @brief Get the setup status of a CeedOperator
361 
362   @param op                  CeedOperator
363   @param[out] is_setup_done  Variable to store setup status
364 
365   @return An error code: 0 - success, otherwise - failure
366 
367   @ref Backend
368 **/
369 
370 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
371   *is_setup_done = op->is_backend_setup;
372   return CEED_ERROR_SUCCESS;
373 }
374 
375 /**
376   @brief Get the QFunction associated with a CeedOperator
377 
378   @param op       CeedOperator
379   @param[out] qf  Variable to store QFunction
380 
381   @return An error code: 0 - success, otherwise - failure
382 
383   @ref Backend
384 **/
385 
386 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
387   if (op->is_composite)
388     // LCOV_EXCL_START
389     return CeedError(op->ceed, CEED_ERROR_MINOR,
390                      "Not defined for composite operator");
391   // LCOV_EXCL_STOP
392 
393   *qf = op->qf;
394   return CEED_ERROR_SUCCESS;
395 }
396 
397 /**
398   @brief Get a boolean value indicating if the CeedOperator is composite
399 
400   @param op                 CeedOperator
401   @param[out] is_composite  Variable to store composite status
402 
403   @return An error code: 0 - success, otherwise - failure
404 
405   @ref Backend
406 **/
407 
408 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
409   *is_composite = op->is_composite;
410   return CEED_ERROR_SUCCESS;
411 }
412 
413 /**
414   @brief Get the number of sub_operators associated with a CeedOperator
415 
416   @param op                     CeedOperator
417   @param[out] num_suboperators  Variable to store number of sub_operators
418 
419   @return An error code: 0 - success, otherwise - failure
420 
421   @ref Backend
422 **/
423 
424 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
425   if (!op->is_composite)
426     // LCOV_EXCL_START
427     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
428   // LCOV_EXCL_STOP
429 
430   *num_suboperators = op->num_suboperators;
431   return CEED_ERROR_SUCCESS;
432 }
433 
434 /**
435   @brief Get the list of sub_operators associated with a CeedOperator
436 
437   @param op                  CeedOperator
438   @param[out] sub_operators  Variable to store list of sub_operators
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref Backend
443 **/
444 
445 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
446   if (!op->is_composite)
447     // LCOV_EXCL_START
448     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
449   // LCOV_EXCL_STOP
450 
451   *sub_operators = op->sub_operators;
452   return CEED_ERROR_SUCCESS;
453 }
454 
455 /**
456   @brief Get the backend data of a CeedOperator
457 
458   @param op         CeedOperator
459   @param[out] data  Variable to store data
460 
461   @return An error code: 0 - success, otherwise - failure
462 
463   @ref Backend
464 **/
465 
466 int CeedOperatorGetData(CeedOperator op, void *data) {
467   *(void **)data = op->data;
468   return CEED_ERROR_SUCCESS;
469 }
470 
471 /**
472   @brief Set the backend data of a CeedOperator
473 
474   @param[out] op  CeedOperator
475   @param data     Data to set
476 
477   @return An error code: 0 - success, otherwise - failure
478 
479   @ref Backend
480 **/
481 
482 int CeedOperatorSetData(CeedOperator op, void *data) {
483   op->data = data;
484   return CEED_ERROR_SUCCESS;
485 }
486 
487 /**
488   @brief Increment the reference counter for a CeedOperator
489 
490   @param op  CeedOperator to increment the reference counter
491 
492   @return An error code: 0 - success, otherwise - failure
493 
494   @ref Backend
495 **/
496 int CeedOperatorReference(CeedOperator op) {
497   op->ref_count++;
498   return CEED_ERROR_SUCCESS;
499 }
500 
501 /**
502   @brief Set the setup flag of a CeedOperator to True
503 
504   @param op  CeedOperator
505 
506   @return An error code: 0 - success, otherwise - failure
507 
508   @ref Backend
509 **/
510 
511 int CeedOperatorSetSetupDone(CeedOperator op) {
512   op->is_backend_setup = true;
513   return CEED_ERROR_SUCCESS;
514 }
515 
516 /// @}
517 
518 /// ----------------------------------------------------------------------------
519 /// CeedOperator Public API
520 /// ----------------------------------------------------------------------------
521 /// @addtogroup CeedOperatorUser
522 /// @{
523 
524 /**
525   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
526            CeedElemRestriction can be associated with CeedQFunction fields with
527            \ref CeedOperatorSetField.
528 
529   @param ceed     A Ceed object where the CeedOperator will be created
530   @param qf       QFunction defining the action of the operator at quadrature points
531   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
532                     @ref CEED_QFUNCTION_NONE)
533   @param dqfT     QFunction defining the action of the transpose of the Jacobian
534                     of @a qf (or @ref CEED_QFUNCTION_NONE)
535   @param[out] op  Address of the variable where the newly created
536                     CeedOperator will be stored
537 
538   @return An error code: 0 - success, otherwise - failure
539 
540   @ref User
541  */
542 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
543                        CeedQFunction dqfT, CeedOperator *op) {
544   int ierr;
545 
546   if (!ceed->OperatorCreate) {
547     Ceed delegate;
548     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
549 
550     if (!delegate)
551       // LCOV_EXCL_START
552       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
553                        "Backend does not support OperatorCreate");
554     // LCOV_EXCL_STOP
555 
556     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
557     return CEED_ERROR_SUCCESS;
558   }
559 
560   if (!qf || qf == CEED_QFUNCTION_NONE)
561     // LCOV_EXCL_START
562     return CeedError(ceed, CEED_ERROR_MINOR,
563                      "Operator must have a valid QFunction.");
564   // LCOV_EXCL_STOP
565   ierr = CeedCalloc(1, op); CeedChk(ierr);
566   (*op)->ceed = ceed;
567   ierr = CeedReference(ceed); CeedChk(ierr);
568   (*op)->ref_count = 1;
569   (*op)->qf = qf;
570   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
571   if (dqf && dqf != CEED_QFUNCTION_NONE) {
572     (*op)->dqf = dqf;
573     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
574   }
575   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
576     (*op)->dqfT = dqfT;
577     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
578   }
579   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields); CeedChk(ierr);
580   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields); CeedChk(ierr);
581   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
582   return CEED_ERROR_SUCCESS;
583 }
584 
585 /**
586   @brief Create an operator that composes the action of several operators
587 
588   @param ceed     A Ceed object where the CeedOperator will be created
589   @param[out] op  Address of the variable where the newly created
590                     Composite CeedOperator will be stored
591 
592   @return An error code: 0 - success, otherwise - failure
593 
594   @ref User
595  */
596 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
597   int ierr;
598 
599   if (!ceed->CompositeOperatorCreate) {
600     Ceed delegate;
601     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
602 
603     if (delegate) {
604       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
605       return CEED_ERROR_SUCCESS;
606     }
607   }
608 
609   ierr = CeedCalloc(1, op); CeedChk(ierr);
610   (*op)->ceed = ceed;
611   ierr = CeedReference(ceed); CeedChk(ierr);
612   (*op)->is_composite = true;
613   ierr = CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators); CeedChk(ierr);
614 
615   if (ceed->CompositeOperatorCreate) {
616     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
617   }
618   return CEED_ERROR_SUCCESS;
619 }
620 
621 /**
622   @brief Copy the pointer to a CeedOperator. Both pointers should
623            be destroyed with `CeedOperatorDestroy()`;
624            Note: If `*op_copy` is non-NULL, then it is assumed that
625            `*op_copy` is a pointer to a CeedOperator. This
626            CeedOperator will be destroyed if `*op_copy` is the only
627            reference to this CeedOperator.
628 
629   @param op            CeedOperator to copy reference to
630   @param[out] op_copy  Variable to store copied reference
631 
632   @return An error code: 0 - success, otherwise - failure
633 
634   @ref User
635 **/
636 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
637   int ierr;
638 
639   ierr = CeedOperatorReference(op); CeedChk(ierr);
640   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
641   *op_copy = op;
642   return CEED_ERROR_SUCCESS;
643 }
644 
645 /**
646   @brief Provide a field to a CeedOperator for use by its CeedQFunction
647 
648   This function is used to specify both active and passive fields to a
649   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
650   fields can inputs or outputs (updated in-place when operator is applied).
651 
652   Active fields must be specified using this function, but their data (in a
653   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
654   input and at most one active output.
655 
656   @param op          CeedOperator on which to provide the field
657   @param field_name  Name of the field (to be matched with the name used by
658                        CeedQFunction)
659   @param r           CeedElemRestriction
660   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
661                        if collocated with quadrature points
662   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
663                        if field is active or @ref CEED_VECTOR_NONE if using
664                        @ref CEED_EVAL_WEIGHT in the QFunction
665 
666   @return An error code: 0 - success, otherwise - failure
667 
668   @ref User
669 **/
670 int CeedOperatorSetField(CeedOperator op, const char *field_name,
671                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
672   int ierr;
673   if (op->is_composite)
674     // LCOV_EXCL_START
675     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
676                      "Cannot add field to composite operator.");
677   // LCOV_EXCL_STOP
678   if (op->is_immutable)
679     // LCOV_EXCL_START
680     return CeedError(op->ceed, CEED_ERROR_MAJOR,
681                      "Operator cannot be changed after set as immutable");
682   // LCOV_EXCL_STOP
683   if (!r)
684     // LCOV_EXCL_START
685     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
686                      "ElemRestriction r for field \"%s\" must be non-NULL.",
687                      field_name);
688   // LCOV_EXCL_STOP
689   if (!b)
690     // LCOV_EXCL_START
691     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
692                      "Basis b for field \"%s\" must be non-NULL.",
693                      field_name);
694   // LCOV_EXCL_STOP
695   if (!v)
696     // LCOV_EXCL_START
697     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
698                      "Vector v for field \"%s\" must be non-NULL.",
699                      field_name);
700   // LCOV_EXCL_STOP
701 
702   CeedInt num_elem;
703   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
704   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
705       op->num_elem != num_elem)
706     // LCOV_EXCL_START
707     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
708                      "ElemRestriction with %d elements incompatible with prior "
709                      "%d elements", num_elem, op->num_elem);
710   // LCOV_EXCL_STOP
711 
712   CeedInt num_qpts = 0;
713   if (b != CEED_BASIS_COLLOCATED) {
714     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
715     if (op->num_qpts && op->num_qpts != num_qpts)
716       // LCOV_EXCL_START
717       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
718                        "Basis with %d quadrature points "
719                        "incompatible with prior %d points", num_qpts,
720                        op->num_qpts);
721     // LCOV_EXCL_STOP
722   }
723   CeedQFunctionField qf_field;
724   CeedOperatorField *op_field;
725   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
726     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
727       qf_field = op->qf->input_fields[i];
728       op_field = &op->input_fields[i];
729       goto found;
730     }
731   }
732   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
733     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
734       qf_field = op->qf->output_fields[i];
735       op_field = &op->output_fields[i];
736       goto found;
737     }
738   }
739   // LCOV_EXCL_START
740   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
741                    "QFunction has no knowledge of field '%s'",
742                    field_name);
743   // LCOV_EXCL_STOP
744 found:
745   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
746   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
747 
748   (*op_field)->vec = v;
749   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
750     ierr = CeedVectorReference(v); CeedChk(ierr);
751   }
752 
753   (*op_field)->elem_restr = r;
754   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
755   if (r != CEED_ELEMRESTRICTION_NONE) {
756     op->num_elem = num_elem;
757     op->has_restriction = true; // Restriction set, but num_elem may be 0
758   }
759 
760   (*op_field)->basis = b;
761   if (b != CEED_BASIS_COLLOCATED) {
762     if (!op->num_qpts) {
763       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
764     }
765     ierr = CeedBasisReference(b); CeedChk(ierr);
766   }
767 
768   op->num_fields += 1;
769   ierr = CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name);
770   CeedChk(ierr);
771   return CEED_ERROR_SUCCESS;
772 }
773 
774 /**
775   @brief Get the CeedOperatorFields of a CeedOperator
776 
777   Note: Calling this function asserts that setup is complete
778           and sets the CeedOperator as immutable.
779 
780   @param op                      CeedOperator
781   @param[out] num_input_fields   Variable to store number of input fields
782   @param[out] input_fields       Variable to store input_fields
783   @param[out] num_output_fields  Variable to store number of output fields
784   @param[out] output_fields      Variable to store output_fields
785 
786   @return An error code: 0 - success, otherwise - failure
787 
788   @ref Advanced
789 **/
790 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
791                           CeedOperatorField **input_fields,
792                           CeedInt *num_output_fields,
793                           CeedOperatorField **output_fields) {
794   int ierr;
795 
796   if (op->is_composite)
797     // LCOV_EXCL_START
798     return CeedError(op->ceed, CEED_ERROR_MINOR,
799                      "Not defined for composite operator");
800   // LCOV_EXCL_STOP
801   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
802 
803   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
804   if (input_fields) *input_fields = op->input_fields;
805   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
806   if (output_fields) *output_fields = op->output_fields;
807   return CEED_ERROR_SUCCESS;
808 }
809 
810 /**
811   @brief Get the name of a CeedOperatorField
812 
813   @param op_field         CeedOperatorField
814   @param[out] field_name  Variable to store the field name
815 
816   @return An error code: 0 - success, otherwise - failure
817 
818   @ref Advanced
819 **/
820 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
821   *field_name = (char *)op_field->field_name;
822   return CEED_ERROR_SUCCESS;
823 }
824 
825 /**
826   @brief Get the CeedElemRestriction of a CeedOperatorField
827 
828   @param op_field   CeedOperatorField
829   @param[out] rstr  Variable to store CeedElemRestriction
830 
831   @return An error code: 0 - success, otherwise - failure
832 
833   @ref Advanced
834 **/
835 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
836                                         CeedElemRestriction *rstr) {
837   *rstr = op_field->elem_restr;
838   return CEED_ERROR_SUCCESS;
839 }
840 
841 /**
842   @brief Get the CeedBasis of a CeedOperatorField
843 
844   @param op_field    CeedOperatorField
845   @param[out] basis  Variable to store CeedBasis
846 
847   @return An error code: 0 - success, otherwise - failure
848 
849   @ref Advanced
850 **/
851 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
852   *basis = op_field->basis;
853   return CEED_ERROR_SUCCESS;
854 }
855 
856 /**
857   @brief Get the CeedVector of a CeedOperatorField
858 
859   @param op_field  CeedOperatorField
860   @param[out] vec  Variable to store CeedVector
861 
862   @return An error code: 0 - success, otherwise - failure
863 
864   @ref Advanced
865 **/
866 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
867   *vec = op_field->vec;
868   return CEED_ERROR_SUCCESS;
869 }
870 
871 /**
872   @brief Add a sub-operator to a composite CeedOperator
873 
874   @param[out] composite_op  Composite CeedOperator
875   @param      sub_op        Sub-operator CeedOperator
876 
877   @return An error code: 0 - success, otherwise - failure
878 
879   @ref User
880  */
881 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
882                                 CeedOperator sub_op) {
883   int ierr;
884 
885   if (!composite_op->is_composite)
886     // LCOV_EXCL_START
887     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
888                      "CeedOperator is not a composite operator");
889   // LCOV_EXCL_STOP
890 
891   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
892     // LCOV_EXCL_START
893     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
894                      "Cannot add additional sub_operators");
895   // LCOV_EXCL_STOP
896   if (composite_op->is_immutable)
897     // LCOV_EXCL_START
898     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
899                      "Operator cannot be changed after set as immutable");
900   // LCOV_EXCL_STOP
901 
902   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
903   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
904   composite_op->num_suboperators++;
905   return CEED_ERROR_SUCCESS;
906 }
907 
908 /**
909   @brief Check if a CeedOperator is ready to be used.
910 
911   @param[in] op  CeedOperator to check
912 
913   @return An error code: 0 - success, otherwise - failure
914 
915   @ref User
916 **/
917 int CeedOperatorCheckReady(CeedOperator op) {
918   int ierr;
919   Ceed ceed;
920   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
921 
922   if (op->is_interface_setup)
923     return CEED_ERROR_SUCCESS;
924 
925   CeedQFunction qf = op->qf;
926   if (op->is_composite) {
927     if (!op->num_suboperators)
928       // LCOV_EXCL_START
929       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
930     // LCOV_EXCL_STOP
931     for (CeedInt i = 0; i < op->num_suboperators; i++) {
932       ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
933     }
934   } else {
935     if (op->num_fields == 0)
936       // LCOV_EXCL_START
937       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
938     // LCOV_EXCL_STOP
939     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
940       // LCOV_EXCL_START
941       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
942     // LCOV_EXCL_STOP
943     if (!op->has_restriction)
944       // LCOV_EXCL_START
945       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
946                        "At least one restriction required");
947     // LCOV_EXCL_STOP
948     if (op->num_qpts == 0)
949       // LCOV_EXCL_START
950       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
951                        "At least one non-collocated basis is required "
952                        "or the number of quadrature points must be set");
953     // LCOV_EXCL_STOP
954   }
955 
956   // Flag as immutable and ready
957   op->is_interface_setup = true;
958   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
959     // LCOV_EXCL_START
960     op->qf->is_immutable = true;
961   // LCOV_EXCL_STOP
962   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
963     // LCOV_EXCL_START
964     op->dqf->is_immutable = true;
965   // LCOV_EXCL_STOP
966   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
967     // LCOV_EXCL_START
968     op->dqfT->is_immutable = true;
969   // LCOV_EXCL_STOP
970   return CEED_ERROR_SUCCESS;
971 }
972 
973 /**
974   @brief Set the number of quadrature points associated with a CeedOperator.
975            This should be used when creating a CeedOperator where every
976            field has a collocated basis. This function cannot be used for
977            composite CeedOperators.
978 
979   @param op        CeedOperator
980   @param num_qpts  Number of quadrature points to set
981 
982   @return An error code: 0 - success, otherwise - failure
983 
984   @ref Advanced
985 **/
986 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
987   if (op->is_composite)
988     // LCOV_EXCL_START
989     return CeedError(op->ceed, CEED_ERROR_MINOR,
990                      "Not defined for composite operator");
991   // LCOV_EXCL_STOP
992   if (op->num_qpts)
993     // LCOV_EXCL_START
994     return CeedError(op->ceed, CEED_ERROR_MINOR,
995                      "Number of quadrature points already defined");
996   // LCOV_EXCL_STOP
997   if (op->is_immutable)
998     // LCOV_EXCL_START
999     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1000                      "Operator cannot be changed after set as immutable");
1001   // LCOV_EXCL_STOP
1002 
1003   op->num_qpts = num_qpts;
1004   return CEED_ERROR_SUCCESS;
1005 }
1006 
1007 /**
1008   @brief View a CeedOperator
1009 
1010   @param[in] op      CeedOperator to view
1011   @param[in] stream  Stream to write; typically stdout/stderr or a file
1012 
1013   @return Error code: 0 - success, otherwise - failure
1014 
1015   @ref User
1016 **/
1017 int CeedOperatorView(CeedOperator op, FILE *stream) {
1018   int ierr;
1019 
1020   if (op->is_composite) {
1021     fprintf(stream, "Composite CeedOperator\n");
1022 
1023     for (CeedInt i=0; i<op->num_suboperators; i++) {
1024       fprintf(stream, "  SubOperator [%d]:\n", i);
1025       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1026       CeedChk(ierr);
1027     }
1028   } else {
1029     fprintf(stream, "CeedOperator\n");
1030     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1031   }
1032   return CEED_ERROR_SUCCESS;
1033 }
1034 
1035 /**
1036   @brief Get the Ceed associated with a CeedOperator
1037 
1038   @param op         CeedOperator
1039   @param[out] ceed  Variable to store Ceed
1040 
1041   @return An error code: 0 - success, otherwise - failure
1042 
1043   @ref Advanced
1044 **/
1045 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1046   *ceed = op->ceed;
1047   return CEED_ERROR_SUCCESS;
1048 }
1049 
1050 /**
1051   @brief Get the number of elements associated with a CeedOperator
1052 
1053   @param op             CeedOperator
1054   @param[out] num_elem  Variable to store number of elements
1055 
1056   @return An error code: 0 - success, otherwise - failure
1057 
1058   @ref Advanced
1059 **/
1060 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1061   if (op->is_composite)
1062     // LCOV_EXCL_START
1063     return CeedError(op->ceed, CEED_ERROR_MINOR,
1064                      "Not defined for composite operator");
1065   // LCOV_EXCL_STOP
1066 
1067   *num_elem = op->num_elem;
1068   return CEED_ERROR_SUCCESS;
1069 }
1070 
1071 /**
1072   @brief Get the number of quadrature points associated with a CeedOperator
1073 
1074   @param op             CeedOperator
1075   @param[out] num_qpts  Variable to store vector number of quadrature points
1076 
1077   @return An error code: 0 - success, otherwise - failure
1078 
1079   @ref Advanced
1080 **/
1081 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1082   if (op->is_composite)
1083     // LCOV_EXCL_START
1084     return CeedError(op->ceed, CEED_ERROR_MINOR,
1085                      "Not defined for composite operator");
1086   // LCOV_EXCL_STOP
1087 
1088   *num_qpts = op->num_qpts;
1089   return CEED_ERROR_SUCCESS;
1090 }
1091 
1092 /**
1093   @brief Set QFunctionContext field holding a double precision value.
1094            For composite operators, the value is set in all
1095            sub-operator QFunctionContexts that have a matching `field_name`.
1096 
1097   @param op         CeedOperator
1098   @param field_name Name of field to register
1099   @param value      Value to set
1100 
1101   @return An error code: 0 - success, otherwise - failure
1102 
1103   @ref User
1104 **/
1105 int CeedOperatorContextSetDouble(CeedOperator op, const char *field_name,
1106                                  double value) {
1107   return CeedOperatorContextSetGeneric(op, field_name, CEED_CONTEXT_FIELD_DOUBLE,
1108                                        &value);
1109 }
1110 
1111 /**
1112   @brief Set QFunctionContext field holding an int32 value.
1113            For composite operators, the value is set in all
1114            sub-operator QFunctionContexts that have a matching `field_name`.
1115 
1116   @param op         CeedOperator
1117   @param field_name Name of field to set
1118   @param value      Value to set
1119 
1120   @return An error code: 0 - success, otherwise - failure
1121 
1122   @ref User
1123 **/
1124 int CeedOperatorContextSetInt32(CeedOperator op, const char *field_name,
1125                                 int value) {
1126   return CeedOperatorContextSetGeneric(op, field_name, CEED_CONTEXT_FIELD_INT32,
1127                                        &value);
1128   return CEED_ERROR_SUCCESS;
1129 }
1130 
1131 /**
1132   @brief Apply CeedOperator to a vector
1133 
1134   This computes the action of the operator on the specified (active) input,
1135   yielding its (active) output.  All inputs and outputs must be specified using
1136   CeedOperatorSetField().
1137 
1138   Note: Calling this function asserts that setup is complete
1139           and sets the CeedOperator as immutable.
1140 
1141   @param op        CeedOperator to apply
1142   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1143                      there are no active inputs
1144   @param[out] out  CeedVector to store result of applying operator (must be
1145                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1146                      active outputs
1147   @param request   Address of CeedRequest for non-blocking completion, else
1148                      @ref CEED_REQUEST_IMMEDIATE
1149 
1150   @return An error code: 0 - success, otherwise - failure
1151 
1152   @ref User
1153 **/
1154 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1155                       CeedRequest *request) {
1156   int ierr;
1157   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1158 
1159   if (op->num_elem)  {
1160     // Standard Operator
1161     if (op->Apply) {
1162       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1163     } else {
1164       // Zero all output vectors
1165       CeedQFunction qf = op->qf;
1166       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1167         CeedVector vec = op->output_fields[i]->vec;
1168         if (vec == CEED_VECTOR_ACTIVE)
1169           vec = out;
1170         if (vec != CEED_VECTOR_NONE) {
1171           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1172         }
1173       }
1174       // Apply
1175       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1176     }
1177   } else if (op->is_composite) {
1178     // Composite Operator
1179     if (op->ApplyComposite) {
1180       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1181     } else {
1182       CeedInt num_suboperators;
1183       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1184       CeedOperator *sub_operators;
1185       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1186 
1187       // Zero all output vectors
1188       if (out != CEED_VECTOR_NONE) {
1189         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1190       }
1191       for (CeedInt i=0; i<num_suboperators; i++) {
1192         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1193           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1194           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1195             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1196           }
1197         }
1198       }
1199       // Apply
1200       for (CeedInt i=0; i<op->num_suboperators; i++) {
1201         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1202         CeedChk(ierr);
1203       }
1204     }
1205   }
1206   return CEED_ERROR_SUCCESS;
1207 }
1208 
1209 /**
1210   @brief Apply CeedOperator to a vector and add result to output vector
1211 
1212   This computes the action of the operator on the specified (active) input,
1213   yielding its (active) output.  All inputs and outputs must be specified using
1214   CeedOperatorSetField().
1215 
1216   @param op        CeedOperator to apply
1217   @param[in] in    CeedVector containing input state or NULL if there are no
1218                      active inputs
1219   @param[out] out  CeedVector to sum in result of applying operator (must be
1220                      distinct from @a in) or NULL if there are no active outputs
1221   @param request   Address of CeedRequest for non-blocking completion, else
1222                      @ref CEED_REQUEST_IMMEDIATE
1223 
1224   @return An error code: 0 - success, otherwise - failure
1225 
1226   @ref User
1227 **/
1228 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1229                          CeedRequest *request) {
1230   int ierr;
1231   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1232 
1233   if (op->num_elem)  {
1234     // Standard Operator
1235     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1236   } else if (op->is_composite) {
1237     // Composite Operator
1238     if (op->ApplyAddComposite) {
1239       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1240     } else {
1241       CeedInt num_suboperators;
1242       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1243       CeedOperator *sub_operators;
1244       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1245 
1246       for (CeedInt i=0; i<num_suboperators; i++) {
1247         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1248         CeedChk(ierr);
1249       }
1250     }
1251   }
1252   return CEED_ERROR_SUCCESS;
1253 }
1254 
1255 /**
1256   @brief Destroy a CeedOperator
1257 
1258   @param op  CeedOperator to destroy
1259 
1260   @return An error code: 0 - success, otherwise - failure
1261 
1262   @ref User
1263 **/
1264 int CeedOperatorDestroy(CeedOperator *op) {
1265   int ierr;
1266 
1267   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1268   if ((*op)->Destroy) {
1269     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1270   }
1271   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1272   // Free fields
1273   for (int i=0; i<(*op)->num_fields; i++)
1274     if ((*op)->input_fields[i]) {
1275       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1276         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1277         CeedChk(ierr);
1278       }
1279       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1280         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1281       }
1282       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1283           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1284         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1285       }
1286       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1287       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1288     }
1289   for (int i=0; i<(*op)->num_fields; i++)
1290     if ((*op)->output_fields[i]) {
1291       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1292       CeedChk(ierr);
1293       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1294         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1295       }
1296       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1297           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1298         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1299       }
1300       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1301       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1302     }
1303   // Destroy sub_operators
1304   for (int i=0; i<(*op)->num_suboperators; i++)
1305     if ((*op)->sub_operators[i]) {
1306       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1307     }
1308   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1309   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1310   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1311 
1312   // Destroy fallback
1313   if ((*op)->op_fallback) {
1314     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1315     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1316     ierr = CeedVectorDestroy(&(*op)->op_fallback->qf_assembled); CeedChk(ierr);
1317     ierr = CeedElemRestrictionDestroy(&(*op)->op_fallback->qf_assembled_rstr);
1318     CeedChk(ierr);
1319     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1320     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1321   }
1322 
1323   // Destroy QF assembly cache
1324   ierr = CeedVectorDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1325   ierr = CeedElemRestrictionDestroy(&(*op)->qf_assembled_rstr); CeedChk(ierr);
1326 
1327   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1328   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1329   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1330   ierr = CeedFree(op); CeedChk(ierr);
1331   return CEED_ERROR_SUCCESS;
1332 }
1333 
1334 /// @}
1335