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