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