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