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