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