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