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