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