xref: /libCEED/interface/ceed-operator.c (revision 3bd813ff881321d63f499fd87a0d6e6a47c6a2dc)
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 lmode      CeedTransposeMode which specifies the ordering of the
139                       components of the l-vector used by this CeedOperatorField,
140                       CEED_NOTRANSPOSE indicates the component is the
141                       outermost index and CEED_TRANSPOSE indicates the component
142                       is the innermost index in ordering of the l-vector
143   @param b          CeedBasis in which the field resides or CEED_BASIS_COLLOCATED
144                       if collocated with quadrature points
145   @param v          CeedVector to be used by CeedOperator or CEED_VECTOR_ACTIVE
146                       if field is active or CEED_VECTOR_NONE if using
147                       CEED_EVAL_WEIGHT in the QFunction
148 
149   @return An error code: 0 - success, otherwise - failure
150 
151   @ref Basic
152 **/
153 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
154                          CeedElemRestriction r, CeedTransposeMode lmode,
155                          CeedBasis b, CeedVector v) {
156   int ierr;
157   if (op->composite)
158     // LCOV_EXCL_START
159     return CeedError(op->ceed, 1, "Cannot add field to composite operator.");
160   // LCOV_EXCL_STOP
161   if (!r)
162     // LCOV_EXCL_START
163     return CeedError(op->ceed, 1,
164                      "ElemRestriction r for field \"%s\" must be non-NULL.",
165                      fieldname);
166   // LCOV_EXCL_STOP
167   if (!b)
168     // LCOV_EXCL_START
169     return CeedError(op->ceed, 1, "Basis b for field \"%s\" must be non-NULL.",
170                      fieldname);
171   // LCOV_EXCL_STOP
172   if (!v)
173     // LCOV_EXCL_START
174     return CeedError(op->ceed, 1, "Vector v for field \"%s\" must be non-NULL.",
175                      fieldname);
176   // LCOV_EXCL_STOP
177 
178   CeedInt numelements;
179   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
180   if (op->hasrestriction && op->numelements != numelements)
181     // LCOV_EXCL_START
182     return CeedError(op->ceed, 1,
183                      "ElemRestriction with %d elements incompatible with prior "
184                      "%d elements", numelements, op->numelements);
185   // LCOV_EXCL_STOP
186   op->numelements = numelements;
187   op->hasrestriction = true; // Restriction set, but numelements may be 0
188 
189   if (b != CEED_BASIS_COLLOCATED) {
190     CeedInt numqpoints;
191     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
192     if (op->numqpoints && op->numqpoints != numqpoints)
193       // LCOV_EXCL_START
194       return CeedError(op->ceed, 1, "Basis with %d quadrature points "
195                        "incompatible with prior %d points", numqpoints,
196                        op->numqpoints);
197     // LCOV_EXCL_STOP
198     op->numqpoints = numqpoints;
199   }
200   CeedOperatorField *ofield;
201   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
202     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
203       ofield = &op->inputfields[i];
204       goto found;
205     }
206   }
207   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
208     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
209       ofield = &op->outputfields[i];
210       goto found;
211     }
212   }
213   // LCOV_EXCL_START
214   return CeedError(op->ceed, 1, "QFunction has no knowledge of field '%s'",
215                    fieldname);
216   // LCOV_EXCL_STOP
217 found:
218   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
219   (*ofield)->Erestrict = r;
220   r->refcount += 1;
221   (*ofield)->lmode = lmode;
222   (*ofield)->basis = b;
223   if (b != CEED_BASIS_COLLOCATED)
224     b->refcount += 1;
225   (*ofield)->vec = v;
226   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE)
227     v->refcount += 1;
228   op->nfields += 1;
229   return 0;
230 }
231 
232 /**
233   @brief Add a sub-operator to a composite CeedOperator
234 
235   @param[out] compositeop Composite CeedOperator
236   @param      subop       Sub-operator CeedOperator
237 
238   @return An error code: 0 - success, otherwise - failure
239 
240   @ref Basic
241  */
242 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
243   if (!compositeop->composite)
244     // LCOV_EXCL_START
245     return CeedError(compositeop->ceed, 1, "CeedOperator is not a composite "
246                      "operator");
247   // LCOV_EXCL_STOP
248 
249   if (compositeop->numsub == CEED_COMPOSITE_MAX)
250     // LCOV_EXCL_START
251     return CeedError(compositeop->ceed, 1, "Cannot add additional suboperators");
252   // LCOV_EXCL_STOP
253 
254   compositeop->suboperators[compositeop->numsub] = subop;
255   subop->refcount++;
256   compositeop->numsub++;
257   return 0;
258 }
259 
260 /**
261   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
262            CeedOperator functionality
263 
264   @param op           CeedOperator to create fallback for
265 
266   @return An error code: 0 - success, otherwise - failure
267 
268   @ref Developer
269 **/
270 int CeedOperatorCreateFallback(CeedOperator op) {
271   int ierr;
272 
273   // Fallback Ceed
274   const char *resource, *fallbackresource;
275   ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
276   ierr = CeedGetOperatorFallbackResource(op->ceed, &fallbackresource);
277   CeedChk(ierr);
278   if (!strcmp(resource, fallbackresource))
279     // LCOV_EXCL_START
280     return CeedError(op->ceed, 1, "Backend %s cannot create an operator"
281                      "fallback to resource %s", resource, fallbackresource);
282   // LCOV_EXCL_STOP
283 
284   Ceed ceedref;
285   ierr = CeedInit(fallbackresource, &ceedref); CeedChk(ierr);
286   ceedref->opfallbackparent = op->ceed;
287   op->ceed->opfallbackceed = ceedref;
288 
289   // Clone Op
290   CeedOperator opref;
291   ierr = CeedCalloc(1, &opref); CeedChk(ierr);
292   memcpy(opref, op, sizeof(*opref)); CeedChk(ierr);
293   opref->data = NULL;
294   opref->setupdone = 0;
295   opref->ceed = ceedref;
296   ierr = ceedref->OperatorCreate(opref); CeedChk(ierr);
297   op->opfallback = opref;
298 
299   // Clone QF
300   CeedQFunction qfref;
301   ierr = CeedCalloc(1, &qfref); CeedChk(ierr);
302   memcpy(qfref, (op->qf), sizeof(*qfref)); CeedChk(ierr);
303   qfref->data = NULL;
304   qfref->ceed = ceedref;
305   ierr = ceedref->QFunctionCreate(qfref); CeedChk(ierr);
306   opref->qf = qfref;
307   op->qffallback = qfref;
308 
309   return 0;
310 }
311 
312 /**
313   @brief Check if a CeedOperator is ready to be used.
314 
315   @param[in] ceed Ceed object for error handling
316   @param[in] op   CeedOperator to check
317 
318   @return An error code: 0 - success, otherwise - failure
319 
320   @ref Basic
321 **/
322 static int CeedOperatorCheckReady(Ceed ceed, CeedOperator op) {
323   CeedQFunction qf = op->qf;
324 
325   if (op->composite) {
326     if (!op->numsub)
327       // LCOV_EXCL_START
328       return CeedError(ceed, 1, "No suboperators set");
329     // LCOV_EXCL_STOP
330   } else {
331     if (op->nfields == 0)
332       // LCOV_EXCL_START
333       return CeedError(ceed, 1, "No operator fields set");
334     // LCOV_EXCL_STOP
335     if (op->nfields < qf->numinputfields + qf->numoutputfields)
336       // LCOV_EXCL_START
337       return CeedError(ceed, 1, "Not all operator fields set");
338     // LCOV_EXCL_STOP
339     if (!op->hasrestriction)
340       // LCOV_EXCL_START
341       return CeedError(ceed, 1,"At least one restriction required");
342     // LCOV_EXCL_STOP
343     if (op->numqpoints == 0)
344       // LCOV_EXCL_START
345       return CeedError(ceed, 1,"At least one non-collocated basis required");
346     // LCOV_EXCL_STOP
347   }
348 
349   return 0;
350 }
351 
352 /**
353   @brief Assemble a linear CeedQFunction associated with a CeedOperator
354 
355   This returns a CeedVector containing a matrix at each quadrature point
356     providing the action of the CeedQFunction associated with the CeedOperator.
357     The vector 'assembled' is of shape
358       [num_elements, num_input_fields, num_output_fields, num_quad_points]
359     and contains column-major matrices representing the action of the
360     CeedQFunction for a corresponding quadrature point on an element. Inputs and
361     outputs are in the order provided by the user when adding CeedOperator fields.
362     For example, a QFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
363     'v', provided in that order, would result in an assembled QFunction that
364     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
365     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
366 
367   @param op             CeedOperator to assemble CeedQFunction
368   @param[out] assembled CeedVector to store assembled CeedQFunction at
369                           quadrature points
370   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
371                           CeedQFunction
372   @param request        Address of CeedRequest for non-blocking completion, else
373                           CEED_REQUEST_IMMEDIATE
374 
375   @return An error code: 0 - success, otherwise - failure
376 
377   @ref Basic
378 **/
379 int CeedOperatorAssembleLinearQFunction(CeedOperator op, CeedVector *assembled,
380                                         CeedElemRestriction *rstr,
381                                         CeedRequest *request) {
382   int ierr;
383   Ceed ceed = op->ceed;
384   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
385 
386   if (op->AssembleLinearQFunction) {
387     ierr = op->AssembleLinearQFunction(op, assembled, rstr, request);
388     CeedChk(ierr);
389   } else {
390     // Fallback to reference Ceed
391     if (!op->opfallback) {
392       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
393     }
394     // Assemble
395     ierr = op->opfallback->AssembleLinearQFunction(op->opfallback, assembled,
396            rstr, request); CeedChk(ierr);
397   }
398 
399   return 0;
400 }
401 
402 /**
403   @brief Assemble the diagonal of a square linear Operator
404 
405   This returns a CeedVector containing the diagonal of a linear CeedOperator.
406 
407   @param op             CeedOperator to assemble CeedQFunction
408   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
409   @param request        Address of CeedRequest for non-blocking completion, else
410                           CEED_REQUEST_IMMEDIATE
411 
412   @return An error code: 0 - success, otherwise - failure
413 
414   @ref Basic
415 **/
416 int CeedOperatorAssembleLinearDiagonal(CeedOperator op, CeedVector *assembled,
417                                        CeedRequest *request) {
418   int ierr;
419   Ceed ceed = op->ceed;
420   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
421 
422   // Use backend version, if available
423   if (op->AssembleLinearDiagonal) {
424     ierr = op->AssembleLinearDiagonal(op, assembled, request); CeedChk(ierr);
425     return 0;
426   }
427 
428   // Assemble QFunction
429   CeedQFunction qf = op->qf;
430   CeedVector assembledqf;
431   CeedElemRestriction rstr;
432   ierr = CeedOperatorAssembleLinearQFunction(op,  &assembledqf, &rstr, request);
433   CeedChk(ierr);
434   ierr = CeedElemRestrictionDestroy(&rstr); CeedChk(ierr);
435 
436   // Determine active input basis
437   CeedInt numemodein = 0, ncomp, dim = 1;
438   CeedEvalMode *emodein = NULL;
439   CeedBasis basisin = NULL;
440   CeedElemRestriction rstrin = NULL;
441   for (CeedInt i=0; i<qf->numinputfields; i++)
442     if (op->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
443       ierr = CeedOperatorFieldGetBasis(op->inputfields[i], &basisin);
444       CeedChk(ierr);
445       ierr = CeedBasisGetNumComponents(basisin, &ncomp); CeedChk(ierr);
446       ierr = CeedBasisGetDimension(basisin, &dim); CeedChk(ierr);
447       ierr = CeedOperatorFieldGetElemRestriction(op->inputfields[i], &rstrin);
448       CeedChk(ierr);
449       CeedEvalMode emode;
450       ierr = CeedQFunctionFieldGetEvalMode(qf->inputfields[i], &emode);
451       CeedChk(ierr);
452       switch (emode) {
453       case CEED_EVAL_NONE:
454       case CEED_EVAL_INTERP:
455         ierr = CeedRealloc(numemodein + 1, &emodein); CeedChk(ierr);
456         emodein[numemodein] = emode;
457         numemodein += 1;
458         break;
459       case CEED_EVAL_GRAD:
460         ierr = CeedRealloc(numemodein + dim, &emodein); CeedChk(ierr);
461         for (CeedInt d=0; d<dim; d++)
462           emodein[numemodein+d] = emode;
463         numemodein += dim;
464         break;
465       case CEED_EVAL_WEIGHT:
466       case CEED_EVAL_DIV:
467       case CEED_EVAL_CURL:
468         break; // Caught by QF Assembly
469       }
470     }
471 
472   // Determine active output basis
473   CeedInt numemodeout = 0;
474   CeedEvalMode *emodeout = NULL;
475   CeedBasis basisout = NULL;
476   CeedElemRestriction rstrout = NULL;
477   CeedTransposeMode lmodeout = CEED_NOTRANSPOSE;
478   for (CeedInt i=0; i<qf->numoutputfields; i++)
479     if (op->outputfields[i]->vec == CEED_VECTOR_ACTIVE) {
480       ierr = CeedOperatorFieldGetBasis(op->outputfields[i], &basisout);
481       CeedChk(ierr);
482       ierr = CeedOperatorFieldGetElemRestriction(op->outputfields[i], &rstrout);
483       CeedChk(ierr);
484       ierr = CeedOperatorFieldGetLMode(op->outputfields[i], &lmodeout);
485       CeedChk(ierr);
486       CeedEvalMode emode;
487       ierr = CeedQFunctionFieldGetEvalMode(qf->outputfields[i], &emode);
488       CeedChk(ierr);
489       switch (emode) {
490       case CEED_EVAL_NONE:
491       case CEED_EVAL_INTERP:
492         ierr = CeedRealloc(numemodeout + 1, &emodeout); CeedChk(ierr);
493         emodeout[numemodeout] = emode;
494         numemodeout += 1;
495         break;
496       case CEED_EVAL_GRAD:
497         ierr = CeedRealloc(numemodeout + dim, &emodeout); CeedChk(ierr);
498         for (CeedInt d=0; d<dim; d++)
499           emodeout[numemodeout+d] = emode;
500         numemodeout += dim;
501         break;
502       case CEED_EVAL_WEIGHT:
503       case CEED_EVAL_DIV:
504       case CEED_EVAL_CURL:
505         break; // Caught by QF Assembly
506       }
507     }
508 
509   // Create diagonal vector
510   CeedVector elemdiag;
511   ierr = CeedElemRestrictionCreateVector(rstrin, assembled, &elemdiag);
512   CeedChk(ierr);
513 
514   // Assemble element operator diagonals
515   CeedScalar *elemdiagarray, *assembledqfarray;
516   ierr = CeedVectorSetValue(elemdiag, 0.0); CeedChk(ierr);
517   ierr = CeedVectorGetArray(elemdiag, CEED_MEM_HOST, &elemdiagarray);
518   CeedChk(ierr);
519   ierr = CeedVectorGetArray(assembledqf, CEED_MEM_HOST, &assembledqfarray);
520   CeedChk(ierr);
521   CeedInt nelem, nnodes, nqpts;
522   ierr = CeedElemRestrictionGetNumElements(rstrin, &nelem); CeedChk(ierr);
523   ierr = CeedBasisGetNumNodes(basisin, &nnodes); CeedChk(ierr);
524   ierr = CeedBasisGetNumQuadraturePoints(basisin, &nqpts); CeedChk(ierr);
525   // Compute the diagonal of B^T D B
526   // Each node, qpt pair
527   for (CeedInt n=0; n<nnodes; n++)
528     for (CeedInt q=0; q<nqpts; q++) {
529       CeedInt dout = -1;
530       // Each basis eval mode pair
531       for (CeedInt eout=0; eout<numemodeout; eout++) {
532         CeedScalar bt = 1.0;
533         if (emodeout[eout] == CEED_EVAL_GRAD)
534           dout += 1;
535         ierr = CeedBasisGetValue(basisout, emodeout[eout], q, n, dout, &bt);
536         CeedChk(ierr);
537         CeedInt din = -1;
538         for (CeedInt ein=0; ein<numemodein; ein++) {
539           CeedScalar b = 0.0;
540           if (emodein[ein] == CEED_EVAL_GRAD)
541             din += 1;
542           ierr = CeedBasisGetValue(basisin, emodein[ein], q, n, din, &b);
543           CeedChk(ierr);
544           // Each element and component
545           for (CeedInt e=0; e<nelem; e++)
546             for (CeedInt cout=0; cout<ncomp; cout++) {
547               CeedScalar db = 0.0;
548               for (CeedInt cin=0; cin<ncomp; cin++)
549                 db += assembledqfarray[((((e*numemodein+ein)*ncomp+cin)*
550                                          numemodeout+eout)*ncomp+cout)*nqpts+q]*b;
551               elemdiagarray[(e*ncomp+cout)*nnodes+n] += bt * db;
552             }
553         }
554       }
555     }
556 
557   ierr = CeedVectorRestoreArray(elemdiag, &elemdiagarray); CeedChk(ierr);
558   ierr = CeedVectorRestoreArray(assembledqf, &assembledqfarray); CeedChk(ierr);
559 
560   // Assemble local operator diagonal
561   ierr = CeedVectorSetValue(*assembled, 0.0); CeedChk(ierr);
562   ierr = CeedElemRestrictionApply(rstrout, CEED_TRANSPOSE, lmodeout, elemdiag,
563                                   *assembled, request); CeedChk(ierr);
564 
565   // Cleanup
566   ierr = CeedVectorDestroy(&assembledqf); CeedChk(ierr);
567   ierr = CeedVectorDestroy(&elemdiag); CeedChk(ierr);
568   ierr = CeedFree(&emodein); CeedChk(ierr);
569   ierr = CeedFree(&emodeout); CeedChk(ierr);
570 
571   return 0;
572 }
573 
574 /**
575   @brief Build a FDM based approximate inverse for each element for a
576            CeedOperator
577 
578   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
579     Method based approximate inverse. This function obtains the simultaneous
580     diagonalization for the 1D mass and Laplacian operators,
581       M = V^T V, K = V^T S V.
582     The assembled QFunction is used to modify the eigenvalues from simultaneous
583     diagonalization and obtain an approximate inverse of the form
584       V^T S^hat V. The CeedOperator must be linear and non-composite. The
585     associated CeedQFunction must therefore also be linear.
586 
587   @param op             CeedOperator to create element inverses
588   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
589                           for each element
590   @param[out] qdata     CeedVector to hold qdata for fdminv
591   @param request        Address of CeedRequest for non-blocking completion, else
592                           CEED_REQUEST_IMMEDIATE
593 
594   @return An error code: 0 - success, otherwise - failure
595 
596   @ref Advanced
597 **/
598 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
599     CeedRequest *request) {
600   int ierr;
601   Ceed ceed = op->ceed;
602 
603   // Determine active input basis
604   bool interp = false, grad = false;
605   CeedBasis basis = NULL;
606   CeedElemRestriction rstr = NULL;
607   for (CeedInt i=0; i<op->qf->numinputfields; i++)
608     if (op->inputfields[i] && op->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
609       basis = op->inputfields[i]->basis;
610       interp = interp || op->qf->inputfields[i]->emode == CEED_EVAL_INTERP;
611       grad = grad || op->qf->inputfields[i]->emode == CEED_EVAL_GRAD;
612       rstr = op->inputfields[i]->Erestrict;
613     }
614   if (!basis)
615     return CeedError(ceed, 1, "No active field set");
616   CeedInt P1d, Q1d, elemsize, nqpts, dim, ncomp = 1, nelem = 1, nnodes = 1;
617   ierr = CeedBasisGetNumNodes1D(basis, &P1d); CeedChk(ierr);
618   ierr = CeedBasisGetNumNodes(basis, &elemsize); CeedChk(ierr);
619   ierr = CeedBasisGetNumQuadraturePoints1D(basis, &Q1d); CeedChk(ierr);
620   ierr = CeedBasisGetNumQuadraturePoints(basis, &nqpts); CeedChk(ierr);
621   ierr = CeedBasisGetDimension(basis, &dim); CeedChk(ierr);
622   ierr = CeedBasisGetNumComponents(basis, &ncomp); CeedChk(ierr);
623   ierr = CeedElemRestrictionGetNumElements(rstr, &nelem); CeedChk(ierr);
624   ierr = CeedElemRestrictionGetNumNodes(rstr, &nnodes); CeedChk(ierr);
625 
626   // Build and diagonalize 1D Mass and Laplacian
627   if (!basis->tensorbasis)
628     return CeedError(ceed, 1, "FDMElementInverse only supported for tensor "
629                      "bases");
630   CeedScalar *work, *mass, *laplace, *x, *x2, *lambda;
631   ierr = CeedMalloc(Q1d*P1d, &work); CeedChk(ierr);
632   ierr = CeedMalloc(P1d*P1d, &mass); CeedChk(ierr);
633   ierr = CeedMalloc(P1d*P1d, &laplace); CeedChk(ierr);
634   ierr = CeedMalloc(P1d*P1d, &x); CeedChk(ierr);
635   ierr = CeedMalloc(P1d*P1d, &x2); CeedChk(ierr);
636   ierr = CeedMalloc(P1d, &lambda); CeedChk(ierr);
637   // -- Mass
638   for (CeedInt i=0; i<Q1d; i++)
639     for (CeedInt j=0; j<P1d; j++)
640       work[i+j*Q1d] = basis->interp1d[i*P1d+j]*basis->qweight1d[i];
641   ierr = CeedMatrixMultiply(ceed, work, basis->interp1d, mass, P1d, P1d, Q1d);
642   CeedChk(ierr);
643   // -- Laplacian
644   for (CeedInt i=0; i<Q1d; i++)
645     for (CeedInt j=0; j<P1d; j++)
646       work[i+j*Q1d] = basis->grad1d[i*P1d+j]*basis->qweight1d[i];
647   ierr = CeedMatrixMultiply(ceed, work, basis->grad1d, laplace, P1d, P1d, Q1d);
648   CeedChk(ierr);
649   // -- Diagonalize
650   ierr = CeedSimultaneousDiagonalization(ceed, laplace, mass, x, lambda, P1d);
651   CeedChk(ierr);
652   ierr = CeedFree(&work); CeedChk(ierr);
653   ierr = CeedFree(&mass); CeedChk(ierr);
654   ierr = CeedFree(&laplace); CeedChk(ierr);
655   for (CeedInt i=0; i<P1d; i++)
656     for (CeedInt j=0; j<P1d; j++)
657     x2[i+j*P1d] = x[j+i*P1d];
658   ierr = CeedFree(&x); CeedChk(ierr);
659 
660   // Assemble QFunction
661   CeedVector assembled;
662   CeedElemRestriction rstr_qf;
663   ierr =  CeedOperatorAssembleLinearQFunction(op, &assembled, &rstr_qf,
664                                               request); CeedChk(ierr);
665   ierr = CeedElemRestrictionDestroy(&rstr_qf); CeedChk(ierr);
666 
667   // Calculate element averages
668   CeedInt nfields = ((interp?1:0) + (grad?dim:0))*((interp?1:0) + (grad?dim:0));
669   CeedScalar *elemavg;
670   const CeedScalar *assembledarray, *qweightsarray;
671   CeedVector qweights;
672   ierr = CeedVectorCreate(ceed, nqpts, &qweights); CeedChk(ierr);
673   ierr = CeedBasisApply(basis, 1, CEED_NOTRANSPOSE, CEED_EVAL_WEIGHT,
674                         CEED_VECTOR_NONE, qweights); CeedChk(ierr);
675   ierr = CeedVectorGetArrayRead(assembled, CEED_MEM_HOST, &assembledarray);
676   CeedChk(ierr);
677   ierr = CeedVectorGetArrayRead(qweights, CEED_MEM_HOST, &qweightsarray);
678   CeedChk(ierr);
679   ierr = CeedCalloc(nelem, &elemavg); CeedChk(ierr);
680   for (CeedInt e=0; e<nelem; e++) {
681     CeedInt count = 0;
682       for (CeedInt q=0; q<nqpts; q++)
683         for (CeedInt i=0; i<ncomp*ncomp*nfields; i++)
684           if (fabs(assembledarray[e*nelem*nqpts*ncomp*ncomp*nfields +
685                                   i*nqpts + q]) > 1e-15) {
686             elemavg[e] += assembledarray[e*nelem*nqpts*ncomp*ncomp*nfields +
687                                          i*nqpts + q] / qweightsarray[q];
688             count++;
689           }
690     if (count)
691       elemavg[e] /= count;
692   }
693   ierr = CeedVectorRestoreArrayRead(assembled, &assembledarray); CeedChk(ierr);
694   ierr = CeedVectorDestroy(&assembled); CeedChk(ierr);
695   ierr = CeedVectorRestoreArrayRead(qweights, &qweightsarray); CeedChk(ierr);
696   ierr = CeedVectorDestroy(&qweights); CeedChk(ierr);
697 
698   // Build FDM diagonal
699   CeedVector qdata;
700   CeedScalar *qdataarray;
701   ierr = CeedVectorCreate(ceed, nelem*ncomp*nnodes, &qdata); CeedChk(ierr);
702   ierr = CeedVectorSetArray(qdata, CEED_MEM_HOST, CEED_COPY_VALUES, NULL);
703   CeedChk(ierr);
704   ierr = CeedVectorGetArray(qdata, CEED_MEM_HOST, &qdataarray); CeedChk(ierr);
705   for (CeedInt e=0; e<nelem; e++)
706     for (CeedInt c=0; c<ncomp; c++)
707       for (CeedInt n=0; n<nnodes; n++) {
708         if (interp)
709           qdataarray[(e*ncomp+c)*nnodes+n] = 1;
710         if (grad)
711           for (CeedInt d=0; d<dim; d++) {
712             CeedInt i = (n / CeedIntPow(P1d, d)) % P1d;
713             qdataarray[(e*ncomp+c)*nnodes+n] += lambda[i];
714           }
715         qdataarray[(e*ncomp+c)*nnodes+n] = 1 / (elemavg[e] *
716                                            qdataarray[(e*ncomp+c)*nnodes+n]);
717       }
718   ierr = CeedFree(&elemavg); CeedChk(ierr);
719   ierr = CeedVectorRestoreArray(qdata, &qdataarray); CeedChk(ierr);
720 
721   // Setup FDM operator
722   // -- Basis
723   CeedBasis fdm_basis;
724   CeedScalar *graddummy, *qrefdummy, *qweightdummy;
725   ierr = CeedCalloc(P1d*P1d, &graddummy); CeedChk(ierr);
726   ierr = CeedCalloc(P1d, &qrefdummy); CeedChk(ierr);
727   ierr = CeedCalloc(P1d, &qweightdummy); CeedChk(ierr);
728   ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, P1d, P1d, x2, graddummy,
729                                  qrefdummy, qweightdummy, &fdm_basis);
730   CeedChk(ierr);
731   ierr = CeedFree(&graddummy); CeedChk(ierr);
732   ierr = CeedFree(&qrefdummy); CeedChk(ierr);
733   ierr = CeedFree(&qweightdummy); CeedChk(ierr);
734   ierr = CeedFree(&x2); CeedChk(ierr);
735   ierr = CeedFree(&lambda); CeedChk(ierr);
736 
737   // -- Restriction
738   CeedElemRestriction rstr_i;
739   ierr = CeedElemRestrictionCreateIdentity(ceed, nelem, nnodes, nnodes*nelem,
740                                            ncomp, &rstr_i); CeedChk(ierr);
741   // -- QFunction
742   CeedQFunction mass_qf;
743   ierr = CeedQFunctionCreateInteriorByName(ceed, "MassApply", &mass_qf);
744   CeedChk(ierr);
745   // -- Operator
746   ierr = CeedOperatorCreate(ceed, mass_qf, NULL, NULL, fdminv); CeedChk(ierr);
747   CeedOperatorSetField(*fdminv, "u", rstr_i, CEED_NOTRANSPOSE,
748                        fdm_basis, CEED_VECTOR_ACTIVE); CeedChk(ierr);
749   CeedOperatorSetField(*fdminv, "qdata", rstr_i, CEED_NOTRANSPOSE,
750                        CEED_BASIS_COLLOCATED, qdata); CeedChk(ierr);
751   CeedOperatorSetField(*fdminv, "v", rstr_i, CEED_NOTRANSPOSE,
752                        fdm_basis, CEED_VECTOR_ACTIVE); CeedChk(ierr);
753 
754   // Cleanup
755   ierr = CeedVectorDestroy(&qdata); CeedChk(ierr);
756   ierr = CeedBasisDestroy(&fdm_basis); CeedChk(ierr);
757   ierr = CeedElemRestrictionDestroy(&rstr_i); CeedChk(ierr);
758   ierr = CeedQFunctionDestroy(&mass_qf); CeedChk(ierr);
759 
760   return 0;
761 }
762 
763 
764 /**
765   @brief Apply CeedOperator to a vector
766 
767   This computes the action of the operator on the specified (active) input,
768   yielding its (active) output.  All inputs and outputs must be specified using
769   CeedOperatorSetField().
770 
771   @param op        CeedOperator to apply
772   @param[in] in    CeedVector containing input state or CEED_VECTOR_NONE if
773                   there are no active inputs
774   @param[out] out  CeedVector to store result of applying operator (must be
775                      distinct from @a in) or CEED_VECTOR_NONE if there are no
776                      active outputs
777   @param request   Address of CeedRequest for non-blocking completion, else
778                      CEED_REQUEST_IMMEDIATE
779 
780   @return An error code: 0 - success, otherwise - failure
781 
782   @ref Basic
783 **/
784 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
785                       CeedRequest *request) {
786   int ierr;
787   Ceed ceed = op->ceed;
788   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
789 
790   if (op->numelements)  {
791     // Standard Operator
792     if (op->Apply) {
793       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
794     } else {
795       // Zero all output vectors
796       CeedQFunction qf = op->qf;
797       for (CeedInt i=0; i<qf->numoutputfields; i++) {
798         CeedVector vec = op->outputfields[i]->vec;
799         if (vec == CEED_VECTOR_ACTIVE)
800           vec = out;
801         if (vec != CEED_VECTOR_NONE) {
802           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
803         }
804       }
805       // Apply
806       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
807     }
808   } else if (op->composite) {
809     // Composite Operator
810     if (op->ApplyComposite) {
811       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
812     } else {
813       CeedInt numsub;
814       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
815       CeedOperator *suboperators;
816       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
817 
818       // Zero all output vectors
819       if (out != CEED_VECTOR_NONE) {
820         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
821       }
822       for (CeedInt i=0; i<numsub; i++) {
823         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
824           CeedVector vec = suboperators[i]->outputfields[j]->vec;
825           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
826             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
827           }
828         }
829       }
830       // Apply
831       for (CeedInt i=0; i<op->numsub; i++) {
832         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
833         CeedChk(ierr);
834       }
835     }
836   }
837 
838   return 0;
839 }
840 
841 /**
842   @brief Apply CeedOperator to a vector and add result to output vector
843 
844   This computes the action of the operator on the specified (active) input,
845   yielding its (active) output.  All inputs and outputs must be specified using
846   CeedOperatorSetField().
847 
848   @param op        CeedOperator to apply
849   @param[in] in    CeedVector containing input state or NULL if there are no
850                      active inputs
851   @param[out] out  CeedVector to sum in result of applying operator (must be
852                      distinct from @a in) or NULL if there are no active outputs
853   @param request   Address of CeedRequest for non-blocking completion, else
854                      CEED_REQUEST_IMMEDIATE
855 
856   @return An error code: 0 - success, otherwise - failure
857 
858   @ref Basic
859 **/
860 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
861                          CeedRequest *request) {
862   int ierr;
863   Ceed ceed = op->ceed;
864   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
865 
866   if (op->numelements)  {
867     // Standard Operator
868     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
869   } else if (op->composite) {
870     // Composite Operator
871     if (op->ApplyAddComposite) {
872       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
873     } else {
874       CeedInt numsub;
875       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
876       CeedOperator *suboperators;
877       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
878 
879       for (CeedInt i=0; i<numsub; i++) {
880         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
881         CeedChk(ierr);
882       }
883     }
884   }
885 
886   return 0;
887 }
888 
889 /**
890   @brief Get the Ceed associated with a CeedOperator
891 
892   @param op              CeedOperator
893   @param[out] ceed       Variable to store Ceed
894 
895   @return An error code: 0 - success, otherwise - failure
896 
897   @ref Advanced
898 **/
899 
900 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
901   *ceed = op->ceed;
902   return 0;
903 }
904 
905 /**
906   @brief Get the number of elements associated with a CeedOperator
907 
908   @param op              CeedOperator
909   @param[out] numelem    Variable to store number of elements
910 
911   @return An error code: 0 - success, otherwise - failure
912 
913   @ref Advanced
914 **/
915 
916 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
917   if (op->composite)
918     // LCOV_EXCL_START
919     return CeedError(op->ceed, 1, "Not defined for composite operator");
920   // LCOV_EXCL_STOP
921 
922   *numelem = op->numelements;
923   return 0;
924 }
925 
926 /**
927   @brief Get the number of quadrature points associated with a CeedOperator
928 
929   @param op              CeedOperator
930   @param[out] numqpts    Variable to store vector number of quadrature points
931 
932   @return An error code: 0 - success, otherwise - failure
933 
934   @ref Advanced
935 **/
936 
937 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
938   if (op->composite)
939     // LCOV_EXCL_START
940     return CeedError(op->ceed, 1, "Not defined for composite operator");
941   // LCOV_EXCL_STOP
942 
943   *numqpts = op->numqpoints;
944   return 0;
945 }
946 
947 /**
948   @brief Get the number of arguments associated with a CeedOperator
949 
950   @param op              CeedOperator
951   @param[out] numargs    Variable to store vector number of arguments
952 
953   @return An error code: 0 - success, otherwise - failure
954 
955   @ref Advanced
956 **/
957 
958 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
959   if (op->composite)
960     // LCOV_EXCL_START
961     return CeedError(op->ceed, 1, "Not defined for composite operators");
962   // LCOV_EXCL_STOP
963 
964   *numargs = op->nfields;
965   return 0;
966 }
967 
968 /**
969   @brief Get the setup status of a CeedOperator
970 
971   @param op             CeedOperator
972   @param[out] setupdone Variable to store setup status
973 
974   @return An error code: 0 - success, otherwise - failure
975 
976   @ref Advanced
977 **/
978 
979 int CeedOperatorGetSetupStatus(CeedOperator op, bool *setupdone) {
980   *setupdone = op->setupdone;
981   return 0;
982 }
983 
984 /**
985   @brief Get the QFunction associated with a CeedOperator
986 
987   @param op              CeedOperator
988   @param[out] qf         Variable to store QFunction
989 
990   @return An error code: 0 - success, otherwise - failure
991 
992   @ref Advanced
993 **/
994 
995 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
996   if (op->composite)
997     // LCOV_EXCL_START
998     return CeedError(op->ceed, 1, "Not defined for composite operator");
999   // LCOV_EXCL_STOP
1000 
1001   *qf = op->qf;
1002   return 0;
1003 }
1004 
1005 /**
1006   @brief Get the number of suboperators associated with a CeedOperator
1007 
1008   @param op              CeedOperator
1009   @param[out] numsub     Variable to store number of suboperators
1010 
1011   @return An error code: 0 - success, otherwise - failure
1012 
1013   @ref Advanced
1014 **/
1015 
1016 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
1017   if (!op->composite)
1018     // LCOV_EXCL_START
1019     return CeedError(op->ceed, 1, "Not a composite operator");
1020   // LCOV_EXCL_STOP
1021 
1022   *numsub = op->numsub;
1023   return 0;
1024 }
1025 
1026 /**
1027   @brief Get the list of suboperators associated with a CeedOperator
1028 
1029   @param op                CeedOperator
1030   @param[out] suboperators Variable to store list of suboperators
1031 
1032   @return An error code: 0 - success, otherwise - failure
1033 
1034   @ref Advanced
1035 **/
1036 
1037 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
1038   if (!op->composite)
1039     // LCOV_EXCL_START
1040     return CeedError(op->ceed, 1, "Not a composite operator");
1041   // LCOV_EXCL_STOP
1042 
1043   *suboperators = op->suboperators;
1044   return 0;
1045 }
1046 
1047 /**
1048   @brief Set the backend data of a CeedOperator
1049 
1050   @param[out] op         CeedOperator
1051   @param data            Data to set
1052 
1053   @return An error code: 0 - success, otherwise - failure
1054 
1055   @ref Advanced
1056 **/
1057 
1058 int CeedOperatorSetData(CeedOperator op, void **data) {
1059   op->data = *data;
1060   return 0;
1061 }
1062 
1063 /**
1064   @brief Get the backend data of a CeedOperator
1065 
1066   @param op              CeedOperator
1067   @param[out] data       Variable to store data
1068 
1069   @return An error code: 0 - success, otherwise - failure
1070 
1071   @ref Advanced
1072 **/
1073 
1074 int CeedOperatorGetData(CeedOperator op, void **data) {
1075   *data = op->data;
1076   return 0;
1077 }
1078 
1079 /**
1080   @brief Set the setup flag of a CeedOperator to True
1081 
1082   @param op              CeedOperator
1083 
1084   @return An error code: 0 - success, otherwise - failure
1085 
1086   @ref Advanced
1087 **/
1088 
1089 int CeedOperatorSetSetupDone(CeedOperator op) {
1090   op->setupdone = 1;
1091   return 0;
1092 }
1093 
1094 /**
1095   @brief View a field of a CeedOperator
1096 
1097   @param[in] field       Operator field to view
1098   @param[in] fieldnumber Number of field being viewed
1099   @param[in] stream      Stream to view to, e.g., stdout
1100 
1101   @return An error code: 0 - success, otherwise - failure
1102 
1103   @ref Utility
1104 **/
1105 static int CeedOperatorFieldView(CeedOperatorField field,
1106                                  CeedQFunctionField qffield,
1107                                  CeedInt fieldnumber, bool sub, bool in,
1108                                  FILE *stream) {
1109   const char *pre = sub ? "  " : "";
1110   const char *inout = in ? "Input" : "Output";
1111 
1112   fprintf(stream, "%s    %s Field [%d]:\n"
1113           "%s      Name: \"%s\"\n"
1114           "%s      Lmode: \"%s\"\n",
1115           pre, inout, fieldnumber, pre, qffield->fieldname,
1116           pre, CeedTransposeModes[field->lmode]);
1117 
1118   if (field->basis == CEED_BASIS_COLLOCATED)
1119     fprintf(stream, "%s      Collocated basis\n", pre);
1120 
1121   if (field->vec == CEED_VECTOR_ACTIVE)
1122     fprintf(stream, "%s      Active vector\n", pre);
1123   else if (field->vec == CEED_VECTOR_NONE)
1124     fprintf(stream, "%s      No vector\n", pre);
1125 
1126   return 0;
1127 }
1128 
1129 /**
1130   @brief View a single CeedOperator
1131 
1132   @param[in] op     CeedOperator to view
1133   @param[in] stream Stream to write; typically stdout/stderr or a file
1134 
1135   @return Error code: 0 - success, otherwise - failure
1136 
1137   @ref Utility
1138 **/
1139 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
1140   int ierr;
1141   const char *pre = sub ? "  " : "";
1142 
1143   CeedInt totalfields;
1144   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
1145 
1146   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
1147 
1148   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
1149           op->qf->numinputfields>1 ? "s" : "");
1150   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
1151     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
1152                                  i, sub, 1, stream); CeedChk(ierr);
1153   }
1154 
1155   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
1156           op->qf->numoutputfields>1 ? "s" : "");
1157   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
1158     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->inputfields[i],
1159                                  i, sub, 0, stream); CeedChk(ierr);
1160   }
1161 
1162   return 0;
1163 }
1164 
1165 /**
1166   @brief View a CeedOperator
1167 
1168   @param[in] op     CeedOperator to view
1169   @param[in] stream Stream to write; typically stdout/stderr or a file
1170 
1171   @return Error code: 0 - success, otherwise - failure
1172 
1173   @ref Utility
1174 **/
1175 int CeedOperatorView(CeedOperator op, FILE *stream) {
1176   int ierr;
1177 
1178   if (op->composite) {
1179     fprintf(stream, "Composite CeedOperator\n");
1180 
1181     for (CeedInt i=0; i<op->numsub; i++) {
1182       fprintf(stream, "  SubOperator [%d]:\n", i);
1183       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1184       CeedChk(ierr);
1185     }
1186   } else {
1187     fprintf(stream, "CeedOperator\n");
1188     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1189   }
1190 
1191   return 0;
1192 }
1193 
1194 /**
1195   @brief Get the CeedOperatorFields of a CeedOperator
1196 
1197   @param op                 CeedOperator
1198   @param[out] inputfields   Variable to store inputfields
1199   @param[out] outputfields  Variable to store outputfields
1200 
1201   @return An error code: 0 - success, otherwise - failure
1202 
1203   @ref Advanced
1204 **/
1205 
1206 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
1207                           CeedOperatorField **outputfields) {
1208   if (op->composite)
1209     // LCOV_EXCL_START
1210     return CeedError(op->ceed, 1, "Not defined for composite operator");
1211   // LCOV_EXCL_STOP
1212 
1213   if (inputfields) *inputfields = op->inputfields;
1214   if (outputfields) *outputfields = op->outputfields;
1215   return 0;
1216 }
1217 
1218 /**
1219   @brief Get the L vector CeedTransposeMode of a CeedOperatorField
1220 
1221   @param opfield         CeedOperatorField
1222   @param[out] lmode      Variable to store CeedTransposeMode
1223 
1224   @return An error code: 0 - success, otherwise - failure
1225 
1226   @ref Advanced
1227 **/
1228 
1229 int CeedOperatorFieldGetLMode(CeedOperatorField opfield,
1230                               CeedTransposeMode *lmode) {
1231   *lmode = opfield->lmode;
1232   return 0;
1233 }
1234 
1235 /**
1236   @brief Get the CeedElemRestriction of a CeedOperatorField
1237 
1238   @param opfield         CeedOperatorField
1239   @param[out] rstr       Variable to store CeedElemRestriction
1240 
1241   @return An error code: 0 - success, otherwise - failure
1242 
1243   @ref Advanced
1244 **/
1245 
1246 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
1247                                         CeedElemRestriction *rstr) {
1248   *rstr = opfield->Erestrict;
1249   return 0;
1250 }
1251 
1252 /**
1253   @brief Get the CeedBasis of a CeedOperatorField
1254 
1255   @param opfield         CeedOperatorField
1256   @param[out] basis      Variable to store CeedBasis
1257 
1258   @return An error code: 0 - success, otherwise - failure
1259 
1260   @ref Advanced
1261 **/
1262 
1263 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
1264   *basis = opfield->basis;
1265   return 0;
1266 }
1267 
1268 /**
1269   @brief Get the CeedVector of a CeedOperatorField
1270 
1271   @param opfield         CeedOperatorField
1272   @param[out] vec        Variable to store CeedVector
1273 
1274   @return An error code: 0 - success, otherwise - failure
1275 
1276   @ref Advanced
1277 **/
1278 
1279 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
1280   *vec = opfield->vec;
1281   return 0;
1282 }
1283 
1284 /**
1285   @brief Destroy a CeedOperator
1286 
1287   @param op CeedOperator to destroy
1288 
1289   @return An error code: 0 - success, otherwise - failure
1290 
1291   @ref Basic
1292 **/
1293 int CeedOperatorDestroy(CeedOperator *op) {
1294   int ierr;
1295 
1296   if (!*op || --(*op)->refcount > 0) return 0;
1297   if ((*op)->Destroy) {
1298     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1299   }
1300   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1301   // Free fields
1302   for (int i=0; i<(*op)->nfields; i++)
1303     if ((*op)->inputfields[i]) {
1304       ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1305       CeedChk(ierr);
1306       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1307         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1308       }
1309       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1310           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1311         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1312       }
1313       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1314     }
1315   for (int i=0; i<(*op)->nfields; i++)
1316     if ((*op)->outputfields[i]) {
1317       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1318       CeedChk(ierr);
1319       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1320         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1321       }
1322       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1323           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1324         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1325       }
1326       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1327     }
1328   // Destroy suboperators
1329   for (int i=0; i<(*op)->numsub; i++)
1330     if ((*op)->suboperators[i]) {
1331       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1332     }
1333   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1334   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1335   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1336 
1337   // Destroy fallback
1338   if ((*op)->opfallback) {
1339     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1340     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1341     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1342     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1343   }
1344 
1345   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1346   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1347   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1348   ierr = CeedFree(op); CeedChk(ierr);
1349   return 0;
1350 }
1351 
1352 /// @}
1353