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