xref: /libCEED/interface/ceed-operator.c (revision cd88d23c377cb39cade98b31cad31ac580d0d240)
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_label Label 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     CeedContextFieldLabel field_label, CeedContextFieldType field_type,
286     void *value) {
287   int ierr;
288 
289   if (!field_label)
290     // LCOV_EXCL_START
291     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
292                      "Invalid field label");
293   // LCOV_EXCL_STOP
294 
295   bool is_composite = false;
296   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
297   if (is_composite) {
298     CeedInt num_sub;
299     CeedOperator *sub_operators;
300 
301     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
302     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
303     if (num_sub != field_label->num_sub_labels)
304       // LCOV_EXCL_START
305       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
306                        "ContextLabel does not correspond to composite operator.\n"
307                        "Use CeedOperatorGetContextFieldLabel().");
308     // LCOV_EXCL_STOP
309 
310     for (CeedInt i = 0; i < num_sub; i++) {
311       // Try every sub-operator, ok if some sub-operators do not have field
312       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
313         ierr = CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx,
314                                               field_label->sub_labels[i],
315                                               field_type, value); CeedChk(ierr);
316       }
317     }
318   } else {
319     if (!op->qf->ctx)
320       // LCOV_EXCL_START
321       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
322                        "QFunction does not have context data");
323     // LCOV_EXCL_STOP
324 
325     ierr = CeedQFunctionContextSetGeneric(op->qf->ctx, field_label,
326                                           field_type, value); CeedChk(ierr);
327   }
328 
329   return CEED_ERROR_SUCCESS;
330 }
331 
332 /// @}
333 
334 /// ----------------------------------------------------------------------------
335 /// CeedOperator Backend API
336 /// ----------------------------------------------------------------------------
337 /// @addtogroup CeedOperatorBackend
338 /// @{
339 
340 /**
341   @brief Get the number of arguments associated with a CeedOperator
342 
343   @param op             CeedOperator
344   @param[out] num_args  Variable to store vector number of arguments
345 
346   @return An error code: 0 - success, otherwise - failure
347 
348   @ref Backend
349 **/
350 
351 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
352   if (op->is_composite)
353     // LCOV_EXCL_START
354     return CeedError(op->ceed, CEED_ERROR_MINOR,
355                      "Not defined for composite operators");
356   // LCOV_EXCL_STOP
357 
358   *num_args = op->num_fields;
359   return CEED_ERROR_SUCCESS;
360 }
361 
362 /**
363   @brief Get the setup status of a CeedOperator
364 
365   @param op                  CeedOperator
366   @param[out] is_setup_done  Variable to store setup status
367 
368   @return An error code: 0 - success, otherwise - failure
369 
370   @ref Backend
371 **/
372 
373 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
374   *is_setup_done = op->is_backend_setup;
375   return CEED_ERROR_SUCCESS;
376 }
377 
378 /**
379   @brief Get the QFunction associated with a CeedOperator
380 
381   @param op       CeedOperator
382   @param[out] qf  Variable to store QFunction
383 
384   @return An error code: 0 - success, otherwise - failure
385 
386   @ref Backend
387 **/
388 
389 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
390   if (op->is_composite)
391     // LCOV_EXCL_START
392     return CeedError(op->ceed, CEED_ERROR_MINOR,
393                      "Not defined for composite operator");
394   // LCOV_EXCL_STOP
395 
396   *qf = op->qf;
397   return CEED_ERROR_SUCCESS;
398 }
399 
400 /**
401   @brief Get a boolean value indicating if the CeedOperator is composite
402 
403   @param op                 CeedOperator
404   @param[out] is_composite  Variable to store composite status
405 
406   @return An error code: 0 - success, otherwise - failure
407 
408   @ref Backend
409 **/
410 
411 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
412   *is_composite = op->is_composite;
413   return CEED_ERROR_SUCCESS;
414 }
415 
416 /**
417   @brief Get the number of sub_operators associated with a CeedOperator
418 
419   @param op                     CeedOperator
420   @param[out] num_suboperators  Variable to store number of sub_operators
421 
422   @return An error code: 0 - success, otherwise - failure
423 
424   @ref Backend
425 **/
426 
427 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
428   if (!op->is_composite)
429     // LCOV_EXCL_START
430     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
431   // LCOV_EXCL_STOP
432 
433   *num_suboperators = op->num_suboperators;
434   return CEED_ERROR_SUCCESS;
435 }
436 
437 /**
438   @brief Get the list of sub_operators associated with a CeedOperator
439 
440   @param op                  CeedOperator
441   @param[out] sub_operators  Variable to store list of sub_operators
442 
443   @return An error code: 0 - success, otherwise - failure
444 
445   @ref Backend
446 **/
447 
448 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
449   if (!op->is_composite)
450     // LCOV_EXCL_START
451     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
452   // LCOV_EXCL_STOP
453 
454   *sub_operators = op->sub_operators;
455   return CEED_ERROR_SUCCESS;
456 }
457 
458 /**
459   @brief Get the backend data of a CeedOperator
460 
461   @param op         CeedOperator
462   @param[out] data  Variable to store data
463 
464   @return An error code: 0 - success, otherwise - failure
465 
466   @ref Backend
467 **/
468 
469 int CeedOperatorGetData(CeedOperator op, void *data) {
470   *(void **)data = op->data;
471   return CEED_ERROR_SUCCESS;
472 }
473 
474 /**
475   @brief Set the backend data of a CeedOperator
476 
477   @param[out] op  CeedOperator
478   @param data     Data to set
479 
480   @return An error code: 0 - success, otherwise - failure
481 
482   @ref Backend
483 **/
484 
485 int CeedOperatorSetData(CeedOperator op, void *data) {
486   op->data = data;
487   return CEED_ERROR_SUCCESS;
488 }
489 
490 /**
491   @brief Increment the reference counter for a CeedOperator
492 
493   @param op  CeedOperator to increment the reference counter
494 
495   @return An error code: 0 - success, otherwise - failure
496 
497   @ref Backend
498 **/
499 int CeedOperatorReference(CeedOperator op) {
500   op->ref_count++;
501   return CEED_ERROR_SUCCESS;
502 }
503 
504 /**
505   @brief Set the setup flag of a CeedOperator to True
506 
507   @param op  CeedOperator
508 
509   @return An error code: 0 - success, otherwise - failure
510 
511   @ref Backend
512 **/
513 
514 int CeedOperatorSetSetupDone(CeedOperator op) {
515   op->is_backend_setup = true;
516   return CEED_ERROR_SUCCESS;
517 }
518 
519 /// @}
520 
521 /// ----------------------------------------------------------------------------
522 /// CeedOperator Public API
523 /// ----------------------------------------------------------------------------
524 /// @addtogroup CeedOperatorUser
525 /// @{
526 
527 /**
528   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
529            CeedElemRestriction can be associated with CeedQFunction fields with
530            \ref CeedOperatorSetField.
531 
532   @param ceed     A Ceed object where the CeedOperator will be created
533   @param qf       QFunction defining the action of the operator at quadrature points
534   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
535                     @ref CEED_QFUNCTION_NONE)
536   @param dqfT     QFunction defining the action of the transpose of the Jacobian
537                     of @a qf (or @ref CEED_QFUNCTION_NONE)
538   @param[out] op  Address of the variable where the newly created
539                     CeedOperator will be stored
540 
541   @return An error code: 0 - success, otherwise - failure
542 
543   @ref User
544  */
545 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
546                        CeedQFunction dqfT, CeedOperator *op) {
547   int ierr;
548 
549   if (!ceed->OperatorCreate) {
550     Ceed delegate;
551     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
552 
553     if (!delegate)
554       // LCOV_EXCL_START
555       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
556                        "Backend does not support OperatorCreate");
557     // LCOV_EXCL_STOP
558 
559     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
560     return CEED_ERROR_SUCCESS;
561   }
562 
563   if (!qf || qf == CEED_QFUNCTION_NONE)
564     // LCOV_EXCL_START
565     return CeedError(ceed, CEED_ERROR_MINOR,
566                      "Operator must have a valid QFunction.");
567   // LCOV_EXCL_STOP
568   ierr = CeedCalloc(1, op); CeedChk(ierr);
569   (*op)->ceed = ceed;
570   ierr = CeedReference(ceed); CeedChk(ierr);
571   (*op)->ref_count = 1;
572   (*op)->qf = qf;
573   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
574   if (dqf && dqf != CEED_QFUNCTION_NONE) {
575     (*op)->dqf = dqf;
576     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
577   }
578   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
579     (*op)->dqfT = dqfT;
580     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
581   }
582   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields); CeedChk(ierr);
583   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields); CeedChk(ierr);
584   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
585   return CEED_ERROR_SUCCESS;
586 }
587 
588 /**
589   @brief Create an operator that composes the action of several operators
590 
591   @param ceed     A Ceed object where the CeedOperator will be created
592   @param[out] op  Address of the variable where the newly created
593                     Composite CeedOperator will be stored
594 
595   @return An error code: 0 - success, otherwise - failure
596 
597   @ref User
598  */
599 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
600   int ierr;
601 
602   if (!ceed->CompositeOperatorCreate) {
603     Ceed delegate;
604     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
605 
606     if (delegate) {
607       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
608       return CEED_ERROR_SUCCESS;
609     }
610   }
611 
612   ierr = CeedCalloc(1, op); CeedChk(ierr);
613   (*op)->ceed = ceed;
614   ierr = CeedReference(ceed); CeedChk(ierr);
615   (*op)->is_composite = true;
616   ierr = CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators); CeedChk(ierr);
617 
618   if (ceed->CompositeOperatorCreate) {
619     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
620   }
621   return CEED_ERROR_SUCCESS;
622 }
623 
624 /**
625   @brief Copy the pointer to a CeedOperator. Both pointers should
626            be destroyed with `CeedOperatorDestroy()`;
627            Note: If `*op_copy` is non-NULL, then it is assumed that
628            `*op_copy` is a pointer to a CeedOperator. This
629            CeedOperator will be destroyed if `*op_copy` is the only
630            reference to this CeedOperator.
631 
632   @param op            CeedOperator to copy reference to
633   @param[out] op_copy  Variable to store copied reference
634 
635   @return An error code: 0 - success, otherwise - failure
636 
637   @ref User
638 **/
639 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
640   int ierr;
641 
642   ierr = CeedOperatorReference(op); CeedChk(ierr);
643   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
644   *op_copy = op;
645   return CEED_ERROR_SUCCESS;
646 }
647 
648 /**
649   @brief Provide a field to a CeedOperator for use by its CeedQFunction
650 
651   This function is used to specify both active and passive fields to a
652   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
653   fields can inputs or outputs (updated in-place when operator is applied).
654 
655   Active fields must be specified using this function, but their data (in a
656   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
657   input and at most one active output.
658 
659   @param op          CeedOperator on which to provide the field
660   @param field_name  Name of the field (to be matched with the name used by
661                        CeedQFunction)
662   @param r           CeedElemRestriction
663   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
664                        if collocated with quadrature points
665   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
666                        if field is active or @ref CEED_VECTOR_NONE if using
667                        @ref CEED_EVAL_WEIGHT in the QFunction
668 
669   @return An error code: 0 - success, otherwise - failure
670 
671   @ref User
672 **/
673 int CeedOperatorSetField(CeedOperator op, const char *field_name,
674                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
675   int ierr;
676   if (op->is_composite)
677     // LCOV_EXCL_START
678     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
679                      "Cannot add field to composite operator.");
680   // LCOV_EXCL_STOP
681   if (op->is_immutable)
682     // LCOV_EXCL_START
683     return CeedError(op->ceed, CEED_ERROR_MAJOR,
684                      "Operator cannot be changed after set as immutable");
685   // LCOV_EXCL_STOP
686   if (!r)
687     // LCOV_EXCL_START
688     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
689                      "ElemRestriction r for field \"%s\" must be non-NULL.",
690                      field_name);
691   // LCOV_EXCL_STOP
692   if (!b)
693     // LCOV_EXCL_START
694     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
695                      "Basis b for field \"%s\" must be non-NULL.",
696                      field_name);
697   // LCOV_EXCL_STOP
698   if (!v)
699     // LCOV_EXCL_START
700     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
701                      "Vector v for field \"%s\" must be non-NULL.",
702                      field_name);
703   // LCOV_EXCL_STOP
704 
705   CeedInt num_elem;
706   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
707   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
708       op->num_elem != num_elem)
709     // LCOV_EXCL_START
710     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
711                      "ElemRestriction with %d elements incompatible with prior "
712                      "%d elements", num_elem, op->num_elem);
713   // LCOV_EXCL_STOP
714 
715   CeedInt num_qpts = 0;
716   if (b != CEED_BASIS_COLLOCATED) {
717     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
718     if (op->num_qpts && op->num_qpts != num_qpts)
719       // LCOV_EXCL_START
720       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
721                        "Basis with %d quadrature points "
722                        "incompatible with prior %d points", num_qpts,
723                        op->num_qpts);
724     // LCOV_EXCL_STOP
725   }
726   CeedQFunctionField qf_field;
727   CeedOperatorField *op_field;
728   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
729     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
730       qf_field = op->qf->input_fields[i];
731       op_field = &op->input_fields[i];
732       goto found;
733     }
734   }
735   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
736     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
737       qf_field = op->qf->output_fields[i];
738       op_field = &op->output_fields[i];
739       goto found;
740     }
741   }
742   // LCOV_EXCL_START
743   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
744                    "QFunction has no knowledge of field '%s'",
745                    field_name);
746   // LCOV_EXCL_STOP
747 found:
748   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
749   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
750 
751   (*op_field)->vec = v;
752   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
753     ierr = CeedVectorReference(v); CeedChk(ierr);
754   }
755 
756   (*op_field)->elem_restr = r;
757   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
758   if (r != CEED_ELEMRESTRICTION_NONE) {
759     op->num_elem = num_elem;
760     op->has_restriction = true; // Restriction set, but num_elem may be 0
761   }
762 
763   (*op_field)->basis = b;
764   if (b != CEED_BASIS_COLLOCATED) {
765     if (!op->num_qpts) {
766       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
767     }
768     ierr = CeedBasisReference(b); CeedChk(ierr);
769   }
770 
771   op->num_fields += 1;
772   ierr = CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name);
773   CeedChk(ierr);
774   return CEED_ERROR_SUCCESS;
775 }
776 
777 /**
778   @brief Get the CeedOperatorFields of a CeedOperator
779 
780   Note: Calling this function asserts that setup is complete
781           and sets the CeedOperator as immutable.
782 
783   @param op                      CeedOperator
784   @param[out] num_input_fields   Variable to store number of input fields
785   @param[out] input_fields       Variable to store input_fields
786   @param[out] num_output_fields  Variable to store number of output fields
787   @param[out] output_fields      Variable to store output_fields
788 
789   @return An error code: 0 - success, otherwise - failure
790 
791   @ref Advanced
792 **/
793 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
794                           CeedOperatorField **input_fields,
795                           CeedInt *num_output_fields,
796                           CeedOperatorField **output_fields) {
797   int ierr;
798 
799   if (op->is_composite)
800     // LCOV_EXCL_START
801     return CeedError(op->ceed, CEED_ERROR_MINOR,
802                      "Not defined for composite operator");
803   // LCOV_EXCL_STOP
804   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
805 
806   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
807   if (input_fields) *input_fields = op->input_fields;
808   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
809   if (output_fields) *output_fields = op->output_fields;
810   return CEED_ERROR_SUCCESS;
811 }
812 
813 /**
814   @brief Get the name of a CeedOperatorField
815 
816   @param op_field         CeedOperatorField
817   @param[out] field_name  Variable to store the field name
818 
819   @return An error code: 0 - success, otherwise - failure
820 
821   @ref Advanced
822 **/
823 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
824   *field_name = (char *)op_field->field_name;
825   return CEED_ERROR_SUCCESS;
826 }
827 
828 /**
829   @brief Get the CeedElemRestriction of a CeedOperatorField
830 
831   @param op_field   CeedOperatorField
832   @param[out] rstr  Variable to store CeedElemRestriction
833 
834   @return An error code: 0 - success, otherwise - failure
835 
836   @ref Advanced
837 **/
838 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
839                                         CeedElemRestriction *rstr) {
840   *rstr = op_field->elem_restr;
841   return CEED_ERROR_SUCCESS;
842 }
843 
844 /**
845   @brief Get the CeedBasis of a CeedOperatorField
846 
847   @param op_field    CeedOperatorField
848   @param[out] basis  Variable to store CeedBasis
849 
850   @return An error code: 0 - success, otherwise - failure
851 
852   @ref Advanced
853 **/
854 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
855   *basis = op_field->basis;
856   return CEED_ERROR_SUCCESS;
857 }
858 
859 /**
860   @brief Get the CeedVector of a CeedOperatorField
861 
862   @param op_field  CeedOperatorField
863   @param[out] vec  Variable to store CeedVector
864 
865   @return An error code: 0 - success, otherwise - failure
866 
867   @ref Advanced
868 **/
869 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
870   *vec = op_field->vec;
871   return CEED_ERROR_SUCCESS;
872 }
873 
874 /**
875   @brief Add a sub-operator to a composite CeedOperator
876 
877   @param[out] composite_op  Composite CeedOperator
878   @param      sub_op        Sub-operator CeedOperator
879 
880   @return An error code: 0 - success, otherwise - failure
881 
882   @ref User
883  */
884 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
885                                 CeedOperator sub_op) {
886   int ierr;
887 
888   if (!composite_op->is_composite)
889     // LCOV_EXCL_START
890     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
891                      "CeedOperator is not a composite operator");
892   // LCOV_EXCL_STOP
893 
894   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
895     // LCOV_EXCL_START
896     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
897                      "Cannot add additional sub_operators");
898   // LCOV_EXCL_STOP
899   if (composite_op->is_immutable)
900     // LCOV_EXCL_START
901     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
902                      "Operator cannot be changed after set as immutable");
903   // LCOV_EXCL_STOP
904 
905   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
906   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
907   composite_op->num_suboperators++;
908   return CEED_ERROR_SUCCESS;
909 }
910 
911 /**
912   @brief Check if a CeedOperator is ready to be used.
913 
914   @param[in] op  CeedOperator to check
915 
916   @return An error code: 0 - success, otherwise - failure
917 
918   @ref User
919 **/
920 int CeedOperatorCheckReady(CeedOperator op) {
921   int ierr;
922   Ceed ceed;
923   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
924 
925   if (op->is_interface_setup)
926     return CEED_ERROR_SUCCESS;
927 
928   CeedQFunction qf = op->qf;
929   if (op->is_composite) {
930     if (!op->num_suboperators)
931       // LCOV_EXCL_START
932       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
933     // LCOV_EXCL_STOP
934     for (CeedInt i = 0; i < op->num_suboperators; i++) {
935       ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
936     }
937   } else {
938     if (op->num_fields == 0)
939       // LCOV_EXCL_START
940       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
941     // LCOV_EXCL_STOP
942     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
943       // LCOV_EXCL_START
944       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
945     // LCOV_EXCL_STOP
946     if (!op->has_restriction)
947       // LCOV_EXCL_START
948       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
949                        "At least one restriction required");
950     // LCOV_EXCL_STOP
951     if (op->num_qpts == 0)
952       // LCOV_EXCL_START
953       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
954                        "At least one non-collocated basis is required "
955                        "or the number of quadrature points must be set");
956     // LCOV_EXCL_STOP
957   }
958 
959   // Flag as immutable and ready
960   op->is_interface_setup = true;
961   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
962     // LCOV_EXCL_START
963     op->qf->is_immutable = true;
964   // LCOV_EXCL_STOP
965   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
966     // LCOV_EXCL_START
967     op->dqf->is_immutable = true;
968   // LCOV_EXCL_STOP
969   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
970     // LCOV_EXCL_START
971     op->dqfT->is_immutable = true;
972   // LCOV_EXCL_STOP
973   return CEED_ERROR_SUCCESS;
974 }
975 
976 /**
977   @brief Set the number of quadrature points associated with a CeedOperator.
978            This should be used when creating a CeedOperator where every
979            field has a collocated basis. This function cannot be used for
980            composite CeedOperators.
981 
982   @param op        CeedOperator
983   @param num_qpts  Number of quadrature points to set
984 
985   @return An error code: 0 - success, otherwise - failure
986 
987   @ref Advanced
988 **/
989 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
990   if (op->is_composite)
991     // LCOV_EXCL_START
992     return CeedError(op->ceed, CEED_ERROR_MINOR,
993                      "Not defined for composite operator");
994   // LCOV_EXCL_STOP
995   if (op->num_qpts)
996     // LCOV_EXCL_START
997     return CeedError(op->ceed, CEED_ERROR_MINOR,
998                      "Number of quadrature points already defined");
999   // LCOV_EXCL_STOP
1000   if (op->is_immutable)
1001     // LCOV_EXCL_START
1002     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1003                      "Operator cannot be changed after set as immutable");
1004   // LCOV_EXCL_STOP
1005 
1006   op->num_qpts = num_qpts;
1007   return CEED_ERROR_SUCCESS;
1008 }
1009 
1010 /**
1011   @brief View a CeedOperator
1012 
1013   @param[in] op      CeedOperator to view
1014   @param[in] stream  Stream to write; typically stdout/stderr or a file
1015 
1016   @return Error code: 0 - success, otherwise - failure
1017 
1018   @ref User
1019 **/
1020 int CeedOperatorView(CeedOperator op, FILE *stream) {
1021   int ierr;
1022 
1023   if (op->is_composite) {
1024     fprintf(stream, "Composite CeedOperator\n");
1025 
1026     for (CeedInt i=0; i<op->num_suboperators; i++) {
1027       fprintf(stream, "  SubOperator [%d]:\n", i);
1028       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1029       CeedChk(ierr);
1030     }
1031   } else {
1032     fprintf(stream, "CeedOperator\n");
1033     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1034   }
1035   return CEED_ERROR_SUCCESS;
1036 }
1037 
1038 /**
1039   @brief Get the Ceed associated with a CeedOperator
1040 
1041   @param op         CeedOperator
1042   @param[out] ceed  Variable to store Ceed
1043 
1044   @return An error code: 0 - success, otherwise - failure
1045 
1046   @ref Advanced
1047 **/
1048 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1049   *ceed = op->ceed;
1050   return CEED_ERROR_SUCCESS;
1051 }
1052 
1053 /**
1054   @brief Get the number of elements associated with a CeedOperator
1055 
1056   @param op             CeedOperator
1057   @param[out] num_elem  Variable to store number of elements
1058 
1059   @return An error code: 0 - success, otherwise - failure
1060 
1061   @ref Advanced
1062 **/
1063 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1064   if (op->is_composite)
1065     // LCOV_EXCL_START
1066     return CeedError(op->ceed, CEED_ERROR_MINOR,
1067                      "Not defined for composite operator");
1068   // LCOV_EXCL_STOP
1069 
1070   *num_elem = op->num_elem;
1071   return CEED_ERROR_SUCCESS;
1072 }
1073 
1074 /**
1075   @brief Get the number of quadrature points associated with a CeedOperator
1076 
1077   @param op             CeedOperator
1078   @param[out] num_qpts  Variable to store vector number of quadrature points
1079 
1080   @return An error code: 0 - success, otherwise - failure
1081 
1082   @ref Advanced
1083 **/
1084 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1085   if (op->is_composite)
1086     // LCOV_EXCL_START
1087     return CeedError(op->ceed, CEED_ERROR_MINOR,
1088                      "Not defined for composite operator");
1089   // LCOV_EXCL_STOP
1090 
1091   *num_qpts = op->num_qpts;
1092   return CEED_ERROR_SUCCESS;
1093 }
1094 
1095 /**
1096   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1097            field has been registered with this `field_name`.
1098 
1099   @param[in] op            CeedOperator
1100   @param[in] field_name    Name of field to retrieve label
1101   @param[out] field_label  Variable to field label
1102 
1103   @return An error code: 0 - success, otherwise - failure
1104 
1105   @ref User
1106 **/
1107 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1108                                      const char *field_name,
1109                                      CeedContextFieldLabel *field_label) {
1110   int ierr;
1111 
1112   bool is_composite;
1113   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1114   if (is_composite) {
1115     // Check if composite label already created
1116     for (CeedInt i=0; i<op->num_context_labels; i++) {
1117       if (!strcmp(op->context_labels[i]->name, field_name)) {
1118         *field_label = op->context_labels[i];
1119         return CEED_ERROR_SUCCESS;
1120       }
1121     }
1122 
1123     // Create composite label if needed
1124     CeedInt num_sub;
1125     CeedOperator *sub_operators;
1126     CeedContextFieldLabel new_field_label;
1127 
1128     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1129     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1130     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1131     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1132     new_field_label->num_sub_labels = num_sub;
1133 
1134     bool label_found = false;
1135     for (CeedInt i=0; i<num_sub; i++) {
1136       if (sub_operators[i]->qf->ctx) {
1137         CeedContextFieldLabel new_field_label_i;
1138         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1139                &new_field_label_i); CeedChk(ierr);
1140         if (new_field_label_i) {
1141           label_found = true;
1142           new_field_label->sub_labels[i] = new_field_label_i;
1143           new_field_label->name = new_field_label_i->name;
1144           new_field_label->description = new_field_label_i->description;
1145         }
1146       }
1147     }
1148     if (!label_found) {
1149       // LCOV_EXCL_START
1150       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1151       *field_label = NULL;
1152       // LCOV_EXCL_STOP
1153     } else {
1154       // Move new composite label to operator
1155       if (op->num_context_labels == 0) {
1156         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1157         op->max_context_labels = 1;
1158       } else if (op->num_context_labels == op->max_context_labels) {
1159         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1160         CeedChk(ierr);
1161         op->max_context_labels *= 2;
1162       }
1163       op->context_labels[op->num_context_labels] = new_field_label;
1164       *field_label = new_field_label;
1165       op->num_context_labels++;
1166     }
1167 
1168     return CEED_ERROR_SUCCESS;
1169   } else {
1170     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1171   }
1172 }
1173 
1174 /**
1175   @brief Set QFunctionContext field holding a double precision value.
1176            For composite operators, the value is set in all
1177            sub-operator QFunctionContexts that have a matching `field_name`.
1178 
1179   @param op          CeedOperator
1180   @param field_label Label of field to register
1181   @param value       Value to set
1182 
1183   @return An error code: 0 - success, otherwise - failure
1184 
1185   @ref User
1186 **/
1187 int CeedOperatorContextSetDouble(CeedOperator op,
1188                                  CeedContextFieldLabel field_label,
1189                                  double value) {
1190   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1191                                        &value);
1192 }
1193 
1194 /**
1195   @brief Set QFunctionContext field holding an int32 value.
1196            For composite operators, the value is set in all
1197            sub-operator QFunctionContexts that have a matching `field_name`.
1198 
1199   @param op          CeedOperator
1200   @param field_label Label of field to set
1201   @param value       Value to set
1202 
1203   @return An error code: 0 - success, otherwise - failure
1204 
1205   @ref User
1206 **/
1207 int CeedOperatorContextSetInt32(CeedOperator op,
1208                                 CeedContextFieldLabel field_label,
1209                                 int value) {
1210   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1211                                        &value);
1212 }
1213 
1214 /**
1215   @brief Apply CeedOperator to a vector
1216 
1217   This computes the action of the operator on the specified (active) input,
1218   yielding its (active) output.  All inputs and outputs must be specified using
1219   CeedOperatorSetField().
1220 
1221   Note: Calling this function asserts that setup is complete
1222           and sets the CeedOperator as immutable.
1223 
1224   @param op        CeedOperator to apply
1225   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1226                      there are no active inputs
1227   @param[out] out  CeedVector to store result of applying operator (must be
1228                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1229                      active outputs
1230   @param request   Address of CeedRequest for non-blocking completion, else
1231                      @ref CEED_REQUEST_IMMEDIATE
1232 
1233   @return An error code: 0 - success, otherwise - failure
1234 
1235   @ref User
1236 **/
1237 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1238                       CeedRequest *request) {
1239   int ierr;
1240   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1241 
1242   if (op->num_elem)  {
1243     // Standard Operator
1244     if (op->Apply) {
1245       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1246     } else {
1247       // Zero all output vectors
1248       CeedQFunction qf = op->qf;
1249       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1250         CeedVector vec = op->output_fields[i]->vec;
1251         if (vec == CEED_VECTOR_ACTIVE)
1252           vec = out;
1253         if (vec != CEED_VECTOR_NONE) {
1254           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1255         }
1256       }
1257       // Apply
1258       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1259     }
1260   } else if (op->is_composite) {
1261     // Composite Operator
1262     if (op->ApplyComposite) {
1263       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1264     } else {
1265       CeedInt num_suboperators;
1266       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1267       CeedOperator *sub_operators;
1268       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1269 
1270       // Zero all output vectors
1271       if (out != CEED_VECTOR_NONE) {
1272         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1273       }
1274       for (CeedInt i=0; i<num_suboperators; i++) {
1275         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1276           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1277           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1278             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1279           }
1280         }
1281       }
1282       // Apply
1283       for (CeedInt i=0; i<op->num_suboperators; i++) {
1284         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1285         CeedChk(ierr);
1286       }
1287     }
1288   }
1289   return CEED_ERROR_SUCCESS;
1290 }
1291 
1292 /**
1293   @brief Apply CeedOperator to a vector and add result to output vector
1294 
1295   This computes the action of the operator on the specified (active) input,
1296   yielding its (active) output.  All inputs and outputs must be specified using
1297   CeedOperatorSetField().
1298 
1299   @param op        CeedOperator to apply
1300   @param[in] in    CeedVector containing input state or NULL if there are no
1301                      active inputs
1302   @param[out] out  CeedVector to sum in result of applying operator (must be
1303                      distinct from @a in) or NULL if there are no active outputs
1304   @param request   Address of CeedRequest for non-blocking completion, else
1305                      @ref CEED_REQUEST_IMMEDIATE
1306 
1307   @return An error code: 0 - success, otherwise - failure
1308 
1309   @ref User
1310 **/
1311 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1312                          CeedRequest *request) {
1313   int ierr;
1314   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1315 
1316   if (op->num_elem)  {
1317     // Standard Operator
1318     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1319   } else if (op->is_composite) {
1320     // Composite Operator
1321     if (op->ApplyAddComposite) {
1322       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1323     } else {
1324       CeedInt num_suboperators;
1325       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1326       CeedOperator *sub_operators;
1327       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1328 
1329       for (CeedInt i=0; i<num_suboperators; i++) {
1330         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1331         CeedChk(ierr);
1332       }
1333     }
1334   }
1335   return CEED_ERROR_SUCCESS;
1336 }
1337 
1338 /**
1339   @brief Destroy a CeedOperator
1340 
1341   @param op  CeedOperator to destroy
1342 
1343   @return An error code: 0 - success, otherwise - failure
1344 
1345   @ref User
1346 **/
1347 int CeedOperatorDestroy(CeedOperator *op) {
1348   int ierr;
1349 
1350   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1351   if ((*op)->Destroy) {
1352     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1353   }
1354   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1355   // Free fields
1356   for (CeedInt i=0; i<(*op)->num_fields; i++)
1357     if ((*op)->input_fields[i]) {
1358       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1359         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1360         CeedChk(ierr);
1361       }
1362       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1363         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1364       }
1365       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1366           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1367         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1368       }
1369       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1370       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1371     }
1372   for (CeedInt i=0; i<(*op)->num_fields; i++)
1373     if ((*op)->output_fields[i]) {
1374       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1375       CeedChk(ierr);
1376       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1377         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1378       }
1379       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1380           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1381         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1382       }
1383       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1384       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1385     }
1386   // Destroy sub_operators
1387   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1388     if ((*op)->sub_operators[i]) {
1389       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1390     }
1391   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1392   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1393   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1394   // Destroy any composite labels
1395   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1396     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1397     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1398   }
1399   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1400 
1401   // Destroy fallback
1402   if ((*op)->op_fallback) {
1403     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1404     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1405     ierr = CeedVectorDestroy(&(*op)->op_fallback->qf_assembled); CeedChk(ierr);
1406     ierr = CeedElemRestrictionDestroy(&(*op)->op_fallback->qf_assembled_rstr);
1407     CeedChk(ierr);
1408     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1409     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1410   }
1411 
1412   // Destroy QF assembly cache
1413   ierr = CeedVectorDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1414   ierr = CeedElemRestrictionDestroy(&(*op)->qf_assembled_rstr); CeedChk(ierr);
1415 
1416   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1417   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1418   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1419   ierr = CeedFree(op); CeedChk(ierr);
1420   return CEED_ERROR_SUCCESS;
1421 }
1422 
1423 /// @}
1424