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