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