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