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