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