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