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