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