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