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