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