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