xref: /libCEED/interface/ceed.c (revision 5883eba0d2359a571ceeab5246edc6c017c73008)
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-impl.h>
19 #include <ceed-backend.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @cond DOXYGEN_SKIP
28 static CeedRequest ceed_request_immediate;
29 static CeedRequest ceed_request_ordered;
30 
31 static struct {
32   char prefix[CEED_MAX_RESOURCE_LEN];
33   int (*init)(const char *resource, Ceed f);
34   unsigned int priority;
35 } backends[32];
36 static size_t num_backends;
37 
38 #define CEED_FTABLE_ENTRY(class, method) \
39   {#class #method, offsetof(struct class ##_private, method)}
40 /// @endcond
41 
42 /// @file
43 /// Implementation of core components of Ceed library
44 
45 /// @addtogroup CeedUser
46 /// @{
47 
48 /**
49   @brief Request immediate completion
50 
51   This predefined constant is passed as the \ref CeedRequest argument to
52   interfaces when the caller wishes for the operation to be performed
53   immediately.  The code
54 
55   @code
56     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
57   @endcode
58 
59   is semantically equivalent to
60 
61   @code
62     CeedRequest request;
63     CeedOperatorApply(op, ..., &request);
64     CeedRequestWait(&request);
65   @endcode
66 
67   @sa CEED_REQUEST_ORDERED
68 **/
69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
70 
71 /**
72   @brief Request ordered completion
73 
74   This predefined constant is passed as the \ref CeedRequest argument to
75   interfaces when the caller wishes for the operation to be completed in the
76   order that it is submitted to the device.  It is typically used in a construct
77   such as
78 
79   @code
80     CeedRequest request;
81     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
82     CeedOperatorApply(op2, ..., &request);
83     // other optional work
84     CeedWait(&request);
85   @endcode
86 
87   which allows the sequence to complete asynchronously but does not start
88   `op2` until `op1` has completed.
89 
90   @todo The current implementation is overly strict, offering equivalent
91   semantics to @ref CEED_REQUEST_IMMEDIATE.
92 
93   @sa CEED_REQUEST_IMMEDIATE
94  */
95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
96 
97 /**
98   @brief Wait for a CeedRequest to complete.
99 
100   Calling CeedRequestWait on a NULL request is a no-op.
101 
102   @param req Address of CeedRequest to wait for; zeroed on completion.
103 
104   @return An error code: 0 - success, otherwise - failure
105 
106   @ref User
107 **/
108 int CeedRequestWait(CeedRequest *req) {
109   if (!*req)
110     return 0;
111   return CeedError(NULL, 2, "CeedRequestWait not implemented");
112 }
113 
114 /// @}
115 
116 /// ----------------------------------------------------------------------------
117 /// Ceed Library Internal Functions
118 /// ----------------------------------------------------------------------------
119 /// @addtogroup CeedDeveloper
120 /// @{
121 
122 /**
123   @brief Error handler that returns without printing anything.
124 
125   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
126 
127   @ref Developer
128 **/
129 // LCOV_EXCL_START
130 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
131                     const char *func, int ecode, const char *format,
132                     va_list args) {
133   return ecode;
134 }
135 // LCOV_EXCL_STOP
136 
137 /**
138   @brief Error handler that prints to stderr and aborts
139 
140   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
141 
142   @ref Developer
143 **/
144 // LCOV_EXCL_START
145 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
146                    const char *func, int ecode, const char *format,
147                    va_list args) {
148   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
149   vfprintf(stderr, format, args);
150   fprintf(stderr, "\n");
151   abort();
152   return ecode;
153 }
154 // LCOV_EXCL_STOP
155 
156 /**
157   @brief Error handler that prints to stderr and exits
158 
159   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
160 
161   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
162   handlers (e.g., as used by gcov) are run.
163 
164   @ref Developer
165 **/
166 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, const char *func,
167                   int ecode, const char *format, va_list args) {
168   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
169   vfprintf(stderr, format, args);
170   fprintf(stderr, "\n");
171   exit(ecode);
172   return ecode;
173 }
174 
175 /**
176   @brief Set error handler
177 
178   A default error handler is set in CeedInit().  Use this function to change
179   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
180   error handler.
181 
182   @ref Developer
183 **/
184 int CeedSetErrorHandler(Ceed ceed,
185                         int (*eh)(Ceed, const char *, int, const char *,
186                                   int, const char *, va_list)) {
187   ceed->Error = eh;
188   return 0;
189 }
190 
191 /// @}
192 
193 /// ----------------------------------------------------------------------------
194 /// Ceed Backend API
195 /// ----------------------------------------------------------------------------
196 /// @addtogroup CeedBackend
197 /// @{
198 
199 /**
200   @brief Print Ceed debugging information
201 
202   @param ceed    Ceed context
203   @param format  Printing format
204 
205   @return None
206 
207   @ref Backend
208 **/
209 // LCOV_EXCL_START
210 void CeedDebugImpl(const Ceed ceed, const char *format,...) {
211   if (!ceed->debug) return;
212   va_list args;
213   va_start(args, format);
214   CeedDebugImpl256(ceed, 0, format, args);
215   va_end(args);
216 }
217 // LCOV_EXCL_STOP
218 
219 /**
220   @brief Print Ceed debugging information in color
221 
222   @param ceed    Ceed context
223   @param color   Color to print
224   @param format  Printing format
225 
226   @return None
227 
228   @ref Backend
229 **/
230 // LCOV_EXCL_START
231 void CeedDebugImpl256(const Ceed ceed, const unsigned char color,
232                       const char *format,...) {
233   if (!ceed->debug) return;
234   va_list args;
235   va_start(args, format);
236   fflush(stdout);
237   fprintf(stdout, "\033[38;5;%dm", color);
238   vfprintf(stdout, format, args);
239   fprintf(stdout, "\033[m");
240   fprintf(stdout, "\n");
241   fflush(stdout);
242   va_end(args);
243 }
244 // LCOV_EXCL_STOP
245 
246 /**
247   @brief Allocate an array on the host; use CeedMalloc()
248 
249   Memory usage can be tracked by the library.  This ensures sufficient
250     alignment for vectorization and should be used for large allocations.
251 
252   @param n Number of units to allocate
253   @param unit Size of each unit
254   @param p Address of pointer to hold the result.
255 
256   @return An error code: 0 - success, otherwise - failure
257 
258   @sa CeedFree()
259 
260   @ref Backend
261 **/
262 int CeedMallocArray(size_t n, size_t unit, void *p) {
263   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
264   if (ierr)
265     // LCOV_EXCL_START
266     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
267                      "members of size %zd\n", n, unit);
268   // LCOV_EXCL_STOP
269 
270   return 0;
271 }
272 
273 /**
274   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
275 
276   Memory usage can be tracked by the library.
277 
278   @param n    Number of units to allocate
279   @param unit Size of each unit
280   @param p    Address of pointer to hold the result.
281 
282   @return An error code: 0 - success, otherwise - failure
283 
284   @sa CeedFree()
285 
286   @ref Backend
287 **/
288 int CeedCallocArray(size_t n, size_t unit, void *p) {
289   *(void **)p = calloc(n, unit);
290   if (n && unit && !*(void **)p)
291     // LCOV_EXCL_START
292     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
293                      "%zd\n", n, unit);
294   // LCOV_EXCL_STOP
295 
296   return 0;
297 }
298 
299 /**
300   @brief Reallocate an array on the host; use CeedRealloc()
301 
302   Memory usage can be tracked by the library.
303 
304   @param n    Number of units to allocate
305   @param unit Size of each unit
306   @param p    Address of pointer to hold the result.
307 
308   @return An error code: 0 - success, otherwise - failure
309 
310   @sa CeedFree()
311 
312   @ref Backend
313 **/
314 int CeedReallocArray(size_t n, size_t unit, void *p) {
315   *(void **)p = realloc(*(void **)p, n*unit);
316   if (n && unit && !*(void **)p)
317     // LCOV_EXCL_START
318     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
319                      "%zd\n", n, unit);
320   // LCOV_EXCL_STOP
321 
322   return 0;
323 }
324 
325 /** Free memory allocated using CeedMalloc() or CeedCalloc()
326 
327   @param p address of pointer to memory.  This argument is of type void* to
328              avoid needing a cast, but is the address of the pointer (which is
329              zeroed) rather than the pointer.
330 **/
331 int CeedFree(void *p) {
332   free(*(void **)p);
333   *(void **)p = NULL;
334   return 0;
335 }
336 
337 /**
338   @brief Register a Ceed backend
339 
340   @param prefix   Prefix of resources for this backend to respond to.  For
341                     example, the reference backend responds to "/cpu/self".
342   @param init     Initialization function called by CeedInit() when the backend
343                     is selected to drive the requested resource.
344   @param priority Integer priority.  Lower values are preferred in case the
345                     resource requested by CeedInit() has non-unique best prefix
346                     match.
347 
348   @return An error code: 0 - success, otherwise - failure
349 
350   @ref Backend
351 **/
352 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
353                  unsigned int priority) {
354   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
355     // LCOV_EXCL_START
356     return CeedError(NULL, 1, "Too many backends");
357   // LCOV_EXCL_STOP
358 
359   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
360   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
361   backends[num_backends].init = init;
362   backends[num_backends].priority = priority;
363   num_backends++;
364   return 0;
365 }
366 
367 /**
368   @brief Return debugging status flag
369 
370   @param ceed     Ceed context to get debugging flag
371   @param isDebug  Variable to store debugging flag
372 
373   @return An error code: 0 - success, otherwise - failure
374 
375   @ref Bcakend
376 **/
377 int CeedIsDebug(Ceed ceed, bool *isDebug) {
378   *isDebug = ceed->debug;
379   return 0;
380 }
381 
382 /**
383   @brief Retrieve a parent Ceed context
384 
385   @param ceed        Ceed context to retrieve parent of
386   @param[out] parent Address to save the parent to
387 
388   @return An error code: 0 - success, otherwise - failure
389 
390   @ref Backend
391 **/
392 int CeedGetParent(Ceed ceed, Ceed *parent) {
393   int ierr;
394   if (ceed->parent) {
395     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
396     return 0;
397   }
398   *parent = ceed;
399   return 0;
400 }
401 
402 /**
403   @brief Retrieve a delegate Ceed context
404 
405   @param ceed          Ceed context to retrieve delegate of
406   @param[out] delegate Address to save the delegate to
407 
408   @return An error code: 0 - success, otherwise - failure
409 
410   @ref Backend
411 **/
412 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
413   *delegate = ceed->delegate;
414   return 0;
415 }
416 
417 /**
418   @brief Set a delegate Ceed context
419 
420   This function allows a Ceed context to set a delegate Ceed context. All
421     backend implementations default to the delegate Ceed context, unless
422     overridden.
423 
424   @param ceed           Ceed context to set delegate of
425   @param[out] delegate  Address to set the delegate to
426 
427   @return An error code: 0 - success, otherwise - failure
428 
429   @ref Backend
430 **/
431 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
432   ceed->delegate = delegate;
433   delegate->parent = ceed;
434   return 0;
435 }
436 
437 /**
438   @brief Retrieve a delegate Ceed context for a specific object type
439 
440   @param ceed           Ceed context to retrieve delegate of
441   @param[out] delegate  Address to save the delegate to
442   @param[in] objname    Name of the object type to retrieve delegate for
443 
444   @return An error code: 0 - success, otherwise - failure
445 
446   @ref Backend
447 **/
448 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
449   CeedInt ierr;
450 
451   // Check for object delegate
452   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
453     if (!strcmp(objname, ceed->objdelegates->objname)) {
454       *delegate = ceed->objdelegates->delegate;
455       return 0;
456     }
457 
458   // Use default delegate if no object delegate
459   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
460 
461   return 0;
462 }
463 
464 /**
465   @brief Set a delegate Ceed context for a specific object type
466 
467   This function allows a Ceed context to set a delegate Ceed context for a
468     given type of Ceed object. All backend implementations default to the
469     delegate Ceed context for this object. For example,
470     CeedSetObjectDelegate(ceed, refceed, "Basis")
471   uses refceed implementations for all CeedBasis backend functions.
472 
473   @param ceed          Ceed context to set delegate of
474   @param[out] delegate Address to set the delegate to
475   @param[in] objname   Name of the object type to set delegate for
476 
477   @return An error code: 0 - success, otherwise - failure
478 
479   @ref Backend
480 **/
481 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
482   CeedInt ierr;
483   CeedInt count = ceed->objdelegatecount;
484 
485   // Malloc or Realloc
486   if (count) {
487     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
488   } else {
489     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
490   }
491   ceed->objdelegatecount++;
492 
493   // Set object delegate
494   ceed->objdelegates[count].delegate = delegate;
495   size_t slen = strlen(objname) + 1;
496   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
497   memcpy(ceed->objdelegates[count].objname, objname, slen);
498 
499   // Set delegate parent
500   delegate->parent = ceed;
501 
502   return 0;
503 }
504 
505 /**
506   @brief Get the fallback resource for CeedOperators
507 
508   @param ceed          Ceed context
509   @param[out] resource Variable to store fallback resource
510 
511   @return An error code: 0 - success, otherwise - failure
512 
513   @ref Backend
514 **/
515 
516 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
517   *resource = (const char *)ceed->opfallbackresource;
518   return 0;
519 }
520 
521 /**
522   @brief Set the fallback resource for CeedOperators. The current resource, if
523            any, is freed by calling this function. This string is freed upon the
524            destruction of the Ceed context.
525 
526   @param[out] ceed Ceed context
527   @param resource  Fallback resource to set
528 
529   @return An error code: 0 - success, otherwise - failure
530 
531   @ref Backend
532 **/
533 
534 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
535   int ierr;
536 
537   // Free old
538   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
539 
540   // Set new
541   size_t len = strlen(resource);
542   char *tmp;
543   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
544   memcpy(tmp, resource, len+1);
545   ceed->opfallbackresource = tmp;
546 
547   return 0;
548 }
549 
550 /**
551   @brief Get the parent Ceed context associated with a fallback Ceed context
552            for a CeedOperator
553 
554   @param ceed         Ceed context
555   @param[out] parent  Variable to store parent Ceed context
556 
557   @return An error code: 0 - success, otherwise - failure
558 
559   @ref Backend
560 **/
561 
562 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
563   *parent = ceed->opfallbackparent;
564   return 0;
565 }
566 
567 /**
568   @brief Flag Ceed context as deterministic
569 
570   @param ceed     Ceed to flag as deterministic
571 
572   @return An error code: 0 - success, otherwise - failure
573 
574   @ref Backend
575 **/
576 
577 int CeedSetDeterministic(Ceed ceed, bool isDeterministic) {
578   ceed->isDeterministic = isDeterministic;
579   return 0;
580 }
581 
582 /**
583   @brief Set a backend function
584 
585   This function is used for a backend to set the function associated with
586   the Ceed objects. For example,
587     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
588   sets the backend implementation of 'CeedVectorCreate' and
589     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
590   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
591   is not required for the object type ("Basis" vs "CeedBasis").
592 
593   @param ceed           Ceed context for error handling
594   @param type           Type of Ceed object to set function for
595   @param[out] object    Ceed object to set function for
596   @param fname          Name of function to set
597   @param f              Function to set
598 
599   @return An error code: 0 - success, otherwise - failure
600 
601   @ref Backend
602 **/
603 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
604                            const char *fname, int (*f)()) {
605   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
606 
607   // Build lookup name
608   if (strcmp(type, "Ceed"))
609     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
610   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
611   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
612 
613   // Find and use offset
614   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
615     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
616       size_t offset = ceed->foffsets[i].offset;
617       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
618       *fpointer = f;
619       return 0;
620     }
621 
622   // LCOV_EXCL_START
623   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
624                    "object '%s'", fname, type);
625   // LCOV_EXCL_STOP
626 }
627 
628 /**
629   @brief Retrieve backend data for a Ceed context
630 
631   @param ceed      Ceed context to retrieve data of
632   @param[out] data Address to save data to
633 
634   @return An error code: 0 - success, otherwise - failure
635 
636   @ref Backend
637 **/
638 int CeedGetData(Ceed ceed, void **data) {
639   *data = ceed->data;
640   return 0;
641 }
642 
643 /**
644   @brief Set backend data for a Ceed context
645 
646   @param ceed           Ceed context to set data of
647   @param data           Address of data to set
648 
649   @return An error code: 0 - success, otherwise - failure
650 
651   @ref Backend
652 **/
653 int CeedSetData(Ceed ceed, void **data) {
654   ceed->data = *data;
655   return 0;
656 }
657 
658 /// @}
659 
660 /// ----------------------------------------------------------------------------
661 /// Ceed Public API
662 /// ----------------------------------------------------------------------------
663 /// @addtogroup CeedUser
664 /// @{
665 
666 /**
667   @brief Initialize a \ref Ceed context to use the specified resource.
668 
669   @param resource  Resource to use, e.g., "/cpu/self"
670   @param ceed      The library context
671   @sa CeedRegister() CeedDestroy()
672 
673   @return An error code: 0 - success, otherwise - failure
674 
675   @ref User
676 **/
677 int CeedInit(const char *resource, Ceed *ceed) {
678   int ierr;
679   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
680 
681   // Find matching backend
682   if (!resource)
683     // LCOV_EXCL_START
684     return CeedError(NULL, 1, "No resource provided");
685   // LCOV_EXCL_STOP
686 
687   for (size_t i=0; i<num_backends; i++) {
688     size_t n;
689     const char *prefix = backends[i].prefix;
690     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
691     priority = backends[i].priority;
692     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
693       matchlen = n;
694       matchpriority = priority;
695       matchidx = i;
696     }
697   }
698   if (!matchlen)
699     // LCOV_EXCL_START
700     return CeedError(NULL, 1, "No suitable backend: %s", resource);
701   // LCOV_EXCL_STOP
702 
703   // Setup Ceed
704   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
705   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
706   if (!ceed_error_handler)
707     ceed_error_handler = "abort";
708   if (!strcmp(ceed_error_handler, "exit"))
709     (*ceed)->Error = CeedErrorExit;
710   else
711     (*ceed)->Error = CeedErrorAbort;
712   (*ceed)->refcount = 1;
713   (*ceed)->data = NULL;
714 
715   // Set lookup table
716   foffset foffsets[] = {
717     CEED_FTABLE_ENTRY(Ceed, Error),
718     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
719     CEED_FTABLE_ENTRY(Ceed, Destroy),
720     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
721     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
722     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
723     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
724     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
725     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
726     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
727     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
728     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
729     CEED_FTABLE_ENTRY(CeedVector, SetArray),
730     CEED_FTABLE_ENTRY(CeedVector, TakeArray),
731     CEED_FTABLE_ENTRY(CeedVector, SetValue),
732     CEED_FTABLE_ENTRY(CeedVector, GetArray),
733     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
734     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
735     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
736     CEED_FTABLE_ENTRY(CeedVector, Norm),
737     CEED_FTABLE_ENTRY(CeedVector, Destroy),
738     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
739     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
740     CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
741     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
742     CEED_FTABLE_ENTRY(CeedBasis, Apply),
743     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
744     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
745     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
746     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
747     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
748     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
749     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
750     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
751     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
752     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
753     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
754     CEED_FTABLE_ENTRY(CeedOperator, Apply),
755     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
756     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
757     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
758     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
759     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
760     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
761   };
762 
763   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
764   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
765 
766   // Set fallback for advanced CeedOperator functions
767   const char fallbackresource[] = "/cpu/self/ref/serial";
768   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
769   CeedChk(ierr);
770 
771   // Record env variables CEED_DEBUG or DBG
772   (*ceed)->debug = !!getenv("CEED_DEBUG") || !!getenv("DBG");
773 
774   // Backend specific setup
775   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
776 
777   // Copy resource prefix, if backend setup sucessful
778   size_t len = strlen(backends[matchidx].prefix);
779   char *tmp;
780   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
781   memcpy(tmp, backends[matchidx].prefix, len+1);
782   (*ceed)->resource = tmp;
783 
784   return 0;
785 }
786 
787 /**
788   @brief Get the full resource name for a Ceed context
789 
790   @param ceed            Ceed context to get resource name of
791   @param[out] resource   Variable to store resource name
792 
793   @return An error code: 0 - success, otherwise - failure
794 
795   @ref User
796 **/
797 
798 int CeedGetResource(Ceed ceed, const char **resource) {
799   *resource = (const char *)ceed->resource;
800   return 0;
801 }
802 
803 /**
804   @brief Return Ceed context preferred memory type
805 
806   @param ceed      Ceed context to get preferred memory type of
807   @param[out] type Address to save preferred memory type to
808 
809   @return An error code: 0 - success, otherwise - failure
810 
811   @ref User
812 **/
813 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
814   int ierr;
815 
816   if (ceed->GetPreferredMemType) {
817     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
818   } else {
819     Ceed delegate;
820     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
821 
822     if (delegate) {
823       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
824     } else {
825       *type = CEED_MEM_HOST;
826     }
827   }
828 
829   return 0;
830 }
831 
832 /**
833   @brief Get deterministic status of Ceed
834 
835   @param[in] ceed              Ceed
836   @param[out] isDeterministic  Variable to store deterministic status
837 
838   @return An error code: 0 - success, otherwise - failure
839 
840   @ref User
841 **/
842 int CeedIsDeterministic(Ceed ceed, bool *isDeterministic) {
843   *isDeterministic = ceed->isDeterministic;
844   return 0;
845 }
846 
847 /**
848   @brief View a Ceed
849 
850   @param[in] ceed          Ceed to view
851   @param[in] stream        Filestream to write to
852 
853   @return An error code: 0 - success, otherwise - failure
854 
855   @ref User
856 **/
857 int CeedView(Ceed ceed, FILE *stream) {
858   int ierr;
859   CeedMemType memtype;
860 
861   ierr = CeedGetPreferredMemType(ceed, &memtype); CeedChk(ierr);
862 
863   fprintf(stream, "Ceed\n"
864           "  Ceed Resource: %s\n"
865           "  Preferred MemType: %s\n",
866           ceed->resource, CeedMemTypes[memtype]);
867 
868   return 0;
869 }
870 
871 /**
872   @brief Destroy a Ceed context
873 
874   @param ceed Address of Ceed context to destroy
875 
876   @return An error code: 0 - success, otherwise - failure
877 
878   @ref User
879 **/
880 int CeedDestroy(Ceed *ceed) {
881   int ierr;
882   if (!*ceed || --(*ceed)->refcount > 0)
883     return 0;
884 
885   if ((*ceed)->delegate) {
886     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
887   }
888 
889   if ((*ceed)->objdelegatecount > 0) {
890     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
891       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
892       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
893     }
894     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
895   }
896 
897   if ((*ceed)->Destroy) {
898     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
899   }
900 
901   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
902   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
903   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
904   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
905   ierr = CeedFree(ceed); CeedChk(ierr);
906   return 0;
907 }
908 
909 /**
910   @brief Error handling implementation; use \ref CeedError instead.
911 
912   @ref Developer
913 **/
914 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
915                   int ecode, const char *format, ...) {
916   va_list args;
917   int retval;
918   va_start(args, format);
919   if (ceed) {
920     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
921   } else {
922     // This function doesn't actually return
923     // LCOV_EXCL_START
924     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
925   }
926   va_end(args);
927   return retval;
928   // LCOV_EXCL_STOP
929 }
930 
931 /// @}
932