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