xref: /libCEED/interface/ceed.c (revision f8fffaefe2b0bf336e4f45347da405b61e8c02d8)
1 // Copyright (c) 2017-2025, 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) {
353         if (*(void **)target_array_borrowed) {
354           *(void **)target_array = *(void **)target_array_borrowed;
355         } else {
356           if (!*(void **)target_array_owned) CeedCall(CeedCallocArray(num_values, size_unit, target_array_owned));
357           *(void **)target_array = *(void **)target_array_owned;
358         }
359       }
360       if (source_array) memcpy(*(void **)target_array, source_array, size_unit * num_values);
361       break;
362     case CEED_OWN_POINTER:
363       CeedCall(CeedFree(target_array_owned));
364       *(void **)target_array_owned    = (void *)source_array;
365       *(void **)target_array_borrowed = NULL;
366       *(void **)target_array          = *(void **)target_array_owned;
367       break;
368     case CEED_USE_POINTER:
369       CeedCall(CeedFree(target_array_owned));
370       *(void **)target_array_owned    = NULL;
371       *(void **)target_array_borrowed = (void *)source_array;
372       *(void **)target_array          = *(void **)target_array_borrowed;
373   }
374   return CEED_ERROR_SUCCESS;
375 }
376 
377 /** Manage handoff of user `bool` `source_array` to backend with proper @ref CeedCopyMode behavior.
378 
379   @param[in]     source_array          Source data provided by user
380   @param[in]     copy_mode             Copy mode for the data
381   @param[in]     num_values            Number of values to handle
382   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
383   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
384   @param[out]    target_array          Pointer to location for data
385 
386   @return An error code: 0 - success, otherwise - failure
387 
388   @ref Backend
389 **/
390 int CeedSetHostBoolArray(const bool *source_array, CeedCopyMode copy_mode, CeedSize num_values, const bool **target_array_owned,
391                          const bool **target_array_borrowed, const bool **target_array) {
392   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(bool), num_values, target_array_owned, target_array_borrowed, target_array));
393   return CEED_ERROR_SUCCESS;
394 }
395 
396 /** Manage handoff of user `CeedInt8` `source_array` to backend with proper @ref CeedCopyMode behavior.
397 
398   @param[in]     source_array          Source data provided by user
399   @param[in]     copy_mode             Copy mode for the data
400   @param[in]     num_values            Number of values to handle
401   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
402   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
403   @param[out]    target_array          Pointer to location for data
404 
405   @return An error code: 0 - success, otherwise - failure
406 
407   @ref Backend
408 **/
409 int CeedSetHostCeedInt8Array(const CeedInt8 *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt8 **target_array_owned,
410                              const CeedInt8 **target_array_borrowed, const CeedInt8 **target_array) {
411   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt8), num_values, target_array_owned, target_array_borrowed, target_array));
412   return CEED_ERROR_SUCCESS;
413 }
414 
415 /** Manage handoff of user `CeedInt` `source_array` to backend with proper @ref CeedCopyMode behavior.
416 
417   @param[in]     source_array          Source data provided by user
418   @param[in]     copy_mode             Copy mode for the data
419   @param[in]     num_values            Number of values to handle
420   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
421   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
422   @param[out]    target_array          Pointer to location for data
423 
424   @return An error code: 0 - success, otherwise - failure
425 
426   @ref Backend
427 **/
428 int CeedSetHostCeedIntArray(const CeedInt *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt **target_array_owned,
429                             const CeedInt **target_array_borrowed, const CeedInt **target_array) {
430   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt), num_values, target_array_owned, target_array_borrowed, target_array));
431   return CEED_ERROR_SUCCESS;
432 }
433 
434 /** Manage handoff of user `CeedScalar` `source_array` to backend with proper @ref CeedCopyMode behavior.
435 
436   @param[in]     source_array          Source data provided by user
437   @param[in]     copy_mode             Copy mode for the data
438   @param[in]     num_values            Number of values to handle
439   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
440   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
441   @param[out]    target_array          Pointer to location for data
442 
443   @return An error code: 0 - success, otherwise - failure
444 
445   @ref Backend
446 **/
447 int CeedSetHostCeedScalarArray(const CeedScalar *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedScalar **target_array_owned,
448                                const CeedScalar **target_array_borrowed, const CeedScalar **target_array) {
449   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedScalar), num_values, target_array_owned, target_array_borrowed, target_array));
450   return CEED_ERROR_SUCCESS;
451 }
452 
453 /**
454   @brief Register a `Ceed` backend
455 
456   @param[in] prefix   Prefix of resources for this backend to respond to.
457                         For example, the reference backend responds to "/cpu/self".
458   @param[in] init     Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
459   @param[in] priority Integer priority.
460                         Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
461 
462   @return An error code: 0 - success, otherwise - failure
463 
464   @ref Backend
465 **/
466 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
467   CeedDebugEnv("Backend Register: %s", prefix);
468   CeedRegisterImpl(prefix, init, priority);
469   return CEED_ERROR_SUCCESS;
470 }
471 
472 /**
473   @brief Return debugging status flag
474 
475   @param[in]  ceed     `Ceed` context to get debugging flag
476   @param[out] is_debug Variable to store debugging flag
477 
478   @return An error code: 0 - success, otherwise - failure
479 
480   @ref Backend
481 **/
482 int CeedIsDebug(Ceed ceed, bool *is_debug) {
483   *is_debug = ceed->is_debug;
484   return CEED_ERROR_SUCCESS;
485 }
486 
487 /**
488   @brief Get the root of the requested resource.
489 
490   Note: Caller is responsible for calling @ref CeedFree() on the `resource_root`.
491 
492   @param[in]  ceed          `Ceed` context to get resource name of
493   @param[in]  resource      Full user specified resource
494   @param[in]  delineator    Delineator to break `resource_root` and `resource_spec`
495   @param[out] resource_root Variable to store resource root
496 
497   @return An error code: 0 - success, otherwise - failure
498 
499   @ref Backend
500 **/
501 int CeedGetResourceRoot(Ceed ceed, const char *resource, const char *delineator, char **resource_root) {
502   char  *device_spec       = strstr(resource, delineator);
503   size_t resource_root_len = device_spec ? (size_t)(device_spec - resource) + 1 : strlen(resource) + 1;
504 
505   CeedCall(CeedCalloc(resource_root_len, resource_root));
506   memcpy(*resource_root, resource, resource_root_len - 1);
507   return CEED_ERROR_SUCCESS;
508 }
509 
510 /**
511   @brief Retrieve a parent `Ceed` context
512 
513   @param[in]  ceed   `Ceed` context to retrieve parent of
514   @param[out] parent Address to save the parent to
515 
516   @return An error code: 0 - success, otherwise - failure
517 
518   @ref Backend
519 **/
520 int CeedGetParent(Ceed ceed, Ceed *parent) {
521   if (ceed->parent) {
522     CeedCall(CeedGetParent(ceed->parent, parent));
523     return CEED_ERROR_SUCCESS;
524   }
525   *parent = NULL;
526   CeedCall(CeedReferenceCopy(ceed, parent));
527   return CEED_ERROR_SUCCESS;
528 }
529 
530 /**
531   @brief Retrieve a delegate `Ceed` context
532 
533   @param[in]  ceed     `Ceed` context to retrieve delegate of
534   @param[out] delegate Address to save the delegate to
535 
536   @return An error code: 0 - success, otherwise - failure
537 
538   @ref Backend
539 **/
540 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
541   *delegate = NULL;
542   if (ceed->delegate) CeedCall(CeedReferenceCopy(ceed->delegate, delegate));
543   return CEED_ERROR_SUCCESS;
544 }
545 
546 /**
547   @brief Set a delegate `Ceed` context
548 
549   This function allows a `Ceed` context to set a delegate `Ceed` context.
550   All backend implementations default to the delegate `Ceed` context, unless overridden.
551 
552   @param[in]  ceed     `Ceed` context to set delegate of
553   @param[out] delegate Address to set the delegate to
554 
555   @return An error code: 0 - success, otherwise - failure
556 
557   @ref Backend
558 **/
559 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
560   CeedCall(CeedReferenceCopy(delegate, &ceed->delegate));
561   delegate->parent = ceed;
562   return CEED_ERROR_SUCCESS;
563 }
564 
565 /**
566   @brief Retrieve a delegate `Ceed` context for a specific object type
567 
568   @param[in]  ceed     `Ceed` context to retrieve delegate of
569   @param[out] delegate Address to save the delegate to
570   @param[in]  obj_name Name of the object type to retrieve delegate for
571 
572   @return An error code: 0 - success, otherwise - failure
573 
574   @ref Backend
575 **/
576 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) {
577   // Check for object delegate
578   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) {
579     if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) {
580       *delegate = NULL;
581       CeedCall(CeedReferenceCopy(ceed->obj_delegates->delegate, delegate));
582       return CEED_ERROR_SUCCESS;
583     }
584   }
585 
586   // Use default delegate if no object delegate
587   CeedCall(CeedGetDelegate(ceed, delegate));
588   return CEED_ERROR_SUCCESS;
589 }
590 
591 /**
592   @brief Set a delegate `Ceed` context for a specific object type
593 
594   This function allows a `Ceed` context to set a delegate `Ceed` context for a given type of `Ceed` object.
595   All backend implementations default to the delegate `Ceed` context for this object.
596   For example, `CeedSetObjectDelegate(ceed, delegate, "Basis")` uses delegate implementations for all `CeedBasis` backend functions.
597 
598   @param[in,out] ceed     `Ceed` context to set delegate of
599   @param[in]     delegate `Ceed` context to use for delegation
600   @param[in]     obj_name Name of the object type to set delegate for
601 
602   @return An error code: 0 - success, otherwise - failure
603 
604   @ref Backend
605 **/
606 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) {
607   CeedInt count = ceed->obj_delegate_count;
608 
609   // Malloc or Realloc
610   if (count) {
611     CeedCall(CeedRealloc(count + 1, &ceed->obj_delegates));
612   } else {
613     CeedCall(CeedCalloc(1, &ceed->obj_delegates));
614   }
615   ceed->obj_delegate_count++;
616 
617   // Set object delegate
618   CeedCall(CeedReferenceCopy(delegate, &ceed->obj_delegates[count].delegate));
619   CeedCall(CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name));
620 
621   // Set delegate parent
622   delegate->parent = ceed;
623   return CEED_ERROR_SUCCESS;
624 }
625 
626 /**
627   @brief Get the fallback resource for `CeedOperator`
628 
629   @param[in]  ceed     `Ceed` context
630   @param[out] resource Variable to store fallback resource
631 
632   @return An error code: 0 - success, otherwise - failure
633 
634   @ref Backend
635 **/
636 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
637   *resource = (const char *)ceed->op_fallback_resource;
638   return CEED_ERROR_SUCCESS;
639 }
640 
641 /**
642   @brief Get the fallback `Ceed` for `CeedOperator`
643 
644   @param[in]  ceed          `Ceed` context
645   @param[out] fallback_ceed Variable to store fallback `Ceed`
646 
647   @return An error code: 0 - success, otherwise - failure
648 
649   @ref Backend
650 **/
651 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) {
652   if (ceed->has_valid_op_fallback_resource) {
653     CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n");
654     CeedDebug(ceed, "Getting fallback from %s to %s\n", ceed->resource, ceed->op_fallback_resource);
655   }
656 
657   // Create fallback Ceed if uninitalized
658   if (!ceed->op_fallback_ceed && ceed->has_valid_op_fallback_resource) {
659     CeedDebug(ceed, "Creating fallback Ceed");
660 
661     Ceed        fallback_ceed;
662     const char *fallback_resource;
663 
664     CeedCall(CeedGetOperatorFallbackResource(ceed, &fallback_resource));
665     CeedCall(CeedInit(fallback_resource, &fallback_ceed));
666     fallback_ceed->op_fallback_parent = ceed;
667     fallback_ceed->Error              = ceed->Error;
668     ceed->op_fallback_ceed            = fallback_ceed;
669     {
670       const char **jit_source_roots;
671       CeedInt      num_jit_source_roots = 0;
672 
673       CeedCall(CeedGetJitSourceRoots(ceed, &num_jit_source_roots, &jit_source_roots));
674       for (CeedInt i = 0; i < num_jit_source_roots; i++) {
675         CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_source_roots[i]));
676       }
677       CeedCall(CeedRestoreJitSourceRoots(ceed, &jit_source_roots));
678     }
679     {
680       const char **jit_defines;
681       CeedInt      num_jit_defines = 0;
682 
683       CeedCall(CeedGetJitDefines(ceed, &num_jit_defines, &jit_defines));
684       for (CeedInt i = 0; i < num_jit_defines; i++) {
685         CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_defines[i]));
686       }
687       CeedCall(CeedRestoreJitDefines(ceed, &jit_defines));
688     }
689   }
690   *fallback_ceed = NULL;
691   if (ceed->op_fallback_ceed) CeedCall(CeedReferenceCopy(ceed->op_fallback_ceed, fallback_ceed));
692   return CEED_ERROR_SUCCESS;
693 }
694 
695 /**
696   @brief Set the fallback resource for `CeedOperator`.
697 
698   The current resource, if any, is freed by calling this function.
699   This string is freed upon the destruction of the `Ceed` context.
700 
701   @param[in,out] ceed     `Ceed` context
702   @param[in]     resource Fallback resource to set
703 
704   @return An error code: 0 - success, otherwise - failure
705 
706   @ref Backend
707 **/
708 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
709   // Free old
710   CeedCall(CeedFree(&ceed->op_fallback_resource));
711 
712   // Set new
713   CeedCall(CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource));
714 
715   // Check validity
716   ceed->has_valid_op_fallback_resource = ceed->op_fallback_resource && ceed->resource && strcmp(ceed->op_fallback_resource, ceed->resource);
717   return CEED_ERROR_SUCCESS;
718 }
719 
720 /**
721   @brief Flag `Ceed` context as deterministic
722 
723   @param[in]  ceed             `Ceed` to flag as deterministic
724   @param[out] is_deterministic Deterministic status to set
725 
726   @return An error code: 0 - success, otherwise - failure
727 
728   @ref Backend
729 **/
730 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
731   ceed->is_deterministic = is_deterministic;
732   return CEED_ERROR_SUCCESS;
733 }
734 
735 /**
736   @brief Set a backend function.
737 
738   This function is used for a backend to set the function associated with the Ceed objects.
739   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().
740   Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
741 
742   @param[in]  ceed      `Ceed` context for error handling
743   @param[in]  type      Type of Ceed object to set function for
744   @param[out] object    Ceed object to set function for
745   @param[in]  func_name Name of function to set
746   @param[in]  f         Function to set
747 
748   @return An error code: 0 - success, otherwise - failure
749 
750   @ref Backend
751 **/
752 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
753   char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
754 
755   // Build lookup name
756   if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
757   strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
758   strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
759 
760   // Find and use offset
761   for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
762     if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
763       size_t offset          = ceed->f_offsets[i].offset;
764       int (**fpointer)(void) = (int (**)(void))((char *)object + offset);  // *NOPAD*
765 
766       *fpointer = (int (*)(void))f;
767       return CEED_ERROR_SUCCESS;
768     }
769   }
770 
771   // LCOV_EXCL_START
772   return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
773   // LCOV_EXCL_STOP
774 }
775 
776 /**
777   @brief Retrieve backend data for a `Ceed` context
778 
779   @param[in]  ceed `Ceed` context to retrieve data of
780   @param[out] data Address to save data to
781 
782   @return An error code: 0 - success, otherwise - failure
783 
784   @ref Backend
785 **/
786 int CeedGetData(Ceed ceed, void *data) {
787   *(void **)data = ceed->data;
788   return CEED_ERROR_SUCCESS;
789 }
790 
791 /**
792   @brief Set backend data for a `Ceed` context
793 
794   @param[in,out] ceed `Ceed` context to set data of
795   @param[in]     data Address of data to set
796 
797   @return An error code: 0 - success, otherwise - failure
798 
799   @ref Backend
800 **/
801 int CeedSetData(Ceed ceed, void *data) {
802   ceed->data = data;
803   return CEED_ERROR_SUCCESS;
804 }
805 
806 /**
807   @brief Increment the reference counter for a `Ceed` context
808 
809   @param[in,out] ceed `Ceed` context to increment the reference counter
810 
811   @return An error code: 0 - success, otherwise - failure
812 
813   @ref Backend
814 **/
815 int CeedReference(Ceed ceed) {
816   ceed->ref_count++;
817   return CEED_ERROR_SUCCESS;
818 }
819 
820 /**
821   @brief Computes the current memory usage of the work vectors in a `Ceed` context and prints to debug.abort
822 
823   @param[in] ceed `Ceed` context
824 
825   @return An error code: 0 - success, otherwise - failure
826 
827   @ref Developer
828 **/
829 int CeedGetWorkVectorMemoryUsage(Ceed ceed, CeedScalar *usage_mb) {
830   if (!ceed->VectorCreate) {
831     Ceed delegate;
832 
833     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
834     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
835     CeedCall(CeedGetWorkVectorMemoryUsage(delegate, usage_mb));
836     CeedCall(CeedDestroy(&delegate));
837     return CEED_ERROR_SUCCESS;
838   }
839   *usage_mb = 0.0;
840   if (ceed->work_vectors) {
841     for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
842       CeedSize vec_len;
843       CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len));
844       *usage_mb += vec_len;
845     }
846     *usage_mb *= sizeof(CeedScalar) * 1e-6;
847     CeedDebug(ceed, "Resource {%s}: Work vectors memory usage: %" CeedInt_FMT " vectors, %g MB\n", ceed->resource, ceed->work_vectors->num_vecs,
848               *usage_mb);
849   }
850   return CEED_ERROR_SUCCESS;
851 }
852 
853 /**
854   @brief Clear inactive work vectors in a `Ceed` context below a minimum length.
855 
856   @param[in,out] ceed    `Ceed` context
857   @param[in]     min_len Minimum length of work vector to keep
858 
859   @return An error code: 0 - success, otherwise - failure
860 
861   @ref Backend
862 **/
863 int CeedClearWorkVectors(Ceed ceed, CeedSize min_len) {
864   if (!ceed->VectorCreate) {
865     Ceed delegate;
866 
867     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
868     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
869     CeedCall(CeedClearWorkVectors(delegate, min_len));
870     CeedCall(CeedDestroy(&delegate));
871     return CEED_ERROR_SUCCESS;
872   }
873   if (!ceed->work_vectors) return CEED_ERROR_SUCCESS;
874   for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
875     if (ceed->work_vectors->is_in_use[i]) continue;
876     CeedSize vec_len;
877     CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len));
878     if (vec_len < min_len) {
879       ceed->ref_count += 2;  // Note: increase ref_count to prevent Ceed destructor from triggering
880       CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i]));
881       ceed->ref_count -= 1;  // Note: restore ref_count
882       ceed->work_vectors->num_vecs--;
883       if (ceed->work_vectors->num_vecs > 0) {
884         ceed->work_vectors->vecs[i]                                 = ceed->work_vectors->vecs[ceed->work_vectors->num_vecs];
885         ceed->work_vectors->is_in_use[i]                            = ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs];
886         ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs] = false;
887         i--;
888       }
889     }
890   }
891   return CEED_ERROR_SUCCESS;
892 }
893 
894 /**
895   @brief Get a `CeedVector` for scratch work from a `Ceed` context.
896 
897   Note: This vector must be restored with @ref CeedRestoreWorkVector().
898 
899   @param[in]  ceed `Ceed` context
900   @param[in]  len  Minimum length of work vector
901   @param[out] vec  Address of the variable where `CeedVector` will be stored
902 
903   @return An error code: 0 - success, otherwise - failure
904 
905   @ref Backend
906 **/
907 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) {
908   CeedInt    i = 0;
909   CeedScalar usage_mb;
910 
911   if (!ceed->VectorCreate) {
912     Ceed delegate;
913 
914     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
915     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
916     CeedCall(CeedGetWorkVector(delegate, len, vec));
917     CeedCall(CeedDestroy(&delegate));
918     return CEED_ERROR_SUCCESS;
919   }
920 
921   if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed));
922 
923   // Search for big enough work vector
924   for (i = 0; i < ceed->work_vectors->num_vecs; i++) {
925     if (!ceed->work_vectors->is_in_use[i]) {
926       CeedSize work_len;
927 
928       CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len));
929       if (work_len >= len) break;
930     }
931   }
932   // Long enough vector was not found
933   if (i == ceed->work_vectors->num_vecs) {
934     if (ceed->work_vectors->max_vecs == 0) {
935       ceed->work_vectors->max_vecs = 1;
936       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
937       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
938     } else if (ceed->work_vectors->max_vecs == i) {
939       ceed->work_vectors->max_vecs *= 2;
940       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
941       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
942     }
943     ceed->work_vectors->num_vecs++;
944     CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i]));
945     ceed->ref_count--;  // Note: ref_count manipulation to prevent a ref-loop
946     if (ceed->is_debug) CeedGetWorkVectorMemoryUsage(ceed, &usage_mb);
947   }
948   // Return pointer to work vector
949   ceed->work_vectors->is_in_use[i] = true;
950   *vec                             = NULL;
951   CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec));
952   ceed->ref_count++;  // Note: bump ref_count to account for external access
953   return CEED_ERROR_SUCCESS;
954 }
955 
956 /**
957   @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector()
958 
959   @param[in]  ceed `Ceed` context
960   @param[out] vec  `CeedVector` to restore
961 
962   @return An error code: 0 - success, otherwise - failure
963 
964   @ref Backend
965 **/
966 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) {
967   if (!ceed->VectorCreate) {
968     Ceed delegate;
969 
970     CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
971     CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
972     CeedCall(CeedRestoreWorkVector(delegate, vec));
973     CeedCall(CeedDestroy(&delegate));
974     return CEED_ERROR_SUCCESS;
975   }
976 
977   for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
978     if (*vec == ceed->work_vectors->vecs[i]) {
979       CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned");
980       CeedCall(CeedVectorDestroy(vec));
981       ceed->work_vectors->is_in_use[i] = false;
982       ceed->ref_count--;  // Note: reduce ref_count again to prevent a ref-loop
983       return CEED_ERROR_SUCCESS;
984     }
985   }
986   // LCOV_EXCL_START
987   return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()");
988   // LCOV_EXCL_STOP
989 }
990 
991 /**
992   @brief Retrieve list of additional JiT source roots from `Ceed` context.
993 
994   Note: The caller is responsible for restoring `jit_source_roots` with @ref CeedRestoreJitSourceRoots().
995 
996   @param[in]  ceed             `Ceed` context
997   @param[out] num_source_roots Number of JiT source directories
998   @param[out] jit_source_roots Absolute paths to additional JiT source directories
999 
1000   @return An error code: 0 - success, otherwise - failure
1001 
1002   @ref Backend
1003 **/
1004 int CeedGetJitSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***jit_source_roots) {
1005   Ceed ceed_parent;
1006 
1007   CeedCall(CeedGetParent(ceed, &ceed_parent));
1008   *num_source_roots = ceed_parent->num_jit_source_roots;
1009   *jit_source_roots = (const char **)ceed_parent->jit_source_roots;
1010   ceed_parent->num_jit_source_roots_readers++;
1011   CeedCall(CeedDestroy(&ceed_parent));
1012   return CEED_ERROR_SUCCESS;
1013 }
1014 
1015 /**
1016   @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots()
1017 
1018   @param[in]  ceed             `Ceed` context
1019   @param[out] jit_source_roots Absolute paths to additional JiT source directories
1020 
1021   @return An error code: 0 - success, otherwise - failure
1022 
1023   @ref Backend
1024 **/
1025 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) {
1026   Ceed ceed_parent;
1027 
1028   CeedCall(CeedGetParent(ceed, &ceed_parent));
1029   *jit_source_roots = NULL;
1030   ceed_parent->num_jit_source_roots_readers--;
1031   CeedCall(CeedDestroy(&ceed_parent));
1032   return CEED_ERROR_SUCCESS;
1033 }
1034 
1035 /**
1036   @brief Retrieve list of additional JiT defines from `Ceed` context.
1037 
1038   Note: The caller is responsible for restoring `jit_defines` with @ref CeedRestoreJitDefines().
1039 
1040   @param[in]  ceed            `Ceed` context
1041   @param[out] num_jit_defines Number of JiT defines
1042   @param[out] jit_defines     Strings such as `foo=bar`, used as `-Dfoo=bar` in JiT
1043 
1044   @return An error code: 0 - success, otherwise - failure
1045 
1046   @ref Backend
1047 **/
1048 int CeedGetJitDefines(Ceed ceed, CeedInt *num_jit_defines, const char ***jit_defines) {
1049   Ceed ceed_parent;
1050 
1051   CeedCall(CeedGetParent(ceed, &ceed_parent));
1052   *num_jit_defines = ceed_parent->num_jit_defines;
1053   *jit_defines     = (const char **)ceed_parent->jit_defines;
1054   ceed_parent->num_jit_defines_readers++;
1055   CeedCall(CeedDestroy(&ceed_parent));
1056   return CEED_ERROR_SUCCESS;
1057 }
1058 
1059 /**
1060   @brief Restore list of additional JiT defines from with @ref CeedGetJitDefines()
1061 
1062   @param[in]  ceed        `Ceed` context
1063   @param[out] jit_defines String such as `foo=bar`, used as `-Dfoo=bar` in JiT
1064 
1065   @return An error code: 0 - success, otherwise - failure
1066 
1067   @ref Backend
1068 **/
1069 int CeedRestoreJitDefines(Ceed ceed, const char ***jit_defines) {
1070   Ceed ceed_parent;
1071 
1072   CeedCall(CeedGetParent(ceed, &ceed_parent));
1073   *jit_defines = NULL;
1074   ceed_parent->num_jit_defines_readers--;
1075   CeedCall(CeedDestroy(&ceed_parent));
1076   return CEED_ERROR_SUCCESS;
1077 }
1078 
1079 /// @}
1080 
1081 /// ----------------------------------------------------------------------------
1082 /// Ceed Public API
1083 /// ----------------------------------------------------------------------------
1084 /// @addtogroup CeedUser
1085 /// @{
1086 
1087 /**
1088   @brief Get the list of available resource names for `Ceed` contexts
1089 
1090   Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
1091 
1092   @param[out] n          Number of available resources
1093   @param[out] resources  List of available resource names
1094   @param[out] priorities Resource name prioritization values, lower is better
1095 
1096   @return An error code: 0 - success, otherwise - failure
1097 
1098   @ref User
1099 **/
1100 // LCOV_EXCL_START
1101 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
1102   *n         = 0;
1103   *resources = malloc(num_backends * sizeof(**resources));
1104   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
1105   if (priorities) {
1106     *priorities = malloc(num_backends * sizeof(**priorities));
1107     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
1108   }
1109   for (size_t i = 0; i < num_backends; i++) {
1110     // Only report compiled backends
1111     if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
1112       *resources[i] = backends[i].prefix;
1113       if (priorities) *priorities[i] = backends[i].priority;
1114       *n += 1;
1115     }
1116   }
1117   CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
1118   *resources = realloc(*resources, *n * sizeof(**resources));
1119   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1120   if (priorities) {
1121     *priorities = realloc(*priorities, *n * sizeof(**priorities));
1122     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1123   }
1124   return CEED_ERROR_SUCCESS;
1125 }
1126 // LCOV_EXCL_STOP
1127 
1128 /**
1129   @brief Initialize a `Ceed` context to use the specified resource.
1130 
1131   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`.
1132 
1133   @param[in]  resource Resource to use, e.g., "/cpu/self"
1134   @param[out] ceed     The library context
1135 
1136   @return An error code: 0 - success, otherwise - failure
1137 
1138   @ref User
1139 
1140   @sa CeedRegister() CeedDestroy()
1141 **/
1142 int CeedInit(const char *resource, Ceed *ceed) {
1143   size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
1144 
1145   // Find matching backend
1146   CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
1147   CeedCall(CeedRegisterAll());
1148 
1149   // Check for help request
1150   const char *help_prefix = "help";
1151   size_t      match_help  = 0;
1152   while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
1153   if (match_help == 4) {
1154     fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
1155             CEED_VERSION_RELEASE ? "" : "+development");
1156     fprintf(stderr, "Available backend resources:\n");
1157     for (size_t i = 0; i < num_backends; i++) {
1158       // Only report compiled backends
1159       if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, "  %s\n", backends[i].prefix);
1160     }
1161     fflush(stderr);
1162     match_help = 5;  // Delineating character expected
1163   } else {
1164     match_help = 0;
1165   }
1166 
1167   // Find best match, computed as number of matching characters from requested resource stem
1168   size_t stem_length = 0;
1169   while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
1170   for (size_t i = 0; i < num_backends; i++) {
1171     size_t      n      = 0;
1172     const char *prefix = backends[i].prefix;
1173     while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
1174     priority = backends[i].priority;
1175     if (n > match_len || (n == match_len && match_priority > priority)) {
1176       match_len      = n;
1177       match_priority = priority;
1178       match_index    = i;
1179     }
1180   }
1181   // Using Levenshtein distance to find closest match
1182   if (match_len <= 1 || match_len != stem_length) {
1183     // LCOV_EXCL_START
1184     size_t lev_dis   = UINT_MAX;
1185     size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
1186     for (size_t i = 0; i < num_backends; i++) {
1187       const char *prefix        = backends[i].prefix;
1188       size_t      prefix_length = strlen(backends[i].prefix);
1189       size_t      min_len       = (prefix_length < stem_length) ? prefix_length : stem_length;
1190       size_t      column[min_len + 1];
1191       for (size_t j = 0; j <= min_len; j++) column[j] = j;
1192       for (size_t j = 1; j <= min_len; j++) {
1193         column[0] = j;
1194         for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
1195           size_t old_diag = column[k];
1196           size_t min_1    = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
1197           size_t min_2    = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
1198           column[k]       = (min_1 < min_2) ? min_1 : min_2;
1199           last_diag       = old_diag;
1200         }
1201       }
1202       size_t n = column[min_len];
1203       priority = backends[i].priority;
1204       if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
1205         lev_dis      = n;
1206         lev_priority = priority;
1207         lev_index    = i;
1208       }
1209     }
1210     const char *prefix_lev = backends[lev_index].prefix;
1211     size_t      lev_length = 0;
1212     while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
1213     size_t m = (lev_length < stem_length) ? lev_length : stem_length;
1214     if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
1215     else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
1216     // LCOV_EXCL_STOP
1217   }
1218 
1219   // Setup Ceed
1220   CeedCall(CeedCalloc(1, ceed));
1221   CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
1222   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1223   if (!ceed_error_handler) ceed_error_handler = "abort";
1224   if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
1225   else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
1226   else (*ceed)->Error = CeedErrorAbort;
1227   memcpy((*ceed)->err_msg, "No error message stored", 24);
1228   (*ceed)->ref_count = 1;
1229   (*ceed)->data      = NULL;
1230 
1231   // Set lookup table
1232   FOffset f_offsets[] = {
1233       CEED_FTABLE_ENTRY(Ceed, Error),
1234       CEED_FTABLE_ENTRY(Ceed, SetStream),
1235       CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
1236       CEED_FTABLE_ENTRY(Ceed, Destroy),
1237       CEED_FTABLE_ENTRY(Ceed, VectorCreate),
1238       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
1239       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
1240       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
1241       CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
1242       CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
1243       CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
1244       CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
1245       CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
1246       CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
1247       CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
1248       CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
1249       CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
1250       CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
1251       CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
1252       CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
1253       CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
1254       CEED_FTABLE_ENTRY(CeedVector, SetArray),
1255       CEED_FTABLE_ENTRY(CeedVector, TakeArray),
1256       CEED_FTABLE_ENTRY(CeedVector, SetValue),
1257       CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
1258       CEED_FTABLE_ENTRY(CeedVector, SyncArray),
1259       CEED_FTABLE_ENTRY(CeedVector, GetArray),
1260       CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
1261       CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
1262       CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
1263       CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
1264       CEED_FTABLE_ENTRY(CeedVector, Norm),
1265       CEED_FTABLE_ENTRY(CeedVector, Scale),
1266       CEED_FTABLE_ENTRY(CeedVector, AXPY),
1267       CEED_FTABLE_ENTRY(CeedVector, AXPBY),
1268       CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
1269       CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
1270       CEED_FTABLE_ENTRY(CeedVector, Destroy),
1271       CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
1272       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
1273       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
1274       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
1275       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
1276       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
1277       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
1278       CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
1279       CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
1280       CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
1281       CEED_FTABLE_ENTRY(CeedBasis, Apply),
1282       CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
1283       CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
1284       CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
1285       CEED_FTABLE_ENTRY(CeedBasis, Destroy),
1286       CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
1287       CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
1288       CEED_FTABLE_ENTRY(CeedQFunction, Apply),
1289       CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
1290       CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
1291       CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
1292       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
1293       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
1294       CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
1295       CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
1296       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
1297       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
1298       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
1299       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
1300       CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
1301       CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
1302       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
1303       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
1304       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
1305       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
1306       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
1307       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
1308       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
1309       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
1310       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
1311       CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
1312       CEED_FTABLE_ENTRY(CeedOperator, Apply),
1313       CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
1314       CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
1315       CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
1316       CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
1317       CEED_FTABLE_ENTRY(CeedOperator, Destroy),
1318       {NULL, 0}  // End of lookup table - used in SetBackendFunction loop
1319   };
1320 
1321   CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
1322   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
1323 
1324   // Set fallback for advanced CeedOperator functions
1325   const char fallback_resource[] = "";
1326   CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource));
1327 
1328   // Record env variables CEED_DEBUG or DBG
1329   (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1330 
1331   // Copy resource prefix, if backend setup successful
1332   CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1333 
1334   // Set default JiT source root
1335   // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1336   CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1337 
1338   // Backend specific setup
1339   CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1340   return CEED_ERROR_SUCCESS;
1341 }
1342 
1343 /**
1344   @brief Set the GPU stream for a `Ceed` context
1345 
1346   @param[in,out] ceed   `Ceed` context to set the stream
1347   @param[in]     handle Handle to GPU stream
1348 
1349   @return An error code: 0 - success, otherwise - failure
1350 
1351   @ref User
1352 **/
1353 int CeedSetStream(Ceed ceed, void *handle) {
1354   CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1355   if (ceed->SetStream) {
1356     CeedCall(ceed->SetStream(ceed, handle));
1357   } else {
1358     Ceed delegate;
1359     CeedCall(CeedGetDelegate(ceed, &delegate));
1360 
1361     if (delegate) CeedCall(CeedSetStream(delegate, handle));
1362     else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1363     CeedCall(CeedDestroy(&delegate));
1364   }
1365   return CEED_ERROR_SUCCESS;
1366 }
1367 
1368 /**
1369   @brief Copy the pointer to a `Ceed` context.
1370 
1371   Both pointers should be destroyed with @ref CeedDestroy().
1372 
1373   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.
1374         This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1375 
1376   @param[in]     ceed      `Ceed` context to copy reference to
1377   @param[in,out] ceed_copy Variable to store copied reference
1378 
1379   @return An error code: 0 - success, otherwise - failure
1380 
1381   @ref User
1382 **/
1383 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1384   CeedCall(CeedReference(ceed));
1385   CeedCall(CeedDestroy(ceed_copy));
1386   *ceed_copy = ceed;
1387   return CEED_ERROR_SUCCESS;
1388 }
1389 
1390 /**
1391   @brief Get the full resource name for a `Ceed` context
1392 
1393   @param[in]  ceed     `Ceed` context to get resource name of
1394   @param[out] resource Variable to store resource name
1395 
1396   @return An error code: 0 - success, otherwise - failure
1397 
1398   @ref User
1399 **/
1400 int CeedGetResource(Ceed ceed, const char **resource) {
1401   *resource = (const char *)ceed->resource;
1402   return CEED_ERROR_SUCCESS;
1403 }
1404 
1405 /**
1406   @brief Return `Ceed` context preferred memory type
1407 
1408   @param[in]  ceed     `Ceed` context to get preferred memory type of
1409   @param[out] mem_type Address to save preferred memory type to
1410 
1411   @return An error code: 0 - success, otherwise - failure
1412 
1413   @ref User
1414 **/
1415 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1416   if (ceed->GetPreferredMemType) {
1417     CeedCall(ceed->GetPreferredMemType(mem_type));
1418   } else {
1419     Ceed delegate;
1420     CeedCall(CeedGetDelegate(ceed, &delegate));
1421 
1422     if (delegate) {
1423       CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1424     } else {
1425       *mem_type = CEED_MEM_HOST;
1426     }
1427     CeedCall(CeedDestroy(&delegate));
1428   }
1429   return CEED_ERROR_SUCCESS;
1430 }
1431 
1432 /**
1433   @brief Get deterministic status of `Ceed` context
1434 
1435   @param[in]  ceed             `Ceed` context
1436   @param[out] is_deterministic Variable to store deterministic status
1437 
1438   @return An error code: 0 - success, otherwise - failure
1439 
1440   @ref User
1441 **/
1442 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1443   *is_deterministic = ceed->is_deterministic;
1444   return CEED_ERROR_SUCCESS;
1445 }
1446 
1447 /**
1448   @brief Set additional JiT source root for `Ceed` context
1449 
1450   @param[in,out] ceed            `Ceed` context
1451   @param[in]     jit_source_root Absolute path to additional JiT source directory
1452 
1453   @return An error code: 0 - success, otherwise - failure
1454 
1455   @ref User
1456 **/
1457 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1458   Ceed ceed_parent;
1459 
1460   CeedCall(CeedGetParent(ceed, &ceed_parent));
1461   CeedCheck(!ceed_parent->num_jit_source_roots_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access has not been restored");
1462 
1463   CeedInt index       = ceed_parent->num_jit_source_roots;
1464   size_t  path_length = strlen(jit_source_root);
1465 
1466   if (ceed_parent->num_jit_source_roots == ceed_parent->max_jit_source_roots) {
1467     if (ceed_parent->max_jit_source_roots == 0) ceed_parent->max_jit_source_roots = 1;
1468     ceed_parent->max_jit_source_roots *= 2;
1469     CeedCall(CeedRealloc(ceed_parent->max_jit_source_roots, &ceed_parent->jit_source_roots));
1470   }
1471   CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1472   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1473   ceed_parent->num_jit_source_roots++;
1474   CeedCall(CeedDestroy(&ceed_parent));
1475   return CEED_ERROR_SUCCESS;
1476 }
1477 
1478 /**
1479   @brief Set additional JiT compiler define for `Ceed` context
1480 
1481   @param[in,out] ceed       `Ceed` context
1482   @param[in]     jit_define String such as `foo=bar`, used as `-Dfoo=bar` in JiT
1483 
1484   @return An error code: 0 - success, otherwise - failure
1485 
1486   @ref User
1487 **/
1488 int CeedAddJitDefine(Ceed ceed, const char *jit_define) {
1489   Ceed ceed_parent;
1490 
1491   CeedCall(CeedGetParent(ceed, &ceed_parent));
1492   CeedCheck(!ceed_parent->num_jit_defines_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT define, read access has not been restored");
1493 
1494   CeedInt index         = ceed_parent->num_jit_defines;
1495   size_t  define_length = strlen(jit_define);
1496 
1497   if (ceed_parent->num_jit_defines == ceed_parent->max_jit_defines) {
1498     if (ceed_parent->max_jit_defines == 0) ceed_parent->max_jit_defines = 1;
1499     ceed_parent->max_jit_defines *= 2;
1500     CeedCall(CeedRealloc(ceed_parent->max_jit_defines, &ceed_parent->jit_defines));
1501   }
1502   CeedCall(CeedCalloc(define_length + 1, &ceed_parent->jit_defines[index]));
1503   memcpy(ceed_parent->jit_defines[index], jit_define, define_length);
1504   ceed_parent->num_jit_defines++;
1505   CeedCall(CeedDestroy(&ceed_parent));
1506   return CEED_ERROR_SUCCESS;
1507 }
1508 
1509 /**
1510   @brief View a `Ceed`
1511 
1512   @param[in] ceed   `Ceed` to view
1513   @param[in] stream Filestream to write to
1514 
1515   @return An error code: 0 - success, otherwise - failure
1516 
1517   @ref User
1518 **/
1519 int CeedView(Ceed ceed, FILE *stream) {
1520   CeedMemType mem_type;
1521 
1522   CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1523 
1524   fprintf(stream,
1525           "Ceed\n"
1526           "  Ceed Resource: %s\n"
1527           "  Preferred MemType: %s\n",
1528           ceed->resource, CeedMemTypes[mem_type]);
1529   return CEED_ERROR_SUCCESS;
1530 }
1531 
1532 /**
1533   @brief Destroy a `Ceed`
1534 
1535   @param[in,out] ceed Address of `Ceed` context to destroy
1536 
1537   @return An error code: 0 - success, otherwise - failure
1538 
1539   @ref User
1540 **/
1541 int CeedDestroy(Ceed *ceed) {
1542   if (!*ceed || --(*ceed)->ref_count > 0) {
1543     *ceed = NULL;
1544     return CEED_ERROR_SUCCESS;
1545   }
1546 
1547   CeedCheck(!(*ceed)->num_jit_source_roots_readers, *ceed, CEED_ERROR_ACCESS,
1548             "Cannot destroy ceed context, read access for JiT source roots has been granted");
1549   CeedCheck(!(*ceed)->num_jit_defines_readers, *ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access for JiT defines has been granted");
1550 
1551   if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1552 
1553   if ((*ceed)->obj_delegate_count > 0) {
1554     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1555       CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1556       CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1557     }
1558     CeedCall(CeedFree(&(*ceed)->obj_delegates));
1559   }
1560 
1561   if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1562 
1563   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1564     CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1565   }
1566   CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1567 
1568   for (CeedInt i = 0; i < (*ceed)->num_jit_defines; i++) {
1569     CeedCall(CeedFree(&(*ceed)->jit_defines[i]));
1570   }
1571   CeedCall(CeedFree(&(*ceed)->jit_defines));
1572 
1573   CeedCall(CeedFree(&(*ceed)->f_offsets));
1574   CeedCall(CeedFree(&(*ceed)->resource));
1575   CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1576   CeedCall(CeedFree(&(*ceed)->op_fallback_resource));
1577   CeedCall(CeedWorkVectorsDestroy(*ceed));
1578   CeedCall(CeedFree(ceed));
1579   return CEED_ERROR_SUCCESS;
1580 }
1581 
1582 // LCOV_EXCL_START
1583 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1584   if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1585   if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1586   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1587   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args);  // NOLINT
1588   return ceed->err_msg;
1589 }
1590 // LCOV_EXCL_STOP
1591 
1592 /**
1593   @brief Error handling implementation; use @ref CeedError() instead.
1594 
1595   @return An error code: 0 - success, otherwise - failure
1596 
1597   @ref Developer
1598 **/
1599 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1600   va_list args;
1601   int     ret_val;
1602 
1603   va_start(args, format);
1604   if (ceed) {
1605     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1606   } else {
1607     // LCOV_EXCL_START
1608     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1609     if (!ceed_error_handler) ceed_error_handler = "abort";
1610     if (!strcmp(ceed_error_handler, "return")) {
1611       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1612     } else {
1613       // This function will not return
1614       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1615     }
1616   }
1617   va_end(args);
1618   return ret_val;
1619   // LCOV_EXCL_STOP
1620 }
1621 
1622 /**
1623   @brief Error handler that returns without printing anything.
1624 
1625   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1626 
1627   @return An error code: 0 - success, otherwise - failure
1628 
1629   @ref Developer
1630 **/
1631 // LCOV_EXCL_START
1632 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1633   return err_code;
1634 }
1635 // LCOV_EXCL_STOP
1636 
1637 /**
1638   @brief Error handler that stores the error message for future use and returns the error.
1639 
1640   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1641 
1642   @return An error code: 0 - success, otherwise - failure
1643 
1644   @ref Developer
1645 **/
1646 // LCOV_EXCL_START
1647 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1648   if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1649   if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args);
1650 
1651   // Build message
1652   int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1653   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1654   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);  // NOLINT
1655   return err_code;
1656 }
1657 // LCOV_EXCL_STOP
1658 
1659 /**
1660   @brief Error handler that prints to `stderr` and aborts
1661 
1662   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1663 
1664   @return An error code: 0 - success, otherwise - failure
1665 
1666   @ref Developer
1667 **/
1668 // LCOV_EXCL_START
1669 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1670   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1671   vfprintf(stderr, format, *args);
1672   fprintf(stderr, "\n");
1673   abort();
1674   return err_code;
1675 }
1676 // LCOV_EXCL_STOP
1677 
1678 /**
1679   @brief Error handler that prints to `stderr` and exits.
1680 
1681   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1682 
1683   In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1684 
1685   @return An error code: 0 - success, otherwise - failure
1686 
1687   @ref Developer
1688 **/
1689 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1690   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1691   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1692   vfprintf(stderr, format, *args);  // NOLINT
1693   fprintf(stderr, "\n");
1694   exit(err_code);
1695   return err_code;
1696 }
1697 
1698 /**
1699   @brief Set error handler
1700 
1701   A default error handler is set in @ref CeedInit().
1702   Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1703 
1704   @return An error code: 0 - success, otherwise - failure
1705 
1706   @ref Developer
1707 **/
1708 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1709   ceed->Error = handler;
1710   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1711   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1712   return CEED_ERROR_SUCCESS;
1713 }
1714 
1715 /**
1716   @brief Get error message
1717 
1718   The error message is only stored when using the error handler @ref CeedErrorStore()
1719 
1720   @param[in]  ceed    `Ceed` context to retrieve error message
1721   @param[out] err_msg Char pointer to hold error message
1722 
1723   @return An error code: 0 - success, otherwise - failure
1724 
1725   @ref Developer
1726 **/
1727 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1728   if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1729   if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1730   *err_msg = ceed->err_msg;
1731   return CEED_ERROR_SUCCESS;
1732 }
1733 
1734 /**
1735   @brief Restore error message.
1736 
1737   The error message is only stored when using the error handler @ref CeedErrorStore().
1738 
1739   @param[in]  ceed    `Ceed` context to restore error message
1740   @param[out] err_msg Char pointer that holds error message
1741 
1742   @return An error code: 0 - success, otherwise - failure
1743 
1744   @ref Developer
1745 **/
1746 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1747   if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1748   if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1749   *err_msg = NULL;
1750   memcpy(ceed->err_msg, "No error message stored", 24);
1751   return CEED_ERROR_SUCCESS;
1752 }
1753 
1754 /**
1755   @brief Get libCEED library version information.
1756 
1757   libCEED version numbers have the form major.minor.patch.
1758   Non-release versions may contain unstable interfaces.
1759 
1760   @param[out] major   Major version of the library
1761   @param[out] minor   Minor version of the library
1762   @param[out] patch   Patch (subminor) version of the library
1763   @param[out] release True for releases; false for development branches
1764 
1765   The caller may pass `NULL` for any arguments that are not needed.
1766 
1767   @return An error code: 0 - success, otherwise - failure
1768 
1769   @ref Developer
1770 
1771   @sa CEED_VERSION_GE() CeedGetGitVersion() CeedGetBuildConfiguration()
1772 */
1773 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1774   if (major) *major = CEED_VERSION_MAJOR;
1775   if (minor) *minor = CEED_VERSION_MINOR;
1776   if (patch) *patch = CEED_VERSION_PATCH;
1777   if (release) *release = CEED_VERSION_RELEASE;
1778   return CEED_ERROR_SUCCESS;
1779 }
1780 
1781 /**
1782   @brief Get libCEED scalar type, such as F64 or F32
1783 
1784   @param[out] scalar_type Type of libCEED scalars
1785 
1786   @return An error code: 0 - success, otherwise - failure
1787 
1788   @ref Developer
1789 */
1790 int CeedGetScalarType(CeedScalarType *scalar_type) {
1791   *scalar_type = CEED_SCALAR_TYPE;
1792   return CEED_ERROR_SUCCESS;
1793 }
1794 
1795 /// @}
1796