xref: /libCEED/interface/ceed.c (revision 2bbc7fe82c1aed53ecee4961fe801e771115348c)
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/ceed.h>
19 #include <ceed/backend.h>
20 #include <ceed-impl.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 /// @cond DOXYGEN_SKIP
29 static CeedRequest ceed_request_immediate;
30 static CeedRequest ceed_request_ordered;
31 
32 static struct {
33   char prefix[CEED_MAX_RESOURCE_LEN];
34   int (*init)(const char *resource, Ceed f);
35   unsigned int priority;
36 } backends[32];
37 static size_t num_backends;
38 
39 #define CEED_FTABLE_ENTRY(class, method) \
40   {#class #method, offsetof(struct class ##_private, method)}
41 /// @endcond
42 
43 /// @file
44 /// Implementation of core components of Ceed library
45 
46 /// @addtogroup CeedUser
47 /// @{
48 
49 /**
50   @brief Request immediate completion
51 
52   This predefined constant is passed as the \ref CeedRequest argument to
53   interfaces when the caller wishes for the operation to be performed
54   immediately.  The code
55 
56   @code
57     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
58   @endcode
59 
60   is semantically equivalent to
61 
62   @code
63     CeedRequest request;
64     CeedOperatorApply(op, ..., &request);
65     CeedRequestWait(&request);
66   @endcode
67 
68   @sa CEED_REQUEST_ORDERED
69 **/
70 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
71 
72 /**
73   @brief Request ordered completion
74 
75   This predefined constant is passed as the \ref CeedRequest argument to
76   interfaces when the caller wishes for the operation to be completed in the
77   order that it is submitted to the device.  It is typically used in a construct
78   such as
79 
80   @code
81     CeedRequest request;
82     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
83     CeedOperatorApply(op2, ..., &request);
84     // other optional work
85     CeedWait(&request);
86   @endcode
87 
88   which allows the sequence to complete asynchronously but does not start
89   `op2` until `op1` has completed.
90 
91   @todo The current implementation is overly strict, offering equivalent
92   semantics to @ref CEED_REQUEST_IMMEDIATE.
93 
94   @sa CEED_REQUEST_IMMEDIATE
95  */
96 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
97 
98 /**
99   @brief Wait for a CeedRequest to complete.
100 
101   Calling CeedRequestWait on a NULL request is a no-op.
102 
103   @param req Address of CeedRequest to wait for; zeroed on completion.
104 
105   @return An error code: 0 - success, otherwise - failure
106 
107   @ref User
108 **/
109 int CeedRequestWait(CeedRequest *req) {
110   if (!*req)
111     return CEED_ERROR_SUCCESS;
112   return CeedError(NULL, CEED_ERROR_UNSUPPORTED,
113                    "CeedRequestWait not implemented");
114 }
115 
116 /// @}
117 
118 /// ----------------------------------------------------------------------------
119 /// Ceed Library Internal Functions
120 /// ----------------------------------------------------------------------------
121 /// @addtogroup CeedDeveloper
122 /// @{
123 
124 /// @}
125 
126 /// ----------------------------------------------------------------------------
127 /// Ceed Backend API
128 /// ----------------------------------------------------------------------------
129 /// @addtogroup CeedBackend
130 /// @{
131 
132 /**
133   @brief Print Ceed debugging information
134 
135   @param ceed    Ceed context
136   @param format  Printing format
137 
138   @return None
139 
140   @ref Backend
141 **/
142 // LCOV_EXCL_START
143 void CeedDebugImpl(const Ceed ceed, const char *format,...) {
144   if (!ceed->debug) return;
145   va_list args;
146   va_start(args, format);
147   CeedDebugImpl256(ceed, 0, format, args);
148   va_end(args);
149 }
150 // LCOV_EXCL_STOP
151 
152 /**
153   @brief Print Ceed debugging information in color
154 
155   @param ceed    Ceed context
156   @param color   Color to print
157   @param format  Printing format
158 
159   @return None
160 
161   @ref Backend
162 **/
163 // LCOV_EXCL_START
164 void CeedDebugImpl256(const Ceed ceed, const unsigned char color,
165                       const char *format,...) {
166   if (!ceed->debug) return;
167   va_list args;
168   va_start(args, format);
169   fflush(stdout);
170   fprintf(stdout, "\033[38;5;%dm", color);
171   vfprintf(stdout, format, args);
172   fprintf(stdout, "\033[m");
173   fprintf(stdout, "\n");
174   fflush(stdout);
175   va_end(args);
176 }
177 // LCOV_EXCL_STOP
178 
179 /**
180   @brief Allocate an array on the host; use CeedMalloc()
181 
182   Memory usage can be tracked by the library.  This ensures sufficient
183     alignment for vectorization and should be used for large allocations.
184 
185   @param n Number of units to allocate
186   @param unit Size of each unit
187   @param p Address of pointer to hold the result.
188 
189   @return An error code: 0 - success, otherwise - failure
190 
191   @sa CeedFree()
192 
193   @ref Backend
194 **/
195 int CeedMallocArray(size_t n, size_t unit, void *p) {
196   int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit);
197   if (ierr)
198     // LCOV_EXCL_START
199     return CeedError(NULL, CEED_ERROR_MAJOR,
200                      "posix_memalign failed to allocate %zd "
201                      "members of size %zd\n", n, unit);
202   // LCOV_EXCL_STOP
203   return CEED_ERROR_SUCCESS;
204 }
205 
206 /**
207   @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc()
208 
209   Memory usage can be tracked by the library.
210 
211   @param n    Number of units to allocate
212   @param unit Size of each unit
213   @param p    Address of pointer to hold the result.
214 
215   @return An error code: 0 - success, otherwise - failure
216 
217   @sa CeedFree()
218 
219   @ref Backend
220 **/
221 int CeedCallocArray(size_t n, size_t unit, void *p) {
222   *(void **)p = calloc(n, unit);
223   if (n && unit && !*(void **)p)
224     // LCOV_EXCL_START
225     return CeedError(NULL, CEED_ERROR_MAJOR,
226                      "calloc failed to allocate %zd members of size "
227                      "%zd\n", n, unit);
228   // LCOV_EXCL_STOP
229   return CEED_ERROR_SUCCESS;
230 }
231 
232 /**
233   @brief Reallocate an array on the host; use CeedRealloc()
234 
235   Memory usage can be tracked by the library.
236 
237   @param n    Number of units to allocate
238   @param unit Size of each unit
239   @param p    Address of pointer to hold the result.
240 
241   @return An error code: 0 - success, otherwise - failure
242 
243   @sa CeedFree()
244 
245   @ref Backend
246 **/
247 int CeedReallocArray(size_t n, size_t unit, void *p) {
248   *(void **)p = realloc(*(void **)p, n*unit);
249   if (n && unit && !*(void **)p)
250     // LCOV_EXCL_START
251     return CeedError(NULL, CEED_ERROR_MAJOR,
252                      "realloc failed to allocate %zd members of size "
253                      "%zd\n", n, unit);
254   // LCOV_EXCL_STOP
255   return CEED_ERROR_SUCCESS;
256 }
257 
258 /** Free memory allocated using CeedMalloc() or CeedCalloc()
259 
260   @param p address of pointer to memory.  This argument is of type void* to
261              avoid needing a cast, but is the address of the pointer (which is
262              zeroed) rather than the pointer.
263 **/
264 int CeedFree(void *p) {
265   free(*(void **)p);
266   *(void **)p = NULL;
267   return CEED_ERROR_SUCCESS;
268 }
269 
270 /**
271   @brief Register a Ceed backend
272 
273   @param prefix   Prefix of resources for this backend to respond to.  For
274                     example, the reference backend responds to "/cpu/self".
275   @param init     Initialization function called by CeedInit() when the backend
276                     is selected to drive the requested resource.
277   @param priority Integer priority.  Lower values are preferred in case the
278                     resource requested by CeedInit() has non-unique best prefix
279                     match.
280 
281   @return An error code: 0 - success, otherwise - failure
282 
283   @ref Backend
284 **/
285 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed),
286                  unsigned int priority) {
287   if (num_backends >= sizeof(backends) / sizeof(backends[0]))
288     // LCOV_EXCL_START
289     return CeedError(NULL, CEED_ERROR_MAJOR, "Too many backends");
290   // LCOV_EXCL_STOP
291 
292   strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
293   backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0;
294   backends[num_backends].init = init;
295   backends[num_backends].priority = priority;
296   num_backends++;
297   return CEED_ERROR_SUCCESS;
298 }
299 
300 /**
301   @brief Return debugging status flag
302 
303   @param ceed     Ceed context to get debugging flag
304   @param isDebug  Variable to store debugging flag
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Bcakend
309 **/
310 int CeedIsDebug(Ceed ceed, bool *isDebug) {
311   *isDebug = ceed->debug;
312   return CEED_ERROR_SUCCESS;
313 }
314 
315 /**
316   @brief Retrieve a parent Ceed context
317 
318   @param ceed        Ceed context to retrieve parent of
319   @param[out] parent Address to save the parent to
320 
321   @return An error code: 0 - success, otherwise - failure
322 
323   @ref Backend
324 **/
325 int CeedGetParent(Ceed ceed, Ceed *parent) {
326   int ierr;
327   if (ceed->parent) {
328     ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr);
329     return CEED_ERROR_SUCCESS;
330   }
331   *parent = ceed;
332   return CEED_ERROR_SUCCESS;
333 }
334 
335 /**
336   @brief Retrieve a delegate Ceed context
337 
338   @param ceed          Ceed context to retrieve delegate of
339   @param[out] delegate Address to save the delegate to
340 
341   @return An error code: 0 - success, otherwise - failure
342 
343   @ref Backend
344 **/
345 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
346   *delegate = ceed->delegate;
347   return CEED_ERROR_SUCCESS;
348 }
349 
350 /**
351   @brief Set a delegate Ceed context
352 
353   This function allows a Ceed context to set a delegate Ceed context. All
354     backend implementations default to the delegate Ceed context, unless
355     overridden.
356 
357   @param ceed           Ceed context to set delegate of
358   @param[out] delegate  Address to set the delegate to
359 
360   @return An error code: 0 - success, otherwise - failure
361 
362   @ref Backend
363 **/
364 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
365   ceed->delegate = delegate;
366   delegate->parent = ceed;
367   return CEED_ERROR_SUCCESS;
368 }
369 
370 /**
371   @brief Retrieve a delegate Ceed context for a specific object type
372 
373   @param ceed           Ceed context to retrieve delegate of
374   @param[out] delegate  Address to save the delegate to
375   @param[in] objname    Name of the object type to retrieve delegate for
376 
377   @return An error code: 0 - success, otherwise - failure
378 
379   @ref Backend
380 **/
381 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *objname) {
382   CeedInt ierr;
383 
384   // Check for object delegate
385   for (CeedInt i=0; i<ceed->objdelegatecount; i++)
386     if (!strcmp(objname, ceed->objdelegates->objname)) {
387       *delegate = ceed->objdelegates->delegate;
388       return CEED_ERROR_SUCCESS;
389     }
390 
391   // Use default delegate if no object delegate
392   ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr);
393   return CEED_ERROR_SUCCESS;
394 }
395 
396 /**
397   @brief Set a delegate Ceed context for a specific object type
398 
399   This function allows a Ceed context to set a delegate Ceed context for a
400     given type of Ceed object. All backend implementations default to the
401     delegate Ceed context for this object. For example,
402     CeedSetObjectDelegate(ceed, refceed, "Basis")
403   uses refceed implementations for all CeedBasis backend functions.
404 
405   @param ceed          Ceed context to set delegate of
406   @param[out] delegate Address to set the delegate to
407   @param[in] objname   Name of the object type to set delegate for
408 
409   @return An error code: 0 - success, otherwise - failure
410 
411   @ref Backend
412 **/
413 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *objname) {
414   CeedInt ierr;
415   CeedInt count = ceed->objdelegatecount;
416 
417   // Malloc or Realloc
418   if (count) {
419     ierr = CeedRealloc(count+1, &ceed->objdelegates); CeedChk(ierr);
420   } else {
421     ierr = CeedCalloc(1, &ceed->objdelegates); CeedChk(ierr);
422   }
423   ceed->objdelegatecount++;
424 
425   // Set object delegate
426   ceed->objdelegates[count].delegate = delegate;
427   size_t slen = strlen(objname) + 1;
428   ierr = CeedMalloc(slen, &ceed->objdelegates[count].objname); CeedChk(ierr);
429   memcpy(ceed->objdelegates[count].objname, objname, slen);
430 
431   // Set delegate parent
432   delegate->parent = ceed;
433   return CEED_ERROR_SUCCESS;
434 }
435 
436 /**
437   @brief Get the fallback resource for CeedOperators
438 
439   @param ceed          Ceed context
440   @param[out] resource Variable to store fallback resource
441 
442   @return An error code: 0 - success, otherwise - failure
443 
444   @ref Backend
445 **/
446 
447 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
448   *resource = (const char *)ceed->opfallbackresource;
449   return CEED_ERROR_SUCCESS;
450 }
451 
452 /**
453   @brief Set the fallback resource for CeedOperators. The current resource, if
454            any, is freed by calling this function. This string is freed upon the
455            destruction of the Ceed context.
456 
457   @param[out] ceed Ceed context
458   @param resource  Fallback resource to set
459 
460   @return An error code: 0 - success, otherwise - failure
461 
462   @ref Backend
463 **/
464 
465 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
466   int ierr;
467 
468   // Free old
469   ierr = CeedFree(&ceed->opfallbackresource); CeedChk(ierr);
470 
471   // Set new
472   size_t len = strlen(resource);
473   char *tmp;
474   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
475   memcpy(tmp, resource, len+1);
476   ceed->opfallbackresource = tmp;
477   return CEED_ERROR_SUCCESS;
478 }
479 
480 /**
481   @brief Get the parent Ceed context associated with a fallback Ceed context
482            for a CeedOperator
483 
484   @param ceed         Ceed context
485   @param[out] parent  Variable to store parent Ceed context
486 
487   @return An error code: 0 - success, otherwise - failure
488 
489   @ref Backend
490 **/
491 
492 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) {
493   *parent = ceed->opfallbackparent;
494   return CEED_ERROR_SUCCESS;
495 }
496 
497 /**
498   @brief Flag Ceed context as deterministic
499 
500   @param ceed     Ceed to flag as deterministic
501 
502   @return An error code: 0 - success, otherwise - failure
503 
504   @ref Backend
505 **/
506 
507 int CeedSetDeterministic(Ceed ceed, bool isDeterministic) {
508   ceed->isDeterministic = isDeterministic;
509   return CEED_ERROR_SUCCESS;
510 }
511 
512 /**
513   @brief Set a backend function
514 
515   This function is used for a backend to set the function associated with
516   the Ceed objects. For example,
517     CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)
518   sets the backend implementation of 'CeedVectorCreate' and
519     CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)
520   sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed'
521   is not required for the object type ("Basis" vs "CeedBasis").
522 
523   @param ceed           Ceed context for error handling
524   @param type           Type of Ceed object to set function for
525   @param[out] object    Ceed object to set function for
526   @param fname          Name of function to set
527   @param f              Function to set
528 
529   @return An error code: 0 - success, otherwise - failure
530 
531   @ref Backend
532 **/
533 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object,
534                            const char *fname, int (*f)()) {
535   char lookupname[CEED_MAX_RESOURCE_LEN+1] = "";
536 
537   // Build lookup name
538   if (strcmp(type, "Ceed"))
539     strncat (lookupname, "Ceed", CEED_MAX_RESOURCE_LEN);
540   strncat(lookupname, type, CEED_MAX_RESOURCE_LEN);
541   strncat(lookupname, fname, CEED_MAX_RESOURCE_LEN);
542 
543   // Find and use offset
544   for (CeedInt i = 0; ceed->foffsets[i].fname; i++)
545     if (!strcmp(ceed->foffsets[i].fname, lookupname)) {
546       size_t offset = ceed->foffsets[i].offset;
547       int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
548       *fpointer = f;
549       return CEED_ERROR_SUCCESS;
550     }
551 
552   // LCOV_EXCL_START
553   return CeedError(ceed, CEED_ERROR_UNSUPPORTED,
554                    "Requested function '%s' was not found for CEED "
555                    "object '%s'", fname, type);
556   // LCOV_EXCL_STOP
557 }
558 
559 /**
560   @brief Retrieve backend data for a Ceed context
561 
562   @param ceed      Ceed context to retrieve data of
563   @param[out] data Address to save data to
564 
565   @return An error code: 0 - success, otherwise - failure
566 
567   @ref Backend
568 **/
569 int CeedGetData(Ceed ceed, void *data) {
570   *(void **)data = ceed->data;
571   return CEED_ERROR_SUCCESS;
572 }
573 
574 /**
575   @brief Set backend data for a Ceed context
576 
577   @param ceed           Ceed context to set data of
578   @param data           Address of data to set
579 
580   @return An error code: 0 - success, otherwise - failure
581 
582   @ref Backend
583 **/
584 int CeedSetData(Ceed ceed, void *data) {
585   ceed->data = data;
586   return CEED_ERROR_SUCCESS;
587 }
588 
589 /// @}
590 
591 /// ----------------------------------------------------------------------------
592 /// Ceed Public API
593 /// ----------------------------------------------------------------------------
594 /// @addtogroup CeedUser
595 /// @{
596 
597 /**
598   @brief Initialize a \ref Ceed context to use the specified resource.
599 
600   @param resource  Resource to use, e.g., "/cpu/self"
601   @param ceed      The library context
602   @sa CeedRegister() CeedDestroy()
603 
604   @return An error code: 0 - success, otherwise - failure
605 
606   @ref User
607 **/
608 int CeedInit(const char *resource, Ceed *ceed) {
609   int ierr;
610   size_t matchlen = 0, matchidx = UINT_MAX, matchpriority = UINT_MAX, priority;
611 
612   // Find matching backend
613   if (!resource)
614     // LCOV_EXCL_START
615     return CeedError(NULL, CEED_ERROR_MAJOR, "No resource provided");
616   // LCOV_EXCL_STOP
617   ierr = CeedRegisterAll(); CeedChk(ierr);
618 
619   size_t stem_length;
620   for (stem_length=0; resource[stem_length]
621        && resource[stem_length] != ':'; stem_length++) {}
622   for (size_t i=0; i<num_backends; i++) {
623     size_t n;
624     const char *prefix = backends[i].prefix;
625     for (n=0; prefix[n] && prefix[n] == resource[n]; n++) {}
626     priority = backends[i].priority;
627     if (n > matchlen || (n == matchlen && matchpriority > priority)) {
628       matchlen = n;
629       matchpriority = priority;
630       matchidx = i;
631     }
632   }
633   if (matchlen <= 1) {
634     // LCOV_EXCL_START
635     return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s",
636                      resource);
637     // LCOV_EXCL_STOP
638   } else if (matchlen != stem_length) {
639     // LCOV_EXCL_START
640     return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s "
641                      "Closest match: %s", resource, backends[matchidx].prefix);
642     // LCOV_EXCL_STOP
643   }
644 
645   // Setup Ceed
646   ierr = CeedCalloc(1, ceed); CeedChk(ierr);
647   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
648   if (!ceed_error_handler)
649     ceed_error_handler = "abort";
650   if (!strcmp(ceed_error_handler, "exit"))
651     (*ceed)->Error = CeedErrorExit;
652   else if (!strcmp(ceed_error_handler, "store"))
653     (*ceed)->Error = CeedErrorStore;
654   else
655     (*ceed)->Error = CeedErrorAbort;
656   memcpy((*ceed)->errmsg, "No error message stored", 24);
657   (*ceed)->refcount = 1;
658   (*ceed)->data = NULL;
659 
660   // Set lookup table
661   foffset foffsets[] = {
662     CEED_FTABLE_ENTRY(Ceed, Error),
663     CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
664     CEED_FTABLE_ENTRY(Ceed, Destroy),
665     CEED_FTABLE_ENTRY(Ceed, VectorCreate),
666     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
667     CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
668     CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
669     CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
670     CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
671     CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
672     CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
673     CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
674     CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
675     CEED_FTABLE_ENTRY(CeedVector, SetArray),
676     CEED_FTABLE_ENTRY(CeedVector, TakeArray),
677     CEED_FTABLE_ENTRY(CeedVector, SetValue),
678     CEED_FTABLE_ENTRY(CeedVector, GetArray),
679     CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
680     CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
681     CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
682     CEED_FTABLE_ENTRY(CeedVector, Norm),
683     CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
684     CEED_FTABLE_ENTRY(CeedVector, Destroy),
685     CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
686     CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
687     CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
688     CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
689     CEED_FTABLE_ENTRY(CeedBasis, Apply),
690     CEED_FTABLE_ENTRY(CeedBasis, Destroy),
691     CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
692     CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
693     CEED_FTABLE_ENTRY(CeedQFunction, Apply),
694     CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
695     CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
696     CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
697     CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
698     CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
699     CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
700     CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
701     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
702     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
703     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
704     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
705     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
706     CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
707     CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
708     CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
709     CEED_FTABLE_ENTRY(CeedOperator, Apply),
710     CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
711     CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
712     CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
713     CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
714     CEED_FTABLE_ENTRY(CeedOperator, Destroy),
715     {NULL, 0} // End of lookup table - used in SetBackendFunction loop
716   };
717 
718   ierr = CeedCalloc(sizeof(foffsets), &(*ceed)->foffsets); CeedChk(ierr);
719   memcpy((*ceed)->foffsets, foffsets, sizeof(foffsets));
720 
721   // Set fallback for advanced CeedOperator functions
722   const char fallbackresource[] = "";
723   ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource);
724   CeedChk(ierr);
725 
726   // Record env variables CEED_DEBUG or DBG
727   (*ceed)->debug = !!getenv("CEED_DEBUG") || !!getenv("DBG");
728 
729   // Backend specific setup
730   ierr = backends[matchidx].init(resource, *ceed); CeedChk(ierr);
731 
732   // Copy resource prefix, if backend setup sucessful
733   size_t len = strlen(backends[matchidx].prefix);
734   char *tmp;
735   ierr = CeedCalloc(len+1, &tmp); CeedChk(ierr);
736   memcpy(tmp, backends[matchidx].prefix, len+1);
737   (*ceed)->resource = tmp;
738   return CEED_ERROR_SUCCESS;
739 }
740 
741 /**
742   @brief Get the full resource name for a Ceed context
743 
744   @param ceed            Ceed context to get resource name of
745   @param[out] resource   Variable to store resource name
746 
747   @return An error code: 0 - success, otherwise - failure
748 
749   @ref User
750 **/
751 
752 int CeedGetResource(Ceed ceed, const char **resource) {
753   *resource = (const char *)ceed->resource;
754   return CEED_ERROR_SUCCESS;
755 }
756 
757 /**
758   @brief Return Ceed context preferred memory type
759 
760   @param ceed      Ceed context to get preferred memory type of
761   @param[out] type Address to save preferred memory type to
762 
763   @return An error code: 0 - success, otherwise - failure
764 
765   @ref User
766 **/
767 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *type) {
768   int ierr;
769 
770   if (ceed->GetPreferredMemType) {
771     ierr = ceed->GetPreferredMemType(type); CeedChk(ierr);
772   } else {
773     Ceed delegate;
774     ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr);
775 
776     if (delegate) {
777       ierr = CeedGetPreferredMemType(delegate, type); CeedChk(ierr);
778     } else {
779       *type = CEED_MEM_HOST;
780     }
781   }
782   return CEED_ERROR_SUCCESS;
783 }
784 
785 /**
786   @brief Get deterministic status of Ceed
787 
788   @param[in] ceed              Ceed
789   @param[out] isDeterministic  Variable to store deterministic status
790 
791   @return An error code: 0 - success, otherwise - failure
792 
793   @ref User
794 **/
795 int CeedIsDeterministic(Ceed ceed, bool *isDeterministic) {
796   *isDeterministic = ceed->isDeterministic;
797   return CEED_ERROR_SUCCESS;
798 }
799 
800 /**
801   @brief View a Ceed
802 
803   @param[in] ceed          Ceed to view
804   @param[in] stream        Filestream to write to
805 
806   @return An error code: 0 - success, otherwise - failure
807 
808   @ref User
809 **/
810 int CeedView(Ceed ceed, FILE *stream) {
811   int ierr;
812   CeedMemType memtype;
813 
814   ierr = CeedGetPreferredMemType(ceed, &memtype); CeedChk(ierr);
815 
816   fprintf(stream, "Ceed\n"
817           "  Ceed Resource: %s\n"
818           "  Preferred MemType: %s\n",
819           ceed->resource, CeedMemTypes[memtype]);
820   return CEED_ERROR_SUCCESS;
821 }
822 
823 /**
824   @brief Destroy a Ceed context
825 
826   @param ceed Address of Ceed context to destroy
827 
828   @return An error code: 0 - success, otherwise - failure
829 
830   @ref User
831 **/
832 int CeedDestroy(Ceed *ceed) {
833   int ierr;
834   if (!*ceed || --(*ceed)->refcount > 0) return CEED_ERROR_SUCCESS;
835   if ((*ceed)->delegate) {
836     ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr);
837   }
838 
839   if ((*ceed)->objdelegatecount > 0) {
840     for (int i=0; i<(*ceed)->objdelegatecount; i++) {
841       ierr = CeedDestroy(&((*ceed)->objdelegates[i].delegate)); CeedChk(ierr);
842       ierr = CeedFree(&(*ceed)->objdelegates[i].objname); CeedChk(ierr);
843     }
844     ierr = CeedFree(&(*ceed)->objdelegates); CeedChk(ierr);
845   }
846 
847   if ((*ceed)->Destroy) {
848     ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr);
849   }
850 
851   ierr = CeedFree(&(*ceed)->foffsets); CeedChk(ierr);
852   ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr);
853   ierr = CeedDestroy(&(*ceed)->opfallbackceed); CeedChk(ierr);
854   ierr = CeedFree(&(*ceed)->opfallbackresource); CeedChk(ierr);
855   ierr = CeedFree(ceed); CeedChk(ierr);
856   return CEED_ERROR_SUCCESS;
857 }
858 
859 // LCOV_EXCL_START
860 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
861   if (ceed->parent)
862     return CeedErrorFormat(ceed->parent, format, args);
863   if (ceed->opfallbackparent)
864     return CeedErrorFormat(ceed->opfallbackparent, format, args);
865   vsnprintf(ceed->errmsg, CEED_MAX_RESOURCE_LEN, format, *args);
866   return ceed->errmsg;
867 }
868 // LCOV_EXCL_STOP
869 
870 /**
871   @brief Error handling implementation; use \ref CeedError instead.
872 
873   @ref Developer
874 **/
875 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func,
876                   int ecode, const char *format, ...) {
877   va_list args;
878   int retval;
879   va_start(args, format);
880   if (ceed) {
881     retval = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
882   } else {
883     // LCOV_EXCL_START
884     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
885     if (!ceed_error_handler)
886       ceed_error_handler = "abort";
887     if (!strcmp(ceed_error_handler, "return"))
888       retval = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
889     else
890       // This function will not return
891       retval = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
892   }
893   va_end(args);
894   return retval;
895   // LCOV_EXCL_STOP
896 }
897 
898 /**
899   @brief Error handler that returns without printing anything.
900 
901   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
902 
903   @ref Developer
904 **/
905 // LCOV_EXCL_START
906 int CeedErrorReturn(Ceed ceed, const char *filename, int lineno,
907                     const char *func, int ecode, const char *format,
908                     va_list *args) {
909   return ecode;
910 }
911 // LCOV_EXCL_STOP
912 
913 /**
914   @brief Error handler that stores the error message for future use and returns
915            the error.
916 
917   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
918 
919   @ref Developer
920 **/
921 // LCOV_EXCL_START
922 int CeedErrorStore(Ceed ceed, const char *filename, int lineno,
923                    const char *func, int ecode, const char *format,
924                    va_list *args) {
925   if (ceed->parent)
926     return CeedErrorStore(ceed->parent, filename, lineno, func, ecode, format,
927                           args);
928   if (ceed->opfallbackparent)
929     return CeedErrorStore(ceed->opfallbackparent, filename, lineno, func, ecode,
930                           format, args);
931 
932   // Build message
933   CeedInt len;
934   len = snprintf(ceed->errmsg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ",
935                  filename, lineno, func);
936   vsnprintf(ceed->errmsg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);
937   return ecode;
938 }
939 // LCOV_EXCL_STOP
940 
941 /**
942   @brief Error handler that prints to stderr and aborts
943 
944   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
945 
946   @ref Developer
947 **/
948 // LCOV_EXCL_START
949 int CeedErrorAbort(Ceed ceed, const char *filename, int lineno,
950                    const char *func, int ecode, const char *format,
951                    va_list *args) {
952   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
953   vfprintf(stderr, format, *args);
954   fprintf(stderr, "\n");
955   abort();
956   return ecode;
957 }
958 // LCOV_EXCL_STOP
959 
960 /**
961   @brief Error handler that prints to stderr and exits
962 
963   Pass this to CeedSetErrorHandler() to obtain this error handling behavior.
964 
965   In contrast to CeedErrorAbort(), this exits without a signal, so atexit()
966   handlers (e.g., as used by gcov) are run.
967 
968   @ref Developer
969 **/
970 int CeedErrorExit(Ceed ceed, const char *filename, int lineno, const char *func,
971                   int ecode, const char *format, va_list *args) {
972   fprintf(stderr, "%s:%d in %s(): ", filename, lineno, func);
973   vfprintf(stderr, format, *args);
974   fprintf(stderr, "\n");
975   exit(ecode);
976   return ecode;
977 }
978 
979 /**
980   @brief Set error handler
981 
982   A default error handler is set in CeedInit().  Use this function to change
983   the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined
984   error handler.
985 
986   @ref Developer
987 **/
988 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler eh) {
989   ceed->Error = eh;
990   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, eh);
991   for (int i=0; i<ceed->objdelegatecount; i++)
992     CeedSetErrorHandler(ceed->objdelegates[i].delegate, eh);
993   return CEED_ERROR_SUCCESS;
994 }
995 
996 /**
997   @brief Get error message
998 
999   The error message is only stored when using the error handler
1000     CeedErrorStore()
1001 
1002   @param[in] ceed     Ceed contex to retrieve error message
1003   @param[out] errmsg  Char pointer to hold error message
1004 
1005   @ref Developer
1006 **/
1007 int CeedGetErrorMessage(Ceed ceed, const char **errmsg) {
1008   if (ceed->parent)
1009     return CeedGetErrorMessage(ceed->parent, errmsg);
1010   if (ceed->opfallbackparent)
1011     return CeedGetErrorMessage(ceed->opfallbackparent, errmsg);
1012   *errmsg = ceed->errmsg;
1013   return CEED_ERROR_SUCCESS;
1014 }
1015 
1016 /**
1017   @brief Restore error message
1018 
1019   The error message is only stored when using the error handler
1020     CeedErrorStore()
1021 
1022   @param[in] ceed     Ceed contex to restore error message
1023   @param[out] errmsg  Char pointer that holds error message
1024 
1025   @ref Developer
1026 **/
1027 int CeedResetErrorMessage(Ceed ceed, const char **errmsg) {
1028   if (ceed->parent)
1029     return CeedResetErrorMessage(ceed->parent, errmsg);
1030   if (ceed->opfallbackparent)
1031     return CeedResetErrorMessage(ceed->opfallbackparent, errmsg);
1032   *errmsg = NULL;
1033   memcpy(ceed->errmsg, "No error message stored", 24);
1034   return CEED_ERROR_SUCCESS;
1035 }
1036 
1037 /**
1038   @brief Get libCEED library version info
1039 
1040   libCEED version numbers have the form major.minor.patch. Non-release versions
1041   may contain unstable interfaces.
1042 
1043   @param[out] major    Major version of the library
1044   @param[out] minor    Minor version of the library
1045   @param[out] patch    Patch (subminor) version of the library
1046   @param[out] release  True for releases; false for development branches.
1047 
1048   The caller may pass NULL for any arguments that are not needed.
1049 
1050   @sa CEED_VERSION_GE()
1051 
1052   @ref Developer
1053 */
1054 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1055   if (major) *major = CEED_VERSION_MAJOR;
1056   if (minor) *minor = CEED_VERSION_MINOR;
1057   if (patch) *patch = CEED_VERSION_PATCH;
1058   if (release) *release = CEED_VERSION_RELEASE;
1059   return 0;
1060 }
1061 
1062 /// @}
1063