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