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