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, ApplyBlock), 818 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets), 819 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 820 CEED_FTABLE_ENTRY(CeedBasis, Apply), 821 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 822 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 823 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 824 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 825 CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction), 826 CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction), 827 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 828 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData), 829 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType), 830 CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData), 831 CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData), 832 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData), 833 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead), 834 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData), 835 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead), 836 CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy), 837 CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy), 838 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction), 839 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate), 840 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal), 841 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal), 842 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal), 843 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal), 844 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic), 845 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble), 846 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle), 847 CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse), 848 CEED_FTABLE_ENTRY(CeedOperator, Apply), 849 CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite), 850 CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd), 851 CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite), 852 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 853 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 854 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 855 }; 856 857 CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets)); 858 memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets)); 859 860 // Set fallback for advanced CeedOperator functions 861 const char fallbackresource[] = ""; 862 CeedCall(CeedSetOperatorFallbackResource(*ceed, fallbackresource)); 863 864 // Record env variables CEED_DEBUG or DBG 865 (*ceed)->is_debug = !!getenv("CEED_DEBUG") || !!getenv("DEBUG") || !!getenv("DBG"); 866 867 // Copy resource prefix, if backend setup successful 868 CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource)); 869 870 // Set default JiT source root 871 // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent 872 CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault)); 873 874 // Backend specific setup 875 CeedCall(backends[match_index].init(&resource[match_help], *ceed)); 876 877 return CEED_ERROR_SUCCESS; 878 } 879 880 /** 881 @brief Copy the pointer to a Ceed context. 882 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