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