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 `Ceed` for `CeedOperator` 628 629 @param[in] ceed `Ceed` context 630 @param[out] fallback_ceed Variable to store fallback `Ceed` 631 632 @return An error code: 0 - success, otherwise - failure 633 634 @ref Backend 635 **/ 636 int CeedGetOperatorFallbackCeed(Ceed ceed, Ceed *fallback_ceed) { 637 if (ceed->op_fallback_ceed) { 638 CeedDebug256(ceed, CEED_DEBUG_COLOR_SUCCESS, "---------- Ceed Fallback ----------\n"); 639 CeedDebug(ceed, "Falling back from Ceed with backend %s at address %p to Ceed with backend %s at address %p", ceed->resource, ceed, 640 ceed->op_fallback_ceed->resource, ceed->op_fallback_ceed); 641 } 642 643 *fallback_ceed = NULL; 644 if (ceed->op_fallback_ceed) CeedCall(CeedReferenceCopy(ceed->op_fallback_ceed, fallback_ceed)); 645 return CEED_ERROR_SUCCESS; 646 } 647 648 /** 649 @brief Set the fallback resource for `CeedOperator`. 650 651 The current fallback, if any, is freed by calling this function. 652 653 @param[in,out] ceed `Ceed` context 654 @param[in] fallback_ceed `Ceed` context to create fallback operators 655 656 @return An error code: 0 - success, otherwise - failure 657 658 @ref Backend 659 **/ 660 int CeedSetOperatorFallbackCeed(Ceed ceed, Ceed fallback_ceed) { 661 CeedCall(CeedReferenceCopy(fallback_ceed, &ceed->op_fallback_ceed)); 662 fallback_ceed->parent = ceed; 663 return CEED_ERROR_SUCCESS; 664 } 665 666 /** 667 @brief Flag `Ceed` context as deterministic 668 669 @param[in] ceed `Ceed` to flag as deterministic 670 @param[out] is_deterministic Deterministic status to set 671 672 @return An error code: 0 - success, otherwise - failure 673 674 @ref Backend 675 **/ 676 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) { 677 ceed->is_deterministic = is_deterministic; 678 return CEED_ERROR_SUCCESS; 679 } 680 681 /** 682 @brief Set a backend function. 683 684 This function is used for a backend to set the function associated with the Ceed objects. 685 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(). 686 Note, the prefix 'Ceed' is not required for the object type ("Basis" vs "CeedBasis"). 687 688 @param[in] ceed `Ceed` context for error handling 689 @param[in] type Type of Ceed object to set function for 690 @param[out] object Ceed object to set function for 691 @param[in] func_name Name of function to set 692 @param[in] f Function to set 693 694 @return An error code: 0 - success, otherwise - failure 695 696 @ref Backend 697 **/ 698 int CeedSetBackendFunctionImpl(Ceed ceed, const char *type, void *object, const char *func_name, void (*f)(void)) { 699 char lookup_name[CEED_MAX_RESOURCE_LEN + 1] = ""; 700 701 // Build lookup name 702 if (strcmp(type, "Ceed")) strncat(lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN); 703 strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN); 704 strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN); 705 706 // Find and use offset 707 for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) { 708 if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) { 709 size_t offset = ceed->f_offsets[i].offset; 710 int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD* 711 712 *fpointer = (int (*)(void))f; 713 return CEED_ERROR_SUCCESS; 714 } 715 } 716 717 // LCOV_EXCL_START 718 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Requested function '%s' was not found for CEED object '%s'", func_name, type); 719 // LCOV_EXCL_STOP 720 } 721 722 /** 723 @brief Retrieve backend data for a `Ceed` context 724 725 @param[in] ceed `Ceed` context to retrieve data of 726 @param[out] data Address to save data to 727 728 @return An error code: 0 - success, otherwise - failure 729 730 @ref Backend 731 **/ 732 int CeedGetData(Ceed ceed, void *data) { 733 *(void **)data = ceed->data; 734 return CEED_ERROR_SUCCESS; 735 } 736 737 /** 738 @brief Set backend data for a `Ceed` context 739 740 @param[in,out] ceed `Ceed` context to set data of 741 @param[in] data Address of data to set 742 743 @return An error code: 0 - success, otherwise - failure 744 745 @ref Backend 746 **/ 747 int CeedSetData(Ceed ceed, void *data) { 748 ceed->data = data; 749 return CEED_ERROR_SUCCESS; 750 } 751 752 /** 753 @brief Increment the reference counter for a `Ceed` context 754 755 @param[in,out] ceed `Ceed` context to increment the reference counter 756 757 @return An error code: 0 - success, otherwise - failure 758 759 @ref Backend 760 **/ 761 int CeedReference(Ceed ceed) { 762 ceed->ref_count++; 763 return CEED_ERROR_SUCCESS; 764 } 765 766 /** 767 @brief Computes the current memory usage of the work vectors in a `Ceed` context and prints to debug.abort 768 769 @param[in] ceed `Ceed` context 770 @param[out] usage_mb Address of the variable where the MB of work vector usage will be stored 771 772 @return An error code: 0 - success, otherwise - failure 773 774 @ref Developer 775 **/ 776 int CeedGetWorkVectorMemoryUsage(Ceed ceed, CeedScalar *usage_mb) { 777 if (!ceed->VectorCreate) { 778 Ceed delegate; 779 780 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector")); 781 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate"); 782 CeedCall(CeedGetWorkVectorMemoryUsage(delegate, usage_mb)); 783 CeedCall(CeedDestroy(&delegate)); 784 return CEED_ERROR_SUCCESS; 785 } 786 *usage_mb = 0.0; 787 if (ceed->work_vectors) { 788 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) { 789 CeedSize vec_len; 790 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len)); 791 *usage_mb += vec_len; 792 } 793 *usage_mb *= sizeof(CeedScalar) * 1e-6; 794 CeedDebug(ceed, "Resource {%s}: Work vectors memory usage: %" CeedInt_FMT " vectors, %g MB\n", ceed->resource, ceed->work_vectors->num_vecs, 795 *usage_mb); 796 } 797 return CEED_ERROR_SUCCESS; 798 } 799 800 /** 801 @brief Clear inactive work vectors in a `Ceed` context below a minimum length. 802 803 @param[in,out] ceed `Ceed` context 804 @param[in] min_len Minimum length of work vector to keep 805 806 @return An error code: 0 - success, otherwise - failure 807 808 @ref Backend 809 **/ 810 int CeedClearWorkVectors(Ceed ceed, CeedSize min_len) { 811 if (!ceed->VectorCreate) { 812 Ceed delegate; 813 814 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector")); 815 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate"); 816 CeedCall(CeedClearWorkVectors(delegate, min_len)); 817 CeedCall(CeedDestroy(&delegate)); 818 return CEED_ERROR_SUCCESS; 819 } 820 if (!ceed->work_vectors) return CEED_ERROR_SUCCESS; 821 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) { 822 if (ceed->work_vectors->is_in_use[i]) continue; 823 CeedSize vec_len; 824 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &vec_len)); 825 if (vec_len < min_len) { 826 ceed->ref_count += 2; // Note: increase ref_count to prevent Ceed destructor from triggering 827 CeedCall(CeedVectorDestroy(&ceed->work_vectors->vecs[i])); 828 ceed->ref_count -= 1; // Note: restore ref_count 829 ceed->work_vectors->num_vecs--; 830 if (ceed->work_vectors->num_vecs > 0) { 831 ceed->work_vectors->vecs[i] = ceed->work_vectors->vecs[ceed->work_vectors->num_vecs]; 832 ceed->work_vectors->is_in_use[i] = ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs]; 833 ceed->work_vectors->is_in_use[ceed->work_vectors->num_vecs] = false; 834 i--; 835 } 836 } 837 } 838 return CEED_ERROR_SUCCESS; 839 } 840 841 /** 842 @brief Get a `CeedVector` for scratch work from a `Ceed` context. 843 844 Note: This vector must be restored with @ref CeedRestoreWorkVector(). 845 846 @param[in] ceed `Ceed` context 847 @param[in] len Minimum length of work vector 848 @param[out] vec Address of the variable where `CeedVector` will be stored 849 850 @return An error code: 0 - success, otherwise - failure 851 852 @ref Backend 853 **/ 854 int CeedGetWorkVector(Ceed ceed, CeedSize len, CeedVector *vec) { 855 CeedInt i = 0; 856 CeedScalar usage_mb; 857 858 if (!ceed->VectorCreate) { 859 Ceed delegate; 860 861 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector")); 862 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate"); 863 CeedCall(CeedGetWorkVector(delegate, len, vec)); 864 CeedCall(CeedDestroy(&delegate)); 865 return CEED_ERROR_SUCCESS; 866 } 867 868 if (!ceed->work_vectors) CeedCall(CeedWorkVectorsCreate(ceed)); 869 870 // Search for big enough work vector 871 for (i = 0; i < ceed->work_vectors->num_vecs; i++) { 872 if (!ceed->work_vectors->is_in_use[i]) { 873 CeedSize work_len; 874 875 CeedCall(CeedVectorGetLength(ceed->work_vectors->vecs[i], &work_len)); 876 if (work_len >= len) break; 877 } 878 } 879 // Long enough vector was not found 880 if (i == ceed->work_vectors->num_vecs) { 881 if (ceed->work_vectors->max_vecs == 0) { 882 ceed->work_vectors->max_vecs = 1; 883 CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs)); 884 CeedCall(CeedCalloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use)); 885 } else if (ceed->work_vectors->max_vecs == i) { 886 ceed->work_vectors->max_vecs *= 2; 887 CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->vecs)); 888 CeedCall(CeedRealloc(ceed->work_vectors->max_vecs, &ceed->work_vectors->is_in_use)); 889 } 890 ceed->work_vectors->num_vecs++; 891 CeedCallBackend(CeedVectorCreate(ceed, len, &ceed->work_vectors->vecs[i])); 892 ceed->ref_count--; // Note: ref_count manipulation to prevent a ref-loop 893 if (ceed->is_debug) CeedGetWorkVectorMemoryUsage(ceed, &usage_mb); 894 } 895 // Return pointer to work vector 896 ceed->work_vectors->is_in_use[i] = true; 897 *vec = NULL; 898 CeedCall(CeedVectorReferenceCopy(ceed->work_vectors->vecs[i], vec)); 899 ceed->ref_count++; // Note: bump ref_count to account for external access 900 return CEED_ERROR_SUCCESS; 901 } 902 903 /** 904 @brief Restore a `CeedVector` for scratch work from a `Ceed` context from @ref CeedGetWorkVector() 905 906 @param[in] ceed `Ceed` context 907 @param[out] vec `CeedVector` to restore 908 909 @return An error code: 0 - success, otherwise - failure 910 911 @ref Backend 912 **/ 913 int CeedRestoreWorkVector(Ceed ceed, CeedVector *vec) { 914 if (!ceed->VectorCreate) { 915 Ceed delegate; 916 917 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector")); 918 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate"); 919 CeedCall(CeedRestoreWorkVector(delegate, vec)); 920 CeedCall(CeedDestroy(&delegate)); 921 return CEED_ERROR_SUCCESS; 922 } 923 924 for (CeedInt i = 0; i < ceed->work_vectors->num_vecs; i++) { 925 if (*vec == ceed->work_vectors->vecs[i]) { 926 CeedCheck(ceed->work_vectors->is_in_use[i], ceed, CEED_ERROR_ACCESS, "Work vector %" CeedSize_FMT " was not checked out but is being returned"); 927 CeedCall(CeedVectorDestroy(vec)); 928 ceed->work_vectors->is_in_use[i] = false; 929 ceed->ref_count--; // Note: reduce ref_count again to prevent a ref-loop 930 return CEED_ERROR_SUCCESS; 931 } 932 } 933 // LCOV_EXCL_START 934 return CeedError(ceed, CEED_ERROR_MAJOR, "vec was not checked out via CeedGetWorkVector()"); 935 // LCOV_EXCL_STOP 936 } 937 938 /** 939 @brief Retrieve list of additional JiT source roots from `Ceed` context. 940 941 Note: The caller is responsible for restoring `jit_source_roots` with @ref CeedRestoreJitSourceRoots(). 942 943 @param[in] ceed `Ceed` context 944 @param[out] num_source_roots Number of JiT source directories 945 @param[out] jit_source_roots Absolute paths to additional JiT source directories 946 947 @return An error code: 0 - success, otherwise - failure 948 949 @ref Backend 950 **/ 951 int CeedGetJitSourceRoots(Ceed ceed, CeedInt *num_source_roots, const char ***jit_source_roots) { 952 Ceed ceed_parent; 953 954 CeedCall(CeedGetParent(ceed, &ceed_parent)); 955 *num_source_roots = ceed_parent->num_jit_source_roots; 956 *jit_source_roots = (const char **)ceed_parent->jit_source_roots; 957 ceed_parent->num_jit_source_roots_readers++; 958 CeedCall(CeedDestroy(&ceed_parent)); 959 return CEED_ERROR_SUCCESS; 960 } 961 962 /** 963 @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots() 964 965 @param[in] ceed `Ceed` context 966 @param[out] jit_source_roots Absolute paths to additional JiT source directories 967 968 @return An error code: 0 - success, otherwise - failure 969 970 @ref Backend 971 **/ 972 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) { 973 Ceed ceed_parent; 974 975 CeedCall(CeedGetParent(ceed, &ceed_parent)); 976 *jit_source_roots = NULL; 977 ceed_parent->num_jit_source_roots_readers--; 978 CeedCall(CeedDestroy(&ceed_parent)); 979 return CEED_ERROR_SUCCESS; 980 } 981 982 /** 983 @brief Retrieve list of additional JiT defines from `Ceed` context. 984 985 Note: The caller is responsible for restoring `jit_defines` with @ref CeedRestoreJitDefines(). 986 987 @param[in] ceed `Ceed` context 988 @param[out] num_jit_defines Number of JiT defines 989 @param[out] jit_defines Strings such as `foo=bar`, used as `-Dfoo=bar` in JiT 990 991 @return An error code: 0 - success, otherwise - failure 992 993 @ref Backend 994 **/ 995 int CeedGetJitDefines(Ceed ceed, CeedInt *num_jit_defines, const char ***jit_defines) { 996 Ceed ceed_parent; 997 998 CeedCall(CeedGetParent(ceed, &ceed_parent)); 999 *num_jit_defines = ceed_parent->num_jit_defines; 1000 *jit_defines = (const char **)ceed_parent->jit_defines; 1001 ceed_parent->num_jit_defines_readers++; 1002 CeedCall(CeedDestroy(&ceed_parent)); 1003 return CEED_ERROR_SUCCESS; 1004 } 1005 1006 /** 1007 @brief Restore list of additional JiT defines from with @ref CeedGetJitDefines() 1008 1009 @param[in] ceed `Ceed` context 1010 @param[out] jit_defines String such as `foo=bar`, used as `-Dfoo=bar` in JiT 1011 1012 @return An error code: 0 - success, otherwise - failure 1013 1014 @ref Backend 1015 **/ 1016 int CeedRestoreJitDefines(Ceed ceed, const char ***jit_defines) { 1017 Ceed ceed_parent; 1018 1019 CeedCall(CeedGetParent(ceed, &ceed_parent)); 1020 *jit_defines = NULL; 1021 ceed_parent->num_jit_defines_readers--; 1022 CeedCall(CeedDestroy(&ceed_parent)); 1023 return CEED_ERROR_SUCCESS; 1024 } 1025 1026 /// @} 1027 1028 /// ---------------------------------------------------------------------------- 1029 /// Ceed Public API 1030 /// ---------------------------------------------------------------------------- 1031 /// @addtogroup CeedUser 1032 /// @{ 1033 1034 /** 1035 @brief Get the list of available resource names for `Ceed` contexts 1036 1037 Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array. 1038 1039 @param[out] n Number of available resources 1040 @param[out] resources List of available resource names 1041 @param[out] priorities Resource name prioritization values, lower is better 1042 1043 @return An error code: 0 - success, otherwise - failure 1044 1045 @ref User 1046 **/ 1047 // LCOV_EXCL_START 1048 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) { 1049 *n = 0; 1050 *resources = malloc(num_backends * sizeof(**resources)); 1051 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure"); 1052 if (priorities) { 1053 *priorities = malloc(num_backends * sizeof(**priorities)); 1054 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure"); 1055 } 1056 for (size_t i = 0; i < num_backends; i++) { 1057 // Only report compiled backends 1058 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) { 1059 *resources[i] = backends[i].prefix; 1060 if (priorities) *priorities[i] = backends[i].priority; 1061 *n += 1; 1062 } 1063 } 1064 CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed"); 1065 *resources = realloc(*resources, *n * sizeof(**resources)); 1066 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure"); 1067 if (priorities) { 1068 *priorities = realloc(*priorities, *n * sizeof(**priorities)); 1069 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure"); 1070 } 1071 return CEED_ERROR_SUCCESS; 1072 } 1073 // LCOV_EXCL_STOP 1074 1075 /** 1076 @brief Initialize a `Ceed` context to use the specified resource. 1077 1078 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`. 1079 1080 @param[in] resource Resource to use, e.g., "/cpu/self" 1081 @param[out] ceed The library context 1082 1083 @return An error code: 0 - success, otherwise - failure 1084 1085 @ref User 1086 1087 @sa CeedRegister() CeedDestroy() 1088 **/ 1089 int CeedInit(const char *resource, Ceed *ceed) { 1090 size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority; 1091 1092 // Find matching backend 1093 CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided"); 1094 CeedCall(CeedRegisterAll()); 1095 1096 // Check for help request 1097 const char *help_prefix = "help"; 1098 size_t match_help = 0; 1099 while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++; 1100 if (match_help == 4) { 1101 fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH, 1102 CEED_VERSION_RELEASE ? "" : "+development"); 1103 fprintf(stderr, "Available backend resources:\n"); 1104 for (size_t i = 0; i < num_backends; i++) { 1105 // Only report compiled backends 1106 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, " %s\n", backends[i].prefix); 1107 } 1108 fflush(stderr); 1109 match_help = 5; // Delineating character expected 1110 } else { 1111 match_help = 0; 1112 } 1113 1114 // Find best match, computed as number of matching characters from requested resource stem 1115 size_t stem_length = 0; 1116 while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++; 1117 for (size_t i = 0; i < num_backends; i++) { 1118 size_t n = 0; 1119 const char *prefix = backends[i].prefix; 1120 while (prefix[n] && prefix[n] == resource[n + match_help]) n++; 1121 priority = backends[i].priority; 1122 if (n > match_len || (n == match_len && match_priority > priority)) { 1123 match_len = n; 1124 match_priority = priority; 1125 match_index = i; 1126 } 1127 } 1128 // Using Levenshtein distance to find closest match 1129 if (match_len <= 1 || match_len != stem_length) { 1130 // LCOV_EXCL_START 1131 size_t lev_dis = UINT_MAX; 1132 size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY; 1133 for (size_t i = 0; i < num_backends; i++) { 1134 const char *prefix = backends[i].prefix; 1135 size_t prefix_length = strlen(backends[i].prefix); 1136 size_t min_len = (prefix_length < stem_length) ? prefix_length : stem_length; 1137 size_t column[min_len + 1]; 1138 for (size_t j = 0; j <= min_len; j++) column[j] = j; 1139 for (size_t j = 1; j <= min_len; j++) { 1140 column[0] = j; 1141 for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) { 1142 size_t old_diag = column[k]; 1143 size_t min_1 = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1; 1144 size_t min_2 = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1); 1145 column[k] = (min_1 < min_2) ? min_1 : min_2; 1146 last_diag = old_diag; 1147 } 1148 } 1149 size_t n = column[min_len]; 1150 priority = backends[i].priority; 1151 if (n < lev_dis || (n == lev_dis && lev_priority > priority)) { 1152 lev_dis = n; 1153 lev_priority = priority; 1154 lev_index = i; 1155 } 1156 } 1157 const char *prefix_lev = backends[lev_index].prefix; 1158 size_t lev_length = 0; 1159 while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++; 1160 size_t m = (lev_length < stem_length) ? lev_length : stem_length; 1161 if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource); 1162 else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix); 1163 // LCOV_EXCL_STOP 1164 } 1165 1166 // Setup Ceed 1167 CeedCall(CeedCalloc(1, ceed)); 1168 CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots)); 1169 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 1170 if (!ceed_error_handler) ceed_error_handler = "abort"; 1171 if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit; 1172 else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore; 1173 else (*ceed)->Error = CeedErrorAbort; 1174 memcpy((*ceed)->err_msg, "No error message stored", 24); 1175 (*ceed)->ref_count = 1; 1176 (*ceed)->data = NULL; 1177 1178 // Set lookup table 1179 FOffset f_offsets[] = { 1180 CEED_FTABLE_ENTRY(Ceed, Error), 1181 CEED_FTABLE_ENTRY(Ceed, SetStream), 1182 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 1183 CEED_FTABLE_ENTRY(Ceed, Destroy), 1184 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 1185 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 1186 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints), 1187 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 1188 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 1189 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 1190 CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv), 1191 CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl), 1192 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 1193 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 1194 CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate), 1195 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 1196 CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints), 1197 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 1198 CEED_FTABLE_ENTRY(CeedVector, HasValidArray), 1199 CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType), 1200 CEED_FTABLE_ENTRY(CeedVector, CopyStrided), 1201 CEED_FTABLE_ENTRY(CeedVector, SetArray), 1202 CEED_FTABLE_ENTRY(CeedVector, TakeArray), 1203 CEED_FTABLE_ENTRY(CeedVector, SetValue), 1204 CEED_FTABLE_ENTRY(CeedVector, SetValueStrided), 1205 CEED_FTABLE_ENTRY(CeedVector, SyncArray), 1206 CEED_FTABLE_ENTRY(CeedVector, GetArray), 1207 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 1208 CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite), 1209 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 1210 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 1211 CEED_FTABLE_ENTRY(CeedVector, Norm), 1212 CEED_FTABLE_ENTRY(CeedVector, Scale), 1213 CEED_FTABLE_ENTRY(CeedVector, AXPY), 1214 CEED_FTABLE_ENTRY(CeedVector, AXPBY), 1215 CEED_FTABLE_ENTRY(CeedVector, PointwiseMult), 1216 CEED_FTABLE_ENTRY(CeedVector, Reciprocal), 1217 CEED_FTABLE_ENTRY(CeedVector, Destroy), 1218 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 1219 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned), 1220 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented), 1221 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement), 1222 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 1223 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets), 1224 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations), 1225 CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations), 1226 CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset), 1227 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 1228 CEED_FTABLE_ENTRY(CeedBasis, Apply), 1229 CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd), 1230 CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints), 1231 CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints), 1232 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 1233 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 1234 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 1235 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 1236 CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction), 1237 CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction), 1238 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 1239 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData), 1240 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType), 1241 CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData), 1242 CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData), 1243 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData), 1244 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead), 1245 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData), 1246 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead), 1247 CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy), 1248 CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy), 1249 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction), 1250 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate), 1251 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal), 1252 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal), 1253 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal), 1254 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal), 1255 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic), 1256 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble), 1257 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle), 1258 CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse), 1259 CEED_FTABLE_ENTRY(CeedOperator, Apply), 1260 CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite), 1261 CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd), 1262 CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite), 1263 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 1264 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 1265 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 1266 }; 1267 1268 CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets)); 1269 memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets)); 1270 1271 // Record env variables CEED_DEBUG or DBG 1272 (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG"); 1273 1274 // Copy resource prefix, if backend setup successful 1275 CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource)); 1276 1277 // Set default JiT source root 1278 // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent 1279 CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault)); 1280 1281 // Backend specific setup 1282 CeedCall(backends[match_index].init(&resource[match_help], *ceed)); 1283 return CEED_ERROR_SUCCESS; 1284 } 1285 1286 /** 1287 @brief Set the GPU stream for a `Ceed` context 1288 1289 @param[in,out] ceed `Ceed` context to set the stream 1290 @param[in] handle Handle to GPU stream 1291 1292 @return An error code: 0 - success, otherwise - failure 1293 1294 @ref User 1295 **/ 1296 int CeedSetStream(Ceed ceed, void *handle) { 1297 CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL"); 1298 if (ceed->SetStream) { 1299 CeedCall(ceed->SetStream(ceed, handle)); 1300 } else { 1301 Ceed delegate; 1302 CeedCall(CeedGetDelegate(ceed, &delegate)); 1303 1304 if (delegate) CeedCall(CeedSetStream(delegate, handle)); 1305 else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream"); 1306 CeedCall(CeedDestroy(&delegate)); 1307 } 1308 return CEED_ERROR_SUCCESS; 1309 } 1310 1311 /** 1312 @brief Copy the pointer to a `Ceed` context. 1313 1314 Both pointers should be destroyed with @ref CeedDestroy(). 1315 1316 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. 1317 This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context. 1318 1319 @param[in] ceed `Ceed` context to copy reference to 1320 @param[in,out] ceed_copy Variable to store copied reference 1321 1322 @return An error code: 0 - success, otherwise - failure 1323 1324 @ref User 1325 **/ 1326 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) { 1327 CeedCall(CeedReference(ceed)); 1328 CeedCall(CeedDestroy(ceed_copy)); 1329 *ceed_copy = ceed; 1330 return CEED_ERROR_SUCCESS; 1331 } 1332 1333 /** 1334 @brief Get the full resource name for a `Ceed` context 1335 1336 @param[in] ceed `Ceed` context to get resource name of 1337 @param[out] resource Variable to store resource name 1338 1339 @return An error code: 0 - success, otherwise - failure 1340 1341 @ref User 1342 **/ 1343 int CeedGetResource(Ceed ceed, const char **resource) { 1344 *resource = (const char *)ceed->resource; 1345 return CEED_ERROR_SUCCESS; 1346 } 1347 1348 /** 1349 @brief Return `Ceed` context preferred memory type 1350 1351 @param[in] ceed `Ceed` context to get preferred memory type of 1352 @param[out] mem_type Address to save preferred memory type to 1353 1354 @return An error code: 0 - success, otherwise - failure 1355 1356 @ref User 1357 **/ 1358 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) { 1359 if (ceed->GetPreferredMemType) { 1360 CeedCall(ceed->GetPreferredMemType(mem_type)); 1361 } else { 1362 Ceed delegate; 1363 CeedCall(CeedGetDelegate(ceed, &delegate)); 1364 1365 if (delegate) { 1366 CeedCall(CeedGetPreferredMemType(delegate, mem_type)); 1367 } else { 1368 *mem_type = CEED_MEM_HOST; 1369 } 1370 CeedCall(CeedDestroy(&delegate)); 1371 } 1372 return CEED_ERROR_SUCCESS; 1373 } 1374 1375 /** 1376 @brief Get deterministic status of `Ceed` context 1377 1378 @param[in] ceed `Ceed` context 1379 @param[out] is_deterministic Variable to store deterministic status 1380 1381 @return An error code: 0 - success, otherwise - failure 1382 1383 @ref User 1384 **/ 1385 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) { 1386 *is_deterministic = ceed->is_deterministic; 1387 return CEED_ERROR_SUCCESS; 1388 } 1389 1390 /** 1391 @brief Set additional JiT source root for `Ceed` context 1392 1393 @param[in,out] ceed `Ceed` context 1394 @param[in] jit_source_root Absolute path to additional JiT source directory 1395 1396 @return An error code: 0 - success, otherwise - failure 1397 1398 @ref User 1399 **/ 1400 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) { 1401 Ceed ceed_parent; 1402 1403 CeedCall(CeedGetParent(ceed, &ceed_parent)); 1404 CeedCheck(!ceed_parent->num_jit_source_roots_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access has not been restored"); 1405 1406 CeedInt index = ceed_parent->num_jit_source_roots; 1407 size_t path_length = strlen(jit_source_root); 1408 1409 if (ceed_parent->num_jit_source_roots == ceed_parent->max_jit_source_roots) { 1410 if (ceed_parent->max_jit_source_roots == 0) ceed_parent->max_jit_source_roots = 1; 1411 ceed_parent->max_jit_source_roots *= 2; 1412 CeedCall(CeedRealloc(ceed_parent->max_jit_source_roots, &ceed_parent->jit_source_roots)); 1413 } 1414 CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index])); 1415 memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length); 1416 ceed_parent->num_jit_source_roots++; 1417 CeedCall(CeedDestroy(&ceed_parent)); 1418 return CEED_ERROR_SUCCESS; 1419 } 1420 1421 /** 1422 @brief Set additional JiT compiler define for `Ceed` context 1423 1424 @param[in,out] ceed `Ceed` context 1425 @param[in] jit_define String such as `foo=bar`, used as `-Dfoo=bar` in JiT 1426 1427 @return An error code: 0 - success, otherwise - failure 1428 1429 @ref User 1430 **/ 1431 int CeedAddJitDefine(Ceed ceed, const char *jit_define) { 1432 Ceed ceed_parent; 1433 1434 CeedCall(CeedGetParent(ceed, &ceed_parent)); 1435 CeedCheck(!ceed_parent->num_jit_defines_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT define, read access has not been restored"); 1436 1437 CeedInt index = ceed_parent->num_jit_defines; 1438 size_t define_length = strlen(jit_define); 1439 1440 if (ceed_parent->num_jit_defines == ceed_parent->max_jit_defines) { 1441 if (ceed_parent->max_jit_defines == 0) ceed_parent->max_jit_defines = 1; 1442 ceed_parent->max_jit_defines *= 2; 1443 CeedCall(CeedRealloc(ceed_parent->max_jit_defines, &ceed_parent->jit_defines)); 1444 } 1445 CeedCall(CeedCalloc(define_length + 1, &ceed_parent->jit_defines[index])); 1446 memcpy(ceed_parent->jit_defines[index], jit_define, define_length); 1447 ceed_parent->num_jit_defines++; 1448 CeedCall(CeedDestroy(&ceed_parent)); 1449 return CEED_ERROR_SUCCESS; 1450 } 1451 1452 /** 1453 @brief View a `Ceed` 1454 1455 @param[in] ceed `Ceed` to view 1456 @param[in] stream Filestream to write to 1457 1458 @return An error code: 0 - success, otherwise - failure 1459 1460 @ref User 1461 **/ 1462 int CeedView(Ceed ceed, FILE *stream) { 1463 CeedMemType mem_type; 1464 1465 CeedCall(CeedGetPreferredMemType(ceed, &mem_type)); 1466 1467 fprintf(stream, 1468 "Ceed\n" 1469 " Ceed Resource: %s\n" 1470 " Preferred MemType: %s\n", 1471 ceed->resource, CeedMemTypes[mem_type]); 1472 return CEED_ERROR_SUCCESS; 1473 } 1474 1475 /** 1476 @brief Destroy a `Ceed` 1477 1478 @param[in,out] ceed Address of `Ceed` context to destroy 1479 1480 @return An error code: 0 - success, otherwise - failure 1481 1482 @ref User 1483 **/ 1484 int CeedDestroy(Ceed *ceed) { 1485 if (!*ceed || --(*ceed)->ref_count > 0) { 1486 *ceed = NULL; 1487 return CEED_ERROR_SUCCESS; 1488 } 1489 1490 CeedCheck(!(*ceed)->num_jit_source_roots_readers, *ceed, CEED_ERROR_ACCESS, 1491 "Cannot destroy ceed context, read access for JiT source roots has been granted"); 1492 CeedCheck(!(*ceed)->num_jit_defines_readers, *ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access for JiT defines has been granted"); 1493 1494 if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate)); 1495 1496 if ((*ceed)->obj_delegate_count > 0) { 1497 for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) { 1498 CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate))); 1499 CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name)); 1500 } 1501 CeedCall(CeedFree(&(*ceed)->obj_delegates)); 1502 } 1503 1504 if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed)); 1505 1506 for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) { 1507 CeedCall(CeedFree(&(*ceed)->jit_source_roots[i])); 1508 } 1509 CeedCall(CeedFree(&(*ceed)->jit_source_roots)); 1510 1511 for (CeedInt i = 0; i < (*ceed)->num_jit_defines; i++) { 1512 CeedCall(CeedFree(&(*ceed)->jit_defines[i])); 1513 } 1514 CeedCall(CeedFree(&(*ceed)->jit_defines)); 1515 1516 CeedCall(CeedFree(&(*ceed)->f_offsets)); 1517 CeedCall(CeedFree(&(*ceed)->resource)); 1518 CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed)); 1519 CeedCall(CeedWorkVectorsDestroy(*ceed)); 1520 CeedCall(CeedFree(ceed)); 1521 return CEED_ERROR_SUCCESS; 1522 } 1523 1524 // LCOV_EXCL_START 1525 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) { 1526 if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args); 1527 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1528 vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args); // NOLINT 1529 return ceed->err_msg; 1530 } 1531 // LCOV_EXCL_STOP 1532 1533 /** 1534 @brief Error handling implementation; use @ref CeedError() instead. 1535 1536 @return An error code: 0 - success, otherwise - failure 1537 1538 @ref Developer 1539 **/ 1540 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) { 1541 va_list args; 1542 int ret_val; 1543 1544 va_start(args, format); 1545 if (ceed) { 1546 ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args); 1547 } else { 1548 // LCOV_EXCL_START 1549 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 1550 if (!ceed_error_handler) ceed_error_handler = "abort"; 1551 if (!strcmp(ceed_error_handler, "return")) { 1552 ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args); 1553 } else { 1554 // This function will not return 1555 ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args); 1556 } 1557 } 1558 va_end(args); 1559 return ret_val; 1560 // LCOV_EXCL_STOP 1561 } 1562 1563 /** 1564 @brief Error handler that returns without printing anything. 1565 1566 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1567 1568 @return An error code: 0 - success, otherwise - failure 1569 1570 @ref Developer 1571 **/ 1572 // LCOV_EXCL_START 1573 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1574 return err_code; 1575 } 1576 // LCOV_EXCL_STOP 1577 1578 /** 1579 @brief Error handler that stores the error message for future use and returns the error. 1580 1581 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1582 1583 @return An error code: 0 - success, otherwise - failure 1584 1585 @ref Developer 1586 **/ 1587 // LCOV_EXCL_START 1588 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1589 if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args); 1590 1591 // Build message 1592 int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func); 1593 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1594 vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT 1595 return err_code; 1596 } 1597 // LCOV_EXCL_STOP 1598 1599 /** 1600 @brief Error handler that prints to `stderr` and aborts 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 CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1610 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1611 vfprintf(stderr, format, *args); 1612 fprintf(stderr, "\n"); 1613 abort(); 1614 return err_code; 1615 } 1616 // LCOV_EXCL_STOP 1617 1618 /** 1619 @brief Error handler that prints to `stderr` and exits. 1620 1621 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1622 1623 In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run. 1624 1625 @return An error code: 0 - success, otherwise - failure 1626 1627 @ref Developer 1628 **/ 1629 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1630 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1631 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1632 vfprintf(stderr, format, *args); // NOLINT 1633 fprintf(stderr, "\n"); 1634 exit(err_code); 1635 return err_code; 1636 } 1637 1638 /** 1639 @brief Set error handler 1640 1641 A default error handler is set in @ref CeedInit(). 1642 Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler. 1643 1644 @return An error code: 0 - success, otherwise - failure 1645 1646 @ref Developer 1647 **/ 1648 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) { 1649 ceed->Error = handler; 1650 if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler); 1651 for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler); 1652 return CEED_ERROR_SUCCESS; 1653 } 1654 1655 /** 1656 @brief Get error message 1657 1658 The error message is only stored when using the error handler @ref CeedErrorStore() 1659 1660 @param[in] ceed `Ceed` context to retrieve error message 1661 @param[out] err_msg Char pointer to hold error message 1662 1663 @return An error code: 0 - success, otherwise - failure 1664 1665 @ref Developer 1666 **/ 1667 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) { 1668 if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg); 1669 *err_msg = ceed->err_msg; 1670 return CEED_ERROR_SUCCESS; 1671 } 1672 1673 /** 1674 @brief Restore error message. 1675 1676 The error message is only stored when using the error handler @ref CeedErrorStore(). 1677 1678 @param[in] ceed `Ceed` context to restore error message 1679 @param[out] err_msg Char pointer that holds error message 1680 1681 @return An error code: 0 - success, otherwise - failure 1682 1683 @ref Developer 1684 **/ 1685 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) { 1686 if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg); 1687 *err_msg = NULL; 1688 memcpy(ceed->err_msg, "No error message stored", 24); 1689 return CEED_ERROR_SUCCESS; 1690 } 1691 1692 /** 1693 @brief Get libCEED library version information. 1694 1695 libCEED version numbers have the form major.minor.patch. 1696 Non-release versions may contain unstable interfaces. 1697 1698 @param[out] major Major version of the library 1699 @param[out] minor Minor version of the library 1700 @param[out] patch Patch (subminor) version of the library 1701 @param[out] release True for releases; false for development branches 1702 1703 The caller may pass `NULL` for any arguments that are not needed. 1704 1705 @return An error code: 0 - success, otherwise - failure 1706 1707 @ref Developer 1708 1709 @sa CEED_VERSION_GE() CeedGetGitVersion() CeedGetBuildConfiguration() 1710 */ 1711 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) { 1712 if (major) *major = CEED_VERSION_MAJOR; 1713 if (minor) *minor = CEED_VERSION_MINOR; 1714 if (patch) *patch = CEED_VERSION_PATCH; 1715 if (release) *release = CEED_VERSION_RELEASE; 1716 return CEED_ERROR_SUCCESS; 1717 } 1718 1719 /** 1720 @brief Get libCEED scalar type, such as F64 or F32 1721 1722 @param[out] scalar_type Type of libCEED scalars 1723 1724 @return An error code: 0 - success, otherwise - failure 1725 1726 @ref Developer 1727 */ 1728 int CeedGetScalarType(CeedScalarType *scalar_type) { 1729 *scalar_type = CEED_SCALAR_TYPE; 1730 return CEED_ERROR_SUCCESS; 1731 } 1732 1733 /// @} 1734