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