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