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