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