xref: /libCEED/interface/ceed-qfunction.c (revision 06988bf74cc6ac18eacafe7930f080803395ba29)
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 <limits.h>
21 
22 /// @file
23 /// Implementation of public CeedQFunction interfaces
24 
25 /// @cond DOXYGEN_SKIP
26 static struct CeedQFunction_private ceed_qfunction_none;
27 /// @endcond
28 
29 /// @addtogroup CeedQFunctionUser
30 /// @{
31 
32 // Indicate that no QFunction is provided by the user
33 const CeedQFunction CEED_QFUNCTION_NONE = &ceed_qfunction_none;
34 
35 /// @}
36 
37 /// @cond DOXYGEN_SKIP
38 static struct {
39   char name[CEED_MAX_RESOURCE_LEN];
40   char source[CEED_MAX_RESOURCE_LEN];
41   CeedInt vlength;
42   CeedQFunctionUser f;
43   int (*init)(Ceed ceed, const char *name, CeedQFunction qf);
44 } qfunctions[1024];
45 static size_t num_qfunctions;
46 /// @endcond
47 
48 /// ----------------------------------------------------------------------------
49 /// CeedQFunction Library Internal Functions
50 /// ----------------------------------------------------------------------------
51 /// @addtogroup CeedQFunctionDeveloper
52 /// @{
53 
54 /**
55   @brief Register a gallery QFunction
56 
57   @param name     Name for this backend to respond to
58   @param source   Absolute path to source of QFunction,
59                     "\path\CEED_DIR\gallery\folder\file.h:function_name"
60   @param vlength  Vector length.  Caller must ensure that number of quadrature
61                     points is a multiple of vlength.
62   @param f        Function pointer to evaluate action at quadrature points.
63                     See \ref CeedQFunctionUser.
64   @param init     Initialization function called by CeedQFunctionInit() when the
65                     QFunction is selected.
66 
67   @return An error code: 0 - success, otherwise - failure
68 
69   @ref Developer
70 **/
71 int CeedQFunctionRegister(const char *name, const char *source,
72                           CeedInt vlength, CeedQFunctionUser f,
73                           int (*init)(Ceed, const char *, CeedQFunction)) {
74   if (num_qfunctions >= sizeof(qfunctions) / sizeof(qfunctions[0]))
75     // LCOV_EXCL_START
76     return CeedError(NULL, 1, "Too many gallery QFunctions");
77   // LCOV_EXCL_STOP
78 
79   strncpy(qfunctions[num_qfunctions].name, name, CEED_MAX_RESOURCE_LEN);
80   qfunctions[num_qfunctions].name[CEED_MAX_RESOURCE_LEN-1] = 0;
81   strncpy(qfunctions[num_qfunctions].source, source, CEED_MAX_RESOURCE_LEN);
82   qfunctions[num_qfunctions].source[CEED_MAX_RESOURCE_LEN-1] = 0;
83   qfunctions[num_qfunctions].vlength = vlength;
84   qfunctions[num_qfunctions].f = f;
85   qfunctions[num_qfunctions].init = init;
86   num_qfunctions++;
87   return 0;
88 }
89 
90 /**
91   @brief Set a CeedQFunction field, used by CeedQFunctionAddInput/Output
92 
93   @param f          CeedQFunctionField
94   @param fieldname  Name of QFunction field
95   @param size       Size of QFunction field, (ncomp * dim) for @ref CEED_EVAL_GRAD or
96                       (ncomp * 1) for @ref CEED_EVAL_NONE, @ref CEED_EVAL_INTERP, and @ref CEED_EVAL_WEIGHT
97   @param emode      \ref CEED_EVAL_NONE to use values directly,
98                       \ref CEED_EVAL_INTERP to use interpolated values,
99                       \ref CEED_EVAL_GRAD to use gradients,
100                       \ref CEED_EVAL_WEIGHT to use quadrature weights.
101 
102   @return An error code: 0 - success, otherwise - failure
103 
104   @ref Developer
105 **/
106 static int CeedQFunctionFieldSet(CeedQFunctionField *f,const char *fieldname,
107                                  CeedInt size, CeedEvalMode emode) {
108   size_t len = strlen(fieldname);
109   char *tmp;
110   int ierr;
111   ierr = CeedCalloc(1,f); CeedChk(ierr);
112 
113   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
114   memcpy(tmp, fieldname, len+1);
115   (*f)->fieldname = tmp;
116   (*f)->size = size;
117   (*f)->emode = emode;
118   return 0;
119 }
120 
121 /**
122   @brief View a field of a CeedQFunction
123 
124   @param[in] field        QFunction field to view
125   @param[in] fieldnumber  Number of field being viewed
126   @param[in] in           true for input field, false for output
127   @param[in] stream       Stream to view to, e.g., stdout
128 
129   @return An error code: 0 - success, otherwise - failure
130 
131   @ref Utility
132 **/
133 static int CeedQFunctionFieldView(CeedQFunctionField field, CeedInt fieldnumber,
134                                   bool in, FILE *stream) {
135   const char *inout = in ? "Input" : "Output";
136   fprintf(stream, "    %s Field [%d]:\n"
137           "      Name: \"%s\"\n"
138           "      Size: %d\n"
139           "      EvalMode: \"%s\"\n",
140           inout, fieldnumber, field->fieldname, field->size,
141           CeedEvalModes[field->emode]);
142 
143   return 0;
144 }
145 
146 
147 /**
148   @brief Set flag to determine if Fortran interface is used
149 
150   @param qf                  CeedQFunction
151   @param status              Boolean value to set as Fortran status
152 
153   @return An error code: 0 - success, otherwise - failure
154 
155   @ref Backend
156 **/
157 int CeedQFunctionSetFortranStatus(CeedQFunction qf, bool status) {
158   qf->fortranstatus = status;
159   return 0;
160 }
161 
162 /// @}
163 
164 /// ----------------------------------------------------------------------------
165 /// CeedQFunction Backend API
166 /// ----------------------------------------------------------------------------
167 /// @addtogroup CeedQFunctionBackend
168 /// @{
169 
170 /**
171   @brief Get the Ceed associated with a CeedQFunction
172 
173   @param qf              CeedQFunction
174   @param[out] ceed       Variable to store Ceed
175 
176   @return An error code: 0 - success, otherwise - failure
177 
178   @ref Backend
179 **/
180 int CeedQFunctionGetCeed(CeedQFunction qf, Ceed *ceed) {
181   *ceed = qf->ceed;
182   return 0;
183 }
184 
185 /**
186   @brief Get the vector length of a CeedQFunction
187 
188   @param qf            CeedQFunction
189   @param[out] vlength  Variable to store vector length
190 
191   @return An error code: 0 - success, otherwise - failure
192 
193   @ref Backend
194 **/
195 int CeedQFunctionGetVectorLength(CeedQFunction qf, CeedInt *vlength) {
196   *vlength = qf->vlength;
197   return 0;
198 }
199 
200 /**
201   @brief Get the number of inputs and outputs to a CeedQFunction
202 
203   @param qf              CeedQFunction
204   @param[out] numinput   Variable to store number of input fields
205   @param[out] numoutput  Variable to store number of output fields
206 
207   @return An error code: 0 - success, otherwise - failure
208 
209   @ref Backend
210 **/
211 int CeedQFunctionGetNumArgs(CeedQFunction qf, CeedInt *numinput,
212                             CeedInt *numoutput) {
213   if (numinput) *numinput = qf->numinputfields;
214   if (numoutput) *numoutput = qf->numoutputfields;
215   return 0;
216 }
217 
218 /**
219   @brief Get the source path string for a CeedQFunction
220 
221   @param qf              CeedQFunction
222   @param[out] source     Variable to store source path string
223 
224   @return An error code: 0 - success, otherwise - failure
225 
226   @ref Backend
227 **/
228 int CeedQFunctionGetSourcePath(CeedQFunction qf, char **source) {
229   *source = (char *) qf->sourcepath;
230   return 0;
231 }
232 
233 /**
234   @brief Get the User Function for a CeedQFunction
235 
236   @param qf              CeedQFunction
237   @param[out] f          Variable to store user function
238 
239   @return An error code: 0 - success, otherwise - failure
240 
241   @ref Backend
242 **/
243 int CeedQFunctionGetUserFunction(CeedQFunction qf, CeedQFunctionUser *f) {
244   *f = qf->function;
245   return 0;
246 }
247 
248 /**
249   @brief Get global context for a CeedQFunction.
250          Note: For QFunctions from the Fortran interface, this
251                function will return the Fortran context
252                CeedQFunctionContext.
253 
254   @param qf              CeedQFunction
255   @param[out] ctx        Variable to store CeedQFunctionContext
256 
257   @return An error code: 0 - success, otherwise - failure
258 
259   @ref Backend
260 **/
261 int CeedQFunctionGetContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
262   *ctx = qf->ctx;
263   return 0;
264 }
265 
266 /**
267   @brief Get true user context for a CeedQFunction
268          Note: For all QFunctions this function will return the user
269                CeedQFunctionContext and not interface context
270                CeedQFunctionContext, if any such object exists.
271 
272   @param qf              CeedQFunction
273   @param[out] ctx        Variable to store CeedQFunctionContext
274 
275   @return An error code: 0 - success, otherwise - failure
276   @ref Backend
277 **/
278 int CeedQFunctionGetInnerContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
279   int ierr;
280   if (qf->fortranstatus) {
281     CeedFortranContext fctx = NULL;
282     ierr = CeedQFunctionContextGetData(qf->ctx, CEED_MEM_HOST, &fctx);
283     CeedChk(ierr);
284     *ctx = fctx->innerctx;
285     ierr = CeedQFunctionContextRestoreData(qf->ctx, (void *)&fctx); CeedChk(ierr);
286   } else {
287     *ctx = qf->ctx;
288   }
289 
290 
291   return 0;
292 }
293 
294 /**
295   @brief Determine if QFunction is identity
296 
297   @param qf               CeedQFunction
298   @param[out] isidentity  Variable to store identity status
299 
300   @return An error code: 0 - success, otherwise - failure
301 
302   @ref Backend
303 **/
304 int CeedQFunctionIsIdentity(CeedQFunction qf, bool *isidentity) {
305   *isidentity = qf->identity;
306   return 0;
307 }
308 
309 /**
310   @brief Get backend data of a CeedQFunction
311 
312   @param qf              CeedQFunction
313   @param[out] data       Variable to store data
314 
315   @return An error code: 0 - success, otherwise - failure
316 
317   @ref Backend
318 **/
319 int CeedQFunctionGetData(CeedQFunction qf, void *data) {
320   *(void **)data = qf->data;
321   return 0;
322 }
323 
324 /**
325   @brief Set backend data of a CeedQFunction
326 
327   @param[out] qf         CeedQFunction
328   @param data            Data to set
329 
330   @return An error code: 0 - success, otherwise - failure
331 
332   @ref Backend
333 **/
334 int CeedQFunctionSetData(CeedQFunction qf, void *data) {
335   qf->data = data;
336   return 0;
337 }
338 
339 /**
340   @brief Get the CeedQFunctionFields of a CeedQFunction
341 
342   @param qf                 CeedQFunction
343   @param[out] inputfields   Variable to store inputfields
344   @param[out] outputfields  Variable to store outputfields
345 
346   @return An error code: 0 - success, otherwise - failure
347 
348   @ref Backend
349 **/
350 int CeedQFunctionGetFields(CeedQFunction qf, CeedQFunctionField **inputfields,
351                            CeedQFunctionField **outputfields) {
352   if (inputfields)
353     *inputfields = qf->inputfields;
354   if (outputfields)
355     *outputfields = qf->outputfields;
356   return 0;
357 }
358 
359 /**
360   @brief Get the name of a CeedQFunctionField
361 
362   @param qffield         CeedQFunctionField
363   @param[out] fieldname  Variable to store the field name
364 
365   @return An error code: 0 - success, otherwise - failure
366 
367   @ref Backend
368 **/
369 int CeedQFunctionFieldGetName(CeedQFunctionField qffield, char **fieldname) {
370   *fieldname = (char *)qffield->fieldname;
371   return 0;
372 }
373 
374 /**
375   @brief Get the number of components of a CeedQFunctionField
376 
377   @param qffield    CeedQFunctionField
378   @param[out] size  Variable to store the size of the field
379 
380   @return An error code: 0 - success, otherwise - failure
381 
382   @ref Backend
383 **/
384 int CeedQFunctionFieldGetSize(CeedQFunctionField qffield, CeedInt *size) {
385   *size = qffield->size;
386   return 0;
387 }
388 
389 /**
390   @brief Get the CeedEvalMode of a CeedQFunctionField
391 
392   @param qffield         CeedQFunctionField
393   @param[out] emode      Variable to store the field evaluation mode
394 
395   @return An error code: 0 - success, otherwise - failure
396 
397   @ref Backend
398 **/
399 int CeedQFunctionFieldGetEvalMode(CeedQFunctionField qffield,
400                                   CeedEvalMode *emode) {
401   *emode = qffield->emode;
402   return 0;
403 }
404 
405 /// @}
406 
407 /// ----------------------------------------------------------------------------
408 /// CeedQFunction Public API
409 /// ----------------------------------------------------------------------------
410 /// @addtogroup CeedQFunctionUser
411 /// @{
412 
413 /**
414   @brief Create a CeedQFunction for evaluating interior (volumetric) terms.
415 
416   @param ceed       A Ceed object where the CeedQFunction will be created
417   @param vlength    Vector length. Caller must ensure that number of quadrature
418                       points is a multiple of vlength.
419   @param f          Function pointer to evaluate action at quadrature points.
420                       See \ref CeedQFunctionUser.
421   @param source     Absolute path to source of QFunction,
422                       "\abs_path\file.h:function_name".
423                       For support across all backends, this source must only
424                       contain constructs supported by C99, C++11, and CUDA.
425   @param[out] qf    Address of the variable where the newly created
426                       CeedQFunction will be stored
427 
428   @return An error code: 0 - success, otherwise - failure
429 
430   See \ref CeedQFunctionUser for details on the call-back function @a f's
431     arguments.
432 
433   @ref User
434 **/
435 int CeedQFunctionCreateInterior(Ceed ceed, CeedInt vlength, CeedQFunctionUser f,
436                                 const char *source, CeedQFunction *qf) {
437   int ierr;
438   char *source_copy;
439 
440   if (!ceed->QFunctionCreate) {
441     Ceed delegate;
442     ierr = CeedGetObjectDelegate(ceed, &delegate, "QFunction"); CeedChk(ierr);
443 
444     if (!delegate)
445       // LCOV_EXCL_START
446       return CeedError(ceed, 1, "Backend does not support QFunctionCreate");
447     // LCOV_EXCL_STOP
448 
449     ierr = CeedQFunctionCreateInterior(delegate, vlength, f, source, qf);
450     CeedChk(ierr);
451     return 0;
452   }
453 
454   ierr = CeedCalloc(1, qf); CeedChk(ierr);
455   (*qf)->ceed = ceed;
456   ceed->refcount++;
457   (*qf)->refcount = 1;
458   (*qf)->vlength = vlength;
459   (*qf)->identity = 0;
460   (*qf)->function = f;
461   size_t slen = strlen(source) + 1;
462   ierr = CeedMalloc(slen, &source_copy); CeedChk(ierr);
463   memcpy(source_copy, source, slen);
464   (*qf)->sourcepath = source_copy;
465   ierr = CeedCalloc(16, &(*qf)->inputfields); CeedChk(ierr);
466   ierr = CeedCalloc(16, &(*qf)->outputfields); CeedChk(ierr);
467   ierr = ceed->QFunctionCreate(*qf); CeedChk(ierr);
468   return 0;
469 }
470 
471 /**
472   @brief Create a CeedQFunction for evaluating interior (volumetric) terms by name.
473 
474   @param ceed       A Ceed object where the CeedQFunction will be created
475   @param name       Name of QFunction to use from gallery
476   @param[out] qf    Address of the variable where the newly created
477                       CeedQFunction will be stored
478 
479   @return An error code: 0 - success, otherwise - failure
480 
481   @ref User
482 **/
483 int CeedQFunctionCreateInteriorByName(Ceed ceed,  const char *name,
484                                       CeedQFunction *qf) {
485   int ierr;
486   size_t matchlen = 0, matchidx = UINT_MAX;
487   char *name_copy;
488 
489   // Find matching backend
490   if (!name) return CeedError(ceed, 1, "No QFunction name provided");
491   for (size_t i=0; i<num_qfunctions; i++) {
492     size_t n;
493     const char *currname = qfunctions[i].name;
494     for (n = 0; currname[n] && currname[n] == name[n]; n++) {}
495     if (n > matchlen) {
496       matchlen = n;
497       matchidx = i;
498     }
499   }
500   if (!matchlen)
501     // LCOV_EXCL_START
502     return CeedError(ceed, 1, "No suitable gallery QFunction");
503   // LCOV_EXCL_STOP
504 
505   // Create QFunction
506   ierr = CeedQFunctionCreateInterior(ceed, qfunctions[matchidx].vlength,
507                                      qfunctions[matchidx].f,
508                                      qfunctions[matchidx].source, qf);
509   CeedChk(ierr);
510 
511   // QFunction specific setup
512   ierr = qfunctions[matchidx].init(ceed, name, *qf); CeedChk(ierr);
513 
514   // Copy name
515   size_t slen = strlen(name) + 1;
516   ierr = CeedMalloc(slen, &name_copy); CeedChk(ierr);
517   memcpy(name_copy, name, slen);
518   (*qf)->qfname = name_copy;
519 
520   return 0;
521 }
522 
523 /**
524   @brief Create an identity CeedQFunction. Inputs are written into outputs in
525            the order given. This is useful for CeedOperators that can be
526            represented with only the action of a CeedRestriction and CeedBasis,
527            such as restriction and prolongation operators for p-multigrid.
528            Backends may optimize CeedOperators with this CeedQFunction to avoid
529            the copy of input data to output fields by using the same memory
530            location for both.
531 
532   @param ceed         A Ceed object where the CeedQFunction will be created
533   @param[in] size     Size of the qfunction fields
534   @param[in] inmode   CeedEvalMode for input to CeedQFunction
535   @param[in] outmode  CeedEvalMode for output to CeedQFunction
536   @param[out] qf      Address of the variable where the newly created
537                         CeedQFunction will be stored
538 
539   @return An error code: 0 - success, otherwise - failure
540 
541   @ref User
542 **/
543 int CeedQFunctionCreateIdentity(Ceed ceed, CeedInt size, CeedEvalMode inmode,
544                                 CeedEvalMode outmode, CeedQFunction *qf) {
545   int ierr;
546 
547   if (inmode == CEED_EVAL_NONE && outmode == CEED_EVAL_NONE)
548     // LCOV_EXCL_START
549     return CeedError(ceed, 1, "CEED_EVAL_NONE for a both the input and "
550                      "output does not make sense with an identity QFunction");
551   // LCOV_EXCL_STOP
552 
553   ierr = CeedQFunctionCreateInteriorByName(ceed, "Identity", qf); CeedChk(ierr);
554   ierr = CeedQFunctionAddInput(*qf, "input", size, inmode); CeedChk(ierr);
555   ierr = CeedQFunctionAddOutput(*qf, "output", size, outmode); CeedChk(ierr);
556 
557   (*qf)->identity = 1;
558   CeedInt *sizeData;
559   ierr = CeedCalloc(1, &sizeData); CeedChk(ierr);
560   sizeData[0] = size;
561   CeedQFunctionContext ctx;
562   ierr = CeedQFunctionContextCreate(ceed, &ctx); CeedChk(ierr);
563   ierr = CeedQFunctionContextSetData(ctx, CEED_MEM_HOST, CEED_OWN_POINTER,
564                                      sizeof(*sizeData), (void *)sizeData);
565   CeedChk(ierr);
566   ierr = CeedQFunctionSetContext(*qf, ctx); CeedChk(ierr);
567   ierr = CeedQFunctionContextDestroy(&ctx); CeedChk(ierr);
568 
569   return 0;
570 }
571 
572 /**
573   @brief Add a CeedQFunction input
574 
575   @param qf         CeedQFunction
576   @param fieldname  Name of QFunction field
577   @param size       Size of QFunction field, (ncomp * dim) for @ref CEED_EVAL_GRAD or
578                       (ncomp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
579   @param emode      \ref CEED_EVAL_NONE to use values directly,
580                       \ref CEED_EVAL_INTERP to use interpolated values,
581                       \ref CEED_EVAL_GRAD to use gradients.
582 
583   @return An error code: 0 - success, otherwise - failure
584 
585   @ref User
586 **/
587 int CeedQFunctionAddInput(CeedQFunction qf, const char *fieldname, CeedInt size,
588                           CeedEvalMode emode) {
589   int ierr = CeedQFunctionFieldSet(&qf->inputfields[qf->numinputfields],
590                                    fieldname, size, emode);
591   CeedChk(ierr);
592   qf->numinputfields++;
593   return 0;
594 }
595 
596 /**
597   @brief Add a CeedQFunction output
598 
599   @param qf         CeedQFunction
600   @param fieldname  Name of QFunction field
601   @param size       Size of QFunction field, (ncomp * dim) for @ref CEED_EVAL_GRAD or
602                       (ncomp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
603   @param emode      \ref CEED_EVAL_NONE to use values directly,
604                       \ref CEED_EVAL_INTERP to use interpolated values,
605                       \ref CEED_EVAL_GRAD to use gradients.
606 
607   @return An error code: 0 - success, otherwise - failure
608 
609   @ref User
610 **/
611 int CeedQFunctionAddOutput(CeedQFunction qf, const char *fieldname,
612                            CeedInt size, CeedEvalMode emode) {
613   if (emode == CEED_EVAL_WEIGHT)
614     // LCOV_EXCL_START
615     return CeedError(qf->ceed, 1, "Cannot create QFunction output with "
616                      "CEED_EVAL_WEIGHT");
617   // LCOV_EXCL_STOP
618   int ierr = CeedQFunctionFieldSet(&qf->outputfields[qf->numoutputfields],
619                                    fieldname, size, emode);
620   CeedChk(ierr);
621   qf->numoutputfields++;
622   return 0;
623 }
624 
625 /**
626   @brief Set global context for a CeedQFunction
627 
628   @param qf       CeedQFunction
629   @param ctx      Context data to set
630 
631   @return An error code: 0 - success, otherwise - failure
632 
633   @ref User
634 **/
635 int CeedQFunctionSetContext(CeedQFunction qf, CeedQFunctionContext ctx) {
636   qf->ctx = ctx;
637   ctx->refcount++;
638   return 0;
639 }
640 
641 /**
642   @brief View a CeedQFunction
643 
644   @param[in] qf      CeedQFunction to view
645   @param[in] stream  Stream to write; typically stdout/stderr or a file
646 
647   @return Error code: 0 - success, otherwise - failure
648 
649   @ref User
650 **/
651 int CeedQFunctionView(CeedQFunction qf, FILE *stream) {
652   int ierr;
653 
654   fprintf(stream, "%sCeedQFunction %s\n",
655           qf->qfname ? "Gallery " : "User ", qf->qfname ? qf->qfname : "");
656 
657   fprintf(stream, "  %d Input Field%s:\n", qf->numinputfields,
658           qf->numinputfields>1 ? "s" : "");
659   for (CeedInt i=0; i<qf->numinputfields; i++) {
660     ierr = CeedQFunctionFieldView(qf->inputfields[i], i, 1, stream);
661     CeedChk(ierr);
662   }
663 
664   fprintf(stream, "  %d Output Field%s:\n", qf->numoutputfields,
665           qf->numoutputfields>1 ? "s" : "");
666   for (CeedInt i=0; i<qf->numoutputfields; i++) {
667     ierr = CeedQFunctionFieldView(qf->outputfields[i], i, 0, stream);
668     CeedChk(ierr);
669   }
670   return 0;
671 }
672 
673 /**
674   @brief Apply the action of a CeedQFunction
675 
676   @param qf      CeedQFunction
677   @param Q       Number of quadrature points
678   @param[in] u   Array of input CeedVectors
679   @param[out] v  Array of output CeedVectors
680 
681   @return An error code: 0 - success, otherwise - failure
682 
683   @ref User
684 **/
685 int CeedQFunctionApply(CeedQFunction qf, CeedInt Q,
686                        CeedVector *u, CeedVector *v) {
687   int ierr;
688   if (!qf->Apply)
689     // LCOV_EXCL_START
690     return CeedError(qf->ceed, 1, "Backend does not support QFunctionApply");
691   // LCOV_EXCL_STOP
692   if (Q % qf->vlength)
693     // LCOV_EXCL_START
694     return CeedError(qf->ceed, 2, "Number of quadrature points %d must be a "
695                      "multiple of %d", Q, qf->vlength);
696   // LCOV_EXCL_STOP
697   ierr = qf->Apply(qf, Q, u, v); CeedChk(ierr);
698   return 0;
699 }
700 
701 /**
702   @brief Destroy a CeedQFunction
703 
704   @param qf CeedQFunction to destroy
705 
706   @return An error code: 0 - success, otherwise - failure
707 
708   @ref User
709 **/
710 int CeedQFunctionDestroy(CeedQFunction *qf) {
711   int ierr;
712 
713   if (!*qf || --(*qf)->refcount > 0) return 0;
714   // Backend destroy
715   if ((*qf)->Destroy) {
716     ierr = (*qf)->Destroy(*qf); CeedChk(ierr);
717   }
718   // Free fields
719   for (int i=0; i<(*qf)->numinputfields; i++) {
720     ierr = CeedFree(&(*(*qf)->inputfields[i]).fieldname); CeedChk(ierr);
721     ierr = CeedFree(&(*qf)->inputfields[i]); CeedChk(ierr);
722   }
723   for (int i=0; i<(*qf)->numoutputfields; i++) {
724     ierr = CeedFree(&(*(*qf)->outputfields[i]).fieldname); CeedChk(ierr);
725     ierr = CeedFree(&(*qf)->outputfields[i]); CeedChk(ierr);
726   }
727   ierr = CeedFree(&(*qf)->inputfields); CeedChk(ierr);
728   ierr = CeedFree(&(*qf)->outputfields); CeedChk(ierr);
729 
730   // User context data object
731   ierr = CeedQFunctionContextDestroy(&(*qf)->ctx); CeedChk(ierr);
732 
733   ierr = CeedFree(&(*qf)->sourcepath); CeedChk(ierr);
734   ierr = CeedFree(&(*qf)->qfname); CeedChk(ierr);
735   ierr = CeedDestroy(&(*qf)->ceed); CeedChk(ierr);
736   ierr = CeedFree(qf); CeedChk(ierr);
737   return 0;
738 }
739 
740 /// @}
741