xref: /libCEED/interface/ceed-operator.c (revision ff40a6a712cac5a35ddf9c674f7779549394649b)
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, Q_comp = 1, restr_num_comp = 1,
41           size = qf_field->size;
42   // Restriction
43   if (r != CEED_ELEMRESTRICTION_NONE) {
44     if (eval_mode == CEED_EVAL_WEIGHT) {
45       // LCOV_EXCL_START
46       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
47                        "CEED_ELEMRESTRICTION_NONE should be used "
48                        "for a field with eval mode CEED_EVAL_WEIGHT");
49       // LCOV_EXCL_STOP
50     }
51     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
52     CeedChk(ierr);
53   }
54   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
55     // LCOV_EXCL_START
56     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
57                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
58                      "must be used together.");
59     // LCOV_EXCL_STOP
60   }
61   // Basis
62   if (b != CEED_BASIS_COLLOCATED) {
63     if (eval_mode == CEED_EVAL_NONE)
64       // LCOV_EXCL_START
65       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
66                        "Field '%s' configured with CEED_EVAL_NONE must "
67                        "be used with CEED_BASIS_COLLOCATED",
68                        qf_field->field_name);
69     // LCOV_EXCL_STOP
70     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
71     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
72     ierr = CeedBasisGetNumQuadratureComponents(b, &Q_comp); CeedChk(ierr);
73     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
74       // LCOV_EXCL_START
75       return CeedError(ceed, CEED_ERROR_DIMENSION,
76                        "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction "
77                        "has %" CeedInt_FMT " components, but Basis has %" CeedInt_FMT " components",
78                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
79                        restr_num_comp, num_comp);
80       // LCOV_EXCL_STOP
81     }
82   } else if (eval_mode != CEED_EVAL_NONE) {
83     // LCOV_EXCL_START
84     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
85                      "Field '%s' configured with %s cannot "
86                      "be used with CEED_BASIS_COLLOCATED",
87                      qf_field->field_name, CeedEvalModes[eval_mode]);
88     // LCOV_EXCL_STOP
89 
90   }
91   // Field size
92   switch(eval_mode) {
93   case CEED_EVAL_NONE:
94     if (size != restr_num_comp)
95       // LCOV_EXCL_START
96       return CeedError(ceed, CEED_ERROR_DIMENSION,
97                        "Field '%s' of size %" CeedInt_FMT " and EvalMode %s: ElemRestriction has "
98                        CeedInt_FMT " components",
99                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
100                        restr_num_comp);
101     // LCOV_EXCL_STOP
102     break;
103   case CEED_EVAL_INTERP:
104     if (size != num_comp*Q_comp)
105       // LCOV_EXCL_START
106       return CeedError(ceed, CEED_ERROR_DIMENSION,
107                        "Field '%s' of size %" CeedInt_FMT
108                        " and EvalMode %s: ElemRestriction/Basis has "
109                        CeedInt_FMT " components",
110                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
111                        num_comp*Q_comp);
112     // LCOV_EXCL_STOP
113     break;
114   case CEED_EVAL_GRAD:
115     if (size != num_comp * dim)
116       // LCOV_EXCL_START
117       return CeedError(ceed, CEED_ERROR_DIMENSION,
118                        "Field '%s' of size %" CeedInt_FMT " and EvalMode %s in %" CeedInt_FMT
119                        " dimensions: "
120                        "ElemRestriction/Basis has %" CeedInt_FMT " components",
121                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
122                        num_comp);
123     // LCOV_EXCL_STOP
124     break;
125   case CEED_EVAL_WEIGHT:
126     // No additional checks required
127     break;
128   case CEED_EVAL_DIV:
129     if (size != num_comp)
130       // LCOV_EXCL_START
131       return CeedError(ceed, CEED_ERROR_DIMENSION,
132                        "Field '%s' of size %" CeedInt_FMT
133                        " and EvalMode %s: ElemRestriction/Basis has "
134                        CeedInt_FMT " components",
135                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
136                        num_comp);
137     // LCOV_EXCL_STOP
138     break;
139   case CEED_EVAL_CURL:
140     // Not implemented
141     break;
142   }
143   return CEED_ERROR_SUCCESS;
144 }
145 
146 /**
147   @brief View a field of a CeedOperator
148 
149   @param[in] field         Operator field to view
150   @param[in] qf_field      QFunction field (carries field name)
151   @param[in] field_number  Number of field being viewed
152   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
153   @param[in] input         true for an input field; false for output field
154   @param[in] stream        Stream to view to, e.g., stdout
155 
156   @return An error code: 0 - success, otherwise - failure
157 
158   @ref Utility
159 **/
160 static int CeedOperatorFieldView(CeedOperatorField field,
161                                  CeedQFunctionField qf_field,
162                                  CeedInt field_number, bool sub, bool input,
163                                  FILE *stream) {
164   const char *pre = sub ? "  " : "";
165   const char *in_out = input ? "Input" : "Output";
166 
167   fprintf(stream, "%s    %s field %" CeedInt_FMT ":\n"
168           "%s      Name: \"%s\"\n",
169           pre, in_out, field_number, pre, qf_field->field_name);
170 
171   fprintf(stream, "%s      Size: %" CeedInt_FMT "\n", pre, qf_field->size);
172 
173   fprintf(stream, "%s      EvalMode: %s\n", pre,
174           CeedEvalModes[qf_field->eval_mode]);
175 
176   if (field->basis == CEED_BASIS_COLLOCATED)
177     fprintf(stream, "%s      Collocated basis\n", pre);
178 
179   if (field->vec == CEED_VECTOR_ACTIVE)
180     fprintf(stream, "%s      Active vector\n", pre);
181   else if (field->vec == CEED_VECTOR_NONE)
182     fprintf(stream, "%s      No vector\n", pre);
183 
184   return CEED_ERROR_SUCCESS;
185 }
186 
187 /**
188   @brief View a single CeedOperator
189 
190   @param[in] op      CeedOperator to view
191   @param[in] sub     Boolean flag for sub-operator
192   @param[in] stream  Stream to write; typically stdout/stderr or a file
193 
194   @return Error code: 0 - success, otherwise - failure
195 
196   @ref Utility
197 **/
198 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
199   int ierr;
200   const char *pre = sub ? "  " : "";
201 
202   CeedInt num_elem, num_qpts;
203   ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChk(ierr);
204   ierr = CeedOperatorGetNumQuadraturePoints(op, &num_qpts); CeedChk(ierr);
205 
206   CeedInt total_fields = 0;
207   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
208   fprintf(stream, "%s  %" CeedInt_FMT " elements with %" CeedInt_FMT
209           " quadrature points each\n",
210           pre, num_elem, num_qpts);
211 
212   fprintf(stream, "%s  %" CeedInt_FMT " field%s\n", pre, total_fields,
213           total_fields>1 ? "s" : "");
214 
215   fprintf(stream, "%s  %" CeedInt_FMT " input field%s:\n", pre,
216           op->qf->num_input_fields,
217           op->qf->num_input_fields>1 ? "s" : "");
218   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
219     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
220                                  i, sub, 1, stream); CeedChk(ierr);
221   }
222 
223   fprintf(stream, "%s  %" CeedInt_FMT " output field%s:\n", pre,
224           op->qf->num_output_fields,
225           op->qf->num_output_fields>1 ? "s" : "");
226   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
227     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
228                                  i, sub, 0, stream); CeedChk(ierr);
229   }
230   return CEED_ERROR_SUCCESS;
231 }
232 
233 /**
234   @brief Find the active vector basis for a non-composite CeedOperator
235 
236   @param[in] op             CeedOperator to find active basis for
237   @param[out] active_basis  Basis for active input vector or NULL for composite operator
238 
239   @return An error code: 0 - success, otherwise - failure
240 
241   @ ref Developer
242 **/
243 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
244   *active_basis = NULL;
245   if (op->is_composite) return CEED_ERROR_SUCCESS;
246   for (CeedInt i = 0; i < op->qf->num_input_fields; i++)
247     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
248       *active_basis = op->input_fields[i]->basis;
249       break;
250     }
251 
252   if (!*active_basis) {
253     // LCOV_EXCL_START
254     int ierr;
255     Ceed ceed;
256     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
257     return CeedError(ceed, CEED_ERROR_MINOR,
258                      "No active CeedBasis found");
259     // LCOV_EXCL_STOP
260   }
261   return CEED_ERROR_SUCCESS;
262 }
263 
264 /**
265   @brief Find the active vector ElemRestriction for a non-composite CeedOperator
266 
267   @param[in] op            CeedOperator to find active basis for
268   @param[out] active_rstr  ElemRestriction for active input vector or NULL for composite operator
269 
270   @return An error code: 0 - success, otherwise - failure
271 
272   @ref Utility
273 **/
274 int CeedOperatorGetActiveElemRestriction(CeedOperator op,
275     CeedElemRestriction *active_rstr) {
276   *active_rstr = NULL;
277   if (op->is_composite) return CEED_ERROR_SUCCESS;
278   for (CeedInt i = 0; i < op->qf->num_input_fields; i++)
279     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
280       *active_rstr = op->input_fields[i]->elem_restr;
281       break;
282     }
283 
284   if (!*active_rstr) {
285     // LCOV_EXCL_START
286     int ierr;
287     Ceed ceed;
288     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
289     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
290                      "No active CeedElemRestriction found");
291     // LCOV_EXCL_STOP
292   }
293   return CEED_ERROR_SUCCESS;
294 }
295 
296 /**
297   @brief Set QFunctionContext field value of the specified type.
298            For composite operators, the value is set in all
299            sub-operator QFunctionContexts that have a matching `field_name`.
300            A non-zero error code is returned for single operators
301            that do not have a matching field of the same type or composite
302            operators that do not have any field of a matching type.
303 
304   @param op          CeedOperator
305   @param field_label Label of field to set
306   @param field_type  Type of field to set
307   @param value       Value to set
308 
309   @return An error code: 0 - success, otherwise - failure
310 
311   @ref User
312 **/
313 static int CeedOperatorContextSetGeneric(CeedOperator op,
314     CeedContextFieldLabel field_label, CeedContextFieldType field_type,
315     void *value) {
316   int ierr;
317 
318   if (!field_label)
319     // LCOV_EXCL_START
320     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
321                      "Invalid field label");
322   // LCOV_EXCL_STOP
323 
324   bool is_composite = false;
325   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
326   if (is_composite) {
327     CeedInt num_sub;
328     CeedOperator *sub_operators;
329 
330     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
331     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
332     if (num_sub != field_label->num_sub_labels)
333       // LCOV_EXCL_START
334       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
335                        "ContextLabel does not correspond to composite operator.\n"
336                        "Use CeedOperatorGetContextFieldLabel().");
337     // LCOV_EXCL_STOP
338 
339     for (CeedInt i = 0; i < num_sub; i++) {
340       // Try every sub-operator, ok if some sub-operators do not have field
341       if (field_label->sub_labels[i] && sub_operators[i]->qf->ctx) {
342         ierr = CeedQFunctionContextSetGeneric(sub_operators[i]->qf->ctx,
343                                               field_label->sub_labels[i],
344                                               field_type, value); CeedChk(ierr);
345       }
346     }
347   } else {
348     if (!op->qf->ctx)
349       // LCOV_EXCL_START
350       return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
351                        "QFunction does not have context data");
352     // LCOV_EXCL_STOP
353 
354     ierr = CeedQFunctionContextSetGeneric(op->qf->ctx, field_label,
355                                           field_type, value); CeedChk(ierr);
356   }
357 
358   return CEED_ERROR_SUCCESS;
359 }
360 
361 /// @}
362 
363 /// ----------------------------------------------------------------------------
364 /// CeedOperator Backend API
365 /// ----------------------------------------------------------------------------
366 /// @addtogroup CeedOperatorBackend
367 /// @{
368 
369 /**
370   @brief Get the number of arguments associated with a CeedOperator
371 
372   @param op             CeedOperator
373   @param[out] num_args  Variable to store vector number of arguments
374 
375   @return An error code: 0 - success, otherwise - failure
376 
377   @ref Backend
378 **/
379 
380 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
381   if (op->is_composite)
382     // LCOV_EXCL_START
383     return CeedError(op->ceed, CEED_ERROR_MINOR,
384                      "Not defined for composite operators");
385   // LCOV_EXCL_STOP
386 
387   *num_args = op->num_fields;
388   return CEED_ERROR_SUCCESS;
389 }
390 
391 /**
392   @brief Get the setup status of a CeedOperator
393 
394   @param op                  CeedOperator
395   @param[out] is_setup_done  Variable to store setup status
396 
397   @return An error code: 0 - success, otherwise - failure
398 
399   @ref Backend
400 **/
401 
402 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
403   *is_setup_done = op->is_backend_setup;
404   return CEED_ERROR_SUCCESS;
405 }
406 
407 /**
408   @brief Get the QFunction associated with a CeedOperator
409 
410   @param op       CeedOperator
411   @param[out] qf  Variable to store QFunction
412 
413   @return An error code: 0 - success, otherwise - failure
414 
415   @ref Backend
416 **/
417 
418 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
419   if (op->is_composite)
420     // LCOV_EXCL_START
421     return CeedError(op->ceed, CEED_ERROR_MINOR,
422                      "Not defined for composite operator");
423   // LCOV_EXCL_STOP
424 
425   *qf = op->qf;
426   return CEED_ERROR_SUCCESS;
427 }
428 
429 /**
430   @brief Get a boolean value indicating if the CeedOperator is composite
431 
432   @param op                 CeedOperator
433   @param[out] is_composite  Variable to store composite status
434 
435   @return An error code: 0 - success, otherwise - failure
436 
437   @ref Backend
438 **/
439 
440 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
441   *is_composite = op->is_composite;
442   return CEED_ERROR_SUCCESS;
443 }
444 
445 /**
446   @brief Get the number of sub_operators associated with a CeedOperator
447 
448   @param op                     CeedOperator
449   @param[out] num_suboperators  Variable to store number of sub_operators
450 
451   @return An error code: 0 - success, otherwise - failure
452 
453   @ref Backend
454 **/
455 
456 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
457   if (!op->is_composite)
458     // LCOV_EXCL_START
459     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
460   // LCOV_EXCL_STOP
461 
462   *num_suboperators = op->num_suboperators;
463   return CEED_ERROR_SUCCESS;
464 }
465 
466 /**
467   @brief Get the list of sub_operators associated with a CeedOperator
468 
469   @param op                  CeedOperator
470   @param[out] sub_operators  Variable to store list of sub_operators
471 
472   @return An error code: 0 - success, otherwise - failure
473 
474   @ref Backend
475 **/
476 
477 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
478   if (!op->is_composite)
479     // LCOV_EXCL_START
480     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
481   // LCOV_EXCL_STOP
482 
483   *sub_operators = op->sub_operators;
484   return CEED_ERROR_SUCCESS;
485 }
486 
487 /**
488   @brief Get the backend data of a CeedOperator
489 
490   @param op         CeedOperator
491   @param[out] data  Variable to store data
492 
493   @return An error code: 0 - success, otherwise - failure
494 
495   @ref Backend
496 **/
497 
498 int CeedOperatorGetData(CeedOperator op, void *data) {
499   *(void **)data = op->data;
500   return CEED_ERROR_SUCCESS;
501 }
502 
503 /**
504   @brief Set the backend data of a CeedOperator
505 
506   @param[out] op  CeedOperator
507   @param data     Data to set
508 
509   @return An error code: 0 - success, otherwise - failure
510 
511   @ref Backend
512 **/
513 
514 int CeedOperatorSetData(CeedOperator op, void *data) {
515   op->data = data;
516   return CEED_ERROR_SUCCESS;
517 }
518 
519 /**
520   @brief Increment the reference counter for a CeedOperator
521 
522   @param op  CeedOperator to increment the reference counter
523 
524   @return An error code: 0 - success, otherwise - failure
525 
526   @ref Backend
527 **/
528 int CeedOperatorReference(CeedOperator op) {
529   op->ref_count++;
530   return CEED_ERROR_SUCCESS;
531 }
532 
533 /**
534   @brief Set the setup flag of a CeedOperator to True
535 
536   @param op  CeedOperator
537 
538   @return An error code: 0 - success, otherwise - failure
539 
540   @ref Backend
541 **/
542 
543 int CeedOperatorSetSetupDone(CeedOperator op) {
544   op->is_backend_setup = true;
545   return CEED_ERROR_SUCCESS;
546 }
547 
548 /// @}
549 
550 /// ----------------------------------------------------------------------------
551 /// CeedOperator Public API
552 /// ----------------------------------------------------------------------------
553 /// @addtogroup CeedOperatorUser
554 /// @{
555 
556 /**
557   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
558            CeedElemRestriction can be associated with CeedQFunction fields with
559            \ref CeedOperatorSetField.
560 
561   @param ceed     A Ceed object where the CeedOperator will be created
562   @param qf       QFunction defining the action of the operator at quadrature points
563   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
564                     @ref CEED_QFUNCTION_NONE)
565   @param dqfT     QFunction defining the action of the transpose of the Jacobian
566                     of @a qf (or @ref CEED_QFUNCTION_NONE)
567   @param[out] op  Address of the variable where the newly created
568                     CeedOperator will be stored
569 
570   @return An error code: 0 - success, otherwise - failure
571 
572   @ref User
573  */
574 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
575                        CeedQFunction dqfT, CeedOperator *op) {
576   int ierr;
577 
578   if (!ceed->OperatorCreate) {
579     Ceed delegate;
580     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
581 
582     if (!delegate)
583       // LCOV_EXCL_START
584       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
585                        "Backend does not support OperatorCreate");
586     // LCOV_EXCL_STOP
587 
588     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
589     return CEED_ERROR_SUCCESS;
590   }
591 
592   if (!qf || qf == CEED_QFUNCTION_NONE)
593     // LCOV_EXCL_START
594     return CeedError(ceed, CEED_ERROR_MINOR,
595                      "Operator must have a valid QFunction.");
596   // LCOV_EXCL_STOP
597   ierr = CeedCalloc(1, op); CeedChk(ierr);
598   (*op)->ceed = ceed;
599   ierr = CeedReference(ceed); CeedChk(ierr);
600   (*op)->ref_count = 1;
601   (*op)->qf = qf;
602   (*op)->input_size = -1;
603   (*op)->output_size = -1;
604   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
605   if (dqf && dqf != CEED_QFUNCTION_NONE) {
606     (*op)->dqf = dqf;
607     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
608   }
609   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
610     (*op)->dqfT = dqfT;
611     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
612   }
613   ierr = CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled);
614   CeedChk(ierr);
615   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields); CeedChk(ierr);
616   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields); CeedChk(ierr);
617   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
618   return CEED_ERROR_SUCCESS;
619 }
620 
621 /**
622   @brief Create an operator that composes the action of several operators
623 
624   @param ceed     A Ceed object where the CeedOperator will be created
625   @param[out] op  Address of the variable where the newly created
626                     Composite CeedOperator will be stored
627 
628   @return An error code: 0 - success, otherwise - failure
629 
630   @ref User
631  */
632 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
633   int ierr;
634 
635   if (!ceed->CompositeOperatorCreate) {
636     Ceed delegate;
637     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
638 
639     if (delegate) {
640       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
641       return CEED_ERROR_SUCCESS;
642     }
643   }
644 
645   ierr = CeedCalloc(1, op); CeedChk(ierr);
646   (*op)->ceed = ceed;
647   ierr = CeedReference(ceed); CeedChk(ierr);
648   (*op)->ref_count = 1;
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