xref: /libCEED/interface/ceed.c (revision b13efd58b277efef1db70d6f06eaaf4d415a7642)
1 // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #define _POSIX_C_SOURCE 200112
9 #include <ceed-impl.h>
10 #include <ceed.h>
11 #include <ceed/backend.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 /// @cond DOXYGEN_SKIP
21 static CeedRequest ceed_request_immediate;
22 static CeedRequest ceed_request_ordered;
23 
24 static struct {
25   char prefix[CEED_MAX_RESOURCE_LEN];
26   int (*init)(const char *resource, Ceed f);
27   unsigned int priority;
28 } backends[32];
29 static size_t num_backends;
30 
31 #define CEED_FTABLE_ENTRY(class, method) {#class #method, offsetof(struct class##_private, method)}
32 /// @endcond
33 
34 /// @file
35 /// Implementation of core components of Ceed library
36 
37 /// @addtogroup CeedUser
38 /// @{
39 
40 /**
41   @brief Request immediate completion
42 
43   This predefined constant is passed as the @ref CeedRequest argument to interfaces when the caller wishes for the operation to be performed immediately.
44   The code
45 
46   @code
47     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
48   @endcode
49 
50   is semantically equivalent to
51 
52   @code
53     CeedRequest request;
54     CeedOperatorApply(op, ..., &request);
55     CeedRequestWait(&request);
56   @endcode
57 
58   @sa CEED_REQUEST_ORDERED
59 **/
60 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
61 
62 /**
63   @brief Request ordered completion
64 
65   This predefined constant is passed as the @ref CeedRequest argument to interfaces when the caller wishes for the operation to be completed in the order that it is submitted to the device.
66   It is typically used in a construct such as:
67 
68   @code
69     CeedRequest request;
70     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
71     CeedOperatorApply(op2, ..., &request);
72     // other optional work
73     CeedRequestWait(&request);
74   @endcode
75 
76   which allows the sequence to complete asynchronously but does not start `op2` until `op1` has completed.
77 
78   @todo The current implementation is overly strict, offering equivalent semantics to @ref CEED_REQUEST_IMMEDIATE.
79 
80   @sa CEED_REQUEST_IMMEDIATE
81  */
82 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
83 
84 /**
85   @brief Wait for a @ref CeedRequest to complete.
86 
87   Calling @ref CeedRequestWait() on a `NULL` request is a no-op.
88 
89   @param[in,out] req Address of @ref CeedRequest to wait for; zeroed on completion.
90 
91   @return An error code: 0 - success, otherwise - failure
92 
93   @ref User
94 **/
95 int CeedRequestWait(CeedRequest *req) {
96   if (!*req) return CEED_ERROR_SUCCESS;
97   return CeedError(NULL, CEED_ERROR_UNSUPPORTED, "CeedRequestWait not implemented");
98 }
99 
100 /// @}
101 
102 /// ----------------------------------------------------------------------------
103 /// Ceed Library Internal Functions
104 /// ----------------------------------------------------------------------------
105 /// @addtogroup CeedDeveloper
106 /// @{
107 
108 /**
109   @brief Register a Ceed backend internally.
110 
111   Note: Backends should call @ref CeedRegister() instead.
112 
113   @param[in] prefix   Prefix of resources for this backend to respond to.
114                         For example, the reference backend responds to "/cpu/self".
115   @param[in] init     Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
116   @param[in] priority Integer priority.
117                         Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
118 
119   @return An error code: 0 - success, otherwise - failure
120 
121   @ref Developer
122 **/
123 int CeedRegisterImpl(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
124   int ierr = 0;
125 
126   CeedPragmaCritical(CeedRegisterImpl) {
127     if (num_backends < sizeof(backends) / sizeof(backends[0])) {
128       strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
129       backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN - 1] = 0;
130       backends[num_backends].init                              = init;
131       backends[num_backends].priority                          = priority;
132       num_backends++;
133     } else {
134       ierr = 1;
135     }
136   }
137   CeedCheck(ierr == 0, NULL, CEED_ERROR_MAJOR, "Too many backends");
138   return CEED_ERROR_SUCCESS;
139 }
140 
141 /**
142   @brief Create a work vector space for a `ceed`
143 
144   @param[in,out] ceed `Ceed` to create work vector space for
145 
146   @return An error code: 0 - success, otherwise - failure
147 
148   @ref Developer
149 **/
150 static int CeedWorkVectorsCreate(Ceed ceed) {
151   CeedCall(CeedCalloc(1, &ceed->work_vectors));
152   return CEED_ERROR_SUCCESS;
153 }
154 
155 /**
156   @brief Destroy a work vector space for a `ceed`
157 
158   @param[in,out] ceed `Ceed` to destroy work vector space for
159 
160   @return An error code: 0 - success, otherwise - failure
161 
162   @ref Developer
163 **/
164 static int CeedWorkVectorsDestroy(Ceed ceed) {
165   if (!ceed->work_vectors) return CEED_ERROR_SUCCESS;
166   for (CeedSize i = 0; i < ceed->work_vectors->num_vecs; i++) {
167     CeedCheck(!ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " checked out but not returned");
168     ceed->ref_count += 2;  // Note: increase ref_count to prevent Ceed destructor from triggering again
169     CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i]));
170     ceed->ref_count -= 1;  // Note: restore ref_count
171   }
172   CeedCall(CeedFree(&ceed->work_vectors->is_in_use));
173   CeedCall(CeedFree(&ceed->work_vectors->vecs));
174   CeedCall(CeedFree(&ceed->work_vectors));
175   return CEED_ERROR_SUCCESS;
176 }
177 
178 /// @}
179 
180 /// ----------------------------------------------------------------------------
181 /// Ceed Backend API
182 /// ----------------------------------------------------------------------------
183 /// @addtogroup CeedBackend
184 /// @{
185 
186 /**
187   @brief Return value of `CEED_DEBUG` environment variable
188 
189   @param[in] ceed `Ceed` context
190 
191   @return Boolean value: true  - debugging mode enabled
192                          false - debugging mode disabled
193 
194   @ref Backend
195 **/
196 // LCOV_EXCL_START
197 bool CeedDebugFlag(const Ceed ceed) { return ceed->is_debug; }
198 // LCOV_EXCL_STOP
199 
200 /**
201   @brief Return value of `CEED_DEBUG` environment variable
202 
203   @return Boolean value: true  - debugging mode enabled
204                          false - debugging mode disabled
205 
206   @ref Backend
207 **/
208 // LCOV_EXCL_START
209 bool CeedDebugFlagEnv(void) { return getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG"); }
210 // LCOV_EXCL_STOP
211 
212 /**
213   @brief Print debugging information in color
214 
215   @param[in] color  Color to print
216   @param[in] format Printing format
217 
218   @ref Backend
219 **/
220 // LCOV_EXCL_START
221 void CeedDebugImpl256(const unsigned char color, const char *format, ...) {
222   va_list args;
223   va_start(args, format);
224   fflush(stdout);
225   if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[38;5;%dm", color);
226   vfprintf(stdout, format, args);
227   if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[m");
228   fprintf(stdout, "\n");
229   fflush(stdout);
230   va_end(args);
231 }
232 // LCOV_EXCL_STOP
233 
234 /**
235   @brief Allocate an array on the host; use @ref CeedMalloc().
236 
237   Memory usage can be tracked by the library.
238   This ensures sufficient alignment for vectorization and should be used for large allocations.
239 
240   @param[in]  n    Number of units to allocate
241   @param[in]  unit Size of each unit
242   @param[out] p    Address of pointer to hold the result
243 
244   @return An error code: 0 - success, otherwise - failure
245 
246   @ref Backend
247 
248   @sa CeedFree()
249 **/
250 int CeedMallocArray(size_t n, size_t unit, void *p) {
251   int ierr = posix_memalign((void **)p, CEED_ALIGN, n * unit);
252   CeedCheck(ierr == 0, NULL, CEED_ERROR_MAJOR, "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
253   return CEED_ERROR_SUCCESS;
254 }
255 
256 /**
257   @brief Allocate a cleared (zeroed) array on the host; use @ref CeedCalloc().
258 
259   Memory usage can be tracked by the library.
260 
261   @param[in]  n    Number of units to allocate
262   @param[in]  unit Size of each unit
263   @param[out] p    Address of pointer to hold the result
264 
265   @return An error code: 0 - success, otherwise - failure
266 
267   @ref Backend
268 
269   @sa CeedFree()
270 **/
271 int CeedCallocArray(size_t n, size_t unit, void *p) {
272   *(void **)p = calloc(n, unit);
273   CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "calloc failed to allocate %zd members of size %zd\n", n, unit);
274   return CEED_ERROR_SUCCESS;
275 }
276 
277 /**
278   @brief Reallocate an array on the host; use @ref CeedRealloc().
279 
280   Memory usage can be tracked by the library.
281 
282   @param[in]  n    Number of units to allocate
283   @param[in]  unit Size of each unit
284   @param[out] p    Address of pointer to hold the result
285 
286   @return An error code: 0 - success, otherwise - failure
287 
288   @ref Backend
289 
290   @sa CeedFree()
291 **/
292 int CeedReallocArray(size_t n, size_t unit, void *p) {
293   *(void **)p = realloc(*(void **)p, n * unit);
294   CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "realloc failed to allocate %zd members of size %zd\n", n, unit);
295   return CEED_ERROR_SUCCESS;
296 }
297 
298 /**
299   @brief Allocate a cleared string buffer on the host.
300 
301   Memory usage can be tracked by the library.
302 
303   @param[in]  source Pointer to string to be copied
304   @param[out] copy   Pointer to variable to hold newly allocated string copy
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Backend
309 
310   @sa CeedFree()
311 **/
312 int CeedStringAllocCopy(const char *source, char **copy) {
313   size_t len = strlen(source);
314   CeedCall(CeedCalloc(len + 1, copy));
315   memcpy(*copy, source, len);
316   return CEED_ERROR_SUCCESS;
317 }
318 
319 /** Free memory allocated using @ref CeedMalloc() or @ref CeedCalloc()
320 
321   @param[in,out] p Address of pointer to memory.
322                      This argument is of type `void*` to avoid needing a cast, but is the address of the pointer (which is zeroed) rather than the pointer.
323 
324   @return An error code: 0 - success, otherwise - failure
325 
326   @ref Backend
327 **/
328 int CeedFree(void *p) {
329   free(*(void **)p);
330   *(void **)p = NULL;
331   return CEED_ERROR_SUCCESS;
332 }
333 
334 /** Internal helper to manage handoff of user `source_array` to backend with proper @ref CeedCopyMode behavior.
335 
336   @param[in]     source_array          Source data provided by user
337   @param[in]     copy_mode             Copy mode for the data
338   @param[in]     num_values            Number of values to handle
339   @param[in]     size_unit             Size of array element in bytes
340   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
341   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
342   @param[out]    target_array          Pointer to location for data
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Backend
347 **/
348 static inline int CeedSetHostGenericArray(const void *source_array, CeedCopyMode copy_mode, size_t size_unit, CeedSize num_values,
349                                           void *target_array_owned, void *target_array_borrowed, void *target_array) {
350   switch (copy_mode) {
351     case CEED_COPY_VALUES:
352       if (!*(void **)target_array_owned) CeedCall(CeedCallocArray(num_values, size_unit, target_array_owned));
353       if (source_array) memcpy(*(void **)target_array_owned, source_array, size_unit * num_values);
354       *(void **)target_array_borrowed = NULL;
355       *(void **)target_array          = *(void **)target_array_owned;
356       break;
357     case CEED_OWN_POINTER:
358       CeedCall(CeedFree(target_array_owned));
359       *(void **)target_array_owned    = (void *)source_array;
360       *(void **)target_array_borrowed = NULL;
361       *(void **)target_array          = *(void **)target_array_owned;
362       break;
363     case CEED_USE_POINTER:
364       CeedCall(CeedFree(target_array_owned));
365       *(void **)target_array_owned    = NULL;
366       *(void **)target_array_borrowed = (void *)source_array;
367       *(void **)target_array          = *(void **)target_array_borrowed;
368   }
369   return CEED_ERROR_SUCCESS;
370 }
371 
372 /** Manage handoff of user `bool` `source_array` to backend with proper @ref CeedCopyMode behavior.
373 
374   @param[in]     source_array          Source data provided by user
375   @param[in]     copy_mode             Copy mode for the data
376   @param[in]     num_values            Number of values to handle
377   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
378   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
379   @param[out]    target_array          Pointer to location for data
380 
381   @return An error code: 0 - success, otherwise - failure
382 
383   @ref Backend
384 **/
385 int CeedSetHostBoolArray(const bool *source_array, CeedCopyMode copy_mode, CeedSize num_values, const bool **target_array_owned,
386                          const bool **target_array_borrowed, const bool **target_array) {
387   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(bool), num_values, target_array_owned, target_array_borrowed, target_array));
388   return CEED_ERROR_SUCCESS;
389 }
390 
391 /** Manage handoff of user `CeedInt8` `source_array` to backend with proper @ref CeedCopyMode behavior.
392 
393   @param[in]     source_array          Source data provided by user
394   @param[in]     copy_mode             Copy mode for the data
395   @param[in]     num_values            Number of values to handle
396   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
397   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
398   @param[out]    target_array          Pointer to location for data
399 
400   @return An error code: 0 - success, otherwise - failure
401 
402   @ref Backend
403 **/
404 int CeedSetHostCeedInt8Array(const CeedInt8 *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt8 **target_array_owned,
405                              const CeedInt8 **target_array_borrowed, const CeedInt8 **target_array) {
406   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt8), num_values, target_array_owned, target_array_borrowed, target_array));
407   return CEED_ERROR_SUCCESS;
408 }
409 
410 /** Manage handoff of user `CeedInt` `source_array` to backend with proper @ref CeedCopyMode behavior.
411 
412   @param[in]     source_array          Source data provided by user
413   @param[in]     copy_mode             Copy mode for the data
414   @param[in]     num_values            Number of values to handle
415   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
416   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
417   @param[out]    target_array          Pointer to location for data
418 
419   @return An error code: 0 - success, otherwise - failure
420 
421   @ref Backend
422 **/
423 int CeedSetHostCeedIntArray(const CeedInt *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt **target_array_owned,
424                             const CeedInt **target_array_borrowed, const CeedInt **target_array) {
425   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt), num_values, target_array_owned, target_array_borrowed, target_array));
426   return CEED_ERROR_SUCCESS;
427 }
428 
429 /** Manage handoff of user `CeedScalar` `source_array` to backend with proper @ref CeedCopyMode behavior.
430 
431   @param[in]     source_array          Source data provided by user
432   @param[in]     copy_mode             Copy mode for the data
433   @param[in]     num_values            Number of values to handle
434   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
435   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
436   @param[out]    target_array          Pointer to location for data
437 
438   @return An error code: 0 - success, otherwise - failure
439 
440   @ref Backend
441 **/
442 int CeedSetHostCeedScalarArray(const CeedScalar *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedScalar **target_array_owned,
443                                const CeedScalar **target_array_borrowed, const CeedScalar **target_array) {
444   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedScalar), num_values, target_array_owned, target_array_borrowed, target_array));
445   return CEED_ERROR_SUCCESS;
446 }
447 
448 /**
449   @brief Register a `Ceed` backend
450 
451   @param[in] prefix   Prefix of resources for this backend to respond to.
452                         For example, the reference backend responds to "/cpu/self".
453   @param[in] init     Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
454   @param[in] priority Integer priority.
455                         Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
456 
457   @return An error code: 0 - success, otherwise - failure
458 
459   @ref Backend
460 **/
461 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
462   CeedDebugEnv("Backend Register: %s", prefix);
463   CeedRegisterImpl(prefix, init, priority);
464   return CEED_ERROR_SUCCESS;
465 }
466 
467 /**
468   @brief Return debugging status flag
469 
470   @param[in]  ceed     `Ceed` context to get debugging flag
471   @param[out] is_debug Variable to store debugging flag
472 
473   @return An error code: 0 - success, otherwise - failure
474 
475   @ref Backend
476 **/
477 int CeedIsDebug(Ceed ceed, bool *is_debug) {
478   *is_debug = ceed->is_debug;
479   return CEED_ERROR_SUCCESS;
480 }
481 
482 /**
483   @brief Get the root of the requested resource.
484 
485   Note: Caller is responsible for calling @ref CeedFree() on the `resource_root`.
486 
487   @param[in]  ceed          `Ceed` context to get resource name of
488   @param[in]  resource      Full user specified resource
489   @param[in]  delineator    Delineator to break `resource_root` and `resource_spec`
490   @param[out] resource_root Variable to store resource root
491 
492   @return An error code: 0 - success, otherwise - failure
493 
494   @ref Backend
495 **/
496 int CeedGetResourceRoot(Ceed ceed, const char *resource, const char *delineator, char **resource_root) {
497   char  *device_spec       = strstr(resource, delineator);
498   size_t resource_root_len = device_spec ? (size_t)(device_spec - resource) + 1 : strlen(resource) + 1;
499 
500   CeedCall(CeedCalloc(resource_root_len, resource_root));
501   memcpy(*resource_root, resource, resource_root_len - 1);
502   return CEED_ERROR_SUCCESS;
503 }
504 
505 /**
506   @brief Retrieve a parent `Ceed` context
507 
508   @param[in]  ceed   `Ceed` context to retrieve parent of
509   @param[out] parent Address to save the parent to
510 
511   @return An error code: 0 - success, otherwise - failure
512 
513   @ref Backend
514 **/
515 int CeedGetParent(Ceed ceed, Ceed *parent) {
516   if (ceed->parent) {
517     CeedCall(CeedGetParent(ceed->parent, parent));
518     return CEED_ERROR_SUCCESS;
519   }
520   *parent = ceed;
521   return CEED_ERROR_SUCCESS;
522 }
523 
524 /**
525   @brief Retrieve a delegate `Ceed` context
526 
527   @param[in]  ceed     `Ceed` context to retrieve delegate of
528   @param[out] delegate Address to save the delegate to
529 
530   @return An error code: 0 - success, otherwise - failure
531 
532   @ref Backend
533 **/
534 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
535   *delegate = ceed->delegate;
536   return CEED_ERROR_SUCCESS;
537 }
538 
539 /**
540   @brief Set a delegate `Ceed` context
541 
542   This function allows a `Ceed` context to set a delegate `Ceed` context.
543   All backend implementations default to the delegate `Ceed` context, unless overridden.
544 
545   @param[in]  ceed     `Ceed` context to set delegate of
546   @param[out] delegate Address to set the delegate to
547 
548   @return An error code: 0 - success, otherwise - failure
549 
550   @ref Backend
551 **/
552 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
553   ceed->delegate   = delegate;
554   delegate->parent = ceed;
555   return CEED_ERROR_SUCCESS;
556 }
557 
558 /**
559   @brief Retrieve a delegate `Ceed` context for a specific object type
560 
561   @param[in]  ceed     `Ceed` context to retrieve delegate of
562   @param[out] delegate Address to save the delegate to
563   @param[in]  obj_name Name of the object type to retrieve delegate for
564 
565   @return An error code: 0 - success, otherwise - failure
566 
567   @ref Backend
568 **/
569 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) {
570   // Check for object delegate
571   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) {
572     if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) {
573       *delegate = ceed->obj_delegates->delegate;
574       return CEED_ERROR_SUCCESS;
575     }
576   }
577 
578   // Use default delegate if no object delegate
579   CeedCall(CeedGetDelegate(ceed, delegate));
580   return CEED_ERROR_SUCCESS;
581 }
582 
583 /**
584   @brief Set a delegate `Ceed` context for a specific object type
585 
586   This function allows a `Ceed` context to set a delegate `Ceed` context for a given type of `Ceed` object.
587   All backend implementations default to the delegate `Ceed` context for this object.
588   For example, `CeedSetObjectDelegate(ceed, delegate, "Basis")` uses delegate implementations for all `CeedBasis` backend functions.
589 
590   @param[in,out] ceed     `Ceed` context to set delegate of
591   @param[in]     delegate `Ceed` context to use for delegation
592   @param[in]     obj_name Name of the object type to set delegate for
593 
594   @return An error code: 0 - success, otherwise - failure
595 
596   @ref Backend
597 **/
598 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) {
599   CeedInt count = ceed->obj_delegate_count;
600 
601   // Malloc or Realloc
602   if (count) {
603     CeedCall(CeedRealloc(count + 1, &ceed->obj_delegates));
604   } else {
605     CeedCall(CeedCalloc(1, &ceed->obj_delegates));
606   }
607   ceed->obj_delegate_count++;
608 
609   // Set object delegate
610   ceed->obj_delegates[count].delegate = delegate;
611   CeedCall(CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name));
612 
613   // Set delegate parent
614   delegate->parent = ceed;
615   return CEED_ERROR_SUCCESS;
616 }
617 
618 /**
619   @brief Get the fallback resource for `CeedOperator`
620 
621   @param[in]  ceed     `Ceed` context
622   @param[out] resource Variable to store fallback resource
623 
624   @return An error code: 0 - success, otherwise - failure
625 
626   @ref Backend
627 **/
628 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
629   *resource = (const char *)ceed->op_fallback_resource;
630   return CEED_ERROR_SUCCESS;
631 }
632 
633 /**
634   @brief Get the fallback `Ceed` for `CeedOperator`
635 
636   @param[in]  ceed          `Ceed` context
637   @param[out] fallback_ceed Variable to store fallback `Ceed`
638 
639   @return An error code: 0 - success, otherwise - failure
640 
641   @ref Backend
642 **/
643 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) {
644   if (ceed->has_valid_op_fallback_resource) {
645     CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n");
646     CeedDebug(ceed, "Getting fallback from %s to %s\n", ceed->resource, ceed->op_fallback_resource);
647   }
648 
649   // Create fallback Ceed if uninitalized
650   if (!ceed->op_fallback_ceed && ceed->has_valid_op_fallback_resource) {
651     CeedDebug(ceed, "Creating fallback Ceed");
652 
653     Ceed        fallback_ceed;
654     const char *fallback_resource;
655 
656     CeedCall(CeedGetOperatorFallbackResource(ceed, &fallback_resource));
657     CeedCall(CeedInit(fallback_resource, &fallback_ceed));
658     fallback_ceed->op_fallback_parent = ceed;
659     fallback_ceed->Error              = ceed->Error;
660     ceed->op_fallback_ceed            = fallback_ceed;
661     {
662       const char **jit_source_dirs;
663       CeedInt      num_jit_source_dirs = 0;
664 
665       CeedCall(CeedGetJitSourceRoots(ceed, &num_jit_source_dirs, &jit_source_dirs));
666       for (CeedInt i = 0; i < num_jit_source_dirs; i++) {
667         CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_source_dirs[i]));
668       }
669       CeedCall(CeedRestoreJitSourceRoots(ceed, &jit_source_dirs));
670     }
671   }
672   *fallback_ceed = ceed->op_fallback_ceed;
673   return CEED_ERROR_SUCCESS;
674 }
675 
676 /**
677   @brief Set the fallback resource for `CeedOperator`.
678 
679   The current resource, if any, is freed by calling this function.
680   This string is freed upon the destruction of the `Ceed` context.
681 
682   @param[in,out] ceed     `Ceed` context
683   @param[in]     resource Fallback resource to set
684 
685   @return An error code: 0 - success, otherwise - failure
686 
687   @ref Backend
688 **/
689 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
690   // Free old
691   CeedCall(CeedFree(&ceed->op_fallback_resource));
692 
693   // Set new
694   CeedCall(CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource));
695 
696   // Check validity
697   ceed->has_valid_op_fallback_resource = ceed->op_fallback_resource && ceed->resource && strcmp(ceed->op_fallback_resource, ceed->resource);
698   return CEED_ERROR_SUCCESS;
699 }
700 
701 /**
702   @brief Flag `Ceed` context as deterministic
703 
704   @param[in]  ceed             `Ceed` to flag as deterministic
705   @param[out] is_deterministic Deterministic status to set
706 
707   @return An error code: 0 - success, otherwise - failure
708 
709   @ref Backend
710 **/
711 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
712   ceed->is_deterministic = is_deterministic;
713   return CEED_ERROR_SUCCESS;
714 }
715 
716 /**
717   @brief Set a backend function.
718 
719   This function is used for a backend to set the function associated with the Ceed objects.
720   For example, `CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)` sets the backend implementation of @ref CeedVectorCreate() and `CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)` sets the backend implementation of @ref CeedBasisApply().
721   Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
722 
723   @param[in]  ceed      `Ceed` context for error handling
724   @param[in]  type      Type of Ceed object to set function for
725   @param[out] object    Ceed object to set function for
726   @param[in]  func_name Name of function to set
727   @param[in]  f         Function to set
728 
729   @return An error code: 0 - success, otherwise - failure
730 
731   @ref Backend
732 **/
733 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
734   char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
735 
736   // Build lookup name
737   if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
738   strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
739   strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
740 
741   // Find and use offset
742   for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
743     if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
744       size_t offset          = ceed->f_offsets[i].offset;
745       int (**fpointer)(void) = (int (**)(void))((char *)object + offset);  // *NOPAD*
746 
747       *fpointer = (int (*)(void))f;
748       return CEED_ERROR_SUCCESS;
749     }
750   }
751 
752   // LCOV_EXCL_START
753   return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
754   // LCOV_EXCL_STOP
755 }
756 
757 /**
758   @brief Retrieve backend data for a `Ceed` context
759 
760   @param[in]  ceed `Ceed` context to retrieve data of
761   @param[out] data Address to save data to
762 
763   @return An error code: 0 - success, otherwise - failure
764 
765   @ref Backend
766 **/
767 int CeedGetData(Ceed ceed, void *data) {
768   *(void **)data = ceed->data;
769   return CEED_ERROR_SUCCESS;
770 }
771 
772 /**
773   @brief Set backend data for a `Ceed` context
774 
775   @param[in,out] ceed `Ceed` context to set data of
776   @param[in]     data Address of data to set
777 
778   @return An error code: 0 - success, otherwise - failure
779 
780   @ref Backend
781 **/
782 int CeedSetData(Ceed ceed, void *data) {
783   ceed->data = data;
784   return CEED_ERROR_SUCCESS;
785 }
786 
787 /**
788   @brief Increment the reference counter for a `Ceed` context
789 
790   @param[in,out] ceed `Ceed` context to increment the reference counter
791 
792   @return An error code: 0 - success, otherwise - failure
793 
794   @ref Backend
795 **/
796 int CeedReference(Ceed ceed) {
797   ceed->ref_count++;
798   return CEED_ERROR_SUCCESS;
799 }
800 
801 /**
802   @brief Get a `CeedVector` for scratch work from a `Ceed` context.
803 
804   Note: This vector must be restored with @ref CeedRestoreWorkVector().
805 
806   @param[in]  ceed `Ceed` context
807   @param[in]  len  Minimum length of work vector
808   @param[out] vec  Address of the variable where `CeedVector` will be stored
809 
810   @return An error code: 0 - success, otherwise - failure
811 
812   @ref Backend
813 **/
814 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) {
815   CeedInt i = 0;
816 
817   if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed));
818 
819   // Search for big enough work vector
820   for (i = 0; i < ceed->work_vectors->num_vecs; i++) {
821     if (!ceed->work_vectors->is_in_use[i]) {
822       CeedSize work_len;
823 
824       CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len));
825       if (work_len >= len) break;
826     }
827   }
828   // Long enough vector was not found
829   if (i == ceed->work_vectors->num_vecs) {
830     if (ceed->work_vectors->max_vecs == 0) {
831       ceed->work_vectors->max_vecs = 1;
832       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
833       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
834     } else if (ceed->work_vectors->max_vecs == i) {
835       ceed->work_vectors->max_vecs *= 2;
836       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
837       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
838     }
839     ceed->work_vectors->num_vecs++;
840     CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i]));
841     ceed->ref_count--;  // Note: ref_count manipulation to prevent a ref-loop
842   }
843   // Return pointer to work vector
844   ceed->work_vectors->is_in_use[i] = true;
845   *vec                             = NULL;
846   CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec));
847   ceed->ref_count++;  // Note: bump ref_count to account for external access
848   return CEED_ERROR_SUCCESS;
849 }
850 
851 /**
852   @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector()
853 
854   @param[in]  ceed `Ceed` context
855   @param[out] vec  `CeedVector` to restore
856 
857   @return An error code: 0 - success, otherwise - failure
858 
859   @ref Backend
860 **/
861 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) {
862   for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
863     if (*vec == ceed->work_vectors->vecs[i]) {
864       CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned");
865       CeedCall(CeedVectorDestroy(vec));
866       ceed->work_vectors->is_in_use[i] = false;
867       ceed->ref_count--;  // Note: reduce ref_count again to prevent a ref-loop
868       return CEED_ERROR_SUCCESS;
869     }
870   }
871   // LCOV_EXCL_START
872   return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()");
873   // LCOV_EXCL_STOP
874 }
875 
876 /**
877   @brief Retrieve list ofadditional JiT source roots from `Ceed` context.
878 
879   Note: The caller is responsible for restoring `jit_source_roots` with @ref CeedRestoreJitSourceRoots().
880 
881   @param[in]  ceed             `Ceed` context
882   @param[out] num_source_roots Number of JiT source directories
883   @param[out] jit_source_roots Absolute paths to additional JiT source directories
884 
885   @return An error code: 0 - success, otherwise - failure
886 
887   @ref Backend
888 **/
889 int CeedGetJitSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***jit_source_roots) {
890   Ceed ceed_parent;
891 
892   CeedCall(CeedGetParent(ceed, &ceed_parent));
893   *num_source_roots = ceed_parent->num_jit_source_roots;
894   *jit_source_roots = (const char **)ceed_parent->jit_source_roots;
895   return CEED_ERROR_SUCCESS;
896 }
897 
898 /**
899   @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots()
900 
901   @param[in]  ceed             `Ceed` context
902   @param[out] jit_source_roots Absolute paths to additional JiT source directories
903 
904   @return An error code: 0 - success, otherwise - failure
905 
906   @ref Backend
907 **/
908 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) {
909   *jit_source_roots = NULL;
910   return CEED_ERROR_SUCCESS;
911 }
912 
913 /// @}
914 
915 /// ----------------------------------------------------------------------------
916 /// Ceed Public API
917 /// ----------------------------------------------------------------------------
918 /// @addtogroup CeedUser
919 /// @{
920 
921 /**
922   @brief Get the list of available resource names for `Ceed` contexts
923 
924   Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
925 
926   @param[out] n          Number of available resources
927   @param[out] resources  List of available resource names
928   @param[out] priorities Resource name prioritization values, lower is better
929 
930   @return An error code: 0 - success, otherwise - failure
931 
932   @ref User
933 **/
934 // LCOV_EXCL_START
935 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
936   *n         = 0;
937   *resources = malloc(num_backends * sizeof(**resources));
938   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
939   if (priorities) {
940     *priorities = malloc(num_backends * sizeof(**priorities));
941     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
942   }
943   for (size_t i = 0; i < num_backends; i++) {
944     // Only report compiled backends
945     if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
946       *resources[i] = backends[i].prefix;
947       if (priorities) *priorities[i] = backends[i].priority;
948       *n += 1;
949     }
950   }
951   CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
952   *resources = realloc(*resources, *n * sizeof(**resources));
953   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
954   if (priorities) {
955     *priorities = realloc(*priorities, *n * sizeof(**priorities));
956     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
957   }
958   return CEED_ERROR_SUCCESS;
959 }
960 // LCOV_EXCL_STOP
961 
962 /**
963   @brief Initialize a `Ceed` context to use the specified resource.
964 
965   Note: Prefixing the resource with "help:" (e.g. "help:/cpu/self") will result in @ref CeedInt() printing the current libCEED version number and a list of current available backend resources to `stderr`.
966 
967   @param[in]  resource Resource to use, e.g., "/cpu/self"
968   @param[out] ceed     The library context
969 
970   @return An error code: 0 - success, otherwise - failure
971 
972   @ref User
973 
974   @sa CeedRegister() CeedDestroy()
975 **/
976 int CeedInit(const char *resource, Ceed *ceed) {
977   size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
978 
979   // Find matching backend
980   CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
981   CeedCall(CeedRegisterAll());
982 
983   // Check for help request
984   const char *help_prefix = "help";
985   size_t      match_help  = 0;
986   while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
987   if (match_help == 4) {
988     fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
989             CEED_VERSION_RELEASE ? "" : "+development");
990     fprintf(stderr, "Available backend resources:\n");
991     for (size_t i = 0; i < num_backends; i++) {
992       // Only report compiled backends
993       if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, "  %s\n", backends[i].prefix);
994     }
995     fflush(stderr);
996     match_help = 5;  // Delineating character expected
997   } else {
998     match_help = 0;
999   }
1000 
1001   // Find best match, computed as number of matching characters from requested resource stem
1002   size_t stem_length = 0;
1003   while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
1004   for (size_t i = 0; i < num_backends; i++) {
1005     size_t      n      = 0;
1006     const char *prefix = backends[i].prefix;
1007     while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
1008     priority = backends[i].priority;
1009     if (n > match_len || (n == match_len && match_priority > priority)) {
1010       match_len      = n;
1011       match_priority = priority;
1012       match_index    = i;
1013     }
1014   }
1015   // Using Levenshtein distance to find closest match
1016   if (match_len <= 1 || match_len != stem_length) {
1017     // LCOV_EXCL_START
1018     size_t lev_dis   = UINT_MAX;
1019     size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
1020     for (size_t i = 0; i < num_backends; i++) {
1021       const char *prefix        = backends[i].prefix;
1022       size_t      prefix_length = strlen(backends[i].prefix);
1023       size_t      min_len       = (prefix_length < stem_length) ? prefix_length : stem_length;
1024       size_t      column[min_len + 1];
1025       for (size_t j = 0; j <= min_len; j++) column[j] = j;
1026       for (size_t j = 1; j <= min_len; j++) {
1027         column[0] = j;
1028         for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
1029           size_t old_diag = column[k];
1030           size_t min_1    = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
1031           size_t min_2    = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
1032           column[k]       = (min_1 < min_2) ? min_1 : min_2;
1033           last_diag       = old_diag;
1034         }
1035       }
1036       size_t n = column[min_len];
1037       priority = backends[i].priority;
1038       if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
1039         lev_dis      = n;
1040         lev_priority = priority;
1041         lev_index    = i;
1042       }
1043     }
1044     const char *prefix_lev = backends[lev_index].prefix;
1045     size_t      lev_length = 0;
1046     while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
1047     size_t m = (lev_length < stem_length) ? lev_length : stem_length;
1048     if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
1049     else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
1050     // LCOV_EXCL_STOP
1051   }
1052 
1053   // Setup Ceed
1054   CeedCall(CeedCalloc(1, ceed));
1055   CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
1056   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1057   if (!ceed_error_handler) ceed_error_handler = "abort";
1058   if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
1059   else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
1060   else (*ceed)->Error = CeedErrorAbort;
1061   memcpy((*ceed)->err_msg, "No error message stored", 24);
1062   (*ceed)->ref_count = 1;
1063   (*ceed)->data      = NULL;
1064 
1065   // Set lookup table
1066   FOffset f_offsets[] = {
1067       CEED_FTABLE_ENTRY(Ceed, Error),
1068       CEED_FTABLE_ENTRY(Ceed, SetStream),
1069       CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
1070       CEED_FTABLE_ENTRY(Ceed, Destroy),
1071       CEED_FTABLE_ENTRY(Ceed, VectorCreate),
1072       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
1073       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
1074       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
1075       CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
1076       CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
1077       CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
1078       CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
1079       CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
1080       CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
1081       CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
1082       CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
1083       CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
1084       CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
1085       CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
1086       CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
1087       CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
1088       CEED_FTABLE_ENTRY(CeedVector, SetArray),
1089       CEED_FTABLE_ENTRY(CeedVector, TakeArray),
1090       CEED_FTABLE_ENTRY(CeedVector, SetValue),
1091       CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
1092       CEED_FTABLE_ENTRY(CeedVector, SyncArray),
1093       CEED_FTABLE_ENTRY(CeedVector, GetArray),
1094       CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
1095       CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
1096       CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
1097       CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
1098       CEED_FTABLE_ENTRY(CeedVector, Norm),
1099       CEED_FTABLE_ENTRY(CeedVector, Scale),
1100       CEED_FTABLE_ENTRY(CeedVector, AXPY),
1101       CEED_FTABLE_ENTRY(CeedVector, AXPBY),
1102       CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
1103       CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
1104       CEED_FTABLE_ENTRY(CeedVector, Destroy),
1105       CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
1106       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
1107       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
1108       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
1109       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
1110       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
1111       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
1112       CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
1113       CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
1114       CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
1115       CEED_FTABLE_ENTRY(CeedBasis, Apply),
1116       CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
1117       CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
1118       CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
1119       CEED_FTABLE_ENTRY(CeedBasis, Destroy),
1120       CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
1121       CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
1122       CEED_FTABLE_ENTRY(CeedQFunction, Apply),
1123       CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
1124       CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
1125       CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
1126       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
1127       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
1128       CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
1129       CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
1130       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
1131       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
1132       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
1133       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
1134       CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
1135       CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
1136       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
1137       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
1138       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
1139       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
1140       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
1141       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
1142       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
1143       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
1144       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
1145       CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
1146       CEED_FTABLE_ENTRY(CeedOperator, Apply),
1147       CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
1148       CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
1149       CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
1150       CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
1151       CEED_FTABLE_ENTRY(CeedOperator, Destroy),
1152       {NULL, 0}  // End of lookup table - used in SetBackendFunction loop
1153   };
1154 
1155   CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
1156   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
1157 
1158   // Set fallback for advanced CeedOperator functions
1159   const char fallback_resource[] = "";
1160   CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource));
1161 
1162   // Record env variables CEED_DEBUG or DBG
1163   (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1164 
1165   // Copy resource prefix, if backend setup successful
1166   CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1167 
1168   // Set default JiT source root
1169   // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1170   CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1171 
1172   // Backend specific setup
1173   CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1174   return CEED_ERROR_SUCCESS;
1175 }
1176 
1177 /**
1178   @brief Set the GPU stream for a `Ceed` context
1179 
1180   @param[in,out] ceed   `Ceed` context to set the stream
1181   @param[in]     handle Handle to GPU stream
1182 
1183   @return An error code: 0 - success, otherwise - failure
1184 
1185   @ref User
1186 **/
1187 int CeedSetStream(Ceed ceed, void *handle) {
1188   CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1189   if (ceed->SetStream) {
1190     CeedCall(ceed->SetStream(ceed, handle));
1191   } else {
1192     Ceed delegate;
1193     CeedCall(CeedGetDelegate(ceed, &delegate));
1194 
1195     if (delegate) CeedCall(CeedSetStream(delegate, handle));
1196     else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1197   }
1198   return CEED_ERROR_SUCCESS;
1199 }
1200 
1201 /**
1202   @brief Copy the pointer to a `Ceed` context.
1203 
1204   Both pointers should be destroyed with @ref CeedDestroy().
1205 
1206   Note: If the value of `*ceed_copy` passed to this function is non-`NULL`, then it is assumed that `*ceed_copy` is a pointer to a `Ceed` context.
1207         This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1208 
1209   @param[in]     ceed      `Ceed` context to copy reference to
1210   @param[in,out] ceed_copy Variable to store copied reference
1211 
1212   @return An error code: 0 - success, otherwise - failure
1213 
1214   @ref User
1215 **/
1216 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1217   CeedCall(CeedReference(ceed));
1218   CeedCall(CeedDestroy(ceed_copy));
1219   *ceed_copy = ceed;
1220   return CEED_ERROR_SUCCESS;
1221 }
1222 
1223 /**
1224   @brief Get the full resource name for a `Ceed` context
1225 
1226   @param[in]  ceed     `Ceed` context to get resource name of
1227   @param[out] resource Variable to store resource name
1228 
1229   @return An error code: 0 - success, otherwise - failure
1230 
1231   @ref User
1232 **/
1233 int CeedGetResource(Ceed ceed, const char **resource) {
1234   *resource = (const char *)ceed->resource;
1235   return CEED_ERROR_SUCCESS;
1236 }
1237 
1238 /**
1239   @brief Return `Ceed` context preferred memory type
1240 
1241   @param[in]  ceed     `Ceed` context to get preferred memory type of
1242   @param[out] mem_type Address to save preferred memory type to
1243 
1244   @return An error code: 0 - success, otherwise - failure
1245 
1246   @ref User
1247 **/
1248 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1249   if (ceed->GetPreferredMemType) {
1250     CeedCall(ceed->GetPreferredMemType(mem_type));
1251   } else {
1252     Ceed delegate;
1253     CeedCall(CeedGetDelegate(ceed, &delegate));
1254 
1255     if (delegate) {
1256       CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1257     } else {
1258       *mem_type = CEED_MEM_HOST;
1259     }
1260   }
1261   return CEED_ERROR_SUCCESS;
1262 }
1263 
1264 /**
1265   @brief Get deterministic status of `Ceed` context
1266 
1267   @param[in]  ceed             `Ceed` context
1268   @param[out] is_deterministic Variable to store deterministic status
1269 
1270   @return An error code: 0 - success, otherwise - failure
1271 
1272   @ref User
1273 **/
1274 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1275   *is_deterministic = ceed->is_deterministic;
1276   return CEED_ERROR_SUCCESS;
1277 }
1278 
1279 /**
1280   @brief Set additional JiT source root for `Ceed` context
1281 
1282   @param[in,out] ceed            `Ceed` context
1283   @param[in]     jit_source_root Absolute path to additional JiT source directory
1284 
1285   @return An error code: 0 - success, otherwise - failure
1286 
1287   @ref User
1288 **/
1289 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1290   Ceed ceed_parent;
1291 
1292   CeedCall(CeedGetParent(ceed, &ceed_parent));
1293 
1294   CeedInt index       = ceed_parent->num_jit_source_roots;
1295   size_t  path_length = strlen(jit_source_root);
1296 
1297   CeedCall(CeedRealloc(index + 1, &ceed_parent->jit_source_roots));
1298   CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1299   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1300   ceed_parent->num_jit_source_roots++;
1301   return CEED_ERROR_SUCCESS;
1302 }
1303 
1304 /**
1305   @brief View a `Ceed`
1306 
1307   @param[in] ceed   `Ceed` to view
1308   @param[in] stream Filestream to write to
1309 
1310   @return An error code: 0 - success, otherwise - failure
1311 
1312   @ref User
1313 **/
1314 int CeedView(Ceed ceed, FILE *stream) {
1315   CeedMemType mem_type;
1316 
1317   CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1318 
1319   fprintf(stream,
1320           "Ceed\n"
1321           "  Ceed Resource: %s\n"
1322           "  Preferred MemType: %s\n",
1323           ceed->resource, CeedMemTypes[mem_type]);
1324   return CEED_ERROR_SUCCESS;
1325 }
1326 
1327 /**
1328   @brief Destroy a `Ceed`
1329 
1330   @param[in,out] ceed Address of `Ceed` context to destroy
1331 
1332   @return An error code: 0 - success, otherwise - failure
1333 
1334   @ref User
1335 **/
1336 int CeedDestroy(Ceed *ceed) {
1337   if (!*ceed || --(*ceed)->ref_count > 0) {
1338     *ceed = NULL;
1339     return CEED_ERROR_SUCCESS;
1340   }
1341   if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1342 
1343   if ((*ceed)->obj_delegate_count > 0) {
1344     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1345       CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1346       CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1347     }
1348     CeedCall(CeedFree(&(*ceed)->obj_delegates));
1349   }
1350 
1351   if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1352 
1353   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1354     CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1355   }
1356   CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1357 
1358   CeedCall(CeedFree(&(*ceed)->f_offsets));
1359   CeedCall(CeedFree(&(*ceed)->resource));
1360   CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1361   CeedCall(CeedFree(&(*ceed)->op_fallback_resource));
1362   CeedCall(CeedWorkVectorsDestroy(*ceed));
1363   CeedCall(CeedFree(ceed));
1364   return CEED_ERROR_SUCCESS;
1365 }
1366 
1367 // LCOV_EXCL_START
1368 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1369   if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1370   if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1371   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1372   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args);  // NOLINT
1373   return ceed->err_msg;
1374 }
1375 // LCOV_EXCL_STOP
1376 
1377 /**
1378   @brief Error handling implementation; use @ref CeedError() instead.
1379 
1380   @return An error code: 0 - success, otherwise - failure
1381 
1382   @ref Developer
1383 **/
1384 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1385   va_list args;
1386   int     ret_val;
1387 
1388   va_start(args, format);
1389   if (ceed) {
1390     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1391   } else {
1392     // LCOV_EXCL_START
1393     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1394     if (!ceed_error_handler) ceed_error_handler = "abort";
1395     if (!strcmp(ceed_error_handler, "return")) {
1396       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1397     } else {
1398       // This function will not return
1399       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1400     }
1401   }
1402   va_end(args);
1403   return ret_val;
1404   // LCOV_EXCL_STOP
1405 }
1406 
1407 /**
1408   @brief Error handler that returns without printing anything.
1409 
1410   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1411 
1412   @return An error code: 0 - success, otherwise - failure
1413 
1414   @ref Developer
1415 **/
1416 // LCOV_EXCL_START
1417 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1418   return err_code;
1419 }
1420 // LCOV_EXCL_STOP
1421 
1422 /**
1423   @brief Error handler that stores the error message for future use and returns the error.
1424 
1425   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1426 
1427   @return An error code: 0 - success, otherwise - failure
1428 
1429   @ref Developer
1430 **/
1431 // LCOV_EXCL_START
1432 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1433   if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1434   if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args);
1435 
1436   // Build message
1437   int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1438   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1439   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);  // NOLINT
1440   return err_code;
1441 }
1442 // LCOV_EXCL_STOP
1443 
1444 /**
1445   @brief Error handler that prints to `stderr` and aborts
1446 
1447   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1448 
1449   @return An error code: 0 - success, otherwise - failure
1450 
1451   @ref Developer
1452 **/
1453 // LCOV_EXCL_START
1454 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1455   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1456   vfprintf(stderr, format, *args);
1457   fprintf(stderr, "\n");
1458   abort();
1459   return err_code;
1460 }
1461 // LCOV_EXCL_STOP
1462 
1463 /**
1464   @brief Error handler that prints to `stderr` and exits.
1465 
1466   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1467 
1468   In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1469 
1470   @return An error code: 0 - success, otherwise - failure
1471 
1472   @ref Developer
1473 **/
1474 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1475   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1476   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1477   vfprintf(stderr, format, *args);  // NOLINT
1478   fprintf(stderr, "\n");
1479   exit(err_code);
1480   return err_code;
1481 }
1482 
1483 /**
1484   @brief Set error handler
1485 
1486   A default error handler is set in @ref CeedInit().
1487   Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1488 
1489   @return An error code: 0 - success, otherwise - failure
1490 
1491   @ref Developer
1492 **/
1493 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1494   ceed->Error = handler;
1495   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1496   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1497   return CEED_ERROR_SUCCESS;
1498 }
1499 
1500 /**
1501   @brief Get error message
1502 
1503   The error message is only stored when using the error handler @ref CeedErrorStore()
1504 
1505   @param[in]  ceed    `Ceed` context to retrieve error message
1506   @param[out] err_msg Char pointer to hold error message
1507 
1508   @return An error code: 0 - success, otherwise - failure
1509 
1510   @ref Developer
1511 **/
1512 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1513   if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1514   if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1515   *err_msg = ceed->err_msg;
1516   return CEED_ERROR_SUCCESS;
1517 }
1518 
1519 /**
1520   @brief Restore error message.
1521 
1522   The error message is only stored when using the error handler @ref CeedErrorStore().
1523 
1524   @param[in]  ceed    `Ceed` context to restore error message
1525   @param[out] err_msg Char pointer that holds error message
1526 
1527   @return An error code: 0 - success, otherwise - failure
1528 
1529   @ref Developer
1530 **/
1531 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1532   if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1533   if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1534   *err_msg = NULL;
1535   memcpy(ceed->err_msg, "No error message stored", 24);
1536   return CEED_ERROR_SUCCESS;
1537 }
1538 
1539 /**
1540   @brief Get libCEED library version information.
1541 
1542   libCEED version numbers have the form major.minor.patch.
1543   Non-release versions may contain unstable interfaces.
1544 
1545   @param[out] major   Major version of the library
1546   @param[out] minor   Minor version of the library
1547   @param[out] patch   Patch (subminor) version of the library
1548   @param[out] release True for releases; false for development branches
1549 
1550   The caller may pass `NULL` for any arguments that are not needed.
1551 
1552   @return An error code: 0 - success, otherwise - failure
1553 
1554   @ref Developer
1555 
1556   @sa CEED_VERSION_GE()
1557 */
1558 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1559   if (major) *major = CEED_VERSION_MAJOR;
1560   if (minor) *minor = CEED_VERSION_MINOR;
1561   if (patch) *patch = CEED_VERSION_PATCH;
1562   if (release) *release = CEED_VERSION_RELEASE;
1563   return CEED_ERROR_SUCCESS;
1564 }
1565 
1566 /**
1567   @brief Get libCEED scalar type, such as F64 or F32
1568 
1569   @param[out] scalar_type Type of libCEED scalars
1570 
1571   @return An error code: 0 - success, otherwise - failure
1572 
1573   @ref Developer
1574 */
1575 int CeedGetScalarType(CeedScalarType *scalar_type) {
1576   *scalar_type = CEED_SCALAR_TYPE;
1577   return CEED_ERROR_SUCCESS;
1578 }
1579 
1580 /// @}
1581