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