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