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