xref: /libCEED/interface/ceed-operator.c (revision 16e5f7d78a9492d7c9935970936464a6b35036cc)
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 = CeedQFunctionAssemblyDataCreate(ceed, &(*op)->qf_assembled);
583   CeedChk(ierr);
584   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->input_fields); CeedChk(ierr);
585   ierr = CeedCalloc(CEED_FIELD_MAX, &(*op)->output_fields); CeedChk(ierr);
586   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
587   return CEED_ERROR_SUCCESS;
588 }
589 
590 /**
591   @brief Create an operator that composes the action of several operators
592 
593   @param ceed     A Ceed object where the CeedOperator will be created
594   @param[out] op  Address of the variable where the newly created
595                     Composite CeedOperator will be stored
596 
597   @return An error code: 0 - success, otherwise - failure
598 
599   @ref User
600  */
601 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
602   int ierr;
603 
604   if (!ceed->CompositeOperatorCreate) {
605     Ceed delegate;
606     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
607 
608     if (delegate) {
609       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
610       return CEED_ERROR_SUCCESS;
611     }
612   }
613 
614   ierr = CeedCalloc(1, op); CeedChk(ierr);
615   (*op)->ceed = ceed;
616   ierr = CeedReference(ceed); CeedChk(ierr);
617   (*op)->is_composite = true;
618   ierr = CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators); CeedChk(ierr);
619 
620   if (ceed->CompositeOperatorCreate) {
621     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
622   }
623   return CEED_ERROR_SUCCESS;
624 }
625 
626 /**
627   @brief Copy the pointer to a CeedOperator. Both pointers should
628            be destroyed with `CeedOperatorDestroy()`;
629            Note: If `*op_copy` is non-NULL, then it is assumed that
630            `*op_copy` is a pointer to a CeedOperator. This
631            CeedOperator will be destroyed if `*op_copy` is the only
632            reference to this CeedOperator.
633 
634   @param op            CeedOperator to copy reference to
635   @param[out] op_copy  Variable to store copied reference
636 
637   @return An error code: 0 - success, otherwise - failure
638 
639   @ref User
640 **/
641 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
642   int ierr;
643 
644   ierr = CeedOperatorReference(op); CeedChk(ierr);
645   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
646   *op_copy = op;
647   return CEED_ERROR_SUCCESS;
648 }
649 
650 /**
651   @brief Provide a field to a CeedOperator for use by its CeedQFunction
652 
653   This function is used to specify both active and passive fields to a
654   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
655   fields can inputs or outputs (updated in-place when operator is applied).
656 
657   Active fields must be specified using this function, but their data (in a
658   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
659   input and at most one active output.
660 
661   @param op          CeedOperator on which to provide the field
662   @param field_name  Name of the field (to be matched with the name used by
663                        CeedQFunction)
664   @param r           CeedElemRestriction
665   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
666                        if collocated with quadrature points
667   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
668                        if field is active or @ref CEED_VECTOR_NONE if using
669                        @ref CEED_EVAL_WEIGHT in the QFunction
670 
671   @return An error code: 0 - success, otherwise - failure
672 
673   @ref User
674 **/
675 int CeedOperatorSetField(CeedOperator op, const char *field_name,
676                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
677   int ierr;
678   if (op->is_composite)
679     // LCOV_EXCL_START
680     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
681                      "Cannot add field to composite operator.");
682   // LCOV_EXCL_STOP
683   if (op->is_immutable)
684     // LCOV_EXCL_START
685     return CeedError(op->ceed, CEED_ERROR_MAJOR,
686                      "Operator cannot be changed after set as immutable");
687   // LCOV_EXCL_STOP
688   if (!r)
689     // LCOV_EXCL_START
690     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
691                      "ElemRestriction r for field \"%s\" must be non-NULL.",
692                      field_name);
693   // LCOV_EXCL_STOP
694   if (!b)
695     // LCOV_EXCL_START
696     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
697                      "Basis b for field \"%s\" must be non-NULL.",
698                      field_name);
699   // LCOV_EXCL_STOP
700   if (!v)
701     // LCOV_EXCL_START
702     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
703                      "Vector v for field \"%s\" must be non-NULL.",
704                      field_name);
705   // LCOV_EXCL_STOP
706 
707   CeedInt num_elem;
708   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
709   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
710       op->num_elem != num_elem)
711     // LCOV_EXCL_START
712     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
713                      "ElemRestriction with %d elements incompatible with prior "
714                      "%d elements", num_elem, op->num_elem);
715   // LCOV_EXCL_STOP
716 
717   CeedInt num_qpts = 0;
718   if (b != CEED_BASIS_COLLOCATED) {
719     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
720     if (op->num_qpts && op->num_qpts != num_qpts)
721       // LCOV_EXCL_START
722       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
723                        "Basis with %d quadrature points "
724                        "incompatible with prior %d points", num_qpts,
725                        op->num_qpts);
726     // LCOV_EXCL_STOP
727   }
728   CeedQFunctionField qf_field;
729   CeedOperatorField *op_field;
730   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
731     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
732       qf_field = op->qf->input_fields[i];
733       op_field = &op->input_fields[i];
734       goto found;
735     }
736   }
737   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
738     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
739       qf_field = op->qf->output_fields[i];
740       op_field = &op->output_fields[i];
741       goto found;
742     }
743   }
744   // LCOV_EXCL_START
745   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
746                    "QFunction has no knowledge of field '%s'",
747                    field_name);
748   // LCOV_EXCL_STOP
749 found:
750   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
751   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
752 
753   (*op_field)->vec = v;
754   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
755     ierr = CeedVectorReference(v); CeedChk(ierr);
756   }
757 
758   (*op_field)->elem_restr = r;
759   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
760   if (r != CEED_ELEMRESTRICTION_NONE) {
761     op->num_elem = num_elem;
762     op->has_restriction = true; // Restriction set, but num_elem may be 0
763   }
764 
765   (*op_field)->basis = b;
766   if (b != CEED_BASIS_COLLOCATED) {
767     if (!op->num_qpts) {
768       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
769     }
770     ierr = CeedBasisReference(b); CeedChk(ierr);
771   }
772 
773   op->num_fields += 1;
774   ierr = CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name);
775   CeedChk(ierr);
776   return CEED_ERROR_SUCCESS;
777 }
778 
779 /**
780   @brief Get the CeedOperatorFields of a CeedOperator
781 
782   Note: Calling this function asserts that setup is complete
783           and sets the CeedOperator as immutable.
784 
785   @param op                      CeedOperator
786   @param[out] num_input_fields   Variable to store number of input fields
787   @param[out] input_fields       Variable to store input_fields
788   @param[out] num_output_fields  Variable to store number of output fields
789   @param[out] output_fields      Variable to store output_fields
790 
791   @return An error code: 0 - success, otherwise - failure
792 
793   @ref Advanced
794 **/
795 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
796                           CeedOperatorField **input_fields,
797                           CeedInt *num_output_fields,
798                           CeedOperatorField **output_fields) {
799   int ierr;
800 
801   if (op->is_composite)
802     // LCOV_EXCL_START
803     return CeedError(op->ceed, CEED_ERROR_MINOR,
804                      "Not defined for composite operator");
805   // LCOV_EXCL_STOP
806   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
807 
808   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
809   if (input_fields) *input_fields = op->input_fields;
810   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
811   if (output_fields) *output_fields = op->output_fields;
812   return CEED_ERROR_SUCCESS;
813 }
814 
815 /**
816   @brief Get the name of a CeedOperatorField
817 
818   @param op_field         CeedOperatorField
819   @param[out] field_name  Variable to store the field name
820 
821   @return An error code: 0 - success, otherwise - failure
822 
823   @ref Advanced
824 **/
825 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
826   *field_name = (char *)op_field->field_name;
827   return CEED_ERROR_SUCCESS;
828 }
829 
830 /**
831   @brief Get the CeedElemRestriction of a CeedOperatorField
832 
833   @param op_field   CeedOperatorField
834   @param[out] rstr  Variable to store CeedElemRestriction
835 
836   @return An error code: 0 - success, otherwise - failure
837 
838   @ref Advanced
839 **/
840 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
841                                         CeedElemRestriction *rstr) {
842   *rstr = op_field->elem_restr;
843   return CEED_ERROR_SUCCESS;
844 }
845 
846 /**
847   @brief Get the CeedBasis of a CeedOperatorField
848 
849   @param op_field    CeedOperatorField
850   @param[out] basis  Variable to store CeedBasis
851 
852   @return An error code: 0 - success, otherwise - failure
853 
854   @ref Advanced
855 **/
856 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
857   *basis = op_field->basis;
858   return CEED_ERROR_SUCCESS;
859 }
860 
861 /**
862   @brief Get the CeedVector of a CeedOperatorField
863 
864   @param op_field  CeedOperatorField
865   @param[out] vec  Variable to store CeedVector
866 
867   @return An error code: 0 - success, otherwise - failure
868 
869   @ref Advanced
870 **/
871 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
872   *vec = op_field->vec;
873   return CEED_ERROR_SUCCESS;
874 }
875 
876 /**
877   @brief Add a sub-operator to a composite CeedOperator
878 
879   @param[out] composite_op  Composite CeedOperator
880   @param      sub_op        Sub-operator CeedOperator
881 
882   @return An error code: 0 - success, otherwise - failure
883 
884   @ref User
885  */
886 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
887                                 CeedOperator sub_op) {
888   int ierr;
889 
890   if (!composite_op->is_composite)
891     // LCOV_EXCL_START
892     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
893                      "CeedOperator is not a composite operator");
894   // LCOV_EXCL_STOP
895 
896   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
897     // LCOV_EXCL_START
898     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
899                      "Cannot add additional sub_operators");
900   // LCOV_EXCL_STOP
901   if (composite_op->is_immutable)
902     // LCOV_EXCL_START
903     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
904                      "Operator cannot be changed after set as immutable");
905   // LCOV_EXCL_STOP
906 
907   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
908   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
909   composite_op->num_suboperators++;
910   return CEED_ERROR_SUCCESS;
911 }
912 
913 /**
914   @brief Check if a CeedOperator is ready to be used.
915 
916   @param[in] op  CeedOperator to check
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref User
921 **/
922 int CeedOperatorCheckReady(CeedOperator op) {
923   int ierr;
924   Ceed ceed;
925   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
926 
927   if (op->is_interface_setup)
928     return CEED_ERROR_SUCCESS;
929 
930   CeedQFunction qf = op->qf;
931   if (op->is_composite) {
932     if (!op->num_suboperators)
933       // LCOV_EXCL_START
934       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
935     // LCOV_EXCL_STOP
936     for (CeedInt i = 0; i < op->num_suboperators; i++) {
937       ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
938     }
939   } else {
940     if (op->num_fields == 0)
941       // LCOV_EXCL_START
942       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
943     // LCOV_EXCL_STOP
944     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
945       // LCOV_EXCL_START
946       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
947     // LCOV_EXCL_STOP
948     if (!op->has_restriction)
949       // LCOV_EXCL_START
950       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
951                        "At least one restriction required");
952     // LCOV_EXCL_STOP
953     if (op->num_qpts == 0)
954       // LCOV_EXCL_START
955       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
956                        "At least one non-collocated basis is required "
957                        "or the number of quadrature points must be set");
958     // LCOV_EXCL_STOP
959   }
960 
961   // Flag as immutable and ready
962   op->is_interface_setup = true;
963   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
964     // LCOV_EXCL_START
965     op->qf->is_immutable = true;
966   // LCOV_EXCL_STOP
967   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
968     // LCOV_EXCL_START
969     op->dqf->is_immutable = true;
970   // LCOV_EXCL_STOP
971   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
972     // LCOV_EXCL_START
973     op->dqfT->is_immutable = true;
974   // LCOV_EXCL_STOP
975   return CEED_ERROR_SUCCESS;
976 }
977 
978 /**
979   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
980            When this function is not used, the CeedQFunction associated with this
981            CeedOperator is re-assembled every time a CeedOperatorLinearAssemble*
982            function is called. When this function is used, the CeedQFunction
983            associated with this CeedOperator is only re-assembled the first time
984            CeedOperatorLinearAssemble* is called after this function is called.
985            The same assembled CeedQFunction data is reused internally until this
986            function is called again.
987 
988   @param op CeedOperator
989 
990   @return An error code: 0 - success, otherwise - failure
991 
992   @ref Advanced
993 **/
994 int CeedOperatorSetQFunctionUpdated(CeedOperator op) {
995   int ierr;
996   bool is_composite;
997 
998   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
999   if (is_composite) {
1000     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1001       ierr = CeedOperatorSetQFunctionUpdated(op->sub_operators[i]); CeedChk(ierr);
1002     }
1003   } else {
1004     ierr = CeedQFunctionAssemblyDataSetQFunctionUpdated(op->qf_assembled, true);
1005     CeedChk(ierr);
1006   }
1007 
1008   return CEED_ERROR_SUCCESS;
1009 }
1010 
1011 /**
1012   @brief Set the number of quadrature points associated with a CeedOperator.
1013            This should be used when creating a CeedOperator where every
1014            field has a collocated basis. This function cannot be used for
1015            composite CeedOperators.
1016 
1017   @param op        CeedOperator
1018   @param num_qpts  Number of quadrature points to set
1019 
1020   @return An error code: 0 - success, otherwise - failure
1021 
1022   @ref Advanced
1023 **/
1024 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1025   if (op->is_composite)
1026     // LCOV_EXCL_START
1027     return CeedError(op->ceed, CEED_ERROR_MINOR,
1028                      "Not defined for composite operator");
1029   // LCOV_EXCL_STOP
1030   if (op->num_qpts)
1031     // LCOV_EXCL_START
1032     return CeedError(op->ceed, CEED_ERROR_MINOR,
1033                      "Number of quadrature points already defined");
1034   // LCOV_EXCL_STOP
1035   if (op->is_immutable)
1036     // LCOV_EXCL_START
1037     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1038                      "Operator cannot be changed after set as immutable");
1039   // LCOV_EXCL_STOP
1040 
1041   op->num_qpts = num_qpts;
1042   return CEED_ERROR_SUCCESS;
1043 }
1044 
1045 /**
1046   @brief View a CeedOperator
1047 
1048   @param[in] op      CeedOperator to view
1049   @param[in] stream  Stream to write; typically stdout/stderr or a file
1050 
1051   @return Error code: 0 - success, otherwise - failure
1052 
1053   @ref User
1054 **/
1055 int CeedOperatorView(CeedOperator op, FILE *stream) {
1056   int ierr;
1057 
1058   if (op->is_composite) {
1059     fprintf(stream, "Composite CeedOperator\n");
1060 
1061     for (CeedInt i=0; i<op->num_suboperators; i++) {
1062       fprintf(stream, "  SubOperator [%d]:\n", i);
1063       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1064       CeedChk(ierr);
1065     }
1066   } else {
1067     fprintf(stream, "CeedOperator\n");
1068     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1069   }
1070   return CEED_ERROR_SUCCESS;
1071 }
1072 
1073 /**
1074   @brief Get the Ceed associated with a CeedOperator
1075 
1076   @param op         CeedOperator
1077   @param[out] ceed  Variable to store Ceed
1078 
1079   @return An error code: 0 - success, otherwise - failure
1080 
1081   @ref Advanced
1082 **/
1083 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1084   *ceed = op->ceed;
1085   return CEED_ERROR_SUCCESS;
1086 }
1087 
1088 /**
1089   @brief Get the number of elements associated with a CeedOperator
1090 
1091   @param op             CeedOperator
1092   @param[out] num_elem  Variable to store number of elements
1093 
1094   @return An error code: 0 - success, otherwise - failure
1095 
1096   @ref Advanced
1097 **/
1098 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1099   if (op->is_composite)
1100     // LCOV_EXCL_START
1101     return CeedError(op->ceed, CEED_ERROR_MINOR,
1102                      "Not defined for composite operator");
1103   // LCOV_EXCL_STOP
1104 
1105   *num_elem = op->num_elem;
1106   return CEED_ERROR_SUCCESS;
1107 }
1108 
1109 /**
1110   @brief Get the number of quadrature points associated with a CeedOperator
1111 
1112   @param op             CeedOperator
1113   @param[out] num_qpts  Variable to store vector number of quadrature points
1114 
1115   @return An error code: 0 - success, otherwise - failure
1116 
1117   @ref Advanced
1118 **/
1119 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1120   if (op->is_composite)
1121     // LCOV_EXCL_START
1122     return CeedError(op->ceed, CEED_ERROR_MINOR,
1123                      "Not defined for composite operator");
1124   // LCOV_EXCL_STOP
1125 
1126   *num_qpts = op->num_qpts;
1127   return CEED_ERROR_SUCCESS;
1128 }
1129 
1130 /**
1131   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1132            field has been registered with this `field_name`.
1133 
1134   @param[in] op            CeedOperator
1135   @param[in] field_name    Name of field to retrieve label
1136   @param[out] field_label  Variable to field label
1137 
1138   @return An error code: 0 - success, otherwise - failure
1139 
1140   @ref User
1141 **/
1142 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1143                                      const char *field_name,
1144                                      CeedContextFieldLabel *field_label) {
1145   int ierr;
1146 
1147   bool is_composite;
1148   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1149   if (is_composite) {
1150     // Check if composite label already created
1151     for (CeedInt i=0; i<op->num_context_labels; i++) {
1152       if (!strcmp(op->context_labels[i]->name, field_name)) {
1153         *field_label = op->context_labels[i];
1154         return CEED_ERROR_SUCCESS;
1155       }
1156     }
1157 
1158     // Create composite label if needed
1159     CeedInt num_sub;
1160     CeedOperator *sub_operators;
1161     CeedContextFieldLabel new_field_label;
1162 
1163     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1164     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1165     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1166     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1167     new_field_label->num_sub_labels = num_sub;
1168 
1169     bool label_found = false;
1170     for (CeedInt i=0; i<num_sub; i++) {
1171       if (sub_operators[i]->qf->ctx) {
1172         CeedContextFieldLabel new_field_label_i;
1173         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1174                &new_field_label_i); CeedChk(ierr);
1175         if (new_field_label_i) {
1176           label_found = true;
1177           new_field_label->sub_labels[i] = new_field_label_i;
1178           new_field_label->name = new_field_label_i->name;
1179           new_field_label->description = new_field_label_i->description;
1180           if (new_field_label->type &&
1181               new_field_label->type != new_field_label_i->type) {
1182             // LCOV_EXCL_START
1183             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1184             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1185                              "Incompatible field types on sub-operator contexts. "
1186                              "%s != %s",
1187                              CeedContextFieldTypes[new_field_label->type],
1188                              CeedContextFieldTypes[new_field_label_i->type]);
1189             // LCOV_EXCL_STOP
1190           } else {
1191             new_field_label->type = new_field_label_i->type;
1192           }
1193           if (new_field_label->num_values != 0 &&
1194               new_field_label->num_values != new_field_label_i->num_values) {
1195             // LCOV_EXCL_START
1196             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1197             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1198                              "Incompatible field number of values on sub-operator"
1199                              " contexts. %ld != %ld",
1200                              new_field_label->num_values, new_field_label_i->num_values);
1201             // LCOV_EXCL_STOP
1202           } else {
1203             new_field_label->num_values = new_field_label_i->num_values;
1204           }
1205         }
1206       }
1207     }
1208     if (!label_found) {
1209       // LCOV_EXCL_START
1210       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1211       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1212       *field_label = NULL;
1213       // LCOV_EXCL_STOP
1214     } else {
1215       // Move new composite label to operator
1216       if (op->num_context_labels == 0) {
1217         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1218         op->max_context_labels = 1;
1219       } else if (op->num_context_labels == op->max_context_labels) {
1220         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1221         CeedChk(ierr);
1222         op->max_context_labels *= 2;
1223       }
1224       op->context_labels[op->num_context_labels] = new_field_label;
1225       *field_label = new_field_label;
1226       op->num_context_labels++;
1227     }
1228 
1229     return CEED_ERROR_SUCCESS;
1230   } else {
1231     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1232   }
1233 }
1234 
1235 /**
1236   @brief Set QFunctionContext field holding a double precision value.
1237            For composite operators, the value is set in all
1238            sub-operator QFunctionContexts that have a matching `field_name`.
1239 
1240   @param op          CeedOperator
1241   @param field_label Label of field to register
1242   @param values      Values to set
1243 
1244   @return An error code: 0 - success, otherwise - failure
1245 
1246   @ref User
1247 **/
1248 int CeedOperatorContextSetDouble(CeedOperator op,
1249                                  CeedContextFieldLabel field_label,
1250                                  double *values) {
1251   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1252                                        values);
1253 }
1254 
1255 /**
1256   @brief Set QFunctionContext field holding an int32 value.
1257            For composite operators, the value is set in all
1258            sub-operator QFunctionContexts that have a matching `field_name`.
1259 
1260   @param op          CeedOperator
1261   @param field_label Label of field to set
1262   @param values      Values to set
1263 
1264   @return An error code: 0 - success, otherwise - failure
1265 
1266   @ref User
1267 **/
1268 int CeedOperatorContextSetInt32(CeedOperator op,
1269                                 CeedContextFieldLabel field_label,
1270                                 int *values) {
1271   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1272                                        values);
1273 }
1274 
1275 /**
1276   @brief Apply CeedOperator to a vector
1277 
1278   This computes the action of the operator on the specified (active) input,
1279   yielding its (active) output.  All inputs and outputs must be specified using
1280   CeedOperatorSetField().
1281 
1282   Note: Calling this function asserts that setup is complete
1283           and sets the CeedOperator as immutable.
1284 
1285   @param op        CeedOperator to apply
1286   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1287                      there are no active inputs
1288   @param[out] out  CeedVector to store result of applying operator (must be
1289                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1290                      active outputs
1291   @param request   Address of CeedRequest for non-blocking completion, else
1292                      @ref CEED_REQUEST_IMMEDIATE
1293 
1294   @return An error code: 0 - success, otherwise - failure
1295 
1296   @ref User
1297 **/
1298 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1299                       CeedRequest *request) {
1300   int ierr;
1301   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1302 
1303   if (op->num_elem)  {
1304     // Standard Operator
1305     if (op->Apply) {
1306       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1307     } else {
1308       // Zero all output vectors
1309       CeedQFunction qf = op->qf;
1310       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1311         CeedVector vec = op->output_fields[i]->vec;
1312         if (vec == CEED_VECTOR_ACTIVE)
1313           vec = out;
1314         if (vec != CEED_VECTOR_NONE) {
1315           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1316         }
1317       }
1318       // Apply
1319       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1320     }
1321   } else if (op->is_composite) {
1322     // Composite Operator
1323     if (op->ApplyComposite) {
1324       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1325     } else {
1326       CeedInt num_suboperators;
1327       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1328       CeedOperator *sub_operators;
1329       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1330 
1331       // Zero all output vectors
1332       if (out != CEED_VECTOR_NONE) {
1333         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1334       }
1335       for (CeedInt i=0; i<num_suboperators; i++) {
1336         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1337           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1338           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1339             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1340           }
1341         }
1342       }
1343       // Apply
1344       for (CeedInt i=0; i<op->num_suboperators; i++) {
1345         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1346         CeedChk(ierr);
1347       }
1348     }
1349   }
1350   return CEED_ERROR_SUCCESS;
1351 }
1352 
1353 /**
1354   @brief Apply CeedOperator to a vector and add result to output vector
1355 
1356   This computes the action of the operator on the specified (active) input,
1357   yielding its (active) output.  All inputs and outputs must be specified using
1358   CeedOperatorSetField().
1359 
1360   @param op        CeedOperator to apply
1361   @param[in] in    CeedVector containing input state or NULL if there are no
1362                      active inputs
1363   @param[out] out  CeedVector to sum in result of applying operator (must be
1364                      distinct from @a in) or NULL if there are no active outputs
1365   @param request   Address of CeedRequest for non-blocking completion, else
1366                      @ref CEED_REQUEST_IMMEDIATE
1367 
1368   @return An error code: 0 - success, otherwise - failure
1369 
1370   @ref User
1371 **/
1372 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1373                          CeedRequest *request) {
1374   int ierr;
1375   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1376 
1377   if (op->num_elem)  {
1378     // Standard Operator
1379     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1380   } else if (op->is_composite) {
1381     // Composite Operator
1382     if (op->ApplyAddComposite) {
1383       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1384     } else {
1385       CeedInt num_suboperators;
1386       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1387       CeedOperator *sub_operators;
1388       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1389 
1390       for (CeedInt i=0; i<num_suboperators; i++) {
1391         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1392         CeedChk(ierr);
1393       }
1394     }
1395   }
1396   return CEED_ERROR_SUCCESS;
1397 }
1398 
1399 /**
1400   @brief Destroy a CeedOperator
1401 
1402   @param op  CeedOperator to destroy
1403 
1404   @return An error code: 0 - success, otherwise - failure
1405 
1406   @ref User
1407 **/
1408 int CeedOperatorDestroy(CeedOperator *op) {
1409   int ierr;
1410 
1411   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1412   if ((*op)->Destroy) {
1413     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1414   }
1415   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1416   // Free fields
1417   for (CeedInt i=0; i<(*op)->num_fields; i++)
1418     if ((*op)->input_fields[i]) {
1419       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1420         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1421         CeedChk(ierr);
1422       }
1423       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1424         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1425       }
1426       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1427           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1428         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1429       }
1430       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1431       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1432     }
1433   for (CeedInt i=0; i<(*op)->num_fields; i++)
1434     if ((*op)->output_fields[i]) {
1435       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1436       CeedChk(ierr);
1437       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1438         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1439       }
1440       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1441           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1442         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1443       }
1444       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1445       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1446     }
1447   // Destroy sub_operators
1448   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1449     if ((*op)->sub_operators[i]) {
1450       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1451     }
1452   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1453   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1454   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1455   // Destroy any composite labels
1456   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1457     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1458     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1459   }
1460   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1461 
1462   // Destroy fallback
1463   if ((*op)->op_fallback) {
1464     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1465     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1466     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1467     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1468   }
1469 
1470   // Destroy QF assembly cache
1471   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1472 
1473   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1474   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1475   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1476   ierr = CeedFree(op); CeedChk(ierr);
1477   return CEED_ERROR_SUCCESS;
1478 }
1479 
1480 /// @}
1481