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