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