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