xref: /libCEED/interface/ceed-operator.c (revision 480fae850cb0898970f2dd40c5ea1abc8a8b0488)
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 = CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled);
583   CeedChk(ierr);
584   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields); CeedChk(ierr);
585   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields); CeedChk(ierr);
586   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
587   return CEED_ERROR_SUCCESS;
588 }
589 
590 /**
591   @brief Create an operator that composes the action of several operators
592 
593   @param ceed     A Ceed object where the CeedOperator will be created
594   @param[out] op  Address of the variable where the newly created
595                     Composite CeedOperator will be stored
596 
597   @return An error code: 0 - success, otherwise - failure
598 
599   @ref User
600  */
601 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
602   int ierr;
603 
604   if (!ceed->CompositeOperatorCreate) {
605     Ceed delegate;
606     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
607 
608     if (delegate) {
609       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
610       return CEED_ERROR_SUCCESS;
611     }
612   }
613 
614   ierr = CeedCalloc(1, op); CeedChk(ierr);
615   (*op)->ceed = ceed;
616   ierr = CeedReference(ceed); CeedChk(ierr);
617   (*op)->is_composite = true;
618   ierr = CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators); CeedChk(ierr);
619 
620   if (ceed->CompositeOperatorCreate) {
621     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
622   }
623   return CEED_ERROR_SUCCESS;
624 }
625 
626 /**
627   @brief Copy the pointer to a CeedOperator. Both pointers should
628            be destroyed with `CeedOperatorDestroy()`;
629            Note: If `*op_copy` is non-NULL, then it is assumed that
630            `*op_copy` is a pointer to a CeedOperator. This
631            CeedOperator will be destroyed if `*op_copy` is the only
632            reference to this CeedOperator.
633 
634   @param op            CeedOperator to copy reference to
635   @param[out] op_copy  Variable to store copied reference
636 
637   @return An error code: 0 - success, otherwise - failure
638 
639   @ref User
640 **/
641 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
642   int ierr;
643 
644   ierr = CeedOperatorReference(op); CeedChk(ierr);
645   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
646   *op_copy = op;
647   return CEED_ERROR_SUCCESS;
648 }
649 
650 /**
651   @brief Provide a field to a CeedOperator for use by its CeedQFunction
652 
653   This function is used to specify both active and passive fields to a
654   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
655   fields can inputs or outputs (updated in-place when operator is applied).
656 
657   Active fields must be specified using this function, but their data (in a
658   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
659   input and at most one active output.
660 
661   @param op          CeedOperator on which to provide the field
662   @param field_name  Name of the field (to be matched with the name used by
663                        CeedQFunction)
664   @param r           CeedElemRestriction
665   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
666                        if collocated with quadrature points
667   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
668                        if field is active or @ref CEED_VECTOR_NONE if using
669                        @ref CEED_EVAL_WEIGHT in the QFunction
670 
671   @return An error code: 0 - success, otherwise - failure
672 
673   @ref User
674 **/
675 int CeedOperatorSetField(CeedOperator op, const char *field_name,
676                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
677   int ierr;
678   if (op->is_composite)
679     // LCOV_EXCL_START
680     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
681                      "Cannot add field to composite operator.");
682   // LCOV_EXCL_STOP
683   if (op->is_immutable)
684     // LCOV_EXCL_START
685     return CeedError(op->ceed, CEED_ERROR_MAJOR,
686                      "Operator cannot be changed after set as immutable");
687   // LCOV_EXCL_STOP
688   if (!r)
689     // LCOV_EXCL_START
690     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
691                      "ElemRestriction r for field \"%s\" must be non-NULL.",
692                      field_name);
693   // LCOV_EXCL_STOP
694   if (!b)
695     // LCOV_EXCL_START
696     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
697                      "Basis b for field \"%s\" must be non-NULL.",
698                      field_name);
699   // LCOV_EXCL_STOP
700   if (!v)
701     // LCOV_EXCL_START
702     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
703                      "Vector v for field \"%s\" must be non-NULL.",
704                      field_name);
705   // LCOV_EXCL_STOP
706 
707   CeedInt num_elem;
708   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
709   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
710       op->num_elem != num_elem)
711     // LCOV_EXCL_START
712     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
713                      "ElemRestriction with %d elements incompatible with prior "
714                      "%d elements", num_elem, op->num_elem);
715   // LCOV_EXCL_STOP
716 
717   CeedInt num_qpts = 0;
718   if (b != CEED_BASIS_COLLOCATED) {
719     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
720     if (op->num_qpts && op->num_qpts != num_qpts)
721       // LCOV_EXCL_START
722       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
723                        "Basis with %d quadrature points "
724                        "incompatible with prior %d points", num_qpts,
725                        op->num_qpts);
726     // LCOV_EXCL_STOP
727   }
728   CeedQFunctionField qf_field;
729   CeedOperatorField *op_field;
730   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
731     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
732       qf_field = op->qf->input_fields[i];
733       op_field = &op->input_fields[i];
734       goto found;
735     }
736   }
737   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
738     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
739       qf_field = op->qf->output_fields[i];
740       op_field = &op->output_fields[i];
741       goto found;
742     }
743   }
744   // LCOV_EXCL_START
745   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
746                    "QFunction has no knowledge of field '%s'",
747                    field_name);
748   // LCOV_EXCL_STOP
749 found:
750   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
751   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
752 
753   (*op_field)->vec = v;
754   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
755     ierr = CeedVectorReference(v); CeedChk(ierr);
756   }
757 
758   (*op_field)->elem_restr = r;
759   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
760   if (r != CEED_ELEMRESTRICTION_NONE) {
761     op->num_elem = num_elem;
762     op->has_restriction = true; // Restriction set, but num_elem may be 0
763   }
764 
765   (*op_field)->basis = b;
766   if (b != CEED_BASIS_COLLOCATED) {
767     if (!op->num_qpts) {
768       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
769     }
770     ierr = CeedBasisReference(b); CeedChk(ierr);
771   }
772 
773   op->num_fields += 1;
774   ierr = CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name);
775   CeedChk(ierr);
776   return CEED_ERROR_SUCCESS;
777 }
778 
779 /**
780   @brief Get the CeedOperatorFields of a CeedOperator
781 
782   Note: Calling this function asserts that setup is complete
783           and sets the CeedOperator as immutable.
784 
785   @param op                      CeedOperator
786   @param[out] num_input_fields   Variable to store number of input fields
787   @param[out] input_fields       Variable to store input_fields
788   @param[out] num_output_fields  Variable to store number of output fields
789   @param[out] output_fields      Variable to store output_fields
790 
791   @return An error code: 0 - success, otherwise - failure
792 
793   @ref Advanced
794 **/
795 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
796                           CeedOperatorField **input_fields,
797                           CeedInt *num_output_fields,
798                           CeedOperatorField **output_fields) {
799   int ierr;
800 
801   if (op->is_composite)
802     // LCOV_EXCL_START
803     return CeedError(op->ceed, CEED_ERROR_MINOR,
804                      "Not defined for composite operator");
805   // LCOV_EXCL_STOP
806   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
807 
808   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
809   if (input_fields) *input_fields = op->input_fields;
810   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
811   if (output_fields) *output_fields = op->output_fields;
812   return CEED_ERROR_SUCCESS;
813 }
814 
815 /**
816   @brief Get the name of a CeedOperatorField
817 
818   @param op_field         CeedOperatorField
819   @param[out] field_name  Variable to store the field name
820 
821   @return An error code: 0 - success, otherwise - failure
822 
823   @ref Advanced
824 **/
825 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
826   *field_name = (char *)op_field->field_name;
827   return CEED_ERROR_SUCCESS;
828 }
829 
830 /**
831   @brief Get the CeedElemRestriction of a CeedOperatorField
832 
833   @param op_field   CeedOperatorField
834   @param[out] rstr  Variable to store CeedElemRestriction
835 
836   @return An error code: 0 - success, otherwise - failure
837 
838   @ref Advanced
839 **/
840 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
841                                         CeedElemRestriction *rstr) {
842   *rstr = op_field->elem_restr;
843   return CEED_ERROR_SUCCESS;
844 }
845 
846 /**
847   @brief Get the CeedBasis of a CeedOperatorField
848 
849   @param op_field    CeedOperatorField
850   @param[out] basis  Variable to store CeedBasis
851 
852   @return An error code: 0 - success, otherwise - failure
853 
854   @ref Advanced
855 **/
856 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
857   *basis = op_field->basis;
858   return CEED_ERROR_SUCCESS;
859 }
860 
861 /**
862   @brief Get the CeedVector of a CeedOperatorField
863 
864   @param op_field  CeedOperatorField
865   @param[out] vec  Variable to store CeedVector
866 
867   @return An error code: 0 - success, otherwise - failure
868 
869   @ref Advanced
870 **/
871 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
872   *vec = op_field->vec;
873   return CEED_ERROR_SUCCESS;
874 }
875 
876 /**
877   @brief Add a sub-operator to a composite CeedOperator
878 
879   @param[out] composite_op  Composite CeedOperator
880   @param      sub_op        Sub-operator CeedOperator
881 
882   @return An error code: 0 - success, otherwise - failure
883 
884   @ref User
885  */
886 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
887                                 CeedOperator sub_op) {
888   int ierr;
889 
890   if (!composite_op->is_composite)
891     // LCOV_EXCL_START
892     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
893                      "CeedOperator is not a composite operator");
894   // LCOV_EXCL_STOP
895 
896   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
897     // LCOV_EXCL_START
898     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
899                      "Cannot add additional sub_operators");
900   // LCOV_EXCL_STOP
901   if (composite_op->is_immutable)
902     // LCOV_EXCL_START
903     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
904                      "Operator cannot be changed after set as immutable");
905   // LCOV_EXCL_STOP
906 
907   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
908   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
909   composite_op->num_suboperators++;
910   return CEED_ERROR_SUCCESS;
911 }
912 
913 /**
914   @brief Check if a CeedOperator is ready to be used.
915 
916   @param[in] op  CeedOperator to check
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref User
921 **/
922 int CeedOperatorCheckReady(CeedOperator op) {
923   int ierr;
924   Ceed ceed;
925   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
926 
927   if (op->is_interface_setup)
928     return CEED_ERROR_SUCCESS;
929 
930   CeedQFunction qf = op->qf;
931   if (op->is_composite) {
932     if (!op->num_suboperators)
933       // LCOV_EXCL_START
934       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
935     // LCOV_EXCL_STOP
936     for (CeedInt i = 0; i < op->num_suboperators; i++) {
937       ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
938     }
939   } else {
940     if (op->num_fields == 0)
941       // LCOV_EXCL_START
942       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
943     // LCOV_EXCL_STOP
944     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
945       // LCOV_EXCL_START
946       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
947     // LCOV_EXCL_STOP
948     if (!op->has_restriction)
949       // LCOV_EXCL_START
950       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
951                        "At least one restriction required");
952     // LCOV_EXCL_STOP
953     if (op->num_qpts == 0)
954       // LCOV_EXCL_START
955       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
956                        "At least one non-collocated basis is required "
957                        "or the number of quadrature points must be set");
958     // LCOV_EXCL_STOP
959   }
960 
961   // Flag as immutable and ready
962   op->is_interface_setup = true;
963   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
964     // LCOV_EXCL_START
965     op->qf->is_immutable = true;
966   // LCOV_EXCL_STOP
967   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
968     // LCOV_EXCL_START
969     op->dqf->is_immutable = true;
970   // LCOV_EXCL_STOP
971   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
972     // LCOV_EXCL_START
973     op->dqfT->is_immutable = true;
974   // LCOV_EXCL_STOP
975   return CEED_ERROR_SUCCESS;
976 }
977 
978 /**
979   @brief Set the number of quadrature points associated with a CeedOperator.
980            This should be used when creating a CeedOperator where every
981            field has a collocated basis. This function cannot be used for
982            composite CeedOperators.
983 
984   @param op        CeedOperator
985   @param num_qpts  Number of quadrature points to set
986 
987   @return An error code: 0 - success, otherwise - failure
988 
989   @ref Advanced
990 **/
991 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
992   if (op->is_composite)
993     // LCOV_EXCL_START
994     return CeedError(op->ceed, CEED_ERROR_MINOR,
995                      "Not defined for composite operator");
996   // LCOV_EXCL_STOP
997   if (op->num_qpts)
998     // LCOV_EXCL_START
999     return CeedError(op->ceed, CEED_ERROR_MINOR,
1000                      "Number of quadrature points already defined");
1001   // LCOV_EXCL_STOP
1002   if (op->is_immutable)
1003     // LCOV_EXCL_START
1004     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1005                      "Operator cannot be changed after set as immutable");
1006   // LCOV_EXCL_STOP
1007 
1008   op->num_qpts = num_qpts;
1009   return CEED_ERROR_SUCCESS;
1010 }
1011 
1012 /**
1013   @brief View a CeedOperator
1014 
1015   @param[in] op      CeedOperator to view
1016   @param[in] stream  Stream to write; typically stdout/stderr or a file
1017 
1018   @return Error code: 0 - success, otherwise - failure
1019 
1020   @ref User
1021 **/
1022 int CeedOperatorView(CeedOperator op, FILE *stream) {
1023   int ierr;
1024 
1025   if (op->is_composite) {
1026     fprintf(stream, "Composite CeedOperator\n");
1027 
1028     for (CeedInt i=0; i<op->num_suboperators; i++) {
1029       fprintf(stream, "  SubOperator [%d]:\n", i);
1030       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1031       CeedChk(ierr);
1032     }
1033   } else {
1034     fprintf(stream, "CeedOperator\n");
1035     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1036   }
1037   return CEED_ERROR_SUCCESS;
1038 }
1039 
1040 /**
1041   @brief Get the Ceed associated with a CeedOperator
1042 
1043   @param op         CeedOperator
1044   @param[out] ceed  Variable to store Ceed
1045 
1046   @return An error code: 0 - success, otherwise - failure
1047 
1048   @ref Advanced
1049 **/
1050 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1051   *ceed = op->ceed;
1052   return CEED_ERROR_SUCCESS;
1053 }
1054 
1055 /**
1056   @brief Get the number of elements associated with a CeedOperator
1057 
1058   @param op             CeedOperator
1059   @param[out] num_elem  Variable to store number of elements
1060 
1061   @return An error code: 0 - success, otherwise - failure
1062 
1063   @ref Advanced
1064 **/
1065 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1066   if (op->is_composite)
1067     // LCOV_EXCL_START
1068     return CeedError(op->ceed, CEED_ERROR_MINOR,
1069                      "Not defined for composite operator");
1070   // LCOV_EXCL_STOP
1071 
1072   *num_elem = op->num_elem;
1073   return CEED_ERROR_SUCCESS;
1074 }
1075 
1076 /**
1077   @brief Get the number of quadrature points associated with a CeedOperator
1078 
1079   @param op             CeedOperator
1080   @param[out] num_qpts  Variable to store vector number of quadrature points
1081 
1082   @return An error code: 0 - success, otherwise - failure
1083 
1084   @ref Advanced
1085 **/
1086 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1087   if (op->is_composite)
1088     // LCOV_EXCL_START
1089     return CeedError(op->ceed, CEED_ERROR_MINOR,
1090                      "Not defined for composite operator");
1091   // LCOV_EXCL_STOP
1092 
1093   *num_qpts = op->num_qpts;
1094   return CEED_ERROR_SUCCESS;
1095 }
1096 
1097 /**
1098   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1099            field has been registered with this `field_name`.
1100 
1101   @param[in] op            CeedOperator
1102   @param[in] field_name    Name of field to retrieve label
1103   @param[out] field_label  Variable to field label
1104 
1105   @return An error code: 0 - success, otherwise - failure
1106 
1107   @ref User
1108 **/
1109 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1110                                      const char *field_name,
1111                                      CeedContextFieldLabel *field_label) {
1112   int ierr;
1113 
1114   bool is_composite;
1115   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1116   if (is_composite) {
1117     // Check if composite label already created
1118     for (CeedInt i=0; i<op->num_context_labels; i++) {
1119       if (!strcmp(op->context_labels[i]->name, field_name)) {
1120         *field_label = op->context_labels[i];
1121         return CEED_ERROR_SUCCESS;
1122       }
1123     }
1124 
1125     // Create composite label if needed
1126     CeedInt num_sub;
1127     CeedOperator *sub_operators;
1128     CeedContextFieldLabel new_field_label;
1129 
1130     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1131     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1132     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1133     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1134     new_field_label->num_sub_labels = num_sub;
1135 
1136     bool label_found = false;
1137     for (CeedInt i=0; i<num_sub; i++) {
1138       if (sub_operators[i]->qf->ctx) {
1139         CeedContextFieldLabel new_field_label_i;
1140         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1141                &new_field_label_i); CeedChk(ierr);
1142         if (new_field_label_i) {
1143           label_found = true;
1144           new_field_label->sub_labels[i] = new_field_label_i;
1145           new_field_label->name = new_field_label_i->name;
1146           new_field_label->description = new_field_label_i->description;
1147           if (new_field_label->type &&
1148               new_field_label->type != new_field_label_i->type) {
1149             // LCOV_EXCL_START
1150             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1151             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1152                              "Incompatible field types on sub-operator contexts. "
1153                              "%s != %s",
1154                              CeedContextFieldTypes[new_field_label->type],
1155                              CeedContextFieldTypes[new_field_label_i->type]);
1156             // LCOV_EXCL_STOP
1157           } else {
1158             new_field_label->type = new_field_label_i->type;
1159           }
1160           if (new_field_label->num_values != 0 &&
1161               new_field_label->num_values != new_field_label_i->num_values) {
1162             // LCOV_EXCL_START
1163             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1164             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1165                              "Incompatible field number of values on sub-operator"
1166                              " contexts. %ld != %ld",
1167                              new_field_label->num_values, new_field_label_i->num_values);
1168             // LCOV_EXCL_STOP
1169           } else {
1170             new_field_label->num_values = new_field_label_i->num_values;
1171           }
1172         }
1173       }
1174     }
1175     if (!label_found) {
1176       // LCOV_EXCL_START
1177       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1178       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1179       *field_label = NULL;
1180       // LCOV_EXCL_STOP
1181     } else {
1182       // Move new composite label to operator
1183       if (op->num_context_labels == 0) {
1184         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1185         op->max_context_labels = 1;
1186       } else if (op->num_context_labels == op->max_context_labels) {
1187         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1188         CeedChk(ierr);
1189         op->max_context_labels *= 2;
1190       }
1191       op->context_labels[op->num_context_labels] = new_field_label;
1192       *field_label = new_field_label;
1193       op->num_context_labels++;
1194     }
1195 
1196     return CEED_ERROR_SUCCESS;
1197   } else {
1198     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1199   }
1200 }
1201 
1202 /**
1203   @brief Set QFunctionContext field holding a double precision value.
1204            For composite operators, the value is set in all
1205            sub-operator QFunctionContexts that have a matching `field_name`.
1206 
1207   @param op          CeedOperator
1208   @param field_label Label of field to register
1209   @param values      Values to set
1210 
1211   @return An error code: 0 - success, otherwise - failure
1212 
1213   @ref User
1214 **/
1215 int CeedOperatorContextSetDouble(CeedOperator op,
1216                                  CeedContextFieldLabel field_label,
1217                                  double *values) {
1218   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1219                                        values);
1220 }
1221 
1222 /**
1223   @brief Set QFunctionContext field holding an int32 value.
1224            For composite operators, the value is set in all
1225            sub-operator QFunctionContexts that have a matching `field_name`.
1226 
1227   @param op          CeedOperator
1228   @param field_label Label of field to set
1229   @param values      Values to set
1230 
1231   @return An error code: 0 - success, otherwise - failure
1232 
1233   @ref User
1234 **/
1235 int CeedOperatorContextSetInt32(CeedOperator op,
1236                                 CeedContextFieldLabel field_label,
1237                                 int *values) {
1238   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1239                                        values);
1240 }
1241 
1242 /**
1243   @brief Apply CeedOperator to a vector
1244 
1245   This computes the action of the operator on the specified (active) input,
1246   yielding its (active) output.  All inputs and outputs must be specified using
1247   CeedOperatorSetField().
1248 
1249   Note: Calling this function asserts that setup is complete
1250           and sets the CeedOperator as immutable.
1251 
1252   @param op        CeedOperator to apply
1253   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1254                      there are no active inputs
1255   @param[out] out  CeedVector to store result of applying operator (must be
1256                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1257                      active outputs
1258   @param request   Address of CeedRequest for non-blocking completion, else
1259                      @ref CEED_REQUEST_IMMEDIATE
1260 
1261   @return An error code: 0 - success, otherwise - failure
1262 
1263   @ref User
1264 **/
1265 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1266                       CeedRequest *request) {
1267   int ierr;
1268   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1269 
1270   if (op->num_elem)  {
1271     // Standard Operator
1272     if (op->Apply) {
1273       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1274     } else {
1275       // Zero all output vectors
1276       CeedQFunction qf = op->qf;
1277       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1278         CeedVector vec = op->output_fields[i]->vec;
1279         if (vec == CEED_VECTOR_ACTIVE)
1280           vec = out;
1281         if (vec != CEED_VECTOR_NONE) {
1282           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1283         }
1284       }
1285       // Apply
1286       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1287     }
1288   } else if (op->is_composite) {
1289     // Composite Operator
1290     if (op->ApplyComposite) {
1291       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1292     } else {
1293       CeedInt num_suboperators;
1294       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1295       CeedOperator *sub_operators;
1296       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1297 
1298       // Zero all output vectors
1299       if (out != CEED_VECTOR_NONE) {
1300         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1301       }
1302       for (CeedInt i=0; i<num_suboperators; i++) {
1303         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1304           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1305           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1306             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1307           }
1308         }
1309       }
1310       // Apply
1311       for (CeedInt i=0; i<op->num_suboperators; i++) {
1312         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1313         CeedChk(ierr);
1314       }
1315     }
1316   }
1317   return CEED_ERROR_SUCCESS;
1318 }
1319 
1320 /**
1321   @brief Apply CeedOperator to a vector and add result to output vector
1322 
1323   This computes the action of the operator on the specified (active) input,
1324   yielding its (active) output.  All inputs and outputs must be specified using
1325   CeedOperatorSetField().
1326 
1327   @param op        CeedOperator to apply
1328   @param[in] in    CeedVector containing input state or NULL if there are no
1329                      active inputs
1330   @param[out] out  CeedVector to sum in result of applying operator (must be
1331                      distinct from @a in) or NULL if there are no active outputs
1332   @param request   Address of CeedRequest for non-blocking completion, else
1333                      @ref CEED_REQUEST_IMMEDIATE
1334 
1335   @return An error code: 0 - success, otherwise - failure
1336 
1337   @ref User
1338 **/
1339 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1340                          CeedRequest *request) {
1341   int ierr;
1342   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1343 
1344   if (op->num_elem)  {
1345     // Standard Operator
1346     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1347   } else if (op->is_composite) {
1348     // Composite Operator
1349     if (op->ApplyAddComposite) {
1350       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1351     } else {
1352       CeedInt num_suboperators;
1353       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1354       CeedOperator *sub_operators;
1355       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1356 
1357       for (CeedInt i=0; i<num_suboperators; i++) {
1358         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1359         CeedChk(ierr);
1360       }
1361     }
1362   }
1363   return CEED_ERROR_SUCCESS;
1364 }
1365 
1366 /**
1367   @brief Destroy a CeedOperator
1368 
1369   @param op  CeedOperator to destroy
1370 
1371   @return An error code: 0 - success, otherwise - failure
1372 
1373   @ref User
1374 **/
1375 int CeedOperatorDestroy(CeedOperator *op) {
1376   int ierr;
1377 
1378   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1379   if ((*op)->Destroy) {
1380     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1381   }
1382   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1383   // Free fields
1384   for (CeedInt i=0; i<(*op)->num_fields; i++)
1385     if ((*op)->input_fields[i]) {
1386       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1387         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1388         CeedChk(ierr);
1389       }
1390       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1391         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1392       }
1393       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1394           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1395         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1396       }
1397       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1398       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1399     }
1400   for (CeedInt i=0; i<(*op)->num_fields; i++)
1401     if ((*op)->output_fields[i]) {
1402       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1403       CeedChk(ierr);
1404       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1405         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1406       }
1407       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1408           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1409         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1410       }
1411       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1412       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1413     }
1414   // Destroy sub_operators
1415   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1416     if ((*op)->sub_operators[i]) {
1417       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1418     }
1419   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1420   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1421   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1422   // Destroy any composite labels
1423   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1424     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1425     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1426   }
1427   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1428 
1429   // Destroy fallback
1430   if ((*op)->op_fallback) {
1431     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1432     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1433     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1434     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1435   }
1436 
1437   // Destroy QF assembly cache
1438   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1439 
1440   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1441   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1442   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1443   ierr = CeedFree(op); CeedChk(ierr);
1444   return CEED_ERROR_SUCCESS;
1445 }
1446 
1447 /// @}
1448