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