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