xref: /libCEED/interface/ceed-operator.c (revision cdf95791513f7c35170bef3ba2e19f272fe04533)
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); CeedChk(ierr);
1176       *field_label = NULL;
1177       // LCOV_EXCL_STOP
1178     } else {
1179       // Move new composite label to operator
1180       if (op->num_context_labels == 0) {
1181         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1182         op->max_context_labels = 1;
1183       } else if (op->num_context_labels == op->max_context_labels) {
1184         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1185         CeedChk(ierr);
1186         op->max_context_labels *= 2;
1187       }
1188       op->context_labels[op->num_context_labels] = new_field_label;
1189       *field_label = new_field_label;
1190       op->num_context_labels++;
1191     }
1192 
1193     return CEED_ERROR_SUCCESS;
1194   } else {
1195     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1196   }
1197 }
1198 
1199 /**
1200   @brief Set QFunctionContext field holding a double precision value.
1201            For composite operators, the value is set in all
1202            sub-operator QFunctionContexts that have a matching `field_name`.
1203 
1204   @param op          CeedOperator
1205   @param field_label Label of field to register
1206   @param values      Values to set
1207 
1208   @return An error code: 0 - success, otherwise - failure
1209 
1210   @ref User
1211 **/
1212 int CeedOperatorContextSetDouble(CeedOperator op,
1213                                  CeedContextFieldLabel field_label,
1214                                  double *values) {
1215   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1216                                        values);
1217 }
1218 
1219 /**
1220   @brief Set QFunctionContext field holding an int32 value.
1221            For composite operators, the value is set in all
1222            sub-operator QFunctionContexts that have a matching `field_name`.
1223 
1224   @param op          CeedOperator
1225   @param field_label Label of field to set
1226   @param values      Values to set
1227 
1228   @return An error code: 0 - success, otherwise - failure
1229 
1230   @ref User
1231 **/
1232 int CeedOperatorContextSetInt32(CeedOperator op,
1233                                 CeedContextFieldLabel field_label,
1234                                 int *values) {
1235   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1236                                        values);
1237 }
1238 
1239 /**
1240   @brief Apply CeedOperator to a vector
1241 
1242   This computes the action of the operator on the specified (active) input,
1243   yielding its (active) output.  All inputs and outputs must be specified using
1244   CeedOperatorSetField().
1245 
1246   Note: Calling this function asserts that setup is complete
1247           and sets the CeedOperator as immutable.
1248 
1249   @param op        CeedOperator to apply
1250   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1251                      there are no active inputs
1252   @param[out] out  CeedVector to store result of applying operator (must be
1253                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1254                      active outputs
1255   @param request   Address of CeedRequest for non-blocking completion, else
1256                      @ref CEED_REQUEST_IMMEDIATE
1257 
1258   @return An error code: 0 - success, otherwise - failure
1259 
1260   @ref User
1261 **/
1262 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1263                       CeedRequest *request) {
1264   int ierr;
1265   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1266 
1267   if (op->num_elem)  {
1268     // Standard Operator
1269     if (op->Apply) {
1270       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1271     } else {
1272       // Zero all output vectors
1273       CeedQFunction qf = op->qf;
1274       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1275         CeedVector vec = op->output_fields[i]->vec;
1276         if (vec == CEED_VECTOR_ACTIVE)
1277           vec = out;
1278         if (vec != CEED_VECTOR_NONE) {
1279           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1280         }
1281       }
1282       // Apply
1283       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1284     }
1285   } else if (op->is_composite) {
1286     // Composite Operator
1287     if (op->ApplyComposite) {
1288       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1289     } else {
1290       CeedInt num_suboperators;
1291       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1292       CeedOperator *sub_operators;
1293       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1294 
1295       // Zero all output vectors
1296       if (out != CEED_VECTOR_NONE) {
1297         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1298       }
1299       for (CeedInt i=0; i<num_suboperators; i++) {
1300         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1301           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1302           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1303             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1304           }
1305         }
1306       }
1307       // Apply
1308       for (CeedInt i=0; i<op->num_suboperators; i++) {
1309         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1310         CeedChk(ierr);
1311       }
1312     }
1313   }
1314   return CEED_ERROR_SUCCESS;
1315 }
1316 
1317 /**
1318   @brief Apply CeedOperator to a vector and add result to output vector
1319 
1320   This computes the action of the operator on the specified (active) input,
1321   yielding its (active) output.  All inputs and outputs must be specified using
1322   CeedOperatorSetField().
1323 
1324   @param op        CeedOperator to apply
1325   @param[in] in    CeedVector containing input state or NULL if there are no
1326                      active inputs
1327   @param[out] out  CeedVector to sum in result of applying operator (must be
1328                      distinct from @a in) or NULL if there are no active outputs
1329   @param request   Address of CeedRequest for non-blocking completion, else
1330                      @ref CEED_REQUEST_IMMEDIATE
1331 
1332   @return An error code: 0 - success, otherwise - failure
1333 
1334   @ref User
1335 **/
1336 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1337                          CeedRequest *request) {
1338   int ierr;
1339   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1340 
1341   if (op->num_elem)  {
1342     // Standard Operator
1343     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1344   } else if (op->is_composite) {
1345     // Composite Operator
1346     if (op->ApplyAddComposite) {
1347       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1348     } else {
1349       CeedInt num_suboperators;
1350       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1351       CeedOperator *sub_operators;
1352       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1353 
1354       for (CeedInt i=0; i<num_suboperators; i++) {
1355         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1356         CeedChk(ierr);
1357       }
1358     }
1359   }
1360   return CEED_ERROR_SUCCESS;
1361 }
1362 
1363 /**
1364   @brief Destroy a CeedOperator
1365 
1366   @param op  CeedOperator to destroy
1367 
1368   @return An error code: 0 - success, otherwise - failure
1369 
1370   @ref User
1371 **/
1372 int CeedOperatorDestroy(CeedOperator *op) {
1373   int ierr;
1374 
1375   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1376   if ((*op)->Destroy) {
1377     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1378   }
1379   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1380   // Free fields
1381   for (CeedInt i=0; i<(*op)->num_fields; i++)
1382     if ((*op)->input_fields[i]) {
1383       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1384         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1385         CeedChk(ierr);
1386       }
1387       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1388         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1389       }
1390       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1391           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1392         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1393       }
1394       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1395       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1396     }
1397   for (CeedInt i=0; i<(*op)->num_fields; i++)
1398     if ((*op)->output_fields[i]) {
1399       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1400       CeedChk(ierr);
1401       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1402         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1403       }
1404       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1405           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1406         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1407       }
1408       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1409       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1410     }
1411   // Destroy sub_operators
1412   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1413     if ((*op)->sub_operators[i]) {
1414       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1415     }
1416   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1417   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1418   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1419   // Destroy any composite labels
1420   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1421     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1422     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1423   }
1424   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1425 
1426   // Destroy fallback
1427   if ((*op)->op_fallback) {
1428     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1429     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1430     ierr = CeedVectorDestroy(&(*op)->op_fallback->qf_assembled); CeedChk(ierr);
1431     ierr = CeedElemRestrictionDestroy(&(*op)->op_fallback->qf_assembled_rstr);
1432     CeedChk(ierr);
1433     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1434     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1435   }
1436 
1437   // Destroy QF assembly cache
1438   ierr = CeedVectorDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1439   ierr = CeedElemRestrictionDestroy(&(*op)->qf_assembled_rstr); CeedChk(ierr);
1440 
1441   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1442   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1443   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1444   ierr = CeedFree(op); CeedChk(ierr);
1445   return CEED_ERROR_SUCCESS;
1446 }
1447 
1448 /// @}
1449