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