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