xref: /libCEED/interface/ceed.c (revision 73501bfef0b51f07a8a68767e825f66e6042112c)
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   *fallback_ceed = ceed->op_fallback_ceed;
663   return CEED_ERROR_SUCCESS;
664 }
665 
666 /**
667   @brief Set the fallback resource for `CeedOperator`.
668 
669   The current resource, if any, is freed by calling this function.
670   This string is freed upon the destruction of the `Ceed` context.
671 
672   @param[in,out] ceed     `Ceed` context
673   @param[in]     resource Fallback resource to set
674 
675   @return An error code: 0 - success, otherwise - failure
676 
677   @ref Backend
678 **/
679 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
680   // Free old
681   CeedCall(CeedFree(&ceed->op_fallback_resource));
682 
683   // Set new
684   CeedCall(CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource));
685 
686   // Check validity
687   ceed->has_valid_op_fallback_resource = ceed->op_fallback_resource && ceed->resource && strcmp(ceed->op_fallback_resource, ceed->resource);
688   return CEED_ERROR_SUCCESS;
689 }
690 
691 /**
692   @brief Flag `Ceed` context as deterministic
693 
694   @param[in]  ceed             `Ceed` to flag as deterministic
695   @param[out] is_deterministic Deterministic status to set
696 
697   @return An error code: 0 - success, otherwise - failure
698 
699   @ref Backend
700 **/
701 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
702   ceed->is_deterministic = is_deterministic;
703   return CEED_ERROR_SUCCESS;
704 }
705 
706 /**
707   @brief Set a backend function.
708 
709   This function is used for a backend to set the function associated with the Ceed objects.
710   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().
711   Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
712 
713   @param[in]  ceed      `Ceed` context for error handling
714   @param[in]  type      Type of Ceed object to set function for
715   @param[out] object    Ceed object to set function for
716   @param[in]  func_name Name of function to set
717   @param[in]  f         Function to set
718 
719   @return An error code: 0 - success, otherwise - failure
720 
721   @ref Backend
722 **/
723 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
724   char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
725 
726   // Build lookup name
727   if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
728   strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
729   strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
730 
731   // Find and use offset
732   for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
733     if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
734       size_t offset          = ceed->f_offsets[i].offset;
735       int (**fpointer)(void) = (int (**)(void))((char *)object + offset);  // *NOPAD*
736 
737       *fpointer = (int (*)(void))f;
738       return CEED_ERROR_SUCCESS;
739     }
740   }
741 
742   // LCOV_EXCL_START
743   return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
744   // LCOV_EXCL_STOP
745 }
746 
747 /**
748   @brief Retrieve backend data for a `Ceed` context
749 
750   @param[in]  ceed `Ceed` context to retrieve data of
751   @param[out] data Address to save data to
752 
753   @return An error code: 0 - success, otherwise - failure
754 
755   @ref Backend
756 **/
757 int CeedGetData(Ceed ceed, void *data) {
758   *(void **)data = ceed->data;
759   return CEED_ERROR_SUCCESS;
760 }
761 
762 /**
763   @brief Set backend data for a `Ceed` context
764 
765   @param[in,out] ceed `Ceed` context to set data of
766   @param[in]     data Address of data to set
767 
768   @return An error code: 0 - success, otherwise - failure
769 
770   @ref Backend
771 **/
772 int CeedSetData(Ceed ceed, void *data) {
773   ceed->data = data;
774   return CEED_ERROR_SUCCESS;
775 }
776 
777 /**
778   @brief Increment the reference counter for a `Ceed` context
779 
780   @param[in,out] ceed `Ceed` context to increment the reference counter
781 
782   @return An error code: 0 - success, otherwise - failure
783 
784   @ref Backend
785 **/
786 int CeedReference(Ceed ceed) {
787   ceed->ref_count++;
788   return CEED_ERROR_SUCCESS;
789 }
790 
791 /**
792   @brief Get a `CeedVector` for scratch work from a `Ceed` context.
793 
794   Note: This vector must be restored with @ref CeedRestoreWorkVector().
795 
796   @param[in]  ceed `Ceed` context
797   @param[in]  len  Minimum length of work vector
798   @param[out] vec  Address of the variable where `CeedVector` will be stored
799 
800   @return An error code: 0 - success, otherwise - failure
801 
802   @ref Backend
803 **/
804 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) {
805   CeedInt i = 0;
806 
807   if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed));
808 
809   // Search for big enough work vector
810   for (i = 0; i < ceed->work_vectors->num_vecs; i++) {
811     if (!ceed->work_vectors->is_in_use[i]) {
812       CeedSize work_len;
813 
814       CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len));
815       if (work_len >= len) break;
816     }
817   }
818   // Long enough vector was not found
819   if (i == ceed->work_vectors->num_vecs) {
820     if (ceed->work_vectors->max_vecs == 0) {
821       ceed->work_vectors->max_vecs = 1;
822       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
823       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
824     } else if (ceed->work_vectors->max_vecs == i) {
825       ceed->work_vectors->max_vecs *= 2;
826       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
827       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
828     }
829     ceed->work_vectors->num_vecs++;
830     CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i]));
831     ceed->ref_count--;  // Note: ref_count manipulation to prevent a ref-loop
832   }
833   // Return pointer to work vector
834   ceed->work_vectors->is_in_use[i] = true;
835   *vec                             = NULL;
836   CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec));
837   ceed->ref_count++;  // Note: bump ref_count to account for external access
838   return CEED_ERROR_SUCCESS;
839 }
840 
841 /**
842   @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector()
843 
844   @param[in]  ceed `Ceed` context
845   @param[out] vec  `CeedVector` to restore
846 
847   @return An error code: 0 - success, otherwise - failure
848 
849   @ref Backend
850 **/
851 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) {
852   for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
853     if (*vec == ceed->work_vectors->vecs[i]) {
854       CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned");
855       CeedCall(CeedVectorDestroy(vec));
856       ceed->work_vectors->is_in_use[i] = false;
857       ceed->ref_count--;  // Note: reduce ref_count again to prevent a ref-loop
858       return CEED_ERROR_SUCCESS;
859     }
860   }
861   // LCOV_EXCL_START
862   return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()");
863   // LCOV_EXCL_STOP
864 }
865 
866 /// @}
867 
868 /// ----------------------------------------------------------------------------
869 /// Ceed Public API
870 /// ----------------------------------------------------------------------------
871 /// @addtogroup CeedUser
872 /// @{
873 
874 /**
875   @brief Get the list of available resource names for `Ceed` contexts
876 
877   Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
878 
879   @param[out] n          Number of available resources
880   @param[out] resources  List of available resource names
881   @param[out] priorities Resource name prioritization values, lower is better
882 
883   @return An error code: 0 - success, otherwise - failure
884 
885   @ref User
886 **/
887 // LCOV_EXCL_START
888 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
889   *n         = 0;
890   *resources = malloc(num_backends * sizeof(**resources));
891   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
892   if (priorities) {
893     *priorities = malloc(num_backends * sizeof(**priorities));
894     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
895   }
896   for (size_t i = 0; i < num_backends; i++) {
897     // Only report compiled backends
898     if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
899       *resources[i] = backends[i].prefix;
900       if (priorities) *priorities[i] = backends[i].priority;
901       *n += 1;
902     }
903   }
904   CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
905   *resources = realloc(*resources, *n * sizeof(**resources));
906   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
907   if (priorities) {
908     *priorities = realloc(*priorities, *n * sizeof(**priorities));
909     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
910   }
911   return CEED_ERROR_SUCCESS;
912 }
913 // LCOV_EXCL_STOP
914 
915 /**
916   @brief Initialize a `Ceed` context to use the specified resource.
917 
918   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`.
919 
920   @param[in]  resource Resource to use, e.g., "/cpu/self"
921   @param[out] ceed     The library context
922 
923   @return An error code: 0 - success, otherwise - failure
924 
925   @ref User
926 
927   @sa CeedRegister() CeedDestroy()
928 **/
929 int CeedInit(const char *resource, Ceed *ceed) {
930   size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
931 
932   // Find matching backend
933   CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
934   CeedCall(CeedRegisterAll());
935 
936   // Check for help request
937   const char *help_prefix = "help";
938   size_t      match_help  = 0;
939   while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
940   if (match_help == 4) {
941     fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
942             CEED_VERSION_RELEASE ? "" : "+development");
943     fprintf(stderr, "Available backend resources:\n");
944     for (size_t i = 0; i < num_backends; i++) {
945       // Only report compiled backends
946       if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, "  %s\n", backends[i].prefix);
947     }
948     fflush(stderr);
949     match_help = 5;  // Delineating character expected
950   } else {
951     match_help = 0;
952   }
953 
954   // Find best match, computed as number of matching characters from requested resource stem
955   size_t stem_length = 0;
956   while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
957   for (size_t i = 0; i < num_backends; i++) {
958     size_t      n      = 0;
959     const char *prefix = backends[i].prefix;
960     while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
961     priority = backends[i].priority;
962     if (n > match_len || (n == match_len && match_priority > priority)) {
963       match_len      = n;
964       match_priority = priority;
965       match_index    = i;
966     }
967   }
968   // Using Levenshtein distance to find closest match
969   if (match_len <= 1 || match_len != stem_length) {
970     // LCOV_EXCL_START
971     size_t lev_dis   = UINT_MAX;
972     size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
973     for (size_t i = 0; i < num_backends; i++) {
974       const char *prefix        = backends[i].prefix;
975       size_t      prefix_length = strlen(backends[i].prefix);
976       size_t      min_len       = (prefix_length < stem_length) ? prefix_length : stem_length;
977       size_t      column[min_len + 1];
978       for (size_t j = 0; j <= min_len; j++) column[j] = j;
979       for (size_t j = 1; j <= min_len; j++) {
980         column[0] = j;
981         for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
982           size_t old_diag = column[k];
983           size_t min_1    = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
984           size_t min_2    = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
985           column[k]       = (min_1 < min_2) ? min_1 : min_2;
986           last_diag       = old_diag;
987         }
988       }
989       size_t n = column[min_len];
990       priority = backends[i].priority;
991       if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
992         lev_dis      = n;
993         lev_priority = priority;
994         lev_index    = i;
995       }
996     }
997     const char *prefix_lev = backends[lev_index].prefix;
998     size_t      lev_length = 0;
999     while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
1000     size_t m = (lev_length < stem_length) ? lev_length : stem_length;
1001     if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
1002     else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
1003     // LCOV_EXCL_STOP
1004   }
1005 
1006   // Setup Ceed
1007   CeedCall(CeedCalloc(1, ceed));
1008   CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
1009   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1010   if (!ceed_error_handler) ceed_error_handler = "abort";
1011   if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
1012   else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
1013   else (*ceed)->Error = CeedErrorAbort;
1014   memcpy((*ceed)->err_msg, "No error message stored", 24);
1015   (*ceed)->ref_count = 1;
1016   (*ceed)->data      = NULL;
1017 
1018   // Set lookup table
1019   FOffset f_offsets[] = {
1020       CEED_FTABLE_ENTRY(Ceed, Error),
1021       CEED_FTABLE_ENTRY(Ceed, SetStream),
1022       CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
1023       CEED_FTABLE_ENTRY(Ceed, Destroy),
1024       CEED_FTABLE_ENTRY(Ceed, VectorCreate),
1025       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
1026       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
1027       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
1028       CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
1029       CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
1030       CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
1031       CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
1032       CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
1033       CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
1034       CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
1035       CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
1036       CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
1037       CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
1038       CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
1039       CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
1040       CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
1041       CEED_FTABLE_ENTRY(CeedVector, SetArray),
1042       CEED_FTABLE_ENTRY(CeedVector, TakeArray),
1043       CEED_FTABLE_ENTRY(CeedVector, SetValue),
1044       CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
1045       CEED_FTABLE_ENTRY(CeedVector, SyncArray),
1046       CEED_FTABLE_ENTRY(CeedVector, GetArray),
1047       CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
1048       CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
1049       CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
1050       CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
1051       CEED_FTABLE_ENTRY(CeedVector, Norm),
1052       CEED_FTABLE_ENTRY(CeedVector, Scale),
1053       CEED_FTABLE_ENTRY(CeedVector, AXPY),
1054       CEED_FTABLE_ENTRY(CeedVector, AXPBY),
1055       CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
1056       CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
1057       CEED_FTABLE_ENTRY(CeedVector, Destroy),
1058       CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
1059       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
1060       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
1061       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
1062       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
1063       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
1064       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
1065       CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
1066       CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
1067       CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
1068       CEED_FTABLE_ENTRY(CeedBasis, Apply),
1069       CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
1070       CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
1071       CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
1072       CEED_FTABLE_ENTRY(CeedBasis, Destroy),
1073       CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
1074       CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
1075       CEED_FTABLE_ENTRY(CeedQFunction, Apply),
1076       CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
1077       CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
1078       CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
1079       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
1080       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
1081       CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
1082       CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
1083       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
1084       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
1085       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
1086       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
1087       CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
1088       CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
1089       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
1090       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
1091       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
1092       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
1093       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
1094       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
1095       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
1096       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
1097       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
1098       CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
1099       CEED_FTABLE_ENTRY(CeedOperator, Apply),
1100       CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
1101       CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
1102       CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
1103       CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
1104       CEED_FTABLE_ENTRY(CeedOperator, Destroy),
1105       {NULL, 0}  // End of lookup table - used in SetBackendFunction loop
1106   };
1107 
1108   CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
1109   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
1110 
1111   // Set fallback for advanced CeedOperator functions
1112   const char fallback_resource[] = "";
1113   CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource));
1114 
1115   // Record env variables CEED_DEBUG or DBG
1116   (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1117 
1118   // Copy resource prefix, if backend setup successful
1119   CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1120 
1121   // Set default JiT source root
1122   // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1123   CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1124 
1125   // Backend specific setup
1126   CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1127   return CEED_ERROR_SUCCESS;
1128 }
1129 
1130 /**
1131   @brief Set the GPU stream for a `Ceed` context
1132 
1133   @param[in,out] ceed   `Ceed` context to set the stream
1134   @param[in]     handle Handle to GPU stream
1135 
1136   @return An error code: 0 - success, otherwise - failure
1137 
1138   @ref User
1139 **/
1140 int CeedSetStream(Ceed ceed, void *handle) {
1141   CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1142   if (ceed->SetStream) {
1143     CeedCall(ceed->SetStream(ceed, handle));
1144   } else {
1145     Ceed delegate;
1146     CeedCall(CeedGetDelegate(ceed, &delegate));
1147 
1148     if (delegate) CeedCall(CeedSetStream(delegate, handle));
1149     else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1150   }
1151   return CEED_ERROR_SUCCESS;
1152 }
1153 
1154 /**
1155   @brief Copy the pointer to a `Ceed` context.
1156 
1157   Both pointers should be destroyed with @ref CeedDestroy().
1158 
1159   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.
1160         This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1161 
1162   @param[in]     ceed      `Ceed` context to copy reference to
1163   @param[in,out] ceed_copy Variable to store copied reference
1164 
1165   @return An error code: 0 - success, otherwise - failure
1166 
1167   @ref User
1168 **/
1169 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1170   CeedCall(CeedReference(ceed));
1171   CeedCall(CeedDestroy(ceed_copy));
1172   *ceed_copy = ceed;
1173   return CEED_ERROR_SUCCESS;
1174 }
1175 
1176 /**
1177   @brief Get the full resource name for a `Ceed` context
1178 
1179   @param[in]  ceed     `Ceed` context to get resource name of
1180   @param[out] resource Variable to store resource name
1181 
1182   @return An error code: 0 - success, otherwise - failure
1183 
1184   @ref User
1185 **/
1186 int CeedGetResource(Ceed ceed, const char **resource) {
1187   *resource = (const char *)ceed->resource;
1188   return CEED_ERROR_SUCCESS;
1189 }
1190 
1191 /**
1192   @brief Return `Ceed` context preferred memory type
1193 
1194   @param[in]  ceed     `Ceed` context to get preferred memory type of
1195   @param[out] mem_type Address to save preferred memory type to
1196 
1197   @return An error code: 0 - success, otherwise - failure
1198 
1199   @ref User
1200 **/
1201 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1202   if (ceed->GetPreferredMemType) {
1203     CeedCall(ceed->GetPreferredMemType(mem_type));
1204   } else {
1205     Ceed delegate;
1206     CeedCall(CeedGetDelegate(ceed, &delegate));
1207 
1208     if (delegate) {
1209       CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1210     } else {
1211       *mem_type = CEED_MEM_HOST;
1212     }
1213   }
1214   return CEED_ERROR_SUCCESS;
1215 }
1216 
1217 /**
1218   @brief Get deterministic status of `Ceed` context
1219 
1220   @param[in]  ceed             `Ceed` context
1221   @param[out] is_deterministic Variable to store deterministic status
1222 
1223   @return An error code: 0 - success, otherwise - failure
1224 
1225   @ref User
1226 **/
1227 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1228   *is_deterministic = ceed->is_deterministic;
1229   return CEED_ERROR_SUCCESS;
1230 }
1231 
1232 /**
1233   @brief Set additional JiT source root for `Ceed` context
1234 
1235   @param[in,out] ceed            `Ceed` context
1236   @param[in]     jit_source_root Absolute path to additional JiT source directory
1237 
1238   @return An error code: 0 - success, otherwise - failure
1239 
1240   @ref User
1241 **/
1242 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1243   Ceed ceed_parent;
1244 
1245   CeedCall(CeedGetParent(ceed, &ceed_parent));
1246 
1247   CeedInt index       = ceed_parent->num_jit_source_roots;
1248   size_t  path_length = strlen(jit_source_root);
1249 
1250   CeedCall(CeedRealloc(index + 1, &ceed_parent->jit_source_roots));
1251   CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1252   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1253   ceed_parent->num_jit_source_roots++;
1254   return CEED_ERROR_SUCCESS;
1255 }
1256 
1257 /**
1258   @brief View a `Ceed`
1259 
1260   @param[in] ceed   `Ceed` to view
1261   @param[in] stream Filestream to write to
1262 
1263   @return An error code: 0 - success, otherwise - failure
1264 
1265   @ref User
1266 **/
1267 int CeedView(Ceed ceed, FILE *stream) {
1268   CeedMemType mem_type;
1269 
1270   CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1271 
1272   fprintf(stream,
1273           "Ceed\n"
1274           "  Ceed Resource: %s\n"
1275           "  Preferred MemType: %s\n",
1276           ceed->resource, CeedMemTypes[mem_type]);
1277   return CEED_ERROR_SUCCESS;
1278 }
1279 
1280 /**
1281   @brief Destroy a `Ceed`
1282 
1283   @param[in,out] ceed Address of `Ceed` context to destroy
1284 
1285   @return An error code: 0 - success, otherwise - failure
1286 
1287   @ref User
1288 **/
1289 int CeedDestroy(Ceed *ceed) {
1290   if (!*ceed || --(*ceed)->ref_count > 0) {
1291     *ceed = NULL;
1292     return CEED_ERROR_SUCCESS;
1293   }
1294   if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1295 
1296   if ((*ceed)->obj_delegate_count > 0) {
1297     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1298       CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1299       CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1300     }
1301     CeedCall(CeedFree(&(*ceed)->obj_delegates));
1302   }
1303 
1304   if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1305 
1306   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1307     CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1308   }
1309   CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1310 
1311   CeedCall(CeedFree(&(*ceed)->f_offsets));
1312   CeedCall(CeedFree(&(*ceed)->resource));
1313   CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1314   CeedCall(CeedFree(&(*ceed)->op_fallback_resource));
1315   CeedCall(CeedWorkVectorsDestroy(*ceed));
1316   CeedCall(CeedFree(ceed));
1317   return CEED_ERROR_SUCCESS;
1318 }
1319 
1320 // LCOV_EXCL_START
1321 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1322   if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1323   if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1324   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1325   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args);  // NOLINT
1326   return ceed->err_msg;
1327 }
1328 // LCOV_EXCL_STOP
1329 
1330 /**
1331   @brief Error handling implementation; use @ref CeedError() instead.
1332 
1333   @return An error code: 0 - success, otherwise - failure
1334 
1335   @ref Developer
1336 **/
1337 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1338   va_list args;
1339   int     ret_val;
1340 
1341   va_start(args, format);
1342   if (ceed) {
1343     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1344   } else {
1345     // LCOV_EXCL_START
1346     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1347     if (!ceed_error_handler) ceed_error_handler = "abort";
1348     if (!strcmp(ceed_error_handler, "return")) {
1349       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1350     } else {
1351       // This function will not return
1352       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1353     }
1354   }
1355   va_end(args);
1356   return ret_val;
1357   // LCOV_EXCL_STOP
1358 }
1359 
1360 /**
1361   @brief Error handler that returns without printing anything.
1362 
1363   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1364 
1365   @return An error code: 0 - success, otherwise - failure
1366 
1367   @ref Developer
1368 **/
1369 // LCOV_EXCL_START
1370 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1371   return err_code;
1372 }
1373 // LCOV_EXCL_STOP
1374 
1375 /**
1376   @brief Error handler that stores the error message for future use and returns the error.
1377 
1378   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1379 
1380   @return An error code: 0 - success, otherwise - failure
1381 
1382   @ref Developer
1383 **/
1384 // LCOV_EXCL_START
1385 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1386   if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1387   if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args);
1388 
1389   // Build message
1390   int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1391   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1392   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);  // NOLINT
1393   return err_code;
1394 }
1395 // LCOV_EXCL_STOP
1396 
1397 /**
1398   @brief Error handler that prints to `stderr` and aborts
1399 
1400   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1401 
1402   @return An error code: 0 - success, otherwise - failure
1403 
1404   @ref Developer
1405 **/
1406 // LCOV_EXCL_START
1407 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1408   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1409   vfprintf(stderr, format, *args);
1410   fprintf(stderr, "\n");
1411   abort();
1412   return err_code;
1413 }
1414 // LCOV_EXCL_STOP
1415 
1416 /**
1417   @brief Error handler that prints to `stderr` and exits.
1418 
1419   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1420 
1421   In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1422 
1423   @return An error code: 0 - success, otherwise - failure
1424 
1425   @ref Developer
1426 **/
1427 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1428   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1429   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1430   vfprintf(stderr, format, *args);  // NOLINT
1431   fprintf(stderr, "\n");
1432   exit(err_code);
1433   return err_code;
1434 }
1435 
1436 /**
1437   @brief Set error handler
1438 
1439   A default error handler is set in @ref CeedInit().
1440   Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1441 
1442   @return An error code: 0 - success, otherwise - failure
1443 
1444   @ref Developer
1445 **/
1446 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1447   ceed->Error = handler;
1448   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1449   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1450   return CEED_ERROR_SUCCESS;
1451 }
1452 
1453 /**
1454   @brief Get error message
1455 
1456   The error message is only stored when using the error handler @ref CeedErrorStore()
1457 
1458   @param[in]  ceed    `Ceed` context to retrieve error message
1459   @param[out] err_msg Char pointer to hold error message
1460 
1461   @return An error code: 0 - success, otherwise - failure
1462 
1463   @ref Developer
1464 **/
1465 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1466   if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1467   if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1468   *err_msg = ceed->err_msg;
1469   return CEED_ERROR_SUCCESS;
1470 }
1471 
1472 /**
1473   @brief Restore error message.
1474 
1475   The error message is only stored when using the error handler @ref CeedErrorStore().
1476 
1477   @param[in]  ceed    `Ceed` context to restore error message
1478   @param[out] err_msg Char pointer that holds error message
1479 
1480   @return An error code: 0 - success, otherwise - failure
1481 
1482   @ref Developer
1483 **/
1484 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1485   if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1486   if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1487   *err_msg = NULL;
1488   memcpy(ceed->err_msg, "No error message stored", 24);
1489   return CEED_ERROR_SUCCESS;
1490 }
1491 
1492 /**
1493   @brief Get libCEED library version information.
1494 
1495   libCEED version numbers have the form major.minor.patch.
1496   Non-release versions may contain unstable interfaces.
1497 
1498   @param[out] major   Major version of the library
1499   @param[out] minor   Minor version of the library
1500   @param[out] patch   Patch (subminor) version of the library
1501   @param[out] release True for releases; false for development branches
1502 
1503   The caller may pass `NULL` for any arguments that are not needed.
1504 
1505   @return An error code: 0 - success, otherwise - failure
1506 
1507   @ref Developer
1508 
1509   @sa CEED_VERSION_GE()
1510 */
1511 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1512   if (major) *major = CEED_VERSION_MAJOR;
1513   if (minor) *minor = CEED_VERSION_MINOR;
1514   if (patch) *patch = CEED_VERSION_PATCH;
1515   if (release) *release = CEED_VERSION_RELEASE;
1516   return CEED_ERROR_SUCCESS;
1517 }
1518 
1519 /**
1520   @brief Get libCEED scalar type, such as F64 or F32
1521 
1522   @param[out] scalar_type Type of libCEED scalars
1523 
1524   @return An error code: 0 - success, otherwise - failure
1525 
1526   @ref Developer
1527 */
1528 int CeedGetScalarType(CeedScalarType *scalar_type) {
1529   *scalar_type = CEED_SCALAR_TYPE;
1530   return CEED_ERROR_SUCCESS;
1531 }
1532 
1533 /// @}
1534