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