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