xref: /libCEED/interface/ceed-operator.c (revision d264344313546611a0e282df1f09990e3ff407ce)
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 Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
980            When `reuse_assembly_data = false` (default), the CeedQFunction associated
981            with this CeedOperator is re-assembled every time a `CeedOperatorLinearAssemble*`
982            function is called.
983            When `reuse_assembly_data = true`, the CeedQFunction associated with
984            this CeedOperator is reused between calls to
985            `CeedOperatorSetQFunctionAssemblyDataUpdated`.
986 
987   @param[in] op                  CeedOperator
988   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
989 
990   @return An error code: 0 - success, otherwise - failure
991 
992   @ref Advanced
993 **/
994 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op,
995     bool reuse_assembly_data) {
996   int ierr;
997   bool is_composite;
998 
999   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1000   if (is_composite) {
1001     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1002       ierr = CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i],
1003              reuse_assembly_data); CeedChk(ierr);
1004     }
1005   } else {
1006     ierr = CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data);
1007     CeedChk(ierr);
1008   }
1009 
1010   return CEED_ERROR_SUCCESS;
1011 }
1012 
1013 /**
1014   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1015 
1016   @param[in] op                  CeedOperator
1017   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1018 
1019   @return An error code: 0 - success, otherwise - failure
1020 
1021   @ref Advanced
1022 **/
1023 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op,
1024     bool needs_data_update) {
1025   int ierr;
1026   bool is_composite;
1027 
1028   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1029   if (is_composite) {
1030     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1031       ierr = CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i],
1032              needs_data_update); CeedChk(ierr);
1033     }
1034   } else {
1035     ierr = CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled,
1036            needs_data_update);
1037     CeedChk(ierr);
1038   }
1039 
1040   return CEED_ERROR_SUCCESS;
1041 }
1042 
1043 /**
1044   @brief Set the number of quadrature points associated with a CeedOperator.
1045            This should be used when creating a CeedOperator where every
1046            field has a collocated basis. This function cannot be used for
1047            composite CeedOperators.
1048 
1049   @param op        CeedOperator
1050   @param num_qpts  Number of quadrature points to set
1051 
1052   @return An error code: 0 - success, otherwise - failure
1053 
1054   @ref Advanced
1055 **/
1056 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1057   if (op->is_composite)
1058     // LCOV_EXCL_START
1059     return CeedError(op->ceed, CEED_ERROR_MINOR,
1060                      "Not defined for composite operator");
1061   // LCOV_EXCL_STOP
1062   if (op->num_qpts)
1063     // LCOV_EXCL_START
1064     return CeedError(op->ceed, CEED_ERROR_MINOR,
1065                      "Number of quadrature points already defined");
1066   // LCOV_EXCL_STOP
1067   if (op->is_immutable)
1068     // LCOV_EXCL_START
1069     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1070                      "Operator cannot be changed after set as immutable");
1071   // LCOV_EXCL_STOP
1072 
1073   op->num_qpts = num_qpts;
1074   return CEED_ERROR_SUCCESS;
1075 }
1076 
1077 /**
1078   @brief View a CeedOperator
1079 
1080   @param[in] op      CeedOperator to view
1081   @param[in] stream  Stream to write; typically stdout/stderr or a file
1082 
1083   @return Error code: 0 - success, otherwise - failure
1084 
1085   @ref User
1086 **/
1087 int CeedOperatorView(CeedOperator op, FILE *stream) {
1088   int ierr;
1089 
1090   if (op->is_composite) {
1091     fprintf(stream, "Composite CeedOperator\n");
1092 
1093     for (CeedInt i=0; i<op->num_suboperators; i++) {
1094       fprintf(stream, "  SubOperator [%d]:\n", i);
1095       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1096       CeedChk(ierr);
1097     }
1098   } else {
1099     fprintf(stream, "CeedOperator\n");
1100     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1101   }
1102   return CEED_ERROR_SUCCESS;
1103 }
1104 
1105 /**
1106   @brief Get the Ceed associated with a CeedOperator
1107 
1108   @param op         CeedOperator
1109   @param[out] ceed  Variable to store Ceed
1110 
1111   @return An error code: 0 - success, otherwise - failure
1112 
1113   @ref Advanced
1114 **/
1115 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1116   *ceed = op->ceed;
1117   return CEED_ERROR_SUCCESS;
1118 }
1119 
1120 /**
1121   @brief Get the number of elements associated with a CeedOperator
1122 
1123   @param op             CeedOperator
1124   @param[out] num_elem  Variable to store number of elements
1125 
1126   @return An error code: 0 - success, otherwise - failure
1127 
1128   @ref Advanced
1129 **/
1130 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1131   if (op->is_composite)
1132     // LCOV_EXCL_START
1133     return CeedError(op->ceed, CEED_ERROR_MINOR,
1134                      "Not defined for composite operator");
1135   // LCOV_EXCL_STOP
1136 
1137   *num_elem = op->num_elem;
1138   return CEED_ERROR_SUCCESS;
1139 }
1140 
1141 /**
1142   @brief Get the number of quadrature points associated with a CeedOperator
1143 
1144   @param op             CeedOperator
1145   @param[out] num_qpts  Variable to store vector number of quadrature points
1146 
1147   @return An error code: 0 - success, otherwise - failure
1148 
1149   @ref Advanced
1150 **/
1151 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1152   if (op->is_composite)
1153     // LCOV_EXCL_START
1154     return CeedError(op->ceed, CEED_ERROR_MINOR,
1155                      "Not defined for composite operator");
1156   // LCOV_EXCL_STOP
1157 
1158   *num_qpts = op->num_qpts;
1159   return CEED_ERROR_SUCCESS;
1160 }
1161 
1162 /**
1163   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1164            field has been registered with this `field_name`.
1165 
1166   @param[in] op            CeedOperator
1167   @param[in] field_name    Name of field to retrieve label
1168   @param[out] field_label  Variable to field label
1169 
1170   @return An error code: 0 - success, otherwise - failure
1171 
1172   @ref User
1173 **/
1174 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1175                                      const char *field_name,
1176                                      CeedContextFieldLabel *field_label) {
1177   int ierr;
1178 
1179   bool is_composite;
1180   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1181   if (is_composite) {
1182     // Check if composite label already created
1183     for (CeedInt i=0; i<op->num_context_labels; i++) {
1184       if (!strcmp(op->context_labels[i]->name, field_name)) {
1185         *field_label = op->context_labels[i];
1186         return CEED_ERROR_SUCCESS;
1187       }
1188     }
1189 
1190     // Create composite label if needed
1191     CeedInt num_sub;
1192     CeedOperator *sub_operators;
1193     CeedContextFieldLabel new_field_label;
1194 
1195     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1196     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1197     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1198     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1199     new_field_label->num_sub_labels = num_sub;
1200 
1201     bool label_found = false;
1202     for (CeedInt i=0; i<num_sub; i++) {
1203       if (sub_operators[i]->qf->ctx) {
1204         CeedContextFieldLabel new_field_label_i;
1205         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1206                &new_field_label_i); CeedChk(ierr);
1207         if (new_field_label_i) {
1208           label_found = true;
1209           new_field_label->sub_labels[i] = new_field_label_i;
1210           new_field_label->name = new_field_label_i->name;
1211           new_field_label->description = new_field_label_i->description;
1212           if (new_field_label->type &&
1213               new_field_label->type != new_field_label_i->type) {
1214             // LCOV_EXCL_START
1215             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1216             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1217                              "Incompatible field types on sub-operator contexts. "
1218                              "%s != %s",
1219                              CeedContextFieldTypes[new_field_label->type],
1220                              CeedContextFieldTypes[new_field_label_i->type]);
1221             // LCOV_EXCL_STOP
1222           } else {
1223             new_field_label->type = new_field_label_i->type;
1224           }
1225           if (new_field_label->num_values != 0 &&
1226               new_field_label->num_values != new_field_label_i->num_values) {
1227             // LCOV_EXCL_START
1228             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1229             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1230                              "Incompatible field number of values on sub-operator"
1231                              " contexts. %ld != %ld",
1232                              new_field_label->num_values, new_field_label_i->num_values);
1233             // LCOV_EXCL_STOP
1234           } else {
1235             new_field_label->num_values = new_field_label_i->num_values;
1236           }
1237         }
1238       }
1239     }
1240     if (!label_found) {
1241       // LCOV_EXCL_START
1242       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1243       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1244       *field_label = NULL;
1245       // LCOV_EXCL_STOP
1246     } else {
1247       // Move new composite label to operator
1248       if (op->num_context_labels == 0) {
1249         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1250         op->max_context_labels = 1;
1251       } else if (op->num_context_labels == op->max_context_labels) {
1252         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1253         CeedChk(ierr);
1254         op->max_context_labels *= 2;
1255       }
1256       op->context_labels[op->num_context_labels] = new_field_label;
1257       *field_label = new_field_label;
1258       op->num_context_labels++;
1259     }
1260 
1261     return CEED_ERROR_SUCCESS;
1262   } else {
1263     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1264   }
1265 }
1266 
1267 /**
1268   @brief Set QFunctionContext field holding a double precision value.
1269            For composite operators, the value is set in all
1270            sub-operator QFunctionContexts that have a matching `field_name`.
1271 
1272   @param op          CeedOperator
1273   @param field_label Label of field to register
1274   @param values      Values to set
1275 
1276   @return An error code: 0 - success, otherwise - failure
1277 
1278   @ref User
1279 **/
1280 int CeedOperatorContextSetDouble(CeedOperator op,
1281                                  CeedContextFieldLabel field_label,
1282                                  double *values) {
1283   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1284                                        values);
1285 }
1286 
1287 /**
1288   @brief Set QFunctionContext field holding an int32 value.
1289            For composite operators, the value is set in all
1290            sub-operator QFunctionContexts that have a matching `field_name`.
1291 
1292   @param op          CeedOperator
1293   @param field_label Label of field to set
1294   @param values      Values to set
1295 
1296   @return An error code: 0 - success, otherwise - failure
1297 
1298   @ref User
1299 **/
1300 int CeedOperatorContextSetInt32(CeedOperator op,
1301                                 CeedContextFieldLabel field_label,
1302                                 int *values) {
1303   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1304                                        values);
1305 }
1306 
1307 /**
1308   @brief Apply CeedOperator to a vector
1309 
1310   This computes the action of the operator on the specified (active) input,
1311   yielding its (active) output.  All inputs and outputs must be specified using
1312   CeedOperatorSetField().
1313 
1314   Note: Calling this function asserts that setup is complete
1315           and sets the CeedOperator as immutable.
1316 
1317   @param op        CeedOperator to apply
1318   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1319                      there are no active inputs
1320   @param[out] out  CeedVector to store result of applying operator (must be
1321                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1322                      active outputs
1323   @param request   Address of CeedRequest for non-blocking completion, else
1324                      @ref CEED_REQUEST_IMMEDIATE
1325 
1326   @return An error code: 0 - success, otherwise - failure
1327 
1328   @ref User
1329 **/
1330 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1331                       CeedRequest *request) {
1332   int ierr;
1333   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1334 
1335   if (op->num_elem)  {
1336     // Standard Operator
1337     if (op->Apply) {
1338       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1339     } else {
1340       // Zero all output vectors
1341       CeedQFunction qf = op->qf;
1342       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1343         CeedVector vec = op->output_fields[i]->vec;
1344         if (vec == CEED_VECTOR_ACTIVE)
1345           vec = out;
1346         if (vec != CEED_VECTOR_NONE) {
1347           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1348         }
1349       }
1350       // Apply
1351       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1352     }
1353   } else if (op->is_composite) {
1354     // Composite Operator
1355     if (op->ApplyComposite) {
1356       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1357     } else {
1358       CeedInt num_suboperators;
1359       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1360       CeedOperator *sub_operators;
1361       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1362 
1363       // Zero all output vectors
1364       if (out != CEED_VECTOR_NONE) {
1365         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1366       }
1367       for (CeedInt i=0; i<num_suboperators; i++) {
1368         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1369           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1370           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1371             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1372           }
1373         }
1374       }
1375       // Apply
1376       for (CeedInt i=0; i<op->num_suboperators; i++) {
1377         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1378         CeedChk(ierr);
1379       }
1380     }
1381   }
1382   return CEED_ERROR_SUCCESS;
1383 }
1384 
1385 /**
1386   @brief Apply CeedOperator to a vector and add result to output vector
1387 
1388   This computes the action of the operator on the specified (active) input,
1389   yielding its (active) output.  All inputs and outputs must be specified using
1390   CeedOperatorSetField().
1391 
1392   @param op        CeedOperator to apply
1393   @param[in] in    CeedVector containing input state or NULL if there are no
1394                      active inputs
1395   @param[out] out  CeedVector to sum in result of applying operator (must be
1396                      distinct from @a in) or NULL if there are no active outputs
1397   @param request   Address of CeedRequest for non-blocking completion, else
1398                      @ref CEED_REQUEST_IMMEDIATE
1399 
1400   @return An error code: 0 - success, otherwise - failure
1401 
1402   @ref User
1403 **/
1404 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1405                          CeedRequest *request) {
1406   int ierr;
1407   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1408 
1409   if (op->num_elem)  {
1410     // Standard Operator
1411     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1412   } else if (op->is_composite) {
1413     // Composite Operator
1414     if (op->ApplyAddComposite) {
1415       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1416     } else {
1417       CeedInt num_suboperators;
1418       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1419       CeedOperator *sub_operators;
1420       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1421 
1422       for (CeedInt i=0; i<num_suboperators; i++) {
1423         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1424         CeedChk(ierr);
1425       }
1426     }
1427   }
1428   return CEED_ERROR_SUCCESS;
1429 }
1430 
1431 /**
1432   @brief Destroy a CeedOperator
1433 
1434   @param op  CeedOperator to destroy
1435 
1436   @return An error code: 0 - success, otherwise - failure
1437 
1438   @ref User
1439 **/
1440 int CeedOperatorDestroy(CeedOperator *op) {
1441   int ierr;
1442 
1443   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1444   if ((*op)->Destroy) {
1445     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1446   }
1447   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1448   // Free fields
1449   for (CeedInt i=0; i<(*op)->num_fields; i++)
1450     if ((*op)->input_fields[i]) {
1451       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1452         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1453         CeedChk(ierr);
1454       }
1455       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1456         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1457       }
1458       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1459           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1460         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1461       }
1462       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1463       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1464     }
1465   for (CeedInt i=0; i<(*op)->num_fields; i++)
1466     if ((*op)->output_fields[i]) {
1467       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1468       CeedChk(ierr);
1469       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1470         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1471       }
1472       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1473           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1474         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1475       }
1476       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1477       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1478     }
1479   // Destroy sub_operators
1480   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1481     if ((*op)->sub_operators[i]) {
1482       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1483     }
1484   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1485   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1486   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1487   // Destroy any composite labels
1488   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1489     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1490     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1491   }
1492   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1493 
1494   // Destroy fallback
1495   if ((*op)->op_fallback) {
1496     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1497     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1498     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1499     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1500   }
1501 
1502   // Destroy QF assembly cache
1503   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1504 
1505   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1506   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1507   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1508   ierr = CeedFree(op); CeedChk(ierr);
1509   return CEED_ERROR_SUCCESS;
1510 }
1511 
1512 /// @}
1513