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