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