xref: /libCEED/interface/ceed.c (revision 80ac2e4337097b2baa4325022af48978c55bdefe)
1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights
3 // reserved. See files LICENSE and NOTICE for details.
4 //
5 // This file is part of CEED, a collection of benchmarks, miniapps, software
6 // libraries and APIs for efficient high-order finite element and spectral
7 // element discretizations for exascale applications. For more information and
8 // source code availability see http://github.com/ceed.
9 //
10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC,
11 // a collaborative effort of two U.S. Department of Energy organizations (Office
12 // of Science and the National Nuclear Security Administration) responsible for
13 // the planning and preparation of a capable exascale ecosystem, including
14 // software, applications, hardware, advanced system engineering and early
15 // testbed platforms, in support of the nation's exascale computing imperative.
16 
17 #define _POSIX_C_SOURCE 200112
18 #include <ceed-impl.h>
19 #include <ceed-backend.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stddef.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 /// @cond DOXYGEN_SKIP
28 static CeedRequest ceed_request_immediate;
29 static CeedRequest ceed_request_ordered;
30 
31 static struct {
32   char prefix[CEED_MAX_RESOURCE_LEN];
33   int (*init)(const char *resource, Ceed f);
34   unsigned int priority;
35 } backends[32];
36 static size_t num_backends;
37 
38 #define CEED_FTABLE_ENTRY(class, method) \
39   {#class #method, offsetof(struct class ##_private, method)}
40 /// @endcond
41 
42 /// @file
43 /// Implementation of core components of Ceed library
44 
45 /// @addtogroup CeedUser
46 /// @{
47 
48 /**
49   @brief Request immediate completion
50 
51   This predefined constant is passed as the \ref CeedRequest argument to
52   interfaces when the caller wishes for the operation to be performed
53   immediately.  The code
54 
55   @code
56     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
57   @endcode
58 
59   is semantically equivalent to
60 
61   @code
62     CeedRequest request;
63     CeedOperatorApply(op, ..., &request);
64     CeedRequestWait(&request);
65   @endcode
66 
67   @sa CEED_REQUEST_ORDERED
68 **/
69 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
70 
71 /**
72   @brief Request ordered completion
73 
74   This predefined constant is passed as the \ref CeedRequest argument to
75   interfaces when the caller wishes for the operation to be completed in the
76   order that it is submitted to the device.  It is typically used in a construct
77   such as
78 
79   @code
80     CeedRequest request;
81     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
82     CeedOperatorApply(op2, ..., &request);
83     // other optional work
84     CeedWait(&request);
85   @endcode
86 
87   which allows the sequence to complete asynchronously but does not start
88   `op2` until `op1` has completed.
89 
90   @todo The current implementation is overly strict, offering equivalent
91   semantics to @ref CEED_REQUEST_IMMEDIATE.
92 
93   @sa CEED_REQUEST_IMMEDIATE
94  */
95 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
96 
97 /**
98   @brief Wait for a CeedRequest to complete.
99 
100   Calling CeedRequestWait on a NULL request is a no-op.
101 
102   @param req Address of CeedRequest to wait for; zeroed on completion.
103 
104   @return An error code: 0 - success, otherwise - failure
105 
106   @ref User
107 **/
108 int CeedRequestWait(CeedRequest *req) {
109   if (!*req)
110     return 0;
111   return CeedError(NULL, 2, "CeedRequestWait not implemented");
112 }
113 
114 /// @}
115 
116 /// ----------------------------------------------------------------------------
117 /// Ceed Library Internal Functions
118 /// ----------------------------------------------------------------------------
119 /// @addtogroup CeedDeveloper
120 /// @{
121 
122 /**
123   @brief Error handler that returns without printing anything.
124 
125   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
126 
127   @ref Developer
128 **/
129 // LCOV_EXCL_START
130 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
131                     const char *func, int ecode, const char *format,
132                     va_list args) {
133   return ecode;
134 }
135 // LCOV_EXCL_STOP
136 
137 /**
138   @brief Error handler that prints to stderr and aborts
139 
140   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
141 
142   @ref Developer
143 **/
144 // LCOV_EXCL_START
145 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
146                    const char *func, int ecode, const char *format,
147                    va_list args) {
148   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
149   vfprintf(stderr, format, args);
150   fprintf(stderr, "\n");
151   abort();
152   return ecode;
153 }
154 // LCOV_EXCL_STOP
155 
156 /**
157   @brief Error handler that prints to stderr and exits
158 
159   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
160 
161   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
162   handlers (e.g., as used by gcov) are run.
163 
164   @ref Developer
165 **/
166 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, const char *func,
167                   int ecode, const char *format, va_list args) {
168   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
169   vfprintf(stderr, format, args);
170   fprintf(stderr, "\n");
171   exit(ecode);
172   return ecode;
173 }
174 
175 /**
176   @brief Set error handler
177 
178   A default error handler is set in CeedInit().  Use this function to change
179   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
180   error handler.
181 
182   @ref Developer
183 **/
184 int CeedSetErrorHandler(Ceed ceed,
185                         int (*eh)(Ceed, const char *, int, const char *,
186                                   int, const char *, va_list)) {
187   ceed->Error = eh;
188   return 0;
189 }
190 
191 /// @}
192 
193 /// ----------------------------------------------------------------------------
194 /// Ceed Backend API
195 /// ----------------------------------------------------------------------------
196 /// @addtogroup CeedBackend
197 /// @{
198 
199 /**
200   @brief Allocate an array on the host; use CeedMalloc()
201 
202   Memory usage can be tracked by the library.  This ensures sufficient
203     alignment for vectorization and should be used for large allocations.
204 
205   @param n Number of units to allocate
206   @param unit Size of each unit
207   @param p Address of pointer to hold the result.
208 
209   @return An error code: 0 - success, otherwise - failure
210 
211   @sa CeedFree()
212 
213   @ref Backend
214 **/
215 int CeedMallocArray(size_t n, size_t unit, void *p) {
216   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
217   if (ierr)
218     // LCOV_EXCL_START
219     return CeedError(NULL, ierr, "posix_memalign failed to allocate %zd "
220                      "members of size %zd\n", n, unit);
221   // LCOV_EXCL_STOP
222 
223   return 0;
224 }
225 
226 /**
227   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
228 
229   Memory usage can be tracked by the library.
230 
231   @param n    Number of units to allocate
232   @param unit Size of each unit
233   @param p    Address of pointer to hold the result.
234 
235   @return An error code: 0 - success, otherwise - failure
236 
237   @sa CeedFree()
238 
239   @ref Backend
240 **/
241 int CeedCallocArray(size_t n, size_t unit, void *p) {
242   *(void **)p = calloc(n, unit);
243   if (n && unit && !*(void **)p)
244     // LCOV_EXCL_START
245     return CeedError(NULL, 1, "calloc failed to allocate %zd members of size "
246                      "%zd\n", n, unit);
247   // LCOV_EXCL_STOP
248 
249   return 0;
250 }
251 
252 /**
253   @brief Reallocate an array on the host; use CeedRealloc()
254 
255   Memory usage can be tracked by the library.
256 
257   @param n    Number of units to allocate
258   @param unit Size of each unit
259   @param p    Address of pointer to hold the result.
260 
261   @return An error code: 0 - success, otherwise - failure
262 
263   @sa CeedFree()
264 
265   @ref Backend
266 **/
267 int CeedReallocArray(size_t n, size_t unit, void *p) {
268   *(void **)p = realloc(*(void **)p, n*unit);
269   if (n && unit && !*(void **)p)
270     // LCOV_EXCL_START
271     return CeedError(NULL, 1, "realloc failed to allocate %zd members of size "
272                      "%zd\n", n, unit);
273   // LCOV_EXCL_STOP
274 
275   return 0;
276 }
277 
278 /** Free memory allocated using CeedMalloc() or CeedCalloc()
279 
280   @param p address of pointer to memory.  This argument is of type void* to
281              avoid needing a cast, but is the address of the pointer (which is
282              zeroed) rather than the pointer.
283 **/
284 int CeedFree(void *p) {
285   free(*(void **)p);
286   *(void **)p = NULL;
287   return 0;
288 }
289 
290 /**
291   @brief Register a Ceed backend
292 
293   @param prefix   Prefix of resources for this backend to respond to.  For
294                     example, the reference backend responds to "/cpu/self".
295   @param init     Initialization function called by CeedInit() when the backend
296                     is selected to drive the requested resource.
297   @param priority Integer priority.  Lower values are preferred in case the
298                     resource requested by CeedInit() has non-unique best prefix
299                     match.
300 
301   @return An error code: 0 - success, otherwise - failure
302 
303   @ref Backend
304 **/
305 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
306                  unsigned int priority) {
307   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
308     // LCOV_EXCL_START
309     return CeedError(NULL, 1, "Too many backends");
310   // LCOV_EXCL_STOP
311 
312   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
313   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
314   backends[num_backends].init = init;
315   backends[num_backends].priority = priority;
316   num_backends++;
317   return 0;
318 }
319 
320 /**
321   @brief Retrieve a parent Ceed context
322 
323   @param ceed        Ceed context to retrieve parent of
324   @param[out] parent Address to save the parent to
325 
326   @return An error code: 0 - success, otherwise - failure
327 
328   @ref Backend
329 **/
330 int CeedGetParent(Ceed ceed, Ceed *parent) {
331   int ierr;
332   if (ceed->parent) {
333     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
334     return 0;
335   }
336   *parent = ceed;
337   return 0;
338 }
339 
340 /**
341   @brief Retrieve a delegate Ceed context
342 
343   @param ceed          Ceed context to retrieve delegate of
344   @param[out] delegate Address to save the delegate to
345 
346   @return An error code: 0 - success, otherwise - failure
347 
348   @ref Backend
349 **/
350 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
351   *delegate = ceed->delegate;
352   return 0;
353 }
354 
355 /**
356   @brief Set a delegate Ceed context
357 
358   This function allows a Ceed context to set a delegate Ceed context. All
359     backend implementations default to the delegate Ceed context, unless
360     overridden.
361 
362   @param ceed           Ceed context to set delegate of
363   @param[out] delegate  Address to set the delegate to
364 
365   @return An error code: 0 - success, otherwise - failure
366 
367   @ref Backend
368 **/
369 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
370   ceed->delegate = delegate;
371   delegate->parent = ceed;
372   return 0;
373 }
374 
375 /**
376   @brief Retrieve a delegate Ceed context for a specific object type
377 
378   @param ceed           Ceed context to retrieve delegate of
379   @param[out] delegate  Address to save the delegate to
380   @param[in] objname    Name of the object type to retrieve delegate for
381 
382   @return An error code: 0 - success, otherwise - failure
383 
384   @ref Backend
385 **/
386 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
387   CeedInt ierr;
388 
389   // Check for object delegate
390   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
391     if (!strcmp(objname, ceed->objdelegates->objname)) {
392       *delegate = ceed->objdelegates->delegate;
393       return 0;
394     }
395 
396   // Use default delegate if no object delegate
397   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
398 
399   return 0;
400 }
401 
402 /**
403   @brief Set a delegate Ceed context for a specific object type
404 
405   This function allows a Ceed context to set a delegate Ceed context for a
406     given type of Ceed object. All backend implementations default to the
407     delegate Ceed context for this object. For example,
408     CeedSetObjectDelegate(ceed, refceed, "Basis")
409   uses refceed implementations for all CeedBasis backend functions.
410 
411   @param ceed          Ceed context to set delegate of
412   @param[out] delegate Address to set the delegate to
413   @param[in] objname   Name of the object type to set delegate for
414 
415   @return An error code: 0 - success, otherwise - failure
416 
417   @ref Backend
418 **/
419 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
420   CeedInt ierr;
421   CeedInt count = ceed->objdelegatecount;
422 
423   // Malloc or Realloc
424   if (count) {
425     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
426   } else {
427     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
428   }
429   ceed->objdelegatecount++;
430 
431   // Set object delegate
432   ceed->objdelegates[count].delegate = delegate;
433   size_t slen = strlen(objname) + 1;
434   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
435   memcpy(ceed->objdelegates[count].objname, objname, slen);
436 
437   // Set delegate parent
438   delegate->parent = ceed;
439 
440   return 0;
441 }
442 
443 /**
444   @brief Get the fallback resource for CeedOperators
445 
446   @param ceed          Ceed context
447   @param[out] resource Variable to store fallback resource
448 
449   @return An error code: 0 - success, otherwise - failure
450 
451   @ref Backend
452 **/
453 
454 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
455   *resource = (const char *)ceed->opfallbackresource;
456   return 0;
457 }
458 
459 /**
460   @brief Set the fallback resource for CeedOperators. The current resource, if
461            any, is freed by calling this function. This string is freed upon the
462            destruction of the Ceed context.
463 
464   @param[out] ceed Ceed context
465   @param resource  Fallback resource to set
466 
467   @return An error code: 0 - success, otherwise - failure
468 
469   @ref Backend
470 **/
471 
472 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
473   int ierr;
474 
475   // Free old
476   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
477 
478   // Set new
479   size_t len = strlen(resource);
480   char *tmp;
481   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
482   memcpy(tmp, resource, len+1);
483   ceed->opfallbackresource = tmp;
484 
485   return 0;
486 }
487 
488 /**
489   @brief Get the parent Ceed context associated with a fallback Ceed context
490            for a CeedOperator
491 
492   @param ceed         Ceed context
493   @param[out] parent  Variable to store parent Ceed context
494 
495   @return An error code: 0 - success, otherwise - failure
496 
497   @ref Backend
498 **/
499 
500 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
501   *parent = ceed->opfallbackparent;
502   return 0;
503 }
504 
505 /**
506   @brief Set a backend function
507 
508   This function is used for a backend to set the function associated with
509   the Ceed objects. For example,
510     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
511   sets the backend implementation of 'CeedVectorCreate' and
512     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
513   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
514   is not required for the object type ("Basis" vs "CeedBasis").
515 
516   @param ceed           Ceed context for error handling
517   @param type           Type of Ceed object to set function for
518   @param[out] object    Ceed object to set function for
519   @param fname          Name of function to set
520   @param f              Function to set
521 
522   @return An error code: 0 - success, otherwise - failure
523 
524   @ref Backend
525 **/
526 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
527                            const char *fname, int (*f)()) {
528   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
529 
530   // Build lookup name
531   if (strcmp(type, "Ceed"))
532     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
533   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
534   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
535 
536   // Find and use offset
537   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
538     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
539       size_t offset = ceed->foffsets[i].offset;
540       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
541       *fpointer = f;
542       return 0;
543     }
544 
545   // LCOV_EXCL_START
546   return CeedError(ceed, 1, "Requested function '%s' was not found for CEED "
547                    "object '%s'", fname, type);
548   // LCOV_EXCL_STOP
549 }
550 
551 /**
552   @brief Retrieve backend data for a Ceed context
553 
554   @param ceed      Ceed context to retrieve data of
555   @param[out] data Address to save data to
556 
557   @return An error code: 0 - success, otherwise - failure
558 
559   @ref Backend
560 **/
561 int CeedGetData(Ceed ceed, void **data) {
562   *data = ceed->data;
563   return 0;
564 }
565 
566 /**
567   @brief Set backend data for a Ceed context
568 
569   @param ceed           Ceed context to set data of
570   @param data           Address of data to set
571 
572   @return An error code: 0 - success, otherwise - failure
573 
574   @ref Backend
575 **/
576 int CeedSetData(Ceed ceed, void **data) {
577   ceed->data = *data;
578   return 0;
579 }
580 
581 /// @}
582 
583 /// ----------------------------------------------------------------------------
584 /// Ceed Public API
585 /// ----------------------------------------------------------------------------
586 /// @addtogroup CeedUser
587 /// @{
588 
589 /**
590   @brief Initialize a \ref Ceed context to use the specified resource.
591 
592   @param resource  Resource to use, e.g., "/cpu/self"
593   @param ceed      The library context
594   @sa CeedRegister() CeedDestroy()
595 
596   @return An error code: 0 - success, otherwise - failure
597 
598   @ref User
599 **/
600 int CeedInit(const char *resource, Ceed *ceed) {
601   int ierr;
602   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
603 
604   // Find matching backend
605   if (!resource)
606     // LCOV_EXCL_START
607     return CeedError(NULL, 1, "No resource provided");
608   // LCOV_EXCL_STOP
609 
610   for (size_t i=0; i<num_backends; i++) {
611     size_t n;
612     const char *prefix = backends[i].prefix;
613     for (n = 0; prefix[n] && prefix[n] == resource[n]; n++) {}
614     priority = backends[i].priority;
615     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
616       matchlen = n;
617       matchpriority = priority;
618       matchidx = i;
619     }
620   }
621   if (!matchlen)
622     // LCOV_EXCL_START
623     return CeedError(NULL, 1, "No suitable backend: %s", resource);
624   // LCOV_EXCL_STOP
625 
626   // Setup Ceed
627   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
628   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
629   if (!ceed_error_handler)
630     ceed_error_handler = "abort";
631   if (!strcmp(ceed_error_handler, "exit"))
632     (*ceed)->Error = CeedErrorExit;
633   else
634     (*ceed)->Error = CeedErrorAbort;
635   (*ceed)->refcount = 1;
636   (*ceed)->data = NULL;
637 
638   // Set lookup table
639   foffset foffsets[] = {
640     CEED_FTABLE_ENTRY(Ceed, Error),
641     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
642     CEED_FTABLE_ENTRY(Ceed, Destroy),
643     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
644     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
645     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
646     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
647     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
648     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
649     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
650     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
651     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
652     CEED_FTABLE_ENTRY(CeedVector, SetArray),
653     CEED_FTABLE_ENTRY(CeedVector, SetValue),
654     CEED_FTABLE_ENTRY(CeedVector, GetArray),
655     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
656     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
657     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
658     CEED_FTABLE_ENTRY(CeedVector, Norm),
659     CEED_FTABLE_ENTRY(CeedVector, Destroy),
660     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
661     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
662     CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
663     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
664     CEED_FTABLE_ENTRY(CeedBasis, Apply),
665     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
666     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
667     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
668     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
669     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
670     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
671     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
672     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
673     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
674     CEED_FTABLE_ENTRY(CeedOperator, Apply),
675     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
676     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
677     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
678     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
679     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
680     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
681   };
682 
683   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
684   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
685 
686   // Set fallback for advanced CeedOperator functions
687   const char fallbackresource[] = "/cpu/self/ref/serial";
688   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
689   CeedChk(ierr);
690 
691   // Backend specific setup
692   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
693 
694   // Copy resource prefix, if backend setup sucessful
695   size_t len = strlen(backends[matchidx].prefix);
696   char *tmp;
697   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
698   memcpy(tmp, backends[matchidx].prefix, len+1);
699   (*ceed)->resource = tmp;
700 
701   return 0;
702 }
703 
704 /**
705   @brief Get the full resource name for a Ceed context
706 
707   @param ceed            Ceed context to get resource name of
708   @param[out] resource   Variable to store resource name
709 
710   @return An error code: 0 - success, otherwise - failure
711 
712   @ref User
713 **/
714 
715 int CeedGetResource(Ceed ceed, const char **resource) {
716   *resource = (const char *)ceed->resource;
717   return 0;
718 }
719 
720 /**
721   @brief Return Ceed context preferred memory type
722 
723   @param ceed      Ceed context to get preferred memory type of
724   @param[out] type Address to save preferred memory type to
725 
726   @return An error code: 0 - success, otherwise - failure
727 
728   @ref User
729 **/
730 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
731   int ierr;
732 
733   if (ceed->GetPreferredMemType) {
734     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
735   } else {
736     Ceed delegate;
737     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
738 
739     if (delegate) {
740       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
741     } else {
742       *type = CEED_MEM_HOST;
743     }
744   }
745 
746   return 0;
747 }
748 
749 /**
750   @brief View a Ceed
751 
752   @param[in] ceed          Ceed to view
753   @param[in] stream        Filestream to write to
754 
755   @return An error code: 0 - success, otherwise - failure
756 
757   @ref User
758 **/
759 int CeedView(Ceed ceed, FILE *stream) {
760   int ierr;
761   CeedMemType memtype;
762 
763   ierr = CeedGetPreferredMemType(ceed, &memtype); CeedChk(ierr);
764 
765   fprintf(stream, "Ceed\n"
766           "  Ceed Resource: %s\n"
767           "  Preferred MemType: %s\n",
768           ceed->resource, CeedMemTypes[memtype]);
769 
770   return 0;
771 }
772 
773 /**
774   @brief Destroy a Ceed context
775 
776   @param ceed Address of Ceed context to destroy
777 
778   @return An error code: 0 - success, otherwise - failure
779 
780   @ref User
781 **/
782 int CeedDestroy(Ceed *ceed) {
783   int ierr;
784   if (!*ceed || --(*ceed)->refcount > 0)
785     return 0;
786 
787   if ((*ceed)->delegate) {
788     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
789   }
790 
791   if ((*ceed)->objdelegatecount > 0) {
792     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
793       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
794       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
795     }
796     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
797   }
798 
799   if ((*ceed)->Destroy) {
800     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
801   }
802 
803   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
804   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
805   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
806   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
807   ierr = CeedFree(ceed); CeedChk(ierr);
808   return 0;
809 }
810 
811 /**
812   @brief Error handling implementation; use \ref CeedError instead.
813 
814   @ref Developer
815 **/
816 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
817                   int ecode, const char *format, ...) {
818   va_list args;
819   int retval;
820   va_start(args, format);
821   if (ceed) {
822     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, args);
823   } else {
824     // This function doesn't actually return
825     // LCOV_EXCL_START
826     retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, args);
827   }
828   va_end(args);
829   return retval;
830   // LCOV_EXCL_STOP
831 }
832 
833 /// @}
834