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