xref: /libCEED/interface/ceed-qfunction.c (revision 6a6224a1a47cd78a9f5d31ac282da39a8c250ecc)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #include <ceed/ceed.h>
18 #include <ceed/backend.h>
19 #include <ceed/jit-tools.h>
20 #include <ceed-impl.h>
21 #include <limits.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @file
28 /// Implementation of public CeedQFunction interfaces
29 
30 /// @cond DOXYGEN_SKIP
31 static struct CeedQFunction_private ceed_qfunction_none;
32 /// @endcond
33 
34 /// @addtogroup CeedQFunctionUser
35 /// @{
36 
37 // Indicate that no QFunction is provided by the user
38 const CeedQFunction CEED_QFUNCTION_NONE = &ceed_qfunction_none;
39 
40 /// @}
41 
42 /// @cond DOXYGEN_SKIP
43 static struct {
44   char name[CEED_MAX_RESOURCE_LEN];
45   char source[CEED_MAX_RESOURCE_LEN];
46   CeedInt vec_length;
47   CeedQFunctionUser f;
48   int (*init)(Ceed ceed, const char *name, CeedQFunction qf);
49 } gallery_qfunctions[1024];
50 static size_t num_qfunctions;
51 /// @endcond
52 
53 /// ----------------------------------------------------------------------------
54 /// CeedQFunction Library Internal Functions
55 /// ----------------------------------------------------------------------------
56 /// @addtogroup CeedQFunctionDeveloper
57 /// @{
58 
59 /**
60   @brief Register a gallery QFunction
61 
62   @param name        Name for this backend to respond to
63   @param source      Absolute path to source of QFunction,
64                        "\path\CEED_DIR\gallery\folder\file.h:function_name"
65   @param vec_length  Vector length.  Caller must ensure that number of quadrature
66                        points is a multiple of vec_length.
67   @param f           Function pointer to evaluate action at quadrature points.
68                        See \ref CeedQFunctionUser.
69   @param init        Initialization function called by CeedQFunctionInit() when the
70                        QFunction is selected.
71 
72   @return An error code: 0 - success, otherwise - failure
73 
74   @ref Developer
75 **/
76 int CeedQFunctionRegister(const char *name, const char *source,
77                           CeedInt vec_length, CeedQFunctionUser f,
78                           int (*init)(Ceed, const char *, CeedQFunction)) {
79   if (num_qfunctions >= sizeof(gallery_qfunctions) / sizeof(
80         gallery_qfunctions[0]))
81     // LCOV_EXCL_START
82     return CeedError(NULL, CEED_ERROR_MAJOR, "Too many gallery QFunctions");
83   // LCOV_EXCL_STOP
84 
85   CeedDebugEnv("Gallery Register: %s", name);
86 
87   strncpy(gallery_qfunctions[num_qfunctions].name, name, CEED_MAX_RESOURCE_LEN);
88   gallery_qfunctions[num_qfunctions].name[CEED_MAX_RESOURCE_LEN-1] = 0;
89   strncpy(gallery_qfunctions[num_qfunctions].source, source,
90           CEED_MAX_RESOURCE_LEN);
91   gallery_qfunctions[num_qfunctions].source[CEED_MAX_RESOURCE_LEN-1] = 0;
92   gallery_qfunctions[num_qfunctions].vec_length = vec_length;
93   gallery_qfunctions[num_qfunctions].f = f;
94   gallery_qfunctions[num_qfunctions].init = init;
95   num_qfunctions++;
96   return CEED_ERROR_SUCCESS;
97 }
98 
99 /**
100   @brief Set a CeedQFunction field, used by CeedQFunctionAddInput/Output
101 
102   @param f           CeedQFunctionField
103   @param field_name  Name of QFunction field
104   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
105                        (num_comp * 1) for @ref CEED_EVAL_NONE, @ref CEED_EVAL_INTERP, and @ref CEED_EVAL_WEIGHT
106   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
107                        \ref CEED_EVAL_INTERP to use interpolated values,
108                        \ref CEED_EVAL_GRAD to use gradients,
109                        \ref CEED_EVAL_WEIGHT to use quadrature weights.
110 
111   @return An error code: 0 - success, otherwise - failure
112 
113   @ref Developer
114 **/
115 static int CeedQFunctionFieldSet(CeedQFunctionField *f,const char *field_name,
116                                  CeedInt size, CeedEvalMode eval_mode) {
117   size_t len = strlen(field_name);
118   char *tmp;
119   int ierr;
120 
121   ierr = CeedCalloc(1, f); CeedChk(ierr);
122   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
123   memcpy(tmp, field_name, len+1);
124   (*f)->field_name = tmp;
125   (*f)->size = size;
126   (*f)->eval_mode = eval_mode;
127   return CEED_ERROR_SUCCESS;
128 }
129 
130 /**
131   @brief View a field of a CeedQFunction
132 
133   @param[in] field         QFunction field to view
134   @param[in] field_number  Number of field being viewed
135   @param[in] in            true for input field, false for output
136   @param[in] stream        Stream to view to, e.g., stdout
137 
138   @return An error code: 0 - success, otherwise - failure
139 
140   @ref Utility
141 **/
142 static int CeedQFunctionFieldView(CeedQFunctionField field,
143                                   CeedInt field_number,
144                                   bool in, FILE *stream) {
145   int ierr;
146   const char *inout = in ? "Input" : "Output";
147   char *field_name;
148   ierr = CeedQFunctionFieldGetName(field, &field_name); CeedChk(ierr);
149   CeedInt size;
150   ierr = CeedQFunctionFieldGetSize(field, &size); CeedChk(ierr);
151   CeedEvalMode eval_mode;
152   ierr = CeedQFunctionFieldGetEvalMode(field, &eval_mode); CeedChk(ierr);
153   fprintf(stream, "    %s Field [%d]:\n"
154           "      Name: \"%s\"\n"
155           "      Size: %d\n"
156           "      EvalMode: \"%s\"\n",
157           inout, field_number, field_name, size, CeedEvalModes[eval_mode]);
158   return CEED_ERROR_SUCCESS;
159 }
160 
161 /**
162   @brief Set flag to determine if Fortran interface is used
163 
164   @param qf      CeedQFunction
165   @param status  Boolean value to set as Fortran status
166 
167   @return An error code: 0 - success, otherwise - failure
168 
169   @ref Backend
170 **/
171 int CeedQFunctionSetFortranStatus(CeedQFunction qf, bool status) {
172   qf->is_fortran = status;
173   return CEED_ERROR_SUCCESS;
174 }
175 
176 /// @}
177 
178 /// ----------------------------------------------------------------------------
179 /// CeedQFunction Backend API
180 /// ----------------------------------------------------------------------------
181 /// @addtogroup CeedQFunctionBackend
182 /// @{
183 
184 /**
185   @brief Get the vector length of a CeedQFunction
186 
187   @param qf               CeedQFunction
188   @param[out] vec_length  Variable to store vector length
189 
190   @return An error code: 0 - success, otherwise - failure
191 
192   @ref Backend
193 **/
194 int CeedQFunctionGetVectorLength(CeedQFunction qf, CeedInt *vec_length) {
195   *vec_length = qf->vec_length;
196   return CEED_ERROR_SUCCESS;
197 }
198 
199 /**
200   @brief Get the number of inputs and outputs to a CeedQFunction
201 
202   @param qf               CeedQFunction
203   @param[out] num_input   Variable to store number of input fields
204   @param[out] num_output  Variable to store number of output fields
205 
206   @return An error code: 0 - success, otherwise - failure
207 
208   @ref Backend
209 **/
210 int CeedQFunctionGetNumArgs(CeedQFunction qf, CeedInt *num_input,
211                             CeedInt *num_output) {
212   if (num_input) *num_input = qf->num_input_fields;
213   if (num_output) *num_output = qf->num_output_fields;
214   return CEED_ERROR_SUCCESS;
215 }
216 
217 /**
218   @brief Get the name of the user function for a CeedQFunction
219 
220   @param qf                CeedQFunction
221   @param[out] kernel_name  Variable to store source path string
222 
223   @return An error code: 0 - success, otherwise - failure
224 
225   @ref Backend
226 **/
227 int CeedQFunctionGetKernelName(CeedQFunction qf, char **kernel_name) {
228   *kernel_name = (char *) qf->kernel_name;
229   return CEED_ERROR_SUCCESS;
230 }
231 
232 /**
233   @brief Get the source path string for a CeedQFunction
234 
235   @param qf                CeedQFunction
236   @param[out] source_path  Variable to store source path string
237 
238   @return An error code: 0 - success, otherwise - failure
239 
240   @ref Backend
241 **/
242 int CeedQFunctionGetSourcePath(CeedQFunction qf, char **source_path) {
243   *source_path = (char *) qf->source_path;
244   return CEED_ERROR_SUCCESS;
245 }
246 
247 /**
248   @brief Initalize and load QFunction source file into string buffer, including
249            full text of local files in place of `#include "local.h"`.
250            The `buffer` is set to `NULL` if there is no QFunction source file.
251          Note: Caller is responsible for freeing the string buffer with `CeedFree()`.
252 
253   @param qf                  CeedQFunction
254   @param[out] source_buffer  String buffer for source file contents
255 
256   @return An error code: 0 - success, otherwise - failure
257 
258   @ref Backend
259 **/
260 int CeedQFunctionLoadSourceToBuffer(CeedQFunction qf, char **source_buffer) {
261   int ierr;
262   char *source_path;
263 
264   ierr = CeedQFunctionGetSourcePath(qf, &source_path); CeedChk(ierr);
265   *source_buffer = NULL;
266   if (source_path) {
267     ierr = CeedLoadSourceToBuffer(qf->ceed, source_path, source_buffer);
268     CeedChk(ierr);
269   }
270 
271   return CEED_ERROR_SUCCESS;
272 }
273 
274 /**
275   @brief Get the User Function for a CeedQFunction
276 
277   @param qf      CeedQFunction
278   @param[out] f  Variable to store user function
279 
280   @return An error code: 0 - success, otherwise - failure
281 
282   @ref Backend
283 **/
284 int CeedQFunctionGetUserFunction(CeedQFunction qf, CeedQFunctionUser *f) {
285   *f = qf->function;
286   return CEED_ERROR_SUCCESS;
287 }
288 
289 /**
290   @brief Get global context for a CeedQFunction.
291            Note: For QFunctions from the Fortran interface, this
292              function will return the Fortran context
293              CeedQFunctionContext.
294 
295   @param qf        CeedQFunction
296   @param[out] ctx  Variable to store CeedQFunctionContext
297 
298   @return An error code: 0 - success, otherwise - failure
299 
300   @ref Backend
301 **/
302 int CeedQFunctionGetContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
303   *ctx = qf->ctx;
304   return CEED_ERROR_SUCCESS;
305 }
306 
307 /**
308   @brief Get true user context for a CeedQFunction
309            Note: For all QFunctions this function will return the user
310              CeedQFunctionContext and not interface context
311              CeedQFunctionContext, if any such object exists.
312 
313   @param qf        CeedQFunction
314   @param[out] ctx  Variable to store CeedQFunctionContext
315 
316   @return An error code: 0 - success, otherwise - failure
317   @ref Backend
318 **/
319 int CeedQFunctionGetInnerContext(CeedQFunction qf, CeedQFunctionContext *ctx) {
320   int ierr;
321   if (qf->is_fortran) {
322     CeedFortranContext fortran_ctx = NULL;
323     ierr = CeedQFunctionContextGetData(qf->ctx, CEED_MEM_HOST, &fortran_ctx);
324     CeedChk(ierr);
325     *ctx = fortran_ctx->innerctx;
326     ierr = CeedQFunctionContextRestoreData(qf->ctx, (void *)&fortran_ctx);
327     CeedChk(ierr);
328   } else {
329     *ctx = qf->ctx;
330   }
331   return CEED_ERROR_SUCCESS;
332 }
333 
334 /**
335   @brief Determine if QFunction is identity
336 
337   @param qf                CeedQFunction
338   @param[out] is_identity  Variable to store identity status
339 
340   @return An error code: 0 - success, otherwise - failure
341 
342   @ref Backend
343 **/
344 int CeedQFunctionIsIdentity(CeedQFunction qf, bool *is_identity) {
345   *is_identity = qf->is_identity;
346   return CEED_ERROR_SUCCESS;
347 }
348 
349 /**
350   @brief Get backend data of a CeedQFunction
351 
352   @param qf         CeedQFunction
353   @param[out] data  Variable to store data
354 
355   @return An error code: 0 - success, otherwise - failure
356 
357   @ref Backend
358 **/
359 int CeedQFunctionGetData(CeedQFunction qf, void *data) {
360   *(void **)data = qf->data;
361   return CEED_ERROR_SUCCESS;
362 }
363 
364 /**
365   @brief Set backend data of a CeedQFunction
366 
367   @param[out] qf  CeedQFunction
368   @param data     Data to set
369 
370   @return An error code: 0 - success, otherwise - failure
371 
372   @ref Backend
373 **/
374 int CeedQFunctionSetData(CeedQFunction qf, void *data) {
375   qf->data = data;
376   return CEED_ERROR_SUCCESS;
377 }
378 
379 /**
380   @brief Increment the reference counter for a CeedQFunction
381 
382   @param qf  CeedQFunction to increment the reference counter
383 
384   @return An error code: 0 - success, otherwise - failure
385 
386   @ref Backend
387 **/
388 int CeedQFunctionReference(CeedQFunction qf) {
389   qf->ref_count++;
390   return CEED_ERROR_SUCCESS;
391 }
392 
393 /// @}
394 
395 /// ----------------------------------------------------------------------------
396 /// CeedQFunction Public API
397 /// ----------------------------------------------------------------------------
398 /// @addtogroup CeedQFunctionUser
399 /// @{
400 
401 /**
402   @brief Create a CeedQFunction for evaluating interior (volumetric) terms.
403 
404   @param ceed        A Ceed object where the CeedQFunction will be created
405   @param vec_length  Vector length. Caller must ensure that number of quadrature
406                        points is a multiple of vec_length.
407   @param f           Function pointer to evaluate action at quadrature points.
408                        See \ref CeedQFunctionUser.
409   @param source      Absolute path to source of QFunction,
410                        "\abs_path\file.h:function_name".
411                        For support across all backends, this source must only
412                        contain constructs supported by C99, C++11, and CUDA.
413   @param[out] qf     Address of the variable where the newly created
414                        CeedQFunction will be stored
415 
416   @return An error code: 0 - success, otherwise - failure
417 
418   See \ref CeedQFunctionUser for details on the call-back function @a f's
419     arguments.
420 
421   @ref User
422 **/
423 int CeedQFunctionCreateInterior(Ceed ceed, CeedInt vec_length,
424                                 CeedQFunctionUser f,
425                                 const char *source, CeedQFunction *qf) {
426   int ierr;
427   char *source_copy, *kernel_name_copy;
428 
429   if (!ceed->QFunctionCreate) {
430     Ceed delegate;
431     ierr = CeedGetObjectDelegate(ceed, &delegate, "QFunction"); CeedChk(ierr);
432 
433     if (!delegate)
434       // LCOV_EXCL_START
435       return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
436                        "Backend does not support QFunctionCreate");
437     // LCOV_EXCL_STOP
438 
439     ierr = CeedQFunctionCreateInterior(delegate, vec_length, f, source, qf);
440     CeedChk(ierr);
441     return CEED_ERROR_SUCCESS;
442   }
443 
444   if (strlen(source) && !strrchr(source, ':'))\
445     // LCOV_EXCL_START
446     return CeedError(ceed, CEED_ERROR_INCOMPLETE,
447                      "Provided path to source does not include function name. "
448                      "Provided: \"%s\"\nRequired: \"\\abs_path\\file.h:function_name\"",
449                      source);
450   // LCOV_EXCL_STOP
451 
452   ierr = CeedCalloc(1, qf); CeedChk(ierr);
453   (*qf)->ceed = ceed;
454   ierr = CeedReference(ceed); CeedChk(ierr);
455   (*qf)->ref_count = 1;
456   (*qf)->vec_length = vec_length;
457   (*qf)->is_identity = false;
458   (*qf)->function = f;
459   if (strlen(source)) {
460     const char *kernel_name = strrchr(source, ':') + 1;
461     size_t kernel_name_len = strlen(kernel_name);
462     ierr = CeedCalloc(kernel_name_len + 1, &kernel_name_copy); CeedChk(ierr);
463     strncpy(kernel_name_copy, kernel_name, kernel_name_len);
464     (*qf)->kernel_name = kernel_name_copy;
465 
466     size_t source_len = strlen(source) - kernel_name_len - 1;
467     ierr = CeedCalloc(source_len + 1, &source_copy); CeedChk(ierr);
468     strncpy(source_copy, source, source_len);
469     (*qf)->source_path = source_copy;
470   }
471   ierr = CeedCalloc(CEED_FIELD_MAX, &(*qf)->input_fields); CeedChk(ierr);
472   ierr = CeedCalloc(CEED_FIELD_MAX, &(*qf)->output_fields); CeedChk(ierr);
473   ierr = ceed->QFunctionCreate(*qf); CeedChk(ierr);
474   return CEED_ERROR_SUCCESS;
475 }
476 
477 /**
478   @brief Create a CeedQFunction for evaluating interior (volumetric) terms by name.
479 
480   @param ceed     A Ceed object where the CeedQFunction will be created
481   @param name     Name of QFunction to use from gallery
482   @param[out] qf  Address of the variable where the newly created
483                     CeedQFunction will be stored
484 
485   @return An error code: 0 - success, otherwise - failure
486 
487   @ref User
488 **/
489 int CeedQFunctionCreateInteriorByName(Ceed ceed,  const char *name,
490                                       CeedQFunction *qf) {
491   int ierr;
492   size_t match_len = 0, match_idx = UINT_MAX;
493   char *name_copy;
494 
495   ierr = CeedQFunctionRegisterAll(); CeedChk(ierr);
496   // Find matching backend
497   if (!name) return CeedError(ceed, CEED_ERROR_INCOMPLETE,
498                                 "No QFunction name provided");
499   for (size_t i=0; i<num_qfunctions; i++) {
500     size_t n;
501     const char *curr_name = gallery_qfunctions[i].name;
502     for (n = 0; curr_name[n] && curr_name[n] == name[n]; n++) {}
503     if (n > match_len) {
504       match_len = n;
505       match_idx = i;
506     }
507   }
508   if (!match_len)
509     // LCOV_EXCL_START
510     return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "No suitable gallery QFunction");
511   // LCOV_EXCL_STOP
512 
513   // Create QFunction
514   ierr = CeedQFunctionCreateInterior(ceed,
515                                      gallery_qfunctions[match_idx].vec_length,
516                                      gallery_qfunctions[match_idx].f,
517                                      gallery_qfunctions[match_idx].source, qf);
518   CeedChk(ierr);
519 
520   // QFunction specific setup
521   ierr = gallery_qfunctions[match_idx].init(ceed, name, *qf); CeedChk(ierr);
522 
523   // Copy name
524   size_t slen = strlen(name) + 1;
525   ierr = CeedMalloc(slen, &name_copy); CeedChk(ierr);
526   memcpy(name_copy, name, slen);
527   (*qf)->gallery_name = name_copy;
528   (*qf)->is_gallery = true;
529   return CEED_ERROR_SUCCESS;
530 }
531 
532 /**
533   @brief Create an identity CeedQFunction. Inputs are written into outputs in
534            the order given. This is useful for CeedOperators that can be
535            represented with only the action of a CeedRestriction and CeedBasis,
536            such as restriction and prolongation operators for p-multigrid.
537            Backends may optimize CeedOperators with this CeedQFunction to avoid
538            the copy of input data to output fields by using the same memory
539            location for both.
540 
541   @param ceed          A Ceed object where the CeedQFunction will be created
542   @param[in] size      Size of the QFunction fields
543   @param[in] in_mode   CeedEvalMode for input to CeedQFunction
544   @param[in] out_mode  CeedEvalMode for output to CeedQFunction
545   @param[out] qf       Address of the variable where the newly created
546                          CeedQFunction will be stored
547 
548   @return An error code: 0 - success, otherwise - failure
549 
550   @ref User
551 **/
552 int CeedQFunctionCreateIdentity(Ceed ceed, CeedInt size, CeedEvalMode in_mode,
553                                 CeedEvalMode out_mode, CeedQFunction *qf) {
554   int ierr;
555 
556   ierr = CeedQFunctionCreateInteriorByName(ceed, "Identity", qf); CeedChk(ierr);
557   ierr = CeedQFunctionAddInput(*qf, "input", size, in_mode); CeedChk(ierr);
558   ierr = CeedQFunctionAddOutput(*qf, "output", size, out_mode); CeedChk(ierr);
559 
560   (*qf)->is_identity = true;
561   CeedInt *size_data;
562   ierr = CeedCalloc(1, &size_data); CeedChk(ierr);
563   size_data[0] = size;
564   CeedQFunctionContext ctx;
565   ierr = CeedQFunctionContextCreate(ceed, &ctx); CeedChk(ierr);
566   ierr = CeedQFunctionContextSetData(ctx, CEED_MEM_HOST, CEED_OWN_POINTER,
567                                      sizeof(*size_data), (void *)size_data);
568   CeedChk(ierr);
569   ierr = CeedQFunctionSetContext(*qf, ctx); CeedChk(ierr);
570   ierr = CeedQFunctionContextDestroy(&ctx); CeedChk(ierr);
571   return CEED_ERROR_SUCCESS;
572 }
573 
574 /**
575   @brief Copy the pointer to a CeedQFunction. Both pointers should
576            be destroyed with `CeedQFunctionDestroy()`;
577            Note: If `*qf_copy` is non-NULL, then it is assumed that
578            `*qf_copy` is a pointer to a CeedQFunction. This
579            CeedQFunction will be destroyed if `*qf_copy` is the only
580            reference to this CeedQFunction.
581 
582   @param qf            CeedQFunction to copy reference to
583   @param[out] qf_copy  Variable to store copied reference
584 
585   @return An error code: 0 - success, otherwise - failure
586 
587   @ref User
588 **/
589 int CeedQFunctionReferenceCopy(CeedQFunction qf, CeedQFunction *qf_copy) {
590   int ierr;
591 
592   ierr = CeedQFunctionReference(qf); CeedChk(ierr);
593   ierr = CeedQFunctionDestroy(qf_copy); CeedChk(ierr);
594   *qf_copy = qf;
595   return CEED_ERROR_SUCCESS;
596 }
597 
598 /**
599   @brief Add a CeedQFunction input
600 
601   @param qf          CeedQFunction
602   @param field_name  Name of QFunction field
603   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
604                        (num_comp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
605   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
606                        \ref CEED_EVAL_INTERP to use interpolated values,
607                        \ref CEED_EVAL_GRAD to use gradients.
608 
609   @return An error code: 0 - success, otherwise - failure
610 
611   @ref User
612 **/
613 int CeedQFunctionAddInput(CeedQFunction qf, const char *field_name,
614                           CeedInt size,
615                           CeedEvalMode eval_mode) {
616   if (qf->is_immutable)
617     // LCOV_EXCL_START
618     return CeedError(qf->ceed, CEED_ERROR_MAJOR,
619                      "QFunction cannot be changed after set as immutable");
620   // LCOV_EXCL_STOP
621   if ((eval_mode == CEED_EVAL_WEIGHT) && (size != 1))
622     // LCOV_EXCL_START
623     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
624                      "CEED_EVAL_WEIGHT should have size 1");
625   // LCOV_EXCL_STOP
626   int ierr = CeedQFunctionFieldSet(&qf->input_fields[qf->num_input_fields],
627                                    field_name, size, eval_mode);
628   CeedChk(ierr);
629   qf->num_input_fields++;
630   return CEED_ERROR_SUCCESS;
631 }
632 
633 /**
634   @brief Add a CeedQFunction output
635 
636   @param qf          CeedQFunction
637   @param field_name  Name of QFunction field
638   @param size        Size of QFunction field, (num_comp * dim) for @ref CEED_EVAL_GRAD or
639                        (num_comp * 1) for @ref CEED_EVAL_NONE and @ref CEED_EVAL_INTERP
640   @param eval_mode   \ref CEED_EVAL_NONE to use values directly,
641                        \ref CEED_EVAL_INTERP to use interpolated values,
642                        \ref CEED_EVAL_GRAD to use gradients.
643 
644   @return An error code: 0 - success, otherwise - failure
645 
646   @ref User
647 **/
648 int CeedQFunctionAddOutput(CeedQFunction qf, const char *field_name,
649                            CeedInt size, CeedEvalMode eval_mode) {
650   if (qf->is_immutable)
651     // LCOV_EXCL_START
652     return CeedError(qf->ceed, CEED_ERROR_MAJOR,
653                      "QFunction cannot be changed after set as immutable");
654   // LCOV_EXCL_STOP
655   if (eval_mode == CEED_EVAL_WEIGHT)
656     // LCOV_EXCL_START
657     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
658                      "Cannot create QFunction output with "
659                      "CEED_EVAL_WEIGHT");
660   // LCOV_EXCL_STOP
661   int ierr = CeedQFunctionFieldSet(&qf->output_fields[qf->num_output_fields],
662                                    field_name, size, eval_mode);
663   CeedChk(ierr);
664   qf->num_output_fields++;
665   return CEED_ERROR_SUCCESS;
666 }
667 
668 /**
669   @brief Get the CeedQFunctionFields of a CeedQFunction
670 
671   Note: Calling this function asserts that setup is complete
672           and sets the CeedQFunction as immutable.
673 
674   @param qf                      CeedQFunction
675   @param[out] num_input_fields   Variable to store number of input fields
676   @param[out] input_fields       Variable to store input fields
677   @param[out] num_output_fields  Variable to store number of output fields
678   @param[out] output_fields      Variable to store output fields
679 
680   @return An error code: 0 - success, otherwise - failure
681 
682   @ref Advanced
683 **/
684 int CeedQFunctionGetFields(CeedQFunction qf, CeedInt *num_input_fields,
685                            CeedQFunctionField **input_fields,
686                            CeedInt *num_output_fields,
687                            CeedQFunctionField **output_fields) {
688   qf->is_immutable = true;
689   if (num_input_fields) *num_input_fields = qf->num_input_fields;
690   if (input_fields) *input_fields = qf->input_fields;
691   if (num_output_fields) *num_output_fields = qf->num_output_fields;
692   if (output_fields) *output_fields = qf->output_fields;
693   return CEED_ERROR_SUCCESS;
694 }
695 
696 /**
697   @brief Get the name of a CeedQFunctionField
698 
699   @param qf_field         CeedQFunctionField
700   @param[out] field_name  Variable to store the field name
701 
702   @return An error code: 0 - success, otherwise - failure
703 
704   @ref Advanced
705 **/
706 int CeedQFunctionFieldGetName(CeedQFunctionField qf_field, char **field_name) {
707   *field_name = (char *)qf_field->field_name;
708   return CEED_ERROR_SUCCESS;
709 }
710 
711 /**
712   @brief Get the number of components of a CeedQFunctionField
713 
714   @param qf_field   CeedQFunctionField
715   @param[out] size  Variable to store the size of the field
716 
717   @return An error code: 0 - success, otherwise - failure
718 
719   @ref Advanced
720 **/
721 int CeedQFunctionFieldGetSize(CeedQFunctionField qf_field, CeedInt *size) {
722   *size = qf_field->size;
723   return CEED_ERROR_SUCCESS;
724 }
725 
726 /**
727   @brief Get the CeedEvalMode of a CeedQFunctionField
728 
729   @param qf_field        CeedQFunctionField
730   @param[out] eval_mode  Variable to store the field evaluation mode
731 
732   @return An error code: 0 - success, otherwise - failure
733 
734   @ref Advanced
735 **/
736 int CeedQFunctionFieldGetEvalMode(CeedQFunctionField qf_field,
737                                   CeedEvalMode *eval_mode) {
738   *eval_mode = qf_field->eval_mode;
739   return CEED_ERROR_SUCCESS;
740 }
741 
742 /**
743   @brief Set global context for a CeedQFunction
744 
745   @param qf   CeedQFunction
746   @param ctx  Context data to set
747 
748   @return An error code: 0 - success, otherwise - failure
749 
750   @ref User
751 **/
752 int CeedQFunctionSetContext(CeedQFunction qf, CeedQFunctionContext ctx) {
753   int ierr;
754   qf->ctx = ctx;
755   ierr = CeedQFunctionContextReference(ctx); CeedChk(ierr);
756   return CEED_ERROR_SUCCESS;
757 }
758 
759 /**
760   @brief View a CeedQFunction
761 
762   @param[in] qf      CeedQFunction to view
763   @param[in] stream  Stream to write; typically stdout/stderr or a file
764 
765   @return Error code: 0 - success, otherwise - failure
766 
767   @ref User
768 **/
769 int CeedQFunctionView(CeedQFunction qf, FILE *stream) {
770   int ierr;
771 
772   fprintf(stream, "%sCeedQFunction %s\n",
773           qf->is_gallery ? "Gallery " : "User ",
774           qf->is_gallery ? qf->gallery_name : qf->kernel_name);
775 
776   fprintf(stream, "  %d Input Field%s:\n", qf->num_input_fields,
777           qf->num_input_fields>1 ? "s" : "");
778   for (CeedInt i=0; i<qf->num_input_fields; i++) {
779     ierr = CeedQFunctionFieldView(qf->input_fields[i], i, 1, stream);
780     CeedChk(ierr);
781   }
782 
783   fprintf(stream, "  %d Output Field%s:\n", qf->num_output_fields,
784           qf->num_output_fields>1 ? "s" : "");
785   for (CeedInt i=0; i<qf->num_output_fields; i++) {
786     ierr = CeedQFunctionFieldView(qf->output_fields[i], i, 0, stream);
787     CeedChk(ierr);
788   }
789   return CEED_ERROR_SUCCESS;
790 }
791 
792 /**
793   @brief Get the Ceed associated with a CeedQFunction
794 
795   @param qf              CeedQFunction
796   @param[out] ceed       Variable to store Ceed
797 
798   @return An error code: 0 - success, otherwise - failure
799 
800   @ref Advanced
801 **/
802 int CeedQFunctionGetCeed(CeedQFunction qf, Ceed *ceed) {
803   *ceed = qf->ceed;
804   return CEED_ERROR_SUCCESS;
805 }
806 
807 /**
808   @brief Apply the action of a CeedQFunction
809 
810   Note: Calling this function asserts that setup is complete
811           and sets the CeedQFunction as immutable.
812 
813   @param qf      CeedQFunction
814   @param Q       Number of quadrature points
815   @param[in] u   Array of input CeedVectors
816   @param[out] v  Array of output CeedVectors
817 
818   @return An error code: 0 - success, otherwise - failure
819 
820   @ref User
821 **/
822 int CeedQFunctionApply(CeedQFunction qf, CeedInt Q,
823                        CeedVector *u, CeedVector *v) {
824   int ierr;
825   if (!qf->Apply)
826     // LCOV_EXCL_START
827     return CeedError(qf->ceed, CEED_ERROR_UNSUPPORTED,
828                      "Backend does not support QFunctionApply");
829   // LCOV_EXCL_STOP
830   if (Q % qf->vec_length)
831     // LCOV_EXCL_START
832     return CeedError(qf->ceed, CEED_ERROR_DIMENSION,
833                      "Number of quadrature points %d must be a "
834                      "multiple of %d", Q, qf->vec_length);
835   // LCOV_EXCL_STOP
836   qf->is_immutable = true;
837   ierr = qf->Apply(qf, Q, u, v); CeedChk(ierr);
838   return CEED_ERROR_SUCCESS;
839 }
840 
841 /**
842   @brief Destroy a CeedQFunction
843 
844   @param qf  CeedQFunction to destroy
845 
846   @return An error code: 0 - success, otherwise - failure
847 
848   @ref User
849 **/
850 int CeedQFunctionDestroy(CeedQFunction *qf) {
851   int ierr;
852 
853   if (!*qf || --(*qf)->ref_count > 0) return CEED_ERROR_SUCCESS;
854   // Backend destroy
855   if ((*qf)->Destroy) {
856     ierr = (*qf)->Destroy(*qf); CeedChk(ierr);
857   }
858   // Free fields
859   for (int i=0; i<(*qf)->num_input_fields; i++) {
860     ierr = CeedFree(&(*(*qf)->input_fields[i]).field_name); CeedChk(ierr);
861     ierr = CeedFree(&(*qf)->input_fields[i]); CeedChk(ierr);
862   }
863   for (int i=0; i<(*qf)->num_output_fields; i++) {
864     ierr = CeedFree(&(*(*qf)->output_fields[i]).field_name); CeedChk(ierr);
865     ierr = CeedFree(&(*qf)->output_fields[i]); CeedChk(ierr);
866   }
867   ierr = CeedFree(&(*qf)->input_fields); CeedChk(ierr);
868   ierr = CeedFree(&(*qf)->output_fields); CeedChk(ierr);
869 
870   // User context data object
871   ierr = CeedQFunctionContextDestroy(&(*qf)->ctx); CeedChk(ierr);
872 
873   ierr = CeedFree(&(*qf)->source_path); CeedChk(ierr);
874   ierr = CeedFree(&(*qf)->gallery_name); CeedChk(ierr);
875   ierr = CeedFree(&(*qf)->kernel_name); CeedChk(ierr);
876   ierr = CeedDestroy(&(*qf)->ceed); CeedChk(ierr);
877   ierr = CeedFree(qf); CeedChk(ierr);
878   return CEED_ERROR_SUCCESS;
879 }
880 
881 /// @}
882