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