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