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