xref: /libCEED/interface/ceed.c (revision b0812a454286a178b5527bf1d1f1a428af10126c)
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 Ceed
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 CEED_REQUEST_IMMEDIATE.
92 
93   @sa CEED_REQUEST_IMMEDIATE
94  */
95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
96 
97 /**
98   @brief Error handling implementation; use \ref CeedError instead.
99 
100   @ref Developer
101 **/
102 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
103                   int ecode, const char *format, ...) {
104   va_list args;
105   int retval;
106   va_start(args, format);
107   if (ceed) {
108     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
109   } else {
110     // This function doesn't actually return
111     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
112   }
113   va_end(args);
114   return retval;
115 }
116 
117 /**
118   @brief Error handler that returns without printing anything.
119 
120   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
121 
122   @ref Developer
123 **/
124 // LCOV_EXCL_START
125 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
126                     const char *func, int ecode, const char *format,
127                     va_list args) {
128   return ecode;
129 }
130 // LCOV_EXCL_STOP
131 
132 /**
133   @brief Error handler that prints to stderr and aborts
134 
135   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
136 
137   @ref Developer
138 **/
139 // LCOV_EXCL_START
140 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
141                    const char *func, int ecode, const char *format,
142                    va_list args) {
143   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
144   vfprintf(stderr, format, args);
145   fprintf(stderr, "\n");
146   abort();
147   return ecode;
148 }
149 // LCOV_EXCL_STOP
150 
151 /**
152   @brief Error handler that prints to stderr and exits
153 
154   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
155 
156   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
157   handlers (e.g., as used by gcov) are run.
158 
159   @ref Developer
160 **/
161 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, const char *func,
162                   int ecode, const char *format, va_list args) {
163   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
164   vfprintf(stderr, format, args);
165   fprintf(stderr, "\n");
166   exit(ecode);
167   return ecode;
168 }
169 
170 /**
171   @brief Set error handler
172 
173   A default error handler is set in CeedInit().  Use this function to change
174   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
175   error handler.
176 
177   @ref Developer
178 **/
179 int CeedSetErrorHandler(Ceed ceed,
180                         int (eh)(Ceed, const char *, int, const char *,
181                                  int, const char *, va_list)) {
182   ceed->Error = eh;
183   return 0;
184 }
185 
186 /**
187   @brief Register a Ceed backend
188 
189   @param prefix   Prefix of resources for this backend to respond to.  For
190                     example, the reference backend responds to "/cpu/self".
191   @param init     Initialization function called by CeedInit() when the backend
192                     is selected to drive the requested resource.
193   @param priority Integer priority.  Lower values are preferred in case the
194                     resource requested by CeedInit() has non-unique best prefix
195                     match.
196 
197   @return An error code: 0 - success, otherwise - failure
198 
199   @ref Advanced
200 **/
201 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
202                  unsigned int priority) {
203   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
204     // LCOV_EXCL_START
205     return CeedError(NULL, 1, "Too many backends");
206   // LCOV_EXCL_STOP
207 
208   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
209   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
210   backends[num_backends].init = init;
211   backends[num_backends].priority = priority;
212   num_backends++;
213   return 0;
214 }
215 
216 /**
217   @brief Allocate an array on the host; use CeedMalloc()
218 
219   Memory usage can be tracked by the library.  This ensures sufficient
220     alignment for vectorization and should be used for large allocations.
221 
222   @param n Number of units to allocate
223   @param unit Size of each unit
224   @param p Address of pointer to hold the result.
225 
226   @return An error code: 0 - success, otherwise - failure
227 
228   @sa CeedFree()
229 
230   @ref Advanced
231 **/
232 int CeedMallocArray(size_t n, size_t unit, void *p) {
233   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
234   if (ierr)
235     // LCOV_EXCL_START
236     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
237                      "members of size %zd\n", n, unit);
238   // LCOV_EXCL_STOP
239 
240   return 0;
241 }
242 
243 /**
244   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
245 
246   Memory usage can be tracked by the library.
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 Advanced
257 **/
258 int CeedCallocArray(size_t n, size_t unit, void *p) {
259   *(void **)p = calloc(n, unit);
260   if (n && unit && !*(void **)p)
261     // LCOV_EXCL_START
262     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
263                      "%zd\n", n, unit);
264   // LCOV_EXCL_STOP
265 
266   return 0;
267 }
268 
269 /**
270   @brief Reallocate an array on the host; use CeedRealloc()
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 Advanced
283 **/
284 int CeedReallocArray(size_t n, size_t unit, void *p) {
285   *(void **)p = realloc(*(void **)p, n*unit);
286   if (n && unit && !*(void **)p)
287     // LCOV_EXCL_START
288     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
289                      "%zd\n", n, unit);
290   // LCOV_EXCL_STOP
291 
292   return 0;
293 }
294 
295 /** Free memory allocated using CeedMalloc() or CeedCalloc()
296 
297   @param p address of pointer to memory.  This argument is of type void* to
298              avoid needing a cast, but is the address of the pointer (which is
299              zeroed) rather than the pointer.
300 **/
301 int CeedFree(void *p) {
302   free(*(void **)p);
303   *(void **)p = NULL;
304   return 0;
305 }
306 
307 /**
308   @brief Wait for a CeedRequest to complete.
309 
310   Calling CeedRequestWait on a NULL request is a no-op.
311 
312   @param req Address of CeedRequest to wait for; zeroed on completion.
313 
314   @return An error code: 0 - success, otherwise - failure
315 
316   @ref Advanced
317 **/
318 int CeedRequestWait(CeedRequest *req) {
319   if (!*req)
320     return 0;
321   return CeedError(NULL, 2, "CeedRequestWait not implemented");
322 }
323 
324 /**
325   @brief Initialize a \ref Ceed context to use the specified resource.
326 
327   @param resource  Resource to use, e.g., "/cpu/self"
328   @param ceed      The library context
329   @sa CeedRegister() CeedDestroy()
330 
331   @return An error code: 0 - success, otherwise - failure
332 
333   @ref Basic
334 **/
335 int CeedInit(const char *resource, Ceed *ceed) {
336   int ierr;
337   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
338 
339   // Find matching backend
340   if (!resource)
341     // LCOV_EXCL_START
342     return CeedError(NULL, 1, "No resource provided");
343   // LCOV_EXCL_STOP
344 
345   for (size_t i=0; i<num_backends; i++) {
346     size_t n;
347     const char *prefix = backends[i].prefix;
348     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
349     priority = backends[i].priority;
350     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
351       matchlen = n;
352       matchpriority = priority;
353       matchidx = i;
354     }
355   }
356   if (!matchlen)
357     // LCOV_EXCL_START
358     return CeedError(NULL, 1, "No suitable backend");
359   // LCOV_EXCL_STOP
360 
361   // Setup Ceed
362   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
363   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
364   if (!ceed_error_handler)
365     ceed_error_handler = "abort";
366   if (!strcmp(ceed_error_handler, "exit"))
367     (*ceed)->Error = CeedErrorExit;
368   else
369     (*ceed)->Error = CeedErrorAbort;
370   (*ceed)->refcount = 1;
371   (*ceed)->data = NULL;
372 
373   // Set lookup table
374   foffset foffsets[] = {
375     CEED_FTABLE_ENTRY(Ceed, Error),
376     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
377     CEED_FTABLE_ENTRY(Ceed, Destroy),
378     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
379     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
380     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
381     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
382     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
383     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
384     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
385     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
386     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
387     CEED_FTABLE_ENTRY(CeedVector, SetArray),
388     CEED_FTABLE_ENTRY(CeedVector, SetValue),
389     CEED_FTABLE_ENTRY(CeedVector, GetArray),
390     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
391     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
392     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
393     CEED_FTABLE_ENTRY(CeedVector, Norm),
394     CEED_FTABLE_ENTRY(CeedVector, Destroy),
395     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
396     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
397     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
398     CEED_FTABLE_ENTRY(CeedBasis, Apply),
399     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
400     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
401     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
402     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
403     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
404     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
405     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
406     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
407     CEED_FTABLE_ENTRY(CeedOperator, Apply),
408     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
409     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
410     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
411     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
412     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
413     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
414   };
415 
416   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
417   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
418 
419   // Set fallback for advanced CeedOperator functions
420   const char fallbackresource[] = "/cpu/self/ref/serial";
421   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
422   CeedChk(ierr);
423 
424   // Backend specific setup
425   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
426 
427   // Copy resource prefix, if backend setup sucessful
428   size_t len = strlen(backends[matchidx].prefix);
429   char *tmp;
430   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
431   memcpy(tmp, backends[matchidx].prefix, len+1);
432   (*ceed)->resource = tmp;
433 
434   return 0;
435 }
436 
437 /**
438   @brief Retrieve a parent Ceed context
439 
440   @param ceed        Ceed context to retrieve parent of
441   @param[out] parent Address to save the parent to
442 
443   @return An error code: 0 - success, otherwise - failure
444 
445   @ref Developer
446 **/
447 int CeedGetParent(Ceed ceed, Ceed *parent) {
448   int ierr;
449   if (ceed->parent) {
450     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
451     return 0;
452   }
453   *parent = ceed;
454   return 0;
455 }
456 
457 /**
458   @brief Retrieve a delegate Ceed context
459 
460   @param ceed          Ceed context to retrieve delegate of
461   @param[out] delegate Address to save the delegate to
462 
463   @return An error code: 0 - success, otherwise - failure
464 
465   @ref Developer
466 **/
467 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
468   *delegate = ceed->delegate;
469   return 0;
470 }
471 
472 /**
473   @brief Set a delegate Ceed context
474 
475   This function allows a Ceed context to set a delegate Ceed context. All
476     backend implementations default to the delegate Ceed context, unless
477     overridden.
478 
479   @param ceed           Ceed context to set delegate of
480   @param[out] delegate  Address to set the delegate to
481 
482   @return An error code: 0 - success, otherwise - failure
483 
484   @ref Advanced
485 **/
486 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
487   ceed->delegate = delegate;
488   delegate->parent = ceed;
489   return 0;
490 }
491 
492 /**
493   @brief Retrieve a delegate Ceed context for a specific object type
494 
495   @param ceed           Ceed context to retrieve delegate of
496   @param[out] delegate  Address to save the delegate to
497   @param[in] objname    Name of the object type to retrieve delegate for
498 
499   @return An error code: 0 - success, otherwise - failure
500 
501   @ref Developer
502 **/
503 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
504   CeedInt ierr;
505 
506   // Check for object delegate
507   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
508     if (!strcmp(objname, ceed->objdelegates->objname)) {
509       *delegate = ceed->objdelegates->delegate;
510       return 0;
511     }
512 
513   // Use default delegate if no object delegate
514   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
515 
516   return 0;
517 }
518 
519 /**
520   @brief Set a delegate Ceed context for a specific object type
521 
522   This function allows a Ceed context to set a delegate Ceed context for a
523     given type of Ceed object. All backend implementations default to the
524     delegate Ceed context for this object. For example,
525     CeedSetObjectDelegate(ceed, refceed, "Basis")
526   uses refceed implementations for all CeedBasis backend functions.
527 
528   @param ceed          Ceed context to set delegate of
529   @param[out] delegate Address to set the delegate to
530   @param[in] objname   Name of the object type to set delegate for
531 
532   @return An error code: 0 - success, otherwise - failure
533 
534   @ref Advanced
535 **/
536 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
537   CeedInt ierr;
538   CeedInt count = ceed->objdelegatecount;
539 
540   // Malloc or Realloc
541   if (count) {
542     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
543   } else {
544     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
545   }
546   ceed->objdelegatecount++;
547 
548   // Set object delegate
549   ceed->objdelegates[count].delegate = delegate;
550   size_t slen = strlen(objname) + 1;
551   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
552   memcpy(ceed->objdelegates[count].objname, objname, slen);
553 
554   // Set delegate parent
555   delegate->parent = ceed;
556 
557   return 0;
558 }
559 
560 /**
561   @brief Set the fallback resource for CeedOperators. The current resource, if
562            any, is freed by calling this function. This string is freed upon the
563            destruction of the Ceed context.
564 
565   @param[out] ceed Ceed context
566   @param resource  Fallback resource to set
567 
568   @return An error code: 0 - success, otherwise - failure
569 
570   @ref Advanced
571 **/
572 
573 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
574   int ierr;
575 
576   // Free old
577   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
578 
579   // Set new
580   size_t len = strlen(resource);
581   char *tmp;
582   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
583   memcpy(tmp, resource, len+1);
584   ceed->opfallbackresource = tmp;
585 
586   return 0;
587 }
588 
589 /**
590   @brief Get the fallback resource for CeedOperators
591 
592   @param ceed          Ceed context
593   @param[out] resource Variable to store fallback resource
594 
595   @return An error code: 0 - success, otherwise - failure
596 
597   @ref Advanced
598 **/
599 
600 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
601   *resource = (const char *)ceed->opfallbackresource;
602   return 0;
603 }
604 
605 /**
606   @brief Get the parent Ceed context associated with a fallback Ceed context
607            for a CeedOperator
608 
609   @param ceed         Ceed context
610   @param[out] parent  Variable to store parent Ceed context
611 
612   @return An error code: 0 - success, otherwise - failure
613 
614   @ref Advanced
615 **/
616 
617 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
618   *parent = ceed->opfallbackparent;
619   return 0;
620 }
621 
622 /**
623   @brief Return Ceed context preferred memory type
624 
625   @param ceed      Ceed context to get preferred memory type of
626   @param[out] type Address to save preferred memory type to
627 
628   @return An error code: 0 - success, otherwise - failure
629 
630   @ref Basic
631 **/
632 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
633   int ierr;
634 
635   if (ceed->GetPreferredMemType) {
636     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
637   } else {
638     Ceed delegate;
639     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
640 
641     if (delegate) {
642       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
643     } else {
644       *type = CEED_MEM_HOST;
645     }
646   }
647 
648   return 0;
649 }
650 
651 /**
652   @brief Set a backend function
653 
654   This function is used for a backend to set the function associated with
655   the Ceed objects. For example,
656     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
657   sets the backend implementation of 'CeedVectorCreate' and
658     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
659   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
660   is not required for the object type ("Basis" vs "CeedBasis").
661 
662   @param ceed           Ceed context for error handling
663   @param type           Type of Ceed object to set function for
664   @param[out] object    Ceed object to set function for
665   @param fname          Name of function to set
666   @param f              Function to set
667 
668   @return An error code: 0 - success, otherwise - failure
669 
670   @ref Advanced
671 **/
672 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
673                            const char *fname, int (*f)()) {
674   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
675 
676   // Build lookup name
677   if (strcmp(type, "Ceed"))
678     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
679   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
680   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
681 
682   // Find and use offset
683   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
684     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
685       size_t offset = ceed->foffsets[i].offset;
686       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
687       *fpointer = f;
688       return 0;
689     }
690 
691   // LCOV_EXCL_START
692   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
693                    "object '%s'", fname, type);
694   // LCOV_EXCL_STOP
695 }
696 
697 /**
698   @brief Retrieve backend data for a Ceed context
699 
700   @param ceed      Ceed context to retrieve data of
701   @param[out] data Address to save data to
702 
703   @return An error code: 0 - success, otherwise - failure
704 
705   @ref Advanced
706 **/
707 int CeedGetData(Ceed ceed, void **data) {
708   *data = ceed->data;
709   return 0;
710 }
711 
712 /**
713   @brief Set backend data for a Ceed context
714 
715   @param ceed           Ceed context to set data of
716   @param data           Address of data to set
717 
718   @return An error code: 0 - success, otherwise - failure
719 
720   @ref Advanced
721 **/
722 int CeedSetData(Ceed ceed, void **data) {
723   ceed->data = *data;
724   return 0;
725 }
726 
727 /**
728   @brief Get the full resource name for a Ceed context
729 
730   @param ceed            Ceed context to get resource name of
731   @param[out] resource   Variable to store resource name
732 
733   @return An error code: 0 - success, otherwise - failure
734 
735   @ref Basic
736 **/
737 
738 int CeedGetResource(Ceed ceed, const char **resource) {
739   *resource = (const char *)ceed->resource;
740   return 0;
741 }
742 
743 /**
744   @brief Destroy a Ceed context
745 
746   @param ceed Address of Ceed context to destroy
747 
748   @return An error code: 0 - success, otherwise - failure
749 
750   @ref Basic
751 **/
752 int CeedDestroy(Ceed *ceed) {
753   int ierr;
754   if (!*ceed || --(*ceed)->refcount > 0)
755     return 0;
756 
757   if ((*ceed)->delegate) {
758     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
759   }
760 
761   if ((*ceed)->objdelegatecount > 0) {
762     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
763       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
764       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
765     }
766     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
767   }
768 
769   if ((*ceed)->Destroy) {
770     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
771   }
772 
773   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
774   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
775   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
776   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
777   ierr = CeedFree(ceed); CeedChk(ierr);
778   return 0;
779 }
780 
781 /// @}
782