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