xref: /libCEED/interface/ceed.c (revision 4753b775a3a8f79e2dd83c2aab10890a6a04e913)
1 // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors.
2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
3 //
4 // SPDX-License-Identifier: BSD-2-Clause
5 //
6 // This file is part of CEED:  http://github.com/ceed
7 
8 #define _POSIX_C_SOURCE 200112
9 #include <ceed-impl.h>
10 #include <ceed.h>
11 #include <ceed/backend.h>
12 #include <limits.h>
13 #include <stdarg.h>
14 #include <stdbool.h>
15 #include <stddef.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 
20 /// @cond DOXYGEN_SKIP
21 static CeedRequest ceed_request_immediate;
22 static CeedRequest ceed_request_ordered;
23 
24 static struct {
25   char prefix[CEED_MAX_RESOURCE_LEN];
26   int (*init)(const char *resource, Ceed f);
27   unsigned int priority;
28 } backends[32];
29 static size_t num_backends;
30 
31 #define CEED_FTABLE_ENTRY(class, method) {#class #method, offsetof(struct class##_private, method)}
32 /// @endcond
33 
34 /// @file
35 /// Implementation of core components of Ceed library
36 
37 /// @addtogroup CeedUser
38 /// @{
39 
40 /**
41   @brief Request immediate completion
42 
43   This predefined constant is passed as the @ref CeedRequest argument to interfaces when the caller wishes for the operation to be performed immediately.
44   The code
45 
46   @code
47     CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE);
48   @endcode
49 
50   is semantically equivalent to
51 
52   @code
53     CeedRequest request;
54     CeedOperatorApply(op, ..., &request);
55     CeedRequestWait(&request);
56   @endcode
57 
58   @sa CEED_REQUEST_ORDERED
59 **/
60 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate;
61 
62 /**
63   @brief Request ordered completion
64 
65   This predefined constant is passed as the @ref CeedRequest argument to interfaces when the caller wishes for the operation to be completed in the order that it is submitted to the device.
66   It is typically used in a construct such as:
67 
68   @code
69     CeedRequest request;
70     CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED);
71     CeedOperatorApply(op2, ..., &request);
72     // other optional work
73     CeedRequestWait(&request);
74   @endcode
75 
76   which allows the sequence to complete asynchronously but does not start `op2` until `op1` has completed.
77 
78   @todo The current implementation is overly strict, offering equivalent semantics to @ref CEED_REQUEST_IMMEDIATE.
79 
80   @sa CEED_REQUEST_IMMEDIATE
81  */
82 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered;
83 
84 /**
85   @brief Wait for a @ref CeedRequest to complete.
86 
87   Calling @ref CeedRequestWait() on a `NULL` request is a no-op.
88 
89   @param[in,out] req Address of @ref CeedRequest to wait for; zeroed on completion.
90 
91   @return An error code: 0 - success, otherwise - failure
92 
93   @ref User
94 **/
95 int CeedRequestWait(CeedRequest *req) {
96   if (!*req) return CEED_ERROR_SUCCESS;
97   return CeedError(NULL, CEED_ERROR_UNSUPPORTED, "CeedRequestWait not implemented");
98 }
99 
100 /// @}
101 
102 /// ----------------------------------------------------------------------------
103 /// Ceed Library Internal Functions
104 /// ----------------------------------------------------------------------------
105 /// @addtogroup CeedDeveloper
106 /// @{
107 
108 /**
109   @brief Register a Ceed backend internally.
110 
111   Note: Backends should call @ref CeedRegister() instead.
112 
113   @param[in] prefix   Prefix of resources for this backend to respond to.
114                         For example, the reference backend responds to "/cpu/self".
115   @param[in] init     Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
116   @param[in] priority Integer priority.
117                         Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
118 
119   @return An error code: 0 - success, otherwise - failure
120 
121   @ref Developer
122 **/
123 int CeedRegisterImpl(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
124   int ierr = 0;
125 
126   CeedPragmaCritical(CeedRegisterImpl) {
127     if (num_backends < sizeof(backends) / sizeof(backends[0])) {
128       strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN);
129       backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN - 1] = 0;
130       backends[num_backends].init                              = init;
131       backends[num_backends].priority                          = priority;
132       num_backends++;
133     } else {
134       ierr = 1;
135     }
136   }
137   CeedCheck(ierr == 0, NULL, CEED_ERROR_MAJOR, "Too many backends");
138   return CEED_ERROR_SUCCESS;
139 }
140 
141 /**
142   @brief Create a work vector space for a `ceed`
143 
144   @param[in,out] ceed `Ceed` to create work vector space for
145 
146   @return An error code: 0 - success, otherwise - failure
147 
148   @ref Developer
149 **/
150 static int CeedWorkVectorsCreate(Ceed ceed) {
151   CeedCall(CeedCalloc(1, &ceed->work_vectors));
152   return CEED_ERROR_SUCCESS;
153 }
154 
155 /**
156   @brief Destroy a work vector space for a `ceed`
157 
158   @param[in,out] ceed `Ceed` to destroy work vector space for
159 
160   @return An error code: 0 - success, otherwise - failure
161 
162   @ref Developer
163 **/
164 static int CeedWorkVectorsDestroy(Ceed ceed) {
165   if (!ceed->work_vectors) return CEED_ERROR_SUCCESS;
166   for (CeedSize i = 0; i < ceed->work_vectors->num_vecs; i++) {
167     CeedCheck(!ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " checked out but not returned");
168     ceed->ref_count += 2;  // Note: increase ref_count to prevent Ceed destructor from triggering again
169     CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i]));
170     ceed->ref_count -= 1;  // Note: restore ref_count
171   }
172   CeedCall(CeedFree(&ceed->work_vectors->is_in_use));
173   CeedCall(CeedFree(&ceed->work_vectors->vecs));
174   CeedCall(CeedFree(&ceed->work_vectors));
175   return CEED_ERROR_SUCCESS;
176 }
177 
178 /// @}
179 
180 /// ----------------------------------------------------------------------------
181 /// Ceed Backend API
182 /// ----------------------------------------------------------------------------
183 /// @addtogroup CeedBackend
184 /// @{
185 
186 /**
187   @brief Return value of `CEED_DEBUG` environment variable
188 
189   @param[in] ceed `Ceed` context
190 
191   @return Boolean value: true  - debugging mode enabled
192                          false - debugging mode disabled
193 
194   @ref Backend
195 **/
196 // LCOV_EXCL_START
197 bool CeedDebugFlag(const Ceed ceed) { return ceed->is_debug; }
198 // LCOV_EXCL_STOP
199 
200 /**
201   @brief Return value of `CEED_DEBUG` environment variable
202 
203   @return Boolean value: true  - debugging mode enabled
204                          false - debugging mode disabled
205 
206   @ref Backend
207 **/
208 // LCOV_EXCL_START
209 bool CeedDebugFlagEnv(void) { return getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG"); }
210 // LCOV_EXCL_STOP
211 
212 /**
213   @brief Print debugging information in color
214 
215   @param[in] color  Color to print
216   @param[in] format Printing format
217 
218   @ref Backend
219 **/
220 // LCOV_EXCL_START
221 void CeedDebugImpl256(const unsigned char color, const char *format, ...) {
222   va_list args;
223   va_start(args, format);
224   fflush(stdout);
225   if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[38;5;%dm", color);
226   vfprintf(stdout, format, args);
227   if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[m");
228   fprintf(stdout, "\n");
229   fflush(stdout);
230   va_end(args);
231 }
232 // LCOV_EXCL_STOP
233 
234 /**
235   @brief Allocate an array on the host; use @ref CeedMalloc().
236 
237   Memory usage can be tracked by the library.
238   This ensures sufficient alignment for vectorization and should be used for large allocations.
239 
240   @param[in]  n    Number of units to allocate
241   @param[in]  unit Size of each unit
242   @param[out] p    Address of pointer to hold the result
243 
244   @return An error code: 0 - success, otherwise - failure
245 
246   @ref Backend
247 
248   @sa CeedFree()
249 **/
250 int CeedMallocArray(size_t n, size_t unit, void *p) {
251   int ierr = posix_memalign((void **)p, CEED_ALIGN, n * unit);
252   CeedCheck(ierr == 0, NULL, CEED_ERROR_MAJOR, "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
253   return CEED_ERROR_SUCCESS;
254 }
255 
256 /**
257   @brief Allocate a cleared (zeroed) array on the host; use @ref CeedCalloc().
258 
259   Memory usage can be tracked by the library.
260 
261   @param[in]  n    Number of units to allocate
262   @param[in]  unit Size of each unit
263   @param[out] p    Address of pointer to hold the result
264 
265   @return An error code: 0 - success, otherwise - failure
266 
267   @ref Backend
268 
269   @sa CeedFree()
270 **/
271 int CeedCallocArray(size_t n, size_t unit, void *p) {
272   *(void **)p = calloc(n, unit);
273   CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "calloc failed to allocate %zd members of size %zd\n", n, unit);
274   return CEED_ERROR_SUCCESS;
275 }
276 
277 /**
278   @brief Reallocate an array on the host; use @ref CeedRealloc().
279 
280   Memory usage can be tracked by the library.
281 
282   @param[in]  n    Number of units to allocate
283   @param[in]  unit Size of each unit
284   @param[out] p    Address of pointer to hold the result
285 
286   @return An error code: 0 - success, otherwise - failure
287 
288   @ref Backend
289 
290   @sa CeedFree()
291 **/
292 int CeedReallocArray(size_t n, size_t unit, void *p) {
293   *(void **)p = realloc(*(void **)p, n * unit);
294   CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "realloc failed to allocate %zd members of size %zd\n", n, unit);
295   return CEED_ERROR_SUCCESS;
296 }
297 
298 /**
299   @brief Allocate a cleared string buffer on the host.
300 
301   Memory usage can be tracked by the library.
302 
303   @param[in]  source Pointer to string to be copied
304   @param[out] copy   Pointer to variable to hold newly allocated string copy
305 
306   @return An error code: 0 - success, otherwise - failure
307 
308   @ref Backend
309 
310   @sa CeedFree()
311 **/
312 int CeedStringAllocCopy(const char *source, char **copy) {
313   size_t len = strlen(source);
314   CeedCall(CeedCalloc(len + 1, copy));
315   memcpy(*copy, source, len);
316   return CEED_ERROR_SUCCESS;
317 }
318 
319 /** Free memory allocated using @ref CeedMalloc() or @ref CeedCalloc()
320 
321   @param[in,out] p Address of pointer to memory.
322                      This argument is of type `void*` to avoid needing a cast, but is the address of the pointer (which is zeroed) rather than the pointer.
323 
324   @return An error code: 0 - success, otherwise - failure
325 
326   @ref Backend
327 **/
328 int CeedFree(void *p) {
329   free(*(void **)p);
330   *(void **)p = NULL;
331   return CEED_ERROR_SUCCESS;
332 }
333 
334 /** Internal helper to manage handoff of user `source_array` to backend with proper @ref CeedCopyMode behavior.
335 
336   @param[in]     source_array          Source data provided by user
337   @param[in]     copy_mode             Copy mode for the data
338   @param[in]     num_values            Number of values to handle
339   @param[in]     size_unit             Size of array element in bytes
340   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
341   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
342   @param[out]    target_array          Pointer to location for data
343 
344   @return An error code: 0 - success, otherwise - failure
345 
346   @ref Backend
347 **/
348 static inline int CeedSetHostGenericArray(const void *source_array, CeedCopyMode copy_mode, size_t size_unit, CeedSize num_values,
349                                           void *target_array_owned, void *target_array_borrowed, void *target_array) {
350   switch (copy_mode) {
351     case CEED_COPY_VALUES:
352       if (!*(void **)target_array_owned) CeedCall(CeedCallocArray(num_values, size_unit, target_array_owned));
353       if (source_array) memcpy(*(void **)target_array_owned, source_array, size_unit * num_values);
354       *(void **)target_array_borrowed = NULL;
355       *(void **)target_array          = *(void **)target_array_owned;
356       break;
357     case CEED_OWN_POINTER:
358       CeedCall(CeedFree(target_array_owned));
359       *(void **)target_array_owned    = (void *)source_array;
360       *(void **)target_array_borrowed = NULL;
361       *(void **)target_array          = *(void **)target_array_owned;
362       break;
363     case CEED_USE_POINTER:
364       CeedCall(CeedFree(target_array_owned));
365       *(void **)target_array_owned    = NULL;
366       *(void **)target_array_borrowed = (void *)source_array;
367       *(void **)target_array          = *(void **)target_array_borrowed;
368   }
369   return CEED_ERROR_SUCCESS;
370 }
371 
372 /** Manage handoff of user `bool` `source_array` to backend with proper @ref CeedCopyMode behavior.
373 
374   @param[in]     source_array          Source data provided by user
375   @param[in]     copy_mode             Copy mode for the data
376   @param[in]     num_values            Number of values to handle
377   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
378   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
379   @param[out]    target_array          Pointer to location for data
380 
381   @return An error code: 0 - success, otherwise - failure
382 
383   @ref Backend
384 **/
385 int CeedSetHostBoolArray(const bool *source_array, CeedCopyMode copy_mode, CeedSize num_values, const bool **target_array_owned,
386                          const bool **target_array_borrowed, const bool **target_array) {
387   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(bool), num_values, target_array_owned, target_array_borrowed, target_array));
388   return CEED_ERROR_SUCCESS;
389 }
390 
391 /** Manage handoff of user `CeedInt8` `source_array` to backend with proper @ref CeedCopyMode behavior.
392 
393   @param[in]     source_array          Source data provided by user
394   @param[in]     copy_mode             Copy mode for the data
395   @param[in]     num_values            Number of values to handle
396   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
397   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
398   @param[out]    target_array          Pointer to location for data
399 
400   @return An error code: 0 - success, otherwise - failure
401 
402   @ref Backend
403 **/
404 int CeedSetHostCeedInt8Array(const CeedInt8 *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt8 **target_array_owned,
405                              const CeedInt8 **target_array_borrowed, const CeedInt8 **target_array) {
406   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt8), num_values, target_array_owned, target_array_borrowed, target_array));
407   return CEED_ERROR_SUCCESS;
408 }
409 
410 /** Manage handoff of user `CeedInt` `source_array` to backend with proper @ref CeedCopyMode behavior.
411 
412   @param[in]     source_array          Source data provided by user
413   @param[in]     copy_mode             Copy mode for the data
414   @param[in]     num_values            Number of values to handle
415   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
416   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
417   @param[out]    target_array          Pointer to location for data
418 
419   @return An error code: 0 - success, otherwise - failure
420 
421   @ref Backend
422 **/
423 int CeedSetHostCeedIntArray(const CeedInt *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt **target_array_owned,
424                             const CeedInt **target_array_borrowed, const CeedInt **target_array) {
425   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt), num_values, target_array_owned, target_array_borrowed, target_array));
426   return CEED_ERROR_SUCCESS;
427 }
428 
429 /** Manage handoff of user `CeedScalar` `source_array` to backend with proper @ref CeedCopyMode behavior.
430 
431   @param[in]     source_array          Source data provided by user
432   @param[in]     copy_mode             Copy mode for the data
433   @param[in]     num_values            Number of values to handle
434   @param[in,out] target_array_owned    Pointer to location to allocated or hold owned data, may be freed if already allocated
435   @param[out]    target_array_borrowed Pointer to location to hold borrowed data
436   @param[out]    target_array          Pointer to location for data
437 
438   @return An error code: 0 - success, otherwise - failure
439 
440   @ref Backend
441 **/
442 int CeedSetHostCeedScalarArray(const CeedScalar *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedScalar **target_array_owned,
443                                const CeedScalar **target_array_borrowed, const CeedScalar **target_array) {
444   CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedScalar), num_values, target_array_owned, target_array_borrowed, target_array));
445   return CEED_ERROR_SUCCESS;
446 }
447 
448 /**
449   @brief Register a `Ceed` backend
450 
451   @param[in] prefix   Prefix of resources for this backend to respond to.
452                         For example, the reference backend responds to "/cpu/self".
453   @param[in] init     Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
454   @param[in] priority Integer priority.
455                         Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
456 
457   @return An error code: 0 - success, otherwise - failure
458 
459   @ref Backend
460 **/
461 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
462   CeedDebugEnv("Backend Register: %s", prefix);
463   CeedRegisterImpl(prefix, init, priority);
464   return CEED_ERROR_SUCCESS;
465 }
466 
467 /**
468   @brief Return debugging status flag
469 
470   @param[in]  ceed     `Ceed` context to get debugging flag
471   @param[out] is_debug Variable to store debugging flag
472 
473   @return An error code: 0 - success, otherwise - failure
474 
475   @ref Backend
476 **/
477 int CeedIsDebug(Ceed ceed, bool *is_debug) {
478   *is_debug = ceed->is_debug;
479   return CEED_ERROR_SUCCESS;
480 }
481 
482 /**
483   @brief Get the root of the requested resource.
484 
485   Note: Caller is responsible for calling @ref CeedFree() on the `resource_root`.
486 
487   @param[in]  ceed          `Ceed` context to get resource name of
488   @param[in]  resource      Full user specified resource
489   @param[in]  delineator    Delineator to break `resource_root` and `resource_spec`
490   @param[out] resource_root Variable to store resource root
491 
492   @return An error code: 0 - success, otherwise - failure
493 
494   @ref Backend
495 **/
496 int CeedGetResourceRoot(Ceed ceed, const char *resource, const char *delineator, char **resource_root) {
497   char  *device_spec       = strstr(resource, delineator);
498   size_t resource_root_len = device_spec ? (size_t)(device_spec - resource) + 1 : strlen(resource) + 1;
499 
500   CeedCall(CeedCalloc(resource_root_len, resource_root));
501   memcpy(*resource_root, resource, resource_root_len - 1);
502   return CEED_ERROR_SUCCESS;
503 }
504 
505 /**
506   @brief Retrieve a parent `Ceed` context
507 
508   @param[in]  ceed   `Ceed` context to retrieve parent of
509   @param[out] parent Address to save the parent to
510 
511   @return An error code: 0 - success, otherwise - failure
512 
513   @ref Backend
514 **/
515 int CeedGetParent(Ceed ceed, Ceed *parent) {
516   if (ceed->parent) {
517     CeedCall(CeedGetParent(ceed->parent, parent));
518     return CEED_ERROR_SUCCESS;
519   }
520   *parent = ceed;
521   return CEED_ERROR_SUCCESS;
522 }
523 
524 /**
525   @brief Retrieve a delegate `Ceed` context
526 
527   @param[in]  ceed     `Ceed` context to retrieve delegate of
528   @param[out] delegate Address to save the delegate to
529 
530   @return An error code: 0 - success, otherwise - failure
531 
532   @ref Backend
533 **/
534 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
535   *delegate = ceed->delegate;
536   return CEED_ERROR_SUCCESS;
537 }
538 
539 /**
540   @brief Set a delegate `Ceed` context
541 
542   This function allows a `Ceed` context to set a delegate `Ceed` context.
543   All backend implementations default to the delegate `Ceed` context, unless overridden.
544 
545   @param[in]  ceed     `Ceed` context to set delegate of
546   @param[out] delegate Address to set the delegate to
547 
548   @return An error code: 0 - success, otherwise - failure
549 
550   @ref Backend
551 **/
552 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
553   ceed->delegate   = delegate;
554   delegate->parent = ceed;
555   return CEED_ERROR_SUCCESS;
556 }
557 
558 /**
559   @brief Retrieve a delegate `Ceed` context for a specific object type
560 
561   @param[in]  ceed     `Ceed` context to retrieve delegate of
562   @param[out] delegate Address to save the delegate to
563   @param[in]  obj_name Name of the object type to retrieve delegate for
564 
565   @return An error code: 0 - success, otherwise - failure
566 
567   @ref Backend
568 **/
569 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) {
570   // Check for object delegate
571   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) {
572     if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) {
573       *delegate = ceed->obj_delegates->delegate;
574       return CEED_ERROR_SUCCESS;
575     }
576   }
577 
578   // Use default delegate if no object delegate
579   CeedCall(CeedGetDelegate(ceed, delegate));
580   return CEED_ERROR_SUCCESS;
581 }
582 
583 /**
584   @brief Set a delegate `Ceed` context for a specific object type
585 
586   This function allows a `Ceed` context to set a delegate `Ceed` context for a given type of `Ceed` object.
587   All backend implementations default to the delegate `Ceed` context for this object.
588   For example, `CeedSetObjectDelegate(ceed, delegate, "Basis")` uses delegate implementations for all `CeedBasis` backend functions.
589 
590   @param[in,out] ceed     `Ceed` context to set delegate of
591   @param[in]     delegate `Ceed` context to use for delegation
592   @param[in]     obj_name Name of the object type to set delegate for
593 
594   @return An error code: 0 - success, otherwise - failure
595 
596   @ref Backend
597 **/
598 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) {
599   CeedInt count = ceed->obj_delegate_count;
600 
601   // Malloc or Realloc
602   if (count) {
603     CeedCall(CeedRealloc(count + 1, &ceed->obj_delegates));
604   } else {
605     CeedCall(CeedCalloc(1, &ceed->obj_delegates));
606   }
607   ceed->obj_delegate_count++;
608 
609   // Set object delegate
610   ceed->obj_delegates[count].delegate = delegate;
611   CeedCall(CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name));
612 
613   // Set delegate parent
614   delegate->parent = ceed;
615   return CEED_ERROR_SUCCESS;
616 }
617 
618 /**
619   @brief Get the fallback resource for `CeedOperator`
620 
621   @param[in]  ceed     `Ceed` context
622   @param[out] resource Variable to store fallback resource
623 
624   @return An error code: 0 - success, otherwise - failure
625 
626   @ref Backend
627 **/
628 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) {
629   *resource = (const char *)ceed->op_fallback_resource;
630   return CEED_ERROR_SUCCESS;
631 }
632 
633 /**
634   @brief Get the fallback `Ceed` for `CeedOperator`
635 
636   @param[in]  ceed          `Ceed` context
637   @param[out] fallback_ceed Variable to store fallback `Ceed`
638 
639   @return An error code: 0 - success, otherwise - failure
640 
641   @ref Backend
642 **/
643 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) {
644   if (ceed->has_valid_op_fallback_resource) {
645     CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- CeedOperator Fallback ----------\n");
646     CeedDebug(ceed, "Getting fallback from %s to %s\n", ceed->resource, ceed->op_fallback_resource);
647   }
648 
649   // Create fallback Ceed if uninitalized
650   if (!ceed->op_fallback_ceed && ceed->has_valid_op_fallback_resource) {
651     CeedDebug(ceed, "Creating fallback Ceed");
652 
653     Ceed        fallback_ceed;
654     const char *fallback_resource;
655 
656     CeedCall(CeedGetOperatorFallbackResource(ceed, &fallback_resource));
657     CeedCall(CeedInit(fallback_resource, &fallback_ceed));
658     fallback_ceed->op_fallback_parent = ceed;
659     fallback_ceed->Error              = ceed->Error;
660     ceed->op_fallback_ceed            = fallback_ceed;
661     {
662       const char **jit_source_dirs;
663       CeedInt      num_jit_source_dirs = 0;
664 
665       CeedCall(CeedGetJitSourceRoots(ceed, &num_jit_source_dirs, &jit_source_dirs));
666       for (CeedInt i = 0; i < num_jit_source_dirs; i++) {
667         CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_source_dirs[i]));
668       }
669       CeedCall(CeedRestoreJitSourceRoots(ceed, &jit_source_dirs));
670     }
671     {
672       const char **jit_defines;
673       CeedInt      num_jit_defines = 0;
674 
675       CeedCall(CeedGetJitDefines(ceed, &num_jit_defines, &jit_defines));
676       for (CeedInt i = 0; i < num_jit_defines; i++) {
677         CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_defines[i]));
678       }
679       CeedCall(CeedRestoreJitDefines(ceed, &jit_defines));
680     }
681   }
682   *fallback_ceed = ceed->op_fallback_ceed;
683   return CEED_ERROR_SUCCESS;
684 }
685 
686 /**
687   @brief Set the fallback resource for `CeedOperator`.
688 
689   The current resource, if any, is freed by calling this function.
690   This string is freed upon the destruction of the `Ceed` context.
691 
692   @param[in,out] ceed     `Ceed` context
693   @param[in]     resource Fallback resource to set
694 
695   @return An error code: 0 - success, otherwise - failure
696 
697   @ref Backend
698 **/
699 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) {
700   // Free old
701   CeedCall(CeedFree(&ceed->op_fallback_resource));
702 
703   // Set new
704   CeedCall(CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource));
705 
706   // Check validity
707   ceed->has_valid_op_fallback_resource = ceed->op_fallback_resource && ceed->resource && strcmp(ceed->op_fallback_resource, ceed->resource);
708   return CEED_ERROR_SUCCESS;
709 }
710 
711 /**
712   @brief Flag `Ceed` context as deterministic
713 
714   @param[in]  ceed             `Ceed` to flag as deterministic
715   @param[out] is_deterministic Deterministic status to set
716 
717   @return An error code: 0 - success, otherwise - failure
718 
719   @ref Backend
720 **/
721 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
722   ceed->is_deterministic = is_deterministic;
723   return CEED_ERROR_SUCCESS;
724 }
725 
726 /**
727   @brief Set a backend function.
728 
729   This function is used for a backend to set the function associated with the Ceed objects.
730   For example, `CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate)` sets the backend implementation of @ref CeedVectorCreate() and `CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply)` sets the backend implementation of @ref CeedBasisApply().
731   Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
732 
733   @param[in]  ceed      `Ceed` context for error handling
734   @param[in]  type      Type of Ceed object to set function for
735   @param[out] object    Ceed object to set function for
736   @param[in]  func_name Name of function to set
737   @param[in]  f         Function to set
738 
739   @return An error code: 0 - success, otherwise - failure
740 
741   @ref Backend
742 **/
743 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
744   char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
745 
746   // Build lookup name
747   if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
748   strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
749   strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
750 
751   // Find and use offset
752   for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
753     if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
754       size_t offset          = ceed->f_offsets[i].offset;
755       int (**fpointer)(void) = (int (**)(void))((char *)object + offset);  // *NOPAD*
756 
757       *fpointer = (int (*)(void))f;
758       return CEED_ERROR_SUCCESS;
759     }
760   }
761 
762   // LCOV_EXCL_START
763   return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
764   // LCOV_EXCL_STOP
765 }
766 
767 /**
768   @brief Retrieve backend data for a `Ceed` context
769 
770   @param[in]  ceed `Ceed` context to retrieve data of
771   @param[out] data Address to save data to
772 
773   @return An error code: 0 - success, otherwise - failure
774 
775   @ref Backend
776 **/
777 int CeedGetData(Ceed ceed, void *data) {
778   *(void **)data = ceed->data;
779   return CEED_ERROR_SUCCESS;
780 }
781 
782 /**
783   @brief Set backend data for a `Ceed` context
784 
785   @param[in,out] ceed `Ceed` context to set data of
786   @param[in]     data Address of data to set
787 
788   @return An error code: 0 - success, otherwise - failure
789 
790   @ref Backend
791 **/
792 int CeedSetData(Ceed ceed, void *data) {
793   ceed->data = data;
794   return CEED_ERROR_SUCCESS;
795 }
796 
797 /**
798   @brief Increment the reference counter for a `Ceed` context
799 
800   @param[in,out] ceed `Ceed` context to increment the reference counter
801 
802   @return An error code: 0 - success, otherwise - failure
803 
804   @ref Backend
805 **/
806 int CeedReference(Ceed ceed) {
807   ceed->ref_count++;
808   return CEED_ERROR_SUCCESS;
809 }
810 
811 /**
812   @brief Get a `CeedVector` for scratch work from a `Ceed` context.
813 
814   Note: This vector must be restored with @ref CeedRestoreWorkVector().
815 
816   @param[in]  ceed `Ceed` context
817   @param[in]  len  Minimum length of work vector
818   @param[out] vec  Address of the variable where `CeedVector` will be stored
819 
820   @return An error code: 0 - success, otherwise - failure
821 
822   @ref Backend
823 **/
824 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) {
825   CeedInt i = 0;
826 
827   if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed));
828 
829   // Search for big enough work vector
830   for (i = 0; i < ceed->work_vectors->num_vecs; i++) {
831     if (!ceed->work_vectors->is_in_use[i]) {
832       CeedSize work_len;
833 
834       CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len));
835       if (work_len >= len) break;
836     }
837   }
838   // Long enough vector was not found
839   if (i == ceed->work_vectors->num_vecs) {
840     if (ceed->work_vectors->max_vecs == 0) {
841       ceed->work_vectors->max_vecs = 1;
842       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
843       CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
844     } else if (ceed->work_vectors->max_vecs == i) {
845       ceed->work_vectors->max_vecs *= 2;
846       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
847       CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
848     }
849     ceed->work_vectors->num_vecs++;
850     CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i]));
851     ceed->ref_count--;  // Note: ref_count manipulation to prevent a ref-loop
852   }
853   // Return pointer to work vector
854   ceed->work_vectors->is_in_use[i] = true;
855   *vec                             = NULL;
856   CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec));
857   ceed->ref_count++;  // Note: bump ref_count to account for external access
858   return CEED_ERROR_SUCCESS;
859 }
860 
861 /**
862   @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector()
863 
864   @param[in]  ceed `Ceed` context
865   @param[out] vec  `CeedVector` to restore
866 
867   @return An error code: 0 - success, otherwise - failure
868 
869   @ref Backend
870 **/
871 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) {
872   for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
873     if (*vec == ceed->work_vectors->vecs[i]) {
874       CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned");
875       CeedCall(CeedVectorDestroy(vec));
876       ceed->work_vectors->is_in_use[i] = false;
877       ceed->ref_count--;  // Note: reduce ref_count again to prevent a ref-loop
878       return CEED_ERROR_SUCCESS;
879     }
880   }
881   // LCOV_EXCL_START
882   return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()");
883   // LCOV_EXCL_STOP
884 }
885 
886 /**
887   @brief Retrieve list of additional JiT source roots from `Ceed` context.
888 
889   Note: The caller is responsible for restoring `jit_source_roots` with @ref CeedRestoreJitSourceRoots().
890 
891   @param[in]  ceed             `Ceed` context
892   @param[out] num_source_roots Number of JiT source directories
893   @param[out] jit_source_roots Absolute paths to additional JiT source directories
894 
895   @return An error code: 0 - success, otherwise - failure
896 
897   @ref Backend
898 **/
899 int CeedGetJitSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***jit_source_roots) {
900   Ceed ceed_parent;
901 
902   CeedCall(CeedGetParent(ceed, &ceed_parent));
903   *num_source_roots = ceed_parent->num_jit_source_roots;
904   *jit_source_roots = (const char **)ceed_parent->jit_source_roots;
905   return CEED_ERROR_SUCCESS;
906 }
907 
908 /**
909   @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots()
910 
911   @param[in]  ceed             `Ceed` context
912   @param[out] jit_source_roots Absolute paths to additional JiT source directories
913 
914   @return An error code: 0 - success, otherwise - failure
915 
916   @ref Backend
917 **/
918 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) {
919   *jit_source_roots = NULL;
920   return CEED_ERROR_SUCCESS;
921 }
922 
923 /**
924   @brief Retrieve list of additional JiT defines from `Ceed` context.
925 
926   Note: The caller is responsible for restoring `jit_defines` with @ref CeedRestoreJitDefines().
927 
928   @param[in]  ceed            `Ceed` context
929   @param[out] num_jit_defines Number of JiT defines
930   @param[out] jit_defines     Strings such as `foo=bar`, used as `-Dfoo=bar` in JiT
931 
932   @return An error code: 0 - success, otherwise - failure
933 
934   @ref Backend
935 **/
936 int CeedGetJitDefines(Ceed ceed, CeedInt *num_defines, const char ***jit_defines) {
937   Ceed ceed_parent;
938 
939   CeedCall(CeedGetParent(ceed, &ceed_parent));
940   *num_defines = ceed_parent->num_jit_defines;
941   *jit_defines = (const char **)ceed_parent->jit_defines;
942   return CEED_ERROR_SUCCESS;
943 }
944 
945 /**
946   @brief Restore list of additional JiT defines from with @ref CeedGetJitDefines()
947 
948   @param[in]  ceed        `Ceed` context
949   @param[out] jit_defines String such as `foo=bar`, used as `-Dfoo=bar` in JiT
950 
951   @return An error code: 0 - success, otherwise - failure
952 
953   @ref Backend
954 **/
955 int CeedRestoreJitDefines(Ceed ceed, const char ***jit_defines) {
956   *jit_defines = NULL;
957   return CEED_ERROR_SUCCESS;
958 }
959 
960 /// @}
961 
962 /// ----------------------------------------------------------------------------
963 /// Ceed Public API
964 /// ----------------------------------------------------------------------------
965 /// @addtogroup CeedUser
966 /// @{
967 
968 /**
969   @brief Get the list of available resource names for `Ceed` contexts
970 
971   Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
972 
973   @param[out] n          Number of available resources
974   @param[out] resources  List of available resource names
975   @param[out] priorities Resource name prioritization values, lower is better
976 
977   @return An error code: 0 - success, otherwise - failure
978 
979   @ref User
980 **/
981 // LCOV_EXCL_START
982 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
983   *n         = 0;
984   *resources = malloc(num_backends * sizeof(**resources));
985   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
986   if (priorities) {
987     *priorities = malloc(num_backends * sizeof(**priorities));
988     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
989   }
990   for (size_t i = 0; i < num_backends; i++) {
991     // Only report compiled backends
992     if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
993       *resources[i] = backends[i].prefix;
994       if (priorities) *priorities[i] = backends[i].priority;
995       *n += 1;
996     }
997   }
998   CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
999   *resources = realloc(*resources, *n * sizeof(**resources));
1000   CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1001   if (priorities) {
1002     *priorities = realloc(*priorities, *n * sizeof(**priorities));
1003     CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1004   }
1005   return CEED_ERROR_SUCCESS;
1006 }
1007 // LCOV_EXCL_STOP
1008 
1009 /**
1010   @brief Initialize a `Ceed` context to use the specified resource.
1011 
1012   Note: Prefixing the resource with "help:" (e.g. "help:/cpu/self") will result in @ref CeedInt() printing the current libCEED version number and a list of current available backend resources to `stderr`.
1013 
1014   @param[in]  resource Resource to use, e.g., "/cpu/self"
1015   @param[out] ceed     The library context
1016 
1017   @return An error code: 0 - success, otherwise - failure
1018 
1019   @ref User
1020 
1021   @sa CeedRegister() CeedDestroy()
1022 **/
1023 int CeedInit(const char *resource, Ceed *ceed) {
1024   size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
1025 
1026   // Find matching backend
1027   CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
1028   CeedCall(CeedRegisterAll());
1029 
1030   // Check for help request
1031   const char *help_prefix = "help";
1032   size_t      match_help  = 0;
1033   while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
1034   if (match_help == 4) {
1035     fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
1036             CEED_VERSION_RELEASE ? "" : "+development");
1037     fprintf(stderr, "Available backend resources:\n");
1038     for (size_t i = 0; i < num_backends; i++) {
1039       // Only report compiled backends
1040       if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, "  %s\n", backends[i].prefix);
1041     }
1042     fflush(stderr);
1043     match_help = 5;  // Delineating character expected
1044   } else {
1045     match_help = 0;
1046   }
1047 
1048   // Find best match, computed as number of matching characters from requested resource stem
1049   size_t stem_length = 0;
1050   while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
1051   for (size_t i = 0; i < num_backends; i++) {
1052     size_t      n      = 0;
1053     const char *prefix = backends[i].prefix;
1054     while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
1055     priority = backends[i].priority;
1056     if (n > match_len || (n == match_len && match_priority > priority)) {
1057       match_len      = n;
1058       match_priority = priority;
1059       match_index    = i;
1060     }
1061   }
1062   // Using Levenshtein distance to find closest match
1063   if (match_len <= 1 || match_len != stem_length) {
1064     // LCOV_EXCL_START
1065     size_t lev_dis   = UINT_MAX;
1066     size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
1067     for (size_t i = 0; i < num_backends; i++) {
1068       const char *prefix        = backends[i].prefix;
1069       size_t      prefix_length = strlen(backends[i].prefix);
1070       size_t      min_len       = (prefix_length < stem_length) ? prefix_length : stem_length;
1071       size_t      column[min_len + 1];
1072       for (size_t j = 0; j <= min_len; j++) column[j] = j;
1073       for (size_t j = 1; j <= min_len; j++) {
1074         column[0] = j;
1075         for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
1076           size_t old_diag = column[k];
1077           size_t min_1    = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
1078           size_t min_2    = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
1079           column[k]       = (min_1 < min_2) ? min_1 : min_2;
1080           last_diag       = old_diag;
1081         }
1082       }
1083       size_t n = column[min_len];
1084       priority = backends[i].priority;
1085       if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
1086         lev_dis      = n;
1087         lev_priority = priority;
1088         lev_index    = i;
1089       }
1090     }
1091     const char *prefix_lev = backends[lev_index].prefix;
1092     size_t      lev_length = 0;
1093     while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
1094     size_t m = (lev_length < stem_length) ? lev_length : stem_length;
1095     if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
1096     else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
1097     // LCOV_EXCL_STOP
1098   }
1099 
1100   // Setup Ceed
1101   CeedCall(CeedCalloc(1, ceed));
1102   CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
1103   const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1104   if (!ceed_error_handler) ceed_error_handler = "abort";
1105   if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
1106   else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
1107   else (*ceed)->Error = CeedErrorAbort;
1108   memcpy((*ceed)->err_msg, "No error message stored", 24);
1109   (*ceed)->ref_count = 1;
1110   (*ceed)->data      = NULL;
1111 
1112   // Set lookup table
1113   FOffset f_offsets[] = {
1114       CEED_FTABLE_ENTRY(Ceed, Error),
1115       CEED_FTABLE_ENTRY(Ceed, SetStream),
1116       CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
1117       CEED_FTABLE_ENTRY(Ceed, Destroy),
1118       CEED_FTABLE_ENTRY(Ceed, VectorCreate),
1119       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
1120       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
1121       CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
1122       CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
1123       CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
1124       CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
1125       CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
1126       CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
1127       CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
1128       CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
1129       CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
1130       CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
1131       CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
1132       CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
1133       CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
1134       CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
1135       CEED_FTABLE_ENTRY(CeedVector, SetArray),
1136       CEED_FTABLE_ENTRY(CeedVector, TakeArray),
1137       CEED_FTABLE_ENTRY(CeedVector, SetValue),
1138       CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
1139       CEED_FTABLE_ENTRY(CeedVector, SyncArray),
1140       CEED_FTABLE_ENTRY(CeedVector, GetArray),
1141       CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
1142       CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
1143       CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
1144       CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
1145       CEED_FTABLE_ENTRY(CeedVector, Norm),
1146       CEED_FTABLE_ENTRY(CeedVector, Scale),
1147       CEED_FTABLE_ENTRY(CeedVector, AXPY),
1148       CEED_FTABLE_ENTRY(CeedVector, AXPBY),
1149       CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
1150       CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
1151       CEED_FTABLE_ENTRY(CeedVector, Destroy),
1152       CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
1153       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
1154       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
1155       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
1156       CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
1157       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
1158       CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
1159       CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
1160       CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
1161       CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
1162       CEED_FTABLE_ENTRY(CeedBasis, Apply),
1163       CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
1164       CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
1165       CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
1166       CEED_FTABLE_ENTRY(CeedBasis, Destroy),
1167       CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
1168       CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
1169       CEED_FTABLE_ENTRY(CeedQFunction, Apply),
1170       CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
1171       CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
1172       CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
1173       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
1174       CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
1175       CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
1176       CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
1177       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
1178       CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
1179       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
1180       CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
1181       CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
1182       CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
1183       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
1184       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
1185       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
1186       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
1187       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
1188       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
1189       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
1190       CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
1191       CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
1192       CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
1193       CEED_FTABLE_ENTRY(CeedOperator, Apply),
1194       CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
1195       CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
1196       CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
1197       CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
1198       CEED_FTABLE_ENTRY(CeedOperator, Destroy),
1199       {NULL, 0}  // End of lookup table - used in SetBackendFunction loop
1200   };
1201 
1202   CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
1203   memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
1204 
1205   // Set fallback for advanced CeedOperator functions
1206   const char fallback_resource[] = "";
1207   CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource));
1208 
1209   // Record env variables CEED_DEBUG or DBG
1210   (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1211 
1212   // Copy resource prefix, if backend setup successful
1213   CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1214 
1215   // Set default JiT source root
1216   // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1217   CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1218 
1219   // Backend specific setup
1220   CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1221   return CEED_ERROR_SUCCESS;
1222 }
1223 
1224 /**
1225   @brief Set the GPU stream for a `Ceed` context
1226 
1227   @param[in,out] ceed   `Ceed` context to set the stream
1228   @param[in]     handle Handle to GPU stream
1229 
1230   @return An error code: 0 - success, otherwise - failure
1231 
1232   @ref User
1233 **/
1234 int CeedSetStream(Ceed ceed, void *handle) {
1235   CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1236   if (ceed->SetStream) {
1237     CeedCall(ceed->SetStream(ceed, handle));
1238   } else {
1239     Ceed delegate;
1240     CeedCall(CeedGetDelegate(ceed, &delegate));
1241 
1242     if (delegate) CeedCall(CeedSetStream(delegate, handle));
1243     else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1244   }
1245   return CEED_ERROR_SUCCESS;
1246 }
1247 
1248 /**
1249   @brief Copy the pointer to a `Ceed` context.
1250 
1251   Both pointers should be destroyed with @ref CeedDestroy().
1252 
1253   Note: If the value of `*ceed_copy` passed to this function is non-`NULL`, then it is assumed that `*ceed_copy` is a pointer to a `Ceed` context.
1254         This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1255 
1256   @param[in]     ceed      `Ceed` context to copy reference to
1257   @param[in,out] ceed_copy Variable to store copied reference
1258 
1259   @return An error code: 0 - success, otherwise - failure
1260 
1261   @ref User
1262 **/
1263 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1264   CeedCall(CeedReference(ceed));
1265   CeedCall(CeedDestroy(ceed_copy));
1266   *ceed_copy = ceed;
1267   return CEED_ERROR_SUCCESS;
1268 }
1269 
1270 /**
1271   @brief Get the full resource name for a `Ceed` context
1272 
1273   @param[in]  ceed     `Ceed` context to get resource name of
1274   @param[out] resource Variable to store resource name
1275 
1276   @return An error code: 0 - success, otherwise - failure
1277 
1278   @ref User
1279 **/
1280 int CeedGetResource(Ceed ceed, const char **resource) {
1281   *resource = (const char *)ceed->resource;
1282   return CEED_ERROR_SUCCESS;
1283 }
1284 
1285 /**
1286   @brief Return `Ceed` context preferred memory type
1287 
1288   @param[in]  ceed     `Ceed` context to get preferred memory type of
1289   @param[out] mem_type Address to save preferred memory type to
1290 
1291   @return An error code: 0 - success, otherwise - failure
1292 
1293   @ref User
1294 **/
1295 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1296   if (ceed->GetPreferredMemType) {
1297     CeedCall(ceed->GetPreferredMemType(mem_type));
1298   } else {
1299     Ceed delegate;
1300     CeedCall(CeedGetDelegate(ceed, &delegate));
1301 
1302     if (delegate) {
1303       CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1304     } else {
1305       *mem_type = CEED_MEM_HOST;
1306     }
1307   }
1308   return CEED_ERROR_SUCCESS;
1309 }
1310 
1311 /**
1312   @brief Get deterministic status of `Ceed` context
1313 
1314   @param[in]  ceed             `Ceed` context
1315   @param[out] is_deterministic Variable to store deterministic status
1316 
1317   @return An error code: 0 - success, otherwise - failure
1318 
1319   @ref User
1320 **/
1321 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1322   *is_deterministic = ceed->is_deterministic;
1323   return CEED_ERROR_SUCCESS;
1324 }
1325 
1326 /**
1327   @brief Set additional JiT source root for `Ceed` context
1328 
1329   @param[in,out] ceed            `Ceed` context
1330   @param[in]     jit_source_root Absolute path to additional JiT source directory
1331 
1332   @return An error code: 0 - success, otherwise - failure
1333 
1334   @ref User
1335 **/
1336 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1337   Ceed ceed_parent;
1338 
1339   CeedCall(CeedGetParent(ceed, &ceed_parent));
1340 
1341   CeedInt index       = ceed_parent->num_jit_source_roots;
1342   size_t  path_length = strlen(jit_source_root);
1343 
1344   if (ceed_parent->num_jit_source_roots == ceed_parent->max_jit_source_roots) {
1345     if (ceed_parent->max_jit_source_roots == 0) ceed_parent->max_jit_source_roots = 1;
1346     ceed_parent->max_jit_source_roots *= 2;
1347     CeedCall(CeedRealloc(ceed_parent->max_jit_source_roots, &ceed_parent->jit_source_roots));
1348   }
1349   CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1350   memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1351   ceed_parent->num_jit_source_roots++;
1352   return CEED_ERROR_SUCCESS;
1353 }
1354 
1355 /**
1356   @brief Set additional JiT compiler define for `Ceed` context
1357 
1358   @param[in,out] ceed       `Ceed` context
1359   @param[in]     jit_define String such as `foo=bar`, used as `-Dfoo=bar` in JiT
1360 
1361   @return An error code: 0 - success, otherwise - failure
1362 
1363   @ref User
1364 **/
1365 int CeedAddJitDefine(Ceed ceed, const char *jit_define) {
1366   Ceed ceed_parent;
1367 
1368   CeedCall(CeedGetParent(ceed, &ceed_parent));
1369 
1370   CeedInt index         = ceed_parent->num_jit_defines;
1371   size_t  define_length = strlen(jit_define);
1372 
1373   if (ceed_parent->num_jit_defines == ceed_parent->max_jit_defines) {
1374     if (ceed_parent->max_jit_defines == 0) ceed_parent->max_jit_defines = 1;
1375     ceed_parent->max_jit_defines *= 2;
1376     CeedCall(CeedRealloc(ceed_parent->max_jit_defines, &ceed_parent->jit_defines));
1377   }
1378   CeedCall(CeedCalloc(define_length + 1, &ceed_parent->jit_defines[index]));
1379   memcpy(ceed_parent->jit_defines[index], jit_define, define_length);
1380   ceed_parent->num_jit_defines++;
1381   return CEED_ERROR_SUCCESS;
1382 }
1383 
1384 /**
1385   @brief View a `Ceed`
1386 
1387   @param[in] ceed   `Ceed` to view
1388   @param[in] stream Filestream to write to
1389 
1390   @return An error code: 0 - success, otherwise - failure
1391 
1392   @ref User
1393 **/
1394 int CeedView(Ceed ceed, FILE *stream) {
1395   CeedMemType mem_type;
1396 
1397   CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1398 
1399   fprintf(stream,
1400           "Ceed\n"
1401           "  Ceed Resource: %s\n"
1402           "  Preferred MemType: %s\n",
1403           ceed->resource, CeedMemTypes[mem_type]);
1404   return CEED_ERROR_SUCCESS;
1405 }
1406 
1407 /**
1408   @brief Destroy a `Ceed`
1409 
1410   @param[in,out] ceed Address of `Ceed` context to destroy
1411 
1412   @return An error code: 0 - success, otherwise - failure
1413 
1414   @ref User
1415 **/
1416 int CeedDestroy(Ceed *ceed) {
1417   if (!*ceed || --(*ceed)->ref_count > 0) {
1418     *ceed = NULL;
1419     return CEED_ERROR_SUCCESS;
1420   }
1421   if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1422 
1423   if ((*ceed)->obj_delegate_count > 0) {
1424     for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1425       CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1426       CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1427     }
1428     CeedCall(CeedFree(&(*ceed)->obj_delegates));
1429   }
1430 
1431   if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1432 
1433   for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1434     CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1435   }
1436   CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1437 
1438   for (CeedInt i = 0; i < (*ceed)->num_jit_defines; i++) {
1439     CeedCall(CeedFree(&(*ceed)->jit_defines[i]));
1440   }
1441   CeedCall(CeedFree(&(*ceed)->jit_defines));
1442 
1443   CeedCall(CeedFree(&(*ceed)->f_offsets));
1444   CeedCall(CeedFree(&(*ceed)->resource));
1445   CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1446   CeedCall(CeedFree(&(*ceed)->op_fallback_resource));
1447   CeedCall(CeedWorkVectorsDestroy(*ceed));
1448   CeedCall(CeedFree(ceed));
1449   return CEED_ERROR_SUCCESS;
1450 }
1451 
1452 // LCOV_EXCL_START
1453 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1454   if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1455   if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args);
1456   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1457   vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args);  // NOLINT
1458   return ceed->err_msg;
1459 }
1460 // LCOV_EXCL_STOP
1461 
1462 /**
1463   @brief Error handling implementation; use @ref CeedError() instead.
1464 
1465   @return An error code: 0 - success, otherwise - failure
1466 
1467   @ref Developer
1468 **/
1469 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1470   va_list args;
1471   int     ret_val;
1472 
1473   va_start(args, format);
1474   if (ceed) {
1475     ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1476   } else {
1477     // LCOV_EXCL_START
1478     const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1479     if (!ceed_error_handler) ceed_error_handler = "abort";
1480     if (!strcmp(ceed_error_handler, "return")) {
1481       ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1482     } else {
1483       // This function will not return
1484       ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1485     }
1486   }
1487   va_end(args);
1488   return ret_val;
1489   // LCOV_EXCL_STOP
1490 }
1491 
1492 /**
1493   @brief Error handler that returns without printing anything.
1494 
1495   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1496 
1497   @return An error code: 0 - success, otherwise - failure
1498 
1499   @ref Developer
1500 **/
1501 // LCOV_EXCL_START
1502 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1503   return err_code;
1504 }
1505 // LCOV_EXCL_STOP
1506 
1507 /**
1508   @brief Error handler that stores the error message for future use and returns the error.
1509 
1510   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1511 
1512   @return An error code: 0 - success, otherwise - failure
1513 
1514   @ref Developer
1515 **/
1516 // LCOV_EXCL_START
1517 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1518   if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1519   if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args);
1520 
1521   // Build message
1522   int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1523   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1524   vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args);  // NOLINT
1525   return err_code;
1526 }
1527 // LCOV_EXCL_STOP
1528 
1529 /**
1530   @brief Error handler that prints to `stderr` and aborts
1531 
1532   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1533 
1534   @return An error code: 0 - success, otherwise - failure
1535 
1536   @ref Developer
1537 **/
1538 // LCOV_EXCL_START
1539 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1540   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1541   vfprintf(stderr, format, *args);
1542   fprintf(stderr, "\n");
1543   abort();
1544   return err_code;
1545 }
1546 // LCOV_EXCL_STOP
1547 
1548 /**
1549   @brief Error handler that prints to `stderr` and exits.
1550 
1551   Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1552 
1553   In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1554 
1555   @return An error code: 0 - success, otherwise - failure
1556 
1557   @ref Developer
1558 **/
1559 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1560   fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1561   // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1562   vfprintf(stderr, format, *args);  // NOLINT
1563   fprintf(stderr, "\n");
1564   exit(err_code);
1565   return err_code;
1566 }
1567 
1568 /**
1569   @brief Set error handler
1570 
1571   A default error handler is set in @ref CeedInit().
1572   Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1573 
1574   @return An error code: 0 - success, otherwise - failure
1575 
1576   @ref Developer
1577 **/
1578 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1579   ceed->Error = handler;
1580   if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1581   for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1582   return CEED_ERROR_SUCCESS;
1583 }
1584 
1585 /**
1586   @brief Get error message
1587 
1588   The error message is only stored when using the error handler @ref CeedErrorStore()
1589 
1590   @param[in]  ceed    `Ceed` context to retrieve error message
1591   @param[out] err_msg Char pointer to hold error message
1592 
1593   @return An error code: 0 - success, otherwise - failure
1594 
1595   @ref Developer
1596 **/
1597 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1598   if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1599   if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg);
1600   *err_msg = ceed->err_msg;
1601   return CEED_ERROR_SUCCESS;
1602 }
1603 
1604 /**
1605   @brief Restore error message.
1606 
1607   The error message is only stored when using the error handler @ref CeedErrorStore().
1608 
1609   @param[in]  ceed    `Ceed` context to restore error message
1610   @param[out] err_msg Char pointer that holds error message
1611 
1612   @return An error code: 0 - success, otherwise - failure
1613 
1614   @ref Developer
1615 **/
1616 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1617   if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1618   if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg);
1619   *err_msg = NULL;
1620   memcpy(ceed->err_msg, "No error message stored", 24);
1621   return CEED_ERROR_SUCCESS;
1622 }
1623 
1624 /**
1625   @brief Get libCEED library version information.
1626 
1627   libCEED version numbers have the form major.minor.patch.
1628   Non-release versions may contain unstable interfaces.
1629 
1630   @param[out] major   Major version of the library
1631   @param[out] minor   Minor version of the library
1632   @param[out] patch   Patch (subminor) version of the library
1633   @param[out] release True for releases; false for development branches
1634 
1635   The caller may pass `NULL` for any arguments that are not needed.
1636 
1637   @return An error code: 0 - success, otherwise - failure
1638 
1639   @ref Developer
1640 
1641   @sa CEED_VERSION_GE()
1642 */
1643 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1644   if (major) *major = CEED_VERSION_MAJOR;
1645   if (minor) *minor = CEED_VERSION_MINOR;
1646   if (patch) *patch = CEED_VERSION_PATCH;
1647   if (release) *release = CEED_VERSION_RELEASE;
1648   return CEED_ERROR_SUCCESS;
1649 }
1650 
1651 /**
1652   @brief Get libCEED scalar type, such as F64 or F32
1653 
1654   @param[out] scalar_type Type of libCEED scalars
1655 
1656   @return An error code: 0 - success, otherwise - failure
1657 
1658   @ref Developer
1659 */
1660 int CeedGetScalarType(CeedScalarType *scalar_type) {
1661   *scalar_type = CEED_SCALAR_TYPE;
1662   return CEED_ERROR_SUCCESS;
1663 }
1664 
1665 /// @}
1666