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