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