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