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