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