xref: /libCEED/interface/ceed-operator.c (revision 6e15d496119073f7bc35f61df7ac100e9376600e)
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] needs_data_update 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 Estimate number of FLOPs required to apply CeedOperator on the active vector
1253 
1254   @param op    Operator to estimate FLOPs for
1255   @param flops Address of variable to hold FLOPs estimate
1256 
1257   @ref Backend
1258 **/
1259 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedInt *flops) {
1260   int ierr;
1261   bool is_composite;
1262   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1263 
1264   *flops = 0;
1265   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1266   if (is_composite) {
1267     CeedInt num_suboperators;
1268     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1269     CeedOperator *sub_operators;
1270     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1271 
1272     // FLOPs for each suboperator
1273     for (CeedInt i = 0; i < num_suboperators; i++) {
1274       CeedInt suboperator_flops;
1275       ierr = CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops);
1276       CeedChk(ierr);
1277       *flops += suboperator_flops;
1278     }
1279   } else {
1280     CeedInt num_input_fields, num_output_fields;
1281     CeedOperatorField *input_fields, *output_fields;
1282 
1283     ierr = CeedOperatorGetFields(op, &num_input_fields, &input_fields,
1284                                  &num_output_fields, &output_fields); CeedChk(ierr);
1285 
1286     CeedInt num_elem = 0;
1287     ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChk(ierr);
1288     // Input FLOPs
1289     for (CeedInt i = 0; i < num_input_fields; i++) {
1290       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1291         CeedInt restr_flops, basis_flops;
1292 
1293         ierr = CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_restr,
1294                CEED_NOTRANSPOSE, &restr_flops); CeedChk(ierr);
1295         *flops += restr_flops;
1296         ierr = CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE,
1297                                          op->qf->input_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1298         *flops += basis_flops * num_elem;
1299       }
1300     }
1301     // QF FLOPs
1302     CeedInt num_qpts, qf_flops;
1303     ierr = CeedOperatorGetNumQuadraturePoints(op, &num_qpts); CeedChk(ierr);
1304     ierr = CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops); CeedChk(ierr);
1305     *flops += num_elem * num_qpts * qf_flops;
1306     // Output FLOPs
1307     for (CeedInt i = 0; i < num_output_fields; i++) {
1308       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1309         CeedInt restr_flops, basis_flops;
1310 
1311         ierr = CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_restr,
1312                CEED_TRANSPOSE, &restr_flops); CeedChk(ierr);
1313         *flops += restr_flops;
1314         ierr = CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE,
1315                                          op->qf->output_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1316         *flops += basis_flops * num_elem;
1317       }
1318     }
1319   }
1320 
1321   return CEED_ERROR_SUCCESS;
1322 }
1323 
1324 /**
1325   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1326            field has been registered with this `field_name`.
1327 
1328   @param[in] op            CeedOperator
1329   @param[in] field_name    Name of field to retrieve label
1330   @param[out] field_label  Variable to field label
1331 
1332   @return An error code: 0 - success, otherwise - failure
1333 
1334   @ref User
1335 **/
1336 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1337                                      const char *field_name,
1338                                      CeedContextFieldLabel *field_label) {
1339   int ierr;
1340 
1341   bool is_composite;
1342   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1343   if (is_composite) {
1344     // Check if composite label already created
1345     for (CeedInt i=0; i<op->num_context_labels; i++) {
1346       if (!strcmp(op->context_labels[i]->name, field_name)) {
1347         *field_label = op->context_labels[i];
1348         return CEED_ERROR_SUCCESS;
1349       }
1350     }
1351 
1352     // Create composite label if needed
1353     CeedInt num_sub;
1354     CeedOperator *sub_operators;
1355     CeedContextFieldLabel new_field_label;
1356 
1357     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1358     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1359     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1360     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1361     new_field_label->num_sub_labels = num_sub;
1362 
1363     bool label_found = false;
1364     for (CeedInt i=0; i<num_sub; i++) {
1365       if (sub_operators[i]->qf->ctx) {
1366         CeedContextFieldLabel new_field_label_i;
1367         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1368                &new_field_label_i); CeedChk(ierr);
1369         if (new_field_label_i) {
1370           label_found = true;
1371           new_field_label->sub_labels[i] = new_field_label_i;
1372           new_field_label->name = new_field_label_i->name;
1373           new_field_label->description = new_field_label_i->description;
1374           if (new_field_label->type &&
1375               new_field_label->type != new_field_label_i->type) {
1376             // LCOV_EXCL_START
1377             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1378             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1379                              "Incompatible field types on sub-operator contexts. "
1380                              "%s != %s",
1381                              CeedContextFieldTypes[new_field_label->type],
1382                              CeedContextFieldTypes[new_field_label_i->type]);
1383             // LCOV_EXCL_STOP
1384           } else {
1385             new_field_label->type = new_field_label_i->type;
1386           }
1387           if (new_field_label->num_values != 0 &&
1388               new_field_label->num_values != new_field_label_i->num_values) {
1389             // LCOV_EXCL_START
1390             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1391             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1392                              "Incompatible field number of values on sub-operator"
1393                              " contexts. %ld != %ld",
1394                              new_field_label->num_values, new_field_label_i->num_values);
1395             // LCOV_EXCL_STOP
1396           } else {
1397             new_field_label->num_values = new_field_label_i->num_values;
1398           }
1399         }
1400       }
1401     }
1402     if (!label_found) {
1403       // LCOV_EXCL_START
1404       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1405       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1406       *field_label = NULL;
1407       // LCOV_EXCL_STOP
1408     } else {
1409       // Move new composite label to operator
1410       if (op->num_context_labels == 0) {
1411         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1412         op->max_context_labels = 1;
1413       } else if (op->num_context_labels == op->max_context_labels) {
1414         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1415         CeedChk(ierr);
1416         op->max_context_labels *= 2;
1417       }
1418       op->context_labels[op->num_context_labels] = new_field_label;
1419       *field_label = new_field_label;
1420       op->num_context_labels++;
1421     }
1422 
1423     return CEED_ERROR_SUCCESS;
1424   } else {
1425     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1426   }
1427 }
1428 
1429 /**
1430   @brief Set QFunctionContext field holding a double precision value.
1431            For composite operators, the value is set in all
1432            sub-operator QFunctionContexts that have a matching `field_name`.
1433 
1434   @param op          CeedOperator
1435   @param field_label Label of field to register
1436   @param values      Values to set
1437 
1438   @return An error code: 0 - success, otherwise - failure
1439 
1440   @ref User
1441 **/
1442 int CeedOperatorContextSetDouble(CeedOperator op,
1443                                  CeedContextFieldLabel field_label,
1444                                  double *values) {
1445   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1446                                        values);
1447 }
1448 
1449 /**
1450   @brief Set QFunctionContext field holding an int32 value.
1451            For composite operators, the value is set in all
1452            sub-operator QFunctionContexts that have a matching `field_name`.
1453 
1454   @param op          CeedOperator
1455   @param field_label Label of field to set
1456   @param values      Values to set
1457 
1458   @return An error code: 0 - success, otherwise - failure
1459 
1460   @ref User
1461 **/
1462 int CeedOperatorContextSetInt32(CeedOperator op,
1463                                 CeedContextFieldLabel field_label,
1464                                 int *values) {
1465   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1466                                        values);
1467 }
1468 
1469 /**
1470   @brief Apply CeedOperator to a vector
1471 
1472   This computes the action of the operator on the specified (active) input,
1473   yielding its (active) output.  All inputs and outputs must be specified using
1474   CeedOperatorSetField().
1475 
1476   Note: Calling this function asserts that setup is complete
1477           and sets the CeedOperator as immutable.
1478 
1479   @param op        CeedOperator to apply
1480   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1481                      there are no active inputs
1482   @param[out] out  CeedVector to store result of applying operator (must be
1483                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1484                      active outputs
1485   @param request   Address of CeedRequest for non-blocking completion, else
1486                      @ref CEED_REQUEST_IMMEDIATE
1487 
1488   @return An error code: 0 - success, otherwise - failure
1489 
1490   @ref User
1491 **/
1492 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1493                       CeedRequest *request) {
1494   int ierr;
1495   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1496 
1497   if (op->num_elem)  {
1498     // Standard Operator
1499     if (op->Apply) {
1500       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1501     } else {
1502       // Zero all output vectors
1503       CeedQFunction qf = op->qf;
1504       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1505         CeedVector vec = op->output_fields[i]->vec;
1506         if (vec == CEED_VECTOR_ACTIVE)
1507           vec = out;
1508         if (vec != CEED_VECTOR_NONE) {
1509           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1510         }
1511       }
1512       // Apply
1513       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1514     }
1515   } else if (op->is_composite) {
1516     // Composite Operator
1517     if (op->ApplyComposite) {
1518       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1519     } else {
1520       CeedInt num_suboperators;
1521       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1522       CeedOperator *sub_operators;
1523       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1524 
1525       // Zero all output vectors
1526       if (out != CEED_VECTOR_NONE) {
1527         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1528       }
1529       for (CeedInt i=0; i<num_suboperators; i++) {
1530         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1531           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1532           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1533             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1534           }
1535         }
1536       }
1537       // Apply
1538       for (CeedInt i=0; i<op->num_suboperators; i++) {
1539         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1540         CeedChk(ierr);
1541       }
1542     }
1543   }
1544   return CEED_ERROR_SUCCESS;
1545 }
1546 
1547 /**
1548   @brief Apply CeedOperator to a vector and add result to output vector
1549 
1550   This computes the action of the operator on the specified (active) input,
1551   yielding its (active) output.  All inputs and outputs must be specified using
1552   CeedOperatorSetField().
1553 
1554   @param op        CeedOperator to apply
1555   @param[in] in    CeedVector containing input state or NULL if there are no
1556                      active inputs
1557   @param[out] out  CeedVector to sum in result of applying operator (must be
1558                      distinct from @a in) or NULL if there are no active outputs
1559   @param request   Address of CeedRequest for non-blocking completion, else
1560                      @ref CEED_REQUEST_IMMEDIATE
1561 
1562   @return An error code: 0 - success, otherwise - failure
1563 
1564   @ref User
1565 **/
1566 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1567                          CeedRequest *request) {
1568   int ierr;
1569   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1570 
1571   if (op->num_elem)  {
1572     // Standard Operator
1573     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1574   } else if (op->is_composite) {
1575     // Composite Operator
1576     if (op->ApplyAddComposite) {
1577       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1578     } else {
1579       CeedInt num_suboperators;
1580       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1581       CeedOperator *sub_operators;
1582       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1583 
1584       for (CeedInt i=0; i<num_suboperators; i++) {
1585         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1586         CeedChk(ierr);
1587       }
1588     }
1589   }
1590   return CEED_ERROR_SUCCESS;
1591 }
1592 
1593 /**
1594   @brief Destroy a CeedOperator
1595 
1596   @param op  CeedOperator to destroy
1597 
1598   @return An error code: 0 - success, otherwise - failure
1599 
1600   @ref User
1601 **/
1602 int CeedOperatorDestroy(CeedOperator *op) {
1603   int ierr;
1604 
1605   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1606   if ((*op)->Destroy) {
1607     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1608   }
1609   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1610   // Free fields
1611   for (CeedInt i=0; i<(*op)->num_fields; i++)
1612     if ((*op)->input_fields[i]) {
1613       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1614         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1615         CeedChk(ierr);
1616       }
1617       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1618         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1619       }
1620       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1621           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1622         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1623       }
1624       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1625       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1626     }
1627   for (CeedInt i=0; i<(*op)->num_fields; i++)
1628     if ((*op)->output_fields[i]) {
1629       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1630       CeedChk(ierr);
1631       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1632         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1633       }
1634       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1635           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1636         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1637       }
1638       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1639       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1640     }
1641   // Destroy sub_operators
1642   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1643     if ((*op)->sub_operators[i]) {
1644       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1645     }
1646   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1647   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1648   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1649   // Destroy any composite labels
1650   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1651     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1652     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1653   }
1654   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1655 
1656   // Destroy fallback
1657   if ((*op)->op_fallback) {
1658     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1659     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1660     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1661     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1662   }
1663 
1664   // Destroy QF assembly cache
1665   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1666 
1667   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1668   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1669   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1670   ierr = CeedFree(op); CeedChk(ierr);
1671   return CEED_ERROR_SUCCESS;
1672 }
1673 
1674 /// @}
1675