xref: /libCEED/interface/ceed.c (revision db2becc9f302fe8eb3a32ace50ce3f3a5d42e6c4)
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   @param[in]  ceed          `Ceed` context to get resource name of
449   @param[in]  resource      Full user specified resource
450   @param[in]  delineator    Delineator to break `resource_root` and `resource_spec`
451   @param[out] resource_root Variable to store resource root
452 
453   @return An error code: 0 - success, otherwise - failure
454 
455   @ref Backend
456 **/
457 int CeedGetResourceRoot(Ceed ceed, const char *resource, const char *delineator, char **resource_root) {
458   char  *device_spec       = strstr(resource, delineator);
459   size_t resource_root_len = device_spec ? (size_t)(device_spec - resource) + 1 : strlen(resource) + 1;
460 
461   CeedCall(CeedCalloc(resource_root_len, resource_root));
462   memcpy(*resource_root, resource, resource_root_len - 1);
463   return CEED_ERROR_SUCCESS;
464 }
465 
466 /**
467   @brief Retrieve a parent `Ceed` context
468 
469   @param[in]  ceed   `Ceed` context to retrieve parent of
470   @param[out] parent Address to save the parent to
471 
472   @return An error code: 0 - success, otherwise - failure
473 
474   @ref Backend
475 **/
476 int CeedGetParent(Ceed ceed, Ceed *parent) {
477   if (ceed->parent) {
478     CeedCall(CeedGetParent(ceed->parent, parent));
479     return CEED_ERROR_SUCCESS;
480   }
481   *parent = ceed;
482   return CEED_ERROR_SUCCESS;
483 }
484 
485 /**
486   @brief Retrieve a delegate `Ceed` context
487 
488   @param[in]  ceed     `Ceed` context to retrieve delegate of
489   @param[out] delegate Address to save the delegate to
490 
491   @return An error code: 0 - success, otherwise - failure
492 
493   @ref Backend
494 **/
495 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
496   *delegate = ceed->delegate;
497   return CEED_ERROR_SUCCESS;
498 }
499 
500 /**
501   @brief Set a delegate `Ceed` context
502 
503   This function allows a `Ceed` context to set a delegate `Ceed` context.
504   All backend implementations default to the delegate `Ceed` context, unless overridden.
505 
506   @param[in]  ceed     `Ceed` context to set delegate of
507   @param[out] delegate Address to set the delegate to
508 
509   @return An error code: 0 - success, otherwise - failure
510 
511   @ref Backend
512 **/
513 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
514   ceed->delegate   = delegate;
515   delegate->parent = ceed;
516   return CEED_ERROR_SUCCESS;
517 }
518 
519 /**
520   @brief Retrieve a delegate `Ceed` context for a specific object type
521 
522   @param[in]  ceed     `Ceed` context to retrieve delegate of
523   @param[out] delegate Address to save the delegate to
524   @param[in]  obj_name Name of the object type to retrieve delegate for
525 
526   @return An error code: 0 - success, otherwise - failure
527 
528   @ref Backend
529 **/
530 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) {
531   // Check for object delegate
532   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) {
533     if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) {
534       *delegate = ceed->obj_delegates->delegate;
535       return CEED_ERROR_SUCCESS;
536     }
537   }
538 
539   // Use default delegate if no object delegate
540   CeedCall(CeedGetDelegate(ceed, delegate));
541   return CEED_ERROR_SUCCESS;
542 }
543 
544 /**
545   @brief Set a delegate `Ceed` context for a specific object type
546 
547   This function allows a `Ceed` context to set a delegate `Ceed` context for a given type of `Ceed` object.
548   All backend implementations default to the delegate `Ceed` context for this object.
549   For example, `CeedSetObjectDelegate(ceed, delegate, "Basis")` uses delegate implementations for all `CeedBasis` backend functions.
550 
551   @param[in,out] ceed     `Ceed` context to set delegate of
552   @param[in]     delegate `Ceed` context to use for delegation
553   @param[in]     obj_name Name of the object type to set delegate for
554 
555   @return An error code: 0 - success, otherwise - failure
556 
557   @ref Backend
558 **/
559 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) {
560   CeedInt count = ceed->obj_delegate_count;
561 
562   // Malloc or Realloc
563   if (count) {
564     CeedCall(CeedRealloc(count + 1, &ceed->obj_delegates));
565   } else {
566     CeedCall(CeedCalloc(1, &ceed->obj_delegates));
567   }
568   ceed->obj_delegate_count++;
569 
570   // Set object delegate
571   ceed->obj_delegates[count].delegate = delegate;
572   CeedCall(CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name));
573 
574   // Set delegate parent
575   delegate->parent = ceed;
576   return CEED_ERROR_SUCCESS;
577 }
578 
579 /**
580   @brief Get the fallback resource for `CeedOperator`
581 
582   @param[in]  ceed     `Ceed` context
583   @param[out] resource Variable to store fallback resource
584 
585   @return An error code: 0 - success, otherwise - failure
586 
587   @ref Backend
588 **/
589 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
590   *resource = (const char *)ceed->op_fallback_resource;
591   return CEED_ERROR_SUCCESS;
592 }
593 
594 /**
595   @brief Get the fallback `Ceed` for `CeedOperator`
596 
597   @param[in]  ceed          `Ceed` context
598   @param[out] fallback_ceed Variable to store fallback `Ceed`
599 
600   @return An error code: 0 - success, otherwise - failure
601 
602   @ref Backend
603 **/
604 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) {
605   if (ceed->has_valid_op_fallback_resource) {
606     CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n");
607     CeedDebug(ceed, "Getting fallback from %s to %s\n", ceed->resource, ceed->op_fallback_resource);
608   }
609 
610   // Create fallback Ceed if uninitalized
611   if (!ceed->op_fallback_ceed && ceed->has_valid_op_fallback_resource) {
612     CeedDebug(ceed, "Creating fallback Ceed");
613 
614     Ceed        fallback_ceed;
615     const char *fallback_resource;
616 
617     CeedCall(CeedGetOperatorFallbackResource(ceed, &fallback_resource));
618     CeedCall(CeedInit(fallback_resource, &fallback_ceed));
619     fallback_ceed->op_fallback_parent = ceed;
620     fallback_ceed->Error              = ceed->Error;
621     ceed->op_fallback_ceed            = fallback_ceed;
622   }
623   *fallback_ceed = ceed->op_fallback_ceed;
624   return CEED_ERROR_SUCCESS;
625 }
626 
627 /**
628   @brief Set the fallback resource for `CeedOperator`.
629 
630   The current resource, if any, is freed by calling this function.
631   This string is freed upon the destruction of the `Ceed` context.
632 
633   @param[in,out] ceed     `Ceed` context
634   @param[in]     resource Fallback resource to set
635 
636   @return An error code: 0 - success, otherwise - failure
637 
638   @ref Backend
639 **/
640 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
641   // Free old
642   CeedCall(CeedFree(&ceed->op_fallback_resource));
643 
644   // Set new
645   CeedCall(CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource));
646 
647   // Check validity
648   ceed->has_valid_op_fallback_resource = ceed->op_fallback_resource && ceed->resource && strcmp(ceed->op_fallback_resource, ceed->resource);
649   return CEED_ERROR_SUCCESS;
650 }
651 
652 /**
653   @brief Flag `Ceed` context as deterministic
654 
655   @param[in]  ceed             `Ceed` to flag as deterministic
656   @param[out] is_deterministic Deterministic status to set
657 
658   @return An error code: 0 - success, otherwise - failure
659 
660   @ref Backend
661 **/
662 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
663   ceed->is_deterministic = is_deterministic;
664   return CEED_ERROR_SUCCESS;
665 }
666 
667 /**
668   @brief Set a backend function.
669 
670   This function is used for a backend to set the function associated with the Ceed objects.
671   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().
672   Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
673 
674   @param[in]  ceed      `Ceed` context for error handling
675   @param[in]  type      Type of Ceed object to set function for
676   @param[out] object    Ceed object to set function for
677   @param[in]  func_name Name of function to set
678   @param[in]  f         Function to set
679 
680   @return An error code: 0 - success, otherwise - failure
681 
682   @ref Backend
683 **/
684 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
685   char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
686 
687   // Build lookup name
688   if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
689   strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
690   strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
691 
692   // Find and use offset
693   for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
694     if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
695       size_t offset          = ceed->f_offsets[i].offset;
696       int (**fpointer)(void) = (int (**)(void))((char *)object + offset);  // *NOPAD*
697 
698       *fpointer = (int (*)(void))f;
699       return CEED_ERROR_SUCCESS;
700     }
701   }
702 
703   // LCOV_EXCL_START
704   return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
705   // LCOV_EXCL_STOP
706 }
707 
708 /**
709   @brief Retrieve backend data for a `Ceed` context
710 
711   @param[in]  ceed `Ceed` context to retrieve data of
712   @param[out] data Address to save data to
713 
714   @return An error code: 0 - success, otherwise - failure
715 
716   @ref Backend
717 **/
718 int CeedGetData(Ceed ceed, void *data) {
719   *(void **)data = ceed->data;
720   return CEED_ERROR_SUCCESS;
721 }
722 
723 /**
724   @brief Set backend data for a `Ceed` context
725 
726   @param[in,out] ceed `Ceed` context to set data of
727   @param[in]     data Address of data to set
728 
729   @return An error code: 0 - success, otherwise - failure
730 
731   @ref Backend
732 **/
733 int CeedSetData(Ceed ceed, void *data) {
734   ceed->data = data;
735   return CEED_ERROR_SUCCESS;
736 }
737 
738 /**
739   @brief Increment the reference counter for a `Ceed` context
740 
741   @param[in,out] ceed `Ceed` context to increment the reference counter
742 
743   @return An error code: 0 - success, otherwise - failure
744 
745   @ref Backend
746 **/
747 int CeedReference(Ceed ceed) {
748   ceed->ref_count++;
749   return CEED_ERROR_SUCCESS;
750 }
751 
752 /// @}
753 
754 /// ----------------------------------------------------------------------------
755 /// Ceed Public API
756 /// ----------------------------------------------------------------------------
757 /// @addtogroup CeedUser
758 /// @{
759 
760 /**
761   @brief Get the list of available resource names for `Ceed` contexts
762 
763   Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
764 
765   @param[out] n          Number of available resources
766   @param[out] resources  List of available resource names
767   @param[out] priorities Resource name prioritization values, lower is better
768 
769   @return An error code: 0 - success, otherwise - failure
770 
771   @ref User
772 **/
773 // LCOV_EXCL_START
774 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
775   *n         = 0;
776   *resources = malloc(num_backends * sizeof(**resources));
777   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
778   if (priorities) {
779     *priorities = malloc(num_backends * sizeof(**priorities));
780     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
781   }
782   for (size_t i = 0; i < num_backends; i++) {
783     // Only report compiled backends
784     if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
785       *resources[i] = backends[i].prefix;
786       if (priorities) *priorities[i] = backends[i].priority;
787       *n += 1;
788     }
789   }
790   CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
791   *resources = realloc(*resources, *n * sizeof(**resources));
792   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
793   if (priorities) {
794     *priorities = realloc(*priorities, *n * sizeof(**priorities));
795     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
796   }
797   return CEED_ERROR_SUCCESS;
798 }
799 // LCOV_EXCL_STOP
800 
801 /**
802   @brief Initialize a `Ceed` context to use the specified resource.
803 
804   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`.
805 
806   @param[in]  resource Resource to use, e.g., "/cpu/self"
807   @param[out] ceed     The library context
808 
809   @return An error code: 0 - success, otherwise - failure
810 
811   @ref User
812 
813   @sa CeedRegister() CeedDestroy()
814 **/
815 int CeedInit(const char *resource, Ceed *ceed) {
816   size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
817 
818   // Find matching backend
819   CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
820   CeedCall(CeedRegisterAll());
821 
822   // Check for help request
823   const char *help_prefix = "help";
824   size_t      match_help  = 0;
825   while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
826   if (match_help == 4) {
827     fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
828             CEED_VERSION_RELEASE ? "" : "+development");
829     fprintf(stderr, "Available backend resources:\n");
830     for (size_t i = 0; i < num_backends; i++) {
831       // Only report compiled backends
832       if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, "  %s\n", backends[i].prefix);
833     }
834     fflush(stderr);
835     match_help = 5;  // Delineating character expected
836   } else {
837     match_help = 0;
838   }
839 
840   // Find best match, computed as number of matching characters from requested resource stem
841   size_t stem_length = 0;
842   while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
843   for (size_t i = 0; i < num_backends; i++) {
844     size_t      n      = 0;
845     const char *prefix = backends[i].prefix;
846     while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
847     priority = backends[i].priority;
848     if (n > match_len || (n == match_len && match_priority > priority)) {
849       match_len      = n;
850       match_priority = priority;
851       match_index    = i;
852     }
853   }
854   // Using Levenshtein distance to find closest match
855   if (match_len <= 1 || match_len != stem_length) {
856     // LCOV_EXCL_START
857     size_t lev_dis   = UINT_MAX;
858     size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
859     for (size_t i = 0; i < num_backends; i++) {
860       const char *prefix        = backends[i].prefix;
861       size_t      prefix_length = strlen(backends[i].prefix);
862       size_t      min_len       = (prefix_length < stem_length) ? prefix_length : stem_length;
863       size_t      column[min_len + 1];
864       for (size_t j = 0; j <= min_len; j++) column[j] = j;
865       for (size_t j = 1; j <= min_len; j++) {
866         column[0] = j;
867         for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
868           size_t old_diag = column[k];
869           size_t min_1    = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
870           size_t min_2    = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
871           column[k]       = (min_1 < min_2) ? min_1 : min_2;
872           last_diag       = old_diag;
873         }
874       }
875       size_t n = column[min_len];
876       priority = backends[i].priority;
877       if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
878         lev_dis      = n;
879         lev_priority = priority;
880         lev_index    = i;
881       }
882     }
883     const char *prefix_lev = backends[lev_index].prefix;
884     size_t      lev_length = 0;
885     while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
886     size_t m = (lev_length < stem_length) ? lev_length : stem_length;
887     if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
888     else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
889     // LCOV_EXCL_STOP
890   }
891 
892   // Setup Ceed
893   CeedCall(CeedCalloc(1, ceed));
894   CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
895   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
896   if (!ceed_error_handler) ceed_error_handler = "abort";
897   if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
898   else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
899   else (*ceed)->Error = CeedErrorAbort;
900   memcpy((*ceed)->err_msg, "No error message stored", 24);
901   (*ceed)->ref_count = 1;
902   (*ceed)->data      = NULL;
903 
904   // Set lookup table
905   FOffset f_offsets[] = {
906       CEED_FTABLE_ENTRY(Ceed, Error),
907       CEED_FTABLE_ENTRY(Ceed, SetStream),
908       CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
909       CEED_FTABLE_ENTRY(Ceed, Destroy),
910       CEED_FTABLE_ENTRY(Ceed, VectorCreate),
911       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
912       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
913       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
914       CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
915       CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
916       CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
917       CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
918       CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
919       CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
920       CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
921       CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
922       CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
923       CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
924       CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
925       CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
926       CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
927       CEED_FTABLE_ENTRY(CeedVector, SetArray),
928       CEED_FTABLE_ENTRY(CeedVector, TakeArray),
929       CEED_FTABLE_ENTRY(CeedVector, SetValue),
930       CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
931       CEED_FTABLE_ENTRY(CeedVector, SyncArray),
932       CEED_FTABLE_ENTRY(CeedVector, GetArray),
933       CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
934       CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
935       CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
936       CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
937       CEED_FTABLE_ENTRY(CeedVector, Norm),
938       CEED_FTABLE_ENTRY(CeedVector, Scale),
939       CEED_FTABLE_ENTRY(CeedVector, AXPY),
940       CEED_FTABLE_ENTRY(CeedVector, AXPBY),
941       CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
942       CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
943       CEED_FTABLE_ENTRY(CeedVector, Destroy),
944       CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
945       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
946       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
947       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
948       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
949       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
950       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
951       CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
952       CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
953       CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
954       CEED_FTABLE_ENTRY(CeedBasis, Apply),
955       CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
956       CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
957       CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
958       CEED_FTABLE_ENTRY(CeedBasis, Destroy),
959       CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
960       CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
961       CEED_FTABLE_ENTRY(CeedQFunction, Apply),
962       CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
963       CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
964       CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
965       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
966       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
967       CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
968       CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
969       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
970       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
971       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
972       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
973       CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
974       CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
975       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
976       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
977       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
978       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
979       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
980       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
981       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
982       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
983       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
984       CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
985       CEED_FTABLE_ENTRY(CeedOperator, Apply),
986       CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
987       CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
988       CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
989       CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
990       CEED_FTABLE_ENTRY(CeedOperator, Destroy),
991       {NULL, 0}  // End of lookup table - used in SetBackendFunction loop
992   };
993 
994   CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
995   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
996 
997   // Set fallback for advanced CeedOperator functions
998   const char fallback_resource[] = "";
999   CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource));
1000 
1001   // Record env variables CEED_DEBUG or DBG
1002   (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1003 
1004   // Copy resource prefix, if backend setup successful
1005   CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1006 
1007   // Set default JiT source root
1008   // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1009   CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1010 
1011   // Backend specific setup
1012   CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1013   return CEED_ERROR_SUCCESS;
1014 }
1015 
1016 /**
1017   @brief Set the GPU stream for a `Ceed` context
1018 
1019   @param[in,out] ceed   `Ceed` context to set the stream
1020   @param[in]     handle Handle to GPU stream
1021 
1022   @return An error code: 0 - success, otherwise - failure
1023 
1024   @ref User
1025 **/
1026 int CeedSetStream(Ceed ceed, void *handle) {
1027   CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1028   if (ceed->SetStream) {
1029     CeedCall(ceed->SetStream(ceed, handle));
1030   } else {
1031     Ceed delegate;
1032     CeedCall(CeedGetDelegate(ceed, &delegate));
1033 
1034     if (delegate) CeedCall(CeedSetStream(delegate, handle));
1035     else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1036   }
1037   return CEED_ERROR_SUCCESS;
1038 }
1039 
1040 /**
1041   @brief Copy the pointer to a `Ceed` context.
1042 
1043   Both pointers should be destroyed with @ref CeedDestroy().
1044 
1045   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.
1046         This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1047 
1048   @param[in]     ceed      `Ceed` context to copy reference to
1049   @param[in,out] ceed_copy Variable to store copied reference
1050 
1051   @return An error code: 0 - success, otherwise - failure
1052 
1053   @ref User
1054 **/
1055 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1056   CeedCall(CeedReference(ceed));
1057   CeedCall(CeedDestroy(ceed_copy));
1058   *ceed_copy = ceed;
1059   return CEED_ERROR_SUCCESS;
1060 }
1061 
1062 /**
1063   @brief Get the full resource name for a `Ceed` context
1064 
1065   @param[in]  ceed     `Ceed` context to get resource name of
1066   @param[out] resource Variable to store resource name
1067 
1068   @return An error code: 0 - success, otherwise - failure
1069 
1070   @ref User
1071 **/
1072 int CeedGetResource(Ceed ceed, const char **resource) {
1073   *resource = (const char *)ceed->resource;
1074   return CEED_ERROR_SUCCESS;
1075 }
1076 
1077 /**
1078   @brief Return `Ceed` context preferred memory type
1079 
1080   @param[in]  ceed     `Ceed` context to get preferred memory type of
1081   @param[out] mem_type Address to save preferred memory type to
1082 
1083   @return An error code: 0 - success, otherwise - failure
1084 
1085   @ref User
1086 **/
1087 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1088   if (ceed->GetPreferredMemType) {
1089     CeedCall(ceed->GetPreferredMemType(mem_type));
1090   } else {
1091     Ceed delegate;
1092     CeedCall(CeedGetDelegate(ceed, &delegate));
1093 
1094     if (delegate) {
1095       CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1096     } else {
1097       *mem_type = CEED_MEM_HOST;
1098     }
1099   }
1100   return CEED_ERROR_SUCCESS;
1101 }
1102 
1103 /**
1104   @brief Get deterministic status of `Ceed` context
1105 
1106   @param[in]  ceed             `Ceed` context
1107   @param[out] is_deterministic Variable to store deterministic status
1108 
1109   @return An error code: 0 - success, otherwise - failure
1110 
1111   @ref User
1112 **/
1113 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1114   *is_deterministic = ceed->is_deterministic;
1115   return CEED_ERROR_SUCCESS;
1116 }
1117 
1118 /**
1119   @brief Set additional JiT source root for `Ceed` context
1120 
1121   @param[in,out] ceed            `Ceed` context
1122   @param[in]     jit_source_root Absolute path to additional JiT source directory
1123 
1124   @return An error code: 0 - success, otherwise - failure
1125 
1126   @ref User
1127 **/
1128 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1129   Ceed ceed_parent;
1130 
1131   CeedCall(CeedGetParent(ceed, &ceed_parent));
1132 
1133   CeedInt index       = ceed_parent->num_jit_source_roots;
1134   size_t  path_length = strlen(jit_source_root);
1135 
1136   CeedCall(CeedRealloc(index + 1, &ceed_parent->jit_source_roots));
1137   CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1138   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1139   ceed_parent->num_jit_source_roots++;
1140   return CEED_ERROR_SUCCESS;
1141 }
1142 
1143 /**
1144   @brief View a `Ceed`
1145 
1146   @param[in] ceed   `Ceed` to view
1147   @param[in] stream Filestream to write to
1148 
1149   @return An error code: 0 - success, otherwise - failure
1150 
1151   @ref User
1152 **/
1153 int CeedView(Ceed ceed, FILE *stream) {
1154   CeedMemType mem_type;
1155 
1156   CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1157 
1158   fprintf(stream,
1159           "Ceed\n"
1160           "  Ceed Resource: %s\n"
1161           "  Preferred MemType: %s\n",
1162           ceed->resource, CeedMemTypes[mem_type]);
1163   return CEED_ERROR_SUCCESS;
1164 }
1165 
1166 /**
1167   @brief Destroy a `Ceed`
1168 
1169   @param[in,out] ceed Address of `Ceed` context to destroy
1170 
1171   @return An error code: 0 - success, otherwise - failure
1172 
1173   @ref User
1174 **/
1175 int CeedDestroy(Ceed *ceed) {
1176   if (!*ceed || --(*ceed)->ref_count > 0) {
1177     *ceed = NULL;
1178     return CEED_ERROR_SUCCESS;
1179   }
1180   if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1181 
1182   if ((*ceed)->obj_delegate_count > 0) {
1183     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1184       CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1185       CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1186     }
1187     CeedCall(CeedFree(&(*ceed)->obj_delegates));
1188   }
1189 
1190   if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1191 
1192   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1193     CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1194   }
1195   CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1196 
1197   CeedCall(CeedFree(&(*ceed)->f_offsets));
1198   CeedCall(CeedFree(&(*ceed)->resource));
1199   CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1200   CeedCall(CeedFree(&(*ceed)->op_fallback_resource));
1201   CeedCall(CeedFree(ceed));
1202   return CEED_ERROR_SUCCESS;
1203 }
1204 
1205 // LCOV_EXCL_START
1206 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1207   if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1208   if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1209   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1210   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args);  // NOLINT
1211   return ceed->err_msg;
1212 }
1213 // LCOV_EXCL_STOP
1214 
1215 /**
1216   @brief Error handling implementation; use @ref CeedError() instead.
1217 
1218   @return An error code: 0 - success, otherwise - failure
1219 
1220   @ref Developer
1221 **/
1222 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1223   va_list args;
1224   int     ret_val;
1225 
1226   va_start(args, format);
1227   if (ceed) {
1228     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1229   } else {
1230     // LCOV_EXCL_START
1231     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1232     if (!ceed_error_handler) ceed_error_handler = "abort";
1233     if (!strcmp(ceed_error_handler, "return")) {
1234       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1235     } else {
1236       // This function will not return
1237       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1238     }
1239   }
1240   va_end(args);
1241   return ret_val;
1242   // LCOV_EXCL_STOP
1243 }
1244 
1245 /**
1246   @brief Error handler that returns without printing anything.
1247 
1248   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1249 
1250   @return An error code: 0 - success, otherwise - failure
1251 
1252   @ref Developer
1253 **/
1254 // LCOV_EXCL_START
1255 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1256   return err_code;
1257 }
1258 // LCOV_EXCL_STOP
1259 
1260 /**
1261   @brief Error handler that stores the error message for future use and returns the error.
1262 
1263   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1264 
1265   @return An error code: 0 - success, otherwise - failure
1266 
1267   @ref Developer
1268 **/
1269 // LCOV_EXCL_START
1270 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1271   if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1272   if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args);
1273 
1274   // Build message
1275   int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1276   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1277   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);  // NOLINT
1278   return err_code;
1279 }
1280 // LCOV_EXCL_STOP
1281 
1282 /**
1283   @brief Error handler that prints to `stderr` and aborts
1284 
1285   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1286 
1287   @return An error code: 0 - success, otherwise - failure
1288 
1289   @ref Developer
1290 **/
1291 // LCOV_EXCL_START
1292 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1293   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1294   vfprintf(stderr, format, *args);
1295   fprintf(stderr, "\n");
1296   abort();
1297   return err_code;
1298 }
1299 // LCOV_EXCL_STOP
1300 
1301 /**
1302   @brief Error handler that prints to `stderr` and exits.
1303 
1304   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1305 
1306   In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1307 
1308   @return An error code: 0 - success, otherwise - failure
1309 
1310   @ref Developer
1311 **/
1312 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1313   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1314   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1315   vfprintf(stderr, format, *args);  // NOLINT
1316   fprintf(stderr, "\n");
1317   exit(err_code);
1318   return err_code;
1319 }
1320 
1321 /**
1322   @brief Set error handler
1323 
1324   A default error handler is set in @ref CeedInit().
1325   Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1326 
1327   @return An error code: 0 - success, otherwise - failure
1328 
1329   @ref Developer
1330 **/
1331 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1332   ceed->Error = handler;
1333   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1334   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1335   return CEED_ERROR_SUCCESS;
1336 }
1337 
1338 /**
1339   @brief Get error message
1340 
1341   The error message is only stored when using the error handler @ref CeedErrorStore()
1342 
1343   @param[in]  ceed    `Ceed` context to retrieve error message
1344   @param[out] err_msg Char pointer to hold error message
1345 
1346   @return An error code: 0 - success, otherwise - failure
1347 
1348   @ref Developer
1349 **/
1350 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1351   if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1352   if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1353   *err_msg = ceed->err_msg;
1354   return CEED_ERROR_SUCCESS;
1355 }
1356 
1357 /**
1358   @brief Restore error message.
1359 
1360   The error message is only stored when using the error handler @ref CeedErrorStore().
1361 
1362   @param[in]  ceed    `Ceed` context to restore error message
1363   @param[out] err_msg Char pointer that holds error message
1364 
1365   @return An error code: 0 - success, otherwise - failure
1366 
1367   @ref Developer
1368 **/
1369 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1370   if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1371   if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1372   *err_msg = NULL;
1373   memcpy(ceed->err_msg, "No error message stored", 24);
1374   return CEED_ERROR_SUCCESS;
1375 }
1376 
1377 /**
1378   @brief Get libCEED library version information.
1379 
1380   libCEED version numbers have the form major.minor.patch.
1381   Non-release versions may contain unstable interfaces.
1382 
1383   @param[out] major   Major version of the library
1384   @param[out] minor   Minor version of the library
1385   @param[out] patch   Patch (subminor) version of the library
1386   @param[out] release True for releases; false for development branches
1387 
1388   The caller may pass `NULL` for any arguments that are not needed.
1389 
1390   @return An error code: 0 - success, otherwise - failure
1391 
1392   @ref Developer
1393 
1394   @sa CEED_VERSION_GE()
1395 */
1396 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1397   if (major) *major = CEED_VERSION_MAJOR;
1398   if (minor) *minor = CEED_VERSION_MINOR;
1399   if (patch) *patch = CEED_VERSION_PATCH;
1400   if (release) *release = CEED_VERSION_RELEASE;
1401   return CEED_ERROR_SUCCESS;
1402 }
1403 
1404 /**
1405   @brief Get libCEED scalar type, such as F64 or F32
1406 
1407   @param[out] scalar_type Type of libCEED scalars
1408 
1409   @return An error code: 0 - success, otherwise - failure
1410 
1411   @ref Developer
1412 */
1413 int CeedGetScalarType(CeedScalarType *scalar_type) {
1414   *scalar_type = CEED_SCALAR_TYPE;
1415   return CEED_ERROR_SUCCESS;
1416 }
1417 
1418 /// @}
1419