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