xref: /libCEED/rust/libceed-sys/c-src/interface/ceed-operator.c (revision e15f9bd09af0280c89b79924fa9af7dd2e3e30be)
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.h>
18 #include <ceed-backend.h>
19 #include <ceed-impl.h>
20 #include <math.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 
25 /// @file
26 /// Implementation of CeedOperator interfaces
27 
28 /// ----------------------------------------------------------------------------
29 /// CeedOperator Library Internal Functions
30 /// ----------------------------------------------------------------------------
31 /// @addtogroup CeedOperatorDeveloper
32 /// @{
33 
34 /**
35   @brief Duplicate a CeedOperator with a reference Ceed to fallback for advanced
36            CeedOperator functionality
37 
38   @param op           CeedOperator to create fallback for
39 
40   @return An error code: 0 - success, otherwise - failure
41 
42   @ref Developer
43 **/
44 int CeedOperatorCreateFallback(CeedOperator op) {
45   int ierr;
46 
47   // Fallback Ceed
48   const char *resource, *fallbackresource;
49   ierr = CeedGetResource(op->ceed, &resource); CeedChk(ierr);
50   ierr = CeedGetOperatorFallbackResource(op->ceed, &fallbackresource);
51   CeedChk(ierr);
52   if (!strcmp(resource, fallbackresource))
53     // LCOV_EXCL_START
54     return CeedError(op->ceed, CEED_ERROR_UNSUPPORTED,
55                      "Backend %s cannot create an operator"
56                      "fallback to resource %s", resource, fallbackresource);
57   // LCOV_EXCL_STOP
58 
59   // Fallback Ceed
60   Ceed ceedref;
61   if (!op->ceed->opfallbackceed) {
62     ierr = CeedInit(fallbackresource, &ceedref); CeedChk(ierr);
63     ceedref->opfallbackparent = op->ceed;
64     ceedref->Error = op->ceed->Error;
65     op->ceed->opfallbackceed = ceedref;
66   }
67   ceedref = op->ceed->opfallbackceed;
68 
69   // Clone Op
70   CeedOperator opref;
71   ierr = CeedCalloc(1, &opref); CeedChk(ierr);
72   memcpy(opref, op, sizeof(*opref));
73   opref->data = NULL;
74   opref->interfacesetup = false;
75   opref->backendsetup = false;
76   opref->ceed = ceedref;
77   ierr = ceedref->OperatorCreate(opref); CeedChk(ierr);
78   op->opfallback = opref;
79 
80   // Clone QF
81   CeedQFunction qfref;
82   ierr = CeedCalloc(1, &qfref); CeedChk(ierr);
83   memcpy(qfref, (op->qf), sizeof(*qfref));
84   qfref->data = NULL;
85   qfref->ceed = ceedref;
86   ierr = ceedref->QFunctionCreate(qfref); CeedChk(ierr);
87   opref->qf = qfref;
88   op->qffallback = qfref;
89   return CEED_ERROR_SUCCESS;
90 }
91 
92 /**
93   @brief Check if a CeedOperator Field matches the QFunction Field
94 
95   @param[in] ceed   Ceed object for error handling
96   @param[in] qfield QFunction Field matching Operator Field
97   @param[in] r      Operator Field ElemRestriction
98   @param[in] b      Operator Field Basis
99 
100   @return An error code: 0 - success, otherwise - failure
101 
102   @ref Developer
103 **/
104 static int CeedOperatorCheckField(Ceed ceed, CeedQFunctionField qfield,
105                                   CeedElemRestriction r, CeedBasis b) {
106   int ierr;
107   CeedEvalMode emode = qfield->emode;
108   CeedInt dim = 1, ncomp = 1, restr_ncomp = 1, size = qfield->size;
109   // Restriction
110   if (r != CEED_ELEMRESTRICTION_NONE) {
111     ierr = CeedElemRestrictionGetNumComponents(r, &restr_ncomp);
112   } else if (emode != CEED_EVAL_WEIGHT) {
113     // LCOV_EXCL_START
114     return CeedError(ceed, CEED_ERROR_MINOR,
115                      "CEED_ELEMRESTRICTION_NONE can only be used "
116                      "for a field with eval mode CEED_EVAL_WEIGHT");
117     // LCOV_EXCL_STOP
118   }
119   // Basis
120   if (b != CEED_BASIS_COLLOCATED) {
121     ierr = CeedBasisGetDimension(b, &dim); CeedChk(ierr);
122     ierr = CeedBasisGetNumComponents(b, &ncomp); CeedChk(ierr);
123     if (r != CEED_ELEMRESTRICTION_NONE && restr_ncomp != ncomp) {
124       // LCOV_EXCL_START
125       return CeedError(ceed, CEED_ERROR_DIMENSION,
126                        "ElemRestriction and Basis have incompatible numbers of components");
127       // LCOV_EXCL_STOP
128     }
129   }
130   // Field size
131   switch(emode) {
132   case CEED_EVAL_NONE:
133     if (size != restr_ncomp)
134       // LCOV_EXCL_START
135       return CeedError(ceed, CEED_ERROR_DIMENSION,
136                        "Incompatible QFunction field size and Operator field "
137                        "ElemRestriction number of components");
138     // LCOV_EXCL_STOP
139     break;
140   case CEED_EVAL_INTERP:
141     if (size != ncomp)
142       // LCOV_EXCL_START
143       return CeedError(ceed, CEED_ERROR_DIMENSION,
144                        "Incompatible QFunction field size and Operator field "
145                        "Basis number of components");
146     // LCOV_EXCL_STOP
147     break;
148   case CEED_EVAL_GRAD:
149     if (size != ncomp * dim)
150       // LCOV_EXCL_START
151       return CeedError(ceed, CEED_ERROR_DIMENSION,
152                        "Incompatible QFunction field size and Operator field "
153                        "Basis dimension and number of components");
154     // LCOV_EXCL_STOP
155     break;
156   case CEED_EVAL_WEIGHT:
157     // No addional checks required
158     break;
159   case CEED_EVAL_DIV:
160     // Not implemented
161     break;
162   case CEED_EVAL_CURL:
163     // Not implemented
164     break;
165   }
166   return CEED_ERROR_SUCCESS;
167 }
168 
169 /**
170   @brief Check if a CeedOperator is ready to be used.
171 
172   @param[in] ceed Ceed object for error handling
173   @param[in] op   CeedOperator to check
174 
175   @return An error code: 0 - success, otherwise - failure
176 
177   @ref Developer
178 **/
179 static int CeedOperatorCheckReady(Ceed ceed, CeedOperator op) {
180   if (op->interfacesetup)
181     return CEED_ERROR_SUCCESS;
182 
183   CeedQFunction qf = op->qf;
184   if (op->composite) {
185     if (!op->numsub)
186       // LCOV_EXCL_START
187       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No suboperators set");
188     // LCOV_EXCL_STOP
189   } else {
190     if (op->nfields == 0)
191       // LCOV_EXCL_START
192       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "No operator fields set");
193     // LCOV_EXCL_STOP
194     if (op->nfields < qf->numinputfields + qf->numoutputfields)
195       // LCOV_EXCL_START
196       return CeedError(ceed, CEED_ERROR_INCOMPLETE, "Not all operator fields set");
197     // LCOV_EXCL_STOP
198     if (!op->hasrestriction)
199       // LCOV_EXCL_START
200       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
201                        "At least one restriction required");
202     // LCOV_EXCL_STOP
203     if (op->numqpoints == 0)
204       // LCOV_EXCL_START
205       return CeedError(ceed, CEED_ERROR_INCOMPLETE,
206                        "At least one non-collocated basis required");
207     // LCOV_EXCL_STOP
208   }
209 
210   // Flag as immutable and ready
211   op->interfacesetup = true;
212   if (op->qf && op->qf != CEED_QFUNCTION_NONE)
213     // LCOV_EXCL_START
214     op->qf->operatorsset++;
215   // LCOV_EXCL_STOP
216   if (op->dqf && op->dqf != CEED_QFUNCTION_NONE)
217     // LCOV_EXCL_START
218     op->dqf->operatorsset++;
219   // LCOV_EXCL_STOP
220   if (op->dqfT && op->dqfT != CEED_QFUNCTION_NONE)
221     // LCOV_EXCL_START
222     op->dqfT->operatorsset++;
223   // LCOV_EXCL_STOP
224   return CEED_ERROR_SUCCESS;
225 }
226 
227 /**
228   @brief View a field of a CeedOperator
229 
230   @param[in] field       Operator field to view
231   @param[in] qffield     QFunction field (carries field name)
232   @param[in] fieldnumber Number of field being viewed
233   @param[in] sub         true indicates sub-operator, which increases indentation; false for top-level operator
234   @param[in] in          true for an input field; false for output field
235   @param[in] stream      Stream to view to, e.g., stdout
236 
237   @return An error code: 0 - success, otherwise - failure
238 
239   @ref Utility
240 **/
241 static int CeedOperatorFieldView(CeedOperatorField field,
242                                  CeedQFunctionField qffield,
243                                  CeedInt fieldnumber, bool sub, bool in,
244                                  FILE *stream) {
245   const char *pre = sub ? "  " : "";
246   const char *inout = in ? "Input" : "Output";
247 
248   fprintf(stream, "%s    %s Field [%d]:\n"
249           "%s      Name: \"%s\"\n",
250           pre, inout, fieldnumber, pre, qffield->fieldname);
251 
252   if (field->basis == CEED_BASIS_COLLOCATED)
253     fprintf(stream, "%s      Collocated basis\n", pre);
254 
255   if (field->vec == CEED_VECTOR_ACTIVE)
256     fprintf(stream, "%s      Active vector\n", pre);
257   else if (field->vec == CEED_VECTOR_NONE)
258     fprintf(stream, "%s      No vector\n", pre);
259   return CEED_ERROR_SUCCESS;
260 }
261 
262 /**
263   @brief View a single CeedOperator
264 
265   @param[in] op     CeedOperator to view
266   @param[in] sub    Boolean flag for sub-operator
267   @param[in] stream Stream to write; typically stdout/stderr or a file
268 
269   @return Error code: 0 - success, otherwise - failure
270 
271   @ref Utility
272 **/
273 int CeedOperatorSingleView(CeedOperator op, bool sub, FILE *stream) {
274   int ierr;
275   const char *pre = sub ? "  " : "";
276 
277   CeedInt totalfields;
278   ierr = CeedOperatorGetNumArgs(op, &totalfields); CeedChk(ierr);
279 
280   fprintf(stream, "%s  %d Field%s\n", pre, totalfields, totalfields>1 ? "s" : "");
281 
282   fprintf(stream, "%s  %d Input Field%s:\n", pre, op->qf->numinputfields,
283           op->qf->numinputfields>1 ? "s" : "");
284   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
285     ierr = CeedOperatorFieldView(op->inputfields[i], op->qf->inputfields[i],
286                                  i, sub, 1, stream); CeedChk(ierr);
287   }
288 
289   fprintf(stream, "%s  %d Output Field%s:\n", pre, op->qf->numoutputfields,
290           op->qf->numoutputfields>1 ? "s" : "");
291   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
292     ierr = CeedOperatorFieldView(op->outputfields[i], op->qf->outputfields[i],
293                                  i, sub, 0, stream); CeedChk(ierr);
294   }
295   return CEED_ERROR_SUCCESS;
296 }
297 
298 /**
299   @brief Find the active vector basis for a CeedOperator
300 
301   @param[in] op            CeedOperator to find active basis for
302   @param[out] activeBasis  Basis for active input vector
303 
304   @return An error code: 0 - success, otherwise - failure
305 
306   @ ref Developer
307 **/
308 static int CeedOperatorGetActiveBasis(CeedOperator op,
309                                       CeedBasis *activeBasis) {
310   *activeBasis = NULL;
311   for (int i = 0; i < op->qf->numinputfields; i++)
312     if (op->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
313       *activeBasis = op->inputfields[i]->basis;
314       break;
315     }
316 
317   if (!*activeBasis) {
318     // LCOV_EXCL_START
319     int ierr;
320     Ceed ceed;
321     ierr = CeedOperatorGetCeed(op, &ceed); CeedChk(ierr);
322     return CeedError(ceed, CEED_ERROR_MINOR,
323                      "No active basis found for automatic multigrid setup");
324     // LCOV_EXCL_STOP
325   }
326   return CEED_ERROR_SUCCESS;
327 }
328 
329 
330 /**
331   @brief Common code for creating a multigrid coarse operator and level
332            transfer operators for a CeedOperator
333 
334   @param[in] opFine       Fine grid operator
335   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
336   @param[in] rstrCoarse   Coarse grid restriction
337   @param[in] basisCoarse  Coarse grid active vector basis
338   @param[in] basisCtoF    Basis for coarse to fine interpolation
339   @param[out] opCoarse    Coarse grid operator
340   @param[out] opProlong   Coarse to fine operator
341   @param[out] opRestrict  Fine to coarse operator
342 
343   @return An error code: 0 - success, otherwise - failure
344 
345   @ref Developer
346 **/
347 static int CeedOperatorMultigridLevel_Core(CeedOperator opFine,
348     CeedVector PMultFine, CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
349     CeedBasis basisCtoF, CeedOperator *opCoarse, CeedOperator *opProlong,
350     CeedOperator *opRestrict) {
351   int ierr;
352   Ceed ceed;
353   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
354 
355   // Check for composite operator
356   bool isComposite;
357   ierr = CeedOperatorIsComposite(opFine, &isComposite); CeedChk(ierr);
358   if (isComposite)
359     // LCOV_EXCL_START
360     return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
361                      "Automatic multigrid setup for composite operators not supported");
362   // LCOV_EXCL_STOP
363 
364   // Coarse Grid
365   ierr = CeedOperatorCreate(ceed, opFine->qf, opFine->dqf, opFine->dqfT,
366                             opCoarse); CeedChk(ierr);
367   CeedElemRestriction rstrFine = NULL;
368   // -- Clone input fields
369   for (int i = 0; i < opFine->qf->numinputfields; i++) {
370     if (opFine->inputfields[i]->vec == CEED_VECTOR_ACTIVE) {
371       rstrFine = opFine->inputfields[i]->Erestrict;
372       ierr = CeedOperatorSetField(*opCoarse, opFine->inputfields[i]->fieldname,
373                                   rstrCoarse, basisCoarse, CEED_VECTOR_ACTIVE);
374       CeedChk(ierr);
375     } else {
376       ierr = CeedOperatorSetField(*opCoarse, opFine->inputfields[i]->fieldname,
377                                   opFine->inputfields[i]->Erestrict,
378                                   opFine->inputfields[i]->basis,
379                                   opFine->inputfields[i]->vec); CeedChk(ierr);
380     }
381   }
382   // -- Clone output fields
383   for (int i = 0; i < opFine->qf->numoutputfields; i++) {
384     if (opFine->outputfields[i]->vec == CEED_VECTOR_ACTIVE) {
385       ierr = CeedOperatorSetField(*opCoarse, opFine->outputfields[i]->fieldname,
386                                   rstrCoarse, basisCoarse, CEED_VECTOR_ACTIVE);
387       CeedChk(ierr);
388     } else {
389       ierr = CeedOperatorSetField(*opCoarse, opFine->outputfields[i]->fieldname,
390                                   opFine->outputfields[i]->Erestrict,
391                                   opFine->outputfields[i]->basis,
392                                   opFine->outputfields[i]->vec); CeedChk(ierr);
393     }
394   }
395 
396   // Multiplicity vector
397   CeedVector multVec, multE;
398   ierr = CeedElemRestrictionCreateVector(rstrFine, &multVec, &multE);
399   CeedChk(ierr);
400   ierr = CeedVectorSetValue(multE, 0.0); CeedChk(ierr);
401   ierr = CeedElemRestrictionApply(rstrFine, CEED_NOTRANSPOSE, PMultFine, multE,
402                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
403   ierr = CeedVectorSetValue(multVec, 0.0); CeedChk(ierr);
404   ierr = CeedElemRestrictionApply(rstrFine, CEED_TRANSPOSE, multE, multVec,
405                                   CEED_REQUEST_IMMEDIATE); CeedChk(ierr);
406   ierr = CeedVectorDestroy(&multE); CeedChk(ierr);
407   ierr = CeedVectorReciprocal(multVec); CeedChk(ierr);
408 
409   // Restriction
410   CeedInt ncomp;
411   ierr = CeedBasisGetNumComponents(basisCoarse, &ncomp); CeedChk(ierr);
412   CeedQFunction qfRestrict;
413   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qfRestrict);
414   CeedChk(ierr);
415   CeedInt *ncompRData;
416   ierr = CeedCalloc(1, &ncompRData); CeedChk(ierr);
417   ncompRData[0] = ncomp;
418   CeedQFunctionContext ctxR;
419   ierr = CeedQFunctionContextCreate(ceed, &ctxR); CeedChk(ierr);
420   ierr = CeedQFunctionContextSetData(ctxR, CEED_MEM_HOST, CEED_OWN_POINTER,
421                                      sizeof(*ncompRData), ncompRData);
422   CeedChk(ierr);
423   ierr = CeedQFunctionSetContext(qfRestrict, ctxR); CeedChk(ierr);
424   ierr = CeedQFunctionContextDestroy(&ctxR); CeedChk(ierr);
425   ierr = CeedQFunctionAddInput(qfRestrict, "input", ncomp, CEED_EVAL_NONE);
426   CeedChk(ierr);
427   ierr = CeedQFunctionAddInput(qfRestrict, "scale", ncomp, CEED_EVAL_NONE);
428   CeedChk(ierr);
429   ierr = CeedQFunctionAddOutput(qfRestrict, "output", ncomp, CEED_EVAL_INTERP);
430   CeedChk(ierr);
431 
432   ierr = CeedOperatorCreate(ceed, qfRestrict, CEED_QFUNCTION_NONE,
433                             CEED_QFUNCTION_NONE, opRestrict);
434   CeedChk(ierr);
435   ierr = CeedOperatorSetField(*opRestrict, "input", rstrFine,
436                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
437   CeedChk(ierr);
438   ierr = CeedOperatorSetField(*opRestrict, "scale", rstrFine,
439                               CEED_BASIS_COLLOCATED, multVec);
440   CeedChk(ierr);
441   ierr = CeedOperatorSetField(*opRestrict, "output", rstrCoarse, basisCtoF,
442                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
443 
444   // Prolongation
445   CeedQFunction qfProlong;
446   ierr = CeedQFunctionCreateInteriorByName(ceed, "Scale", &qfProlong);
447   CeedChk(ierr);
448   CeedInt *ncompPData;
449   ierr = CeedCalloc(1, &ncompPData); CeedChk(ierr);
450   ncompPData[0] = ncomp;
451   CeedQFunctionContext ctxP;
452   ierr = CeedQFunctionContextCreate(ceed, &ctxP); CeedChk(ierr);
453   ierr = CeedQFunctionContextSetData(ctxP, CEED_MEM_HOST, CEED_OWN_POINTER,
454                                      sizeof(*ncompPData), ncompPData);
455   CeedChk(ierr);
456   ierr = CeedQFunctionSetContext(qfProlong, ctxP); CeedChk(ierr);
457   ierr = CeedQFunctionContextDestroy(&ctxP); CeedChk(ierr);
458   ierr = CeedQFunctionAddInput(qfProlong, "input", ncomp, CEED_EVAL_INTERP);
459   CeedChk(ierr);
460   ierr = CeedQFunctionAddInput(qfProlong, "scale", ncomp, CEED_EVAL_NONE);
461   CeedChk(ierr);
462   ierr = CeedQFunctionAddOutput(qfProlong, "output", ncomp, CEED_EVAL_NONE);
463   CeedChk(ierr);
464 
465   ierr = CeedOperatorCreate(ceed, qfProlong, CEED_QFUNCTION_NONE,
466                             CEED_QFUNCTION_NONE, opProlong);
467   CeedChk(ierr);
468   ierr = CeedOperatorSetField(*opProlong, "input", rstrCoarse, basisCtoF,
469                               CEED_VECTOR_ACTIVE); CeedChk(ierr);
470   ierr = CeedOperatorSetField(*opProlong, "scale", rstrFine,
471                               CEED_BASIS_COLLOCATED, multVec);
472   CeedChk(ierr);
473   ierr = CeedOperatorSetField(*opProlong, "output", rstrFine,
474                               CEED_BASIS_COLLOCATED, CEED_VECTOR_ACTIVE);
475   CeedChk(ierr);
476 
477   // Cleanup
478   ierr = CeedVectorDestroy(&multVec); CeedChk(ierr);
479   ierr = CeedBasisDestroy(&basisCtoF); CeedChk(ierr);
480   ierr = CeedQFunctionDestroy(&qfRestrict); CeedChk(ierr);
481   ierr = CeedQFunctionDestroy(&qfProlong); CeedChk(ierr);
482   return CEED_ERROR_SUCCESS;
483 }
484 
485 /// @}
486 
487 /// ----------------------------------------------------------------------------
488 /// CeedOperator Backend API
489 /// ----------------------------------------------------------------------------
490 /// @addtogroup CeedOperatorBackend
491 /// @{
492 
493 /**
494   @brief Get the Ceed associated with a CeedOperator
495 
496   @param op              CeedOperator
497   @param[out] ceed       Variable to store Ceed
498 
499   @return An error code: 0 - success, otherwise - failure
500 
501   @ref Backend
502 **/
503 
504 int CeedOperatorGetCeed(CeedOperator op, Ceed *ceed) {
505   *ceed = op->ceed;
506   return CEED_ERROR_SUCCESS;
507 }
508 
509 /**
510   @brief Get the number of elements associated with a CeedOperator
511 
512   @param op              CeedOperator
513   @param[out] numelem    Variable to store number of elements
514 
515   @return An error code: 0 - success, otherwise - failure
516 
517   @ref Backend
518 **/
519 
520 int CeedOperatorGetNumElements(CeedOperator op, CeedInt *numelem) {
521   if (op->composite)
522     // LCOV_EXCL_START
523     return CeedError(op->ceed, CEED_ERROR_MINOR,
524                      "Not defined for composite operator");
525   // LCOV_EXCL_STOP
526 
527   *numelem = op->numelements;
528   return CEED_ERROR_SUCCESS;
529 }
530 
531 /**
532   @brief Get the number of quadrature points associated with a CeedOperator
533 
534   @param op              CeedOperator
535   @param[out] numqpts    Variable to store vector number of quadrature points
536 
537   @return An error code: 0 - success, otherwise - failure
538 
539   @ref Backend
540 **/
541 
542 int CeedOperatorGetNumQuadraturePoints(CeedOperator op, CeedInt *numqpts) {
543   if (op->composite)
544     // LCOV_EXCL_START
545     return CeedError(op->ceed, CEED_ERROR_MINOR,
546                      "Not defined for composite operator");
547   // LCOV_EXCL_STOP
548 
549   *numqpts = op->numqpoints;
550   return CEED_ERROR_SUCCESS;
551 }
552 
553 /**
554   @brief Get the number of arguments associated with a CeedOperator
555 
556   @param op              CeedOperator
557   @param[out] numargs    Variable to store vector number of arguments
558 
559   @return An error code: 0 - success, otherwise - failure
560 
561   @ref Backend
562 **/
563 
564 int CeedOperatorGetNumArgs(CeedOperator op, CeedInt *numargs) {
565   if (op->composite)
566     // LCOV_EXCL_START
567     return CeedError(op->ceed, CEED_ERROR_MINOR,
568                      "Not defined for composite operators");
569   // LCOV_EXCL_STOP
570 
571   *numargs = op->nfields;
572   return CEED_ERROR_SUCCESS;
573 }
574 
575 /**
576   @brief Get the setup status of a CeedOperator
577 
578   @param op                CeedOperator
579   @param[out] issetupdone  Variable to store setup status
580 
581   @return An error code: 0 - success, otherwise - failure
582 
583   @ref Backend
584 **/
585 
586 int CeedOperatorIsSetupDone(CeedOperator op, bool *issetupdone) {
587   *issetupdone = op->backendsetup;
588   return CEED_ERROR_SUCCESS;
589 }
590 
591 /**
592   @brief Get the QFunction associated with a CeedOperator
593 
594   @param op              CeedOperator
595   @param[out] qf         Variable to store QFunction
596 
597   @return An error code: 0 - success, otherwise - failure
598 
599   @ref Backend
600 **/
601 
602 int CeedOperatorGetQFunction(CeedOperator op, CeedQFunction *qf) {
603   if (op->composite)
604     // LCOV_EXCL_START
605     return CeedError(op->ceed, CEED_ERROR_MINOR,
606                      "Not defined for composite operator");
607   // LCOV_EXCL_STOP
608 
609   *qf = op->qf;
610   return CEED_ERROR_SUCCESS;
611 }
612 
613 /**
614   @brief Get a boolean value indicating if the CeedOperator is composite
615 
616   @param op                CeedOperator
617   @param[out] iscomposite  Variable to store composite status
618 
619   @return An error code: 0 - success, otherwise - failure
620 
621   @ref Backend
622 **/
623 
624 int CeedOperatorIsComposite(CeedOperator op, bool *iscomposite) {
625   *iscomposite = op->composite;
626   return CEED_ERROR_SUCCESS;
627 }
628 
629 /**
630   @brief Get the number of suboperators associated with a CeedOperator
631 
632   @param op              CeedOperator
633   @param[out] numsub     Variable to store number of suboperators
634 
635   @return An error code: 0 - success, otherwise - failure
636 
637   @ref Backend
638 **/
639 
640 int CeedOperatorGetNumSub(CeedOperator op, CeedInt *numsub) {
641   if (!op->composite)
642     // LCOV_EXCL_START
643     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
644   // LCOV_EXCL_STOP
645 
646   *numsub = op->numsub;
647   return CEED_ERROR_SUCCESS;
648 }
649 
650 /**
651   @brief Get the list of suboperators associated with a CeedOperator
652 
653   @param op                CeedOperator
654   @param[out] suboperators Variable to store list of suboperators
655 
656   @return An error code: 0 - success, otherwise - failure
657 
658   @ref Backend
659 **/
660 
661 int CeedOperatorGetSubList(CeedOperator op, CeedOperator **suboperators) {
662   if (!op->composite)
663     // LCOV_EXCL_START
664     return CeedError(op->ceed, CEED_ERROR_MINOR, "Not a composite operator");
665   // LCOV_EXCL_STOP
666 
667   *suboperators = op->suboperators;
668   return CEED_ERROR_SUCCESS;
669 }
670 
671 /**
672   @brief Get the backend data of a CeedOperator
673 
674   @param op              CeedOperator
675   @param[out] data       Variable to store data
676 
677   @return An error code: 0 - success, otherwise - failure
678 
679   @ref Backend
680 **/
681 
682 int CeedOperatorGetData(CeedOperator op, void *data) {
683   *(void **)data = op->data;
684   return CEED_ERROR_SUCCESS;
685 }
686 
687 /**
688   @brief Set the backend data of a CeedOperator
689 
690   @param[out] op         CeedOperator
691   @param data            Data to set
692 
693   @return An error code: 0 - success, otherwise - failure
694 
695   @ref Backend
696 **/
697 
698 int CeedOperatorSetData(CeedOperator op, void *data) {
699   op->data = data;
700   return CEED_ERROR_SUCCESS;
701 }
702 
703 /**
704   @brief Set the setup flag of a CeedOperator to True
705 
706   @param op              CeedOperator
707 
708   @return An error code: 0 - success, otherwise - failure
709 
710   @ref Backend
711 **/
712 
713 int CeedOperatorSetSetupDone(CeedOperator op) {
714   op->backendsetup = true;
715   return CEED_ERROR_SUCCESS;
716 }
717 
718 /**
719   @brief Get the CeedOperatorFields of a CeedOperator
720 
721   @param op                 CeedOperator
722   @param[out] inputfields   Variable to store inputfields
723   @param[out] outputfields  Variable to store outputfields
724 
725   @return An error code: 0 - success, otherwise - failure
726 
727   @ref Backend
728 **/
729 
730 int CeedOperatorGetFields(CeedOperator op, CeedOperatorField **inputfields,
731                           CeedOperatorField **outputfields) {
732   if (op->composite)
733     // LCOV_EXCL_START
734     return CeedError(op->ceed, CEED_ERROR_MINOR,
735                      "Not defined for composite operator");
736   // LCOV_EXCL_STOP
737 
738   if (inputfields) *inputfields = op->inputfields;
739   if (outputfields) *outputfields = op->outputfields;
740   return CEED_ERROR_SUCCESS;
741 }
742 
743 /**
744   @brief Get the CeedElemRestriction of a CeedOperatorField
745 
746   @param opfield         CeedOperatorField
747   @param[out] rstr       Variable to store CeedElemRestriction
748 
749   @return An error code: 0 - success, otherwise - failure
750 
751   @ref Backend
752 **/
753 
754 int CeedOperatorFieldGetElemRestriction(CeedOperatorField opfield,
755                                         CeedElemRestriction *rstr) {
756   *rstr = opfield->Erestrict;
757   return CEED_ERROR_SUCCESS;
758 }
759 
760 /**
761   @brief Get the CeedBasis of a CeedOperatorField
762 
763   @param opfield         CeedOperatorField
764   @param[out] basis      Variable to store CeedBasis
765 
766   @return An error code: 0 - success, otherwise - failure
767 
768   @ref Backend
769 **/
770 
771 int CeedOperatorFieldGetBasis(CeedOperatorField opfield, CeedBasis *basis) {
772   *basis = opfield->basis;
773   return CEED_ERROR_SUCCESS;
774 }
775 
776 /**
777   @brief Get the CeedVector of a CeedOperatorField
778 
779   @param opfield         CeedOperatorField
780   @param[out] vec        Variable to store CeedVector
781 
782   @return An error code: 0 - success, otherwise - failure
783 
784   @ref Backend
785 **/
786 
787 int CeedOperatorFieldGetVector(CeedOperatorField opfield, CeedVector *vec) {
788   *vec = opfield->vec;
789   return CEED_ERROR_SUCCESS;
790 }
791 
792 /// @}
793 
794 /// ----------------------------------------------------------------------------
795 /// CeedOperator Public API
796 /// ----------------------------------------------------------------------------
797 /// @addtogroup CeedOperatorUser
798 /// @{
799 
800 /**
801   @brief Create a CeedOperator and associate a CeedQFunction. A CeedBasis and
802            CeedElemRestriction can be associated with CeedQFunction fields with
803            \ref CeedOperatorSetField.
804 
805   @param ceed    A Ceed object where the CeedOperator will be created
806   @param qf      QFunction defining the action of the operator at quadrature points
807   @param dqf     QFunction defining the action of the Jacobian of @a qf (or
808                    @ref CEED_QFUNCTION_NONE)
809   @param dqfT    QFunction defining the action of the transpose of the Jacobian
810                    of @a qf (or @ref CEED_QFUNCTION_NONE)
811   @param[out] op Address of the variable where the newly created
812                      CeedOperator will be stored
813 
814   @return An error code: 0 - success, otherwise - failure
815 
816   @ref User
817  */
818 int CeedOperatorCreate(Ceed ceed, CeedQFunction qf, CeedQFunction dqf,
819                        CeedQFunction dqfT, CeedOperator *op) {
820   int ierr;
821 
822   if (!ceed->OperatorCreate) {
823     Ceed delegate;
824     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
825 
826     if (!delegate)
827       // LCOV_EXCL_START
828       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
829                        "Backend does not support OperatorCreate");
830     // LCOV_EXCL_STOP
831 
832     ierr = CeedOperatorCreate(delegate, qf, dqf, dqfT, op); CeedChk(ierr);
833     return CEED_ERROR_SUCCESS;
834   }
835 
836   if (!qf || qf == CEED_QFUNCTION_NONE)
837     // LCOV_EXCL_START
838     return CeedError(ceed, CEED_ERROR_MINOR,
839                      "Operator must have a valid QFunction.");
840   // LCOV_EXCL_STOP
841   ierr = CeedCalloc(1, op); CeedChk(ierr);
842   (*op)->ceed = ceed;
843   ceed->refcount++;
844   (*op)->refcount = 1;
845   (*op)->qf = qf;
846   qf->refcount++;
847   if (dqf && dqf != CEED_QFUNCTION_NONE) {
848     (*op)->dqf = dqf;
849     dqf->refcount++;
850   }
851   if (dqfT && dqfT != CEED_QFUNCTION_NONE) {
852     (*op)->dqfT = dqfT;
853     dqfT->refcount++;
854   }
855   ierr = CeedCalloc(16, &(*op)->inputfields); CeedChk(ierr);
856   ierr = CeedCalloc(16, &(*op)->outputfields); CeedChk(ierr);
857   ierr = ceed->OperatorCreate(*op); CeedChk(ierr);
858   return CEED_ERROR_SUCCESS;
859 }
860 
861 /**
862   @brief Create an operator that composes the action of several operators
863 
864   @param ceed    A Ceed object where the CeedOperator will be created
865   @param[out] op Address of the variable where the newly created
866                      Composite CeedOperator will be stored
867 
868   @return An error code: 0 - success, otherwise - failure
869 
870   @ref User
871  */
872 int CeedCompositeOperatorCreate(Ceed ceed, CeedOperator *op) {
873   int ierr;
874 
875   if (!ceed->CompositeOperatorCreate) {
876     Ceed delegate;
877     ierr = CeedGetObjectDelegate(ceed, &delegate, "Operator"); CeedChk(ierr);
878 
879     if (delegate) {
880       ierr = CeedCompositeOperatorCreate(delegate, op); CeedChk(ierr);
881       return CEED_ERROR_SUCCESS;
882     }
883   }
884 
885   ierr = CeedCalloc(1, op); CeedChk(ierr);
886   (*op)->ceed = ceed;
887   ceed->refcount++;
888   (*op)->composite = true;
889   ierr = CeedCalloc(16, &(*op)->suboperators); CeedChk(ierr);
890 
891   if (ceed->CompositeOperatorCreate) {
892     ierr = ceed->CompositeOperatorCreate(*op); CeedChk(ierr);
893   }
894   return CEED_ERROR_SUCCESS;
895 }
896 
897 /**
898   @brief Provide a field to a CeedOperator for use by its CeedQFunction
899 
900   This function is used to specify both active and passive fields to a
901   CeedOperator.  For passive fields, a vector @arg v must be provided.  Passive
902   fields can inputs or outputs (updated in-place when operator is applied).
903 
904   Active fields must be specified using this function, but their data (in a
905   CeedVector) is passed in CeedOperatorApply().  There can be at most one active
906   input and at most one active output.
907 
908   @param op         CeedOperator on which to provide the field
909   @param fieldname  Name of the field (to be matched with the name used by
910                       CeedQFunction)
911   @param r          CeedElemRestriction
912   @param b          CeedBasis in which the field resides or @ref CEED_BASIS_COLLOCATED
913                       if collocated with quadrature points
914   @param v          CeedVector to be used by CeedOperator or @ref CEED_VECTOR_ACTIVE
915                       if field is active or @ref CEED_VECTOR_NONE if using
916                       @ref CEED_EVAL_WEIGHT in the QFunction
917 
918   @return An error code: 0 - success, otherwise - failure
919 
920   @ref User
921 **/
922 int CeedOperatorSetField(CeedOperator op, const char *fieldname,
923                          CeedElemRestriction r, CeedBasis b, CeedVector v) {
924   int ierr;
925   if (op->composite)
926     // LCOV_EXCL_START
927     return CeedError(op->ceed, CEED_ERROR_MINOR,
928                      "Cannot add field to composite operator.");
929   // LCOV_EXCL_STOP
930   if (!r)
931     // LCOV_EXCL_START
932     return CeedError(op->ceed, CEED_ERROR_MINOR,
933                      "ElemRestriction r for field \"%s\" must be non-NULL.",
934                      fieldname);
935   // LCOV_EXCL_STOP
936   if (!b)
937     // LCOV_EXCL_START
938     return CeedError(op->ceed, CEED_ERROR_MINOR,
939                      "Basis b for field \"%s\" must be non-NULL.",
940                      fieldname);
941   // LCOV_EXCL_STOP
942   if (!v)
943     // LCOV_EXCL_START
944     return CeedError(op->ceed, CEED_ERROR_MINOR,
945                      "Vector v for field \"%s\" must be non-NULL.",
946                      fieldname);
947   // LCOV_EXCL_STOP
948 
949   CeedInt numelements;
950   ierr = CeedElemRestrictionGetNumElements(r, &numelements); CeedChk(ierr);
951   if (r != CEED_ELEMRESTRICTION_NONE && op->hasrestriction &&
952       op->numelements != numelements)
953     // LCOV_EXCL_START
954     return CeedError(op->ceed, CEED_ERROR_DIMENSION,
955                      "ElemRestriction with %d elements incompatible with prior "
956                      "%d elements", numelements, op->numelements);
957   // LCOV_EXCL_STOP
958 
959   CeedInt numqpoints;
960   if (b != CEED_BASIS_COLLOCATED) {
961     ierr = CeedBasisGetNumQuadraturePoints(b, &numqpoints); CeedChk(ierr);
962     if (op->numqpoints && op->numqpoints != numqpoints)
963       // LCOV_EXCL_START
964       return CeedError(op->ceed, CEED_ERROR_DIMENSION,
965                        "Basis with %d quadrature points "
966                        "incompatible with prior %d points", numqpoints,
967                        op->numqpoints);
968     // LCOV_EXCL_STOP
969   }
970   CeedQFunctionField qfield;
971   CeedOperatorField *ofield;
972   for (CeedInt i=0; i<op->qf->numinputfields; i++) {
973     if (!strcmp(fieldname, (*op->qf->inputfields[i]).fieldname)) {
974       qfield = op->qf->inputfields[i];
975       ofield = &op->inputfields[i];
976       goto found;
977     }
978   }
979   for (CeedInt i=0; i<op->qf->numoutputfields; i++) {
980     if (!strcmp(fieldname, (*op->qf->outputfields[i]).fieldname)) {
981       qfield = op->qf->outputfields[i];
982       ofield = &op->outputfields[i];
983       goto found;
984     }
985   }
986   // LCOV_EXCL_START
987   return CeedError(op->ceed, CEED_ERROR_INCOMPLETE,
988                    "QFunction has no knowledge of field '%s'",
989                    fieldname);
990   // LCOV_EXCL_STOP
991 found:
992   ierr = CeedOperatorCheckField(op->ceed, qfield, r, b); CeedChk(ierr);
993   ierr = CeedCalloc(1, ofield); CeedChk(ierr);
994 
995   (*ofield)->vec = v;
996   if (v != CEED_VECTOR_ACTIVE && v != CEED_VECTOR_NONE) {
997     v->refcount += 1;
998   }
999 
1000   (*ofield)->Erestrict = r;
1001   r->refcount += 1;
1002   if (r != CEED_ELEMRESTRICTION_NONE) {
1003     op->numelements = numelements;
1004     op->hasrestriction = true; // Restriction set, but numelements may be 0
1005   }
1006 
1007   (*ofield)->basis = b;
1008   if (b != CEED_BASIS_COLLOCATED) {
1009     op->numqpoints = numqpoints;
1010     b->refcount += 1;
1011   }
1012 
1013   op->nfields += 1;
1014   size_t len = strlen(fieldname);
1015   char *tmp;
1016   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
1017   memcpy(tmp, fieldname, len+1);
1018   (*ofield)->fieldname = tmp;
1019   return CEED_ERROR_SUCCESS;
1020 }
1021 
1022 /**
1023   @brief Add a sub-operator to a composite CeedOperator
1024 
1025   @param[out] compositeop Composite CeedOperator
1026   @param      subop       Sub-operator CeedOperator
1027 
1028   @return An error code: 0 - success, otherwise - failure
1029 
1030   @ref User
1031  */
1032 int CeedCompositeOperatorAddSub(CeedOperator compositeop, CeedOperator subop) {
1033   if (!compositeop->composite)
1034     // LCOV_EXCL_START
1035     return CeedError(compositeop->ceed, CEED_ERROR_MINOR,
1036                      "CeedOperator is not a composite "
1037                      "operator");
1038   // LCOV_EXCL_STOP
1039 
1040   if (compositeop->numsub == CEED_COMPOSITE_MAX)
1041     // LCOV_EXCL_START
1042     return CeedError(compositeop->ceed, CEED_ERROR_UNSUPPORTED,
1043                      "Cannot add additional suboperators");
1044   // LCOV_EXCL_STOP
1045 
1046   compositeop->suboperators[compositeop->numsub] = subop;
1047   subop->refcount++;
1048   compositeop->numsub++;
1049   return CEED_ERROR_SUCCESS;
1050 }
1051 
1052 /**
1053   @brief Assemble a linear CeedQFunction associated with a CeedOperator
1054 
1055   This returns a CeedVector containing a matrix at each quadrature point
1056     providing the action of the CeedQFunction associated with the CeedOperator.
1057     The vector 'assembled' is of shape
1058       [num_elements, num_input_fields, num_output_fields, num_quad_points]
1059     and contains column-major matrices representing the action of the
1060     CeedQFunction for a corresponding quadrature point on an element. Inputs and
1061     outputs are in the order provided by the user when adding CeedOperator fields.
1062     For example, a CeedQFunction with inputs 'u' and 'gradu' and outputs 'gradv' and
1063     'v', provided in that order, would result in an assembled QFunction that
1064     consists of (1 + dim) x (dim + 1) matrices at each quadrature point acting
1065     on the input [u, du_0, du_1] and producing the output [dv_0, dv_1, v].
1066 
1067   @param op             CeedOperator to assemble CeedQFunction
1068   @param[out] assembled CeedVector to store assembled CeedQFunction at
1069                           quadrature points
1070   @param[out] rstr      CeedElemRestriction for CeedVector containing assembled
1071                           CeedQFunction
1072   @param request        Address of CeedRequest for non-blocking completion, else
1073                           @ref CEED_REQUEST_IMMEDIATE
1074 
1075   @return An error code: 0 - success, otherwise - failure
1076 
1077   @ref User
1078 **/
1079 int CeedOperatorLinearAssembleQFunction(CeedOperator op, CeedVector *assembled,
1080                                         CeedElemRestriction *rstr,
1081                                         CeedRequest *request) {
1082   int ierr;
1083   Ceed ceed = op->ceed;
1084   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1085 
1086   // Backend version
1087   if (op->LinearAssembleQFunction) {
1088     ierr = op->LinearAssembleQFunction(op, assembled, rstr, request);
1089     CeedChk(ierr);
1090   } else {
1091     // Fallback to reference Ceed
1092     if (!op->opfallback) {
1093       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1094     }
1095     // Assemble
1096     ierr = op->opfallback->LinearAssembleQFunction(op->opfallback, assembled,
1097            rstr, request); CeedChk(ierr);
1098   }
1099   return CEED_ERROR_SUCCESS;
1100 }
1101 
1102 /**
1103   @brief Assemble the diagonal of a square linear CeedOperator
1104 
1105   This overwrites a CeedVector with the diagonal of a linear CeedOperator.
1106 
1107   Note: Currently only non-composite CeedOperators with a single field and
1108           composite CeedOperators with single field sub-operators are supported.
1109 
1110   @param op             CeedOperator to assemble CeedQFunction
1111   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1112   @param request        Address of CeedRequest for non-blocking completion, else
1113                           @ref CEED_REQUEST_IMMEDIATE
1114 
1115   @return An error code: 0 - success, otherwise - failure
1116 
1117   @ref User
1118 **/
1119 int CeedOperatorLinearAssembleDiagonal(CeedOperator op, CeedVector assembled,
1120                                        CeedRequest *request) {
1121   int ierr;
1122   Ceed ceed = op->ceed;
1123   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1124 
1125   // Use backend version, if available
1126   if (op->LinearAssembleDiagonal) {
1127     ierr = op->LinearAssembleDiagonal(op, assembled, request); CeedChk(ierr);
1128   } else if (op->LinearAssembleAddDiagonal) {
1129     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1130     return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1131   } else {
1132     // Fallback to reference Ceed
1133     if (!op->opfallback) {
1134       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1135     }
1136     // Assemble
1137     if (op->opfallback->LinearAssembleDiagonal) {
1138       ierr = op->opfallback->LinearAssembleDiagonal(op->opfallback, assembled,
1139              request); CeedChk(ierr);
1140     } else {
1141       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1142       return CeedOperatorLinearAssembleAddDiagonal(op, assembled, request);
1143     }
1144   }
1145   return CEED_ERROR_SUCCESS;
1146 }
1147 
1148 /**
1149   @brief Assemble the diagonal of a square linear CeedOperator
1150 
1151   This sums into a CeedVector the diagonal of a linear CeedOperator.
1152 
1153   Note: Currently only non-composite CeedOperators with a single field and
1154           composite CeedOperators with single field sub-operators are supported.
1155 
1156   @param op             CeedOperator to assemble CeedQFunction
1157   @param[out] assembled CeedVector to store assembled CeedOperator diagonal
1158   @param request        Address of CeedRequest for non-blocking completion, else
1159                           @ref CEED_REQUEST_IMMEDIATE
1160 
1161   @return An error code: 0 - success, otherwise - failure
1162 
1163   @ref User
1164 **/
1165 int CeedOperatorLinearAssembleAddDiagonal(CeedOperator op, CeedVector assembled,
1166     CeedRequest *request) {
1167   int ierr;
1168   Ceed ceed = op->ceed;
1169   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1170 
1171   // Use backend version, if available
1172   if (op->LinearAssembleAddDiagonal) {
1173     ierr = op->LinearAssembleAddDiagonal(op, assembled, request); CeedChk(ierr);
1174   } else {
1175     // Fallback to reference Ceed
1176     if (!op->opfallback) {
1177       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1178     }
1179     // Assemble
1180     ierr = op->opfallback->LinearAssembleAddDiagonal(op->opfallback, assembled,
1181            request); CeedChk(ierr);
1182   }
1183   return CEED_ERROR_SUCCESS;
1184 }
1185 
1186 /**
1187   @brief Assemble the point block diagonal of a square linear CeedOperator
1188 
1189   This overwrites a CeedVector with the point block diagonal of a linear
1190     CeedOperator.
1191 
1192   Note: Currently only non-composite CeedOperators with a single field and
1193           composite CeedOperators with single field sub-operators are supported.
1194 
1195   @param op             CeedOperator to assemble CeedQFunction
1196   @param[out] assembled CeedVector to store assembled CeedOperator point block
1197                           diagonal, provided in row-major form with an
1198                           @a ncomp * @a ncomp block at each node. The dimensions
1199                           of this vector are derived from the active vector
1200                           for the CeedOperator. The array has shape
1201                           [nodes, component out, component in].
1202   @param request        Address of CeedRequest for non-blocking completion, else
1203                           CEED_REQUEST_IMMEDIATE
1204 
1205   @return An error code: 0 - success, otherwise - failure
1206 
1207   @ref User
1208 **/
1209 int CeedOperatorLinearAssemblePointBlockDiagonal(CeedOperator op,
1210     CeedVector assembled, CeedRequest *request) {
1211   int ierr;
1212   Ceed ceed = op->ceed;
1213   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1214 
1215   // Use backend version, if available
1216   if (op->LinearAssemblePointBlockDiagonal) {
1217     ierr = op->LinearAssemblePointBlockDiagonal(op, assembled, request);
1218     CeedChk(ierr);
1219   } else if (op->LinearAssembleAddPointBlockDiagonal) {
1220     ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1221     return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1222            request);
1223   } else {
1224     // Fallback to reference Ceed
1225     if (!op->opfallback) {
1226       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1227     }
1228     // Assemble
1229     if (op->opfallback->LinearAssemblePointBlockDiagonal) {
1230       ierr = op->opfallback->LinearAssemblePointBlockDiagonal(op->opfallback,
1231              assembled, request); CeedChk(ierr);
1232     } else {
1233       ierr = CeedVectorSetValue(assembled, 0.0); CeedChk(ierr);
1234       return CeedOperatorLinearAssembleAddPointBlockDiagonal(op, assembled,
1235              request);
1236     }
1237   }
1238   return CEED_ERROR_SUCCESS;
1239 }
1240 
1241 /**
1242   @brief Assemble the point block diagonal of a square linear CeedOperator
1243 
1244   This sums into a CeedVector with the point block diagonal of a linear
1245     CeedOperator.
1246 
1247   Note: Currently only non-composite CeedOperators with a single field and
1248           composite CeedOperators with single field sub-operators are supported.
1249 
1250   @param op             CeedOperator to assemble CeedQFunction
1251   @param[out] assembled CeedVector to store assembled CeedOperator point block
1252                           diagonal, provided in row-major form with an
1253                           @a ncomp * @a ncomp block at each node. The dimensions
1254                           of this vector are derived from the active vector
1255                           for the CeedOperator. The array has shape
1256                           [nodes, component out, component in].
1257   @param request        Address of CeedRequest for non-blocking completion, else
1258                           CEED_REQUEST_IMMEDIATE
1259 
1260   @return An error code: 0 - success, otherwise - failure
1261 
1262   @ref User
1263 **/
1264 int CeedOperatorLinearAssembleAddPointBlockDiagonal(CeedOperator op,
1265     CeedVector assembled, CeedRequest *request) {
1266   int ierr;
1267   Ceed ceed = op->ceed;
1268   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1269 
1270   // Use backend version, if available
1271   if (op->LinearAssembleAddPointBlockDiagonal) {
1272     ierr = op->LinearAssembleAddPointBlockDiagonal(op, assembled, request);
1273     CeedChk(ierr);
1274   } else {
1275     // Fallback to reference Ceed
1276     if (!op->opfallback) {
1277       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1278     }
1279     // Assemble
1280     ierr = op->opfallback->LinearAssembleAddPointBlockDiagonal(op->opfallback,
1281            assembled, request); CeedChk(ierr);
1282   }
1283   return CEED_ERROR_SUCCESS;
1284 }
1285 
1286 /**
1287   @brief Create a multigrid coarse operator and level transfer operators
1288            for a CeedOperator, creating the prolongation basis from the
1289            fine and coarse grid interpolation
1290 
1291   @param[in] opFine       Fine grid operator
1292   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1293   @param[in] rstrCoarse   Coarse grid restriction
1294   @param[in] basisCoarse  Coarse grid active vector basis
1295   @param[out] opCoarse    Coarse grid operator
1296   @param[out] opProlong   Coarse to fine operator
1297   @param[out] opRestrict  Fine to coarse operator
1298 
1299   @return An error code: 0 - success, otherwise - failure
1300 
1301   @ref User
1302 **/
1303 int CeedOperatorMultigridLevelCreate(CeedOperator opFine, CeedVector PMultFine,
1304                                      CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1305                                      CeedOperator *opCoarse, CeedOperator *opProlong, CeedOperator *opRestrict) {
1306   int ierr;
1307   Ceed ceed;
1308   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1309 
1310   // Check for compatible quadrature spaces
1311   CeedBasis basisFine;
1312   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1313   CeedInt Qf, Qc;
1314   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1315   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1316   if (Qf != Qc)
1317     // LCOV_EXCL_START
1318     return CeedError(ceed, CEED_ERROR_DIMENSION,
1319                      "Bases must have compatible quadrature spaces");
1320   // LCOV_EXCL_STOP
1321 
1322   // Coarse to fine basis
1323   CeedInt Pf, Pc, Q = Qf;
1324   bool isTensorF, isTensorC;
1325   ierr = CeedBasisIsTensor(basisFine, &isTensorF); CeedChk(ierr);
1326   ierr = CeedBasisIsTensor(basisCoarse, &isTensorC); CeedChk(ierr);
1327   CeedScalar *interpC, *interpF, *interpCtoF, *tau;
1328   if (isTensorF && isTensorC) {
1329     ierr = CeedBasisGetNumNodes1D(basisFine, &Pf); CeedChk(ierr);
1330     ierr = CeedBasisGetNumNodes1D(basisCoarse, &Pc); CeedChk(ierr);
1331     ierr = CeedBasisGetNumQuadraturePoints1D(basisCoarse, &Q); CeedChk(ierr);
1332   } else if (!isTensorF && !isTensorC) {
1333     ierr = CeedBasisGetNumNodes(basisFine, &Pf); CeedChk(ierr);
1334     ierr = CeedBasisGetNumNodes(basisCoarse, &Pc); CeedChk(ierr);
1335   } else {
1336     // LCOV_EXCL_START
1337     return CeedError(ceed, CEED_ERROR_MINOR,
1338                      "Bases must both be tensor or non-tensor");
1339     // LCOV_EXCL_STOP
1340   }
1341 
1342   ierr = CeedMalloc(Q*Pf, &interpF); CeedChk(ierr);
1343   ierr = CeedMalloc(Q*Pc, &interpC); CeedChk(ierr);
1344   ierr = CeedCalloc(Pc*Pf, &interpCtoF); CeedChk(ierr);
1345   ierr = CeedMalloc(Q, &tau); CeedChk(ierr);
1346   if (isTensorF) {
1347     memcpy(interpF, basisFine->interp1d, Q*Pf*sizeof basisFine->interp1d[0]);
1348     memcpy(interpC, basisCoarse->interp1d, Q*Pc*sizeof basisCoarse->interp1d[0]);
1349   } else {
1350     memcpy(interpF, basisFine->interp, Q*Pf*sizeof basisFine->interp[0]);
1351     memcpy(interpC, basisCoarse->interp, Q*Pc*sizeof basisCoarse->interp[0]);
1352   }
1353 
1354   // -- QR Factorization, interpF = Q R
1355   ierr = CeedQRFactorization(ceed, interpF, tau, Q, Pf); CeedChk(ierr);
1356 
1357   // -- Apply Qtranspose, interpC = Qtranspose interpC
1358   CeedHouseholderApplyQ(interpC, interpF, tau, CEED_TRANSPOSE,
1359                         Q, Pc, Pf, Pc, 1);
1360 
1361   // -- Apply Rinv, interpCtoF = Rinv interpC
1362   for (CeedInt j=0; j<Pc; j++) { // Column j
1363     interpCtoF[j+Pc*(Pf-1)] = interpC[j+Pc*(Pf-1)]/interpF[Pf*Pf-1];
1364     for (CeedInt i=Pf-2; i>=0; i--) { // Row i
1365       interpCtoF[j+Pc*i] = interpC[j+Pc*i];
1366       for (CeedInt k=i+1; k<Pf; k++)
1367         interpCtoF[j+Pc*i] -= interpF[k+Pf*i]*interpCtoF[j+Pc*k];
1368       interpCtoF[j+Pc*i] /= interpF[i+Pf*i];
1369     }
1370   }
1371   ierr = CeedFree(&tau); CeedChk(ierr);
1372   ierr = CeedFree(&interpC); CeedChk(ierr);
1373   ierr = CeedFree(&interpF); CeedChk(ierr);
1374 
1375   // Complete with interpCtoF versions of code
1376   if (isTensorF) {
1377     ierr = CeedOperatorMultigridLevelCreateTensorH1(opFine, PMultFine,
1378            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1379     CeedChk(ierr);
1380   } else {
1381     ierr = CeedOperatorMultigridLevelCreateH1(opFine, PMultFine,
1382            rstrCoarse, basisCoarse, interpCtoF, opCoarse, opProlong, opRestrict);
1383     CeedChk(ierr);
1384   }
1385 
1386   // Cleanup
1387   ierr = CeedFree(&interpCtoF); CeedChk(ierr);
1388   return CEED_ERROR_SUCCESS;
1389 }
1390 
1391 /**
1392   @brief Create a multigrid coarse operator and level transfer operators
1393            for a CeedOperator with a tensor basis for the active basis
1394 
1395   @param[in] opFine       Fine grid operator
1396   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1397   @param[in] rstrCoarse   Coarse grid restriction
1398   @param[in] basisCoarse  Coarse grid active vector basis
1399   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1400   @param[out] opCoarse    Coarse grid operator
1401   @param[out] opProlong   Coarse to fine operator
1402   @param[out] opRestrict  Fine to coarse operator
1403 
1404   @return An error code: 0 - success, otherwise - failure
1405 
1406   @ref User
1407 **/
1408 int CeedOperatorMultigridLevelCreateTensorH1(CeedOperator opFine,
1409     CeedVector PMultFine, CeedElemRestriction rstrCoarse, CeedBasis basisCoarse,
1410     const CeedScalar *interpCtoF, CeedOperator *opCoarse,
1411     CeedOperator *opProlong, CeedOperator *opRestrict) {
1412   int ierr;
1413   Ceed ceed;
1414   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1415 
1416   // Check for compatible quadrature spaces
1417   CeedBasis basisFine;
1418   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1419   CeedInt Qf, Qc;
1420   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1421   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1422   if (Qf != Qc)
1423     // LCOV_EXCL_START
1424     return CeedError(ceed, CEED_ERROR_DIMENSION,
1425                      "Bases must have compatible quadrature spaces");
1426   // LCOV_EXCL_STOP
1427 
1428   // Coarse to fine basis
1429   CeedInt dim, ncomp, nnodesCoarse, P1dFine, P1dCoarse;
1430   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1431   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1432   ierr = CeedBasisGetNumNodes1D(basisFine, &P1dFine); CeedChk(ierr);
1433   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1434   CeedChk(ierr);
1435   P1dCoarse = dim == 1 ? nnodesCoarse :
1436               dim == 2 ? sqrt(nnodesCoarse) :
1437               cbrt(nnodesCoarse);
1438   CeedScalar *qref, *qweight, *grad;
1439   ierr = CeedCalloc(P1dFine, &qref); CeedChk(ierr);
1440   ierr = CeedCalloc(P1dFine, &qweight); CeedChk(ierr);
1441   ierr = CeedCalloc(P1dFine*P1dCoarse*dim, &grad); CeedChk(ierr);
1442   CeedBasis basisCtoF;
1443   ierr = CeedBasisCreateTensorH1(ceed, dim, ncomp, P1dCoarse, P1dFine,
1444                                  interpCtoF, grad, qref, qweight, &basisCtoF);
1445   CeedChk(ierr);
1446   ierr = CeedFree(&qref); CeedChk(ierr);
1447   ierr = CeedFree(&qweight); CeedChk(ierr);
1448   ierr = CeedFree(&grad); CeedChk(ierr);
1449 
1450   // Core code
1451   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1452                                          basisCoarse, basisCtoF, opCoarse,
1453                                          opProlong, opRestrict);
1454   CeedChk(ierr);
1455   return CEED_ERROR_SUCCESS;
1456 }
1457 
1458 /**
1459   @brief Create a multigrid coarse operator and level transfer operators
1460            for a CeedOperator with a non-tensor basis for the active vector
1461 
1462   @param[in] opFine       Fine grid operator
1463   @param[in] PMultFine    L-vector multiplicity in parallel gather/scatter
1464   @param[in] rstrCoarse   Coarse grid restriction
1465   @param[in] basisCoarse  Coarse grid active vector basis
1466   @param[in] interpCtoF   Matrix for coarse to fine interpolation
1467   @param[out] opCoarse    Coarse grid operator
1468   @param[out] opProlong   Coarse to fine operator
1469   @param[out] opRestrict  Fine to coarse operator
1470 
1471   @return An error code: 0 - success, otherwise - failure
1472 
1473   @ref User
1474 **/
1475 int CeedOperatorMultigridLevelCreateH1(CeedOperator opFine,
1476                                        CeedVector PMultFine,
1477                                        CeedElemRestriction rstrCoarse,
1478                                        CeedBasis basisCoarse,
1479                                        const CeedScalar *interpCtoF,
1480                                        CeedOperator *opCoarse,
1481                                        CeedOperator *opProlong,
1482                                        CeedOperator *opRestrict) {
1483   int ierr;
1484   Ceed ceed;
1485   ierr = CeedOperatorGetCeed(opFine, &ceed); CeedChk(ierr);
1486 
1487   // Check for compatible quadrature spaces
1488   CeedBasis basisFine;
1489   ierr = CeedOperatorGetActiveBasis(opFine, &basisFine); CeedChk(ierr);
1490   CeedInt Qf, Qc;
1491   ierr = CeedBasisGetNumQuadraturePoints(basisFine, &Qf); CeedChk(ierr);
1492   ierr = CeedBasisGetNumQuadraturePoints(basisCoarse, &Qc); CeedChk(ierr);
1493   if (Qf != Qc)
1494     // LCOV_EXCL_START
1495     return CeedError(ceed, CEED_ERROR_DIMENSION,
1496                      "Bases must have compatible quadrature spaces");
1497   // LCOV_EXCL_STOP
1498 
1499   // Coarse to fine basis
1500   CeedElemTopology topo;
1501   ierr = CeedBasisGetTopology(basisFine, &topo); CeedChk(ierr);
1502   CeedInt dim, ncomp, nnodesCoarse, nnodesFine;
1503   ierr = CeedBasisGetDimension(basisFine, &dim); CeedChk(ierr);
1504   ierr = CeedBasisGetNumComponents(basisFine, &ncomp); CeedChk(ierr);
1505   ierr = CeedBasisGetNumNodes(basisFine, &nnodesFine); CeedChk(ierr);
1506   ierr = CeedElemRestrictionGetElementSize(rstrCoarse, &nnodesCoarse);
1507   CeedChk(ierr);
1508   CeedScalar *qref, *qweight, *grad;
1509   ierr = CeedCalloc(nnodesFine, &qref); CeedChk(ierr);
1510   ierr = CeedCalloc(nnodesFine, &qweight); CeedChk(ierr);
1511   ierr = CeedCalloc(nnodesFine*nnodesCoarse*dim, &grad); CeedChk(ierr);
1512   CeedBasis basisCtoF;
1513   ierr = CeedBasisCreateH1(ceed, topo, ncomp, nnodesCoarse, nnodesFine,
1514                            interpCtoF, grad, qref, qweight, &basisCtoF);
1515   CeedChk(ierr);
1516   ierr = CeedFree(&qref); CeedChk(ierr);
1517   ierr = CeedFree(&qweight); CeedChk(ierr);
1518   ierr = CeedFree(&grad); CeedChk(ierr);
1519 
1520   // Core code
1521   ierr = CeedOperatorMultigridLevel_Core(opFine, PMultFine, rstrCoarse,
1522                                          basisCoarse, basisCtoF, opCoarse,
1523                                          opProlong, opRestrict);
1524   CeedChk(ierr);
1525   return CEED_ERROR_SUCCESS;
1526 }
1527 
1528 /**
1529   @brief Build a FDM based approximate inverse for each element for a
1530            CeedOperator
1531 
1532   This returns a CeedOperator and CeedVector to apply a Fast Diagonalization
1533     Method based approximate inverse. This function obtains the simultaneous
1534     diagonalization for the 1D mass and Laplacian operators,
1535       M = V^T V, K = V^T S V.
1536     The assembled QFunction is used to modify the eigenvalues from simultaneous
1537     diagonalization and obtain an approximate inverse of the form
1538       V^T S^hat V. The CeedOperator must be linear and non-composite. The
1539     associated CeedQFunction must therefore also be linear.
1540 
1541   @param op             CeedOperator to create element inverses
1542   @param[out] fdminv    CeedOperator to apply the action of a FDM based inverse
1543                           for each element
1544   @param request        Address of CeedRequest for non-blocking completion, else
1545                           @ref CEED_REQUEST_IMMEDIATE
1546 
1547   @return An error code: 0 - success, otherwise - failure
1548 
1549   @ref Backend
1550 **/
1551 int CeedOperatorCreateFDMElementInverse(CeedOperator op, CeedOperator *fdminv,
1552                                         CeedRequest *request) {
1553   int ierr;
1554   Ceed ceed = op->ceed;
1555   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1556 
1557   // Use backend version, if available
1558   if (op->CreateFDMElementInverse) {
1559     ierr = op->CreateFDMElementInverse(op, fdminv, request); CeedChk(ierr);
1560   } else {
1561     // Fallback to reference Ceed
1562     if (!op->opfallback) {
1563       ierr = CeedOperatorCreateFallback(op); CeedChk(ierr);
1564     }
1565     // Assemble
1566     ierr = op->opfallback->CreateFDMElementInverse(op->opfallback, fdminv,
1567            request); CeedChk(ierr);
1568   }
1569   return CEED_ERROR_SUCCESS;
1570 }
1571 
1572 /**
1573   @brief View a CeedOperator
1574 
1575   @param[in] op     CeedOperator to view
1576   @param[in] stream Stream to write; typically stdout/stderr or a file
1577 
1578   @return Error code: 0 - success, otherwise - failure
1579 
1580   @ref User
1581 **/
1582 int CeedOperatorView(CeedOperator op, FILE *stream) {
1583   int ierr;
1584 
1585   if (op->composite) {
1586     fprintf(stream, "Composite CeedOperator\n");
1587 
1588     for (CeedInt i=0; i<op->numsub; i++) {
1589       fprintf(stream, "  SubOperator [%d]:\n", i);
1590       ierr = CeedOperatorSingleView(op->suboperators[i], 1, stream);
1591       CeedChk(ierr);
1592     }
1593   } else {
1594     fprintf(stream, "CeedOperator\n");
1595     ierr = CeedOperatorSingleView(op, 0, stream); CeedChk(ierr);
1596   }
1597   return CEED_ERROR_SUCCESS;
1598 }
1599 
1600 /**
1601   @brief Apply CeedOperator to a vector
1602 
1603   This computes the action of the operator on the specified (active) input,
1604   yielding its (active) output.  All inputs and outputs must be specified using
1605   CeedOperatorSetField().
1606 
1607   @param op        CeedOperator to apply
1608   @param[in] in    CeedVector containing input state or @ref CEED_VECTOR_NONE if
1609                   there are no active inputs
1610   @param[out] out  CeedVector to store result of applying operator (must be
1611                      distinct from @a in) or @ref CEED_VECTOR_NONE if there are no
1612                      active outputs
1613   @param request   Address of CeedRequest for non-blocking completion, else
1614                      @ref CEED_REQUEST_IMMEDIATE
1615 
1616   @return An error code: 0 - success, otherwise - failure
1617 
1618   @ref User
1619 **/
1620 int CeedOperatorApply(CeedOperator op, CeedVector in, CeedVector out,
1621                       CeedRequest *request) {
1622   int ierr;
1623   Ceed ceed = op->ceed;
1624   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1625 
1626   if (op->numelements)  {
1627     // Standard Operator
1628     if (op->Apply) {
1629       ierr = op->Apply(op, in, out, request); CeedChk(ierr);
1630     } else {
1631       // Zero all output vectors
1632       CeedQFunction qf = op->qf;
1633       for (CeedInt i=0; i<qf->numoutputfields; i++) {
1634         CeedVector vec = op->outputfields[i]->vec;
1635         if (vec == CEED_VECTOR_ACTIVE)
1636           vec = out;
1637         if (vec != CEED_VECTOR_NONE) {
1638           ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1639         }
1640       }
1641       // Apply
1642       ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1643     }
1644   } else if (op->composite) {
1645     // Composite Operator
1646     if (op->ApplyComposite) {
1647       ierr = op->ApplyComposite(op, in, out, request); CeedChk(ierr);
1648     } else {
1649       CeedInt numsub;
1650       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1651       CeedOperator *suboperators;
1652       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1653 
1654       // Zero all output vectors
1655       if (out != CEED_VECTOR_NONE) {
1656         ierr = CeedVectorSetValue(out, 0.0); CeedChk(ierr);
1657       }
1658       for (CeedInt i=0; i<numsub; i++) {
1659         for (CeedInt j=0; j<suboperators[i]->qf->numoutputfields; j++) {
1660           CeedVector vec = suboperators[i]->outputfields[j]->vec;
1661           if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) {
1662             ierr = CeedVectorSetValue(vec, 0.0); CeedChk(ierr);
1663           }
1664         }
1665       }
1666       // Apply
1667       for (CeedInt i=0; i<op->numsub; i++) {
1668         ierr = CeedOperatorApplyAdd(op->suboperators[i], in, out, request);
1669         CeedChk(ierr);
1670       }
1671     }
1672   }
1673   return CEED_ERROR_SUCCESS;
1674 }
1675 
1676 /**
1677   @brief Apply CeedOperator to a vector and add result to output vector
1678 
1679   This computes the action of the operator on the specified (active) input,
1680   yielding its (active) output.  All inputs and outputs must be specified using
1681   CeedOperatorSetField().
1682 
1683   @param op        CeedOperator to apply
1684   @param[in] in    CeedVector containing input state or NULL if there are no
1685                      active inputs
1686   @param[out] out  CeedVector to sum in result of applying operator (must be
1687                      distinct from @a in) or NULL if there are no active outputs
1688   @param request   Address of CeedRequest for non-blocking completion, else
1689                      @ref CEED_REQUEST_IMMEDIATE
1690 
1691   @return An error code: 0 - success, otherwise - failure
1692 
1693   @ref User
1694 **/
1695 int CeedOperatorApplyAdd(CeedOperator op, CeedVector in, CeedVector out,
1696                          CeedRequest *request) {
1697   int ierr;
1698   Ceed ceed = op->ceed;
1699   ierr = CeedOperatorCheckReady(ceed, op); CeedChk(ierr);
1700 
1701   if (op->numelements)  {
1702     // Standard Operator
1703     ierr = op->ApplyAdd(op, in, out, request); CeedChk(ierr);
1704   } else if (op->composite) {
1705     // Composite Operator
1706     if (op->ApplyAddComposite) {
1707       ierr = op->ApplyAddComposite(op, in, out, request); CeedChk(ierr);
1708     } else {
1709       CeedInt numsub;
1710       ierr = CeedOperatorGetNumSub(op, &numsub); CeedChk(ierr);
1711       CeedOperator *suboperators;
1712       ierr = CeedOperatorGetSubList(op, &suboperators); CeedChk(ierr);
1713 
1714       for (CeedInt i=0; i<numsub; i++) {
1715         ierr = CeedOperatorApplyAdd(suboperators[i], in, out, request);
1716         CeedChk(ierr);
1717       }
1718     }
1719   }
1720   return CEED_ERROR_SUCCESS;
1721 }
1722 
1723 /**
1724   @brief Destroy a CeedOperator
1725 
1726   @param op CeedOperator to destroy
1727 
1728   @return An error code: 0 - success, otherwise - failure
1729 
1730   @ref User
1731 **/
1732 int CeedOperatorDestroy(CeedOperator *op) {
1733   int ierr;
1734 
1735   if (!*op || --(*op)->refcount > 0) return CEED_ERROR_SUCCESS;
1736   if ((*op)->Destroy) {
1737     ierr = (*op)->Destroy(*op); CeedChk(ierr);
1738   }
1739   ierr = CeedDestroy(&(*op)->ceed); CeedChk(ierr);
1740   // Free fields
1741   for (int i=0; i<(*op)->nfields; i++)
1742     if ((*op)->inputfields[i]) {
1743       if ((*op)->inputfields[i]->Erestrict != CEED_ELEMRESTRICTION_NONE) {
1744         ierr = CeedElemRestrictionDestroy(&(*op)->inputfields[i]->Erestrict);
1745         CeedChk(ierr);
1746       }
1747       if ((*op)->inputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1748         ierr = CeedBasisDestroy(&(*op)->inputfields[i]->basis); CeedChk(ierr);
1749       }
1750       if ((*op)->inputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1751           (*op)->inputfields[i]->vec != CEED_VECTOR_NONE ) {
1752         ierr = CeedVectorDestroy(&(*op)->inputfields[i]->vec); CeedChk(ierr);
1753       }
1754       ierr = CeedFree(&(*op)->inputfields[i]->fieldname); CeedChk(ierr);
1755       ierr = CeedFree(&(*op)->inputfields[i]); CeedChk(ierr);
1756     }
1757   for (int i=0; i<(*op)->nfields; i++)
1758     if ((*op)->outputfields[i]) {
1759       ierr = CeedElemRestrictionDestroy(&(*op)->outputfields[i]->Erestrict);
1760       CeedChk(ierr);
1761       if ((*op)->outputfields[i]->basis != CEED_BASIS_COLLOCATED) {
1762         ierr = CeedBasisDestroy(&(*op)->outputfields[i]->basis); CeedChk(ierr);
1763       }
1764       if ((*op)->outputfields[i]->vec != CEED_VECTOR_ACTIVE &&
1765           (*op)->outputfields[i]->vec != CEED_VECTOR_NONE ) {
1766         ierr = CeedVectorDestroy(&(*op)->outputfields[i]->vec); CeedChk(ierr);
1767       }
1768       ierr = CeedFree(&(*op)->outputfields[i]->fieldname); CeedChk(ierr);
1769       ierr = CeedFree(&(*op)->outputfields[i]); CeedChk(ierr);
1770     }
1771   // Destroy suboperators
1772   for (int i=0; i<(*op)->numsub; i++)
1773     if ((*op)->suboperators[i]) {
1774       ierr = CeedOperatorDestroy(&(*op)->suboperators[i]); CeedChk(ierr);
1775     }
1776   if ((*op)->qf)
1777     // LCOV_EXCL_START
1778     (*op)->qf->operatorsset--;
1779   // LCOV_EXCL_STOP
1780   ierr = CeedQFunctionDestroy(&(*op)->qf); CeedChk(ierr);
1781   if ((*op)->dqf && (*op)->dqf != CEED_QFUNCTION_NONE)
1782     // LCOV_EXCL_START
1783     (*op)->dqf->operatorsset--;
1784   // LCOV_EXCL_STOP
1785   ierr = CeedQFunctionDestroy(&(*op)->dqf); CeedChk(ierr);
1786   if ((*op)->dqfT && (*op)->dqfT != CEED_QFUNCTION_NONE)
1787     // LCOV_EXCL_START
1788     (*op)->dqfT->operatorsset--;
1789   // LCOV_EXCL_STOP
1790   ierr = CeedQFunctionDestroy(&(*op)->dqfT); CeedChk(ierr);
1791 
1792   // Destroy fallback
1793   if ((*op)->opfallback) {
1794     ierr = (*op)->qffallback->Destroy((*op)->qffallback); CeedChk(ierr);
1795     ierr = CeedFree(&(*op)->qffallback); CeedChk(ierr);
1796     ierr = (*op)->opfallback->Destroy((*op)->opfallback); CeedChk(ierr);
1797     ierr = CeedFree(&(*op)->opfallback); CeedChk(ierr);
1798   }
1799 
1800   ierr = CeedFree(&(*op)->inputfields); CeedChk(ierr);
1801   ierr = CeedFree(&(*op)->outputfields); CeedChk(ierr);
1802   ierr = CeedFree(&(*op)->suboperators); CeedChk(ierr);
1803   ierr = CeedFree(op); CeedChk(ierr);
1804   return CEED_ERROR_SUCCESS;
1805 }
1806 
1807 /// @}
1808