xref: /libCEED/interface/ceed-operator.c (revision cffd6fdce1224b9ad441ed4022faa643463969db)
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 Set name of CeedOperator for CeedOperatorView output
1175 
1176   @param op    CeedOperator
1177   @param name  Name to set, or NULL to remove previously set name
1178 
1179   @return An error code: 0 - success, otherwise - failure
1180 
1181   @ref User
1182 **/
1183 int CeedOperatorSetName(CeedOperator op, const char *name) {
1184   int ierr;
1185   char *name_copy;
1186   size_t name_len = name ? strlen(name) : 0;
1187 
1188   ierr = CeedFree(&op->name); CeedChk(ierr);
1189   if (name_len > 0) {
1190     ierr = CeedCalloc(name_len + 1, &name_copy); CeedChk(ierr);
1191     memcpy(name_copy, name, name_len);
1192     op->name = name_copy;
1193   }
1194 
1195   return CEED_ERROR_SUCCESS;
1196 }
1197 
1198 /**
1199   @brief View a CeedOperator
1200 
1201   @param[in] op      CeedOperator to view
1202   @param[in] stream  Stream to write; typically stdout/stderr or a file
1203 
1204   @return Error code: 0 - success, otherwise - failure
1205 
1206   @ref User
1207 **/
1208 int CeedOperatorView(CeedOperator op, FILE *stream) {
1209   int ierr;
1210   bool has_name = op->name;
1211 
1212   if (op->is_composite) {
1213     fprintf(stream, "Composite CeedOperator%s%s\n",
1214             has_name ? " - " : "", has_name ? op->name : "");
1215 
1216     for (CeedInt i=0; i<op->num_suboperators; i++) {
1217       has_name = op->sub_operators[i]->name;
1218       fprintf(stream, "  SubOperator %d%s%s:\n", i,
1219               has_name ? " - " : "",
1220               has_name ? op->sub_operators[i]->name : "");
1221       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1222       CeedChk(ierr);
1223     }
1224   } else {
1225     fprintf(stream, "CeedOperator%s%s\n",
1226             has_name ? " - " : "", has_name ? op->name : "");
1227     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1228   }
1229   return CEED_ERROR_SUCCESS;
1230 }
1231 
1232 /**
1233   @brief Get the Ceed associated with a CeedOperator
1234 
1235   @param op         CeedOperator
1236   @param[out] ceed  Variable to store Ceed
1237 
1238   @return An error code: 0 - success, otherwise - failure
1239 
1240   @ref Advanced
1241 **/
1242 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1243   *ceed = op->ceed;
1244   return CEED_ERROR_SUCCESS;
1245 }
1246 
1247 /**
1248   @brief Get the number of elements associated with a CeedOperator
1249 
1250   @param op             CeedOperator
1251   @param[out] num_elem  Variable to store number of elements
1252 
1253   @return An error code: 0 - success, otherwise - failure
1254 
1255   @ref Advanced
1256 **/
1257 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1258   if (op->is_composite)
1259     // LCOV_EXCL_START
1260     return CeedError(op->ceed, CEED_ERROR_MINOR,
1261                      "Not defined for composite operator");
1262   // LCOV_EXCL_STOP
1263 
1264   *num_elem = op->num_elem;
1265   return CEED_ERROR_SUCCESS;
1266 }
1267 
1268 /**
1269   @brief Get the number of quadrature points associated with a CeedOperator
1270 
1271   @param op             CeedOperator
1272   @param[out] num_qpts  Variable to store vector number of quadrature points
1273 
1274   @return An error code: 0 - success, otherwise - failure
1275 
1276   @ref Advanced
1277 **/
1278 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1279   if (op->is_composite)
1280     // LCOV_EXCL_START
1281     return CeedError(op->ceed, CEED_ERROR_MINOR,
1282                      "Not defined for composite operator");
1283   // LCOV_EXCL_STOP
1284 
1285   *num_qpts = op->num_qpts;
1286   return CEED_ERROR_SUCCESS;
1287 }
1288 
1289 /**
1290   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1291 
1292   @param op    Operator to estimate FLOPs for
1293   @param flops Address of variable to hold FLOPs estimate
1294 
1295   @ref Backend
1296 **/
1297 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1298   int ierr;
1299   bool is_composite;
1300   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1301 
1302   *flops = 0;
1303   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1304   if (is_composite) {
1305     CeedInt num_suboperators;
1306     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1307     CeedOperator *sub_operators;
1308     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1309 
1310     // FLOPs for each suboperator
1311     for (CeedInt i = 0; i < num_suboperators; i++) {
1312       CeedSize suboperator_flops;
1313       ierr = CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops);
1314       CeedChk(ierr);
1315       *flops += suboperator_flops;
1316     }
1317   } else {
1318     CeedInt num_input_fields, num_output_fields;
1319     CeedOperatorField *input_fields, *output_fields;
1320 
1321     ierr = CeedOperatorGetFields(op, &num_input_fields, &input_fields,
1322                                  &num_output_fields, &output_fields); CeedChk(ierr);
1323 
1324     CeedInt num_elem = 0;
1325     ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChk(ierr);
1326     // Input FLOPs
1327     for (CeedInt i = 0; i < num_input_fields; i++) {
1328       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1329         CeedSize restr_flops, basis_flops;
1330 
1331         ierr = CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_restr,
1332                CEED_NOTRANSPOSE, &restr_flops); CeedChk(ierr);
1333         *flops += restr_flops;
1334         ierr = CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE,
1335                                          op->qf->input_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1336         *flops += basis_flops * num_elem;
1337       }
1338     }
1339     // QF FLOPs
1340     CeedInt num_qpts;
1341     CeedSize qf_flops;
1342     ierr = CeedOperatorGetNumQuadraturePoints(op, &num_qpts); CeedChk(ierr);
1343     ierr = CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops); CeedChk(ierr);
1344     *flops += num_elem * num_qpts * qf_flops;
1345     // Output FLOPs
1346     for (CeedInt i = 0; i < num_output_fields; i++) {
1347       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1348         CeedSize restr_flops, basis_flops;
1349 
1350         ierr = CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_restr,
1351                CEED_TRANSPOSE, &restr_flops); CeedChk(ierr);
1352         *flops += restr_flops;
1353         ierr = CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE,
1354                                          op->qf->output_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1355         *flops += basis_flops * num_elem;
1356       }
1357     }
1358   }
1359 
1360   return CEED_ERROR_SUCCESS;
1361 }
1362 
1363 /**
1364   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1365            field has been registered with this `field_name`.
1366 
1367   @param[in] op            CeedOperator
1368   @param[in] field_name    Name of field to retrieve label
1369   @param[out] field_label  Variable to field label
1370 
1371   @return An error code: 0 - success, otherwise - failure
1372 
1373   @ref User
1374 **/
1375 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1376                                      const char *field_name,
1377                                      CeedContextFieldLabel *field_label) {
1378   int ierr;
1379 
1380   bool is_composite;
1381   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1382   if (is_composite) {
1383     // Check if composite label already created
1384     for (CeedInt i=0; i<op->num_context_labels; i++) {
1385       if (!strcmp(op->context_labels[i]->name, field_name)) {
1386         *field_label = op->context_labels[i];
1387         return CEED_ERROR_SUCCESS;
1388       }
1389     }
1390 
1391     // Create composite label if needed
1392     CeedInt num_sub;
1393     CeedOperator *sub_operators;
1394     CeedContextFieldLabel new_field_label;
1395 
1396     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1397     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1398     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1399     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1400     new_field_label->num_sub_labels = num_sub;
1401 
1402     bool label_found = false;
1403     for (CeedInt i=0; i<num_sub; i++) {
1404       if (sub_operators[i]->qf->ctx) {
1405         CeedContextFieldLabel new_field_label_i;
1406         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1407                &new_field_label_i); CeedChk(ierr);
1408         if (new_field_label_i) {
1409           label_found = true;
1410           new_field_label->sub_labels[i] = new_field_label_i;
1411           new_field_label->name = new_field_label_i->name;
1412           new_field_label->description = new_field_label_i->description;
1413           if (new_field_label->type &&
1414               new_field_label->type != new_field_label_i->type) {
1415             // LCOV_EXCL_START
1416             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1417             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1418                              "Incompatible field types on sub-operator contexts. "
1419                              "%s != %s",
1420                              CeedContextFieldTypes[new_field_label->type],
1421                              CeedContextFieldTypes[new_field_label_i->type]);
1422             // LCOV_EXCL_STOP
1423           } else {
1424             new_field_label->type = new_field_label_i->type;
1425           }
1426           if (new_field_label->num_values != 0 &&
1427               new_field_label->num_values != new_field_label_i->num_values) {
1428             // LCOV_EXCL_START
1429             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1430             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1431                              "Incompatible field number of values on sub-operator"
1432                              " contexts. %ld != %ld",
1433                              new_field_label->num_values, new_field_label_i->num_values);
1434             // LCOV_EXCL_STOP
1435           } else {
1436             new_field_label->num_values = new_field_label_i->num_values;
1437           }
1438         }
1439       }
1440     }
1441     if (!label_found) {
1442       // LCOV_EXCL_START
1443       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1444       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1445       *field_label = NULL;
1446       // LCOV_EXCL_STOP
1447     } else {
1448       // Move new composite label to operator
1449       if (op->num_context_labels == 0) {
1450         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1451         op->max_context_labels = 1;
1452       } else if (op->num_context_labels == op->max_context_labels) {
1453         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1454         CeedChk(ierr);
1455         op->max_context_labels *= 2;
1456       }
1457       op->context_labels[op->num_context_labels] = new_field_label;
1458       *field_label = new_field_label;
1459       op->num_context_labels++;
1460     }
1461 
1462     return CEED_ERROR_SUCCESS;
1463   } else {
1464     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1465   }
1466 }
1467 
1468 /**
1469   @brief Set QFunctionContext field holding a double precision value.
1470            For composite operators, the value is set in all
1471            sub-operator QFunctionContexts that have a matching `field_name`.
1472 
1473   @param op          CeedOperator
1474   @param field_label Label of field to register
1475   @param values      Values to set
1476 
1477   @return An error code: 0 - success, otherwise - failure
1478 
1479   @ref User
1480 **/
1481 int CeedOperatorContextSetDouble(CeedOperator op,
1482                                  CeedContextFieldLabel field_label,
1483                                  double *values) {
1484   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1485                                        values);
1486 }
1487 
1488 /**
1489   @brief Set QFunctionContext field holding an int32 value.
1490            For composite operators, the value is set in all
1491            sub-operator QFunctionContexts that have a matching `field_name`.
1492 
1493   @param op          CeedOperator
1494   @param field_label Label of field to set
1495   @param values      Values to set
1496 
1497   @return An error code: 0 - success, otherwise - failure
1498 
1499   @ref User
1500 **/
1501 int CeedOperatorContextSetInt32(CeedOperator op,
1502                                 CeedContextFieldLabel field_label,
1503                                 int *values) {
1504   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1505                                        values);
1506 }
1507 
1508 /**
1509   @brief Apply CeedOperator to a vector
1510 
1511   This computes the action of the operator on the specified (active) input,
1512   yielding its (active) output.  All inputs and outputs must be specified using
1513   CeedOperatorSetField().
1514 
1515   Note: Calling this function asserts that setup is complete
1516           and sets the CeedOperator as immutable.
1517 
1518   @param op        CeedOperator to apply
1519   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1520                      there are no active inputs
1521   @param[out] out  CeedVector to store result of applying operator (must be
1522                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1523                      active outputs
1524   @param request   Address of CeedRequest for non-blocking completion, else
1525                      @ref CEED_REQUEST_IMMEDIATE
1526 
1527   @return An error code: 0 - success, otherwise - failure
1528 
1529   @ref User
1530 **/
1531 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1532                       CeedRequest *request) {
1533   int ierr;
1534   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1535 
1536   if (op->num_elem)  {
1537     // Standard Operator
1538     if (op->Apply) {
1539       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1540     } else {
1541       // Zero all output vectors
1542       CeedQFunction qf = op->qf;
1543       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1544         CeedVector vec = op->output_fields[i]->vec;
1545         if (vec == CEED_VECTOR_ACTIVE)
1546           vec = out;
1547         if (vec != CEED_VECTOR_NONE) {
1548           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1549         }
1550       }
1551       // Apply
1552       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1553     }
1554   } else if (op->is_composite) {
1555     // Composite Operator
1556     if (op->ApplyComposite) {
1557       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1558     } else {
1559       CeedInt num_suboperators;
1560       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1561       CeedOperator *sub_operators;
1562       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1563 
1564       // Zero all output vectors
1565       if (out != CEED_VECTOR_NONE) {
1566         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1567       }
1568       for (CeedInt i=0; i<num_suboperators; i++) {
1569         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1570           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1571           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1572             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1573           }
1574         }
1575       }
1576       // Apply
1577       for (CeedInt i=0; i<op->num_suboperators; i++) {
1578         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1579         CeedChk(ierr);
1580       }
1581     }
1582   }
1583   return CEED_ERROR_SUCCESS;
1584 }
1585 
1586 /**
1587   @brief Apply CeedOperator to a vector and add result to output vector
1588 
1589   This computes the action of the operator on the specified (active) input,
1590   yielding its (active) output.  All inputs and outputs must be specified using
1591   CeedOperatorSetField().
1592 
1593   @param op        CeedOperator to apply
1594   @param[in] in    CeedVector containing input state or NULL if there are no
1595                      active inputs
1596   @param[out] out  CeedVector to sum in result of applying operator (must be
1597                      distinct from @a in) or NULL if there are no active outputs
1598   @param request   Address of CeedRequest for non-blocking completion, else
1599                      @ref CEED_REQUEST_IMMEDIATE
1600 
1601   @return An error code: 0 - success, otherwise - failure
1602 
1603   @ref User
1604 **/
1605 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1606                          CeedRequest *request) {
1607   int ierr;
1608   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1609 
1610   if (op->num_elem)  {
1611     // Standard Operator
1612     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1613   } else if (op->is_composite) {
1614     // Composite Operator
1615     if (op->ApplyAddComposite) {
1616       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1617     } else {
1618       CeedInt num_suboperators;
1619       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1620       CeedOperator *sub_operators;
1621       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1622 
1623       for (CeedInt i=0; i<num_suboperators; i++) {
1624         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1625         CeedChk(ierr);
1626       }
1627     }
1628   }
1629   return CEED_ERROR_SUCCESS;
1630 }
1631 
1632 /**
1633   @brief Destroy a CeedOperator
1634 
1635   @param op  CeedOperator to destroy
1636 
1637   @return An error code: 0 - success, otherwise - failure
1638 
1639   @ref User
1640 **/
1641 int CeedOperatorDestroy(CeedOperator *op) {
1642   int ierr;
1643 
1644   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1645   if ((*op)->Destroy) {
1646     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1647   }
1648   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1649   // Free fields
1650   for (CeedInt i=0; i<(*op)->num_fields; i++)
1651     if ((*op)->input_fields[i]) {
1652       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1653         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1654         CeedChk(ierr);
1655       }
1656       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1657         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1658       }
1659       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1660           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1661         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1662       }
1663       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1664       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1665     }
1666   for (CeedInt i=0; i<(*op)->num_fields; i++)
1667     if ((*op)->output_fields[i]) {
1668       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1669       CeedChk(ierr);
1670       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1671         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1672       }
1673       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1674           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1675         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1676       }
1677       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1678       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1679     }
1680   // Destroy sub_operators
1681   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1682     if ((*op)->sub_operators[i]) {
1683       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1684     }
1685   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1686   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1687   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1688   // Destroy any composite labels
1689   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1690     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1691     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1692   }
1693   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1694 
1695   // Destroy fallback
1696   if ((*op)->op_fallback) {
1697     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1698     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1699     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1700     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1701   }
1702 
1703   // Destroy QF assembly cache
1704   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1705 
1706   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1707   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1708   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1709   ierr = CeedFree(&(*op)->name); CeedChk(ierr);
1710   ierr = CeedFree(op); CeedChk(ierr);
1711   return CEED_ERROR_SUCCESS;
1712 }
1713 
1714 /// @}
1715