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