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