xref: /libCEED/interface/ceed.c (revision 0436c2ad4dd95c9ccd89f5346568014600de10a7)
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, Destroy),
394     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
395     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
396     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
397     CEED_FTABLE_ENTRY(CeedBasis, Apply),
398     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
399     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
400     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
401     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
402     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
403     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
404     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
405     CEED_FTABLE_ENTRY(CeedOperator, Apply),
406     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
407     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
408     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
409     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
410     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
411     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
412   };
413 
414   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
415   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
416 
417   // Set fallback for advanced CeedOperator functions
418   const char fallbackresource[] = "/cpu/self/ref/serial";
419   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
420   CeedChk(ierr);
421 
422   // Backend specific setup
423   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
424 
425   // Copy resource prefix, if backend setup sucessful
426   size_t len = strlen(backends[matchidx].prefix);
427   char *tmp;
428   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
429   memcpy(tmp, backends[matchidx].prefix, len+1);
430   (*ceed)->resource = tmp;
431 
432   return 0;
433 }
434 
435 /**
436   @brief Retrieve a parent Ceed context
437 
438   @param ceed        Ceed context to retrieve parent of
439   @param[out] parent Address to save the parent to
440 
441   @return An error code: 0 - success, otherwise - failure
442 
443   @ref Developer
444 **/
445 int CeedGetParent(Ceed ceed, Ceed *parent) {
446   int ierr;
447   if (ceed->parent) {
448     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
449     return 0;
450   }
451   *parent = ceed;
452   return 0;
453 }
454 
455 /**
456   @brief Retrieve a delegate Ceed context
457 
458   @param ceed          Ceed context to retrieve delegate of
459   @param[out] delegate Address to save the delegate to
460 
461   @return An error code: 0 - success, otherwise - failure
462 
463   @ref Developer
464 **/
465 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
466   *delegate = ceed->delegate;
467   return 0;
468 }
469 
470 /**
471   @brief Set a delegate Ceed context
472 
473   This function allows a Ceed context to set a delegate Ceed context. All
474     backend implementations default to the delegate Ceed context, unless
475     overridden.
476 
477   @param ceed           Ceed context to set delegate of
478   @param[out] delegate  Address to set the delegate to
479 
480   @return An error code: 0 - success, otherwise - failure
481 
482   @ref Advanced
483 **/
484 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
485   ceed->delegate = delegate;
486   delegate->parent = ceed;
487   return 0;
488 }
489 
490 /**
491   @brief Retrieve a delegate Ceed context for a specific object type
492 
493   @param ceed           Ceed context to retrieve delegate of
494   @param[out] delegate  Address to save the delegate to
495   @param[in] objname    Name of the object type to retrieve delegate for
496 
497   @return An error code: 0 - success, otherwise - failure
498 
499   @ref Developer
500 **/
501 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
502   CeedInt ierr;
503 
504   // Check for object delegate
505   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
506     if (!strcmp(objname, ceed->objdelegates->objname)) {
507       *delegate = ceed->objdelegates->delegate;
508       return 0;
509     }
510 
511   // Use default delegate if no object delegate
512   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
513 
514   return 0;
515 }
516 
517 /**
518   @brief Set a delegate Ceed context for a specific object type
519 
520   This function allows a Ceed context to set a delegate Ceed context for a
521     given type of Ceed object. All backend implementations default to the
522     delegate Ceed context for this object. For example,
523     CeedSetObjectDelegate(ceed, refceed, "Basis")
524   uses refceed implementations for all CeedBasis backend functions.
525 
526   @param ceed          Ceed context to set delegate of
527   @param[out] delegate Address to set the delegate to
528   @param[in] objname   Name of the object type to set delegate for
529 
530   @return An error code: 0 - success, otherwise - failure
531 
532   @ref Advanced
533 **/
534 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
535   CeedInt ierr;
536   CeedInt count = ceed->objdelegatecount;
537 
538   // Malloc or Realloc
539   if (count) {
540     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
541   } else {
542     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
543   }
544   ceed->objdelegatecount++;
545 
546   // Set object delegate
547   ceed->objdelegates[count].delegate = delegate;
548   size_t slen = strlen(objname) + 1;
549   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
550   memcpy(ceed->objdelegates[count].objname, objname, slen);
551 
552   // Set delegate parent
553   delegate->parent = ceed;
554 
555   return 0;
556 }
557 
558 /**
559   @brief Set the fallback resource for CeedOperators. The current resource, if
560            any, is freed by calling this function. This string is freed upon the
561            destruction of the Ceed context.
562 
563   @param[out] ceed Ceed context
564   @param resource  Fallback resource to set
565 
566   @return An error code: 0 - success, otherwise - failure
567 
568   @ref Advanced
569 **/
570 
571 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
572   int ierr;
573 
574   // Free old
575   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
576 
577   // Set new
578   size_t len = strlen(resource);
579   char *tmp;
580   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
581   memcpy(tmp, resource, len+1);
582   ceed->opfallbackresource = tmp;
583 
584   return 0;
585 }
586 
587 /**
588   @brief Get the fallback resource for CeedOperators
589 
590   @param ceed          Ceed context
591   @param[out] resource Variable to store fallback resource
592 
593   @return An error code: 0 - success, otherwise - failure
594 
595   @ref Advanced
596 **/
597 
598 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
599   *resource = (const char *)ceed->opfallbackresource;
600   return 0;
601 }
602 
603 /**
604   @brief Get the parent Ceed context associated with a fallback Ceed context
605            for a CeedOperator
606 
607   @param ceed            Ceed context
608   @param[out] ceed       Variable to store parent Ceed context
609 
610   @return An error code: 0 - success, otherwise - failure
611 
612   @ref Advanced
613 **/
614 
615 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
616   *parent = ceed->opfallbackparent;
617   return 0;
618 }
619 
620 /**
621   @brief Return Ceed context preferred memory type
622 
623   @param ceed      Ceed context to get preferred memory type of
624   @param[out] type Address to save preferred memory type to
625 
626   @return An error code: 0 - success, otherwise - failure
627 
628   @ref Basic
629 **/
630 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
631   int ierr;
632 
633   if (ceed->GetPreferredMemType) {
634     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
635   } else {
636     Ceed delegate;
637     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
638 
639     if (delegate) {
640       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
641     } else {
642       *type = CEED_MEM_HOST;
643     }
644   }
645 
646   return 0;
647 }
648 
649 /**
650   @brief Set a backend function
651 
652   This function is used for a backend to set the function associated with
653   the Ceed objects. For example,
654     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
655   sets the backend implementation of 'CeedVectorCreate' and
656     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
657   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
658   is not required for the object type ("Basis" vs "CeedBasis").
659 
660   @param ceed           Ceed context for error handling
661   @param type           Type of Ceed object to set function for
662   @param[out] object    Ceed object to set function for
663   @param fname          Name of function to set
664   @param f              Function to set
665 
666   @return An error code: 0 - success, otherwise - failure
667 
668   @ref Advanced
669 **/
670 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
671                            const char *fname, int (*f)()) {
672   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
673 
674   // Build lookup name
675   if (strcmp(type, "Ceed"))
676     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
677   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
678   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
679 
680   // Find and use offset
681   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
682     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
683       size_t offset = ceed->foffsets[i].offset;
684       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
685       *fpointer = f;
686       return 0;
687     }
688 
689   // LCOV_EXCL_START
690   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
691                    "object '%s'", fname, type);
692   // LCOV_EXCL_STOP
693 }
694 
695 /**
696   @brief Retrieve backend data for a Ceed context
697 
698   @param ceed      Ceed context to retrieve data of
699   @param[out] data Address to save data to
700 
701   @return An error code: 0 - success, otherwise - failure
702 
703   @ref Advanced
704 **/
705 int CeedGetData(Ceed ceed, void **data) {
706   *data = ceed->data;
707   return 0;
708 }
709 
710 /**
711   @brief Set backend data for a Ceed context
712 
713   @param ceed           Ceed context to set data of
714   @param data           Address of data to set
715 
716   @return An error code: 0 - success, otherwise - failure
717 
718   @ref Advanced
719 **/
720 int CeedSetData(Ceed ceed, void **data) {
721   ceed->data = *data;
722   return 0;
723 }
724 
725 /**
726   @brief Get the full resource name for a Ceed context
727 
728   @param ceed            Ceed context to get resource name of
729   @param[out] resource   Variable to store resource name
730 
731   @return An error code: 0 - success, otherwise - failure
732 
733   @ref Basic
734 **/
735 
736 int CeedGetResource(Ceed ceed, const char **resource) {
737   *resource = (const char *)ceed->resource;
738   return 0;
739 }
740 
741 /**
742   @brief Destroy a Ceed context
743 
744   @param ceed Address of Ceed context to destroy
745 
746   @return An error code: 0 - success, otherwise - failure
747 
748   @ref Basic
749 **/
750 int CeedDestroy(Ceed *ceed) {
751   int ierr;
752 
753   if (!*ceed || --(*ceed)->refcount > 0)
754     return 0;
755   if ((*ceed)->delegate) {
756     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
757   }
758   if ((*ceed)->objdelegatecount > 0) {
759     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
760       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
761       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
762     }
763     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
764   }
765   if ((*ceed)->Destroy) {
766     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
767   }
768   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
769   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
770   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
771   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
772   ierr = CeedFree(ceed); CeedChk(ierr);
773   return 0;
774 }
775 
776 /// @}
777