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