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