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