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