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