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