xref: /libCEED/interface/ceed-qfunction.c (revision d34e270f91eeccdaafc024e5afc9304ed3811350)
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/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed/jit-tools.h>
20 #include <ceed-impl.h>
21 #include <limits.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @file
28 /// Implementation of public CeedQFunction interfaces
29 
30 /// @cond DOXYGEN_SKIP
31 static struct CeedQFunction_private ceed_qfunction_none;
32 /// @endcond
33 
34 /// @addtogroup CeedQFunctionUser
35 /// @{
36 
37 // Indicate that no QFunction is provided by the user
38 const CeedQFunction CEED_QFUNCTION_NONE = &ceed_qfunction_none;
39 
40 /// @}
41 
42 /// @cond DOXYGEN_SKIP
43 static struct {
44   char name[CEED_MAX_RESOURCE_LEN];
45   char source[CEED_MAX_RESOURCE_LEN];
46   CeedInt vec_length;
47   CeedQFunctionUser f;
48   int (*init)(Ceed ceed, const char *name, CeedQFunction qf);
49 } gallery_qfunctions[1024];
50 static size_t num_qfunctions;
51 /// @endcond
52 
53 /// ----------------------------------------------------------------------------
54 /// CeedQFunction Library Internal Functions
55 /// ----------------------------------------------------------------------------
56 /// @addtogroup CeedQFunctionDeveloper
57 /// @{
58 
59 /**
60   @brief Register a gallery QFunction
61 
62   @param name        Name for this backend to respond to
63   @param source      Absolute path to source of QFunction,
64                        "\path\CEED_DIR\gallery\folder\file.h:function_name"
65   @param vec_length  Vector length.  Caller must ensure that number of quadrature
66                        points is a multiple of vec_length.
67   @param f           Function pointer to evaluate action at quadrature points.
68                        See \ref CeedQFunctionUser.
69   @param init        Initialization function called by CeedQFunctionInit() when the
70                        QFunction is selected.
71 
72   @return An error code: 0 - success, otherwise - failure
73 
74   @ref Developer
75 **/
76 int CeedQFunctionRegister(const char *name, const char *source,
77                           CeedInt vec_length, CeedQFunctionUser f,
78                           int (*init)(Ceed, const char *, CeedQFunction)) {
79   if (num_qfunctions >= sizeof(gallery_qfunctions) / sizeof(
80         gallery_qfunctions[0]))
81     // LCOV_EXCL_START
82     return CeedError(NULL, CEED_ERROR_MAJOR, "Too many gallery QFunctions");
83   // LCOV_EXCL_STOP
84 
85   CeedDebugEnv("Gallery Register: %s", name);
86 
87   strncpy(gallery_qfunctions[num_qfunctions].name, name, CEED_MAX_RESOURCE_LEN);
88   gallery_qfunctions[num_qfunctions].name[CEED_MAX_RESOURCE_LEN-1] = 0;
89   strncpy(gallery_qfunctions[num_qfunctions].source, source,
90           CEED_MAX_RESOURCE_LEN);
91   gallery_qfunctions[num_qfunctions].source[CEED_MAX_RESOURCE_LEN-1] = 0;
92   gallery_qfunctions[num_qfunctions].vec_length = vec_length;
93   gallery_qfunctions[num_qfunctions].f = f;
94   gallery_qfunctions[num_qfunctions].init = init;
95   num_qfunctions++;
96   return CEED_ERROR_SUCCESS;
97 }
98 
99 /**
100   @brief Set a CeedQFunction field, used by CeedQFunctionAddInput/Output
101 
102   @param f           CeedQFunctionField
103   @param field_name  Name of QFunction field
104   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
105                        (num_comp * 1) for @ref CEED_EVAL_NONE, @ref CEED_EVAL_INTERP, and @ref CEED_EVAL_WEIGHT
106   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
107                        \ref CEED_EVAL_INTERP to use interpolated values,
108                        \ref CEED_EVAL_GRAD to use gradients,
109                        \ref CEED_EVAL_WEIGHT to use quadrature weights.
110 
111   @return An error code: 0 - success, otherwise - failure
112 
113   @ref Developer
114 **/
115 static int CeedQFunctionFieldSet(CeedQFunctionField *f, const char *field_name,
116                                  CeedInt size, CeedEvalMode eval_mode) {
117   int ierr;
118 
119   ierr = CeedCalloc(1, f); CeedChk(ierr);
120   ierr = CeedStringAllocCopy(field_name, (char **)&(*f)->field_name);
121   CeedChk(ierr);
122   (*f)->size = size;
123   (*f)->eval_mode = eval_mode;
124   return CEED_ERROR_SUCCESS;
125 }
126 
127 /**
128   @brief View a field of a CeedQFunction
129 
130   @param[in] field         QFunction field to view
131   @param[in] field_number  Number of field being viewed
132   @param[in] in            true for input field, false for output
133   @param[in] stream        Stream to view to, e.g., stdout
134 
135   @return An error code: 0 - success, otherwise - failure
136 
137   @ref Utility
138 **/
139 static int CeedQFunctionFieldView(CeedQFunctionField field,
140                                   CeedInt field_number,
141                                   bool in, FILE *stream) {
142   int ierr;
143   const char *inout = in ? "Input" : "Output";
144   char *field_name;
145   ierr = CeedQFunctionFieldGetName(field, &field_name); CeedChk(ierr);
146   CeedInt size;
147   ierr = CeedQFunctionFieldGetSize(field, &size); CeedChk(ierr);
148   CeedEvalMode eval_mode;
149   ierr = CeedQFunctionFieldGetEvalMode(field, &eval_mode); CeedChk(ierr);
150   fprintf(stream, "    %s Field [%d]:\n"
151           "      Name: \"%s\"\n"
152           "      Size: %d\n"
153           "      EvalMode: \"%s\"\n",
154           inout, field_number, field_name, size, CeedEvalModes[eval_mode]);
155   return CEED_ERROR_SUCCESS;
156 }
157 
158 /**
159   @brief Set flag to determine if Fortran interface is used
160 
161   @param qf      CeedQFunction
162   @param status  Boolean value to set as Fortran status
163 
164   @return An error code: 0 - success, otherwise - failure
165 
166   @ref Backend
167 **/
168 int CeedQFunctionSetFortranStatus(CeedQFunction qf, bool status) {
169   qf->is_fortran = status;
170   return CEED_ERROR_SUCCESS;
171 }
172 
173 /// @}
174 
175 /// ----------------------------------------------------------------------------
176 /// CeedQFunction Backend API
177 /// ----------------------------------------------------------------------------
178 /// @addtogroup CeedQFunctionBackend
179 /// @{
180 
181 /**
182   @brief Get the vector length of a CeedQFunction
183 
184   @param qf               CeedQFunction
185   @param[out] vec_length  Variable to store vector length
186 
187   @return An error code: 0 - success, otherwise - failure
188 
189   @ref Backend
190 **/
191 int CeedQFunctionGetVectorLength(CeedQFunction qf, CeedInt *vec_length) {
192   *vec_length = qf->vec_length;
193   return CEED_ERROR_SUCCESS;
194 }
195 
196 /**
197   @brief Get the number of inputs and outputs to a CeedQFunction
198 
199   @param qf               CeedQFunction
200   @param[out] num_input   Variable to store number of input fields
201   @param[out] num_output  Variable to store number of output fields
202 
203   @return An error code: 0 - success, otherwise - failure
204 
205   @ref Backend
206 **/
207 int CeedQFunctionGetNumArgs(CeedQFunction qf, CeedInt *num_input,
208                             CeedInt *num_output) {
209   if (num_input) *num_input = qf->num_input_fields;
210   if (num_output) *num_output = qf->num_output_fields;
211   return CEED_ERROR_SUCCESS;
212 }
213 
214 /**
215   @brief Get the name of the user function for a CeedQFunction
216 
217   @param qf                CeedQFunction
218   @param[out] kernel_name  Variable to store source path string
219 
220   @return An error code: 0 - success, otherwise - failure
221 
222   @ref Backend
223 **/
224 int CeedQFunctionGetKernelName(CeedQFunction qf, char **kernel_name) {
225   *kernel_name = (char *) qf->kernel_name;
226   return CEED_ERROR_SUCCESS;
227 }
228 
229 /**
230   @brief Get the source path string for a CeedQFunction
231 
232   @param qf                CeedQFunction
233   @param[out] source_path  Variable to store source path string
234 
235   @return An error code: 0 - success, otherwise - failure
236 
237   @ref Backend
238 **/
239 int CeedQFunctionGetSourcePath(CeedQFunction qf, char **source_path) {
240   *source_path = (char *) qf->source_path;
241   return CEED_ERROR_SUCCESS;
242 }
243 
244 /**
245   @brief Initalize and load QFunction source file into string buffer, including
246            full text of local files in place of `#include "local.h"`.
247            The `buffer` is set to `NULL` if there is no QFunction source file.
248          Note: Caller is responsible for freeing the string buffer with `CeedFree()`.
249 
250   @param qf                  CeedQFunction
251   @param[out] source_buffer  String buffer for source file contents
252 
253   @return An error code: 0 - success, otherwise - failure
254 
255   @ref Backend
256 **/
257 int CeedQFunctionLoadSourceToBuffer(CeedQFunction qf, char **source_buffer) {
258   int ierr;
259   char *source_path;
260 
261   ierr = CeedQFunctionGetSourcePath(qf, &source_path); CeedChk(ierr);
262   *source_buffer = NULL;
263   if (source_path) {
264     ierr = CeedLoadSourceToBuffer(qf->ceed, source_path, source_buffer);
265     CeedChk(ierr);
266   }
267 
268   return CEED_ERROR_SUCCESS;
269 }
270 
271 /**
272   @brief Get the User Function for a CeedQFunction
273 
274   @param qf      CeedQFunction
275   @param[out] f  Variable to store user function
276 
277   @return An error code: 0 - success, otherwise - failure
278 
279   @ref Backend
280 **/
281 int CeedQFunctionGetUserFunction(CeedQFunction qf, CeedQFunctionUser *f) {
282   *f = qf->function;
283   return CEED_ERROR_SUCCESS;
284 }
285 
286 /**
287   @brief Get global context for a CeedQFunction.
288            Note: For QFunctions from the Fortran interface, this
289              function will return the Fortran context
290              CeedQFunctionContext.
291 
292   @param qf        CeedQFunction
293   @param[out] ctx  Variable to store CeedQFunctionContext
294 
295   @return An error code: 0 - success, otherwise - failure
296 
297   @ref Backend
298 **/
299 int CeedQFunctionGetContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
300   *ctx = qf->ctx;
301   return CEED_ERROR_SUCCESS;
302 }
303 
304 /**
305   @brief Get context data of a CeedQFunction
306 
307   @param qf         CeedQFunction
308   @param mem_type   Memory type on which to access the data. If the backend
309                       uses a different memory type, this will perform a copy.
310   @param[out] data  Data on memory type mem_type
311 
312   @return An error code: 0 - success, otherwise - failure
313 
314   @ref Backend
315 **/
316 int CeedQFunctionGetContextData(CeedQFunction qf, CeedMemType mem_type,
317                                 void *data) {
318   int ierr;
319   bool is_writable;
320   CeedQFunctionContext ctx;
321 
322   ierr = CeedQFunctionGetContext(qf, &ctx); CeedChk(ierr);
323   if (ctx) {
324     ierr = CeedQFunctionIsContextWritable(qf, &is_writable); CeedChk(ierr);
325     if (is_writable) {
326       ierr = CeedQFunctionContextGetData(ctx, mem_type, data); CeedChk(ierr);
327     } else {
328       ierr = CeedQFunctionContextGetDataRead(ctx, mem_type, data); CeedChk(ierr);
329     }
330   } else {
331     *(void **)data = NULL;
332   }
333   return CEED_ERROR_SUCCESS;
334 }
335 
336 /**
337   @brief Restore context data of a CeedQFunction
338 
339   @param qf    CeedQFunction
340   @param data  Data to restore
341 
342   @return An error code: 0 - success, otherwise - failure
343 
344   @ref Backend
345 **/
346 int CeedQFunctionRestoreContextData(CeedQFunction qf, void *data) {
347   int ierr;
348   bool is_writable;
349   CeedQFunctionContext ctx;
350 
351   ierr = CeedQFunctionGetContext(qf, &ctx); CeedChk(ierr);
352   if (ctx) {
353     ierr = CeedQFunctionIsContextWritable(qf, &is_writable); CeedChk(ierr);
354     if (is_writable) {
355       ierr = CeedQFunctionContextRestoreData(ctx, data); CeedChk(ierr);
356     } else {
357       ierr = CeedQFunctionContextRestoreDataRead(ctx, data); CeedChk(ierr);
358     }
359   }
360   return CEED_ERROR_SUCCESS;
361 }
362 
363 /**
364   @brief Get true user context for a CeedQFunction
365            Note: For all QFunctions this function will return the user
366              CeedQFunctionContext and not interface context
367              CeedQFunctionContext, if any such object exists.
368 
369   @param qf        CeedQFunction
370   @param[out] ctx  Variable to store CeedQFunctionContext
371 
372   @return An error code: 0 - success, otherwise - failure
373   @ref Backend
374 **/
375 int CeedQFunctionGetInnerContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
376   int ierr;
377   if (qf->is_fortran) {
378     CeedFortranContext fortran_ctx = NULL;
379     ierr = CeedQFunctionContextGetData(qf->ctx, CEED_MEM_HOST, &fortran_ctx);
380     CeedChk(ierr);
381     *ctx = fortran_ctx->inner_ctx;
382     ierr = CeedQFunctionContextRestoreData(qf->ctx, (void *)&fortran_ctx);
383     CeedChk(ierr);
384   } else {
385     *ctx = qf->ctx;
386   }
387   return CEED_ERROR_SUCCESS;
388 }
389 
390 /**
391   @brief Get inner context data of a CeedQFunction
392 
393   @param qf         CeedQFunction
394   @param mem_type   Memory type on which to access the data. If the backend
395                       uses a different memory type, this will perform a copy.
396   @param[out] data  Data on memory type mem_type
397 
398   @return An error code: 0 - success, otherwise - failure
399 
400   @ref Backend
401 **/
402 int CeedQFunctionGetInnerContextData(CeedQFunction qf, CeedMemType mem_type,
403                                      void *data) {
404   int ierr;
405   bool is_writable;
406   CeedQFunctionContext ctx;
407 
408   ierr = CeedQFunctionGetInnerContext(qf, &ctx); CeedChk(ierr);
409   if (ctx) {
410     ierr = CeedQFunctionIsContextWritable(qf, &is_writable); CeedChk(ierr);
411     if (is_writable) {
412       ierr = CeedQFunctionContextGetData(ctx, mem_type, data); CeedChk(ierr);
413     } else {
414       ierr = CeedQFunctionContextGetDataRead(ctx, mem_type, data); CeedChk(ierr);
415     }
416   } else {
417     *(void **)data = NULL;
418   }
419   return CEED_ERROR_SUCCESS;
420 }
421 
422 /**
423   @brief Restore inner context data of a CeedQFunction
424 
425   @param qf    CeedQFunction
426   @param data  Data to restore
427 
428   @return An error code: 0 - success, otherwise - failure
429 
430   @ref Backend
431 **/
432 int CeedQFunctionRestoreInnerContextData(CeedQFunction qf, void *data) {
433   int ierr;
434   bool is_writable;
435   CeedQFunctionContext ctx;
436 
437   ierr = CeedQFunctionGetInnerContext(qf, &ctx); CeedChk(ierr);
438   if (ctx) {
439     ierr = CeedQFunctionIsContextWritable(qf, &is_writable); CeedChk(ierr);
440     if (is_writable) {
441       ierr = CeedQFunctionContextRestoreData(ctx, data); CeedChk(ierr);
442     } else {
443       ierr = CeedQFunctionContextRestoreDataRead(ctx, data); CeedChk(ierr);
444     }
445   }
446   return CEED_ERROR_SUCCESS;
447 }
448 
449 /**
450   @brief Determine if QFunction is identity
451 
452   @param qf                CeedQFunction
453   @param[out] is_identity  Variable to store identity status
454 
455   @return An error code: 0 - success, otherwise - failure
456 
457   @ref Backend
458 **/
459 int CeedQFunctionIsIdentity(CeedQFunction qf, bool *is_identity) {
460   *is_identity = qf->is_identity;
461   return CEED_ERROR_SUCCESS;
462 }
463 
464 /**
465   @brief Determine if QFunctionContext is writable
466 
467   @param qf                CeedQFunction
468   @param[out] is_writable  Variable to store context writeable staus
469 
470   @return An error code: 0 - success, otherwise - failure
471 
472   @ref Backend
473 **/
474 int CeedQFunctionIsContextWritable(CeedQFunction qf, bool *is_writable) {
475   *is_writable = qf->is_context_writable;
476   return CEED_ERROR_SUCCESS;
477 }
478 
479 /**
480   @brief Get backend data of a CeedQFunction
481 
482   @param qf         CeedQFunction
483   @param[out] data  Variable to store data
484 
485   @return An error code: 0 - success, otherwise - failure
486 
487   @ref Backend
488 **/
489 int CeedQFunctionGetData(CeedQFunction qf, void *data) {
490   *(void **)data = qf->data;
491   return CEED_ERROR_SUCCESS;
492 }
493 
494 /**
495   @brief Set backend data of a CeedQFunction
496 
497   @param[out] qf  CeedQFunction
498   @param data     Data to set
499 
500   @return An error code: 0 - success, otherwise - failure
501 
502   @ref Backend
503 **/
504 int CeedQFunctionSetData(CeedQFunction qf, void *data) {
505   qf->data = data;
506   return CEED_ERROR_SUCCESS;
507 }
508 
509 /**
510   @brief Increment the reference counter for a CeedQFunction
511 
512   @param qf  CeedQFunction to increment the reference counter
513 
514   @return An error code: 0 - success, otherwise - failure
515 
516   @ref Backend
517 **/
518 int CeedQFunctionReference(CeedQFunction qf) {
519   qf->ref_count++;
520   return CEED_ERROR_SUCCESS;
521 }
522 
523 /// @}
524 
525 /// ----------------------------------------------------------------------------
526 /// CeedQFunction Public API
527 /// ----------------------------------------------------------------------------
528 /// @addtogroup CeedQFunctionUser
529 /// @{
530 
531 /**
532   @brief Create a CeedQFunction for evaluating interior (volumetric) terms.
533 
534   @param ceed        A Ceed object where the CeedQFunction will be created
535   @param vec_length  Vector length. Caller must ensure that number of quadrature
536                        points is a multiple of vec_length.
537   @param f           Function pointer to evaluate action at quadrature points.
538                        See \ref CeedQFunctionUser.
539   @param source      Absolute path to source of QFunction,
540                        "\abs_path\file.h:function_name".
541                        For support across all backends, this source must only
542                        contain constructs supported by C99, C++11, and CUDA.
543   @param[out] qf     Address of the variable where the newly created
544                        CeedQFunction will be stored
545 
546   @return An error code: 0 - success, otherwise - failure
547 
548   See \ref CeedQFunctionUser for details on the call-back function @a f's
549     arguments.
550 
551   @ref User
552 **/
553 int CeedQFunctionCreateInterior(Ceed ceed, CeedInt vec_length,
554                                 CeedQFunctionUser f,
555                                 const char *source, CeedQFunction *qf) {
556   int ierr;
557   char *source_copy, *kernel_name_copy;
558 
559   if (!ceed->QFunctionCreate) {
560     Ceed delegate;
561     ierr = CeedGetObjectDelegate(ceed, &delegate, "QFunction"); CeedChk(ierr);
562 
563     if (!delegate)
564       // LCOV_EXCL_START
565       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
566                        "Backend does not support QFunctionCreate");
567     // LCOV_EXCL_STOP
568 
569     ierr = CeedQFunctionCreateInterior(delegate, vec_length, f, source, qf);
570     CeedChk(ierr);
571     return CEED_ERROR_SUCCESS;
572   }
573 
574   if (strlen(source) && !strrchr(source, ':'))
575     // LCOV_EXCL_START
576     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
577                      "Provided path to source does not include function name. "
578                      "Provided: \"%s\"\nRequired: \"\\abs_path\\file.h:function_name\"",
579                      source);
580   // LCOV_EXCL_STOP
581 
582   ierr = CeedCalloc(1, qf); CeedChk(ierr);
583   (*qf)->ceed = ceed;
584   ierr = CeedReference(ceed); CeedChk(ierr);
585   (*qf)->ref_count = 1;
586   (*qf)->vec_length = vec_length;
587   (*qf)->is_identity = false;
588   (*qf)->is_context_writable = true;
589   (*qf)->function = f;
590   if (strlen(source)) {
591     const char *kernel_name = strrchr(source, ':') + 1;
592     size_t kernel_name_len = strlen(kernel_name);
593     ierr = CeedCalloc(kernel_name_len + 1, &kernel_name_copy); CeedChk(ierr);
594     strncpy(kernel_name_copy, kernel_name, kernel_name_len);
595     (*qf)->kernel_name = kernel_name_copy;
596 
597     size_t source_len = strlen(source) - kernel_name_len - 1;
598     ierr = CeedCalloc(source_len + 1, &source_copy); CeedChk(ierr);
599     strncpy(source_copy, source, source_len);
600     (*qf)->source_path = source_copy;
601   }
602   ierr = CeedCalloc(CEED_FIELD_MAX, &(*qf)->input_fields); CeedChk(ierr);
603   ierr = CeedCalloc(CEED_FIELD_MAX, &(*qf)->output_fields); CeedChk(ierr);
604   ierr = ceed->QFunctionCreate(*qf); CeedChk(ierr);
605   return CEED_ERROR_SUCCESS;
606 }
607 
608 /**
609   @brief Create a CeedQFunction for evaluating interior (volumetric) terms by name.
610 
611   @param ceed     A Ceed object where the CeedQFunction will be created
612   @param name     Name of QFunction to use from gallery
613   @param[out] qf  Address of the variable where the newly created
614                     CeedQFunction will be stored
615 
616   @return An error code: 0 - success, otherwise - failure
617 
618   @ref User
619 **/
620 int CeedQFunctionCreateInteriorByName(Ceed ceed,  const char *name,
621                                       CeedQFunction *qf) {
622   int ierr;
623   size_t match_len = 0, match_index = UINT_MAX;
624 
625   ierr = CeedQFunctionRegisterAll(); CeedChk(ierr);
626   // Find matching backend
627   if (!name) return CeedError(ceed, CEED_ERROR_INCOMPLETE,
628                                 "No QFunction name provided");
629   for (size_t i=0; i<num_qfunctions; i++) {
630     size_t n;
631     const char *curr_name = gallery_qfunctions[i].name;
632     for (n = 0; curr_name[n] && curr_name[n] == name[n]; n++) {}
633     if (n > match_len) {
634       match_len = n;
635       match_index = i;
636     }
637   }
638   if (!match_len)
639     // LCOV_EXCL_START
640     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "No suitable gallery QFunction");
641   // LCOV_EXCL_STOP
642 
643   // Create QFunction
644   ierr = CeedQFunctionCreateInterior(ceed,
645                                      gallery_qfunctions[match_index].vec_length,
646                                      gallery_qfunctions[match_index].f,
647                                      gallery_qfunctions[match_index].source, qf);
648   CeedChk(ierr);
649 
650   // QFunction specific setup
651   ierr = gallery_qfunctions[match_index].init(ceed, name, *qf); CeedChk(ierr);
652 
653   // Copy name
654   ierr = CeedStringAllocCopy(name, (char **)&(*qf)->gallery_name); CeedChk(ierr);
655   (*qf)->is_gallery = true;
656   return CEED_ERROR_SUCCESS;
657 }
658 
659 /**
660   @brief Create an identity CeedQFunction. Inputs are written into outputs in
661            the order given. This is useful for CeedOperators that can be
662            represented with only the action of a CeedRestriction and CeedBasis,
663            such as restriction and prolongation operators for p-multigrid.
664            Backends may optimize CeedOperators with this CeedQFunction to avoid
665            the copy of input data to output fields by using the same memory
666            location for both.
667 
668   @param ceed          A Ceed object where the CeedQFunction will be created
669   @param[in] size      Size of the QFunction fields
670   @param[in] in_mode   CeedEvalMode for input to CeedQFunction
671   @param[in] out_mode  CeedEvalMode for output to CeedQFunction
672   @param[out] qf       Address of the variable where the newly created
673                          CeedQFunction will be stored
674 
675   @return An error code: 0 - success, otherwise - failure
676 
677   @ref User
678 **/
679 int CeedQFunctionCreateIdentity(Ceed ceed, CeedInt size, CeedEvalMode in_mode,
680                                 CeedEvalMode out_mode, CeedQFunction *qf) {
681   int ierr;
682 
683   ierr = CeedQFunctionCreateInteriorByName(ceed, "Identity", qf); CeedChk(ierr);
684   ierr = CeedQFunctionAddInput(*qf, "input", size, in_mode); CeedChk(ierr);
685   ierr = CeedQFunctionAddOutput(*qf, "output", size, out_mode); CeedChk(ierr);
686 
687   (*qf)->is_identity = true;
688 
689   CeedQFunctionContext ctx;
690   CeedContextFieldLabel size_label;
691   ierr = CeedQFunctionGetContext(*qf, &ctx); CeedChk(ierr);
692   ierr = CeedQFunctionContextGetFieldLabel(ctx, "size", &size_label);
693   CeedChk(ierr);
694   ierr = CeedQFunctionContextSetInt32(ctx, size_label, &size); CeedChk(ierr);
695 
696   return CEED_ERROR_SUCCESS;
697 }
698 
699 /**
700   @brief Copy the pointer to a CeedQFunction. Both pointers should
701            be destroyed with `CeedQFunctionDestroy()`;
702            Note: If `*qf_copy` is non-NULL, then it is assumed that
703            `*qf_copy` is a pointer to a CeedQFunction. This
704            CeedQFunction will be destroyed if `*qf_copy` is the only
705            reference to this CeedQFunction.
706 
707   @param qf            CeedQFunction to copy reference to
708   @param[out] qf_copy  Variable to store copied reference
709 
710   @return An error code: 0 - success, otherwise - failure
711 
712   @ref User
713 **/
714 int CeedQFunctionReferenceCopy(CeedQFunction qf, CeedQFunction *qf_copy) {
715   int ierr;
716 
717   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
718   ierr = CeedQFunctionDestroy(qf_copy); CeedChk(ierr);
719   *qf_copy = qf;
720   return CEED_ERROR_SUCCESS;
721 }
722 
723 /**
724   @brief Add a CeedQFunction input
725 
726   @param qf          CeedQFunction
727   @param field_name  Name of QFunction field
728   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
729                        (num_comp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
730   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
731                        \ref CEED_EVAL_INTERP to use interpolated values,
732                        \ref CEED_EVAL_GRAD to use gradients.
733 
734   @return An error code: 0 - success, otherwise - failure
735 
736   @ref User
737 **/
738 int CeedQFunctionAddInput(CeedQFunction qf, const char *field_name,
739                           CeedInt size,
740                           CeedEvalMode eval_mode) {
741   if (qf->is_immutable)
742     // LCOV_EXCL_START
743     return CeedError(qf->ceed, CEED_ERROR_MAJOR,
744                      "QFunction cannot be changed after set as immutable");
745   // LCOV_EXCL_STOP
746   if ((eval_mode == CEED_EVAL_WEIGHT) && (size != 1))
747     // LCOV_EXCL_START
748     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
749                      "CEED_EVAL_WEIGHT should have size 1");
750   // LCOV_EXCL_STOP
751   int ierr = CeedQFunctionFieldSet(&qf->input_fields[qf->num_input_fields],
752                                    field_name, size, eval_mode);
753   CeedChk(ierr);
754   qf->num_input_fields++;
755   return CEED_ERROR_SUCCESS;
756 }
757 
758 /**
759   @brief Add a CeedQFunction output
760 
761   @param qf          CeedQFunction
762   @param field_name  Name of QFunction field
763   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
764                        (num_comp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
765   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
766                        \ref CEED_EVAL_INTERP to use interpolated values,
767                        \ref CEED_EVAL_GRAD to use gradients.
768 
769   @return An error code: 0 - success, otherwise - failure
770 
771   @ref User
772 **/
773 int CeedQFunctionAddOutput(CeedQFunction qf, const char *field_name,
774                            CeedInt size, CeedEvalMode eval_mode) {
775   if (qf->is_immutable)
776     // LCOV_EXCL_START
777     return CeedError(qf->ceed, CEED_ERROR_MAJOR,
778                      "QFunction cannot be changed after set as immutable");
779   // LCOV_EXCL_STOP
780   if (eval_mode == CEED_EVAL_WEIGHT)
781     // LCOV_EXCL_START
782     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
783                      "Cannot create QFunction output with "
784                      "CEED_EVAL_WEIGHT");
785   // LCOV_EXCL_STOP
786   int ierr = CeedQFunctionFieldSet(&qf->output_fields[qf->num_output_fields],
787                                    field_name, size, eval_mode);
788   CeedChk(ierr);
789   qf->num_output_fields++;
790   return CEED_ERROR_SUCCESS;
791 }
792 
793 /**
794   @brief Get the CeedQFunctionFields of a CeedQFunction
795 
796   Note: Calling this function asserts that setup is complete
797           and sets the CeedQFunction as immutable.
798 
799   @param qf                      CeedQFunction
800   @param[out] num_input_fields   Variable to store number of input fields
801   @param[out] input_fields       Variable to store input fields
802   @param[out] num_output_fields  Variable to store number of output fields
803   @param[out] output_fields      Variable to store output fields
804 
805   @return An error code: 0 - success, otherwise - failure
806 
807   @ref Advanced
808 **/
809 int CeedQFunctionGetFields(CeedQFunction qf, CeedInt *num_input_fields,
810                            CeedQFunctionField **input_fields,
811                            CeedInt *num_output_fields,
812                            CeedQFunctionField **output_fields) {
813   qf->is_immutable = true;
814   if (num_input_fields) *num_input_fields = qf->num_input_fields;
815   if (input_fields) *input_fields = qf->input_fields;
816   if (num_output_fields) *num_output_fields = qf->num_output_fields;
817   if (output_fields) *output_fields = qf->output_fields;
818   return CEED_ERROR_SUCCESS;
819 }
820 
821 /**
822   @brief Get the name of a CeedQFunctionField
823 
824   @param qf_field         CeedQFunctionField
825   @param[out] field_name  Variable to store the field name
826 
827   @return An error code: 0 - success, otherwise - failure
828 
829   @ref Advanced
830 **/
831 int CeedQFunctionFieldGetName(CeedQFunctionField qf_field, char **field_name) {
832   *field_name = (char *)qf_field->field_name;
833   return CEED_ERROR_SUCCESS;
834 }
835 
836 /**
837   @brief Get the number of components of a CeedQFunctionField
838 
839   @param qf_field   CeedQFunctionField
840   @param[out] size  Variable to store the size of the field
841 
842   @return An error code: 0 - success, otherwise - failure
843 
844   @ref Advanced
845 **/
846 int CeedQFunctionFieldGetSize(CeedQFunctionField qf_field, CeedInt *size) {
847   *size = qf_field->size;
848   return CEED_ERROR_SUCCESS;
849 }
850 
851 /**
852   @brief Get the CeedEvalMode of a CeedQFunctionField
853 
854   @param qf_field        CeedQFunctionField
855   @param[out] eval_mode  Variable to store the field evaluation mode
856 
857   @return An error code: 0 - success, otherwise - failure
858 
859   @ref Advanced
860 **/
861 int CeedQFunctionFieldGetEvalMode(CeedQFunctionField qf_field,
862                                   CeedEvalMode *eval_mode) {
863   *eval_mode = qf_field->eval_mode;
864   return CEED_ERROR_SUCCESS;
865 }
866 
867 /**
868   @brief Set global context for a CeedQFunction
869 
870   @param qf   CeedQFunction
871   @param ctx  Context data to set
872 
873   @return An error code: 0 - success, otherwise - failure
874 
875   @ref User
876 **/
877 int CeedQFunctionSetContext(CeedQFunction qf, CeedQFunctionContext ctx) {
878   int ierr;
879   ierr = CeedQFunctionContextDestroy(&qf->ctx); CeedChk(ierr);
880   qf->ctx = ctx;
881   ierr = CeedQFunctionContextReference(ctx); CeedChk(ierr);
882   return CEED_ERROR_SUCCESS;
883 }
884 
885 /**
886   @brief Set writability of CeedQFunctionContext when calling the `CeedQFunctionUser`.
887            The default value is 'is_writable == true'.
888 
889            Setting `is_writable == true` indicates the `CeedQFunctionUser` writes
890            into the CeedQFunctionContextData and requires memory syncronization
891            after calling `CeedQFunctionApply()`.
892 
893            Setting 'is_writable == false' asserts that `CeedQFunctionUser` does not
894            modify the CeedQFunctionContextData. Violating this assertion may lead
895            to inconsistent data.
896 
897            Setting `is_writable == false` may offer a performance improvement on GPU backends.
898 
899   @param qf           CeedQFunction
900   @param is_writable  Writability status
901 
902   @return An error code: 0 - success, otherwise - failure
903 
904   @ref User
905 **/
906 int CeedQFunctionSetContextWritable(CeedQFunction qf, bool is_writable) {
907   qf->is_context_writable = is_writable;
908   return CEED_ERROR_SUCCESS;
909 }
910 
911 /**
912   @brief View a CeedQFunction
913 
914   @param[in] qf      CeedQFunction to view
915   @param[in] stream  Stream to write; typically stdout/stderr or a file
916 
917   @return Error code: 0 - success, otherwise - failure
918 
919   @ref User
920 **/
921 int CeedQFunctionView(CeedQFunction qf, FILE *stream) {
922   int ierr;
923 
924   fprintf(stream, "%sCeedQFunction %s\n",
925           qf->is_gallery ? "Gallery " : "User ",
926           qf->is_gallery ? qf->gallery_name : qf->kernel_name);
927 
928   fprintf(stream, "  %d Input Field%s:\n", qf->num_input_fields,
929           qf->num_input_fields>1 ? "s" : "");
930   for (CeedInt i=0; i<qf->num_input_fields; i++) {
931     ierr = CeedQFunctionFieldView(qf->input_fields[i], i, 1, stream);
932     CeedChk(ierr);
933   }
934 
935   fprintf(stream, "  %d Output Field%s:\n", qf->num_output_fields,
936           qf->num_output_fields>1 ? "s" : "");
937   for (CeedInt i=0; i<qf->num_output_fields; i++) {
938     ierr = CeedQFunctionFieldView(qf->output_fields[i], i, 0, stream);
939     CeedChk(ierr);
940   }
941   return CEED_ERROR_SUCCESS;
942 }
943 
944 /**
945   @brief Get the Ceed associated with a CeedQFunction
946 
947   @param qf              CeedQFunction
948   @param[out] ceed       Variable to store Ceed
949 
950   @return An error code: 0 - success, otherwise - failure
951 
952   @ref Advanced
953 **/
954 int CeedQFunctionGetCeed(CeedQFunction qf, Ceed *ceed) {
955   *ceed = qf->ceed;
956   return CEED_ERROR_SUCCESS;
957 }
958 
959 /**
960   @brief Apply the action of a CeedQFunction
961 
962   Note: Calling this function asserts that setup is complete
963           and sets the CeedQFunction as immutable.
964 
965   @param qf      CeedQFunction
966   @param Q       Number of quadrature points
967   @param[in] u   Array of input CeedVectors
968   @param[out] v  Array of output CeedVectors
969 
970   @return An error code: 0 - success, otherwise - failure
971 
972   @ref User
973 **/
974 int CeedQFunctionApply(CeedQFunction qf, CeedInt Q,
975                        CeedVector *u, CeedVector *v) {
976   int ierr;
977   if (!qf->Apply)
978     // LCOV_EXCL_START
979     return CeedError(qf->ceed, CEED_ERROR_UNSUPPORTED,
980                      "Backend does not support QFunctionApply");
981   // LCOV_EXCL_STOP
982   if (Q % qf->vec_length)
983     // LCOV_EXCL_START
984     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
985                      "Number of quadrature points %d must be a "
986                      "multiple of %d", Q, qf->vec_length);
987   // LCOV_EXCL_STOP
988   qf->is_immutable = true;
989   ierr = qf->Apply(qf, Q, u, v); CeedChk(ierr);
990   return CEED_ERROR_SUCCESS;
991 }
992 
993 /**
994   @brief Destroy a CeedQFunction
995 
996   @param qf  CeedQFunction to destroy
997 
998   @return An error code: 0 - success, otherwise - failure
999 
1000   @ref User
1001 **/
1002 int CeedQFunctionDestroy(CeedQFunction *qf) {
1003   int ierr;
1004 
1005   if (!*qf || --(*qf)->ref_count > 0) return CEED_ERROR_SUCCESS;
1006   // Backend destroy
1007   if ((*qf)->Destroy) {
1008     ierr = (*qf)->Destroy(*qf); CeedChk(ierr);
1009   }
1010   // Free fields
1011   for (int i=0; i<(*qf)->num_input_fields; i++) {
1012     ierr = CeedFree(&(*(*qf)->input_fields[i]).field_name); CeedChk(ierr);
1013     ierr = CeedFree(&(*qf)->input_fields[i]); CeedChk(ierr);
1014   }
1015   for (int i=0; i<(*qf)->num_output_fields; i++) {
1016     ierr = CeedFree(&(*(*qf)->output_fields[i]).field_name); CeedChk(ierr);
1017     ierr = CeedFree(&(*qf)->output_fields[i]); CeedChk(ierr);
1018   }
1019   ierr = CeedFree(&(*qf)->input_fields); CeedChk(ierr);
1020   ierr = CeedFree(&(*qf)->output_fields); CeedChk(ierr);
1021 
1022   // User context data object
1023   ierr = CeedQFunctionContextDestroy(&(*qf)->ctx); CeedChk(ierr);
1024 
1025   ierr = CeedFree(&(*qf)->source_path); CeedChk(ierr);
1026   ierr = CeedFree(&(*qf)->gallery_name); CeedChk(ierr);
1027   ierr = CeedFree(&(*qf)->kernel_name); CeedChk(ierr);
1028   ierr = CeedDestroy(&(*qf)->ceed); CeedChk(ierr);
1029   ierr = CeedFree(qf); CeedChk(ierr);
1030   return CEED_ERROR_SUCCESS;
1031 }
1032 
1033 /// @}
1034