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