xref: /libCEED/interface/ceed-operator.c (revision b0812a454286a178b5527bf1d1f1a428af10126c)
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-impl.h>
18 #include <ceed-backend.h>
19 #include <string.h>
20 #include <math.h>
21 
22 /// @file
23 /// Implementation of public CeedOperator interfaces
24 ///
25 /// @addtogroup CeedOperator
26 ///   @{
27 
28 /**
29   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
30            CeedElemRestriction can be associated with CeedQFunction fields with
31            \ref CeedOperatorSetField.
32 
33   @param ceed    A Ceed object where the CeedOperator will be created
34   @param qf      QFunction defining the action of the operator at quadrature points
35   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
36                    CEED_QFUNCTION_NONE)
37   @param dqfT    QFunction defining the action of the transpose of the Jacobian
38                    of @a qf (or CEED_QFUNCTION_NONE)
39   @param[out] op Address of the variable where the newly created
40                      CeedOperator will be stored
41 
42   @return An error code: 0 - success, otherwise - failure
43 
44   @ref Basic
45  */
46 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
47                        CeedQFunction dqfT, CeedOperator *op) {
48   int ierr;
49 
50   if (!ceed->OperatorCreate) {
51     Ceed delegate;
52     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
53 
54     if (!delegate)
55       // LCOV_EXCL_START
56       return CeedError(ceed, 1, "Backend does not support OperatorCreate");
57     // LCOV_EXCL_STOP
58 
59     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
60     return 0;
61   }
62 
63   ierr = CeedCalloc(1, op); CeedChk(ierr);
64   (*op)->ceed = ceed;
65   ceed->refcount++;
66   (*op)->refcount = 1;
67   if (qf == CEED_QFUNCTION_NONE)
68     // LCOV_EXCL_START
69     return CeedError(ceed, 1, "Operator must have a valid QFunction.");
70   // LCOV_EXCL_STOP
71   (*op)->qf = qf;
72   qf->refcount++;
73   if (dqf && dqf != CEED_QFUNCTION_NONE) {
74     (*op)->dqf = dqf;
75     dqf->refcount++;
76   }
77   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
78     (*op)->dqfT = dqfT;
79     dqfT->refcount++;
80   }
81   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
82   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
83   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
84   return 0;
85 }
86 
87 /**
88   @brief Create an operator that composes the action of several operators
89 
90   @param ceed    A Ceed object where the CeedOperator will be created
91   @param[out] op Address of the variable where the newly created
92                      Composite CeedOperator will be stored
93 
94   @return An error code: 0 - success, otherwise - failure
95 
96   @ref Basic
97  */
98 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
99   int ierr;
100 
101   if (!ceed->CompositeOperatorCreate) {
102     Ceed delegate;
103     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
104 
105     if (delegate) {
106       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
107       return 0;
108     }
109   }
110 
111   ierr = CeedCalloc(1, op); CeedChk(ierr);
112   (*op)->ceed = ceed;
113   ceed->refcount++;
114   (*op)->composite = true;
115   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
116 
117   if (ceed->CompositeOperatorCreate) {
118     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
119   }
120   return 0;
121 }
122 
123 /**
124   @brief Provide a field to a CeedOperator for use by its CeedQFunction
125 
126   This function is used to specify both active and passive fields to a
127   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
128   fields can inputs or outputs (updated in-place when operator is applied).
129 
130   Active fields must be specified using this function, but their data (in a
131   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
132   input and at most one active output.
133 
134   @param op         CeedOperator on which to provide the field
135   @param fieldname  Name of the field (to be matched with the name used by
136                       CeedQFunction)
137   @param r          CeedElemRestriction
138   @param b          CeedBasis in which the field resides or CEED_BASIS_COLLOCATED
139                       if collocated with quadrature points
140   @param v          CeedVector to be used by CeedOperator or CEED_VECTOR_ACTIVE
141                       if field is active or CEED_VECTOR_NONE if using
142                       CEED_EVAL_WEIGHT in the QFunction
143 
144   @return An error code: 0 - success, otherwise - failure
145 
146   @ref Basic
147 **/
148 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
149                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
150   int ierr;
151   if (op->composite)
152     // LCOV_EXCL_START
153     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
154   // LCOV_EXCL_STOP
155   if (!r)
156     // LCOV_EXCL_START
157     return CeedError(op->ceed, 1,
158                      "ElemRestriction r for field \"%s\" must be non-NULL.",
159                      fieldname);
160   // LCOV_EXCL_STOP
161   if (!b)
162     // LCOV_EXCL_START
163     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
164                      fieldname);
165   // LCOV_EXCL_STOP
166   if (!v)
167     // LCOV_EXCL_START
168     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
169                      fieldname);
170   // LCOV_EXCL_STOP
171 
172   CeedInt numelements;
173   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
174   if (op->hasrestriction && op->numelements != numelements)
175     // LCOV_EXCL_START
176     return CeedError(op->ceed, 1,
177                      "ElemRestriction with %d elements incompatible with prior "
178                      "%d elements", numelements, op->numelements);
179   // LCOV_EXCL_STOP
180   op->numelements = numelements;
181   op->hasrestriction = true; // Restriction set, but numelements may be 0
182 
183   if (b != CEED_BASIS_COLLOCATED) {
184     CeedInt numqpoints;
185     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
186     if (op->numqpoints && op->numqpoints != numqpoints)
187       // LCOV_EXCL_START
188       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
189                        "incompatible with prior %d points", numqpoints,
190                        op->numqpoints);
191     // LCOV_EXCL_STOP
192     op->numqpoints = numqpoints;
193   }
194   CeedOperatorField *ofield;
195   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
196     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
197       ofield = &op->inputfields[i];
198       goto found;
199     }
200   }
201   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
202     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
203       ofield = &op->outputfields[i];
204       goto found;
205     }
206   }
207   // LCOV_EXCL_START
208   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
209                    fieldname);
210   // LCOV_EXCL_STOP
211 found:
212   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
213   (*ofield)->Erestrict = r;
214   r->refcount += 1;
215   (*ofield)->basis = b;
216   if (b != CEED_BASIS_COLLOCATED)
217     b->refcount += 1;
218   (*ofield)->vec = v;
219   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
220     v->refcount += 1;
221   op->nfields += 1;
222   return 0;
223 }
224 
225 /**
226   @brief Add a sub-operator to a composite CeedOperator
227 
228   @param[out] compositeop Composite CeedOperator
229   @param      subop       Sub-operator CeedOperator
230 
231   @return An error code: 0 - success, otherwise - failure
232 
233   @ref Basic
234  */
235 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
236   if (!compositeop->composite)
237     // LCOV_EXCL_START
238     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
239                      "operator");
240   // LCOV_EXCL_STOP
241 
242   if (compositeop->numsub == CEED_COMPOSITE_MAX)
243     // LCOV_EXCL_START
244     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
245   // LCOV_EXCL_STOP
246 
247   compositeop->suboperators[compositeop->numsub] = subop;
248   subop->refcount++;
249   compositeop->numsub++;
250   return 0;
251 }
252 
253 /**
254   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
255            CeedOperator functionality
256 
257   @param op           CeedOperator to create fallback for
258 
259   @return An error code: 0 - success, otherwise - failure
260 
261   @ref Developer
262 **/
263 int CeedOperatorCreateFallback(CeedOperator op) {
264   int ierr;
265 
266   // Fallback Ceed
267   const char *resource, *fallbackresource;
268   ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
269   ierr = CeedGetOperatorFallbackResource(op->ceed, &fallbackresource);
270   CeedChk(ierr);
271   if (!strcmp(resource, fallbackresource))
272     // LCOV_EXCL_START
273     return CeedError(op->ceed, 1, "Backend %s cannot create an operator"
274                      "fallback to resource %s", resource, fallbackresource);
275   // LCOV_EXCL_STOP
276 
277   // Fallback Ceed
278   Ceed ceedref;
279   if (!op->ceed->opfallbackceed) {
280     ierr = CeedInit(fallbackresource, &ceedref); CeedChk(ierr);
281     ceedref->opfallbackparent = op->ceed;
282     op->ceed->opfallbackceed = ceedref;
283   }
284   ceedref = op->ceed->opfallbackceed;
285 
286   // Clone Op
287   CeedOperator opref;
288   ierr = CeedCalloc(1, &opref); CeedChk(ierr);
289   memcpy(opref, op, sizeof(*opref)); CeedChk(ierr);
290   opref->data = NULL;
291   opref->setupdone = 0;
292   opref->ceed = ceedref;
293   ierr = ceedref->OperatorCreate(opref); CeedChk(ierr);
294   op->opfallback = opref;
295 
296   // Clone QF
297   CeedQFunction qfref;
298   ierr = CeedCalloc(1, &qfref); CeedChk(ierr);
299   memcpy(qfref, (op->qf), sizeof(*qfref)); CeedChk(ierr);
300   qfref->data = NULL;
301   qfref->ceed = ceedref;
302   ierr = ceedref->QFunctionCreate(qfref); CeedChk(ierr);
303   opref->qf = qfref;
304   op->qffallback = qfref;
305 
306   return 0;
307 }
308 
309 /**
310   @brief Check if a CeedOperator is ready to be used.
311 
312   @param[in] ceed Ceed object for error handling
313   @param[in] op   CeedOperator to check
314 
315   @return An error code: 0 - success, otherwise - failure
316 
317   @ref Basic
318 **/
319 static int CeedOperatorCheckReady(Ceed ceed, CeedOperator op) {
320   CeedQFunction qf = op->qf;
321 
322   if (op->composite) {
323     if (!op->numsub)
324       // LCOV_EXCL_START
325       return CeedError(ceed, 1, "No suboperators set");
326     // LCOV_EXCL_STOP
327   } else {
328     if (op->nfields == 0)
329       // LCOV_EXCL_START
330       return CeedError(ceed, 1, "No operator fields set");
331     // LCOV_EXCL_STOP
332     if (op->nfields < qf->numinputfields + qf->numoutputfields)
333       // LCOV_EXCL_START
334       return CeedError(ceed, 1, "Not all operator fields set");
335     // LCOV_EXCL_STOP
336     if (!op->hasrestriction)
337       // LCOV_EXCL_START
338       return CeedError(ceed, 1,"At least one restriction required");
339     // LCOV_EXCL_STOP
340     if (op->numqpoints == 0)
341       // LCOV_EXCL_START
342       return CeedError(ceed, 1,"At least one non-collocated basis required");
343     // LCOV_EXCL_STOP
344   }
345 
346   return 0;
347 }
348 
349 /**
350   @brief Assemble a linear CeedQFunction associated with a CeedOperator
351 
352   This returns a CeedVector containing a matrix at each quadrature point
353     providing the action of the CeedQFunction associated with the CeedOperator.
354     The vector 'assembled' is of shape
355       [num_elements, num_input_fields, num_output_fields, num_quad_points]
356     and contains column-major matrices representing the action of the
357     CeedQFunction for a corresponding quadrature point on an element. Inputs and
358     outputs are in the order provided by the user when adding CeedOperator fields.
359     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
360     'v', provided in that order, would result in an assembled QFunction that
361     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
362     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
363 
364   @param op             CeedOperator to assemble CeedQFunction
365   @param[out] assembled CeedVector to store assembled CeedQFunction at
366                           quadrature points
367   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
368                           CeedQFunction
369   @param request        Address of CeedRequest for non-blocking completion, else
370                           CEED_REQUEST_IMMEDIATE
371 
372   @return An error code: 0 - success, otherwise - failure
373 
374   @ref Basic
375 **/
376 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
377                                         CeedElemRestriction *rstr,
378                                         CeedRequest *request) {
379   int ierr;
380   Ceed ceed = op->ceed;
381   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
382 
383   // Backend version
384   if (op->AssembleLinearQFunction) {
385     ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
386     CeedChk(ierr);
387   } else {
388     // Fallback to reference Ceed
389     if (!op->opfallback) {
390       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
391     }
392     // Assemble
393     ierr = op->opfallback->AssembleLinearQFunction(op->opfallback, assembled,
394            rstr, request); CeedChk(ierr);
395   }
396 
397   return 0;
398 }
399 
400 /**
401   @brief Assemble the diagonal of a square linear Operator
402 
403   This returns a CeedVector containing the diagonal of a linear CeedOperator.
404 
405   @param op             CeedOperator to assemble CeedQFunction
406   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
407   @param request        Address of CeedRequest for non-blocking completion, else
408                           CEED_REQUEST_IMMEDIATE
409 
410   @return An error code: 0 - success, otherwise - failure
411 
412   @ref Basic
413 **/
414 int CeedOperatorAssembleLinearDiagonal(CeedOperator op, CeedVector *assembled,
415                                        CeedRequest *request) {
416   int ierr;
417   Ceed ceed = op->ceed;
418   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
419 
420   // Use backend version, if available
421   if (op->AssembleLinearDiagonal) {
422     ierr = op->AssembleLinearDiagonal(op, assembled, request); CeedChk(ierr);
423   } else {
424     // Fallback to reference Ceed
425     if (!op->opfallback) {
426       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
427     }
428     // Assemble
429     ierr = op->opfallback->AssembleLinearDiagonal(op->opfallback, assembled,
430            request); CeedChk(ierr);
431   }
432 
433   return 0;
434 }
435 
436 /**
437   @brief Build a FDM based approximate inverse for each element for a
438            CeedOperator
439 
440   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
441     Method based approximate inverse. This function obtains the simultaneous
442     diagonalization for the 1D mass and Laplacian operators,
443       M = V^T V, K = V^T S V.
444     The assembled QFunction is used to modify the eigenvalues from simultaneous
445     diagonalization and obtain an approximate inverse of the form
446       V^T S^hat V. The CeedOperator must be linear and non-composite. The
447     associated CeedQFunction must therefore also be linear.
448 
449   @param op             CeedOperator to create element inverses
450   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
451                           for each element
452   @param request        Address of CeedRequest for non-blocking completion, else
453                           CEED_REQUEST_IMMEDIATE
454 
455   @return An error code: 0 - success, otherwise - failure
456 
457   @ref Advanced
458 **/
459 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
460                                         CeedRequest *request) {
461   int ierr;
462   Ceed ceed = op->ceed;
463   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
464 
465   // Use backend version, if available
466   if (op->CreateFDMElementInverse) {
467     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
468   } else {
469     // Fallback to reference Ceed
470     if (!op->opfallback) {
471       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
472     }
473     // Assemble
474     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
475            request); CeedChk(ierr);
476   }
477 
478   return 0;
479 }
480 
481 
482 /**
483   @brief Apply CeedOperator to a vector
484 
485   This computes the action of the operator on the specified (active) input,
486   yielding its (active) output.  All inputs and outputs must be specified using
487   CeedOperatorSetField().
488 
489   @param op        CeedOperator to apply
490   @param[in] in    CeedVector containing input state or CEED_VECTOR_NONE if
491                   there are no active inputs
492   @param[out] out  CeedVector to store result of applying operator (must be
493                      distinct from @a in) or CEED_VECTOR_NONE if there are no
494                      active outputs
495   @param request   Address of CeedRequest for non-blocking completion, else
496                      CEED_REQUEST_IMMEDIATE
497 
498   @return An error code: 0 - success, otherwise - failure
499 
500   @ref Basic
501 **/
502 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
503                       CeedRequest *request) {
504   int ierr;
505   Ceed ceed = op->ceed;
506   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
507 
508   if (op->numelements)  {
509     // Standard Operator
510     if (op->Apply) {
511       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
512     } else {
513       // Zero all output vectors
514       CeedQFunction qf = op->qf;
515       for (CeedInt i=0; i<qf->numoutputfields; i++) {
516         CeedVector vec = op->outputfields[i]->vec;
517         if (vec == CEED_VECTOR_ACTIVE)
518           vec = out;
519         if (vec != CEED_VECTOR_NONE) {
520           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
521         }
522       }
523       // Apply
524       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
525     }
526   } else if (op->composite) {
527     // Composite Operator
528     if (op->ApplyComposite) {
529       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
530     } else {
531       CeedInt numsub;
532       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
533       CeedOperator *suboperators;
534       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
535 
536       // Zero all output vectors
537       if (out != CEED_VECTOR_NONE) {
538         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
539       }
540       for (CeedInt i=0; i<numsub; i++) {
541         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
542           CeedVector vec = suboperators[i]->outputfields[j]->vec;
543           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
544             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
545           }
546         }
547       }
548       // Apply
549       for (CeedInt i=0; i<op->numsub; i++) {
550         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
551         CeedChk(ierr);
552       }
553     }
554   }
555 
556   return 0;
557 }
558 
559 /**
560   @brief Apply CeedOperator to a vector and add result to output vector
561 
562   This computes the action of the operator on the specified (active) input,
563   yielding its (active) output.  All inputs and outputs must be specified using
564   CeedOperatorSetField().
565 
566   @param op        CeedOperator to apply
567   @param[in] in    CeedVector containing input state or NULL if there are no
568                      active inputs
569   @param[out] out  CeedVector to sum in result of applying operator (must be
570                      distinct from @a in) or NULL if there are no active outputs
571   @param request   Address of CeedRequest for non-blocking completion, else
572                      CEED_REQUEST_IMMEDIATE
573 
574   @return An error code: 0 - success, otherwise - failure
575 
576   @ref Basic
577 **/
578 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
579                          CeedRequest *request) {
580   int ierr;
581   Ceed ceed = op->ceed;
582   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
583 
584   if (op->numelements)  {
585     // Standard Operator
586     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
587   } else if (op->composite) {
588     // Composite Operator
589     if (op->ApplyAddComposite) {
590       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
591     } else {
592       CeedInt numsub;
593       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
594       CeedOperator *suboperators;
595       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
596 
597       for (CeedInt i=0; i<numsub; i++) {
598         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
599         CeedChk(ierr);
600       }
601     }
602   }
603 
604   return 0;
605 }
606 
607 /**
608   @brief Get the Ceed associated with a CeedOperator
609 
610   @param op              CeedOperator
611   @param[out] ceed       Variable to store Ceed
612 
613   @return An error code: 0 - success, otherwise - failure
614 
615   @ref Advanced
616 **/
617 
618 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
619   *ceed = op->ceed;
620   return 0;
621 }
622 
623 /**
624   @brief Get the number of elements associated with a CeedOperator
625 
626   @param op              CeedOperator
627   @param[out] numelem    Variable to store number of elements
628 
629   @return An error code: 0 - success, otherwise - failure
630 
631   @ref Advanced
632 **/
633 
634 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
635   if (op->composite)
636     // LCOV_EXCL_START
637     return CeedError(op->ceed, 1, "Not defined for composite operator");
638   // LCOV_EXCL_STOP
639 
640   *numelem = op->numelements;
641   return 0;
642 }
643 
644 /**
645   @brief Get the number of quadrature points associated with a CeedOperator
646 
647   @param op              CeedOperator
648   @param[out] numqpts    Variable to store vector number of quadrature points
649 
650   @return An error code: 0 - success, otherwise - failure
651 
652   @ref Advanced
653 **/
654 
655 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
656   if (op->composite)
657     // LCOV_EXCL_START
658     return CeedError(op->ceed, 1, "Not defined for composite operator");
659   // LCOV_EXCL_STOP
660 
661   *numqpts = op->numqpoints;
662   return 0;
663 }
664 
665 /**
666   @brief Get the number of arguments associated with a CeedOperator
667 
668   @param op              CeedOperator
669   @param[out] numargs    Variable to store vector number of arguments
670 
671   @return An error code: 0 - success, otherwise - failure
672 
673   @ref Advanced
674 **/
675 
676 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
677   if (op->composite)
678     // LCOV_EXCL_START
679     return CeedError(op->ceed, 1, "Not defined for composite operators");
680   // LCOV_EXCL_STOP
681 
682   *numargs = op->nfields;
683   return 0;
684 }
685 
686 /**
687   @brief Get the setup status of a CeedOperator
688 
689   @param op             CeedOperator
690   @param[out] setupdone Variable to store setup status
691 
692   @return An error code: 0 - success, otherwise - failure
693 
694   @ref Advanced
695 **/
696 
697 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
698   *setupdone = op->setupdone;
699   return 0;
700 }
701 
702 /**
703   @brief Get the QFunction associated with a CeedOperator
704 
705   @param op              CeedOperator
706   @param[out] qf         Variable to store QFunction
707 
708   @return An error code: 0 - success, otherwise - failure
709 
710   @ref Advanced
711 **/
712 
713 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
714   if (op->composite)
715     // LCOV_EXCL_START
716     return CeedError(op->ceed, 1, "Not defined for composite operator");
717   // LCOV_EXCL_STOP
718 
719   *qf = op->qf;
720   return 0;
721 }
722 
723 /**
724   @brief Get the number of suboperators associated with a CeedOperator
725 
726   @param op              CeedOperator
727   @param[out] numsub     Variable to store number of suboperators
728 
729   @return An error code: 0 - success, otherwise - failure
730 
731   @ref Advanced
732 **/
733 
734 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
735   if (!op->composite)
736     // LCOV_EXCL_START
737     return CeedError(op->ceed, 1, "Not a composite operator");
738   // LCOV_EXCL_STOP
739 
740   *numsub = op->numsub;
741   return 0;
742 }
743 
744 /**
745   @brief Get the list of suboperators associated with a CeedOperator
746 
747   @param op                CeedOperator
748   @param[out] suboperators Variable to store list of suboperators
749 
750   @return An error code: 0 - success, otherwise - failure
751 
752   @ref Advanced
753 **/
754 
755 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
756   if (!op->composite)
757     // LCOV_EXCL_START
758     return CeedError(op->ceed, 1, "Not a composite operator");
759   // LCOV_EXCL_STOP
760 
761   *suboperators = op->suboperators;
762   return 0;
763 }
764 
765 /**
766   @brief Set the backend data of a CeedOperator
767 
768   @param[out] op         CeedOperator
769   @param data            Data to set
770 
771   @return An error code: 0 - success, otherwise - failure
772 
773   @ref Advanced
774 **/
775 
776 int CeedOperatorSetData(CeedOperator op, void **data) {
777   op->data = *data;
778   return 0;
779 }
780 
781 /**
782   @brief Get the backend data of a CeedOperator
783 
784   @param op              CeedOperator
785   @param[out] data       Variable to store data
786 
787   @return An error code: 0 - success, otherwise - failure
788 
789   @ref Advanced
790 **/
791 
792 int CeedOperatorGetData(CeedOperator op, void **data) {
793   *data = op->data;
794   return 0;
795 }
796 
797 /**
798   @brief Set the setup flag of a CeedOperator to True
799 
800   @param op              CeedOperator
801 
802   @return An error code: 0 - success, otherwise - failure
803 
804   @ref Advanced
805 **/
806 
807 int CeedOperatorSetSetupDone(CeedOperator op) {
808   op->setupdone = 1;
809   return 0;
810 }
811 
812 /**
813   @brief View a field of a CeedOperator
814 
815   @param[in] field       Operator field to view
816   @param[in] fieldnumber Number of field being viewed
817   @param[in] stream      Stream to view to, e.g., stdout
818 
819   @return An error code: 0 - success, otherwise - failure
820 
821   @ref Utility
822 **/
823 static int CeedOperatorFieldView(CeedOperatorField field,
824                                  CeedQFunctionField qffield,
825                                  CeedInt fieldnumber, bool sub, bool in,
826                                  FILE *stream) {
827   const char *pre = sub ? "  " : "";
828   const char *inout = in ? "Input" : "Output";
829 
830   fprintf(stream, "%s    %s Field [%d]:\n"
831           "%s      Name: \"%s\"\n",
832           pre, inout, fieldnumber, pre, qffield->fieldname);
833 
834   if (field->basis == CEED_BASIS_COLLOCATED)
835     fprintf(stream, "%s      Collocated basis\n", pre);
836 
837   if (field->vec == CEED_VECTOR_ACTIVE)
838     fprintf(stream, "%s      Active vector\n", pre);
839   else if (field->vec == CEED_VECTOR_NONE)
840     fprintf(stream, "%s      No vector\n", pre);
841 
842   return 0;
843 }
844 
845 /**
846   @brief View a single CeedOperator
847 
848   @param[in] op     CeedOperator to view
849   @param[in] sub    Boolean flag for sub-operator
850   @param[in] stream Stream to write; typically stdout/stderr or a file
851 
852   @return Error code: 0 - success, otherwise - failure
853 
854   @ref Utility
855 **/
856 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
857   int ierr;
858   const char *pre = sub ? "  " : "";
859 
860   CeedInt totalfields;
861   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
862 
863   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
864 
865   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
866           op->qf->numinputfields>1 ? "s" : "");
867   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
868     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
869                                  i, sub, 1, stream); CeedChk(ierr);
870   }
871 
872   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
873           op->qf->numoutputfields>1 ? "s" : "");
874   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
875     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->inputfields[i],
876                                  i, sub, 0, stream); CeedChk(ierr);
877   }
878 
879   return 0;
880 }
881 
882 /**
883   @brief View a CeedOperator
884 
885   @param[in] op     CeedOperator to view
886   @param[in] stream Stream to write; typically stdout/stderr or a file
887 
888   @return Error code: 0 - success, otherwise - failure
889 
890   @ref Utility
891 **/
892 int CeedOperatorView(CeedOperator op, FILE *stream) {
893   int ierr;
894 
895   if (op->composite) {
896     fprintf(stream, "Composite CeedOperator\n");
897 
898     for (CeedInt i=0; i<op->numsub; i++) {
899       fprintf(stream, "  SubOperator [%d]:\n", i);
900       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
901       CeedChk(ierr);
902     }
903   } else {
904     fprintf(stream, "CeedOperator\n");
905     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
906   }
907 
908   return 0;
909 }
910 
911 /**
912   @brief Get the CeedOperatorFields of a CeedOperator
913 
914   @param op                 CeedOperator
915   @param[out] inputfields   Variable to store inputfields
916   @param[out] outputfields  Variable to store outputfields
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref Advanced
921 **/
922 
923 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
924                           CeedOperatorField **outputfields) {
925   if (op->composite)
926     // LCOV_EXCL_START
927     return CeedError(op->ceed, 1, "Not defined for composite operator");
928   // LCOV_EXCL_STOP
929 
930   if (inputfields) *inputfields = op->inputfields;
931   if (outputfields) *outputfields = op->outputfields;
932   return 0;
933 }
934 
935 /**
936   @brief Get the CeedElemRestriction of a CeedOperatorField
937 
938   @param opfield         CeedOperatorField
939   @param[out] rstr       Variable to store CeedElemRestriction
940 
941   @return An error code: 0 - success, otherwise - failure
942 
943   @ref Advanced
944 **/
945 
946 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
947                                         CeedElemRestriction *rstr) {
948   *rstr = opfield->Erestrict;
949   return 0;
950 }
951 
952 /**
953   @brief Get the CeedBasis of a CeedOperatorField
954 
955   @param opfield         CeedOperatorField
956   @param[out] basis      Variable to store CeedBasis
957 
958   @return An error code: 0 - success, otherwise - failure
959 
960   @ref Advanced
961 **/
962 
963 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
964   *basis = opfield->basis;
965   return 0;
966 }
967 
968 /**
969   @brief Get the CeedVector of a CeedOperatorField
970 
971   @param opfield         CeedOperatorField
972   @param[out] vec        Variable to store CeedVector
973 
974   @return An error code: 0 - success, otherwise - failure
975 
976   @ref Advanced
977 **/
978 
979 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
980   *vec = opfield->vec;
981   return 0;
982 }
983 
984 /**
985   @brief Destroy a CeedOperator
986 
987   @param op CeedOperator to destroy
988 
989   @return An error code: 0 - success, otherwise - failure
990 
991   @ref Basic
992 **/
993 int CeedOperatorDestroy(CeedOperator *op) {
994   int ierr;
995 
996   if (!*op || --(*op)->refcount > 0) return 0;
997   if ((*op)->Destroy) {
998     ierr = (*op)->Destroy(*op); CeedChk(ierr);
999   }
1000   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1001   // Free fields
1002   for (int i=0; i<(*op)->nfields; i++)
1003     if ((*op)->inputfields[i]) {
1004       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1005       CeedChk(ierr);
1006       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1007         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1008       }
1009       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1010           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1011         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1012       }
1013       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1014     }
1015   for (int i=0; i<(*op)->nfields; i++)
1016     if ((*op)->outputfields[i]) {
1017       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1018       CeedChk(ierr);
1019       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1020         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1021       }
1022       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1023           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1024         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1025       }
1026       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1027     }
1028   // Destroy suboperators
1029   for (int i=0; i<(*op)->numsub; i++)
1030     if ((*op)->suboperators[i]) {
1031       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1032     }
1033   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1034   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1035   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1036 
1037   // Destroy fallback
1038   if ((*op)->opfallback) {
1039     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1040     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1041     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1042     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1043   }
1044 
1045   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1046   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1047   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1048   ierr = CeedFree(op); CeedChk(ierr);
1049   return 0;
1050 }
1051 
1052 /// @}
1053