xref: /libCEED/interface/ceed-operator.c (revision c9366a6b60a7724cfea955a0261b10f32d7a0e6c)
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 Get vector lengths for the active input and/or output vectors of a CeedOperator.
982            Note: Lengths of -1 indicate that the CeedOperator does not have an
983            active input and/or output.
984 
985   @param[in] op           CeedOperator
986   @param[out] input_size  Variable to store active input vector length, or NULL
987   @param[out] output_size Variable to store active output vector length, or NULL
988 
989   @return An error code: 0 - success, otherwise - failure
990 
991   @ref User
992 **/
993 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size,
994                                        CeedSize *output_size) {
995   int ierr;
996   bool is_composite;
997 
998   if (input_size) *input_size = -1;
999   if (output_size) *output_size = -1;
1000 
1001   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1002   if (is_composite) {
1003     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1004       CeedSize sub_input_size, sub_output_size;
1005       ierr = CeedOperatorGetActiveVectorLengths(op->sub_operators[i],
1006              input_size ? &sub_input_size : NULL,
1007              output_size ? &sub_output_size : NULL); CeedChk(ierr);
1008       if (input_size && sub_input_size != -1) *input_size = sub_input_size;
1009       if (output_size && sub_output_size != -1) *output_size = sub_output_size;
1010     }
1011   } else {
1012     if (input_size) {
1013       for (CeedInt i = 0; i < op->qf->num_input_fields; i++) {
1014         if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1015           ierr = CeedElemRestrictionGetLVectorSize(op->input_fields[i]->elem_restr,
1016                  input_size); CeedChk(ierr);
1017           break;
1018         }
1019       }
1020     }
1021     if (output_size) {
1022       for (CeedInt i = 0; i < op->qf->num_output_fields; i++) {
1023         if (op->output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1024           ierr = CeedElemRestrictionGetLVectorSize(op->output_fields[i]->elem_restr,
1025                  output_size); CeedChk(ierr);
1026           break;
1027         }
1028       }
1029     }
1030   }
1031 
1032   return CEED_ERROR_SUCCESS;
1033 }
1034 
1035 /**
1036   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
1037            When `reuse_assembly_data = false` (default), the CeedQFunction associated
1038            with this CeedOperator is re-assembled every time a `CeedOperatorLinearAssemble*`
1039            function is called.
1040            When `reuse_assembly_data = true`, the CeedQFunction associated with
1041            this CeedOperator is reused between calls to
1042            `CeedOperatorSetQFunctionAssemblyDataUpdated`.
1043 
1044   @param[in] op                  CeedOperator
1045   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1046 
1047   @return An error code: 0 - success, otherwise - failure
1048 
1049   @ref Advanced
1050 **/
1051 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op,
1052     bool reuse_assembly_data) {
1053   int ierr;
1054   bool is_composite;
1055 
1056   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1057   if (is_composite) {
1058     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1059       ierr = CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i],
1060              reuse_assembly_data); CeedChk(ierr);
1061     }
1062   } else {
1063     ierr = CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data);
1064     CeedChk(ierr);
1065   }
1066 
1067   return CEED_ERROR_SUCCESS;
1068 }
1069 
1070 /**
1071   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1072 
1073   @param[in] op                  CeedOperator
1074   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1075 
1076   @return An error code: 0 - success, otherwise - failure
1077 
1078   @ref Advanced
1079 **/
1080 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op,
1081     bool needs_data_update) {
1082   int ierr;
1083   bool is_composite;
1084 
1085   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1086   if (is_composite) {
1087     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1088       ierr = CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i],
1089              needs_data_update); CeedChk(ierr);
1090     }
1091   } else {
1092     ierr = CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled,
1093            needs_data_update);
1094     CeedChk(ierr);
1095   }
1096 
1097   return CEED_ERROR_SUCCESS;
1098 }
1099 
1100 /**
1101   @brief Set the number of quadrature points associated with a CeedOperator.
1102            This should be used when creating a CeedOperator where every
1103            field has a collocated basis. This function cannot be used for
1104            composite CeedOperators.
1105 
1106   @param op        CeedOperator
1107   @param num_qpts  Number of quadrature points to set
1108 
1109   @return An error code: 0 - success, otherwise - failure
1110 
1111   @ref Advanced
1112 **/
1113 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1114   if (op->is_composite)
1115     // LCOV_EXCL_START
1116     return CeedError(op->ceed, CEED_ERROR_MINOR,
1117                      "Not defined for composite operator");
1118   // LCOV_EXCL_STOP
1119   if (op->num_qpts)
1120     // LCOV_EXCL_START
1121     return CeedError(op->ceed, CEED_ERROR_MINOR,
1122                      "Number of quadrature points already defined");
1123   // LCOV_EXCL_STOP
1124   if (op->is_immutable)
1125     // LCOV_EXCL_START
1126     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1127                      "Operator cannot be changed after set as immutable");
1128   // LCOV_EXCL_STOP
1129 
1130   op->num_qpts = num_qpts;
1131   return CEED_ERROR_SUCCESS;
1132 }
1133 
1134 /**
1135   @brief View a CeedOperator
1136 
1137   @param[in] op      CeedOperator to view
1138   @param[in] stream  Stream to write; typically stdout/stderr or a file
1139 
1140   @return Error code: 0 - success, otherwise - failure
1141 
1142   @ref User
1143 **/
1144 int CeedOperatorView(CeedOperator op, FILE *stream) {
1145   int ierr;
1146 
1147   if (op->is_composite) {
1148     fprintf(stream, "Composite CeedOperator\n");
1149 
1150     for (CeedInt i=0; i<op->num_suboperators; i++) {
1151       fprintf(stream, "  SubOperator [%d]:\n", i);
1152       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1153       CeedChk(ierr);
1154     }
1155   } else {
1156     fprintf(stream, "CeedOperator\n");
1157     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1158   }
1159   return CEED_ERROR_SUCCESS;
1160 }
1161 
1162 /**
1163   @brief Get the Ceed associated with a CeedOperator
1164 
1165   @param op         CeedOperator
1166   @param[out] ceed  Variable to store Ceed
1167 
1168   @return An error code: 0 - success, otherwise - failure
1169 
1170   @ref Advanced
1171 **/
1172 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1173   *ceed = op->ceed;
1174   return CEED_ERROR_SUCCESS;
1175 }
1176 
1177 /**
1178   @brief Get the number of elements associated with a CeedOperator
1179 
1180   @param op             CeedOperator
1181   @param[out] num_elem  Variable to store number of elements
1182 
1183   @return An error code: 0 - success, otherwise - failure
1184 
1185   @ref Advanced
1186 **/
1187 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1188   if (op->is_composite)
1189     // LCOV_EXCL_START
1190     return CeedError(op->ceed, CEED_ERROR_MINOR,
1191                      "Not defined for composite operator");
1192   // LCOV_EXCL_STOP
1193 
1194   *num_elem = op->num_elem;
1195   return CEED_ERROR_SUCCESS;
1196 }
1197 
1198 /**
1199   @brief Get the number of quadrature points associated with a CeedOperator
1200 
1201   @param op             CeedOperator
1202   @param[out] num_qpts  Variable to store vector number of quadrature points
1203 
1204   @return An error code: 0 - success, otherwise - failure
1205 
1206   @ref Advanced
1207 **/
1208 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1209   if (op->is_composite)
1210     // LCOV_EXCL_START
1211     return CeedError(op->ceed, CEED_ERROR_MINOR,
1212                      "Not defined for composite operator");
1213   // LCOV_EXCL_STOP
1214 
1215   *num_qpts = op->num_qpts;
1216   return CEED_ERROR_SUCCESS;
1217 }
1218 
1219 /**
1220   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1221            field has been registered with this `field_name`.
1222 
1223   @param[in] op            CeedOperator
1224   @param[in] field_name    Name of field to retrieve label
1225   @param[out] field_label  Variable to field label
1226 
1227   @return An error code: 0 - success, otherwise - failure
1228 
1229   @ref User
1230 **/
1231 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1232                                      const char *field_name,
1233                                      CeedContextFieldLabel *field_label) {
1234   int ierr;
1235 
1236   bool is_composite;
1237   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1238   if (is_composite) {
1239     // Check if composite label already created
1240     for (CeedInt i=0; i<op->num_context_labels; i++) {
1241       if (!strcmp(op->context_labels[i]->name, field_name)) {
1242         *field_label = op->context_labels[i];
1243         return CEED_ERROR_SUCCESS;
1244       }
1245     }
1246 
1247     // Create composite label if needed
1248     CeedInt num_sub;
1249     CeedOperator *sub_operators;
1250     CeedContextFieldLabel new_field_label;
1251 
1252     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1253     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1254     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1255     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1256     new_field_label->num_sub_labels = num_sub;
1257 
1258     bool label_found = false;
1259     for (CeedInt i=0; i<num_sub; i++) {
1260       if (sub_operators[i]->qf->ctx) {
1261         CeedContextFieldLabel new_field_label_i;
1262         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1263                &new_field_label_i); CeedChk(ierr);
1264         if (new_field_label_i) {
1265           label_found = true;
1266           new_field_label->sub_labels[i] = new_field_label_i;
1267           new_field_label->name = new_field_label_i->name;
1268           new_field_label->description = new_field_label_i->description;
1269           if (new_field_label->type &&
1270               new_field_label->type != new_field_label_i->type) {
1271             // LCOV_EXCL_START
1272             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1273             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1274                              "Incompatible field types on sub-operator contexts. "
1275                              "%s != %s",
1276                              CeedContextFieldTypes[new_field_label->type],
1277                              CeedContextFieldTypes[new_field_label_i->type]);
1278             // LCOV_EXCL_STOP
1279           } else {
1280             new_field_label->type = new_field_label_i->type;
1281           }
1282           if (new_field_label->num_values != 0 &&
1283               new_field_label->num_values != new_field_label_i->num_values) {
1284             // LCOV_EXCL_START
1285             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1286             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1287                              "Incompatible field number of values on sub-operator"
1288                              " contexts. %ld != %ld",
1289                              new_field_label->num_values, new_field_label_i->num_values);
1290             // LCOV_EXCL_STOP
1291           } else {
1292             new_field_label->num_values = new_field_label_i->num_values;
1293           }
1294         }
1295       }
1296     }
1297     if (!label_found) {
1298       // LCOV_EXCL_START
1299       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1300       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1301       *field_label = NULL;
1302       // LCOV_EXCL_STOP
1303     } else {
1304       // Move new composite label to operator
1305       if (op->num_context_labels == 0) {
1306         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1307         op->max_context_labels = 1;
1308       } else if (op->num_context_labels == op->max_context_labels) {
1309         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1310         CeedChk(ierr);
1311         op->max_context_labels *= 2;
1312       }
1313       op->context_labels[op->num_context_labels] = new_field_label;
1314       *field_label = new_field_label;
1315       op->num_context_labels++;
1316     }
1317 
1318     return CEED_ERROR_SUCCESS;
1319   } else {
1320     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1321   }
1322 }
1323 
1324 /**
1325   @brief Set QFunctionContext field holding a double precision value.
1326            For composite operators, the value is set in all
1327            sub-operator QFunctionContexts that have a matching `field_name`.
1328 
1329   @param op          CeedOperator
1330   @param field_label Label of field to register
1331   @param values      Values to set
1332 
1333   @return An error code: 0 - success, otherwise - failure
1334 
1335   @ref User
1336 **/
1337 int CeedOperatorContextSetDouble(CeedOperator op,
1338                                  CeedContextFieldLabel field_label,
1339                                  double *values) {
1340   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1341                                        values);
1342 }
1343 
1344 /**
1345   @brief Set QFunctionContext field holding an int32 value.
1346            For composite operators, the value is set in all
1347            sub-operator QFunctionContexts that have a matching `field_name`.
1348 
1349   @param op          CeedOperator
1350   @param field_label Label of field to set
1351   @param values      Values to set
1352 
1353   @return An error code: 0 - success, otherwise - failure
1354 
1355   @ref User
1356 **/
1357 int CeedOperatorContextSetInt32(CeedOperator op,
1358                                 CeedContextFieldLabel field_label,
1359                                 int *values) {
1360   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1361                                        values);
1362 }
1363 
1364 /**
1365   @brief Apply CeedOperator to a vector
1366 
1367   This computes the action of the operator on the specified (active) input,
1368   yielding its (active) output.  All inputs and outputs must be specified using
1369   CeedOperatorSetField().
1370 
1371   Note: Calling this function asserts that setup is complete
1372           and sets the CeedOperator as immutable.
1373 
1374   @param op        CeedOperator to apply
1375   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1376                      there are no active inputs
1377   @param[out] out  CeedVector to store result of applying operator (must be
1378                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1379                      active outputs
1380   @param request   Address of CeedRequest for non-blocking completion, else
1381                      @ref CEED_REQUEST_IMMEDIATE
1382 
1383   @return An error code: 0 - success, otherwise - failure
1384 
1385   @ref User
1386 **/
1387 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1388                       CeedRequest *request) {
1389   int ierr;
1390   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1391 
1392   if (op->num_elem)  {
1393     // Standard Operator
1394     if (op->Apply) {
1395       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1396     } else {
1397       // Zero all output vectors
1398       CeedQFunction qf = op->qf;
1399       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1400         CeedVector vec = op->output_fields[i]->vec;
1401         if (vec == CEED_VECTOR_ACTIVE)
1402           vec = out;
1403         if (vec != CEED_VECTOR_NONE) {
1404           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1405         }
1406       }
1407       // Apply
1408       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1409     }
1410   } else if (op->is_composite) {
1411     // Composite Operator
1412     if (op->ApplyComposite) {
1413       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1414     } else {
1415       CeedInt num_suboperators;
1416       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1417       CeedOperator *sub_operators;
1418       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1419 
1420       // Zero all output vectors
1421       if (out != CEED_VECTOR_NONE) {
1422         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1423       }
1424       for (CeedInt i=0; i<num_suboperators; i++) {
1425         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1426           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1427           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1428             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1429           }
1430         }
1431       }
1432       // Apply
1433       for (CeedInt i=0; i<op->num_suboperators; i++) {
1434         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1435         CeedChk(ierr);
1436       }
1437     }
1438   }
1439   return CEED_ERROR_SUCCESS;
1440 }
1441 
1442 /**
1443   @brief Apply CeedOperator to a vector and add result to output vector
1444 
1445   This computes the action of the operator on the specified (active) input,
1446   yielding its (active) output.  All inputs and outputs must be specified using
1447   CeedOperatorSetField().
1448 
1449   @param op        CeedOperator to apply
1450   @param[in] in    CeedVector containing input state or NULL if there are no
1451                      active inputs
1452   @param[out] out  CeedVector to sum in result of applying operator (must be
1453                      distinct from @a in) or NULL if there are no active outputs
1454   @param request   Address of CeedRequest for non-blocking completion, else
1455                      @ref CEED_REQUEST_IMMEDIATE
1456 
1457   @return An error code: 0 - success, otherwise - failure
1458 
1459   @ref User
1460 **/
1461 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1462                          CeedRequest *request) {
1463   int ierr;
1464   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1465 
1466   if (op->num_elem)  {
1467     // Standard Operator
1468     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1469   } else if (op->is_composite) {
1470     // Composite Operator
1471     if (op->ApplyAddComposite) {
1472       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1473     } else {
1474       CeedInt num_suboperators;
1475       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1476       CeedOperator *sub_operators;
1477       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1478 
1479       for (CeedInt i=0; i<num_suboperators; i++) {
1480         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1481         CeedChk(ierr);
1482       }
1483     }
1484   }
1485   return CEED_ERROR_SUCCESS;
1486 }
1487 
1488 /**
1489   @brief Destroy a CeedOperator
1490 
1491   @param op  CeedOperator to destroy
1492 
1493   @return An error code: 0 - success, otherwise - failure
1494 
1495   @ref User
1496 **/
1497 int CeedOperatorDestroy(CeedOperator *op) {
1498   int ierr;
1499 
1500   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1501   if ((*op)->Destroy) {
1502     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1503   }
1504   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1505   // Free fields
1506   for (CeedInt i=0; i<(*op)->num_fields; i++)
1507     if ((*op)->input_fields[i]) {
1508       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1509         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1510         CeedChk(ierr);
1511       }
1512       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1513         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1514       }
1515       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1516           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1517         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1518       }
1519       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1520       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1521     }
1522   for (CeedInt i=0; i<(*op)->num_fields; i++)
1523     if ((*op)->output_fields[i]) {
1524       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1525       CeedChk(ierr);
1526       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1527         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1528       }
1529       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1530           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1531         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1532       }
1533       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1534       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1535     }
1536   // Destroy sub_operators
1537   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1538     if ((*op)->sub_operators[i]) {
1539       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1540     }
1541   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1542   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1543   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1544   // Destroy any composite labels
1545   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1546     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1547     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1548   }
1549   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1550 
1551   // Destroy fallback
1552   if ((*op)->op_fallback) {
1553     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1554     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1555     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1556     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1557   }
1558 
1559   // Destroy QF assembly cache
1560   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1561 
1562   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1563   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1564   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1565   ierr = CeedFree(op); CeedChk(ierr);
1566   return CEED_ERROR_SUCCESS;
1567 }
1568 
1569 /// @}
1570