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