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