xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision 390967d5a6981fa3ac285466e6ecfff0eb54d0af)
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, TensorContractCreate),
843     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
844     CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
845     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
846     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
847     CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
848     CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
849     CEED_FTABLE_ENTRY(CeedVector, SetArray),
850     CEED_FTABLE_ENTRY(CeedVector, TakeArray),
851     CEED_FTABLE_ENTRY(CeedVector, SetValue),
852     CEED_FTABLE_ENTRY(CeedVector, GetArray),
853     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
854     CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
855     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
856     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
857     CEED_FTABLE_ENTRY(CeedVector, Norm),
858     CEED_FTABLE_ENTRY(CeedVector, Scale),
859     CEED_FTABLE_ENTRY(CeedVector, AXPY),
860     CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
861     CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
862     CEED_FTABLE_ENTRY(CeedVector, Destroy),
863     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
864     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
865     CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
866     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
867     CEED_FTABLE_ENTRY(CeedBasis, Apply),
868     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
869     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
870     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
871     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
872     CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
873     CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
874     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
875     CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
876     CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
877     CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
878     CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
879     CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
880     CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
881     CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
882     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
883     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
884     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
885     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
886     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
887     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
888     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
889     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
890     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
891     CEED_FTABLE_ENTRY(CeedOperator, Apply),
892     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
893     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
894     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
895     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
896     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
897     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
898   };
899 
900   ierr = CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets); CeedChk(ierr);
901   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
902 
903   // Set fallback for advanced CeedOperator functions
904   const char fallbackresource[] = "";
905   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
906   CeedChk(ierr);
907 
908   // Record env variables CEED_DEBUG or DBG
909   (*ceed)->is_debug = !!getenv("CEED_DEBUG") || !!getenv("DEBUG") ||
910                       !!getenv("DBG");
911 
912   // Backend specific setup
913   ierr = backends[match_index].init(&resource[match_help], *ceed); CeedChk(ierr);
914 
915   // Copy resource prefix, if backend setup successful
916   ierr = CeedStringAllocCopy(backends[match_index].prefix,
917                              (char **)&(*ceed)->resource);
918   CeedChk(ierr);
919   return CEED_ERROR_SUCCESS;
920 }
921 
922 /**
923   @brief Copy the pointer to a Ceed context. Both pointers should
924            be destroyed with `CeedDestroy()`;
925            Note: If `*ceed_copy` is non-NULL, then it is assumed that
926            `*ceed_copy` is a pointer to a Ceed context. This Ceed
927            context will be destroyed if `*ceed_copy` is the only
928            reference to this Ceed context.
929 
930   @param ceed            Ceed context to copy reference to
931   @param[out] ceed_copy  Variable to store copied reference
932 
933   @return An error code: 0 - success, otherwise - failure
934 
935   @ref User
936 **/
937 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
938   int ierr;
939 
940   ierr = CeedReference(ceed); CeedChk(ierr);
941   ierr = CeedDestroy(ceed_copy); CeedChk(ierr);
942   *ceed_copy = ceed;
943   return CEED_ERROR_SUCCESS;
944 }
945 
946 /**
947   @brief Get the full resource name for a Ceed context
948 
949   @param ceed           Ceed context to get resource name of
950   @param[out] resource  Variable to store resource name
951 
952   @return An error code: 0 - success, otherwise - failure
953 
954   @ref User
955 **/
956 int CeedGetResource(Ceed ceed, const char **resource) {
957   *resource = (const char *)ceed->resource;
958   return CEED_ERROR_SUCCESS;
959 }
960 
961 /**
962   @brief Return Ceed context preferred memory type
963 
964   @param ceed           Ceed context to get preferred memory type of
965   @param[out] mem_type  Address to save preferred memory type to
966 
967   @return An error code: 0 - success, otherwise - failure
968 
969   @ref User
970 **/
971 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
972   int ierr;
973 
974   if (ceed->GetPreferredMemType) {
975     ierr = ceed->GetPreferredMemType(mem_type); CeedChk(ierr);
976   } else {
977     Ceed delegate;
978     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
979 
980     if (delegate) {
981       ierr = CeedGetPreferredMemType(delegate, mem_type); CeedChk(ierr);
982     } else {
983       *mem_type = CEED_MEM_HOST;
984     }
985   }
986   return CEED_ERROR_SUCCESS;
987 }
988 
989 /**
990   @brief Get deterministic status of Ceed
991 
992   @param[in] ceed               Ceed
993   @param[out] is_deterministic  Variable to store deterministic status
994 
995   @return An error code: 0 - success, otherwise - failure
996 
997   @ref User
998 **/
999 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1000   *is_deterministic = ceed->is_deterministic;
1001   return CEED_ERROR_SUCCESS;
1002 }
1003 
1004 /**
1005   @brief View a Ceed
1006 
1007   @param[in] ceed    Ceed to view
1008   @param[in] stream  Filestream to write to
1009 
1010   @return An error code: 0 - success, otherwise - failure
1011 
1012   @ref User
1013 **/
1014 int CeedView(Ceed ceed, FILE *stream) {
1015   int ierr;
1016   CeedMemType mem_type;
1017 
1018   ierr = CeedGetPreferredMemType(ceed, &mem_type); CeedChk(ierr);
1019 
1020   fprintf(stream, "Ceed\n"
1021           "  Ceed Resource: %s\n"
1022           "  Preferred MemType: %s\n",
1023           ceed->resource, CeedMemTypes[mem_type]);
1024   return CEED_ERROR_SUCCESS;
1025 }
1026 
1027 /**
1028   @brief Destroy a Ceed context
1029 
1030   @param ceed  Address of Ceed context to destroy
1031 
1032   @return An error code: 0 - success, otherwise - failure
1033 
1034   @ref User
1035 **/
1036 int CeedDestroy(Ceed *ceed) {
1037   int ierr;
1038   if (!*ceed || --(*ceed)->ref_count > 0) return CEED_ERROR_SUCCESS;
1039   if ((*ceed)->delegate) {
1040     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
1041   }
1042 
1043   if ((*ceed)->obj_delegate_count > 0) {
1044     for (int i=0; i<(*ceed)->obj_delegate_count; i++) {
1045       ierr = CeedDestroy(&((*ceed)->obj_delegates[i].delegate)); CeedChk(ierr);
1046       ierr = CeedFree(&(*ceed)->obj_delegates[i].obj_name); CeedChk(ierr);
1047     }
1048     ierr = CeedFree(&(*ceed)->obj_delegates); CeedChk(ierr);
1049   }
1050 
1051   if ((*ceed)->Destroy) {
1052     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
1053   }
1054 
1055   ierr = CeedFree(&(*ceed)->f_offsets); CeedChk(ierr);
1056   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
1057   ierr = CeedDestroy(&(*ceed)->op_fallback_ceed); CeedChk(ierr);
1058   ierr = CeedFree(&(*ceed)->op_fallback_resource); CeedChk(ierr);
1059   ierr = CeedFree(ceed); CeedChk(ierr);
1060   return CEED_ERROR_SUCCESS;
1061 }
1062 
1063 // LCOV_EXCL_START
1064 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1065   if (ceed->parent)
1066     return CeedErrorFormat(ceed->parent, format, args);
1067   if (ceed->op_fallback_parent)
1068     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,
1081                   int ecode, const char *format, ...) {
1082   va_list args;
1083   int ret_val;
1084   va_start(args, format);
1085   if (ceed) {
1086     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1087   } else {
1088     // LCOV_EXCL_START
1089     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1090     if (!ceed_error_handler)
1091       ceed_error_handler = "abort";
1092     if (!strcmp(ceed_error_handler, "return"))
1093       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1094     else
1095       // This function will not return
1096       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1097   }
1098   va_end(args);
1099   return ret_val;
1100   // LCOV_EXCL_STOP
1101 }
1102 
1103 /**
1104   @brief Error handler that returns without printing anything.
1105 
1106   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1107 
1108   @ref Developer
1109 **/
1110 // LCOV_EXCL_START
1111 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no,
1112                     const char *func, int err_code, const char *format,
1113                     va_list *args) {
1114   return err_code;
1115 }
1116 // LCOV_EXCL_STOP
1117 
1118 /**
1119   @brief Error handler that stores the error message for future use and returns
1120            the error.
1121 
1122   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1123 
1124   @ref Developer
1125 **/
1126 // LCOV_EXCL_START
1127 int CeedErrorStore(Ceed ceed, const char *filename, int line_no,
1128                    const char *func, int err_code, const char *format,
1129                    va_list *args) {
1130   if (ceed->parent)
1131     return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format,
1132                           args);
1133   if (ceed->op_fallback_parent)
1134     return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func,
1135                           err_code, format, args);
1136 
1137   // Build message
1138   CeedInt len;
1139   len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ",
1140                  filename, line_no, func);
1141   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1142   // *INDENT-OFF*
1143   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT
1144   // *INDENT-ON*
1145   return err_code;
1146 }
1147 // LCOV_EXCL_STOP
1148 
1149 /**
1150   @brief Error handler that prints to stderr and aborts
1151 
1152   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1153 
1154   @ref Developer
1155 **/
1156 // LCOV_EXCL_START
1157 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no,
1158                    const char *func, int err_code, const char *format,
1159                    va_list *args) {
1160   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1161   vfprintf(stderr, format, *args);
1162   fprintf(stderr, "\n");
1163   abort();
1164   return err_code;
1165 }
1166 // LCOV_EXCL_STOP
1167 
1168 /**
1169   @brief Error handler that prints to stderr and exits
1170 
1171   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
1172 
1173   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
1174   handlers (e.g., as used by gcov) are run.
1175 
1176   @ref Developer
1177 **/
1178 int CeedErrorExit(Ceed ceed, const char *filename, int line_no,
1179                   const char *func, int err_code, const char *format, va_list *args) {
1180   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1181   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1182   vfprintf(stderr, format, *args); // NOLINT
1183   fprintf(stderr, "\n");
1184   exit(err_code);
1185   return err_code;
1186 }
1187 
1188 /**
1189   @brief Set error handler
1190 
1191   A default error handler is set in CeedInit().  Use this function to change
1192   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
1193   error handler.
1194 
1195   @ref Developer
1196 **/
1197 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1198   ceed->Error = handler;
1199   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1200   for (int i=0; i<ceed->obj_delegate_count; i++)
1201     CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1202   return CEED_ERROR_SUCCESS;
1203 }
1204 
1205 /**
1206   @brief Get error message
1207 
1208   The error message is only stored when using the error handler
1209     CeedErrorStore()
1210 
1211   @param[in] ceed      Ceed contex to retrieve error message
1212   @param[out] err_msg  Char pointer to hold error message
1213 
1214   @ref Developer
1215 **/
1216 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1217   if (ceed->parent)
1218     return CeedGetErrorMessage(ceed->parent, err_msg);
1219   if (ceed->op_fallback_parent)
1220     return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1221   *err_msg = ceed->err_msg;
1222   return CEED_ERROR_SUCCESS;
1223 }
1224 
1225 /**
1226   @brief Restore error message
1227 
1228   The error message is only stored when using the error handler
1229     CeedErrorStore()
1230 
1231   @param[in] ceed      Ceed contex to restore error message
1232   @param[out] err_msg  Char pointer that holds error message
1233 
1234   @ref Developer
1235 **/
1236 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1237   if (ceed->parent)
1238     return CeedResetErrorMessage(ceed->parent, err_msg);
1239   if (ceed->op_fallback_parent)
1240     return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1241   *err_msg = NULL;
1242   memcpy(ceed->err_msg, "No error message stored", 24);
1243   return CEED_ERROR_SUCCESS;
1244 }
1245 
1246 /**
1247   @brief Get libCEED library version info
1248 
1249   libCEED version numbers have the form major.minor.patch. Non-release versions
1250   may contain unstable interfaces.
1251 
1252   @param[out] major    Major version of the library
1253   @param[out] minor    Minor version of the library
1254   @param[out] patch    Patch (subminor) version of the library
1255   @param[out] release  True for releases; false for development branches.
1256 
1257   The caller may pass NULL for any arguments that are not needed.
1258 
1259   @sa CEED_VERSION_GE()
1260 
1261   @ref Developer
1262 */
1263 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1264   if (major) *major = CEED_VERSION_MAJOR;
1265   if (minor) *minor = CEED_VERSION_MINOR;
1266   if (patch) *patch = CEED_VERSION_PATCH;
1267   if (release) *release = CEED_VERSION_RELEASE;
1268   return 0;
1269 }
1270 
1271 int CeedGetScalarType(CeedScalarType *scalar_type) {
1272   *scalar_type = CEED_SCALAR_TYPE;
1273   return 0;
1274 }
1275 
1276 
1277 /// @}
1278