xref: /libCEED/interface/ceed-operator.c (revision bf185410af015d53ed6d3b520013ce284095d550)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed-impl.h>
20 #include <math.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 /// @file
26 /// Implementation of CeedOperator interfaces
27 
28 /// ----------------------------------------------------------------------------
29 /// CeedOperator Library Internal Functions
30 /// ----------------------------------------------------------------------------
31 /// @addtogroup CeedOperatorDeveloper
32 /// @{
33 
34 /**
35   @brief Check if a CeedOperator Field matches the QFunction Field
36 
37   @param[in] ceed      Ceed object for error handling
38   @param[in] qf_field  QFunction Field matching Operator Field
39   @param[in] r         Operator Field ElemRestriction
40   @param[in] b         Operator Field Basis
41 
42   @return An error code: 0 - success, otherwise - failure
43 
44   @ref Developer
45 **/
46 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qf_field,
47                                   CeedElemRestriction r, CeedBasis b) {
48   int ierr;
49   CeedEvalMode eval_mode = qf_field->eval_mode;
50   CeedInt dim = 1, num_comp = 1, restr_num_comp = 1, size = qf_field->size;
51   // Restriction
52   if (r != CEED_ELEMRESTRICTION_NONE) {
53     if (eval_mode == CEED_EVAL_WEIGHT) {
54       // LCOV_EXCL_START
55       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
56                        "CEED_ELEMRESTRICTION_NONE should be used "
57                        "for a field with eval mode CEED_EVAL_WEIGHT");
58       // LCOV_EXCL_STOP
59     }
60     ierr = CeedElemRestrictionGetNumComponents(r, &restr_num_comp);
61     CeedChk(ierr);
62   }
63   if ((r == CEED_ELEMRESTRICTION_NONE) != (eval_mode == CEED_EVAL_WEIGHT)) {
64     // LCOV_EXCL_START
65     return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
66                      "CEED_ELEMRESTRICTION_NONE and CEED_EVAL_WEIGHT "
67                      "must be used together.");
68     // LCOV_EXCL_STOP
69   }
70   // Basis
71   if (b != CEED_BASIS_COLLOCATED) {
72     if (eval_mode == CEED_EVAL_NONE)
73       // LCOV_EXCL_START
74       return CeedError(ceed, CEED_ERROR_INCOMPATIBLE,
75                        "Field '%s' configured with CEED_EVAL_NONE must "
76                        "be used with CEED_BASIS_COLLOCATED",
77                        // LCOV_EXCL_STOP
78                        qf_field->field_name);
79     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
80     ierr = CeedBasisGetNumComponents(b, &num_comp); CeedChk(ierr);
81     if (r != CEED_ELEMRESTRICTION_NONE && restr_num_comp != num_comp) {
82       // LCOV_EXCL_START
83       return CeedError(ceed, CEED_ERROR_DIMENSION,
84                        "Field '%s' of size %d and EvalMode %s: ElemRestriction "
85                        "has %d components, but Basis has %d components",
86                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
87                        restr_num_comp,
88                        num_comp);
89       // LCOV_EXCL_STOP
90     }
91   }
92   // Field size
93   switch(eval_mode) {
94   case CEED_EVAL_NONE:
95     if (size != restr_num_comp)
96       // LCOV_EXCL_START
97       return CeedError(ceed, CEED_ERROR_DIMENSION,
98                        "Field '%s' of size %d and EvalMode %s: ElemRestriction has %d 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)
105       // LCOV_EXCL_START
106       return CeedError(ceed, CEED_ERROR_DIMENSION,
107                        "Field '%s' of size %d and EvalMode %s: ElemRestriction/Basis has %d components",
108                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode],
109                        num_comp);
110     // LCOV_EXCL_STOP
111     break;
112   case CEED_EVAL_GRAD:
113     if (size != num_comp * dim)
114       // LCOV_EXCL_START
115       return CeedError(ceed, CEED_ERROR_DIMENSION,
116                        "Field '%s' of size %d and EvalMode %s in %d dimensions: "
117                        "ElemRestriction/Basis has %d components",
118                        qf_field->field_name, qf_field->size, CeedEvalModes[qf_field->eval_mode], dim,
119                        num_comp);
120     // LCOV_EXCL_STOP
121     break;
122   case CEED_EVAL_WEIGHT:
123     // No additional checks required
124     break;
125   case CEED_EVAL_DIV:
126     // Not implemented
127     break;
128   case CEED_EVAL_CURL:
129     // Not implemented
130     break;
131   }
132   return CEED_ERROR_SUCCESS;
133 }
134 
135 /**
136   @brief Check if a CeedOperator is ready to be used.
137 
138   @param[in] op  CeedOperator to check
139 
140   @return An error code: 0 - success, otherwise - failure
141 
142   @ref Developer
143 **/
144 int CeedOperatorCheckReady(CeedOperator op) {
145   int ierr;
146   Ceed ceed;
147   ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
148 
149   if (op->interface_setup)
150     return CEED_ERROR_SUCCESS;
151 
152   CeedQFunction qf = op->qf;
153   if (op->composite) {
154     if (!op->num_suboperators)
155       // LCOV_EXCL_START
156       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No sub_operators set");
157     // LCOV_EXCL_STOP
158   } else {
159     if (op->num_fields == 0)
160       // LCOV_EXCL_START
161       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
162     // LCOV_EXCL_STOP
163     if (op->num_fields < qf->num_input_fields + qf->num_output_fields)
164       // LCOV_EXCL_START
165       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
166     // LCOV_EXCL_STOP
167     if (!op->has_restriction)
168       // LCOV_EXCL_START
169       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
170                        "At least one restriction required");
171     // LCOV_EXCL_STOP
172     if (op->num_qpts == 0)
173       // LCOV_EXCL_START
174       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
175                        "At least one non-collocated basis is required "
176                        "or the number of quadrature points must be set");
177     // LCOV_EXCL_STOP
178   }
179 
180   // Flag as immutable and ready
181   op->interface_setup = true;
182   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
183     // LCOV_EXCL_START
184     op->qf->operators_set++;
185   // LCOV_EXCL_STOP
186   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
187     // LCOV_EXCL_START
188     op->dqf->operators_set++;
189   // LCOV_EXCL_STOP
190   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
191     // LCOV_EXCL_START
192     op->dqfT->operators_set++;
193   // LCOV_EXCL_STOP
194   return CEED_ERROR_SUCCESS;
195 }
196 
197 /**
198   @brief View a field of a CeedOperator
199 
200   @param[in] field         Operator field to view
201   @param[in] qf_field      QFunction field (carries field name)
202   @param[in] field_number  Number of field being viewed
203   @param[in] sub           true indicates sub-operator, which increases indentation; false for top-level operator
204   @param[in] input         true for an input field; false for output field
205   @param[in] stream        Stream to view to, e.g., stdout
206 
207   @return An error code: 0 - success, otherwise - failure
208 
209   @ref Utility
210 **/
211 static int CeedOperatorFieldView(CeedOperatorField field,
212                                  CeedQFunctionField qf_field,
213                                  CeedInt field_number, bool sub, bool input,
214                                  FILE *stream) {
215   const char *pre = sub ? "  " : "";
216   const char *in_out = input ? "Input" : "Output";
217 
218   fprintf(stream, "%s    %s Field [%d]:\n"
219           "%s      Name: \"%s\"\n",
220           pre, in_out, field_number, pre, qf_field->field_name);
221 
222   if (field->basis == CEED_BASIS_COLLOCATED)
223     fprintf(stream, "%s      Collocated basis\n", pre);
224 
225   if (field->vec == CEED_VECTOR_ACTIVE)
226     fprintf(stream, "%s      Active vector\n", pre);
227   else if (field->vec == CEED_VECTOR_NONE)
228     fprintf(stream, "%s      No vector\n", pre);
229   return CEED_ERROR_SUCCESS;
230 }
231 
232 /**
233   @brief View a single CeedOperator
234 
235   @param[in] op      CeedOperator to view
236   @param[in] sub     Boolean flag for sub-operator
237   @param[in] stream  Stream to write; typically stdout/stderr or a file
238 
239   @return Error code: 0 - success, otherwise - failure
240 
241   @ref Utility
242 **/
243 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
244   int ierr;
245   const char *pre = sub ? "  " : "";
246 
247   CeedInt total_fields = 0;
248   ierr = CeedOperatorGetNumArgs(op, &total_fields); CeedChk(ierr);
249 
250   fprintf(stream, "%s  %d Field%s\n", pre, total_fields,
251           total_fields>1 ? "s" : "");
252 
253   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->num_input_fields,
254           op->qf->num_input_fields>1 ? "s" : "");
255   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
256     ierr = CeedOperatorFieldView(op->input_fields[i], op->qf->input_fields[i],
257                                  i, sub, 1, stream); CeedChk(ierr);
258   }
259 
260   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->num_output_fields,
261           op->qf->num_output_fields>1 ? "s" : "");
262   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
263     ierr = CeedOperatorFieldView(op->output_fields[i], op->qf->output_fields[i],
264                                  i, sub, 0, stream); CeedChk(ierr);
265   }
266   return CEED_ERROR_SUCCESS;
267 }
268 
269 /**
270   @brief Find the active vector basis for a CeedOperator
271 
272   @param[in] op             CeedOperator to find active basis for
273   @param[out] active_basis  Basis for active input vector
274 
275   @return An error code: 0 - success, otherwise - failure
276 
277   @ ref Developer
278 **/
279 int CeedOperatorGetActiveBasis(CeedOperator op, CeedBasis *active_basis) {
280   *active_basis = NULL;
281   for (int i = 0; i < op->qf->num_input_fields; i++)
282     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
283       *active_basis = op->input_fields[i]->basis;
284       break;
285     }
286 
287   if (!*active_basis) {
288     // LCOV_EXCL_START
289     int ierr;
290     Ceed ceed;
291     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
292     return CeedError(ceed, CEED_ERROR_MINOR,
293                      "No active CeedBasis found");
294     // LCOV_EXCL_STOP
295   }
296   return CEED_ERROR_SUCCESS;
297 }
298 
299 /**
300   @brief Find the active vector ElemRestriction for a CeedOperator
301 
302   @param[in] op            CeedOperator to find active basis for
303   @param[out] active_rstr  ElemRestriction for active input vector
304 
305   @return An error code: 0 - success, otherwise - failure
306 
307   @ref Utility
308 **/
309 int CeedOperatorGetActiveElemRestriction(CeedOperator op,
310     CeedElemRestriction *active_rstr) {
311   *active_rstr = NULL;
312   for (int i = 0; i < op->qf->num_input_fields; i++)
313     if (op->input_fields[i]->vec == CEED_VECTOR_ACTIVE) {
314       *active_rstr = op->input_fields[i]->elem_restr;
315       break;
316     }
317 
318   if (!*active_rstr) {
319     // LCOV_EXCL_START
320     int ierr;
321     Ceed ceed;
322     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
323     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
324                      "No active CeedElemRestriction found");
325     // LCOV_EXCL_STOP
326   }
327   return CEED_ERROR_SUCCESS;
328 }
329 
330 /// @}
331 
332 /// ----------------------------------------------------------------------------
333 /// CeedOperator Backend API
334 /// ----------------------------------------------------------------------------
335 /// @addtogroup CeedOperatorBackend
336 /// @{
337 
338 /**
339   @brief Get the Ceed associated with a CeedOperator
340 
341   @param op         CeedOperator
342   @param[out] ceed  Variable to store Ceed
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Backend
347 **/
348 
349 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
350   *ceed = op->ceed;
351   return CEED_ERROR_SUCCESS;
352 }
353 
354 /**
355   @brief Get the number of elements associated with a CeedOperator
356 
357   @param op             CeedOperator
358   @param[out] num_elem  Variable to store number of elements
359 
360   @return An error code: 0 - success, otherwise - failure
361 
362   @ref Backend
363 **/
364 
365 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *num_elem) {
366   if (op->composite)
367     // LCOV_EXCL_START
368     return CeedError(op->ceed, CEED_ERROR_MINOR,
369                      "Not defined for composite operator");
370   // LCOV_EXCL_STOP
371 
372   *num_elem = op->num_elem;
373   return CEED_ERROR_SUCCESS;
374 }
375 
376 /**
377   @brief Get the number of quadrature points associated with a CeedOperator
378 
379   @param op             CeedOperator
380   @param[out] num_qpts  Variable to store vector number of quadrature points
381 
382   @return An error code: 0 - success, otherwise - failure
383 
384   @ref Backend
385 **/
386 
387 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *num_qpts) {
388   if (op->composite)
389     // LCOV_EXCL_START
390     return CeedError(op->ceed, CEED_ERROR_MINOR,
391                      "Not defined for composite operator");
392   // LCOV_EXCL_STOP
393 
394   *num_qpts = op->num_qpts;
395   return CEED_ERROR_SUCCESS;
396 }
397 
398 /**
399   @brief Get the number of arguments associated with a CeedOperator
400 
401   @param op             CeedOperator
402   @param[out] num_args  Variable to store vector number of arguments
403 
404   @return An error code: 0 - success, otherwise - failure
405 
406   @ref Backend
407 **/
408 
409 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *num_args) {
410   if (op->composite)
411     // LCOV_EXCL_START
412     return CeedError(op->ceed, CEED_ERROR_MINOR,
413                      "Not defined for composite operators");
414   // LCOV_EXCL_STOP
415 
416   *num_args = op->num_fields;
417   return CEED_ERROR_SUCCESS;
418 }
419 
420 /**
421   @brief Get the setup status of a CeedOperator
422 
423   @param op                  CeedOperator
424   @param[out] is_setup_done  Variable to store setup status
425 
426   @return An error code: 0 - success, otherwise - failure
427 
428   @ref Backend
429 **/
430 
431 int CeedOperatorIsSetupDone(CeedOperator op, bool *is_setup_done) {
432   *is_setup_done = op->backend_setup;
433   return CEED_ERROR_SUCCESS;
434 }
435 
436 /**
437   @brief Get the QFunction associated with a CeedOperator
438 
439   @param op       CeedOperator
440   @param[out] qf  Variable to store QFunction
441 
442   @return An error code: 0 - success, otherwise - failure
443 
444   @ref Backend
445 **/
446 
447 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
448   if (op->composite)
449     // LCOV_EXCL_START
450     return CeedError(op->ceed, CEED_ERROR_MINOR,
451                      "Not defined for composite operator");
452   // LCOV_EXCL_STOP
453 
454   *qf = op->qf;
455   return CEED_ERROR_SUCCESS;
456 }
457 
458 /**
459   @brief Get a boolean value indicating if the CeedOperator is composite
460 
461   @param op                 CeedOperator
462   @param[out] is_composite  Variable to store composite status
463 
464   @return An error code: 0 - success, otherwise - failure
465 
466   @ref Backend
467 **/
468 
469 int CeedOperatorIsComposite(CeedOperator op, bool *is_composite) {
470   *is_composite = op->composite;
471   return CEED_ERROR_SUCCESS;
472 }
473 
474 /**
475   @brief Get the number of sub_operators associated with a CeedOperator
476 
477   @param op                     CeedOperator
478   @param[out] num_suboperators  Variable to store number of sub_operators
479 
480   @return An error code: 0 - success, otherwise - failure
481 
482   @ref Backend
483 **/
484 
485 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *num_suboperators) {
486   if (!op->composite)
487     // LCOV_EXCL_START
488     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
489   // LCOV_EXCL_STOP
490 
491   *num_suboperators = op->num_suboperators;
492   return CEED_ERROR_SUCCESS;
493 }
494 
495 /**
496   @brief Get the list of sub_operators associated with a CeedOperator
497 
498   @param op                  CeedOperator
499   @param[out] sub_operators  Variable to store list of sub_operators
500 
501   @return An error code: 0 - success, otherwise - failure
502 
503   @ref Backend
504 **/
505 
506 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **sub_operators) {
507   if (!op->composite)
508     // LCOV_EXCL_START
509     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
510   // LCOV_EXCL_STOP
511 
512   *sub_operators = op->sub_operators;
513   return CEED_ERROR_SUCCESS;
514 }
515 
516 /**
517   @brief Get the backend data of a CeedOperator
518 
519   @param op         CeedOperator
520   @param[out] data  Variable to store data
521 
522   @return An error code: 0 - success, otherwise - failure
523 
524   @ref Backend
525 **/
526 
527 int CeedOperatorGetData(CeedOperator op, void *data) {
528   *(void **)data = op->data;
529   return CEED_ERROR_SUCCESS;
530 }
531 
532 /**
533   @brief Set the backend data of a CeedOperator
534 
535   @param[out] op  CeedOperator
536   @param data     Data to set
537 
538   @return An error code: 0 - success, otherwise - failure
539 
540   @ref Backend
541 **/
542 
543 int CeedOperatorSetData(CeedOperator op, void *data) {
544   op->data = data;
545   return CEED_ERROR_SUCCESS;
546 }
547 
548 /**
549   @brief Increment the reference counter for a CeedOperator
550 
551   @param op  CeedOperator to increment the reference counter
552 
553   @return An error code: 0 - success, otherwise - failure
554 
555   @ref Backend
556 **/
557 int CeedOperatorReference(CeedOperator op) {
558   op->ref_count++;
559   return CEED_ERROR_SUCCESS;
560 }
561 
562 /**
563   @brief Set the setup flag of a CeedOperator to True
564 
565   @param op  CeedOperator
566 
567   @return An error code: 0 - success, otherwise - failure
568 
569   @ref Backend
570 **/
571 
572 int CeedOperatorSetSetupDone(CeedOperator op) {
573   op->backend_setup = true;
574   return CEED_ERROR_SUCCESS;
575 }
576 
577 /**
578   @brief Get the CeedOperatorFields of a CeedOperator
579 
580   @param op                  CeedOperator
581   @param[out] input_fields   Variable to store input_fields
582   @param[out] output_fields  Variable to store output_fields
583 
584   @return An error code: 0 - success, otherwise - failure
585 
586   @ref Backend
587 **/
588 
589 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **input_fields,
590                           CeedOperatorField **output_fields) {
591   if (op->composite)
592     // LCOV_EXCL_START
593     return CeedError(op->ceed, CEED_ERROR_MINOR,
594                      "Not defined for composite operator");
595   // LCOV_EXCL_STOP
596 
597   if (input_fields) *input_fields = op->input_fields;
598   if (output_fields) *output_fields = op->output_fields;
599   return CEED_ERROR_SUCCESS;
600 }
601 
602 /**
603   @brief Get the CeedElemRestriction of a CeedOperatorField
604 
605   @param op_field   CeedOperatorField
606   @param[out] rstr  Variable to store CeedElemRestriction
607 
608   @return An error code: 0 - success, otherwise - failure
609 
610   @ref Backend
611 **/
612 
613 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
614                                         CeedElemRestriction *rstr) {
615   *rstr = op_field->elem_restr;
616   return CEED_ERROR_SUCCESS;
617 }
618 
619 /**
620   @brief Get the CeedBasis of a CeedOperatorField
621 
622   @param op_field    CeedOperatorField
623   @param[out] basis  Variable to store CeedBasis
624 
625   @return An error code: 0 - success, otherwise - failure
626 
627   @ref Backend
628 **/
629 
630 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
631   *basis = op_field->basis;
632   return CEED_ERROR_SUCCESS;
633 }
634 
635 /**
636   @brief Get the CeedVector of a CeedOperatorField
637 
638   @param op_field  CeedOperatorField
639   @param[out] vec  Variable to store CeedVector
640 
641   @return An error code: 0 - success, otherwise - failure
642 
643   @ref Backend
644 **/
645 
646 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
647   *vec = op_field->vec;
648   return CEED_ERROR_SUCCESS;
649 }
650 
651 /// @}
652 
653 /// ----------------------------------------------------------------------------
654 /// CeedOperator Public API
655 /// ----------------------------------------------------------------------------
656 /// @addtogroup CeedOperatorUser
657 /// @{
658 
659 /**
660   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
661            CeedElemRestriction can be associated with CeedQFunction fields with
662            \ref CeedOperatorSetField.
663 
664   @param ceed     A Ceed object where the CeedOperator will be created
665   @param qf       QFunction defining the action of the operator at quadrature points
666   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
667                     @ref CEED_QFUNCTION_NONE)
668   @param dqfT     QFunction defining the action of the transpose of the Jacobian
669                     of @a qf (or @ref CEED_QFUNCTION_NONE)
670   @param[out] op  Address of the variable where the newly created
671                     CeedOperator will be stored
672 
673   @return An error code: 0 - success, otherwise - failure
674 
675   @ref User
676  */
677 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
678                        CeedQFunction dqfT, CeedOperator *op) {
679   int ierr;
680 
681   if (!ceed->OperatorCreate) {
682     Ceed delegate;
683     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
684 
685     if (!delegate)
686       // LCOV_EXCL_START
687       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
688                        "Backend does not support OperatorCreate");
689     // LCOV_EXCL_STOP
690 
691     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
692     return CEED_ERROR_SUCCESS;
693   }
694 
695   if (!qf || qf == CEED_QFUNCTION_NONE)
696     // LCOV_EXCL_START
697     return CeedError(ceed, CEED_ERROR_MINOR,
698                      "Operator must have a valid QFunction.");
699   // LCOV_EXCL_STOP
700   ierr = CeedCalloc(1, op); CeedChk(ierr);
701   (*op)->ceed = ceed;
702   ierr = CeedReference(ceed); CeedChk(ierr);
703   (*op)->ref_count = 1;
704   (*op)->qf = qf;
705   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
706   if (dqf && dqf != CEED_QFUNCTION_NONE) {
707     (*op)->dqf = dqf;
708     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
709   }
710   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
711     (*op)->dqfT = dqfT;
712     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
713   }
714   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
715   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
716   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
717   return CEED_ERROR_SUCCESS;
718 }
719 
720 /**
721   @brief Create an operator that composes the action of several operators
722 
723   @param ceed     A Ceed object where the CeedOperator will be created
724   @param[out] op  Address of the variable where the newly created
725                     Composite CeedOperator will be stored
726 
727   @return An error code: 0 - success, otherwise - failure
728 
729   @ref User
730  */
731 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
732   int ierr;
733 
734   if (!ceed->CompositeOperatorCreate) {
735     Ceed delegate;
736     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
737 
738     if (delegate) {
739       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
740       return CEED_ERROR_SUCCESS;
741     }
742   }
743 
744   ierr = CeedCalloc(1, op); CeedChk(ierr);
745   (*op)->ceed = ceed;
746   ierr = CeedReference(ceed); CeedChk(ierr);
747   (*op)->composite = true;
748   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
749 
750   if (ceed->CompositeOperatorCreate) {
751     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
752   }
753   return CEED_ERROR_SUCCESS;
754 }
755 
756 /**
757   @brief Copy the pointer to a CeedOperator. Both pointers should
758            be destroyed with `CeedOperatorDestroy()`;
759            Note: If `*op_copy` is non-NULL, then it is assumed that
760            `*op_copy` is a pointer to a CeedOperator. This
761            CeedOperator will be destroyed if `*op_copy` is the only
762            reference to this CeedOperator.
763 
764   @param op            CeedOperator to copy reference to
765   @param[out] op_copy  Variable to store copied reference
766 
767   @return An error code: 0 - success, otherwise - failure
768 
769   @ref User
770 **/
771 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
772   int ierr;
773 
774   ierr = CeedOperatorReference(op); CeedChk(ierr);
775   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
776   *op_copy = op;
777   return CEED_ERROR_SUCCESS;
778 }
779 
780 /**
781   @brief Provide a field to a CeedOperator for use by its CeedQFunction
782 
783   This function is used to specify both active and passive fields to a
784   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
785   fields can inputs or outputs (updated in-place when operator is applied).
786 
787   Active fields must be specified using this function, but their data (in a
788   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
789   input and at most one active output.
790 
791   @param op          CeedOperator on which to provide the field
792   @param field_name  Name of the field (to be matched with the name used by
793                        CeedQFunction)
794   @param r           CeedElemRestriction
795   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
796                        if collocated with quadrature points
797   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
798                        if field is active or @ref CEED_VECTOR_NONE if using
799                        @ref CEED_EVAL_WEIGHT in the QFunction
800 
801   @return An error code: 0 - success, otherwise - failure
802 
803   @ref User
804 **/
805 int CeedOperatorSetField(CeedOperator op, const char *field_name,
806                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
807   int ierr;
808   if (op->composite)
809     // LCOV_EXCL_START
810     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
811                      "Cannot add field to composite operator.");
812   // LCOV_EXCL_STOP
813   if (!r)
814     // LCOV_EXCL_START
815     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
816                      "ElemRestriction r for field \"%s\" must be non-NULL.",
817                      field_name);
818   // LCOV_EXCL_STOP
819   if (!b)
820     // LCOV_EXCL_START
821     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
822                      "Basis b for field \"%s\" must be non-NULL.",
823                      field_name);
824   // LCOV_EXCL_STOP
825   if (!v)
826     // LCOV_EXCL_START
827     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
828                      "Vector v for field \"%s\" must be non-NULL.",
829                      field_name);
830   // LCOV_EXCL_STOP
831 
832   CeedInt num_elem;
833   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
834   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
835       op->num_elem != num_elem)
836     // LCOV_EXCL_START
837     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
838                      "ElemRestriction with %d elements incompatible with prior "
839                      "%d elements", num_elem, op->num_elem);
840   // LCOV_EXCL_STOP
841 
842   CeedInt num_qpts = 0;
843   if (b != CEED_BASIS_COLLOCATED) {
844     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
845     if (op->num_qpts && op->num_qpts != num_qpts)
846       // LCOV_EXCL_START
847       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
848                        "Basis with %d quadrature points "
849                        "incompatible with prior %d points", num_qpts,
850                        op->num_qpts);
851     // LCOV_EXCL_STOP
852   }
853   CeedQFunctionField qf_field;
854   CeedOperatorField *op_field;
855   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
856     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
857       qf_field = op->qf->input_fields[i];
858       op_field = &op->input_fields[i];
859       goto found;
860     }
861   }
862   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
863     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
864       qf_field = op->qf->output_fields[i];
865       op_field = &op->output_fields[i];
866       goto found;
867     }
868   }
869   // LCOV_EXCL_START
870   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
871                    "QFunction has no knowledge of field '%s'",
872                    field_name);
873   // LCOV_EXCL_STOP
874 found:
875   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
876   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
877 
878   (*op_field)->vec = v;
879   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
880     ierr = CeedVectorReference(v); CeedChk(ierr);
881   }
882 
883   (*op_field)->elem_restr = r;
884   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
885   if (r != CEED_ELEMRESTRICTION_NONE) {
886     op->num_elem = num_elem;
887     op->has_restriction = true; // Restriction set, but num_elem may be 0
888   }
889 
890   (*op_field)->basis = b;
891   if (b != CEED_BASIS_COLLOCATED) {
892     if (!op->num_qpts) {
893       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
894     }
895     ierr = CeedBasisReference(b); CeedChk(ierr);
896   }
897 
898   op->num_fields += 1;
899   size_t len = strlen(field_name);
900   char *tmp;
901   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
902   memcpy(tmp, field_name, len+1);
903   (*op_field)->field_name = tmp;
904   return CEED_ERROR_SUCCESS;
905 }
906 
907 /**
908   @brief Add a sub-operator to a composite CeedOperator
909 
910   @param[out] composite_op  Composite CeedOperator
911   @param      sub_op        Sub-operator CeedOperator
912 
913   @return An error code: 0 - success, otherwise - failure
914 
915   @ref User
916  */
917 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
918                                 CeedOperator sub_op) {
919   int ierr;
920 
921   if (!composite_op->composite)
922     // LCOV_EXCL_START
923     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
924                      "CeedOperator is not a composite operator");
925   // LCOV_EXCL_STOP
926 
927   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
928     // LCOV_EXCL_START
929     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
930                      "Cannot add additional sub_operators");
931   // LCOV_EXCL_STOP
932 
933   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
934   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
935   composite_op->num_suboperators++;
936   return CEED_ERROR_SUCCESS;
937 }
938 
939 /**
940   @brief Set the number of quadrature points associated with a CeedOperator.
941            This should be used when creating a CeedOperator where every
942            field has a collocated basis. This function cannot be used for
943            composite CeedOperators.
944 
945   @param op        CeedOperator
946   @param num_qpts  Number of quadrature points to set
947 
948   @return An error code: 0 - success, otherwise - failure
949 
950   @ref Backend
951 **/
952 
953 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
954   if (op->composite)
955     // LCOV_EXCL_START
956     return CeedError(op->ceed, CEED_ERROR_MINOR,
957                      "Not defined for composite operator");
958   // LCOV_EXCL_STOP
959   if (op->num_qpts)
960     // LCOV_EXCL_START
961     return CeedError(op->ceed, CEED_ERROR_MINOR,
962                      "Number of quadrature points already defined");
963   // LCOV_EXCL_STOP
964 
965   op->num_qpts = num_qpts;
966   return CEED_ERROR_SUCCESS;
967 }
968 
969 /**
970   @brief View a CeedOperator
971 
972   @param[in] op      CeedOperator to view
973   @param[in] stream  Stream to write; typically stdout/stderr or a file
974 
975   @return Error code: 0 - success, otherwise - failure
976 
977   @ref User
978 **/
979 int CeedOperatorView(CeedOperator op, FILE *stream) {
980   int ierr;
981 
982   if (op->composite) {
983     fprintf(stream, "Composite CeedOperator\n");
984 
985     for (CeedInt i=0; i<op->num_suboperators; i++) {
986       fprintf(stream, "  SubOperator [%d]:\n", i);
987       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
988       CeedChk(ierr);
989     }
990   } else {
991     fprintf(stream, "CeedOperator\n");
992     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
993   }
994   return CEED_ERROR_SUCCESS;
995 }
996 
997 /**
998   @brief Apply CeedOperator to a vector
999 
1000   This computes the action of the operator on the specified (active) input,
1001   yielding its (active) output.  All inputs and outputs must be specified using
1002   CeedOperatorSetField().
1003 
1004   @param op        CeedOperator to apply
1005   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1006                      there are no active inputs
1007   @param[out] out  CeedVector to store result of applying operator (must be
1008                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1009                      active outputs
1010   @param request   Address of CeedRequest for non-blocking completion, else
1011                      @ref CEED_REQUEST_IMMEDIATE
1012 
1013   @return An error code: 0 - success, otherwise - failure
1014 
1015   @ref User
1016 **/
1017 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1018                       CeedRequest *request) {
1019   int ierr;
1020   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1021 
1022   if (op->num_elem)  {
1023     // Standard Operator
1024     if (op->Apply) {
1025       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1026     } else {
1027       // Zero all output vectors
1028       CeedQFunction qf = op->qf;
1029       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1030         CeedVector vec = op->output_fields[i]->vec;
1031         if (vec == CEED_VECTOR_ACTIVE)
1032           vec = out;
1033         if (vec != CEED_VECTOR_NONE) {
1034           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1035         }
1036       }
1037       // Apply
1038       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1039     }
1040   } else if (op->composite) {
1041     // Composite Operator
1042     if (op->ApplyComposite) {
1043       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1044     } else {
1045       CeedInt num_suboperators;
1046       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1047       CeedOperator *sub_operators;
1048       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1049 
1050       // Zero all output vectors
1051       if (out != CEED_VECTOR_NONE) {
1052         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1053       }
1054       for (CeedInt i=0; i<num_suboperators; i++) {
1055         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1056           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1057           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1058             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1059           }
1060         }
1061       }
1062       // Apply
1063       for (CeedInt i=0; i<op->num_suboperators; i++) {
1064         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1065         CeedChk(ierr);
1066       }
1067     }
1068   }
1069   return CEED_ERROR_SUCCESS;
1070 }
1071 
1072 /**
1073   @brief Apply CeedOperator to a vector and add result to output vector
1074 
1075   This computes the action of the operator on the specified (active) input,
1076   yielding its (active) output.  All inputs and outputs must be specified using
1077   CeedOperatorSetField().
1078 
1079   @param op        CeedOperator to apply
1080   @param[in] in    CeedVector containing input state or NULL if there are no
1081                      active inputs
1082   @param[out] out  CeedVector to sum in result of applying operator (must be
1083                      distinct from @a in) or NULL if there are no active outputs
1084   @param request   Address of CeedRequest for non-blocking completion, else
1085                      @ref CEED_REQUEST_IMMEDIATE
1086 
1087   @return An error code: 0 - success, otherwise - failure
1088 
1089   @ref User
1090 **/
1091 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1092                          CeedRequest *request) {
1093   int ierr;
1094   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1095 
1096   if (op->num_elem)  {
1097     // Standard Operator
1098     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1099   } else if (op->composite) {
1100     // Composite Operator
1101     if (op->ApplyAddComposite) {
1102       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1103     } else {
1104       CeedInt num_suboperators;
1105       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1106       CeedOperator *sub_operators;
1107       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1108 
1109       for (CeedInt i=0; i<num_suboperators; i++) {
1110         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1111         CeedChk(ierr);
1112       }
1113     }
1114   }
1115   return CEED_ERROR_SUCCESS;
1116 }
1117 
1118 /**
1119   @brief Destroy a CeedOperator
1120 
1121   @param op  CeedOperator to destroy
1122 
1123   @return An error code: 0 - success, otherwise - failure
1124 
1125   @ref User
1126 **/
1127 int CeedOperatorDestroy(CeedOperator *op) {
1128   int ierr;
1129 
1130   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1131   if ((*op)->Destroy) {
1132     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1133   }
1134   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1135   // Free fields
1136   for (int i=0; i<(*op)->num_fields; i++)
1137     if ((*op)->input_fields[i]) {
1138       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1139         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1140         CeedChk(ierr);
1141       }
1142       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1143         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1144       }
1145       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1146           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1147         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1148       }
1149       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1150       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1151     }
1152   for (int i=0; i<(*op)->num_fields; i++)
1153     if ((*op)->output_fields[i]) {
1154       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1155       CeedChk(ierr);
1156       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1157         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1158       }
1159       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1160           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1161         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1162       }
1163       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1164       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1165     }
1166   // Destroy sub_operators
1167   for (int i=0; i<(*op)->num_suboperators; i++)
1168     if ((*op)->sub_operators[i]) {
1169       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1170     }
1171   if ((*op)->qf)
1172     // LCOV_EXCL_START
1173     (*op)->qf->operators_set--;
1174   // LCOV_EXCL_STOP
1175   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1176   if ((*op)->dqf && (*op)->dqf != CEED_QFUNCTION_NONE)
1177     // LCOV_EXCL_START
1178     (*op)->dqf->operators_set--;
1179   // LCOV_EXCL_STOP
1180   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1181   if ((*op)->dqfT && (*op)->dqfT != CEED_QFUNCTION_NONE)
1182     // LCOV_EXCL_START
1183     (*op)->dqfT->operators_set--;
1184   // LCOV_EXCL_STOP
1185   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1186 
1187   // Destroy fallback
1188   if ((*op)->op_fallback) {
1189     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1190     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1191     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1192     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1193   }
1194 
1195   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1196   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1197   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1198   ierr = CeedFree(op); CeedChk(ierr);
1199   return CEED_ERROR_SUCCESS;
1200 }
1201 
1202 /// @}
1203