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