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