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