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