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