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