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