1 // Copyright (c) 2017-2026, 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 **/
CeedRequestWait(CeedRequest * req)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 **/
CeedRegisterImpl(const char * prefix,int (* init)(const char *,Ceed),unsigned int priority)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 **/
CeedWorkVectorsCreate(Ceed ceed)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 **/
CeedWorkVectorsDestroy(Ceed ceed)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 // Note: increase ref_count to prevent Ceed destructor from triggering again
169 CeedCall(CeedObjectReference((CeedObject)ceed));
170 CeedCall(CeedObjectReference((CeedObject)ceed));
171 CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i]));
172 // Note: restore ref_count
173 CeedObjectDereference((CeedObject)ceed);
174 }
175 CeedCall(CeedFree(&ceed->work_vectors->is_in_use));
176 CeedCall(CeedFree(&ceed->work_vectors->vecs));
177 CeedCall(CeedFree(&ceed->work_vectors));
178 return CEED_ERROR_SUCCESS;
179 }
180
181 /**
182 @brief View a `Ceed` passed as a `CeedObject`
183
184 @param[in] ceed `Ceed` to view
185 @param[in] stream Filestream to write to
186
187 @return An error code: 0 - success, otherwise - failure
188
189 @ref Developer
190 **/
CeedView_Object(CeedObject ceed,FILE * stream)191 static int CeedView_Object(CeedObject ceed, FILE *stream) {
192 CeedCall(CeedView((Ceed)ceed, stream));
193 return CEED_ERROR_SUCCESS;
194 }
195
196 /**
197 @brief Destroy a `Ceed` passed as a `CeedObject`
198
199 @param[in,out] ceed Address of `Ceed` context to destroy
200
201 @return An error code: 0 - success, otherwise - failure
202
203 @ref Developer
204 **/
CeedDestroy_Object(CeedObject * ceed)205 static int CeedDestroy_Object(CeedObject *ceed) {
206 CeedCall(CeedDestroy((Ceed *)ceed));
207 return CEED_ERROR_SUCCESS;
208 }
209
210 /// @}
211
212 /// ----------------------------------------------------------------------------
213 /// Ceed Backend API
214 /// ----------------------------------------------------------------------------
215 /// @addtogroup CeedBackend
216 /// @{
217
218 /**
219 @brief Return value of `CEED_DEBUG` environment variable
220
221 @param[in] ceed `Ceed` context
222
223 @return Boolean value: true - debugging mode enabled
224 false - debugging mode disabled
225
226 @ref Backend
227 **/
228 // LCOV_EXCL_START
CeedDebugFlag(const Ceed ceed)229 bool CeedDebugFlag(const Ceed ceed) { return ceed->is_debug; }
230 // LCOV_EXCL_STOP
231
232 /**
233 @brief Return value of `CEED_DEBUG` environment variable
234
235 @return Boolean value: true - debugging mode enabled
236 false - debugging mode disabled
237
238 @ref Backend
239 **/
240 // LCOV_EXCL_START
CeedDebugFlagEnv(void)241 bool CeedDebugFlagEnv(void) { return getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG"); }
242 // LCOV_EXCL_STOP
243
244 /**
245 @brief Print debugging information in color
246
247 @param[in] color Color to print
248 @param[in] format Printing format
249
250 @ref Backend
251 **/
252 // LCOV_EXCL_START
CeedDebugImpl256(const unsigned char color,const char * format,...)253 void CeedDebugImpl256(const unsigned char color, const char *format, ...) {
254 va_list args;
255 va_start(args, format);
256 fflush(stdout);
257 if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[38;5;%dm", color);
258 vfprintf(stdout, format, args);
259 if (color != CEED_DEBUG_COLOR_NONE) fprintf(stdout, "\033[m");
260 fprintf(stdout, "\n");
261 fflush(stdout);
262 va_end(args);
263 }
264 // LCOV_EXCL_STOP
265
266 /**
267 @brief Allocate an array on the host; use @ref CeedMalloc().
268
269 Memory usage can be tracked by the library.
270 This ensures sufficient alignment for vectorization and should be used for large allocations.
271
272 @param[in] n Number of units to allocate
273 @param[in] unit Size of each unit
274 @param[out] p Address of pointer to hold the result
275
276 @return An error code: 0 - success, otherwise - failure
277
278 @ref Backend
279
280 @sa CeedFree()
281 **/
CeedMallocArray(size_t n,size_t unit,void * p)282 int CeedMallocArray(size_t n, size_t unit, void *p) {
283 int ierr = posix_memalign((void **)p, CEED_ALIGN, n * unit);
284 CeedCheck(ierr == 0, NULL, CEED_ERROR_MAJOR, "posix_memalign failed to allocate %zd members of size %zd\n", n, unit);
285 return CEED_ERROR_SUCCESS;
286 }
287
288 /**
289 @brief Allocate a cleared (zeroed) array on the host; use @ref CeedCalloc().
290
291 Memory usage can be tracked by the library.
292
293 @param[in] n Number of units to allocate
294 @param[in] unit Size of each unit
295 @param[out] p Address of pointer to hold the result
296
297 @return An error code: 0 - success, otherwise - failure
298
299 @ref Backend
300
301 @sa CeedFree()
302 **/
CeedCallocArray(size_t n,size_t unit,void * p)303 int CeedCallocArray(size_t n, size_t unit, void *p) {
304 *(void **)p = calloc(n, unit);
305 CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "calloc failed to allocate %zd members of size %zd\n", n, unit);
306 return CEED_ERROR_SUCCESS;
307 }
308
309 /**
310 @brief Reallocate an array on the host; use @ref CeedRealloc().
311
312 Memory usage can be tracked by the library.
313
314 @param[in] n Number of units to allocate
315 @param[in] unit Size of each unit
316 @param[out] p Address of pointer to hold the result
317
318 @return An error code: 0 - success, otherwise - failure
319
320 @ref Backend
321
322 @sa CeedFree()
323 **/
CeedReallocArray(size_t n,size_t unit,void * p)324 int CeedReallocArray(size_t n, size_t unit, void *p) {
325 *(void **)p = realloc(*(void **)p, n * unit);
326 CeedCheck(!n || !unit || *(void **)p, NULL, CEED_ERROR_MAJOR, "realloc failed to allocate %zd members of size %zd\n", n, unit);
327 return CEED_ERROR_SUCCESS;
328 }
329
330 /**
331 @brief Allocate a cleared string buffer on the host.
332
333 Memory usage can be tracked by the library.
334
335 @param[in] source Pointer to string to be copied
336 @param[out] copy Pointer to variable to hold newly allocated string copy
337
338 @return An error code: 0 - success, otherwise - failure
339
340 @ref Backend
341
342 @sa CeedFree()
343 **/
CeedStringAllocCopy(const char * source,char ** copy)344 int CeedStringAllocCopy(const char *source, char **copy) {
345 size_t len = strlen(source);
346 CeedCall(CeedCalloc(len + 1, copy));
347 memcpy(*copy, source, len);
348 return CEED_ERROR_SUCCESS;
349 }
350
351 /** Free memory allocated using @ref CeedMalloc() or @ref CeedCalloc()
352
353 @param[in,out] p Address of pointer to memory.
354 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.
355
356 @return An error code: 0 - success, otherwise - failure
357
358 @ref Backend
359 **/
CeedFree(void * p)360 int CeedFree(void *p) {
361 free(*(void **)p);
362 *(void **)p = NULL;
363 return CEED_ERROR_SUCCESS;
364 }
365
366 /** Internal helper to manage handoff of user `source_array` to backend with proper @ref CeedCopyMode behavior.
367
368 @param[in] source_array Source data provided by user
369 @param[in] copy_mode Copy mode for the data
370 @param[in] num_values Number of values to handle
371 @param[in] size_unit Size of array element in bytes
372 @param[in,out] target_array_owned Pointer to location to allocated or hold owned data, may be freed if already allocated
373 @param[out] target_array_borrowed Pointer to location to hold borrowed data
374 @param[out] target_array Pointer to location for data
375
376 @return An error code: 0 - success, otherwise - failure
377
378 @ref Backend
379 **/
CeedSetHostGenericArray(const void * source_array,CeedCopyMode copy_mode,size_t size_unit,CeedSize num_values,void * target_array_owned,void * target_array_borrowed,void * target_array)380 static inline int CeedSetHostGenericArray(const void *source_array, CeedCopyMode copy_mode, size_t size_unit, CeedSize num_values,
381 void *target_array_owned, void *target_array_borrowed, void *target_array) {
382 switch (copy_mode) {
383 case CEED_COPY_VALUES:
384 if (!*(void **)target_array) {
385 if (*(void **)target_array_borrowed) {
386 *(void **)target_array = *(void **)target_array_borrowed;
387 } else {
388 if (!*(void **)target_array_owned) CeedCall(CeedCallocArray(num_values, size_unit, target_array_owned));
389 *(void **)target_array = *(void **)target_array_owned;
390 }
391 }
392 if (source_array) memcpy(*(void **)target_array, source_array, size_unit * num_values);
393 break;
394 case CEED_OWN_POINTER:
395 CeedCall(CeedFree(target_array_owned));
396 *(void **)target_array_owned = (void *)source_array;
397 *(void **)target_array_borrowed = NULL;
398 *(void **)target_array = *(void **)target_array_owned;
399 break;
400 case CEED_USE_POINTER:
401 CeedCall(CeedFree(target_array_owned));
402 *(void **)target_array_owned = NULL;
403 *(void **)target_array_borrowed = (void *)source_array;
404 *(void **)target_array = *(void **)target_array_borrowed;
405 }
406 return CEED_ERROR_SUCCESS;
407 }
408
409 /** Manage handoff of user `bool` `source_array` to backend with proper @ref CeedCopyMode behavior.
410
411 @param[in] source_array Source data provided by user
412 @param[in] copy_mode Copy mode for the data
413 @param[in] num_values Number of values to handle
414 @param[in,out] target_array_owned Pointer to location to allocated or hold owned data, may be freed if already allocated
415 @param[out] target_array_borrowed Pointer to location to hold borrowed data
416 @param[out] target_array Pointer to location for data
417
418 @return An error code: 0 - success, otherwise - failure
419
420 @ref Backend
421 **/
CeedSetHostBoolArray(const bool * source_array,CeedCopyMode copy_mode,CeedSize num_values,const bool ** target_array_owned,const bool ** target_array_borrowed,const bool ** target_array)422 int CeedSetHostBoolArray(const bool *source_array, CeedCopyMode copy_mode, CeedSize num_values, const bool **target_array_owned,
423 const bool **target_array_borrowed, const bool **target_array) {
424 CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(bool), num_values, target_array_owned, target_array_borrowed, target_array));
425 return CEED_ERROR_SUCCESS;
426 }
427
428 /** Manage handoff of user `CeedInt8` `source_array` to backend with proper @ref CeedCopyMode behavior.
429
430 @param[in] source_array Source data provided by user
431 @param[in] copy_mode Copy mode for the data
432 @param[in] num_values Number of values to handle
433 @param[in,out] target_array_owned Pointer to location to allocated or hold owned data, may be freed if already allocated
434 @param[out] target_array_borrowed Pointer to location to hold borrowed data
435 @param[out] target_array Pointer to location for data
436
437 @return An error code: 0 - success, otherwise - failure
438
439 @ref Backend
440 **/
CeedSetHostCeedInt8Array(const CeedInt8 * source_array,CeedCopyMode copy_mode,CeedSize num_values,const CeedInt8 ** target_array_owned,const CeedInt8 ** target_array_borrowed,const CeedInt8 ** target_array)441 int CeedSetHostCeedInt8Array(const CeedInt8 *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt8 **target_array_owned,
442 const CeedInt8 **target_array_borrowed, const CeedInt8 **target_array) {
443 CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt8), num_values, target_array_owned, target_array_borrowed, target_array));
444 return CEED_ERROR_SUCCESS;
445 }
446
447 /** Manage handoff of user `CeedInt` `source_array` to backend with proper @ref CeedCopyMode behavior.
448
449 @param[in] source_array Source data provided by user
450 @param[in] copy_mode Copy mode for the data
451 @param[in] num_values Number of values to handle
452 @param[in,out] target_array_owned Pointer to location to allocated or hold owned data, may be freed if already allocated
453 @param[out] target_array_borrowed Pointer to location to hold borrowed data
454 @param[out] target_array Pointer to location for data
455
456 @return An error code: 0 - success, otherwise - failure
457
458 @ref Backend
459 **/
CeedSetHostCeedIntArray(const CeedInt * source_array,CeedCopyMode copy_mode,CeedSize num_values,const CeedInt ** target_array_owned,const CeedInt ** target_array_borrowed,const CeedInt ** target_array)460 int CeedSetHostCeedIntArray(const CeedInt *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedInt **target_array_owned,
461 const CeedInt **target_array_borrowed, const CeedInt **target_array) {
462 CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedInt), num_values, target_array_owned, target_array_borrowed, target_array));
463 return CEED_ERROR_SUCCESS;
464 }
465
466 /** Manage handoff of user `CeedScalar` `source_array` to backend with proper @ref CeedCopyMode behavior.
467
468 @param[in] source_array Source data provided by user
469 @param[in] copy_mode Copy mode for the data
470 @param[in] num_values Number of values to handle
471 @param[in,out] target_array_owned Pointer to location to allocated or hold owned data, may be freed if already allocated
472 @param[out] target_array_borrowed Pointer to location to hold borrowed data
473 @param[out] target_array Pointer to location for data
474
475 @return An error code: 0 - success, otherwise - failure
476
477 @ref Backend
478 **/
CeedSetHostCeedScalarArray(const CeedScalar * source_array,CeedCopyMode copy_mode,CeedSize num_values,const CeedScalar ** target_array_owned,const CeedScalar ** target_array_borrowed,const CeedScalar ** target_array)479 int CeedSetHostCeedScalarArray(const CeedScalar *source_array, CeedCopyMode copy_mode, CeedSize num_values, const CeedScalar **target_array_owned,
480 const CeedScalar **target_array_borrowed, const CeedScalar **target_array) {
481 CeedCall(CeedSetHostGenericArray(source_array, copy_mode, sizeof(CeedScalar), num_values, target_array_owned, target_array_borrowed, target_array));
482 return CEED_ERROR_SUCCESS;
483 }
484
485 /**
486 @brief Register a `Ceed` backend
487
488 @param[in] prefix Prefix of resources for this backend to respond to.
489 For example, the reference backend responds to "/cpu/self".
490 @param[in] init Initialization function called by @ref CeedInit() when the backend is selected to drive the requested resource
491 @param[in] priority Integer priority.
492 Lower values are preferred in case the resource requested by @ref CeedInit() has non-unique best prefix match.
493
494 @return An error code: 0 - success, otherwise - failure
495
496 @ref Backend
497 **/
CeedRegister(const char * prefix,int (* init)(const char *,Ceed),unsigned int priority)498 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed), unsigned int priority) {
499 CeedDebugEnv("Backend Register: %s", prefix);
500 CeedRegisterImpl(prefix, init, priority);
501 return CEED_ERROR_SUCCESS;
502 }
503
504 /**
505 @brief Return debugging status flag
506
507 @param[in] ceed `Ceed` context to get debugging flag
508 @param[out] is_debug Variable to store debugging flag
509
510 @return An error code: 0 - success, otherwise - failure
511
512 @ref Backend
513 **/
CeedIsDebug(Ceed ceed,bool * is_debug)514 int CeedIsDebug(Ceed ceed, bool *is_debug) {
515 *is_debug = ceed->is_debug;
516 return CEED_ERROR_SUCCESS;
517 }
518
519 /**
520 @brief Get the root of the requested resource.
521
522 Note: Caller is responsible for calling @ref CeedFree() on the `resource_root`.
523
524 @param[in] ceed `Ceed` context to get resource name of
525 @param[in] resource Full user specified resource
526 @param[in] delineator Delineator to break `resource_root` and `resource_spec`
527 @param[out] resource_root Variable to store resource root
528
529 @return An error code: 0 - success, otherwise - failure
530
531 @ref Backend
532 **/
CeedGetResourceRoot(Ceed ceed,const char * resource,const char * delineator,char ** resource_root)533 int CeedGetResourceRoot(Ceed ceed, const char *resource, const char *delineator, char **resource_root) {
534 char *device_spec = strstr(resource, delineator);
535 size_t resource_root_len = device_spec ? (size_t)(device_spec - resource) + 1 : strlen(resource) + 1;
536
537 CeedCall(CeedCalloc(resource_root_len, resource_root));
538 memcpy(*resource_root, resource, resource_root_len - 1);
539 return CEED_ERROR_SUCCESS;
540 }
541
542 /**
543 @brief Retrieve a parent `Ceed` context
544
545 @param[in] ceed `Ceed` context to retrieve parent of
546 @param[out] parent Address to save the parent to
547
548 @return An error code: 0 - success, otherwise - failure
549
550 @ref Backend
551 **/
CeedGetParent(Ceed ceed,Ceed * parent)552 int CeedGetParent(Ceed ceed, Ceed *parent) {
553 if (ceed->parent) {
554 CeedCall(CeedGetParent(ceed->parent, parent));
555 return CEED_ERROR_SUCCESS;
556 }
557 *parent = NULL;
558 CeedCall(CeedReferenceCopy(ceed, parent));
559 return CEED_ERROR_SUCCESS;
560 }
561
562 /**
563 @brief Retrieve a delegate `Ceed` context
564
565 @param[in] ceed `Ceed` context to retrieve delegate of
566 @param[out] delegate Address to save the delegate to
567
568 @return An error code: 0 - success, otherwise - failure
569
570 @ref Backend
571 **/
CeedGetDelegate(Ceed ceed,Ceed * delegate)572 int CeedGetDelegate(Ceed ceed, Ceed *delegate) {
573 *delegate = NULL;
574 if (ceed->delegate) CeedCall(CeedReferenceCopy(ceed->delegate, delegate));
575 return CEED_ERROR_SUCCESS;
576 }
577
578 /**
579 @brief Set a delegate `Ceed` context
580
581 This function allows a `Ceed` context to set a delegate `Ceed` context.
582 All backend implementations default to the delegate `Ceed` context, unless overridden.
583
584 @param[in] ceed `Ceed` context to set delegate of
585 @param[out] delegate Address to set the delegate to
586
587 @return An error code: 0 - success, otherwise - failure
588
589 @ref Backend
590 **/
CeedSetDelegate(Ceed ceed,Ceed delegate)591 int CeedSetDelegate(Ceed ceed, Ceed delegate) {
592 CeedCall(CeedReferenceCopy(delegate, &ceed->delegate));
593 delegate->parent = ceed;
594 return CEED_ERROR_SUCCESS;
595 }
596
597 /**
598 @brief Retrieve a delegate `Ceed` context for a specific object type
599
600 @param[in] ceed `Ceed` context to retrieve delegate of
601 @param[out] delegate Address to save the delegate to
602 @param[in] obj_name Name of the object type to retrieve delegate for
603
604 @return An error code: 0 - success, otherwise - failure
605
606 @ref Backend
607 **/
CeedGetObjectDelegate(Ceed ceed,Ceed * delegate,const char * obj_name)608 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) {
609 // Check for object delegate
610 for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) {
611 if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) {
612 *delegate = NULL;
613 CeedCall(CeedReferenceCopy(ceed->obj_delegates->delegate, delegate));
614 return CEED_ERROR_SUCCESS;
615 }
616 }
617
618 // Use default delegate if no object delegate
619 CeedCall(CeedGetDelegate(ceed, delegate));
620 return CEED_ERROR_SUCCESS;
621 }
622
623 /**
624 @brief Set a delegate `Ceed` context for a specific object type
625
626 This function allows a `Ceed` context to set a delegate `Ceed` context for a given type of `Ceed` object.
627 All backend implementations default to the delegate `Ceed` context for this object.
628 For example, `CeedSetObjectDelegate(ceed, delegate, "Basis")` uses delegate implementations for all `CeedBasis` backend functions.
629
630 @param[in,out] ceed `Ceed` context to set delegate of
631 @param[in] delegate `Ceed` context to use for delegation
632 @param[in] obj_name Name of the object type to set delegate for
633
634 @return An error code: 0 - success, otherwise - failure
635
636 @ref Backend
637 **/
CeedSetObjectDelegate(Ceed ceed,Ceed delegate,const char * obj_name)638 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) {
639 CeedInt count = ceed->obj_delegate_count;
640
641 // Malloc or Realloc
642 if (count) {
643 CeedCall(CeedRealloc(count + 1, &ceed->obj_delegates));
644 } else {
645 CeedCall(CeedCalloc(1, &ceed->obj_delegates));
646 }
647 ceed->obj_delegate_count++;
648
649 // Set object delegate
650 CeedCall(CeedReferenceCopy(delegate, &ceed->obj_delegates[count].delegate));
651 CeedCall(CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name));
652
653 // Set delegate parent
654 delegate->parent = ceed;
655 return CEED_ERROR_SUCCESS;
656 }
657
658 /**
659 @brief Get the fallback `Ceed` for `CeedOperator`
660
661 @param[in] ceed `Ceed` context
662 @param[out] fallback_ceed Variable to store fallback `Ceed`
663
664 @return An error code: 0 - success, otherwise - failure
665
666 @ref Backend
667 **/
CeedGetOperatorFallbackCeed(Ceed ceed,Ceed * fallback_ceed)668 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) {
669 if (ceed->op_fallback_ceed) {
670 CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- Ceed Fallback ----------\n");
671 CeedDebug(ceed, "Falling back from Ceed with backend %s at address %p to Ceed with backend %s at address %p", ceed->resource, ceed,
672 ceed->op_fallback_ceed->resource, ceed->op_fallback_ceed);
673 }
674
675 *fallback_ceed = NULL;
676 if (ceed->op_fallback_ceed) CeedCall(CeedReferenceCopy(ceed->op_fallback_ceed, fallback_ceed));
677 return CEED_ERROR_SUCCESS;
678 }
679
680 /**
681 @brief Set the fallback resource for `CeedOperator`.
682
683 The current fallback, if any, is freed by calling this function.
684
685 @param[in,out] ceed `Ceed` context
686 @param[in] fallback_ceed `Ceed` context to create fallback operators
687
688 @return An error code: 0 - success, otherwise - failure
689
690 @ref Backend
691 **/
CeedSetOperatorFallbackCeed(Ceed ceed,Ceed fallback_ceed)692 int CeedSetOperatorFallbackCeed(Ceed ceed, Ceed fallback_ceed) {
693 CeedCall(CeedReferenceCopy(fallback_ceed, &ceed->op_fallback_ceed));
694 fallback_ceed->parent = ceed;
695 return CEED_ERROR_SUCCESS;
696 }
697
698 /**
699 @brief Flag `Ceed` context as deterministic
700
701 @param[in] ceed `Ceed` to flag as deterministic
702 @param[out] is_deterministic Deterministic status to set
703
704 @return An error code: 0 - success, otherwise - failure
705
706 @ref Backend
707 **/
CeedSetDeterministic(Ceed ceed,bool is_deterministic)708 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) {
709 ceed->is_deterministic = is_deterministic;
710 return CEED_ERROR_SUCCESS;
711 }
712
713 /**
714 @brief Set a backend function.
715
716 This function is used for a backend to set the function associated with the Ceed objects.
717 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().
718 Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis").
719
720 @param[in] ceed `Ceed` context for error handling
721 @param[in] type Type of Ceed object to set function for
722 @param[out] object Ceed object to set function for
723 @param[in] func_name Name of function to set
724 @param[in] f Function to set
725
726 @return An error code: 0 - success, otherwise - failure
727
728 @ref Backend
729 **/
CeedSetBackendFunctionImpl(Ceed ceed,const char * type,void * object,const char * func_name,void (* f)(void))730 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) {
731 char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = "";
732
733 // Build lookup name
734 if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN);
735 strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN);
736 strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN);
737
738 // Find and use offset
739 for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) {
740 if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) {
741 size_t offset = ceed->f_offsets[i].offset;
742 int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD*
743
744 *fpointer = (int (*)(void))f;
745 return CEED_ERROR_SUCCESS;
746 }
747 }
748
749 // LCOV_EXCL_START
750 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type);
751 // LCOV_EXCL_STOP
752 }
753
754 /**
755 @brief Retrieve backend data for a `Ceed` context
756
757 @param[in] ceed `Ceed` context to retrieve data of
758 @param[out] data Address to save data to
759
760 @return An error code: 0 - success, otherwise - failure
761
762 @ref Backend
763 **/
CeedGetData(Ceed ceed,void * data)764 int CeedGetData(Ceed ceed, void *data) {
765 *(void **)data = ceed->data;
766 return CEED_ERROR_SUCCESS;
767 }
768
769 /**
770 @brief Set backend data for a `Ceed` context
771
772 @param[in,out] ceed `Ceed` context to set data of
773 @param[in] data Address of data to set
774
775 @return An error code: 0 - success, otherwise - failure
776
777 @ref Backend
778 **/
CeedSetData(Ceed ceed,void * data)779 int CeedSetData(Ceed ceed, void *data) {
780 ceed->data = data;
781 return CEED_ERROR_SUCCESS;
782 }
783
784 /**
785 @brief Increment the reference counter for a `Ceed` context
786
787 @param[in,out] ceed `Ceed` context to increment the reference counter
788
789 @return An error code: 0 - success, otherwise - failure
790
791 @ref Backend
792 **/
CeedReference(Ceed ceed)793 int CeedReference(Ceed ceed) {
794 CeedCall(CeedObjectReference((CeedObject)ceed));
795 return CEED_ERROR_SUCCESS;
796 }
797
798 /**
799 @brief Computes the current memory usage of the work vectors in a `Ceed` context and prints to debug.abort
800
801 @param[in] ceed `Ceed` context
802 @param[out] usage_mb Address of the variable where the MB of work vector usage will be stored
803
804 @return An error code: 0 - success, otherwise - failure
805
806 @ref Developer
807 **/
CeedGetWorkVectorMemoryUsage(Ceed ceed,CeedScalar * usage_mb)808 int CeedGetWorkVectorMemoryUsage(Ceed ceed, CeedScalar *usage_mb) {
809 if (!ceed->VectorCreate) {
810 Ceed delegate;
811
812 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
813 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
814 CeedCall(CeedGetWorkVectorMemoryUsage(delegate, usage_mb));
815 CeedCall(CeedDestroy(&delegate));
816 return CEED_ERROR_SUCCESS;
817 }
818 *usage_mb = 0.0;
819 if (ceed->work_vectors) {
820 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
821 CeedSize vec_len;
822 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len));
823 *usage_mb += vec_len;
824 }
825 *usage_mb *= sizeof(CeedScalar) * 1e-6;
826 CeedDebug(ceed, "Resource {%s}: Work vectors memory usage: %" CeedInt_FMT " vectors, %g MB\n", ceed->resource, ceed->work_vectors->num_vecs,
827 *usage_mb);
828 }
829 return CEED_ERROR_SUCCESS;
830 }
831
832 /**
833 @brief Clear inactive work vectors in a `Ceed` context below a minimum length.
834
835 @param[in,out] ceed `Ceed` context
836 @param[in] min_len Minimum length of work vector to keep
837
838 @return An error code: 0 - success, otherwise - failure
839
840 @ref Backend
841 **/
CeedClearWorkVectors(Ceed ceed,CeedSize min_len)842 int CeedClearWorkVectors(Ceed ceed, CeedSize min_len) {
843 if (!ceed->VectorCreate) {
844 Ceed delegate;
845
846 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
847 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
848 CeedCall(CeedClearWorkVectors(delegate, min_len));
849 CeedCall(CeedDestroy(&delegate));
850 return CEED_ERROR_SUCCESS;
851 }
852 if (!ceed->work_vectors) return CEED_ERROR_SUCCESS;
853 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
854 if (ceed->work_vectors->is_in_use[i]) continue;
855 CeedSize vec_len;
856 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len));
857 if (vec_len < min_len) {
858 // Note: increase ref_count to prevent Ceed destructor from triggering
859 CeedCall(CeedObjectReference((CeedObject)ceed));
860 CeedCall(CeedObjectReference((CeedObject)ceed));
861 CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i]));
862 // Note: restore ref_count
863 CeedObjectDereference((CeedObject)ceed);
864 ceed->work_vectors->num_vecs--;
865 if (ceed->work_vectors->num_vecs > 0) {
866 ceed->work_vectors->vecs[i] = ceed->work_vectors->vecs[ceed->work_vectors->num_vecs];
867 ceed->work_vectors->is_in_use[i] = ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs];
868 ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs] = false;
869 i--;
870 }
871 }
872 }
873 return CEED_ERROR_SUCCESS;
874 }
875
876 /**
877 @brief Get a `CeedVector` for scratch work from a `Ceed` context.
878
879 Note: This vector must be restored with @ref CeedRestoreWorkVector().
880
881 @param[in] ceed `Ceed` context
882 @param[in] len Minimum length of work vector
883 @param[out] vec Address of the variable where `CeedVector` will be stored
884
885 @return An error code: 0 - success, otherwise - failure
886
887 @ref Backend
888 **/
CeedGetWorkVector(Ceed ceed,CeedSize len,CeedVector * vec)889 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) {
890 CeedInt i = 0;
891 CeedScalar usage_mb;
892
893 if (!ceed->VectorCreate) {
894 Ceed delegate;
895
896 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
897 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
898 CeedCall(CeedGetWorkVector(delegate, len, vec));
899 CeedCall(CeedDestroy(&delegate));
900 return CEED_ERROR_SUCCESS;
901 }
902
903 if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed));
904
905 // Search for big enough work vector
906 for (i = 0; i < ceed->work_vectors->num_vecs; i++) {
907 if (!ceed->work_vectors->is_in_use[i]) {
908 CeedSize work_len;
909
910 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len));
911 if (work_len >= len) break;
912 }
913 }
914 // Long enough vector was not found
915 if (i == ceed->work_vectors->num_vecs) {
916 if (ceed->work_vectors->max_vecs == 0) {
917 ceed->work_vectors->max_vecs = 1;
918 CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
919 CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
920 } else if (ceed->work_vectors->max_vecs == i) {
921 ceed->work_vectors->max_vecs *= 2;
922 CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs));
923 CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use));
924 }
925 ceed->work_vectors->num_vecs++;
926 CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i]));
927 // Note: ref_count manipulation to prevent a ref-loop
928 CeedObjectDereference((CeedObject)ceed);
929 if (ceed->is_debug) CeedGetWorkVectorMemoryUsage(ceed, &usage_mb);
930 }
931 // Return pointer to work vector
932 ceed->work_vectors->is_in_use[i] = true;
933 *vec = NULL;
934 CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec));
935 // Note: bump ref_count to account for external access
936 CeedCall(CeedObjectReference((CeedObject)ceed));
937 return CEED_ERROR_SUCCESS;
938 }
939
940 /**
941 @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector()
942
943 @param[in] ceed `Ceed` context
944 @param[out] vec `CeedVector` to restore
945
946 @return An error code: 0 - success, otherwise - failure
947
948 @ref Backend
949 **/
CeedRestoreWorkVector(Ceed ceed,CeedVector * vec)950 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) {
951 if (!ceed->VectorCreate) {
952 Ceed delegate;
953
954 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector"));
955 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate");
956 CeedCall(CeedRestoreWorkVector(delegate, vec));
957 CeedCall(CeedDestroy(&delegate));
958 return CEED_ERROR_SUCCESS;
959 }
960
961 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) {
962 if (*vec == ceed->work_vectors->vecs[i]) {
963 CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned");
964 CeedCall(CeedVectorDestroy(vec));
965 ceed->work_vectors->is_in_use[i] = false;
966 // Note: reduce ref_count again to prevent a ref-loop
967 CeedObjectDereference((CeedObject)ceed);
968 return CEED_ERROR_SUCCESS;
969 }
970 }
971 // LCOV_EXCL_START
972 return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()");
973 // LCOV_EXCL_STOP
974 }
975
976 /**
977 @brief Retrieve list of additional JiT source roots from `Ceed` context.
978
979 Note: The caller is responsible for restoring `jit_source_roots` with @ref CeedRestoreJitSourceRoots().
980
981 @param[in] ceed `Ceed` context
982 @param[out] num_source_roots Number of JiT source directories
983 @param[out] jit_source_roots Absolute paths to additional JiT source directories
984
985 @return An error code: 0 - success, otherwise - failure
986
987 @ref Backend
988 **/
CeedGetJitSourceRoots(Ceed ceed,CeedInt * num_source_roots,const char *** jit_source_roots)989 int CeedGetJitSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***jit_source_roots) {
990 Ceed ceed_parent;
991
992 CeedCall(CeedGetParent(ceed, &ceed_parent));
993 *num_source_roots = ceed_parent->num_jit_source_roots;
994 *jit_source_roots = (const char **)ceed_parent->jit_source_roots;
995 ceed_parent->num_jit_source_roots_readers++;
996 CeedCall(CeedDestroy(&ceed_parent));
997 return CEED_ERROR_SUCCESS;
998 }
999
1000 /**
1001 @brief Retrieve list of additional Rust source roots from `Ceed` context.
1002
1003 Note: The caller is responsible for restoring `rust_source_roots` with @ref CeedRestoreRustSourceRoots().
1004
1005 @param[in] ceed `Ceed` context
1006 @param[out] num_source_roots Number of JiT source directories
1007 @param[out] rust_source_roots Absolute paths to additional Rust source directories
1008
1009 @return An error code: 0 - success, otherwise - failure
1010
1011 @ref Backend
1012 **/
CeedGetRustSourceRoots(Ceed ceed,CeedInt * num_source_roots,const char *** rust_source_roots)1013 int CeedGetRustSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***rust_source_roots) {
1014 Ceed ceed_parent;
1015
1016 CeedCall(CeedGetParent(ceed, &ceed_parent));
1017 *num_source_roots = ceed_parent->num_rust_source_roots;
1018 *rust_source_roots = (const char **)ceed_parent->rust_source_roots;
1019 ceed_parent->num_rust_source_roots_readers++;
1020 CeedCall(CeedDestroy(&ceed_parent));
1021 return CEED_ERROR_SUCCESS;
1022 }
1023
1024 /**
1025 @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots()
1026
1027 @param[in] ceed `Ceed` context
1028 @param[out] jit_source_roots Absolute paths to additional JiT source directories
1029
1030 @return An error code: 0 - success, otherwise - failure
1031
1032 @ref Backend
1033 **/
CeedRestoreJitSourceRoots(Ceed ceed,const char *** jit_source_roots)1034 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) {
1035 Ceed ceed_parent;
1036
1037 CeedCall(CeedGetParent(ceed, &ceed_parent));
1038 *jit_source_roots = NULL;
1039 ceed_parent->num_jit_source_roots_readers--;
1040 CeedCall(CeedDestroy(&ceed_parent));
1041 return CEED_ERROR_SUCCESS;
1042 }
1043
1044 /**
1045 @brief Restore list of additional Rust source roots from with @ref CeedGetJitSourceRoots()
1046
1047 @param[in] ceed `Ceed` context
1048 @param[out] rust_source_roots Absolute paths to additional Rust source directories
1049
1050 @return An error code: 0 - success, otherwise - failure
1051
1052 @ref Backend
1053 **/
CeedRestoreRustSourceRoots(Ceed ceed,const char *** rust_source_roots)1054 int CeedRestoreRustSourceRoots(Ceed ceed, const char ***rust_source_roots) {
1055 Ceed ceed_parent;
1056
1057 CeedCall(CeedGetParent(ceed, &ceed_parent));
1058 *rust_source_roots = NULL;
1059 ceed_parent->num_rust_source_roots_readers--;
1060 CeedCall(CeedDestroy(&ceed_parent));
1061 return CEED_ERROR_SUCCESS;
1062 }
1063
1064 /**
1065 @brief Retrieve list of additional JiT defines from `Ceed` context.
1066
1067 Note: The caller is responsible for restoring `jit_defines` with @ref CeedRestoreJitDefines().
1068
1069 @param[in] ceed `Ceed` context
1070 @param[out] num_jit_defines Number of JiT defines
1071 @param[out] jit_defines Strings such as `foo=bar`, used as `-Dfoo=bar` in JiT
1072
1073 @return An error code: 0 - success, otherwise - failure
1074
1075 @ref Backend
1076 **/
CeedGetJitDefines(Ceed ceed,CeedInt * num_jit_defines,const char *** jit_defines)1077 int CeedGetJitDefines(Ceed ceed, CeedInt *num_jit_defines, const char ***jit_defines) {
1078 Ceed ceed_parent;
1079
1080 CeedCall(CeedGetParent(ceed, &ceed_parent));
1081 *num_jit_defines = ceed_parent->num_jit_defines;
1082 *jit_defines = (const char **)ceed_parent->jit_defines;
1083 ceed_parent->num_jit_defines_readers++;
1084 CeedCall(CeedDestroy(&ceed_parent));
1085 return CEED_ERROR_SUCCESS;
1086 }
1087
1088 /**
1089 @brief Restore list of additional JiT defines from with @ref CeedGetJitDefines()
1090
1091 @param[in] ceed `Ceed` context
1092 @param[out] jit_defines String such as `foo=bar`, used as `-Dfoo=bar` in JiT
1093
1094 @return An error code: 0 - success, otherwise - failure
1095
1096 @ref Backend
1097 **/
CeedRestoreJitDefines(Ceed ceed,const char *** jit_defines)1098 int CeedRestoreJitDefines(Ceed ceed, const char ***jit_defines) {
1099 Ceed ceed_parent;
1100
1101 CeedCall(CeedGetParent(ceed, &ceed_parent));
1102 *jit_defines = NULL;
1103 ceed_parent->num_jit_defines_readers--;
1104 CeedCall(CeedDestroy(&ceed_parent));
1105 return CEED_ERROR_SUCCESS;
1106 }
1107
1108 /// @}
1109
1110 /// ----------------------------------------------------------------------------
1111 /// Ceed Public API
1112 /// ----------------------------------------------------------------------------
1113 /// @addtogroup CeedUser
1114 /// @{
1115
1116 /**
1117 @brief Get the list of available resource names for `Ceed` contexts
1118
1119 Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array.
1120
1121 @param[out] n Number of available resources
1122 @param[out] resources List of available resource names
1123 @param[out] priorities Resource name prioritization values, lower is better
1124
1125 @return An error code: 0 - success, otherwise - failure
1126
1127 @ref User
1128 **/
1129 // LCOV_EXCL_START
CeedRegistryGetList(size_t * n,char *** const resources,CeedInt ** priorities)1130 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) {
1131 *n = 0;
1132 *resources = malloc(num_backends * sizeof(**resources));
1133 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure");
1134 if (priorities) {
1135 *priorities = malloc(num_backends * sizeof(**priorities));
1136 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure");
1137 }
1138 for (size_t i = 0; i < num_backends; i++) {
1139 // Only report compiled backends
1140 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) {
1141 *resources[i] = backends[i].prefix;
1142 if (priorities) *priorities[i] = backends[i].priority;
1143 *n += 1;
1144 }
1145 }
1146 CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed");
1147 *resources = realloc(*resources, *n * sizeof(**resources));
1148 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1149 if (priorities) {
1150 *priorities = realloc(*priorities, *n * sizeof(**priorities));
1151 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure");
1152 }
1153 return CEED_ERROR_SUCCESS;
1154 }
1155 // LCOV_EXCL_STOP
1156
1157 /**
1158 @brief Initialize a `Ceed` context to use the specified resource.
1159
1160 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`.
1161
1162 @param[in] resource Resource to use, e.g., "/cpu/self"
1163 @param[out] ceed The library context
1164
1165 @return An error code: 0 - success, otherwise - failure
1166
1167 @ref User
1168
1169 @sa CeedRegister() CeedDestroy()
1170 **/
CeedInit(const char * resource,Ceed * ceed)1171 int CeedInit(const char *resource, Ceed *ceed) {
1172 size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority;
1173
1174 // Find matching backend
1175 CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided");
1176 CeedCall(CeedRegisterAll());
1177
1178 // Check for help request
1179 const char *help_prefix = "help";
1180 size_t match_help = 0;
1181 while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++;
1182 if (match_help == 4) {
1183 fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH,
1184 CEED_VERSION_RELEASE ? "" : "+development");
1185 fprintf(stderr, "Available backend resources:\n");
1186 for (size_t i = 0; i < num_backends; i++) {
1187 // Only report compiled backends
1188 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, " %s\n", backends[i].prefix);
1189 }
1190 fflush(stderr);
1191 match_help = 5; // Delineating character expected
1192 } else {
1193 match_help = 0;
1194 }
1195
1196 // Find best match, computed as number of matching characters from requested resource stem
1197 size_t stem_length = 0;
1198 while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++;
1199 for (size_t i = 0; i < num_backends; i++) {
1200 size_t n = 0;
1201 const char *prefix = backends[i].prefix;
1202 while (prefix[n] && prefix[n] == resource[n + match_help]) n++;
1203 priority = backends[i].priority;
1204 if (n > match_len || (n == match_len && match_priority > priority)) {
1205 match_len = n;
1206 match_priority = priority;
1207 match_index = i;
1208 }
1209 }
1210 // Using Levenshtein distance to find closest match
1211 if (match_len <= 1 || match_len != stem_length) {
1212 // LCOV_EXCL_START
1213 size_t lev_dis = UINT_MAX;
1214 size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY;
1215 for (size_t i = 0; i < num_backends; i++) {
1216 const char *prefix = backends[i].prefix;
1217 size_t prefix_length = strlen(backends[i].prefix);
1218 size_t min_len = (prefix_length < stem_length) ? prefix_length : stem_length;
1219 size_t column[min_len + 1];
1220 for (size_t j = 0; j <= min_len; j++) column[j] = j;
1221 for (size_t j = 1; j <= min_len; j++) {
1222 column[0] = j;
1223 for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) {
1224 size_t old_diag = column[k];
1225 size_t min_1 = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1;
1226 size_t min_2 = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1);
1227 column[k] = (min_1 < min_2) ? min_1 : min_2;
1228 last_diag = old_diag;
1229 }
1230 }
1231 size_t n = column[min_len];
1232 priority = backends[i].priority;
1233 if (n < lev_dis || (n == lev_dis && lev_priority > priority)) {
1234 lev_dis = n;
1235 lev_priority = priority;
1236 lev_index = i;
1237 }
1238 }
1239 const char *prefix_lev = backends[lev_index].prefix;
1240 size_t lev_length = 0;
1241 while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++;
1242 size_t m = (lev_length < stem_length) ? lev_length : stem_length;
1243 if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource);
1244 else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix);
1245 // LCOV_EXCL_STOP
1246 }
1247
1248 // Setup Ceed
1249 CeedCall(CeedCalloc(1, ceed));
1250 CeedCall(CeedObjectCreate(NULL, CeedView_Object, CeedDestroy_Object, &(*ceed)->obj));
1251 CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots));
1252 CeedCall(CeedCalloc(1, &(*ceed)->rust_source_roots));
1253 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1254 if (!ceed_error_handler) ceed_error_handler = "abort";
1255 if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit;
1256 else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore;
1257 else (*ceed)->Error = CeedErrorAbort;
1258 memcpy((*ceed)->err_msg, "No error message stored", 24);
1259 (*ceed)->data = NULL;
1260
1261 // Set lookup table
1262 FOffset f_offsets[] = {
1263 CEED_FTABLE_ENTRY(Ceed, Error),
1264 CEED_FTABLE_ENTRY(Ceed, SetStream),
1265 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType),
1266 CEED_FTABLE_ENTRY(Ceed, Destroy),
1267 CEED_FTABLE_ENTRY(Ceed, VectorCreate),
1268 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate),
1269 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints),
1270 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked),
1271 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1),
1272 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1),
1273 CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv),
1274 CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl),
1275 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate),
1276 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate),
1277 CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate),
1278 CEED_FTABLE_ENTRY(Ceed, OperatorCreate),
1279 CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints),
1280 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate),
1281 CEED_FTABLE_ENTRY(CeedVector, HasValidArray),
1282 CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType),
1283 CEED_FTABLE_ENTRY(CeedVector, CopyStrided),
1284 CEED_FTABLE_ENTRY(CeedVector, SetArray),
1285 CEED_FTABLE_ENTRY(CeedVector, TakeArray),
1286 CEED_FTABLE_ENTRY(CeedVector, SetValue),
1287 CEED_FTABLE_ENTRY(CeedVector, SetValueStrided),
1288 CEED_FTABLE_ENTRY(CeedVector, SyncArray),
1289 CEED_FTABLE_ENTRY(CeedVector, GetArray),
1290 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead),
1291 CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite),
1292 CEED_FTABLE_ENTRY(CeedVector, RestoreArray),
1293 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead),
1294 CEED_FTABLE_ENTRY(CeedVector, Norm),
1295 CEED_FTABLE_ENTRY(CeedVector, Scale),
1296 CEED_FTABLE_ENTRY(CeedVector, AXPY),
1297 CEED_FTABLE_ENTRY(CeedVector, AXPBY),
1298 CEED_FTABLE_ENTRY(CeedVector, PointwiseMult),
1299 CEED_FTABLE_ENTRY(CeedVector, Reciprocal),
1300 CEED_FTABLE_ENTRY(CeedVector, Destroy),
1301 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply),
1302 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned),
1303 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented),
1304 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement),
1305 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock),
1306 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets),
1307 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations),
1308 CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations),
1309 CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset),
1310 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy),
1311 CEED_FTABLE_ENTRY(CeedBasis, Apply),
1312 CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd),
1313 CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints),
1314 CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints),
1315 CEED_FTABLE_ENTRY(CeedBasis, Destroy),
1316 CEED_FTABLE_ENTRY(CeedTensorContract, Apply),
1317 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy),
1318 CEED_FTABLE_ENTRY(CeedQFunction, Apply),
1319 CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction),
1320 CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction),
1321 CEED_FTABLE_ENTRY(CeedQFunction, Destroy),
1322 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData),
1323 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType),
1324 CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData),
1325 CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData),
1326 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData),
1327 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead),
1328 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData),
1329 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead),
1330 CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy),
1331 CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy),
1332 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction),
1333 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate),
1334 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal),
1335 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal),
1336 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal),
1337 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal),
1338 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic),
1339 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble),
1340 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle),
1341 CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse),
1342 CEED_FTABLE_ENTRY(CeedOperator, Apply),
1343 CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite),
1344 CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd),
1345 CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite),
1346 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian),
1347 CEED_FTABLE_ENTRY(CeedOperator, Destroy),
1348 {NULL, 0} // End of lookup table - used in SetBackendFunction loop
1349 };
1350
1351 CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets));
1352 memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets));
1353
1354 // Record env variables CEED_DEBUG or DBG
1355 (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG");
1356
1357 // Copy resource prefix, if backend setup successful
1358 CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource));
1359
1360 // Set default JiT source root
1361 // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent
1362 CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault));
1363
1364 // By default, make cuda compile without clang, use nvrtc instead
1365 // Note that this is overridden if a rust file is included (rust requires clang)
1366 const char *env = getenv("GPU_CLANG");
1367
1368 if (env && strcmp(env, "1") == 0) {
1369 (*ceed)->cuda_compile_with_clang = true;
1370 } else {
1371 (*ceed)->cuda_compile_with_clang = false;
1372 }
1373
1374 // Backend specific setup
1375 CeedCall(backends[match_index].init(&resource[match_help], *ceed));
1376 return CEED_ERROR_SUCCESS;
1377 }
1378
1379 /**
1380 @brief Set the GPU stream for a `Ceed` context
1381
1382 @param[in,out] ceed `Ceed` context to set the stream
1383 @param[in] handle Handle to GPU stream
1384
1385 @return An error code: 0 - success, otherwise - failure
1386
1387 @ref User
1388 **/
CeedSetStream(Ceed ceed,void * handle)1389 int CeedSetStream(Ceed ceed, void *handle) {
1390 CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL");
1391 if (ceed->SetStream) {
1392 CeedCall(ceed->SetStream(ceed, handle));
1393 } else {
1394 Ceed delegate;
1395 CeedCall(CeedGetDelegate(ceed, &delegate));
1396
1397 if (delegate) CeedCall(CeedSetStream(delegate, handle));
1398 else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream");
1399 CeedCall(CeedDestroy(&delegate));
1400 }
1401 return CEED_ERROR_SUCCESS;
1402 }
1403
1404 /**
1405 @brief Copy the pointer to a `Ceed` context.
1406
1407 Both pointers should be destroyed with @ref CeedDestroy().
1408
1409 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.
1410 This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context.
1411
1412 @param[in] ceed `Ceed` context to copy reference to
1413 @param[in,out] ceed_copy Variable to store copied reference
1414
1415 @return An error code: 0 - success, otherwise - failure
1416
1417 @ref User
1418 **/
CeedReferenceCopy(Ceed ceed,Ceed * ceed_copy)1419 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) {
1420 CeedCall(CeedReference(ceed));
1421 CeedCall(CeedDestroy(ceed_copy));
1422 *ceed_copy = ceed;
1423 return CEED_ERROR_SUCCESS;
1424 }
1425
1426 /**
1427 @brief Get the full resource name for a `Ceed` context
1428
1429 @param[in] ceed `Ceed` context to get resource name of
1430 @param[out] resource Variable to store resource name
1431
1432 @return An error code: 0 - success, otherwise - failure
1433
1434 @ref User
1435 **/
CeedGetResource(Ceed ceed,const char ** resource)1436 int CeedGetResource(Ceed ceed, const char **resource) {
1437 *resource = (const char *)ceed->resource;
1438 return CEED_ERROR_SUCCESS;
1439 }
1440
1441 /**
1442 @brief Return `Ceed` context preferred memory type
1443
1444 @param[in] ceed `Ceed` context to get preferred memory type of
1445 @param[out] mem_type Address to save preferred memory type to
1446
1447 @return An error code: 0 - success, otherwise - failure
1448
1449 @ref User
1450 **/
CeedGetPreferredMemType(Ceed ceed,CeedMemType * mem_type)1451 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) {
1452 if (ceed->GetPreferredMemType) {
1453 CeedCall(ceed->GetPreferredMemType(mem_type));
1454 } else {
1455 Ceed delegate;
1456 CeedCall(CeedGetDelegate(ceed, &delegate));
1457
1458 if (delegate) {
1459 CeedCall(CeedGetPreferredMemType(delegate, mem_type));
1460 } else {
1461 *mem_type = CEED_MEM_HOST;
1462 }
1463 CeedCall(CeedDestroy(&delegate));
1464 }
1465 return CEED_ERROR_SUCCESS;
1466 }
1467
1468 /**
1469 @brief Get deterministic status of `Ceed` context
1470
1471 @param[in] ceed `Ceed` context
1472 @param[out] is_deterministic Variable to store deterministic status
1473
1474 @return An error code: 0 - success, otherwise - failure
1475
1476 @ref User
1477 **/
CeedIsDeterministic(Ceed ceed,bool * is_deterministic)1478 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) {
1479 *is_deterministic = ceed->is_deterministic;
1480 return CEED_ERROR_SUCCESS;
1481 }
1482
1483 /**
1484 @brief Set additional JiT source root for `Ceed` context
1485
1486 @param[in,out] ceed `Ceed` context
1487 @param[in] jit_source_root Absolute path to additional JiT source directory
1488
1489 @return An error code: 0 - success, otherwise - failure
1490
1491 @ref User
1492 **/
CeedAddJitSourceRoot(Ceed ceed,const char * jit_source_root)1493 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) {
1494 Ceed ceed_parent;
1495
1496 CeedCall(CeedGetParent(ceed, &ceed_parent));
1497 CeedCheck(!ceed_parent->num_jit_source_roots_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access has not been restored");
1498
1499 CeedInt index = ceed_parent->num_jit_source_roots;
1500 size_t path_length = strlen(jit_source_root);
1501
1502 if (ceed_parent->num_jit_source_roots == ceed_parent->max_jit_source_roots) {
1503 if (ceed_parent->max_jit_source_roots == 0) ceed_parent->max_jit_source_roots = 1;
1504 ceed_parent->max_jit_source_roots *= 2;
1505 CeedCall(CeedRealloc(ceed_parent->max_jit_source_roots, &ceed_parent->jit_source_roots));
1506 }
1507 CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index]));
1508 memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length);
1509 ceed_parent->num_jit_source_roots++;
1510 CeedCall(CeedDestroy(&ceed_parent));
1511 return CEED_ERROR_SUCCESS;
1512 }
1513
1514 /**
1515 @brief Set additional Rust source root for `Ceed` context for use in QFunction
1516
1517 @param[in,out] ceed `Ceed` context
1518 @param[in] rust_source_root Absolute path to additional Rust source directory
1519
1520 @return An error code: 0 - success, otherwise - failure
1521
1522 @ref User
1523 **/
CeedAddRustSourceRoot(Ceed ceed,const char * rust_source_root)1524 int CeedAddRustSourceRoot(Ceed ceed, const char *rust_source_root) {
1525 Ceed ceed_parent;
1526
1527 CeedCall(CeedGetParent(ceed, &ceed_parent));
1528 CeedCheck(!ceed_parent->num_rust_source_roots_readers, ceed, CEED_ERROR_ACCESS, "Cannot add Rust source root, read access has not been restored");
1529
1530 CeedInt index = ceed_parent->num_rust_source_roots;
1531 size_t path_length = strlen(rust_source_root);
1532
1533 if (ceed_parent->num_rust_source_roots == ceed_parent->max_rust_source_roots) {
1534 if (ceed_parent->max_rust_source_roots == 0) ceed_parent->max_rust_source_roots = 1;
1535 ceed_parent->max_rust_source_roots *= 2;
1536 CeedCall(CeedRealloc(ceed_parent->max_rust_source_roots, &ceed_parent->rust_source_roots));
1537 }
1538 CeedCall(CeedCalloc(path_length + 1, &ceed_parent->rust_source_roots[index]));
1539 memcpy(ceed_parent->rust_source_roots[index], rust_source_root, path_length);
1540 ceed_parent->num_rust_source_roots++;
1541 ceed_parent->cuda_compile_with_clang = true;
1542 ceed->cuda_compile_with_clang = true;
1543 CeedCall(CeedDestroy(&ceed_parent));
1544 return CEED_ERROR_SUCCESS;
1545 }
1546
1547 /**
1548 @brief Set additional JiT compiler define for `Ceed` context
1549
1550 @param[in,out] ceed `Ceed` context
1551 @param[in] jit_define String such as `foo=bar`, used as `-Dfoo=bar` in JiT
1552
1553 @return An error code: 0 - success, otherwise - failure
1554
1555 @ref User
1556 **/
CeedAddJitDefine(Ceed ceed,const char * jit_define)1557 int CeedAddJitDefine(Ceed ceed, const char *jit_define) {
1558 Ceed ceed_parent;
1559
1560 CeedCall(CeedGetParent(ceed, &ceed_parent));
1561 CeedCheck(!ceed_parent->num_jit_defines_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT define, read access has not been restored");
1562
1563 CeedInt index = ceed_parent->num_jit_defines;
1564 size_t define_length = strlen(jit_define);
1565
1566 if (ceed_parent->num_jit_defines == ceed_parent->max_jit_defines) {
1567 if (ceed_parent->max_jit_defines == 0) ceed_parent->max_jit_defines = 1;
1568 ceed_parent->max_jit_defines *= 2;
1569 CeedCall(CeedRealloc(ceed_parent->max_jit_defines, &ceed_parent->jit_defines));
1570 }
1571 CeedCall(CeedCalloc(define_length + 1, &ceed_parent->jit_defines[index]));
1572 memcpy(ceed_parent->jit_defines[index], jit_define, define_length);
1573 ceed_parent->num_jit_defines++;
1574 CeedCall(CeedDestroy(&ceed_parent));
1575 return CEED_ERROR_SUCCESS;
1576 }
1577
1578 /**
1579 @brief Set the number of tabs to indent for @ref CeedView() output
1580
1581 @param[in] ceed `Ceed` to set the number of view tabs
1582 @param[in] num_tabs Number of view tabs to set
1583
1584 @return Error code: 0 - success, otherwise - failure
1585
1586 @ref User
1587 **/
CeedSetNumViewTabs(Ceed ceed,CeedInt num_tabs)1588 int CeedSetNumViewTabs(Ceed ceed, CeedInt num_tabs) {
1589 CeedCall(CeedObjectSetNumViewTabs((CeedObject)ceed, num_tabs));
1590 return CEED_ERROR_SUCCESS;
1591 }
1592
1593 /**
1594 @brief Get the number of tabs to indent for @ref CeedView() output
1595
1596 @param[in] ceed `Ceed` to get the number of view tabs
1597 @param[out] num_tabs Number of view tabs
1598
1599 @return Error code: 0 - success, otherwise - failure
1600
1601 @ref User
1602 **/
CeedGetNumViewTabs(Ceed ceed,CeedInt * num_tabs)1603 int CeedGetNumViewTabs(Ceed ceed, CeedInt *num_tabs) {
1604 CeedCall(CeedObjectGetNumViewTabs((CeedObject)ceed, num_tabs));
1605 return CEED_ERROR_SUCCESS;
1606 }
1607
1608 /**
1609 @brief View a `Ceed`
1610
1611 @param[in] ceed `Ceed` to view
1612 @param[in] stream Filestream to write to
1613
1614 @return An error code: 0 - success, otherwise - failure
1615
1616 @ref User
1617 **/
CeedView(Ceed ceed,FILE * stream)1618 int CeedView(Ceed ceed, FILE *stream) {
1619 char *tabs = NULL;
1620 CeedMemType mem_type;
1621
1622 CeedCall(CeedGetPreferredMemType(ceed, &mem_type));
1623
1624 {
1625 CeedInt num_tabs = 0;
1626
1627 CeedCall(CeedGetNumViewTabs(ceed, &num_tabs));
1628 CeedCall(CeedCalloc(CEED_TAB_WIDTH * num_tabs + 1, &tabs));
1629 for (CeedInt i = 0; i < CEED_TAB_WIDTH * num_tabs; i++) tabs[i] = ' ';
1630 }
1631 fprintf(stream,
1632 "%sCeed\n"
1633 "%s Ceed Resource: %s\n"
1634 "%s Preferred MemType: %s\n",
1635 tabs, tabs, ceed->resource, tabs, CeedMemTypes[mem_type]);
1636 CeedCall(CeedFree(&tabs));
1637 return CEED_ERROR_SUCCESS;
1638 }
1639
1640 /**
1641 @brief Destroy a `Ceed`
1642
1643 @param[in,out] ceed Address of `Ceed` context to destroy
1644
1645 @return An error code: 0 - success, otherwise - failure
1646
1647 @ref User
1648 **/
CeedDestroy(Ceed * ceed)1649 int CeedDestroy(Ceed *ceed) {
1650 if (!*ceed || CeedObjectDereference((CeedObject)*ceed) > 0) {
1651 *ceed = NULL;
1652 return CEED_ERROR_SUCCESS;
1653 }
1654
1655 CeedCheck(!(*ceed)->num_jit_source_roots_readers, *ceed, CEED_ERROR_ACCESS,
1656 "Cannot destroy ceed context, read access for JiT source roots has been granted");
1657 CeedCheck(!(*ceed)->num_jit_defines_readers, *ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access for JiT defines has been granted");
1658
1659 if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate));
1660
1661 if ((*ceed)->obj_delegate_count > 0) {
1662 for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) {
1663 CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate)));
1664 CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name));
1665 }
1666 CeedCall(CeedFree(&(*ceed)->obj_delegates));
1667 }
1668
1669 if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed));
1670
1671 for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) {
1672 CeedCall(CeedFree(&(*ceed)->jit_source_roots[i]));
1673 }
1674 CeedCall(CeedFree(&(*ceed)->jit_source_roots));
1675
1676 for (CeedInt i = 0; i < (*ceed)->num_jit_defines; i++) {
1677 CeedCall(CeedFree(&(*ceed)->jit_defines[i]));
1678 }
1679 CeedCall(CeedFree(&(*ceed)->jit_defines));
1680
1681 for (CeedInt i = 0; i < (*ceed)->num_rust_source_roots; i++) {
1682 CeedCall(CeedFree(&(*ceed)->rust_source_roots[i]));
1683 }
1684 CeedCall(CeedFree(&(*ceed)->rust_source_roots));
1685
1686 CeedCall(CeedFree(&(*ceed)->f_offsets));
1687 CeedCall(CeedFree(&(*ceed)->resource));
1688 CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed));
1689 CeedCall(CeedWorkVectorsDestroy(*ceed));
1690 CeedCall(CeedObjectDestroy_Private(&(*ceed)->obj));
1691 CeedCall(CeedFree(ceed));
1692 return CEED_ERROR_SUCCESS;
1693 }
1694
1695 // LCOV_EXCL_START
CeedErrorFormat(Ceed ceed,const char * format,va_list * args)1696 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) {
1697 if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args);
1698 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1699 vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args); // NOLINT
1700 return ceed->err_msg;
1701 }
1702 // LCOV_EXCL_STOP
1703
1704 /**
1705 @brief Error handling implementation; use @ref CeedError() instead.
1706
1707 @return An error code: 0 - success, otherwise - failure
1708
1709 @ref Developer
1710 **/
CeedErrorImpl(Ceed ceed,const char * filename,int lineno,const char * func,int ecode,const char * format,...)1711 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) {
1712 va_list args;
1713 int ret_val;
1714
1715 va_start(args, format);
1716 if (ceed) {
1717 ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args);
1718 } else {
1719 // LCOV_EXCL_START
1720 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER");
1721 if (!ceed_error_handler) ceed_error_handler = "abort";
1722 if (!strcmp(ceed_error_handler, "return")) {
1723 ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args);
1724 } else {
1725 // This function will not return
1726 ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args);
1727 }
1728 }
1729 va_end(args);
1730 return ret_val;
1731 // LCOV_EXCL_STOP
1732 }
1733
1734 /**
1735 @brief Error handler that returns without printing anything.
1736
1737 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1738
1739 @return An error code: 0 - success, otherwise - failure
1740
1741 @ref Developer
1742 **/
1743 // LCOV_EXCL_START
CeedErrorReturn(Ceed ceed,const char * filename,int line_no,const char * func,int err_code,const char * format,va_list * args)1744 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1745 return err_code;
1746 }
1747 // LCOV_EXCL_STOP
1748
1749 /**
1750 @brief Error handler that stores the error message for future use and returns the error.
1751
1752 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1753
1754 @return An error code: 0 - success, otherwise - failure
1755
1756 @ref Developer
1757 **/
1758 // LCOV_EXCL_START
CeedErrorStore(Ceed ceed,const char * filename,int line_no,const char * func,int err_code,const char * format,va_list * args)1759 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1760 if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args);
1761
1762 // Build message
1763 int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func);
1764 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1765 vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT
1766 return err_code;
1767 }
1768 // LCOV_EXCL_STOP
1769
1770 /**
1771 @brief Error handler that prints to `stderr` and aborts
1772
1773 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1774
1775 @return An error code: 0 - success, otherwise - failure
1776
1777 @ref Developer
1778 **/
1779 // LCOV_EXCL_START
CeedErrorAbort(Ceed ceed,const char * filename,int line_no,const char * func,int err_code,const char * format,va_list * args)1780 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1781 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1782 vfprintf(stderr, format, *args);
1783 fprintf(stderr, "\n");
1784 abort();
1785 return err_code;
1786 }
1787 // LCOV_EXCL_STOP
1788
1789 /**
1790 @brief Error handler that prints to `stderr` and exits.
1791
1792 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior.
1793
1794 In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run.
1795
1796 @return An error code: 0 - success, otherwise - failure
1797
1798 @ref Developer
1799 **/
CeedErrorExit(Ceed ceed,const char * filename,int line_no,const char * func,int err_code,const char * format,va_list * args)1800 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) {
1801 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func);
1802 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized
1803 vfprintf(stderr, format, *args); // NOLINT
1804 fprintf(stderr, "\n");
1805 exit(err_code);
1806 return err_code;
1807 }
1808
1809 /**
1810 @brief Set error handler
1811
1812 A default error handler is set in @ref CeedInit().
1813 Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler.
1814
1815 @return An error code: 0 - success, otherwise - failure
1816
1817 @ref Developer
1818 **/
CeedSetErrorHandler(Ceed ceed,CeedErrorHandler handler)1819 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) {
1820 ceed->Error = handler;
1821 if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler);
1822 for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler);
1823 return CEED_ERROR_SUCCESS;
1824 }
1825
1826 /**
1827 @brief Get error message
1828
1829 The error message is only stored when using the error handler @ref CeedErrorStore()
1830
1831 @param[in] ceed `Ceed` context to retrieve error message
1832 @param[out] err_msg Char pointer to hold error message
1833
1834 @return An error code: 0 - success, otherwise - failure
1835
1836 @ref Developer
1837 **/
CeedGetErrorMessage(Ceed ceed,const char ** err_msg)1838 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) {
1839 if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg);
1840 *err_msg = ceed->err_msg;
1841 return CEED_ERROR_SUCCESS;
1842 }
1843
1844 /**
1845 @brief Restore error message.
1846
1847 The error message is only stored when using the error handler @ref CeedErrorStore().
1848
1849 @param[in] ceed `Ceed` context to restore error message
1850 @param[out] err_msg Char pointer that holds error message
1851
1852 @return An error code: 0 - success, otherwise - failure
1853
1854 @ref Developer
1855 **/
CeedResetErrorMessage(Ceed ceed,const char ** err_msg)1856 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) {
1857 if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg);
1858 *err_msg = NULL;
1859 memcpy(ceed->err_msg, "No error message stored", 24);
1860 return CEED_ERROR_SUCCESS;
1861 }
1862
1863 /**
1864 @brief Get libCEED library version information.
1865
1866 libCEED version numbers have the form major.minor.patch.
1867 Non-release versions may contain unstable interfaces.
1868
1869 @param[out] major Major version of the library
1870 @param[out] minor Minor version of the library
1871 @param[out] patch Patch (subminor) version of the library
1872 @param[out] release True for releases; false for development branches
1873
1874 The caller may pass `NULL` for any arguments that are not needed.
1875
1876 @return An error code: 0 - success, otherwise - failure
1877
1878 @ref Developer
1879
1880 @sa CEED_VERSION_GE() CeedGetGitVersion() CeedGetBuildConfiguration()
1881 */
CeedGetVersion(int * major,int * minor,int * patch,bool * release)1882 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) {
1883 if (major) *major = CEED_VERSION_MAJOR;
1884 if (minor) *minor = CEED_VERSION_MINOR;
1885 if (patch) *patch = CEED_VERSION_PATCH;
1886 if (release) *release = CEED_VERSION_RELEASE;
1887 return CEED_ERROR_SUCCESS;
1888 }
1889
1890 /**
1891 @brief Get libCEED scalar type, such as F64 or F32
1892
1893 @param[out] scalar_type Type of libCEED scalars
1894
1895 @return An error code: 0 - success, otherwise - failure
1896
1897 @ref Developer
1898 */
CeedGetScalarType(CeedScalarType * scalar_type)1899 int CeedGetScalarType(CeedScalarType *scalar_type) {
1900 *scalar_type = CEED_SCALAR_TYPE;
1901 return CEED_ERROR_SUCCESS;
1902 }
1903
1904 /// @}
1905