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