xref: /libCEED/interface/ceed-operator.c (revision 93b6d8191bb649fce95198206c6bff32567615d1)
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)->ref_count = 1;
649   (*op)->is_composite = true;
650   ierr = CeedCalloc(CEED_COMPOSITE_MAX, &(*op)->sub_operators); CeedChk(ierr);
651   (*op)->input_size = -1;
652   (*op)->output_size = -1;
653 
654   if (ceed->CompositeOperatorCreate) {
655     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
656   }
657   return CEED_ERROR_SUCCESS;
658 }
659 
660 /**
661   @brief Copy the pointer to a CeedOperator. Both pointers should
662            be destroyed with `CeedOperatorDestroy()`;
663            Note: If `*op_copy` is non-NULL, then it is assumed that
664            `*op_copy` is a pointer to a CeedOperator. This
665            CeedOperator will be destroyed if `*op_copy` is the only
666            reference to this CeedOperator.
667 
668   @param op            CeedOperator to copy reference to
669   @param[out] op_copy  Variable to store copied reference
670 
671   @return An error code: 0 - success, otherwise - failure
672 
673   @ref User
674 **/
675 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
676   int ierr;
677 
678   ierr = CeedOperatorReference(op); CeedChk(ierr);
679   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
680   *op_copy = op;
681   return CEED_ERROR_SUCCESS;
682 }
683 
684 /**
685   @brief Provide a field to a CeedOperator for use by its CeedQFunction
686 
687   This function is used to specify both active and passive fields to a
688   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
689   fields can inputs or outputs (updated in-place when operator is applied).
690 
691   Active fields must be specified using this function, but their data (in a
692   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
693   input CeedVector and at most one active output CeedVector passed to
694   CeedOperatorApply().
695 
696   @param op          CeedOperator on which to provide the field
697   @param field_name  Name of the field (to be matched with the name used by
698                        CeedQFunction)
699   @param r           CeedElemRestriction
700   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
701                        if collocated with quadrature points
702   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
703                        if field is active or @ref CEED_VECTOR_NONE if using
704                        @ref CEED_EVAL_WEIGHT in the QFunction
705 
706   @return An error code: 0 - success, otherwise - failure
707 
708   @ref User
709 **/
710 int CeedOperatorSetField(CeedOperator op, const char *field_name,
711                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
712   int ierr;
713   if (op->is_composite)
714     // LCOV_EXCL_START
715     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
716                      "Cannot add field to composite operator.");
717   // LCOV_EXCL_STOP
718   if (op->is_immutable)
719     // LCOV_EXCL_START
720     return CeedError(op->ceed, CEED_ERROR_MAJOR,
721                      "Operator cannot be changed after set as immutable");
722   // LCOV_EXCL_STOP
723   if (!r)
724     // LCOV_EXCL_START
725     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
726                      "ElemRestriction r for field \"%s\" must be non-NULL.",
727                      field_name);
728   // LCOV_EXCL_STOP
729   if (!b)
730     // LCOV_EXCL_START
731     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
732                      "Basis b for field \"%s\" must be non-NULL.",
733                      field_name);
734   // LCOV_EXCL_STOP
735   if (!v)
736     // LCOV_EXCL_START
737     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
738                      "Vector v for field \"%s\" must be non-NULL.",
739                      field_name);
740   // LCOV_EXCL_STOP
741 
742   CeedInt num_elem;
743   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
744   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
745       op->num_elem != num_elem)
746     // LCOV_EXCL_START
747     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
748                      "ElemRestriction with %" CeedInt_FMT " elements incompatible with prior %"
749                      CeedInt_FMT " elements", num_elem, op->num_elem);
750   // LCOV_EXCL_STOP
751 
752   CeedInt num_qpts = 0;
753   if (b != CEED_BASIS_COLLOCATED) {
754     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
755     if (op->num_qpts && op->num_qpts != num_qpts)
756       // LCOV_EXCL_START
757       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
758                        "Basis with %" CeedInt_FMT " quadrature points "
759                        "incompatible with prior %" CeedInt_FMT " points", num_qpts,
760                        op->num_qpts);
761     // LCOV_EXCL_STOP
762   }
763   CeedQFunctionField qf_field;
764   CeedOperatorField *op_field;
765   bool is_input = true;
766   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
767     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
768       qf_field = op->qf->input_fields[i];
769       op_field = &op->input_fields[i];
770       goto found;
771     }
772   }
773   is_input = false;
774   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
775     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
776       qf_field = op->qf->output_fields[i];
777       op_field = &op->output_fields[i];
778       goto found;
779     }
780   }
781   // LCOV_EXCL_START
782   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
783                    "QFunction has no knowledge of field '%s'",
784                    field_name);
785   // LCOV_EXCL_STOP
786 found:
787   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
788   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
789 
790   if (v == CEED_VECTOR_ACTIVE) {
791     CeedSize l_size;
792     ierr = CeedElemRestrictionGetLVectorSize(r, &l_size); CeedChk(ierr);
793     if (is_input) {
794       if (op->input_size == -1) op->input_size = l_size;
795       if (l_size != op->input_size)
796         // LCOV_EXCL_START
797         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
798                          "LVector size %td does not match previous size %td",
799                          l_size, op->input_size);
800       // LCOV_EXCL_STOP
801     } else {
802       if (op->output_size == -1) op->output_size = l_size;
803       if (l_size != op->output_size)
804         // LCOV_EXCL_START
805         return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
806                          "LVector size %td does not match previous size %td",
807                          l_size, op->output_size);
808       // LCOV_EXCL_STOP
809     }
810   }
811 
812   (*op_field)->vec = v;
813   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
814     ierr = CeedVectorReference(v); CeedChk(ierr);
815   }
816 
817   (*op_field)->elem_restr = r;
818   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
819   if (r != CEED_ELEMRESTRICTION_NONE) {
820     op->num_elem = num_elem;
821     op->has_restriction = true; // Restriction set, but num_elem may be 0
822   }
823 
824   (*op_field)->basis = b;
825   if (b != CEED_BASIS_COLLOCATED) {
826     if (!op->num_qpts) {
827       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
828     }
829     ierr = CeedBasisReference(b); CeedChk(ierr);
830   }
831 
832   op->num_fields += 1;
833   ierr = CeedStringAllocCopy(field_name, (char **)&(*op_field)->field_name);
834   CeedChk(ierr);
835   return CEED_ERROR_SUCCESS;
836 }
837 
838 /**
839   @brief Get the CeedOperatorFields of a CeedOperator
840 
841   Note: Calling this function asserts that setup is complete
842           and sets the CeedOperator as immutable.
843 
844   @param op                      CeedOperator
845   @param[out] num_input_fields   Variable to store number of input fields
846   @param[out] input_fields       Variable to store input_fields
847   @param[out] num_output_fields  Variable to store number of output fields
848   @param[out] output_fields      Variable to store output_fields
849 
850   @return An error code: 0 - success, otherwise - failure
851 
852   @ref Advanced
853 **/
854 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
855                           CeedOperatorField **input_fields,
856                           CeedInt *num_output_fields,
857                           CeedOperatorField **output_fields) {
858   int ierr;
859 
860   if (op->is_composite)
861     // LCOV_EXCL_START
862     return CeedError(op->ceed, CEED_ERROR_MINOR,
863                      "Not defined for composite operator");
864   // LCOV_EXCL_STOP
865   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
866 
867   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
868   if (input_fields) *input_fields = op->input_fields;
869   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
870   if (output_fields) *output_fields = op->output_fields;
871   return CEED_ERROR_SUCCESS;
872 }
873 
874 /**
875   @brief Get the name of a CeedOperatorField
876 
877   @param op_field         CeedOperatorField
878   @param[out] field_name  Variable to store the field name
879 
880   @return An error code: 0 - success, otherwise - failure
881 
882   @ref Advanced
883 **/
884 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
885   *field_name = (char *)op_field->field_name;
886   return CEED_ERROR_SUCCESS;
887 }
888 
889 /**
890   @brief Get the CeedElemRestriction of a CeedOperatorField
891 
892   @param op_field   CeedOperatorField
893   @param[out] rstr  Variable to store CeedElemRestriction
894 
895   @return An error code: 0 - success, otherwise - failure
896 
897   @ref Advanced
898 **/
899 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
900                                         CeedElemRestriction *rstr) {
901   *rstr = op_field->elem_restr;
902   return CEED_ERROR_SUCCESS;
903 }
904 
905 /**
906   @brief Get the CeedBasis of a CeedOperatorField
907 
908   @param op_field    CeedOperatorField
909   @param[out] basis  Variable to store CeedBasis
910 
911   @return An error code: 0 - success, otherwise - failure
912 
913   @ref Advanced
914 **/
915 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
916   *basis = op_field->basis;
917   return CEED_ERROR_SUCCESS;
918 }
919 
920 /**
921   @brief Get the CeedVector of a CeedOperatorField
922 
923   @param op_field  CeedOperatorField
924   @param[out] vec  Variable to store CeedVector
925 
926   @return An error code: 0 - success, otherwise - failure
927 
928   @ref Advanced
929 **/
930 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
931   *vec = op_field->vec;
932   return CEED_ERROR_SUCCESS;
933 }
934 
935 /**
936   @brief Add a sub-operator to a composite CeedOperator
937 
938   @param[out] composite_op  Composite CeedOperator
939   @param      sub_op        Sub-operator CeedOperator
940 
941   @return An error code: 0 - success, otherwise - failure
942 
943   @ref User
944  */
945 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
946                                 CeedOperator sub_op) {
947   int ierr;
948 
949   if (!composite_op->is_composite)
950     // LCOV_EXCL_START
951     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
952                      "CeedOperator is not a composite operator");
953   // LCOV_EXCL_STOP
954 
955   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
956     // LCOV_EXCL_START
957     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
958                      "Cannot add additional sub-operators");
959   // LCOV_EXCL_STOP
960   if (composite_op->is_immutable)
961     // LCOV_EXCL_START
962     return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
963                      "Operator cannot be changed after set as immutable");
964   // LCOV_EXCL_STOP
965 
966   {
967     CeedSize input_size, output_size;
968     ierr = CeedOperatorGetActiveVectorLengths(sub_op, &input_size, &output_size);
969     CeedChk(ierr);
970     if (composite_op->input_size == -1) composite_op->input_size = input_size;
971     if (composite_op->output_size == -1) composite_op->output_size = output_size;
972     // Note, a size of -1 means no active vector restriction set, so no incompatibility
973     if ((input_size != -1 && input_size != composite_op->input_size) ||
974         (output_size != -1 && output_size != composite_op->output_size))
975       // LCOV_EXCL_START
976       return CeedError(composite_op->ceed, CEED_ERROR_MAJOR,
977                        "Sub-operators must have compatible dimensions; "
978                        "composite operator of shape (%td, %td) not compatible with "
979                        "sub-operator of shape (%td, %td)",
980                        composite_op->input_size, composite_op->output_size, input_size, output_size);
981     // LCOV_EXCL_STOP
982   }
983 
984   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
985   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
986   composite_op->num_suboperators++;
987   return CEED_ERROR_SUCCESS;
988 }
989 
990 /**
991   @brief Check if a CeedOperator is ready to be used.
992 
993   @param[in] op  CeedOperator to check
994 
995   @return An error code: 0 - success, otherwise - failure
996 
997   @ref User
998 **/
999 int CeedOperatorCheckReady(CeedOperator op) {
1000   int ierr;
1001   Ceed ceed;
1002   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
1003 
1004   if (op->is_interface_setup)
1005     return CEED_ERROR_SUCCESS;
1006 
1007   CeedQFunction qf = op->qf;
1008   if (op->is_composite) {
1009     if (!op->num_suboperators) {
1010       // Empty operator setup
1011       op->input_size = 0;
1012       op->output_size = 0;
1013     } else {
1014       for (CeedInt i = 0; i < op->num_suboperators; i++) {
1015         ierr = CeedOperatorCheckReady(op->sub_operators[i]); CeedChk(ierr);
1016       }
1017       // Sub-operators could be modified after adding to composite operator
1018       // Need to verify no lvec incompatibility from any changes
1019       CeedSize input_size, output_size;
1020       ierr = CeedOperatorGetActiveVectorLengths(op, &input_size, &output_size);
1021       CeedChk(ierr);
1022     }
1023   } else {
1024     if (op->num_fields == 0)
1025       // LCOV_EXCL_START
1026       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
1027     // LCOV_EXCL_STOP
1028     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
1029       // LCOV_EXCL_START
1030       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
1031     // LCOV_EXCL_STOP
1032     if (!op->has_restriction)
1033       // LCOV_EXCL_START
1034       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
1035                        "At least one restriction required");
1036     // LCOV_EXCL_STOP
1037     if (op->num_qpts == 0)
1038       // LCOV_EXCL_START
1039       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
1040                        "At least one non-collocated basis is required "
1041                        "or the number of quadrature points must be set");
1042     // LCOV_EXCL_STOP
1043   }
1044 
1045   // Flag as immutable and ready
1046   op->is_interface_setup = true;
1047   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
1048     // LCOV_EXCL_START
1049     op->qf->is_immutable = true;
1050   // LCOV_EXCL_STOP
1051   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
1052     // LCOV_EXCL_START
1053     op->dqf->is_immutable = true;
1054   // LCOV_EXCL_STOP
1055   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
1056     // LCOV_EXCL_START
1057     op->dqfT->is_immutable = true;
1058   // LCOV_EXCL_STOP
1059   return CEED_ERROR_SUCCESS;
1060 }
1061 
1062 /**
1063   @brief Get vector lengths for the active input and/or output vectors of a CeedOperator.
1064            Note: Lengths of -1 indicate that the CeedOperator does not have an
1065            active input and/or output.
1066 
1067   @param[in] op           CeedOperator
1068   @param[out] input_size  Variable to store active input vector length, or NULL
1069   @param[out] output_size Variable to store active output vector length, or NULL
1070 
1071   @return An error code: 0 - success, otherwise - failure
1072 
1073   @ref User
1074 **/
1075 int CeedOperatorGetActiveVectorLengths(CeedOperator op, CeedSize *input_size,
1076                                        CeedSize *output_size) {
1077   int ierr;
1078   bool is_composite;
1079 
1080   if (input_size) *input_size = op->input_size;
1081   if (output_size) *output_size = op->output_size;
1082 
1083   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1084   if (is_composite && (op->input_size == -1 || op->output_size == -1)) {
1085     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1086       CeedSize sub_input_size, sub_output_size;
1087       ierr = CeedOperatorGetActiveVectorLengths(op->sub_operators[i],
1088              &sub_input_size, &sub_output_size); CeedChk(ierr);
1089       if (op->input_size == -1) op->input_size = sub_input_size;
1090       if (op->output_size == -1) op->output_size = sub_output_size;
1091       // Note, a size of -1 means no active vector restriction set, so no incompatibility
1092       if ((sub_input_size != -1 && sub_input_size != op->input_size) ||
1093           (sub_output_size != -1 && sub_output_size != op->output_size))
1094         // LCOV_EXCL_START
1095         return CeedError(op->ceed, CEED_ERROR_MAJOR,
1096                          "Sub-operators must have compatible dimensions; "
1097                          "composite operator of shape (%td, %td) not compatible with "
1098                          "sub-operator of shape (%td, %td)",
1099                          op->input_size, op->output_size, input_size, output_size);
1100       // LCOV_EXCL_STOP
1101     }
1102   }
1103 
1104   return CEED_ERROR_SUCCESS;
1105 }
1106 
1107 /**
1108   @brief Set reuse of CeedQFunction data in CeedOperatorLinearAssemble* functions.
1109            When `reuse_assembly_data = false` (default), the CeedQFunction associated
1110            with this CeedOperator is re-assembled every time a `CeedOperatorLinearAssemble*`
1111            function is called.
1112            When `reuse_assembly_data = true`, the CeedQFunction associated with
1113            this CeedOperator is reused between calls to
1114            `CeedOperatorSetQFunctionAssemblyDataUpdated`.
1115 
1116   @param[in] op                  CeedOperator
1117   @param[in] reuse_assembly_data Boolean flag setting assembly data reuse
1118 
1119   @return An error code: 0 - success, otherwise - failure
1120 
1121   @ref Advanced
1122 **/
1123 int CeedOperatorSetQFunctionAssemblyReuse(CeedOperator op,
1124     bool reuse_assembly_data) {
1125   int ierr;
1126   bool is_composite;
1127 
1128   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1129   if (is_composite) {
1130     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1131       ierr = CeedOperatorSetQFunctionAssemblyReuse(op->sub_operators[i],
1132              reuse_assembly_data); CeedChk(ierr);
1133     }
1134   } else {
1135     ierr = CeedQFunctionAssemblyDataSetReuse(op->qf_assembled, reuse_assembly_data);
1136     CeedChk(ierr);
1137   }
1138 
1139   return CEED_ERROR_SUCCESS;
1140 }
1141 
1142 /**
1143   @brief Mark CeedQFunction data as updated and the CeedQFunction as requiring re-assembly.
1144 
1145   @param[in] op                CeedOperator
1146   @param[in] needs_data_update Boolean flag setting assembly data reuse
1147 
1148   @return An error code: 0 - success, otherwise - failure
1149 
1150   @ref Advanced
1151 **/
1152 int CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(CeedOperator op,
1153     bool needs_data_update) {
1154   int ierr;
1155   bool is_composite;
1156 
1157   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1158   if (is_composite) {
1159     for (CeedInt i = 0; i < op->num_suboperators; i++) {
1160       ierr = CeedOperatorSetQFunctionAssemblyDataUpdateNeeded(op->sub_operators[i],
1161              needs_data_update); CeedChk(ierr);
1162     }
1163   } else {
1164     ierr = CeedQFunctionAssemblyDataSetUpdateNeeded(op->qf_assembled,
1165            needs_data_update);
1166     CeedChk(ierr);
1167   }
1168 
1169   return CEED_ERROR_SUCCESS;
1170 }
1171 
1172 /**
1173   @brief Set the number of quadrature points associated with a CeedOperator.
1174            This should be used when creating a CeedOperator where every
1175            field has a collocated basis. This function cannot be used for
1176            composite CeedOperators.
1177 
1178   @param op        CeedOperator
1179   @param num_qpts  Number of quadrature points to set
1180 
1181   @return An error code: 0 - success, otherwise - failure
1182 
1183   @ref Advanced
1184 **/
1185 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
1186   if (op->is_composite)
1187     // LCOV_EXCL_START
1188     return CeedError(op->ceed, CEED_ERROR_MINOR,
1189                      "Not defined for composite operator");
1190   // LCOV_EXCL_STOP
1191   if (op->num_qpts)
1192     // LCOV_EXCL_START
1193     return CeedError(op->ceed, CEED_ERROR_MINOR,
1194                      "Number of quadrature points already defined");
1195   // LCOV_EXCL_STOP
1196   if (op->is_immutable)
1197     // LCOV_EXCL_START
1198     return CeedError(op->ceed, CEED_ERROR_MAJOR,
1199                      "Operator cannot be changed after set as immutable");
1200   // LCOV_EXCL_STOP
1201 
1202   op->num_qpts = num_qpts;
1203   return CEED_ERROR_SUCCESS;
1204 }
1205 
1206 /**
1207   @brief Set name of CeedOperator for CeedOperatorView output
1208 
1209   @param op    CeedOperator
1210   @param name  Name to set, or NULL to remove previously set name
1211 
1212   @return An error code: 0 - success, otherwise - failure
1213 
1214   @ref User
1215 **/
1216 int CeedOperatorSetName(CeedOperator op, const char *name) {
1217   int ierr;
1218   char *name_copy;
1219   size_t name_len = name ? strlen(name) : 0;
1220 
1221   ierr = CeedFree(&op->name); CeedChk(ierr);
1222   if (name_len > 0) {
1223     ierr = CeedCalloc(name_len + 1, &name_copy); CeedChk(ierr);
1224     memcpy(name_copy, name, name_len);
1225     op->name = name_copy;
1226   }
1227 
1228   return CEED_ERROR_SUCCESS;
1229 }
1230 
1231 /**
1232   @brief View a CeedOperator
1233 
1234   @param[in] op      CeedOperator to view
1235   @param[in] stream  Stream to write; typically stdout/stderr or a file
1236 
1237   @return Error code: 0 - success, otherwise - failure
1238 
1239   @ref User
1240 **/
1241 int CeedOperatorView(CeedOperator op, FILE *stream) {
1242   int ierr;
1243   bool has_name = op->name;
1244 
1245   if (op->is_composite) {
1246     fprintf(stream, "Composite CeedOperator%s%s\n",
1247             has_name ? " - " : "", has_name ? op->name : "");
1248 
1249     for (CeedInt i=0; i<op->num_suboperators; i++) {
1250       has_name = op->sub_operators[i]->name;
1251       fprintf(stream, "  SubOperator %" CeedInt_FMT "%s%s:\n", i,
1252               has_name ? " - " : "",
1253               has_name ? op->sub_operators[i]->name : "");
1254       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1255       CeedChk(ierr);
1256     }
1257   } else {
1258     fprintf(stream, "CeedOperator%s%s\n",
1259             has_name ? " - " : "", has_name ? op->name : "");
1260     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1261   }
1262   return CEED_ERROR_SUCCESS;
1263 }
1264 
1265 /**
1266   @brief Get the Ceed associated with a CeedOperator
1267 
1268   @param op         CeedOperator
1269   @param[out] ceed  Variable to store Ceed
1270 
1271   @return An error code: 0 - success, otherwise - failure
1272 
1273   @ref Advanced
1274 **/
1275 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
1276   *ceed = op->ceed;
1277   return CEED_ERROR_SUCCESS;
1278 }
1279 
1280 /**
1281   @brief Get the number of elements associated with a CeedOperator
1282 
1283   @param op             CeedOperator
1284   @param[out] num_elem  Variable to store number of elements
1285 
1286   @return An error code: 0 - success, otherwise - failure
1287 
1288   @ref Advanced
1289 **/
1290 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
1291   if (op->is_composite)
1292     // LCOV_EXCL_START
1293     return CeedError(op->ceed, CEED_ERROR_MINOR,
1294                      "Not defined for composite operator");
1295   // LCOV_EXCL_STOP
1296 
1297   *num_elem = op->num_elem;
1298   return CEED_ERROR_SUCCESS;
1299 }
1300 
1301 /**
1302   @brief Get the number of quadrature points associated with a CeedOperator
1303 
1304   @param op             CeedOperator
1305   @param[out] num_qpts  Variable to store vector number of quadrature points
1306 
1307   @return An error code: 0 - success, otherwise - failure
1308 
1309   @ref Advanced
1310 **/
1311 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
1312   if (op->is_composite)
1313     // LCOV_EXCL_START
1314     return CeedError(op->ceed, CEED_ERROR_MINOR,
1315                      "Not defined for composite operator");
1316   // LCOV_EXCL_STOP
1317 
1318   *num_qpts = op->num_qpts;
1319   return CEED_ERROR_SUCCESS;
1320 }
1321 
1322 /**
1323   @brief Estimate number of FLOPs required to apply CeedOperator on the active vector
1324 
1325   @param op    Operator to estimate FLOPs for
1326   @param flops Address of variable to hold FLOPs estimate
1327 
1328   @ref Backend
1329 **/
1330 int CeedOperatorGetFlopsEstimate(CeedOperator op, CeedSize *flops) {
1331   int ierr;
1332   bool is_composite;
1333   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1334 
1335   *flops = 0;
1336   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1337   if (is_composite) {
1338     CeedInt num_suboperators;
1339     ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1340     CeedOperator *sub_operators;
1341     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1342 
1343     // FLOPs for each suboperator
1344     for (CeedInt i = 0; i < num_suboperators; i++) {
1345       CeedSize suboperator_flops;
1346       ierr = CeedOperatorGetFlopsEstimate(sub_operators[i], &suboperator_flops);
1347       CeedChk(ierr);
1348       *flops += suboperator_flops;
1349     }
1350   } else {
1351     CeedInt num_input_fields, num_output_fields;
1352     CeedOperatorField *input_fields, *output_fields;
1353 
1354     ierr = CeedOperatorGetFields(op, &num_input_fields, &input_fields,
1355                                  &num_output_fields, &output_fields); CeedChk(ierr);
1356 
1357     CeedInt num_elem = 0;
1358     ierr = CeedOperatorGetNumElements(op, &num_elem); CeedChk(ierr);
1359     // Input FLOPs
1360     for (CeedInt i = 0; i < num_input_fields; i++) {
1361       if (input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1362         CeedSize restr_flops, basis_flops;
1363 
1364         ierr = CeedElemRestrictionGetFlopsEstimate(input_fields[i]->elem_restr,
1365                CEED_NOTRANSPOSE, &restr_flops); CeedChk(ierr);
1366         *flops += restr_flops;
1367         ierr = CeedBasisGetFlopsEstimate(input_fields[i]->basis, CEED_NOTRANSPOSE,
1368                                          op->qf->input_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1369         *flops += basis_flops * num_elem;
1370       }
1371     }
1372     // QF FLOPs
1373     CeedInt num_qpts;
1374     CeedSize qf_flops;
1375     ierr = CeedOperatorGetNumQuadraturePoints(op, &num_qpts); CeedChk(ierr);
1376     ierr = CeedQFunctionGetFlopsEstimate(op->qf, &qf_flops); CeedChk(ierr);
1377     *flops += num_elem * num_qpts * qf_flops;
1378     // Output FLOPs
1379     for (CeedInt i = 0; i < num_output_fields; i++) {
1380       if (output_fields[i]->vec == CEED_VECTOR_ACTIVE) {
1381         CeedSize restr_flops, basis_flops;
1382 
1383         ierr = CeedElemRestrictionGetFlopsEstimate(output_fields[i]->elem_restr,
1384                CEED_TRANSPOSE, &restr_flops); CeedChk(ierr);
1385         *flops += restr_flops;
1386         ierr = CeedBasisGetFlopsEstimate(output_fields[i]->basis, CEED_TRANSPOSE,
1387                                          op->qf->output_fields[i]->eval_mode, &basis_flops); CeedChk(ierr);
1388         *flops += basis_flops * num_elem;
1389       }
1390     }
1391   }
1392 
1393   return CEED_ERROR_SUCCESS;
1394 }
1395 
1396 /**
1397   @brief Get label for a registered QFunctionContext field, or `NULL` if no
1398            field has been registered with this `field_name`.
1399 
1400   @param[in] op            CeedOperator
1401   @param[in] field_name    Name of field to retrieve label
1402   @param[out] field_label  Variable to field label
1403 
1404   @return An error code: 0 - success, otherwise - failure
1405 
1406   @ref User
1407 **/
1408 int CeedOperatorContextGetFieldLabel(CeedOperator op,
1409                                      const char *field_name,
1410                                      CeedContextFieldLabel *field_label) {
1411   int ierr;
1412 
1413   bool is_composite;
1414   ierr = CeedOperatorIsComposite(op, &is_composite); CeedChk(ierr);
1415   if (is_composite) {
1416     // Check if composite label already created
1417     for (CeedInt i=0; i<op->num_context_labels; i++) {
1418       if (!strcmp(op->context_labels[i]->name, field_name)) {
1419         *field_label = op->context_labels[i];
1420         return CEED_ERROR_SUCCESS;
1421       }
1422     }
1423 
1424     // Create composite label if needed
1425     CeedInt num_sub;
1426     CeedOperator *sub_operators;
1427     CeedContextFieldLabel new_field_label;
1428 
1429     ierr = CeedCalloc(1, &new_field_label); CeedChk(ierr);
1430     ierr = CeedOperatorGetNumSub(op, &num_sub); CeedChk(ierr);
1431     ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1432     ierr = CeedCalloc(num_sub, &new_field_label->sub_labels); CeedChk(ierr);
1433     new_field_label->num_sub_labels = num_sub;
1434 
1435     bool label_found = false;
1436     for (CeedInt i=0; i<num_sub; i++) {
1437       if (sub_operators[i]->qf->ctx) {
1438         CeedContextFieldLabel new_field_label_i;
1439         ierr = CeedQFunctionContextGetFieldLabel(sub_operators[i]->qf->ctx, field_name,
1440                &new_field_label_i); CeedChk(ierr);
1441         if (new_field_label_i) {
1442           label_found = true;
1443           new_field_label->sub_labels[i] = new_field_label_i;
1444           new_field_label->name = new_field_label_i->name;
1445           new_field_label->description = new_field_label_i->description;
1446           if (new_field_label->type &&
1447               new_field_label->type != new_field_label_i->type) {
1448             // LCOV_EXCL_START
1449             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1450             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1451                              "Incompatible field types on sub-operator contexts. "
1452                              "%s != %s",
1453                              CeedContextFieldTypes[new_field_label->type],
1454                              CeedContextFieldTypes[new_field_label_i->type]);
1455             // LCOV_EXCL_STOP
1456           } else {
1457             new_field_label->type = new_field_label_i->type;
1458           }
1459           if (new_field_label->num_values != 0 &&
1460               new_field_label->num_values != new_field_label_i->num_values) {
1461             // LCOV_EXCL_START
1462             ierr = CeedFree(&new_field_label); CeedChk(ierr);
1463             return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
1464                              "Incompatible field number of values on sub-operator"
1465                              " contexts. %ld != %ld",
1466                              new_field_label->num_values, new_field_label_i->num_values);
1467             // LCOV_EXCL_STOP
1468           } else {
1469             new_field_label->num_values = new_field_label_i->num_values;
1470           }
1471         }
1472       }
1473     }
1474     if (!label_found) {
1475       // LCOV_EXCL_START
1476       ierr = CeedFree(&new_field_label->sub_labels); CeedChk(ierr);
1477       ierr = CeedFree(&new_field_label); CeedChk(ierr);
1478       *field_label = NULL;
1479       // LCOV_EXCL_STOP
1480     } else {
1481       // Move new composite label to operator
1482       if (op->num_context_labels == 0) {
1483         ierr = CeedCalloc(1, &op->context_labels); CeedChk(ierr);
1484         op->max_context_labels = 1;
1485       } else if (op->num_context_labels == op->max_context_labels) {
1486         ierr = CeedRealloc(2*op->num_context_labels, &op->context_labels);
1487         CeedChk(ierr);
1488         op->max_context_labels *= 2;
1489       }
1490       op->context_labels[op->num_context_labels] = new_field_label;
1491       *field_label = new_field_label;
1492       op->num_context_labels++;
1493     }
1494 
1495     return CEED_ERROR_SUCCESS;
1496   } else {
1497     return CeedQFunctionContextGetFieldLabel(op->qf->ctx, field_name, field_label);
1498   }
1499 }
1500 
1501 /**
1502   @brief Set QFunctionContext field holding a double precision value.
1503            For composite operators, the value is set in all
1504            sub-operator QFunctionContexts that have a matching `field_name`.
1505 
1506   @param op          CeedOperator
1507   @param field_label Label of field to register
1508   @param values      Values to set
1509 
1510   @return An error code: 0 - success, otherwise - failure
1511 
1512   @ref User
1513 **/
1514 int CeedOperatorContextSetDouble(CeedOperator op,
1515                                  CeedContextFieldLabel field_label,
1516                                  double *values) {
1517   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_DOUBLE,
1518                                        values);
1519 }
1520 
1521 /**
1522   @brief Set QFunctionContext field holding an int32 value.
1523            For composite operators, the value is set in all
1524            sub-operator QFunctionContexts that have a matching `field_name`.
1525 
1526   @param op          CeedOperator
1527   @param field_label Label of field to set
1528   @param values      Values to set
1529 
1530   @return An error code: 0 - success, otherwise - failure
1531 
1532   @ref User
1533 **/
1534 int CeedOperatorContextSetInt32(CeedOperator op,
1535                                 CeedContextFieldLabel field_label,
1536                                 int *values) {
1537   return CeedOperatorContextSetGeneric(op, field_label, CEED_CONTEXT_FIELD_INT32,
1538                                        values);
1539 }
1540 
1541 /**
1542   @brief Apply CeedOperator to a vector
1543 
1544   This computes the action of the operator on the specified (active) input,
1545   yielding its (active) output.  All inputs and outputs must be specified using
1546   CeedOperatorSetField().
1547 
1548   Note: Calling this function asserts that setup is complete
1549           and sets the CeedOperator as immutable.
1550 
1551   @param op        CeedOperator to apply
1552   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1553                      there are no active inputs
1554   @param[out] out  CeedVector to store result of applying operator (must be
1555                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1556                      active outputs
1557   @param request   Address of CeedRequest for non-blocking completion, else
1558                      @ref CEED_REQUEST_IMMEDIATE
1559 
1560   @return An error code: 0 - success, otherwise - failure
1561 
1562   @ref User
1563 **/
1564 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1565                       CeedRequest *request) {
1566   int ierr;
1567   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1568 
1569   if (op->num_elem)  {
1570     // Standard Operator
1571     if (op->Apply) {
1572       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1573     } else {
1574       // Zero all output vectors
1575       CeedQFunction qf = op->qf;
1576       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1577         CeedVector vec = op->output_fields[i]->vec;
1578         if (vec == CEED_VECTOR_ACTIVE)
1579           vec = out;
1580         if (vec != CEED_VECTOR_NONE) {
1581           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1582         }
1583       }
1584       // Apply
1585       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1586     }
1587   } else if (op->is_composite) {
1588     // Composite Operator
1589     if (op->ApplyComposite) {
1590       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1591     } else {
1592       CeedInt num_suboperators;
1593       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1594       CeedOperator *sub_operators;
1595       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1596 
1597       // Zero all output vectors
1598       if (out != CEED_VECTOR_NONE) {
1599         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1600       }
1601       for (CeedInt i=0; i<num_suboperators; i++) {
1602         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1603           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1604           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1605             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1606           }
1607         }
1608       }
1609       // Apply
1610       for (CeedInt i=0; i<op->num_suboperators; i++) {
1611         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1612         CeedChk(ierr);
1613       }
1614     }
1615   }
1616   return CEED_ERROR_SUCCESS;
1617 }
1618 
1619 /**
1620   @brief Apply CeedOperator to a vector and add result to output vector
1621 
1622   This computes the action of the operator on the specified (active) input,
1623   yielding its (active) output.  All inputs and outputs must be specified using
1624   CeedOperatorSetField().
1625 
1626   @param op        CeedOperator to apply
1627   @param[in] in    CeedVector containing input state or NULL if there are no
1628                      active inputs
1629   @param[out] out  CeedVector to sum in result of applying operator (must be
1630                      distinct from @a in) or NULL if there are no active outputs
1631   @param request   Address of CeedRequest for non-blocking completion, else
1632                      @ref CEED_REQUEST_IMMEDIATE
1633 
1634   @return An error code: 0 - success, otherwise - failure
1635 
1636   @ref User
1637 **/
1638 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1639                          CeedRequest *request) {
1640   int ierr;
1641   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1642 
1643   if (op->num_elem)  {
1644     // Standard Operator
1645     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1646   } else if (op->is_composite) {
1647     // Composite Operator
1648     if (op->ApplyAddComposite) {
1649       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1650     } else {
1651       CeedInt num_suboperators;
1652       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1653       CeedOperator *sub_operators;
1654       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1655 
1656       for (CeedInt i=0; i<num_suboperators; i++) {
1657         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1658         CeedChk(ierr);
1659       }
1660     }
1661   }
1662   return CEED_ERROR_SUCCESS;
1663 }
1664 
1665 /**
1666   @brief Destroy a CeedOperator
1667 
1668   @param op  CeedOperator to destroy
1669 
1670   @return An error code: 0 - success, otherwise - failure
1671 
1672   @ref User
1673 **/
1674 int CeedOperatorDestroy(CeedOperator *op) {
1675   int ierr;
1676 
1677   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1678   if ((*op)->Destroy) {
1679     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1680   }
1681   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1682   // Free fields
1683   for (CeedInt i=0; i<(*op)->num_fields; i++)
1684     if ((*op)->input_fields[i]) {
1685       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1686         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1687         CeedChk(ierr);
1688       }
1689       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1690         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1691       }
1692       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1693           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1694         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1695       }
1696       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1697       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1698     }
1699   for (CeedInt i=0; i<(*op)->num_fields; i++)
1700     if ((*op)->output_fields[i]) {
1701       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1702       CeedChk(ierr);
1703       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1704         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1705       }
1706       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1707           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1708         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1709       }
1710       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1711       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1712     }
1713   // Destroy sub_operators
1714   for (CeedInt i=0; i<(*op)->num_suboperators; i++)
1715     if ((*op)->sub_operators[i]) {
1716       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1717     }
1718   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1719   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1720   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1721   // Destroy any composite labels
1722   for (CeedInt i=0; i<(*op)->num_context_labels; i++) {
1723     ierr = CeedFree(&(*op)->context_labels[i]->sub_labels); CeedChk(ierr);
1724     ierr = CeedFree(&(*op)->context_labels[i]); CeedChk(ierr);
1725   }
1726   ierr = CeedFree(&(*op)->context_labels); CeedChk(ierr);
1727 
1728   // Destroy fallback
1729   ierr = CeedOperatorDestroy(&(*op)->op_fallback); CeedChk(ierr);
1730 
1731   // Destroy assembly data
1732   ierr = CeedQFunctionAssemblyDataDestroy(&(*op)->qf_assembled); CeedChk(ierr);
1733   ierr = CeedOperatorAssemblyDataDestroy(&(*op)->op_assembled); CeedChk(ierr);
1734 
1735   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1736   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1737   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1738   ierr = CeedFree(&(*op)->name); CeedChk(ierr);
1739   ierr = CeedFree(op); CeedChk(ierr);
1740   return CEED_ERROR_SUCCESS;
1741 }
1742 
1743 /// @}
1744