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_roots; 663 CeedInt num_jit_source_roots = 0; 664 665 CeedCall(CeedGetJitSourceRoots(ceed, &num_jit_source_roots, &jit_source_roots)); 666 for (CeedInt i = 0; i < num_jit_source_roots; i++) { 667 CeedCall(CeedAddJitSourceRoot(fallback_ceed, jit_source_roots[i])); 668 } 669 CeedCall(CeedRestoreJitSourceRoots(ceed, &jit_source_roots)); 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 ceed_parent->num_jit_source_roots_readers++; 906 return CEED_ERROR_SUCCESS; 907 } 908 909 /** 910 @brief Restore list of additional JiT source roots from with @ref CeedGetJitSourceRoots() 911 912 @param[in] ceed `Ceed` context 913 @param[out] jit_source_roots Absolute paths to additional JiT source directories 914 915 @return An error code: 0 - success, otherwise - failure 916 917 @ref Backend 918 **/ 919 int CeedRestoreJitSourceRoots(Ceed ceed, const char ***jit_source_roots) { 920 Ceed ceed_parent; 921 922 CeedCall(CeedGetParent(ceed, &ceed_parent)); 923 *jit_source_roots = NULL; 924 ceed_parent->num_jit_source_roots_readers--; 925 return CEED_ERROR_SUCCESS; 926 } 927 928 /** 929 @brief Retrieve list of additional JiT defines from `Ceed` context. 930 931 Note: The caller is responsible for restoring `jit_defines` with @ref CeedRestoreJitDefines(). 932 933 @param[in] ceed `Ceed` context 934 @param[out] num_jit_defines Number of JiT defines 935 @param[out] jit_defines Strings such as `foo=bar`, used as `-Dfoo=bar` in JiT 936 937 @return An error code: 0 - success, otherwise - failure 938 939 @ref Backend 940 **/ 941 int CeedGetJitDefines(Ceed ceed, CeedInt *num_defines, const char ***jit_defines) { 942 Ceed ceed_parent; 943 944 CeedCall(CeedGetParent(ceed, &ceed_parent)); 945 *num_defines = ceed_parent->num_jit_defines; 946 *jit_defines = (const char **)ceed_parent->jit_defines; 947 ceed_parent->num_jit_defines_readers++; 948 return CEED_ERROR_SUCCESS; 949 } 950 951 /** 952 @brief Restore list of additional JiT defines from with @ref CeedGetJitDefines() 953 954 @param[in] ceed `Ceed` context 955 @param[out] jit_defines String such as `foo=bar`, used as `-Dfoo=bar` in JiT 956 957 @return An error code: 0 - success, otherwise - failure 958 959 @ref Backend 960 **/ 961 int CeedRestoreJitDefines(Ceed ceed, const char ***jit_defines) { 962 Ceed ceed_parent; 963 964 CeedCall(CeedGetParent(ceed, &ceed_parent)); 965 *jit_defines = NULL; 966 ceed_parent->num_jit_defines_readers--; 967 return CEED_ERROR_SUCCESS; 968 } 969 970 /// @} 971 972 /// ---------------------------------------------------------------------------- 973 /// Ceed Public API 974 /// ---------------------------------------------------------------------------- 975 /// @addtogroup CeedUser 976 /// @{ 977 978 /** 979 @brief Get the list of available resource names for `Ceed` contexts 980 981 Note: The caller is responsible for `free()`ing the resources and priorities arrays, but should not `free()` the contents of the resources array. 982 983 @param[out] n Number of available resources 984 @param[out] resources List of available resource names 985 @param[out] priorities Resource name prioritization values, lower is better 986 987 @return An error code: 0 - success, otherwise - failure 988 989 @ref User 990 **/ 991 // LCOV_EXCL_START 992 int CeedRegistryGetList(size_t *n, char ***const resources, CeedInt **priorities) { 993 *n = 0; 994 *resources = malloc(num_backends * sizeof(**resources)); 995 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "malloc() failure"); 996 if (priorities) { 997 *priorities = malloc(num_backends * sizeof(**priorities)); 998 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "malloc() failure"); 999 } 1000 for (size_t i = 0; i < num_backends; i++) { 1001 // Only report compiled backends 1002 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) { 1003 *resources[i] = backends[i].prefix; 1004 if (priorities) *priorities[i] = backends[i].priority; 1005 *n += 1; 1006 } 1007 } 1008 CeedCheck(*n, NULL, CEED_ERROR_MAJOR, "No backends installed"); 1009 *resources = realloc(*resources, *n * sizeof(**resources)); 1010 CeedCheck(resources, NULL, CEED_ERROR_MAJOR, "realloc() failure"); 1011 if (priorities) { 1012 *priorities = realloc(*priorities, *n * sizeof(**priorities)); 1013 CeedCheck(priorities, NULL, CEED_ERROR_MAJOR, "realloc() failure"); 1014 } 1015 return CEED_ERROR_SUCCESS; 1016 } 1017 // LCOV_EXCL_STOP 1018 1019 /** 1020 @brief Initialize a `Ceed` context to use the specified resource. 1021 1022 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`. 1023 1024 @param[in] resource Resource to use, e.g., "/cpu/self" 1025 @param[out] ceed The library context 1026 1027 @return An error code: 0 - success, otherwise - failure 1028 1029 @ref User 1030 1031 @sa CeedRegister() CeedDestroy() 1032 **/ 1033 int CeedInit(const char *resource, Ceed *ceed) { 1034 size_t match_len = 0, match_index = UINT_MAX, match_priority = CEED_MAX_BACKEND_PRIORITY, priority; 1035 1036 // Find matching backend 1037 CeedCheck(resource, NULL, CEED_ERROR_MAJOR, "No resource provided"); 1038 CeedCall(CeedRegisterAll()); 1039 1040 // Check for help request 1041 const char *help_prefix = "help"; 1042 size_t match_help = 0; 1043 while (match_help < 4 && resource[match_help] == help_prefix[match_help]) match_help++; 1044 if (match_help == 4) { 1045 fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, CEED_VERSION_MINOR, CEED_VERSION_PATCH, 1046 CEED_VERSION_RELEASE ? "" : "+development"); 1047 fprintf(stderr, "Available backend resources:\n"); 1048 for (size_t i = 0; i < num_backends; i++) { 1049 // Only report compiled backends 1050 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) fprintf(stderr, " %s\n", backends[i].prefix); 1051 } 1052 fflush(stderr); 1053 match_help = 5; // Delineating character expected 1054 } else { 1055 match_help = 0; 1056 } 1057 1058 // Find best match, computed as number of matching characters from requested resource stem 1059 size_t stem_length = 0; 1060 while (resource[stem_length + match_help] && resource[stem_length + match_help] != ':') stem_length++; 1061 for (size_t i = 0; i < num_backends; i++) { 1062 size_t n = 0; 1063 const char *prefix = backends[i].prefix; 1064 while (prefix[n] && prefix[n] == resource[n + match_help]) n++; 1065 priority = backends[i].priority; 1066 if (n > match_len || (n == match_len && match_priority > priority)) { 1067 match_len = n; 1068 match_priority = priority; 1069 match_index = i; 1070 } 1071 } 1072 // Using Levenshtein distance to find closest match 1073 if (match_len <= 1 || match_len != stem_length) { 1074 // LCOV_EXCL_START 1075 size_t lev_dis = UINT_MAX; 1076 size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY; 1077 for (size_t i = 0; i < num_backends; i++) { 1078 const char *prefix = backends[i].prefix; 1079 size_t prefix_length = strlen(backends[i].prefix); 1080 size_t min_len = (prefix_length < stem_length) ? prefix_length : stem_length; 1081 size_t column[min_len + 1]; 1082 for (size_t j = 0; j <= min_len; j++) column[j] = j; 1083 for (size_t j = 1; j <= min_len; j++) { 1084 column[0] = j; 1085 for (size_t k = 1, last_diag = j - 1; k <= min_len; k++) { 1086 size_t old_diag = column[k]; 1087 size_t min_1 = (column[k] < column[k - 1]) ? column[k] + 1 : column[k - 1] + 1; 1088 size_t min_2 = last_diag + (resource[k - 1] == prefix[j - 1] ? 0 : 1); 1089 column[k] = (min_1 < min_2) ? min_1 : min_2; 1090 last_diag = old_diag; 1091 } 1092 } 1093 size_t n = column[min_len]; 1094 priority = backends[i].priority; 1095 if (n < lev_dis || (n == lev_dis && lev_priority > priority)) { 1096 lev_dis = n; 1097 lev_priority = priority; 1098 lev_index = i; 1099 } 1100 } 1101 const char *prefix_lev = backends[lev_index].prefix; 1102 size_t lev_length = 0; 1103 while (prefix_lev[lev_length] && prefix_lev[lev_length] != '\0') lev_length++; 1104 size_t m = (lev_length < stem_length) ? lev_length : stem_length; 1105 if (lev_dis + 1 >= m) return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", resource); 1106 else return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\nClosest match: %s", resource, backends[lev_index].prefix); 1107 // LCOV_EXCL_STOP 1108 } 1109 1110 // Setup Ceed 1111 CeedCall(CeedCalloc(1, ceed)); 1112 CeedCall(CeedCalloc(1, &(*ceed)->jit_source_roots)); 1113 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 1114 if (!ceed_error_handler) ceed_error_handler = "abort"; 1115 if (!strcmp(ceed_error_handler, "exit")) (*ceed)->Error = CeedErrorExit; 1116 else if (!strcmp(ceed_error_handler, "store")) (*ceed)->Error = CeedErrorStore; 1117 else (*ceed)->Error = CeedErrorAbort; 1118 memcpy((*ceed)->err_msg, "No error message stored", 24); 1119 (*ceed)->ref_count = 1; 1120 (*ceed)->data = NULL; 1121 1122 // Set lookup table 1123 FOffset f_offsets[] = { 1124 CEED_FTABLE_ENTRY(Ceed, Error), 1125 CEED_FTABLE_ENTRY(Ceed, SetStream), 1126 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 1127 CEED_FTABLE_ENTRY(Ceed, Destroy), 1128 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 1129 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 1130 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateAtPoints), 1131 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 1132 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 1133 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 1134 CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv), 1135 CEED_FTABLE_ENTRY(Ceed, BasisCreateHcurl), 1136 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 1137 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 1138 CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate), 1139 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 1140 CEED_FTABLE_ENTRY(Ceed, OperatorCreateAtPoints), 1141 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 1142 CEED_FTABLE_ENTRY(CeedVector, HasValidArray), 1143 CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType), 1144 CEED_FTABLE_ENTRY(CeedVector, CopyStrided), 1145 CEED_FTABLE_ENTRY(CeedVector, SetArray), 1146 CEED_FTABLE_ENTRY(CeedVector, TakeArray), 1147 CEED_FTABLE_ENTRY(CeedVector, SetValue), 1148 CEED_FTABLE_ENTRY(CeedVector, SetValueStrided), 1149 CEED_FTABLE_ENTRY(CeedVector, SyncArray), 1150 CEED_FTABLE_ENTRY(CeedVector, GetArray), 1151 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 1152 CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite), 1153 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 1154 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 1155 CEED_FTABLE_ENTRY(CeedVector, Norm), 1156 CEED_FTABLE_ENTRY(CeedVector, Scale), 1157 CEED_FTABLE_ENTRY(CeedVector, AXPY), 1158 CEED_FTABLE_ENTRY(CeedVector, AXPBY), 1159 CEED_FTABLE_ENTRY(CeedVector, PointwiseMult), 1160 CEED_FTABLE_ENTRY(CeedVector, Reciprocal), 1161 CEED_FTABLE_ENTRY(CeedVector, Destroy), 1162 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 1163 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnsigned), 1164 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyUnoriented), 1165 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyAtPointsInElement), 1166 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 1167 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets), 1168 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOrientations), 1169 CEED_FTABLE_ENTRY(CeedElemRestriction, GetCurlOrientations), 1170 CEED_FTABLE_ENTRY(CeedElemRestriction, GetAtPointsElementOffset), 1171 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 1172 CEED_FTABLE_ENTRY(CeedBasis, Apply), 1173 CEED_FTABLE_ENTRY(CeedBasis, ApplyAdd), 1174 CEED_FTABLE_ENTRY(CeedBasis, ApplyAtPoints), 1175 CEED_FTABLE_ENTRY(CeedBasis, ApplyAddAtPoints), 1176 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 1177 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 1178 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 1179 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 1180 CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction), 1181 CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction), 1182 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 1183 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData), 1184 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType), 1185 CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData), 1186 CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData), 1187 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData), 1188 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead), 1189 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData), 1190 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead), 1191 CEED_FTABLE_ENTRY(CeedQFunctionContext, DataDestroy), 1192 CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy), 1193 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction), 1194 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate), 1195 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal), 1196 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal), 1197 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal), 1198 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal), 1199 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic), 1200 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble), 1201 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSingle), 1202 CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse), 1203 CEED_FTABLE_ENTRY(CeedOperator, Apply), 1204 CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite), 1205 CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd), 1206 CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite), 1207 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 1208 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 1209 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 1210 }; 1211 1212 CeedCall(CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets)); 1213 memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets)); 1214 1215 // Set fallback for advanced CeedOperator functions 1216 const char fallback_resource[] = ""; 1217 CeedCall(CeedSetOperatorFallbackResource(*ceed, fallback_resource)); 1218 1219 // Record env variables CEED_DEBUG or DBG 1220 (*ceed)->is_debug = getenv("CEED_DEBUG") || getenv("DEBUG") || getenv("DBG"); 1221 1222 // Copy resource prefix, if backend setup successful 1223 CeedCall(CeedStringAllocCopy(backends[match_index].prefix, (char **)&(*ceed)->resource)); 1224 1225 // Set default JiT source root 1226 // Note: there will always be the default root for every Ceed but all additional paths are added to the top-most parent 1227 CeedCall(CeedAddJitSourceRoot(*ceed, (char *)CeedJitSourceRootDefault)); 1228 1229 // Backend specific setup 1230 CeedCall(backends[match_index].init(&resource[match_help], *ceed)); 1231 return CEED_ERROR_SUCCESS; 1232 } 1233 1234 /** 1235 @brief Set the GPU stream for a `Ceed` context 1236 1237 @param[in,out] ceed `Ceed` context to set the stream 1238 @param[in] handle Handle to GPU stream 1239 1240 @return An error code: 0 - success, otherwise - failure 1241 1242 @ref User 1243 **/ 1244 int CeedSetStream(Ceed ceed, void *handle) { 1245 CeedCheck(handle, ceed, CEED_ERROR_INCOMPATIBLE, "Stream handle must be non-NULL"); 1246 if (ceed->SetStream) { 1247 CeedCall(ceed->SetStream(ceed, handle)); 1248 } else { 1249 Ceed delegate; 1250 CeedCall(CeedGetDelegate(ceed, &delegate)); 1251 1252 if (delegate) CeedCall(CeedSetStream(delegate, handle)); 1253 else return CeedError(ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support setting stream"); 1254 } 1255 return CEED_ERROR_SUCCESS; 1256 } 1257 1258 /** 1259 @brief Copy the pointer to a `Ceed` context. 1260 1261 Both pointers should be destroyed with @ref CeedDestroy(). 1262 1263 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. 1264 This `Ceed` context will be destroyed if `*ceed_copy` is the only reference to this `Ceed` context. 1265 1266 @param[in] ceed `Ceed` context to copy reference to 1267 @param[in,out] ceed_copy Variable to store copied reference 1268 1269 @return An error code: 0 - success, otherwise - failure 1270 1271 @ref User 1272 **/ 1273 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) { 1274 CeedCall(CeedReference(ceed)); 1275 CeedCall(CeedDestroy(ceed_copy)); 1276 *ceed_copy = ceed; 1277 return CEED_ERROR_SUCCESS; 1278 } 1279 1280 /** 1281 @brief Get the full resource name for a `Ceed` context 1282 1283 @param[in] ceed `Ceed` context to get resource name of 1284 @param[out] resource Variable to store resource name 1285 1286 @return An error code: 0 - success, otherwise - failure 1287 1288 @ref User 1289 **/ 1290 int CeedGetResource(Ceed ceed, const char **resource) { 1291 *resource = (const char *)ceed->resource; 1292 return CEED_ERROR_SUCCESS; 1293 } 1294 1295 /** 1296 @brief Return `Ceed` context preferred memory type 1297 1298 @param[in] ceed `Ceed` context to get preferred memory type of 1299 @param[out] mem_type Address to save preferred memory type to 1300 1301 @return An error code: 0 - success, otherwise - failure 1302 1303 @ref User 1304 **/ 1305 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) { 1306 if (ceed->GetPreferredMemType) { 1307 CeedCall(ceed->GetPreferredMemType(mem_type)); 1308 } else { 1309 Ceed delegate; 1310 CeedCall(CeedGetDelegate(ceed, &delegate)); 1311 1312 if (delegate) { 1313 CeedCall(CeedGetPreferredMemType(delegate, mem_type)); 1314 } else { 1315 *mem_type = CEED_MEM_HOST; 1316 } 1317 } 1318 return CEED_ERROR_SUCCESS; 1319 } 1320 1321 /** 1322 @brief Get deterministic status of `Ceed` context 1323 1324 @param[in] ceed `Ceed` context 1325 @param[out] is_deterministic Variable to store deterministic status 1326 1327 @return An error code: 0 - success, otherwise - failure 1328 1329 @ref User 1330 **/ 1331 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) { 1332 *is_deterministic = ceed->is_deterministic; 1333 return CEED_ERROR_SUCCESS; 1334 } 1335 1336 /** 1337 @brief Set additional JiT source root for `Ceed` context 1338 1339 @param[in,out] ceed `Ceed` context 1340 @param[in] jit_source_root Absolute path to additional JiT source directory 1341 1342 @return An error code: 0 - success, otherwise - failure 1343 1344 @ref User 1345 **/ 1346 int CeedAddJitSourceRoot(Ceed ceed, const char *jit_source_root) { 1347 Ceed ceed_parent; 1348 1349 CeedCall(CeedGetParent(ceed, &ceed_parent)); 1350 CeedCheck(!ceed_parent->num_jit_source_roots_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access has not been restored"); 1351 1352 CeedInt index = ceed_parent->num_jit_source_roots; 1353 size_t path_length = strlen(jit_source_root); 1354 1355 if (ceed_parent->num_jit_source_roots == ceed_parent->max_jit_source_roots) { 1356 if (ceed_parent->max_jit_source_roots == 0) ceed_parent->max_jit_source_roots = 1; 1357 ceed_parent->max_jit_source_roots *= 2; 1358 CeedCall(CeedRealloc(ceed_parent->max_jit_source_roots, &ceed_parent->jit_source_roots)); 1359 } 1360 CeedCall(CeedCalloc(path_length + 1, &ceed_parent->jit_source_roots[index])); 1361 memcpy(ceed_parent->jit_source_roots[index], jit_source_root, path_length); 1362 ceed_parent->num_jit_source_roots++; 1363 return CEED_ERROR_SUCCESS; 1364 } 1365 1366 /** 1367 @brief Set additional JiT compiler define for `Ceed` context 1368 1369 @param[in,out] ceed `Ceed` context 1370 @param[in] jit_define String such as `foo=bar`, used as `-Dfoo=bar` in JiT 1371 1372 @return An error code: 0 - success, otherwise - failure 1373 1374 @ref User 1375 **/ 1376 int CeedAddJitDefine(Ceed ceed, const char *jit_define) { 1377 Ceed ceed_parent; 1378 1379 CeedCall(CeedGetParent(ceed, &ceed_parent)); 1380 CeedCheck(!ceed_parent->num_jit_defines_readers, ceed, CEED_ERROR_ACCESS, "Cannot add JiT define, read access has not been restored"); 1381 1382 CeedInt index = ceed_parent->num_jit_defines; 1383 size_t define_length = strlen(jit_define); 1384 1385 if (ceed_parent->num_jit_defines == ceed_parent->max_jit_defines) { 1386 if (ceed_parent->max_jit_defines == 0) ceed_parent->max_jit_defines = 1; 1387 ceed_parent->max_jit_defines *= 2; 1388 CeedCall(CeedRealloc(ceed_parent->max_jit_defines, &ceed_parent->jit_defines)); 1389 } 1390 CeedCall(CeedCalloc(define_length + 1, &ceed_parent->jit_defines[index])); 1391 memcpy(ceed_parent->jit_defines[index], jit_define, define_length); 1392 ceed_parent->num_jit_defines++; 1393 return CEED_ERROR_SUCCESS; 1394 } 1395 1396 /** 1397 @brief View a `Ceed` 1398 1399 @param[in] ceed `Ceed` to view 1400 @param[in] stream Filestream to write to 1401 1402 @return An error code: 0 - success, otherwise - failure 1403 1404 @ref User 1405 **/ 1406 int CeedView(Ceed ceed, FILE *stream) { 1407 CeedMemType mem_type; 1408 1409 CeedCall(CeedGetPreferredMemType(ceed, &mem_type)); 1410 1411 fprintf(stream, 1412 "Ceed\n" 1413 " Ceed Resource: %s\n" 1414 " Preferred MemType: %s\n", 1415 ceed->resource, CeedMemTypes[mem_type]); 1416 return CEED_ERROR_SUCCESS; 1417 } 1418 1419 /** 1420 @brief Destroy a `Ceed` 1421 1422 @param[in,out] ceed Address of `Ceed` context to destroy 1423 1424 @return An error code: 0 - success, otherwise - failure 1425 1426 @ref User 1427 **/ 1428 int CeedDestroy(Ceed *ceed) { 1429 if (!*ceed || --(*ceed)->ref_count > 0) { 1430 *ceed = NULL; 1431 return CEED_ERROR_SUCCESS; 1432 } 1433 1434 CeedCheck(!(*ceed)->num_jit_source_roots_readers, *ceed, CEED_ERROR_ACCESS, 1435 "Cannot destroy ceed context, read access for JiT source roots has been granted"); 1436 CeedCheck(!(*ceed)->num_jit_defines_readers, *ceed, CEED_ERROR_ACCESS, "Cannot add JiT source root, read access for JiT defines has been granted"); 1437 1438 if ((*ceed)->delegate) CeedCall(CeedDestroy(&(*ceed)->delegate)); 1439 1440 if ((*ceed)->obj_delegate_count > 0) { 1441 for (CeedInt i = 0; i < (*ceed)->obj_delegate_count; i++) { 1442 CeedCall(CeedDestroy(&((*ceed)->obj_delegates[i].delegate))); 1443 CeedCall(CeedFree(&(*ceed)->obj_delegates[i].obj_name)); 1444 } 1445 CeedCall(CeedFree(&(*ceed)->obj_delegates)); 1446 } 1447 1448 if ((*ceed)->Destroy) CeedCall((*ceed)->Destroy(*ceed)); 1449 1450 for (CeedInt i = 0; i < (*ceed)->num_jit_source_roots; i++) { 1451 CeedCall(CeedFree(&(*ceed)->jit_source_roots[i])); 1452 } 1453 CeedCall(CeedFree(&(*ceed)->jit_source_roots)); 1454 1455 for (CeedInt i = 0; i < (*ceed)->num_jit_defines; i++) { 1456 CeedCall(CeedFree(&(*ceed)->jit_defines[i])); 1457 } 1458 CeedCall(CeedFree(&(*ceed)->jit_defines)); 1459 1460 CeedCall(CeedFree(&(*ceed)->f_offsets)); 1461 CeedCall(CeedFree(&(*ceed)->resource)); 1462 CeedCall(CeedDestroy(&(*ceed)->op_fallback_ceed)); 1463 CeedCall(CeedFree(&(*ceed)->op_fallback_resource)); 1464 CeedCall(CeedWorkVectorsDestroy(*ceed)); 1465 CeedCall(CeedFree(ceed)); 1466 return CEED_ERROR_SUCCESS; 1467 } 1468 1469 // LCOV_EXCL_START 1470 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) { 1471 if (ceed->parent) return CeedErrorFormat(ceed->parent, format, args); 1472 if (ceed->op_fallback_parent) return CeedErrorFormat(ceed->op_fallback_parent, format, args); 1473 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1474 vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args); // NOLINT 1475 return ceed->err_msg; 1476 } 1477 // LCOV_EXCL_STOP 1478 1479 /** 1480 @brief Error handling implementation; use @ref CeedError() instead. 1481 1482 @return An error code: 0 - success, otherwise - failure 1483 1484 @ref Developer 1485 **/ 1486 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, int ecode, const char *format, ...) { 1487 va_list args; 1488 int ret_val; 1489 1490 va_start(args, format); 1491 if (ceed) { 1492 ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args); 1493 } else { 1494 // LCOV_EXCL_START 1495 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 1496 if (!ceed_error_handler) ceed_error_handler = "abort"; 1497 if (!strcmp(ceed_error_handler, "return")) { 1498 ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args); 1499 } else { 1500 // This function will not return 1501 ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args); 1502 } 1503 } 1504 va_end(args); 1505 return ret_val; 1506 // LCOV_EXCL_STOP 1507 } 1508 1509 /** 1510 @brief Error handler that returns without printing anything. 1511 1512 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1513 1514 @return An error code: 0 - success, otherwise - failure 1515 1516 @ref Developer 1517 **/ 1518 // LCOV_EXCL_START 1519 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1520 return err_code; 1521 } 1522 // LCOV_EXCL_STOP 1523 1524 /** 1525 @brief Error handler that stores the error message for future use and returns the error. 1526 1527 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1528 1529 @return An error code: 0 - success, otherwise - failure 1530 1531 @ref Developer 1532 **/ 1533 // LCOV_EXCL_START 1534 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1535 if (ceed->parent) return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, args); 1536 if (ceed->op_fallback_parent) return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, err_code, format, args); 1537 1538 // Build message 1539 int len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", filename, line_no, func); 1540 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1541 vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT 1542 return err_code; 1543 } 1544 // LCOV_EXCL_STOP 1545 1546 /** 1547 @brief Error handler that prints to `stderr` and aborts 1548 1549 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1550 1551 @return An error code: 0 - success, otherwise - failure 1552 1553 @ref Developer 1554 **/ 1555 // LCOV_EXCL_START 1556 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1557 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1558 vfprintf(stderr, format, *args); 1559 fprintf(stderr, "\n"); 1560 abort(); 1561 return err_code; 1562 } 1563 // LCOV_EXCL_STOP 1564 1565 /** 1566 @brief Error handler that prints to `stderr` and exits. 1567 1568 Pass this to @ref CeedSetErrorHandler() to obtain this error handling behavior. 1569 1570 In contrast to @ref CeedErrorAbort(), this exits without a signal, so `atexit()` handlers (e.g., as used by gcov) are run. 1571 1572 @return An error code: 0 - success, otherwise - failure 1573 1574 @ref Developer 1575 **/ 1576 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, const char *func, int err_code, const char *format, va_list *args) { 1577 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1578 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1579 vfprintf(stderr, format, *args); // NOLINT 1580 fprintf(stderr, "\n"); 1581 exit(err_code); 1582 return err_code; 1583 } 1584 1585 /** 1586 @brief Set error handler 1587 1588 A default error handler is set in @ref CeedInit(). 1589 Use this function to change the error handler to @ref CeedErrorReturn(), @ref CeedErrorAbort(), or a user-defined error handler. 1590 1591 @return An error code: 0 - success, otherwise - failure 1592 1593 @ref Developer 1594 **/ 1595 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) { 1596 ceed->Error = handler; 1597 if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler); 1598 for (CeedInt i = 0; i < ceed->obj_delegate_count; i++) CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler); 1599 return CEED_ERROR_SUCCESS; 1600 } 1601 1602 /** 1603 @brief Get error message 1604 1605 The error message is only stored when using the error handler @ref CeedErrorStore() 1606 1607 @param[in] ceed `Ceed` context to retrieve error message 1608 @param[out] err_msg Char pointer to hold error message 1609 1610 @return An error code: 0 - success, otherwise - failure 1611 1612 @ref Developer 1613 **/ 1614 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) { 1615 if (ceed->parent) return CeedGetErrorMessage(ceed->parent, err_msg); 1616 if (ceed->op_fallback_parent) return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg); 1617 *err_msg = ceed->err_msg; 1618 return CEED_ERROR_SUCCESS; 1619 } 1620 1621 /** 1622 @brief Restore error message. 1623 1624 The error message is only stored when using the error handler @ref CeedErrorStore(). 1625 1626 @param[in] ceed `Ceed` context to restore error message 1627 @param[out] err_msg Char pointer that holds error message 1628 1629 @return An error code: 0 - success, otherwise - failure 1630 1631 @ref Developer 1632 **/ 1633 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) { 1634 if (ceed->parent) return CeedResetErrorMessage(ceed->parent, err_msg); 1635 if (ceed->op_fallback_parent) return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg); 1636 *err_msg = NULL; 1637 memcpy(ceed->err_msg, "No error message stored", 24); 1638 return CEED_ERROR_SUCCESS; 1639 } 1640 1641 /** 1642 @brief Get libCEED library version information. 1643 1644 libCEED version numbers have the form major.minor.patch. 1645 Non-release versions may contain unstable interfaces. 1646 1647 @param[out] major Major version of the library 1648 @param[out] minor Minor version of the library 1649 @param[out] patch Patch (subminor) version of the library 1650 @param[out] release True for releases; false for development branches 1651 1652 The caller may pass `NULL` for any arguments that are not needed. 1653 1654 @return An error code: 0 - success, otherwise - failure 1655 1656 @ref Developer 1657 1658 @sa CEED_VERSION_GE() 1659 */ 1660 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) { 1661 if (major) *major = CEED_VERSION_MAJOR; 1662 if (minor) *minor = CEED_VERSION_MINOR; 1663 if (patch) *patch = CEED_VERSION_PATCH; 1664 if (release) *release = CEED_VERSION_RELEASE; 1665 return CEED_ERROR_SUCCESS; 1666 } 1667 1668 /** 1669 @brief Get libCEED scalar type, such as F64 or F32 1670 1671 @param[out] scalar_type Type of libCEED scalars 1672 1673 @return An error code: 0 - success, otherwise - failure 1674 1675 @ref Developer 1676 */ 1677 int CeedGetScalarType(CeedScalarType *scalar_type) { 1678 *scalar_type = CEED_SCALAR_TYPE; 1679 return CEED_ERROR_SUCCESS; 1680 } 1681 1682 /// @} 1683