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