xref: /libCEED/interface/ceed-operator.c (revision 28567f8f11ac5e58fa39329798911e806cc70c57)
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 
579 /// ----------------------------------------------------------------------------
580 /// CeedOperator Public API
581 /// ----------------------------------------------------------------------------
582 /// @addtogroup CeedOperatorUser
583 /// @{
584 
585 /**
586   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
587            CeedElemRestriction can be associated with CeedQFunction fields with
588            \ref CeedOperatorSetField.
589 
590   @param ceed     A Ceed object where the CeedOperator will be created
591   @param qf       QFunction defining the action of the operator at quadrature points
592   @param dqf      QFunction defining the action of the Jacobian of @a qf (or
593                     @ref CEED_QFUNCTION_NONE)
594   @param dqfT     QFunction defining the action of the transpose of the Jacobian
595                     of @a qf (or @ref CEED_QFUNCTION_NONE)
596   @param[out] op  Address of the variable where the newly created
597                     CeedOperator will be stored
598 
599   @return An error code: 0 - success, otherwise - failure
600 
601   @ref User
602  */
603 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
604                        CeedQFunction dqfT, CeedOperator *op) {
605   int ierr;
606 
607   if (!ceed->OperatorCreate) {
608     Ceed delegate;
609     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
610 
611     if (!delegate)
612       // LCOV_EXCL_START
613       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
614                        "Backend does not support OperatorCreate");
615     // LCOV_EXCL_STOP
616 
617     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
618     return CEED_ERROR_SUCCESS;
619   }
620 
621   if (!qf || qf == CEED_QFUNCTION_NONE)
622     // LCOV_EXCL_START
623     return CeedError(ceed, CEED_ERROR_MINOR,
624                      "Operator must have a valid QFunction.");
625   // LCOV_EXCL_STOP
626   ierr = CeedCalloc(1, op); CeedChk(ierr);
627   (*op)->ceed = ceed;
628   ierr = CeedReference(ceed); CeedChk(ierr);
629   (*op)->ref_count = 1;
630   (*op)->qf = qf;
631   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
632   if (dqf && dqf != CEED_QFUNCTION_NONE) {
633     (*op)->dqf = dqf;
634     ierr = CeedQFunctionReference(dqf); CeedChk(ierr);
635   }
636   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
637     (*op)->dqfT = dqfT;
638     ierr = CeedQFunctionReference(dqfT); CeedChk(ierr);
639   }
640   ierr = CeedCalloc(16, &(*op)->input_fields); CeedChk(ierr);
641   ierr = CeedCalloc(16, &(*op)->output_fields); CeedChk(ierr);
642   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
643   return CEED_ERROR_SUCCESS;
644 }
645 
646 /**
647   @brief Create an operator that composes the action of several operators
648 
649   @param ceed     A Ceed object where the CeedOperator will be created
650   @param[out] op  Address of the variable where the newly created
651                     Composite CeedOperator will be stored
652 
653   @return An error code: 0 - success, otherwise - failure
654 
655   @ref User
656  */
657 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
658   int ierr;
659 
660   if (!ceed->CompositeOperatorCreate) {
661     Ceed delegate;
662     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
663 
664     if (delegate) {
665       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
666       return CEED_ERROR_SUCCESS;
667     }
668   }
669 
670   ierr = CeedCalloc(1, op); CeedChk(ierr);
671   (*op)->ceed = ceed;
672   ierr = CeedReference(ceed); CeedChk(ierr);
673   (*op)->composite = true;
674   ierr = CeedCalloc(16, &(*op)->sub_operators); CeedChk(ierr);
675 
676   if (ceed->CompositeOperatorCreate) {
677     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
678   }
679   return CEED_ERROR_SUCCESS;
680 }
681 
682 /**
683   @brief Copy the pointer to a CeedOperator. Both pointers should
684            be destroyed with `CeedOperatorDestroy()`;
685            Note: If `*op_copy` is non-NULL, then it is assumed that
686            `*op_copy` is a pointer to a CeedOperator. This
687            CeedOperator will be destroyed if `*op_copy` is the only
688            reference to this CeedOperator.
689 
690   @param op            CeedOperator to copy reference to
691   @param[out] op_copy  Variable to store copied reference
692 
693   @return An error code: 0 - success, otherwise - failure
694 
695   @ref User
696 **/
697 int CeedOperatorReferenceCopy(CeedOperator op, CeedOperator *op_copy) {
698   int ierr;
699 
700   ierr = CeedOperatorReference(op); CeedChk(ierr);
701   ierr = CeedOperatorDestroy(op_copy); CeedChk(ierr);
702   *op_copy = op;
703   return CEED_ERROR_SUCCESS;
704 }
705 
706 /**
707   @brief Provide a field to a CeedOperator for use by its CeedQFunction
708 
709   This function is used to specify both active and passive fields to a
710   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
711   fields can inputs or outputs (updated in-place when operator is applied).
712 
713   Active fields must be specified using this function, but their data (in a
714   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
715   input and at most one active output.
716 
717   @param op          CeedOperator on which to provide the field
718   @param field_name  Name of the field (to be matched with the name used by
719                        CeedQFunction)
720   @param r           CeedElemRestriction
721   @param b           CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
722                        if collocated with quadrature points
723   @param v           CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
724                        if field is active or @ref CEED_VECTOR_NONE if using
725                        @ref CEED_EVAL_WEIGHT in the QFunction
726 
727   @return An error code: 0 - success, otherwise - failure
728 
729   @ref User
730 **/
731 int CeedOperatorSetField(CeedOperator op, const char *field_name,
732                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
733   int ierr;
734   if (op->composite)
735     // LCOV_EXCL_START
736     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
737                      "Cannot add field to composite operator.");
738   // LCOV_EXCL_STOP
739   if (!r)
740     // LCOV_EXCL_START
741     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
742                      "ElemRestriction r for field \"%s\" must be non-NULL.",
743                      field_name);
744   // LCOV_EXCL_STOP
745   if (!b)
746     // LCOV_EXCL_START
747     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
748                      "Basis b for field \"%s\" must be non-NULL.",
749                      field_name);
750   // LCOV_EXCL_STOP
751   if (!v)
752     // LCOV_EXCL_START
753     return CeedError(op->ceed, CEED_ERROR_INCOMPATIBLE,
754                      "Vector v for field \"%s\" must be non-NULL.",
755                      field_name);
756   // LCOV_EXCL_STOP
757 
758   CeedInt num_elem;
759   ierr = CeedElemRestrictionGetNumElements(r, &num_elem); CeedChk(ierr);
760   if (r != CEED_ELEMRESTRICTION_NONE && op->has_restriction &&
761       op->num_elem != num_elem)
762     // LCOV_EXCL_START
763     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
764                      "ElemRestriction with %d elements incompatible with prior "
765                      "%d elements", num_elem, op->num_elem);
766   // LCOV_EXCL_STOP
767 
768   CeedInt num_qpts = 0;
769   if (b != CEED_BASIS_COLLOCATED) {
770     ierr = CeedBasisGetNumQuadraturePoints(b, &num_qpts); CeedChk(ierr);
771     if (op->num_qpts && op->num_qpts != num_qpts)
772       // LCOV_EXCL_START
773       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
774                        "Basis with %d quadrature points "
775                        "incompatible with prior %d points", num_qpts,
776                        op->num_qpts);
777     // LCOV_EXCL_STOP
778   }
779   CeedQFunctionField qf_field;
780   CeedOperatorField *op_field;
781   for (CeedInt i=0; i<op->qf->num_input_fields; i++) {
782     if (!strcmp(field_name, (*op->qf->input_fields[i]).field_name)) {
783       qf_field = op->qf->input_fields[i];
784       op_field = &op->input_fields[i];
785       goto found;
786     }
787   }
788   for (CeedInt i=0; i<op->qf->num_output_fields; i++) {
789     if (!strcmp(field_name, (*op->qf->output_fields[i]).field_name)) {
790       qf_field = op->qf->output_fields[i];
791       op_field = &op->output_fields[i];
792       goto found;
793     }
794   }
795   // LCOV_EXCL_START
796   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
797                    "QFunction has no knowledge of field '%s'",
798                    field_name);
799   // LCOV_EXCL_STOP
800 found:
801   ierr = CeedOperatorCheckField(op->ceed, qf_field, r, b); CeedChk(ierr);
802   ierr = CeedCalloc(1, op_field); CeedChk(ierr);
803 
804   (*op_field)->vec = v;
805   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
806     ierr = CeedVectorReference(v); CeedChk(ierr);
807   }
808 
809   (*op_field)->elem_restr = r;
810   ierr = CeedElemRestrictionReference(r); CeedChk(ierr);
811   if (r != CEED_ELEMRESTRICTION_NONE) {
812     op->num_elem = num_elem;
813     op->has_restriction = true; // Restriction set, but num_elem may be 0
814   }
815 
816   (*op_field)->basis = b;
817   if (b != CEED_BASIS_COLLOCATED) {
818     if (!op->num_qpts) {
819       ierr = CeedOperatorSetNumQuadraturePoints(op, num_qpts); CeedChk(ierr);
820     }
821     ierr = CeedBasisReference(b); CeedChk(ierr);
822   }
823 
824   op->num_fields += 1;
825   size_t len = strlen(field_name);
826   char *tmp;
827   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
828   memcpy(tmp, field_name, len+1);
829   (*op_field)->field_name = tmp;
830   return CEED_ERROR_SUCCESS;
831 }
832 
833 /**
834   @brief Get the CeedOperatorFields of a CeedOperator
835 
836   @param op                  CeedOperator
837   @param[out] input_fields   Variable to store input_fields
838   @param[out] output_fields  Variable to store output_fields
839 
840   @return An error code: 0 - success, otherwise - failure
841 
842   @ref Backend
843 **/
844 int CeedOperatorGetFields(CeedOperator op, CeedInt *num_input_fields,
845                           CeedOperatorField **input_fields,
846                           CeedInt *num_output_fields,
847                           CeedOperatorField **output_fields) {
848   if (op->composite)
849     // LCOV_EXCL_START
850     return CeedError(op->ceed, CEED_ERROR_MINOR,
851                      "Not defined for composite operator");
852   // LCOV_EXCL_STOP
853 
854   if (num_input_fields) *num_input_fields = op->qf->num_input_fields;
855   if (input_fields) *input_fields = op->input_fields;
856   if (num_output_fields) *num_output_fields = op->qf->num_output_fields;
857   if (output_fields) *output_fields = op->output_fields;
858   return CEED_ERROR_SUCCESS;
859 }
860 
861 /**
862   @brief Get the name of a CeedOperatorField
863 
864   @param op_field         CeedOperatorField
865   @param[out] field_name  Variable to store the field name
866 
867   @return An error code: 0 - success, otherwise - failure
868 
869   @ref Backend
870 **/
871 int CeedOperatorFieldGetName(CeedOperatorField op_field, char **field_name) {
872   *field_name = (char *)op_field->field_name;
873   return CEED_ERROR_SUCCESS;
874 }
875 
876 /**
877   @brief Get the CeedElemRestriction of a CeedOperatorField
878 
879   @param op_field   CeedOperatorField
880   @param[out] rstr  Variable to store CeedElemRestriction
881 
882   @return An error code: 0 - success, otherwise - failure
883 
884   @ref Backend
885 **/
886 int CeedOperatorFieldGetElemRestriction(CeedOperatorField op_field,
887                                         CeedElemRestriction *rstr) {
888   *rstr = op_field->elem_restr;
889   return CEED_ERROR_SUCCESS;
890 }
891 
892 /**
893   @brief Get the CeedBasis of a CeedOperatorField
894 
895   @param op_field    CeedOperatorField
896   @param[out] basis  Variable to store CeedBasis
897 
898   @return An error code: 0 - success, otherwise - failure
899 
900   @ref Backend
901 **/
902 int CeedOperatorFieldGetBasis(CeedOperatorField op_field, CeedBasis *basis) {
903   *basis = op_field->basis;
904   return CEED_ERROR_SUCCESS;
905 }
906 
907 /**
908   @brief Get the CeedVector of a CeedOperatorField
909 
910   @param op_field  CeedOperatorField
911   @param[out] vec  Variable to store CeedVector
912 
913   @return An error code: 0 - success, otherwise - failure
914 
915   @ref Backend
916 **/
917 int CeedOperatorFieldGetVector(CeedOperatorField op_field, CeedVector *vec) {
918   *vec = op_field->vec;
919   return CEED_ERROR_SUCCESS;
920 }
921 
922 /**
923   @brief Add a sub-operator to a composite CeedOperator
924 
925   @param[out] composite_op  Composite CeedOperator
926   @param      sub_op        Sub-operator CeedOperator
927 
928   @return An error code: 0 - success, otherwise - failure
929 
930   @ref User
931  */
932 int CeedCompositeOperatorAddSub(CeedOperator composite_op,
933                                 CeedOperator sub_op) {
934   int ierr;
935 
936   if (!composite_op->composite)
937     // LCOV_EXCL_START
938     return CeedError(composite_op->ceed, CEED_ERROR_MINOR,
939                      "CeedOperator is not a composite operator");
940   // LCOV_EXCL_STOP
941 
942   if (composite_op->num_suboperators == CEED_COMPOSITE_MAX)
943     // LCOV_EXCL_START
944     return CeedError(composite_op->ceed, CEED_ERROR_UNSUPPORTED,
945                      "Cannot add additional sub_operators");
946   // LCOV_EXCL_STOP
947 
948   composite_op->sub_operators[composite_op->num_suboperators] = sub_op;
949   ierr = CeedOperatorReference(sub_op); CeedChk(ierr);
950   composite_op->num_suboperators++;
951   return CEED_ERROR_SUCCESS;
952 }
953 
954 /**
955   @brief Set the number of quadrature points associated with a CeedOperator.
956            This should be used when creating a CeedOperator where every
957            field has a collocated basis. This function cannot be used for
958            composite CeedOperators.
959 
960   @param op        CeedOperator
961   @param num_qpts  Number of quadrature points to set
962 
963   @return An error code: 0 - success, otherwise - failure
964 
965   @ref Backend
966 **/
967 
968 int CeedOperatorSetNumQuadraturePoints(CeedOperator op, CeedInt num_qpts) {
969   if (op->composite)
970     // LCOV_EXCL_START
971     return CeedError(op->ceed, CEED_ERROR_MINOR,
972                      "Not defined for composite operator");
973   // LCOV_EXCL_STOP
974   if (op->num_qpts)
975     // LCOV_EXCL_START
976     return CeedError(op->ceed, CEED_ERROR_MINOR,
977                      "Number of quadrature points already defined");
978   // LCOV_EXCL_STOP
979 
980   op->num_qpts = num_qpts;
981   return CEED_ERROR_SUCCESS;
982 }
983 
984 /**
985   @brief View a CeedOperator
986 
987   @param[in] op      CeedOperator to view
988   @param[in] stream  Stream to write; typically stdout/stderr or a file
989 
990   @return Error code: 0 - success, otherwise - failure
991 
992   @ref User
993 **/
994 int CeedOperatorView(CeedOperator op, FILE *stream) {
995   int ierr;
996 
997   if (op->composite) {
998     fprintf(stream, "Composite CeedOperator\n");
999 
1000     for (CeedInt i=0; i<op->num_suboperators; i++) {
1001       fprintf(stream, "  SubOperator [%d]:\n", i);
1002       ierr = CeedOperatorSingleView(op->sub_operators[i], 1, stream);
1003       CeedChk(ierr);
1004     }
1005   } else {
1006     fprintf(stream, "CeedOperator\n");
1007     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1008   }
1009   return CEED_ERROR_SUCCESS;
1010 }
1011 
1012 /**
1013   @brief Apply CeedOperator to a vector
1014 
1015   This computes the action of the operator on the specified (active) input,
1016   yielding its (active) output.  All inputs and outputs must be specified using
1017   CeedOperatorSetField().
1018 
1019   @param op        CeedOperator to apply
1020   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1021                      there are no active inputs
1022   @param[out] out  CeedVector to store result of applying operator (must be
1023                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1024                      active outputs
1025   @param request   Address of CeedRequest for non-blocking completion, else
1026                      @ref CEED_REQUEST_IMMEDIATE
1027 
1028   @return An error code: 0 - success, otherwise - failure
1029 
1030   @ref User
1031 **/
1032 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1033                       CeedRequest *request) {
1034   int ierr;
1035   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1036 
1037   if (op->num_elem)  {
1038     // Standard Operator
1039     if (op->Apply) {
1040       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1041     } else {
1042       // Zero all output vectors
1043       CeedQFunction qf = op->qf;
1044       for (CeedInt i=0; i<qf->num_output_fields; i++) {
1045         CeedVector vec = op->output_fields[i]->vec;
1046         if (vec == CEED_VECTOR_ACTIVE)
1047           vec = out;
1048         if (vec != CEED_VECTOR_NONE) {
1049           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1050         }
1051       }
1052       // Apply
1053       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1054     }
1055   } else if (op->composite) {
1056     // Composite Operator
1057     if (op->ApplyComposite) {
1058       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1059     } else {
1060       CeedInt num_suboperators;
1061       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1062       CeedOperator *sub_operators;
1063       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1064 
1065       // Zero all output vectors
1066       if (out != CEED_VECTOR_NONE) {
1067         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1068       }
1069       for (CeedInt i=0; i<num_suboperators; i++) {
1070         for (CeedInt j=0; j<sub_operators[i]->qf->num_output_fields; j++) {
1071           CeedVector vec = sub_operators[i]->output_fields[j]->vec;
1072           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1073             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1074           }
1075         }
1076       }
1077       // Apply
1078       for (CeedInt i=0; i<op->num_suboperators; i++) {
1079         ierr = CeedOperatorApplyAdd(op->sub_operators[i], in, out, request);
1080         CeedChk(ierr);
1081       }
1082     }
1083   }
1084   return CEED_ERROR_SUCCESS;
1085 }
1086 
1087 /**
1088   @brief Apply CeedOperator to a vector and add result to output vector
1089 
1090   This computes the action of the operator on the specified (active) input,
1091   yielding its (active) output.  All inputs and outputs must be specified using
1092   CeedOperatorSetField().
1093 
1094   @param op        CeedOperator to apply
1095   @param[in] in    CeedVector containing input state or NULL if there are no
1096                      active inputs
1097   @param[out] out  CeedVector to sum in result of applying operator (must be
1098                      distinct from @a in) or NULL if there are no active outputs
1099   @param request   Address of CeedRequest for non-blocking completion, else
1100                      @ref CEED_REQUEST_IMMEDIATE
1101 
1102   @return An error code: 0 - success, otherwise - failure
1103 
1104   @ref User
1105 **/
1106 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1107                          CeedRequest *request) {
1108   int ierr;
1109   ierr = CeedOperatorCheckReady(op); CeedChk(ierr);
1110 
1111   if (op->num_elem)  {
1112     // Standard Operator
1113     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1114   } else if (op->composite) {
1115     // Composite Operator
1116     if (op->ApplyAddComposite) {
1117       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1118     } else {
1119       CeedInt num_suboperators;
1120       ierr = CeedOperatorGetNumSub(op, &num_suboperators); CeedChk(ierr);
1121       CeedOperator *sub_operators;
1122       ierr = CeedOperatorGetSubList(op, &sub_operators); CeedChk(ierr);
1123 
1124       for (CeedInt i=0; i<num_suboperators; i++) {
1125         ierr = CeedOperatorApplyAdd(sub_operators[i], in, out, request);
1126         CeedChk(ierr);
1127       }
1128     }
1129   }
1130   return CEED_ERROR_SUCCESS;
1131 }
1132 
1133 /**
1134   @brief Destroy a CeedOperator
1135 
1136   @param op  CeedOperator to destroy
1137 
1138   @return An error code: 0 - success, otherwise - failure
1139 
1140   @ref User
1141 **/
1142 int CeedOperatorDestroy(CeedOperator *op) {
1143   int ierr;
1144 
1145   if (!*op || --(*op)->ref_count > 0) return CEED_ERROR_SUCCESS;
1146   if ((*op)->Destroy) {
1147     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1148   }
1149   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1150   // Free fields
1151   for (int i=0; i<(*op)->num_fields; i++)
1152     if ((*op)->input_fields[i]) {
1153       if ((*op)->input_fields[i]->elem_restr != CEED_ELEMRESTRICTION_NONE) {
1154         ierr = CeedElemRestrictionDestroy(&(*op)->input_fields[i]->elem_restr);
1155         CeedChk(ierr);
1156       }
1157       if ((*op)->input_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1158         ierr = CeedBasisDestroy(&(*op)->input_fields[i]->basis); CeedChk(ierr);
1159       }
1160       if ((*op)->input_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1161           (*op)->input_fields[i]->vec != CEED_VECTOR_NONE ) {
1162         ierr = CeedVectorDestroy(&(*op)->input_fields[i]->vec); CeedChk(ierr);
1163       }
1164       ierr = CeedFree(&(*op)->input_fields[i]->field_name); CeedChk(ierr);
1165       ierr = CeedFree(&(*op)->input_fields[i]); CeedChk(ierr);
1166     }
1167   for (int i=0; i<(*op)->num_fields; i++)
1168     if ((*op)->output_fields[i]) {
1169       ierr = CeedElemRestrictionDestroy(&(*op)->output_fields[i]->elem_restr);
1170       CeedChk(ierr);
1171       if ((*op)->output_fields[i]->basis != CEED_BASIS_COLLOCATED) {
1172         ierr = CeedBasisDestroy(&(*op)->output_fields[i]->basis); CeedChk(ierr);
1173       }
1174       if ((*op)->output_fields[i]->vec != CEED_VECTOR_ACTIVE &&
1175           (*op)->output_fields[i]->vec != CEED_VECTOR_NONE ) {
1176         ierr = CeedVectorDestroy(&(*op)->output_fields[i]->vec); CeedChk(ierr);
1177       }
1178       ierr = CeedFree(&(*op)->output_fields[i]->field_name); CeedChk(ierr);
1179       ierr = CeedFree(&(*op)->output_fields[i]); CeedChk(ierr);
1180     }
1181   // Destroy sub_operators
1182   for (int i=0; i<(*op)->num_suboperators; i++)
1183     if ((*op)->sub_operators[i]) {
1184       ierr = CeedOperatorDestroy(&(*op)->sub_operators[i]); CeedChk(ierr);
1185     }
1186   if ((*op)->qf)
1187     // LCOV_EXCL_START
1188     (*op)->qf->operators_set--;
1189   // LCOV_EXCL_STOP
1190   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1191   if ((*op)->dqf && (*op)->dqf != CEED_QFUNCTION_NONE)
1192     // LCOV_EXCL_START
1193     (*op)->dqf->operators_set--;
1194   // LCOV_EXCL_STOP
1195   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1196   if ((*op)->dqfT && (*op)->dqfT != CEED_QFUNCTION_NONE)
1197     // LCOV_EXCL_START
1198     (*op)->dqfT->operators_set--;
1199   // LCOV_EXCL_STOP
1200   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1201 
1202   // Destroy fallback
1203   if ((*op)->op_fallback) {
1204     ierr = (*op)->qf_fallback->Destroy((*op)->qf_fallback); CeedChk(ierr);
1205     ierr = CeedFree(&(*op)->qf_fallback); CeedChk(ierr);
1206     ierr = (*op)->op_fallback->Destroy((*op)->op_fallback); CeedChk(ierr);
1207     ierr = CeedFree(&(*op)->op_fallback); CeedChk(ierr);
1208   }
1209 
1210   ierr = CeedFree(&(*op)->input_fields); CeedChk(ierr);
1211   ierr = CeedFree(&(*op)->output_fields); CeedChk(ierr);
1212   ierr = CeedFree(&(*op)->sub_operators); CeedChk(ierr);
1213   ierr = CeedFree(op); CeedChk(ierr);
1214   return CEED_ERROR_SUCCESS;
1215 }
1216 
1217 /// @}
1218