xref: /libCEED/interface/ceed.c (revision dcd0d0f3054a9244665ebd1f4480c81f2d63fee8)
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     CeedDebug(ceed, "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     CeedDebug(ceed, "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, DataDestroy),
923     CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
924     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
925     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
926     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
927     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
928     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
929     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
930     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
931     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
932     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
933     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
934     CEED_FTABLE_ENTRY(CeedOperator, Apply),
935     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
936     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
937     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
938     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
939     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
940     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
941   };
942 
943   ierr = CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets); CeedChk(ierr);
944   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
945 
946   // Set fallback for advanced CeedOperator functions
947   const char fallbackresource[] = "";
948   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
949   CeedChk(ierr);
950 
951   // Record env variables CEED_DEBUG or DBG
952   (*ceed)->is_debug = !!getenv("CEED_DEBUG") || !!getenv("DEBUG") ||
953                       !!getenv("DBG");
954 
955   // Copy resource prefix, if backend setup successful
956   ierr = CeedStringAllocCopy(backends[match_index].prefix,
957                              (char **)&(*ceed)->resource);
958   CeedChk(ierr);
959 
960   // Set default JiT source root
961   // Note: there will always be the default root for every Ceed
962   // but all additional paths are added to the top-most parent
963   ierr = CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault);
964   CeedChk(ierr);
965 
966   // Backend specific setup
967   ierr = backends[match_index].init(&resource[match_help], *ceed); CeedChk(ierr);
968 
969   return CEED_ERROR_SUCCESS;
970 }
971 
972 /**
973   @brief Copy the pointer to a Ceed context. Both pointers should
974            be destroyed with `CeedDestroy()`;
975            Note: If `*ceed_copy` is non-NULL, then it is assumed that
976            `*ceed_copy` is a pointer to a Ceed context. This Ceed
977            context will be destroyed if `*ceed_copy` is the only
978            reference to this Ceed context.
979 
980   @param ceed            Ceed context to copy reference to
981   @param[out] ceed_copy  Variable to store copied reference
982 
983   @return An error code: 0 - success, otherwise - failure
984 
985   @ref User
986 **/
987 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
988   int ierr;
989 
990   ierr = CeedReference(ceed); CeedChk(ierr);
991   ierr = CeedDestroy(ceed_copy); CeedChk(ierr);
992   *ceed_copy = ceed;
993   return CEED_ERROR_SUCCESS;
994 }
995 
996 /**
997   @brief Get the full resource name for a Ceed context
998 
999   @param ceed           Ceed context to get resource name of
1000   @param[out] resource  Variable to store resource name
1001 
1002   @return An error code: 0 - success, otherwise - failure
1003 
1004   @ref User
1005 **/
1006 int CeedGetResource(Ceed ceed, const char **resource) {
1007   *resource = (const char *)ceed->resource;
1008   return CEED_ERROR_SUCCESS;
1009 }
1010 
1011 /**
1012   @brief Return Ceed context preferred memory type
1013 
1014   @param ceed           Ceed context to get preferred memory type of
1015   @param[out] mem_type  Address to save preferred memory type to
1016 
1017   @return An error code: 0 - success, otherwise - failure
1018 
1019   @ref User
1020 **/
1021 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1022   int ierr;
1023 
1024   if (ceed->GetPreferredMemType) {
1025     ierr = ceed->GetPreferredMemType(mem_type); CeedChk(ierr);
1026   } else {
1027     Ceed delegate;
1028     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
1029 
1030     if (delegate) {
1031       ierr = CeedGetPreferredMemType(delegate, mem_type); CeedChk(ierr);
1032     } else {
1033       *mem_type = CEED_MEM_HOST;
1034     }
1035   }
1036   return CEED_ERROR_SUCCESS;
1037 }
1038 
1039 /**
1040   @brief Get deterministic status of Ceed
1041 
1042   @param[in] ceed               Ceed
1043   @param[out] is_deterministic  Variable to store deterministic status
1044 
1045   @return An error code: 0 - success, otherwise - failure
1046 
1047   @ref User
1048 **/
1049 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1050   *is_deterministic = ceed->is_deterministic;
1051   return CEED_ERROR_SUCCESS;
1052 }
1053 
1054 /**
1055   @brief Set additional JiT source root for Ceed
1056 
1057   @param[in] ceed            Ceed
1058   @param[in] jit_source_root Absolute path to additional JiT source directory
1059 
1060   @return An error code: 0 - success, otherwise - failure
1061 
1062   @ref User
1063 **/
1064 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1065   int ierr;
1066   Ceed ceed_parent;
1067 
1068   ierr = CeedGetParent(ceed, &ceed_parent); CeedChk(ierr);
1069 
1070   CeedInt index = ceed_parent->num_jit_source_roots;
1071   size_t path_length = strlen(jit_source_root);
1072   ierr = CeedRealloc(index + 1, &ceed_parent->jit_source_roots); CeedChk(ierr);
1073   ierr = CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]);
1074   CeedChk(ierr);
1075   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1076   ceed_parent->num_jit_source_roots++;
1077 
1078   return CEED_ERROR_SUCCESS;
1079 }
1080 
1081 /**
1082   @brief View a Ceed
1083 
1084   @param[in] ceed    Ceed to view
1085   @param[in] stream  Filestream to write to
1086 
1087   @return An error code: 0 - success, otherwise - failure
1088 
1089   @ref User
1090 **/
1091 int CeedView(Ceed ceed, FILE *stream) {
1092   int ierr;
1093   CeedMemType mem_type;
1094 
1095   ierr = CeedGetPreferredMemType(ceed, &mem_type); CeedChk(ierr);
1096 
1097   fprintf(stream, "Ceed\n"
1098           "  Ceed Resource: %s\n"
1099           "  Preferred MemType: %s\n",
1100           ceed->resource, CeedMemTypes[mem_type]);
1101   return CEED_ERROR_SUCCESS;
1102 }
1103 
1104 /**
1105   @brief Destroy a Ceed context
1106 
1107   @param ceed  Address of Ceed context to destroy
1108 
1109   @return An error code: 0 - success, otherwise - failure
1110 
1111   @ref User
1112 **/
1113 int CeedDestroy(Ceed *ceed) {
1114   int ierr;
1115   if (!*ceed || --(*ceed)->ref_count > 0) return CEED_ERROR_SUCCESS;
1116   if ((*ceed)->delegate) {
1117     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
1118   }
1119 
1120   if ((*ceed)->obj_delegate_count > 0) {
1121     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1122       ierr = CeedDestroy(&((*ceed)->obj_delegates[i].delegate)); CeedChk(ierr);
1123       ierr = CeedFree(&(*ceed)->obj_delegates[i].obj_name); CeedChk(ierr);
1124     }
1125     ierr = CeedFree(&(*ceed)->obj_delegates); CeedChk(ierr);
1126   }
1127 
1128   if ((*ceed)->Destroy) {
1129     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
1130   }
1131 
1132   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1133     ierr = CeedFree(&(*ceed)->jit_source_roots[i]); CeedChk(ierr);
1134   }
1135   ierr = CeedFree(&(*ceed)->jit_source_roots); CeedChk(ierr);
1136 
1137   ierr = CeedFree(&(*ceed)->f_offsets); CeedChk(ierr);
1138   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
1139   ierr = CeedDestroy(&(*ceed)->op_fallback_ceed); CeedChk(ierr);
1140   ierr = CeedFree(&(*ceed)->op_fallback_resource); CeedChk(ierr);
1141   ierr = CeedFree(ceed); CeedChk(ierr);
1142   return CEED_ERROR_SUCCESS;
1143 }
1144 
1145 // LCOV_EXCL_START
1146 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1147   if (ceed->parent)
1148     return CeedErrorFormat(ceed->parent, format, args);
1149   if (ceed->op_fallback_parent)
1150     return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1151   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1152   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args); // NOLINT
1153   return ceed->err_msg;
1154 }
1155 // LCOV_EXCL_STOP
1156 
1157 /**
1158   @brief Error handling implementation; use \ref CeedError instead.
1159 
1160   @ref Developer
1161 **/
1162 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
1163                   int ecode, const char *format, ...) {
1164   va_list args;
1165   int ret_val;
1166   va_start(args, format);
1167   if (ceed) {
1168     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1169   } else {
1170     // LCOV_EXCL_START
1171     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1172     if (!ceed_error_handler)
1173       ceed_error_handler = "abort";
1174     if (!strcmp(ceed_error_handler, "return"))
1175       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1176     else
1177       // This function will not return
1178       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1179   }
1180   va_end(args);
1181   return ret_val;
1182   // LCOV_EXCL_STOP
1183 }
1184 
1185 /**
1186   @brief Error handler that returns without printing anything.
1187 
1188   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1189 
1190   @ref Developer
1191 **/
1192 // LCOV_EXCL_START
1193 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no,
1194                     const char *func, int err_code, const char *format,
1195                     va_list *args) {
1196   return err_code;
1197 }
1198 // LCOV_EXCL_STOP
1199 
1200 /**
1201   @brief Error handler that stores the error message for future use and returns
1202            the error.
1203 
1204   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1205 
1206   @ref Developer
1207 **/
1208 // LCOV_EXCL_START
1209 int CeedErrorStore(Ceed ceed, const char *filename, int line_no,
1210                    const char *func, int err_code, const char *format,
1211                    va_list *args) {
1212   if (ceed->parent)
1213     return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format,
1214                           args);
1215   if (ceed->op_fallback_parent)
1216     return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func,
1217                           err_code, format, args);
1218 
1219   // Build message
1220   int len;
1221   len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ",
1222                  filename, line_no, func);
1223   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1224   // *INDENT-OFF*
1225   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT
1226   // *INDENT-ON*
1227   return err_code;
1228 }
1229 // LCOV_EXCL_STOP
1230 
1231 /**
1232   @brief Error handler that prints to stderr and aborts
1233 
1234   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1235 
1236   @ref Developer
1237 **/
1238 // LCOV_EXCL_START
1239 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no,
1240                    const char *func, int err_code, const char *format,
1241                    va_list *args) {
1242   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1243   vfprintf(stderr, format, *args);
1244   fprintf(stderr, "\n");
1245   abort();
1246   return err_code;
1247 }
1248 // LCOV_EXCL_STOP
1249 
1250 /**
1251   @brief Error handler that prints to stderr and exits
1252 
1253   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1254 
1255   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
1256   handlers (e.g., as used by gcov) are run.
1257 
1258   @ref Developer
1259 **/
1260 int CeedErrorExit(Ceed ceed, const char *filename, int line_no,
1261                   const char *func, int err_code, const char *format, va_list *args) {
1262   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1263   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1264   vfprintf(stderr, format, *args); // NOLINT
1265   fprintf(stderr, "\n");
1266   exit(err_code);
1267   return err_code;
1268 }
1269 
1270 /**
1271   @brief Set error handler
1272 
1273   A default error handler is set in CeedInit().  Use this function to change
1274   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
1275   error handler.
1276 
1277   @ref Developer
1278 **/
1279 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1280   ceed->Error = handler;
1281   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1282   for (CeedInt i=0; i<ceed->obj_delegate_count; i++)
1283     CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1284   return CEED_ERROR_SUCCESS;
1285 }
1286 
1287 /**
1288   @brief Get error message
1289 
1290   The error message is only stored when using the error handler
1291     CeedErrorStore()
1292 
1293   @param[in] ceed      Ceed contex to retrieve error message
1294   @param[out] err_msg  Char pointer to hold error message
1295 
1296   @ref Developer
1297 **/
1298 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1299   if (ceed->parent)
1300     return CeedGetErrorMessage(ceed->parent, err_msg);
1301   if (ceed->op_fallback_parent)
1302     return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1303   *err_msg = ceed->err_msg;
1304   return CEED_ERROR_SUCCESS;
1305 }
1306 
1307 /**
1308   @brief Restore error message
1309 
1310   The error message is only stored when using the error handler
1311     CeedErrorStore()
1312 
1313   @param[in] ceed      Ceed contex to restore error message
1314   @param[out] err_msg  Char pointer that holds error message
1315 
1316   @ref Developer
1317 **/
1318 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1319   if (ceed->parent)
1320     return CeedResetErrorMessage(ceed->parent, err_msg);
1321   if (ceed->op_fallback_parent)
1322     return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1323   *err_msg = NULL;
1324   memcpy(ceed->err_msg, "No error message stored", 24);
1325   return CEED_ERROR_SUCCESS;
1326 }
1327 
1328 /**
1329   @brief Get libCEED library version info
1330 
1331   libCEED version numbers have the form major.minor.patch. Non-release versions
1332   may contain unstable interfaces.
1333 
1334   @param[out] major    Major version of the library
1335   @param[out] minor    Minor version of the library
1336   @param[out] patch    Patch (subminor) version of the library
1337   @param[out] release  True for releases; false for development branches.
1338 
1339   The caller may pass NULL for any arguments that are not needed.
1340 
1341   @sa CEED_VERSION_GE()
1342 
1343   @ref Developer
1344 */
1345 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1346   if (major) *major = CEED_VERSION_MAJOR;
1347   if (minor) *minor = CEED_VERSION_MINOR;
1348   if (patch) *patch = CEED_VERSION_PATCH;
1349   if (release) *release = CEED_VERSION_RELEASE;
1350   return 0;
1351 }
1352 
1353 int CeedGetScalarType(CeedScalarType *scalar_type) {
1354   *scalar_type = CEED_SCALAR_TYPE;
1355   return 0;
1356 }
1357 
1358 
1359 /// @}
1360