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