xref: /libCEED/interface/ceed.c (revision 60f9e2d6e4644aed5c818838050a6aeac7d17af3)
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 void CeedDebugImpl(const Ceed ceed, const char *format,...) {
210   if (!ceed->debug) return;
211   va_list args;
212   va_start(args, format);
213   CeedDebugImpl256(ceed, 0, format, args);
214   va_end(args);
215 }
216 
217 /**
218   @brief Print Ceed debugging information in color
219 
220   @param ceed    Ceed context
221   @param color   Color to print
222   @param format  Printing format
223 
224   @return None
225 
226   @ref Backend
227 **/
228 void CeedDebugImpl256(const Ceed ceed, const unsigned char color,
229                       const char *format,...) {
230   if (!ceed->debug) return;
231   va_list args;
232   va_start(args, format);
233   fflush(stdout);
234   fprintf(stdout, "\033[38;5;%dm", color);
235   vfprintf(stdout, format, args);
236   fprintf(stdout, "\033[m");
237   fprintf(stdout, "\n");
238   fflush(stdout);
239   va_end(args);
240 }
241 
242 /**
243   @brief Allocate an array on the host; use CeedMalloc()
244 
245   Memory usage can be tracked by the library.  This ensures sufficient
246     alignment for vectorization and should be used for large allocations.
247 
248   @param n Number of units to allocate
249   @param unit Size of each unit
250   @param p Address of pointer to hold the result.
251 
252   @return An error code: 0 - success, otherwise - failure
253 
254   @sa CeedFree()
255 
256   @ref Backend
257 **/
258 int CeedMallocArray(size_t n, size_t unit, void *p) {
259   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
260   if (ierr)
261     // LCOV_EXCL_START
262     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
263                      "members of size %zd\n", n, unit);
264   // LCOV_EXCL_STOP
265 
266   return 0;
267 }
268 
269 /**
270   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
271 
272   Memory usage can be tracked by the library.
273 
274   @param n    Number of units to allocate
275   @param unit Size of each unit
276   @param p    Address of pointer to hold the result.
277 
278   @return An error code: 0 - success, otherwise - failure
279 
280   @sa CeedFree()
281 
282   @ref Backend
283 **/
284 int CeedCallocArray(size_t n, size_t unit, void *p) {
285   *(void **)p = calloc(n, unit);
286   if (n && unit && !*(void **)p)
287     // LCOV_EXCL_START
288     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
289                      "%zd\n", n, unit);
290   // LCOV_EXCL_STOP
291 
292   return 0;
293 }
294 
295 /**
296   @brief Reallocate an array on the host; use CeedRealloc()
297 
298   Memory usage can be tracked by the library.
299 
300   @param n    Number of units to allocate
301   @param unit Size of each unit
302   @param p    Address of pointer to hold the result.
303 
304   @return An error code: 0 - success, otherwise - failure
305 
306   @sa CeedFree()
307 
308   @ref Backend
309 **/
310 int CeedReallocArray(size_t n, size_t unit, void *p) {
311   *(void **)p = realloc(*(void **)p, n*unit);
312   if (n && unit && !*(void **)p)
313     // LCOV_EXCL_START
314     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
315                      "%zd\n", n, unit);
316   // LCOV_EXCL_STOP
317 
318   return 0;
319 }
320 
321 /** Free memory allocated using CeedMalloc() or CeedCalloc()
322 
323   @param p address of pointer to memory.  This argument is of type void* to
324              avoid needing a cast, but is the address of the pointer (which is
325              zeroed) rather than the pointer.
326 **/
327 int CeedFree(void *p) {
328   free(*(void **)p);
329   *(void **)p = NULL;
330   return 0;
331 }
332 
333 /**
334   @brief Register a Ceed backend
335 
336   @param prefix   Prefix of resources for this backend to respond to.  For
337                     example, the reference backend responds to "/cpu/self".
338   @param init     Initialization function called by CeedInit() when the backend
339                     is selected to drive the requested resource.
340   @param priority Integer priority.  Lower values are preferred in case the
341                     resource requested by CeedInit() has non-unique best prefix
342                     match.
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Backend
347 **/
348 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
349                  unsigned int priority) {
350   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
351     // LCOV_EXCL_START
352     return CeedError(NULL, 1, "Too many backends");
353   // LCOV_EXCL_STOP
354 
355   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
356   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
357   backends[num_backends].init = init;
358   backends[num_backends].priority = priority;
359   num_backends++;
360   return 0;
361 }
362 
363 /**
364   @brief Return debugging status flag
365 
366   @param ceed     Ceed context to get debugging flag
367   @param isDebug  Variable to store debugging flag
368 
369   @return An error code: 0 - success, otherwise - failure
370 
371   @ref Bcakend
372 **/
373 int CeedIsDebug(Ceed ceed, bool *isDebug) {
374   *isDebug = ceed->debug;
375   return 0;
376 }
377 
378 /**
379   @brief Retrieve a parent Ceed context
380 
381   @param ceed        Ceed context to retrieve parent of
382   @param[out] parent Address to save the parent to
383 
384   @return An error code: 0 - success, otherwise - failure
385 
386   @ref Backend
387 **/
388 int CeedGetParent(Ceed ceed, Ceed *parent) {
389   int ierr;
390   if (ceed->parent) {
391     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
392     return 0;
393   }
394   *parent = ceed;
395   return 0;
396 }
397 
398 /**
399   @brief Retrieve a delegate Ceed context
400 
401   @param ceed          Ceed context to retrieve delegate of
402   @param[out] delegate Address to save the delegate to
403 
404   @return An error code: 0 - success, otherwise - failure
405 
406   @ref Backend
407 **/
408 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
409   *delegate = ceed->delegate;
410   return 0;
411 }
412 
413 /**
414   @brief Set a delegate Ceed context
415 
416   This function allows a Ceed context to set a delegate Ceed context. All
417     backend implementations default to the delegate Ceed context, unless
418     overridden.
419 
420   @param ceed           Ceed context to set delegate of
421   @param[out] delegate  Address to set the delegate to
422 
423   @return An error code: 0 - success, otherwise - failure
424 
425   @ref Backend
426 **/
427 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
428   ceed->delegate = delegate;
429   delegate->parent = ceed;
430   return 0;
431 }
432 
433 /**
434   @brief Retrieve a delegate Ceed context for a specific object type
435 
436   @param ceed           Ceed context to retrieve delegate of
437   @param[out] delegate  Address to save the delegate to
438   @param[in] objname    Name of the object type to retrieve delegate for
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref Backend
443 **/
444 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
445   CeedInt ierr;
446 
447   // Check for object delegate
448   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
449     if (!strcmp(objname, ceed->objdelegates->objname)) {
450       *delegate = ceed->objdelegates->delegate;
451       return 0;
452     }
453 
454   // Use default delegate if no object delegate
455   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
456 
457   return 0;
458 }
459 
460 /**
461   @brief Set a delegate Ceed context for a specific object type
462 
463   This function allows a Ceed context to set a delegate Ceed context for a
464     given type of Ceed object. All backend implementations default to the
465     delegate Ceed context for this object. For example,
466     CeedSetObjectDelegate(ceed, refceed, "Basis")
467   uses refceed implementations for all CeedBasis backend functions.
468 
469   @param ceed          Ceed context to set delegate of
470   @param[out] delegate Address to set the delegate to
471   @param[in] objname   Name of the object type to set delegate for
472 
473   @return An error code: 0 - success, otherwise - failure
474 
475   @ref Backend
476 **/
477 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
478   CeedInt ierr;
479   CeedInt count = ceed->objdelegatecount;
480 
481   // Malloc or Realloc
482   if (count) {
483     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
484   } else {
485     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
486   }
487   ceed->objdelegatecount++;
488 
489   // Set object delegate
490   ceed->objdelegates[count].delegate = delegate;
491   size_t slen = strlen(objname) + 1;
492   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
493   memcpy(ceed->objdelegates[count].objname, objname, slen);
494 
495   // Set delegate parent
496   delegate->parent = ceed;
497 
498   return 0;
499 }
500 
501 /**
502   @brief Get the fallback resource for CeedOperators
503 
504   @param ceed          Ceed context
505   @param[out] resource Variable to store fallback resource
506 
507   @return An error code: 0 - success, otherwise - failure
508 
509   @ref Backend
510 **/
511 
512 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
513   *resource = (const char *)ceed->opfallbackresource;
514   return 0;
515 }
516 
517 /**
518   @brief Set the fallback resource for CeedOperators. The current resource, if
519            any, is freed by calling this function. This string is freed upon the
520            destruction of the Ceed context.
521 
522   @param[out] ceed Ceed context
523   @param resource  Fallback resource to set
524 
525   @return An error code: 0 - success, otherwise - failure
526 
527   @ref Backend
528 **/
529 
530 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
531   int ierr;
532 
533   // Free old
534   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
535 
536   // Set new
537   size_t len = strlen(resource);
538   char *tmp;
539   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
540   memcpy(tmp, resource, len+1);
541   ceed->opfallbackresource = tmp;
542 
543   return 0;
544 }
545 
546 /**
547   @brief Get the parent Ceed context associated with a fallback Ceed context
548            for a CeedOperator
549 
550   @param ceed         Ceed context
551   @param[out] parent  Variable to store parent Ceed context
552 
553   @return An error code: 0 - success, otherwise - failure
554 
555   @ref Backend
556 **/
557 
558 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
559   *parent = ceed->opfallbackparent;
560   return 0;
561 }
562 
563 /**
564   @brief Flag Ceed context as deterministic
565 
566   @param ceed     Ceed to flag as deterministic
567 
568   @return An error code: 0 - success, otherwise - failure
569 
570   @ref Backend
571 **/
572 
573 int CeedSetDeterministic(Ceed ceed, bool isDeterministic) {
574   ceed->isDeterministic = isDeterministic;
575   return 0;
576 }
577 
578 /**
579   @brief Set a backend function
580 
581   This function is used for a backend to set the function associated with
582   the Ceed objects. For example,
583     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
584   sets the backend implementation of 'CeedVectorCreate' and
585     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
586   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
587   is not required for the object type ("Basis" vs "CeedBasis").
588 
589   @param ceed           Ceed context for error handling
590   @param type           Type of Ceed object to set function for
591   @param[out] object    Ceed object to set function for
592   @param fname          Name of function to set
593   @param f              Function to set
594 
595   @return An error code: 0 - success, otherwise - failure
596 
597   @ref Backend
598 **/
599 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
600                            const char *fname, int (*f)()) {
601   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
602 
603   // Build lookup name
604   if (strcmp(type, "Ceed"))
605     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
606   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
607   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
608 
609   // Find and use offset
610   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
611     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
612       size_t offset = ceed->foffsets[i].offset;
613       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
614       *fpointer = f;
615       return 0;
616     }
617 
618   // LCOV_EXCL_START
619   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
620                    "object '%s'", fname, type);
621   // LCOV_EXCL_STOP
622 }
623 
624 /**
625   @brief Retrieve backend data for a Ceed context
626 
627   @param ceed      Ceed context to retrieve data of
628   @param[out] data Address to save data to
629 
630   @return An error code: 0 - success, otherwise - failure
631 
632   @ref Backend
633 **/
634 int CeedGetData(Ceed ceed, void **data) {
635   *data = ceed->data;
636   return 0;
637 }
638 
639 /**
640   @brief Set backend data for a Ceed context
641 
642   @param ceed           Ceed context to set data of
643   @param data           Address of data to set
644 
645   @return An error code: 0 - success, otherwise - failure
646 
647   @ref Backend
648 **/
649 int CeedSetData(Ceed ceed, void **data) {
650   ceed->data = *data;
651   return 0;
652 }
653 
654 /// @}
655 
656 /// ----------------------------------------------------------------------------
657 /// Ceed Public API
658 /// ----------------------------------------------------------------------------
659 /// @addtogroup CeedUser
660 /// @{
661 
662 /**
663   @brief Initialize a \ref Ceed context to use the specified resource.
664 
665   @param resource  Resource to use, e.g., "/cpu/self"
666   @param ceed      The library context
667   @sa CeedRegister() CeedDestroy()
668 
669   @return An error code: 0 - success, otherwise - failure
670 
671   @ref User
672 **/
673 int CeedInit(const char *resource, Ceed *ceed) {
674   int ierr;
675   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
676 
677   // Find matching backend
678   if (!resource)
679     // LCOV_EXCL_START
680     return CeedError(NULL, 1, "No resource provided");
681   // LCOV_EXCL_STOP
682 
683   for (size_t i=0; i<num_backends; i++) {
684     size_t n;
685     const char *prefix = backends[i].prefix;
686     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
687     priority = backends[i].priority;
688     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
689       matchlen = n;
690       matchpriority = priority;
691       matchidx = i;
692     }
693   }
694   if (!matchlen)
695     // LCOV_EXCL_START
696     return CeedError(NULL, 1, "No suitable backend: %s", resource);
697   // LCOV_EXCL_STOP
698 
699   // Setup Ceed
700   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
701   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
702   if (!ceed_error_handler)
703     ceed_error_handler = "abort";
704   if (!strcmp(ceed_error_handler, "exit"))
705     (*ceed)->Error = CeedErrorExit;
706   else
707     (*ceed)->Error = CeedErrorAbort;
708   (*ceed)->refcount = 1;
709   (*ceed)->data = NULL;
710 
711   // Set lookup table
712   foffset foffsets[] = {
713     CEED_FTABLE_ENTRY(Ceed, Error),
714     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
715     CEED_FTABLE_ENTRY(Ceed, Destroy),
716     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
717     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
718     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
719     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
720     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
721     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
722     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
723     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
724     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
725     CEED_FTABLE_ENTRY(CeedVector, SetArray),
726     CEED_FTABLE_ENTRY(CeedVector, SetValue),
727     CEED_FTABLE_ENTRY(CeedVector, GetArray),
728     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
729     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
730     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
731     CEED_FTABLE_ENTRY(CeedVector, Norm),
732     CEED_FTABLE_ENTRY(CeedVector, Destroy),
733     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
734     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
735     CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
736     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
737     CEED_FTABLE_ENTRY(CeedBasis, Apply),
738     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
739     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
740     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
741     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
742     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
743     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
744     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
745     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
746     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
747     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
748     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
749     CEED_FTABLE_ENTRY(CeedOperator, Apply),
750     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
751     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
752     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
753     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
754     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
755     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
756   };
757 
758   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
759   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
760 
761   // Set fallback for advanced CeedOperator functions
762   const char fallbackresource[] = "/cpu/self/ref/serial";
763   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
764   CeedChk(ierr);
765 
766   // Record env variables CEED_DEBUG or DBG
767   (*ceed)->debug = !!getenv("CEED_DEBUG") || !!getenv("DBG");
768 
769   // Backend specific setup
770   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
771 
772   // Copy resource prefix, if backend setup sucessful
773   size_t len = strlen(backends[matchidx].prefix);
774   char *tmp;
775   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
776   memcpy(tmp, backends[matchidx].prefix, len+1);
777   (*ceed)->resource = tmp;
778 
779   return 0;
780 }
781 
782 /**
783   @brief Get the full resource name for a Ceed context
784 
785   @param ceed            Ceed context to get resource name of
786   @param[out] resource   Variable to store resource name
787 
788   @return An error code: 0 - success, otherwise - failure
789 
790   @ref User
791 **/
792 
793 int CeedGetResource(Ceed ceed, const char **resource) {
794   *resource = (const char *)ceed->resource;
795   return 0;
796 }
797 
798 /**
799   @brief Return Ceed context preferred memory type
800 
801   @param ceed      Ceed context to get preferred memory type of
802   @param[out] type Address to save preferred memory type to
803 
804   @return An error code: 0 - success, otherwise - failure
805 
806   @ref User
807 **/
808 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
809   int ierr;
810 
811   if (ceed->GetPreferredMemType) {
812     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
813   } else {
814     Ceed delegate;
815     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
816 
817     if (delegate) {
818       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
819     } else {
820       *type = CEED_MEM_HOST;
821     }
822   }
823 
824   return 0;
825 }
826 
827 /**
828   @brief Get deterministic status of Ceed
829 
830   @param[in] ceed              Ceed
831   @param[out] isDeterministic  Variable to store deterministic status
832 
833   @return An error code: 0 - success, otherwise - failure
834 
835   @ref User
836 **/
837 int CeedIsDeterministic(Ceed ceed, bool *isDeterministic) {
838   *isDeterministic = ceed->isDeterministic;
839   return 0;
840 }
841 
842 /**
843   @brief View a Ceed
844 
845   @param[in] ceed          Ceed to view
846   @param[in] stream        Filestream to write to
847 
848   @return An error code: 0 - success, otherwise - failure
849 
850   @ref User
851 **/
852 int CeedView(Ceed ceed, FILE *stream) {
853   int ierr;
854   CeedMemType memtype;
855 
856   ierr = CeedGetPreferredMemType(ceed, &memtype); CeedChk(ierr);
857 
858   fprintf(stream, "Ceed\n"
859           "  Ceed Resource: %s\n"
860           "  Preferred MemType: %s\n",
861           ceed->resource, CeedMemTypes[memtype]);
862 
863   return 0;
864 }
865 
866 /**
867   @brief Destroy a Ceed context
868 
869   @param ceed Address of Ceed context to destroy
870 
871   @return An error code: 0 - success, otherwise - failure
872 
873   @ref User
874 **/
875 int CeedDestroy(Ceed *ceed) {
876   int ierr;
877   if (!*ceed || --(*ceed)->refcount > 0)
878     return 0;
879 
880   if ((*ceed)->delegate) {
881     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
882   }
883 
884   if ((*ceed)->objdelegatecount > 0) {
885     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
886       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
887       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
888     }
889     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
890   }
891 
892   if ((*ceed)->Destroy) {
893     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
894   }
895 
896   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
897   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
898   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
899   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
900   ierr = CeedFree(ceed); CeedChk(ierr);
901   return 0;
902 }
903 
904 /**
905   @brief Error handling implementation; use \ref CeedError instead.
906 
907   @ref Developer
908 **/
909 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
910                   int ecode, const char *format, ...) {
911   va_list args;
912   int retval;
913   va_start(args, format);
914   if (ceed) {
915     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
916   } else {
917     // This function doesn't actually return
918     // LCOV_EXCL_START
919     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
920   }
921   va_end(args);
922   return retval;
923   // LCOV_EXCL_STOP
924 }
925 
926 /// @}
927