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