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