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