xref: /libCEED/rust/libceed-sys/c-src/interface/ceed.c (revision c00ee0d77279f2bf063fd372868f15a2482829af)
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 zeroed)
299 /// rather than the pointer.
300 int CeedFree(void *p) {
301   free(*(void **)p);
302   *(void **)p = NULL;
303   return 0;
304 }
305 
306 /**
307   @brief Wait for a CeedRequest to complete.
308 
309   Calling CeedRequestWait on a NULL request is a no-op.
310 
311   @param req Address of CeedRequest to wait for; zeroed on completion.
312 
313   @return An error code: 0 - success, otherwise - failure
314 
315   @ref Advanced
316 **/
317 int CeedRequestWait(CeedRequest *req) {
318   if (!*req)
319     return 0;
320   return CeedError(NULL, 2, "CeedRequestWait not implemented");
321 }
322 
323 /**
324   @brief Initialize a \ref Ceed to use the specified resource.
325 
326   @param resource  Resource to use, e.g., "/cpu/self"
327   @param ceed The library context
328   @sa CeedRegister() CeedDestroy()
329 
330   @return An error code: 0 - success, otherwise - failure
331 
332   @ref Basic
333 **/
334 int CeedInit(const char *resource, Ceed *ceed) {
335   int ierr;
336   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
337 
338   // Find matching backend
339   if (!resource)
340     // LCOV_EXCL_START
341     return CeedError(NULL, 1, "No resource provided");
342   // LCOV_EXCL_STOP
343 
344   for (size_t i=0; i<num_backends; i++) {
345     size_t n;
346     const char *prefix = backends[i].prefix;
347     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
348     priority = backends[i].priority;
349     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
350       matchlen = n;
351       matchpriority = priority;
352       matchidx = i;
353     }
354   }
355   if (!matchlen)
356     // LCOV_EXCL_START
357     return CeedError(NULL, 1, "No suitable backend");
358   // LCOV_EXCL_STOP
359 
360   // Setup Ceed
361   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
362   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
363   if (!ceed_error_handler)
364     ceed_error_handler = "abort";
365   if (!strcmp(ceed_error_handler, "exit"))
366     (*ceed)->Error = CeedErrorExit;
367   else
368     (*ceed)->Error = CeedErrorAbort;
369   (*ceed)->refcount = 1;
370   (*ceed)->data = NULL;
371 
372   // Set lookup table
373   foffset foffsets[] = {
374     CEED_FTABLE_ENTRY(Ceed, Error),
375     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
376     CEED_FTABLE_ENTRY(Ceed, Destroy),
377     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
378     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
379     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
380     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
381     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
382     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
383     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
384     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
385     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
386     CEED_FTABLE_ENTRY(CeedVector, SetArray),
387     CEED_FTABLE_ENTRY(CeedVector, SetValue),
388     CEED_FTABLE_ENTRY(CeedVector, GetArray),
389     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
390     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
391     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
392     CEED_FTABLE_ENTRY(CeedVector, Destroy),
393     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
394     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
395     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
396     CEED_FTABLE_ENTRY(CeedBasis, Apply),
397     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
398     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
399     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
400     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
401     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
402     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearQFunction),
403     CEED_FTABLE_ENTRY(CeedOperator, AssembleLinearDiagonal),
404     CEED_FTABLE_ENTRY(CeedOperator, Apply),
405     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
406     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
407     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
408     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
409     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
410     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
411   };
412 
413   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
414   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
415 
416   // Set fallback for advanced CeedOperator functions
417   const char fallbackresource[] = "/cpu/self/ref/serial";
418   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
419   CeedChk(ierr);
420 
421   // Backend specific setup
422   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
423 
424   // Copy resource prefix, if backend setup sucessful
425   size_t len = strlen(backends[matchidx].prefix);
426   char *tmp;
427   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
428   memcpy(tmp, backends[matchidx].prefix, len+1);
429   (*ceed)->resource = tmp;
430 
431   return 0;
432 }
433 
434 /**
435   @brief Retrieve a parent CEED
436 
437   @param ceed           Ceed to retrieve parent of
438   @param[out] parent    Address to save the parent to
439 
440   @return An error code: 0 - success, otherwise - failure
441 
442   @ref Developer
443 **/
444 int CeedGetParent(Ceed ceed, Ceed *parent) {
445   int ierr;
446   if (ceed->parent) {
447     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
448     return 0;
449   }
450   *parent = ceed;
451   return 0;
452 }
453 
454 /**
455   @brief Retrieve a delegate CEED
456 
457   @param ceed           Ceed to retrieve delegate of
458   @param[out] delegate  Address to save the delegate to
459 
460   @return An error code: 0 - success, otherwise - failure
461 
462   @ref Developer
463 **/
464 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
465   *delegate = ceed->delegate;
466   return 0;
467 }
468 
469 /**
470   @brief Set a delegate CEED
471 
472   This function allows a CEED to set a delegate CEED. All backend
473   implementations default to the delegate CEED, unless overridden.
474 
475   @param ceed           Ceed to set delegate of
476   @param[out] delegate  Address to set the delegate to
477 
478   @return An error code: 0 - success, otherwise - failure
479 
480   @ref Advanced
481 **/
482 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
483   ceed->delegate = delegate;
484   delegate->parent = ceed;
485   return 0;
486 }
487 
488 /**
489   @brief Retrieve a delegate CEED for a specific object type
490 
491   @param ceed           Ceed to retrieve delegate of
492   @param[out] delegate  Address to save the delegate to
493   @param[in] objname    Name of the object type to retrieve delegate for
494 
495   @return An error code: 0 - success, otherwise - failure
496 
497   @ref Developer
498 **/
499 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
500   CeedInt ierr;
501 
502   // Check for object delegate
503   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
504     if (!strcmp(objname, ceed->objdelegates->objname)) {
505       *delegate = ceed->objdelegates->delegate;
506       return 0;
507     }
508 
509   // Use default delegate if no object delegate
510   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
511 
512   return 0;
513 }
514 
515 /**
516   @brief Set a delegate CEED for a specific object type
517 
518   This function allows a CEED to set a delegate CEED for a given type of
519   CEED object. All backend implementations default to the delegate CEED for
520   this object. For example,
521     CeedSetObjectDelegate(ceed, refceed, "Basis")
522   uses refceed implementations for all CeedBasis backend functions.
523 
524   @param ceed           Ceed to set delegate of
525   @param[out] delegate  Address to set the delegate to
526   @param[in] objname    Name of the object type to set delegate for
527 
528   @return An error code: 0 - success, otherwise - failure
529 
530   @ref Advanced
531 **/
532 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
533   CeedInt ierr;
534   CeedInt count = ceed->objdelegatecount;
535 
536   // Malloc or Realloc
537   if (count) {
538     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
539   } else {
540     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
541   }
542   ceed->objdelegatecount++;
543 
544   // Set object delegate
545   ceed->objdelegates[count].delegate = delegate;
546   size_t slen = strlen(objname) + 1;
547   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
548   memcpy(ceed->objdelegates[count].objname, objname, slen);
549 
550   // Set delegate parent
551   delegate->parent = ceed;
552 
553   return 0;
554 }
555 
556 /**
557   @brief Set the fallback resource for CeedOperators. The current resource, if
558            any, is freed by calling this function. This string is freed upon the
559            destruction of the Ceed.
560 
561   @param[out] ceed Ceed context
562   @param resource  Fallback resource to set
563 
564   @return An error code: 0 - success, otherwise - failure
565 
566   @ref Advanced
567 **/
568 
569 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
570   int ierr;
571 
572   // Free old
573   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
574 
575   // Set new
576   size_t len = strlen(resource);
577   char *tmp;
578   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
579   memcpy(tmp, resource, len+1);
580   ceed->opfallbackresource = tmp;
581 
582   return 0;
583 }
584 
585 /**
586   @brief Get the fallback resource for CeedOperators
587 
588   @param ceed          Ceed context
589   @param[out] resource Variable to store fallback resource
590 
591   @return An error code: 0 - success, otherwise - failure
592 
593   @ref Advanced
594 **/
595 
596 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
597   *resource = (const char *)ceed->opfallbackresource;
598   return 0;
599 }
600 
601 /**
602   @brief Get the parent Ceed associated with a fallback Ceed for a CeedOperator
603 
604   @param ceed            Ceed context
605   @param[out] ceed       Variable to store parent Ceed
606 
607   @return An error code: 0 - success, otherwise - failure
608 
609   @ref Advanced
610 **/
611 
612 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
613   *parent = ceed->opfallbackparent;
614   return 0;
615 }
616 
617 /**
618   @brief Return Ceed preferred memory type
619 
620   @param ceed           Ceed to get preferred memory type of
621   @param[out] type      Address to save preferred memory type to
622 
623   @return An error code: 0 - success, otherwise - failure
624 
625   @ref Basic
626 **/
627 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
628   int ierr;
629 
630   if (ceed->GetPreferredMemType) {
631     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
632   } else {
633     Ceed delegate;
634     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
635 
636     if (delegate) {
637       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
638     } else {
639       *type = CEED_MEM_HOST;
640     }
641   }
642 
643   return 0;
644 }
645 
646 /**
647   @brief Set a backend function
648 
649   This function is used for a backend to set the function associated with
650   the CEED objects. For example,
651     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
652   sets the backend implementation of 'CeedVectorCreate' and
653     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
654   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
655   is not required for the object type ("Basis" vs "CeedBasis").
656 
657   @param ceed           Ceed for error handling
658   @param type           Type of Ceed object to set function for
659   @param[out] object    Ceed object to set function for
660   @param fname          Name of function to set
661   @param f              Function to set
662 
663   @return An error code: 0 - success, otherwise - failure
664 
665   @ref Advanced
666 **/
667 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
668                            const char *fname, int (*f)()) {
669   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
670 
671   // Build lookup name
672   if (strcmp(type, "Ceed"))
673     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
674   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
675   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
676 
677   // Find and use offset
678   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
679     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
680       size_t offset = ceed->foffsets[i].offset;
681       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
682       *fpointer = f;
683       return 0;
684     }
685 
686   // LCOV_EXCL_START
687   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
688                    "object '%s'", fname, type);
689   // LCOV_EXCL_STOP
690 }
691 
692 /**
693   @brief Retrieve backend data for a CEED
694 
695   @param ceed           Ceed to retrieve data of
696   @param[out] data      Address to save data to
697 
698   @return An error code: 0 - success, otherwise - failure
699 
700   @ref Advanced
701 **/
702 int CeedGetData(Ceed ceed, void **data) {
703   *data = ceed->data;
704   return 0;
705 }
706 
707 /**
708   @brief Set backend data for a CEED
709 
710   @param ceed           Ceed to set data of
711   @param data           Address of data to set
712 
713   @return An error code: 0 - success, otherwise - failure
714 
715   @ref Advanced
716 **/
717 int CeedSetData(Ceed ceed, void **data) {
718   ceed->data = *data;
719   return 0;
720 }
721 
722 /**
723   @brief Get the full resource name for a CEED
724 
725   @param ceed            Ceed to get resource name of
726   @param[out] resource   Variable to store resource name
727 
728   @return An error code: 0 - success, otherwise - failure
729 
730   @ref Basic
731 **/
732 
733 int CeedGetResource(Ceed ceed, const char **resource) {
734   *resource = (const char *)ceed->resource;
735   return 0;
736 }
737 
738 /**
739   @brief Destroy a Ceed context
740 
741   @param ceed Address of Ceed context to destroy
742 
743   @return An error code: 0 - success, otherwise - failure
744 
745   @ref Basic
746 **/
747 int CeedDestroy(Ceed *ceed) {
748   int ierr;
749 
750   if (!*ceed || --(*ceed)->refcount > 0)
751     return 0;
752   if ((*ceed)->delegate) {
753     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
754   }
755   if ((*ceed)->objdelegatecount > 0) {
756     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
757       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
758       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
759     }
760     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
761   }
762   if ((*ceed)->Destroy) {
763     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
764   }
765   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
766   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
767   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
768   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
769   ierr = CeedFree(ceed); CeedChk(ierr);
770   return 0;
771 }
772 
773 /// @}
774