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