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