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