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