xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision c6e1a279e1a14cf5c07fcf63f27bc1c1e286a3df)
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, ElemRestrictionCreateOriented),
840     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
841     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
842     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
843     CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
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