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