1 // Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at 2 // the Lawrence Livermore National Laboratory. LLNL-CODE-734707. All Rights 3 // reserved. See files LICENSE and NOTICE for details. 4 // 5 // This file is part of CEED, a collection of benchmarks, miniapps, software 6 // libraries and APIs for efficient high-order finite element and spectral 7 // element discretizations for exascale applications. For more information and 8 // source code availability see http://github.com/ceed. 9 // 10 // The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 11 // a collaborative effort of two U.S. Department of Energy organizations (Office 12 // of Science and the National Nuclear Security Administration) responsible for 13 // the planning and preparation of a capable exascale ecosystem, including 14 // software, applications, hardware, advanced system engineering and early 15 // testbed platforms, in support of the nation's exascale computing imperative. 16 17 #define _POSIX_C_SOURCE 200112 18 #include <ceed/ceed.h> 19 #include <ceed/backend.h> 20 #include <ceed-impl.h> 21 #include <limits.h> 22 #include <stdarg.h> 23 #include <stddef.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 /// @cond DOXYGEN_SKIP 29 static CeedRequest ceed_request_immediate; 30 static CeedRequest ceed_request_ordered; 31 32 static struct { 33 char prefix[CEED_MAX_RESOURCE_LEN]; 34 int (*init)(const char *resource, Ceed f); 35 unsigned int priority; 36 } backends[32]; 37 static size_t num_backends; 38 39 #define CEED_FTABLE_ENTRY(class, method) \ 40 {#class #method, offsetof(struct class ##_private, method)} 41 /// @endcond 42 43 /// @file 44 /// Implementation of core components of Ceed library 45 46 /// @addtogroup CeedUser 47 /// @{ 48 49 /** 50 @brief Request immediate completion 51 52 This predefined constant is passed as the \ref CeedRequest argument to 53 interfaces when the caller wishes for the operation to be performed 54 immediately. The code 55 56 @code 57 CeedOperatorApply(op, ..., CEED_REQUEST_IMMEDIATE); 58 @endcode 59 60 is semantically equivalent to 61 62 @code 63 CeedRequest request; 64 CeedOperatorApply(op, ..., &request); 65 CeedRequestWait(&request); 66 @endcode 67 68 @sa CEED_REQUEST_ORDERED 69 **/ 70 CeedRequest *const CEED_REQUEST_IMMEDIATE = &ceed_request_immediate; 71 72 /** 73 @brief Request ordered completion 74 75 This predefined constant is passed as the \ref CeedRequest argument to 76 interfaces when the caller wishes for the operation to be completed in the 77 order that it is submitted to the device. It is typically used in a construct 78 such as 79 80 @code 81 CeedRequest request; 82 CeedOperatorApply(op1, ..., CEED_REQUEST_ORDERED); 83 CeedOperatorApply(op2, ..., &request); 84 // other optional work 85 CeedRequestWait(&request); 86 @endcode 87 88 which allows the sequence to complete asynchronously but does not start 89 `op2` until `op1` has completed. 90 91 @todo The current implementation is overly strict, offering equivalent 92 semantics to @ref CEED_REQUEST_IMMEDIATE. 93 94 @sa CEED_REQUEST_IMMEDIATE 95 */ 96 CeedRequest *const CEED_REQUEST_ORDERED = &ceed_request_ordered; 97 98 /** 99 @brief Wait for a CeedRequest to complete. 100 101 Calling CeedRequestWait on a NULL request is a no-op. 102 103 @param req Address of CeedRequest to wait for; zeroed on completion. 104 105 @return An error code: 0 - success, otherwise - failure 106 107 @ref User 108 **/ 109 int CeedRequestWait(CeedRequest *req) { 110 if (!*req) 111 return CEED_ERROR_SUCCESS; 112 return CeedError(NULL, CEED_ERROR_UNSUPPORTED, 113 "CeedRequestWait not implemented"); 114 } 115 116 /// @} 117 118 /// ---------------------------------------------------------------------------- 119 /// Ceed Library Internal Functions 120 /// ---------------------------------------------------------------------------- 121 /// @addtogroup CeedDeveloper 122 /// @{ 123 124 /** 125 @brief Register a Ceed backend internally. 126 Note: Backends should call `CeedRegister` instead. 127 128 @param prefix Prefix of resources for this backend to respond to. For 129 example, the reference backend responds to "/cpu/self". 130 @param init Initialization function called by CeedInit() when the backend 131 is selected to drive the requested resource. 132 @param priority Integer priority. Lower values are preferred in case the 133 resource requested by CeedInit() has non-unique best prefix 134 match. 135 136 @return An error code: 0 - success, otherwise - failure 137 138 @ref Developer 139 **/ 140 int CeedRegisterImpl(const char *prefix, int (*init)(const char *, Ceed), 141 unsigned int priority) { 142 if (num_backends >= sizeof(backends) / sizeof(backends[0])) 143 // LCOV_EXCL_START 144 return CeedError(NULL, CEED_ERROR_MAJOR, "Too many backends"); 145 // LCOV_EXCL_STOP 146 147 strncpy(backends[num_backends].prefix, prefix, CEED_MAX_RESOURCE_LEN); 148 backends[num_backends].prefix[CEED_MAX_RESOURCE_LEN-1] = 0; 149 backends[num_backends].init = init; 150 backends[num_backends].priority = priority; 151 num_backends++; 152 return CEED_ERROR_SUCCESS; 153 } 154 155 /// @} 156 157 /// ---------------------------------------------------------------------------- 158 /// Ceed Backend API 159 /// ---------------------------------------------------------------------------- 160 /// @addtogroup CeedBackend 161 /// @{ 162 163 /** 164 @brief Return value of CEED_DEBUG environment variable 165 166 @param ceed Ceed context 167 168 @return boolean value: true - debugging mode enabled 169 false - debugging mode disabled 170 171 @ref Backend 172 **/ 173 // LCOV_EXCL_START 174 bool CeedDebugFlag(const Ceed ceed) { 175 return ceed->is_debug; 176 } 177 // LCOV_EXCL_STOP 178 179 /** 180 @brief Return value of CEED_DEBUG environment variable 181 182 @return boolean value: true - debugging mode enabled 183 false - debugging mode disabled 184 185 @ref Backend 186 **/ 187 // LCOV_EXCL_START 188 bool CeedDebugFlagEnv(void) { 189 return !!getenv("CEED_DEBUG") || !!getenv("DEBUG") || !!getenv("DBG"); 190 } 191 // LCOV_EXCL_STOP 192 193 /** 194 @brief Print debugging information in color 195 196 @param color Color to print 197 @param format Printing format 198 199 @ref Backend 200 **/ 201 // LCOV_EXCL_START 202 void CeedDebugImpl256(const unsigned char color, const char *format,...) { 203 va_list args; 204 va_start(args, format); 205 fflush(stdout); 206 if (color != CEED_DEBUG_COLOR_NONE) 207 fprintf(stdout, "\033[38;5;%dm", color); 208 vfprintf(stdout, format, args); 209 if (color != CEED_DEBUG_COLOR_NONE) 210 fprintf(stdout, "\033[m"); 211 fprintf(stdout, "\n"); 212 fflush(stdout); 213 va_end(args); 214 } 215 // LCOV_EXCL_STOP 216 217 /** 218 @brief Allocate an array on the host; use CeedMalloc() 219 220 Memory usage can be tracked by the library. This ensures sufficient 221 alignment for vectorization and should be used for large allocations. 222 223 @param n Number of units to allocate 224 @param unit Size of each unit 225 @param p Address of pointer to hold the result. 226 227 @return An error code: 0 - success, otherwise - failure 228 229 @sa CeedFree() 230 231 @ref Backend 232 **/ 233 int CeedMallocArray(size_t n, size_t unit, void *p) { 234 int ierr = posix_memalign((void **)p, CEED_ALIGN, n*unit); 235 if (ierr) 236 // LCOV_EXCL_START 237 return CeedError(NULL, CEED_ERROR_MAJOR, 238 "posix_memalign failed to allocate %zd " 239 "members of size %zd\n", n, unit); 240 // LCOV_EXCL_STOP 241 return CEED_ERROR_SUCCESS; 242 } 243 244 /** 245 @brief Allocate a cleared (zeroed) array on the host; use CeedCalloc() 246 247 Memory usage can be tracked by the library. 248 249 @param n Number of units to allocate 250 @param unit Size of each unit 251 @param p Address of pointer to hold the result. 252 253 @return An error code: 0 - success, otherwise - failure 254 255 @sa CeedFree() 256 257 @ref Backend 258 **/ 259 int CeedCallocArray(size_t n, size_t unit, void *p) { 260 *(void **)p = calloc(n, unit); 261 if (n && unit && !*(void **)p) 262 // LCOV_EXCL_START 263 return CeedError(NULL, CEED_ERROR_MAJOR, 264 "calloc failed to allocate %zd members of size " 265 "%zd\n", n, unit); 266 // LCOV_EXCL_STOP 267 return CEED_ERROR_SUCCESS; 268 } 269 270 /** 271 @brief Reallocate an array on the host; use CeedRealloc() 272 273 Memory usage can be tracked by the library. 274 275 @param n Number of units to allocate 276 @param unit Size of each unit 277 @param p Address of pointer to hold the result. 278 279 @return An error code: 0 - success, otherwise - failure 280 281 @sa CeedFree() 282 283 @ref Backend 284 **/ 285 int CeedReallocArray(size_t n, size_t unit, void *p) { 286 *(void **)p = realloc(*(void **)p, n*unit); 287 if (n && unit && !*(void **)p) 288 // LCOV_EXCL_START 289 return CeedError(NULL, CEED_ERROR_MAJOR, 290 "realloc failed to allocate %zd members of size " 291 "%zd\n", n, unit); 292 // LCOV_EXCL_STOP 293 return CEED_ERROR_SUCCESS; 294 } 295 296 /** 297 @brief Allocate a cleared string buffer on the host 298 299 Memory usage can be tracked by the library. 300 301 @param source Pointer to string to be copied 302 @param copy Pointer to variable to hold newly allocated string copy 303 304 @return An error code: 0 - success, otherwise - failure 305 306 @sa CeedFree() 307 308 @ref Backend 309 **/ 310 int CeedStringAllocCopy(const char *source, char **copy) { 311 int ierr; 312 size_t len = strlen(source); 313 ierr = CeedCalloc(len + 1, copy); CeedChk(ierr); 314 memcpy(*copy, source, len + 1); 315 return CEED_ERROR_SUCCESS; 316 } 317 318 /** Free memory allocated using CeedMalloc() or CeedCalloc() 319 320 @param p address of pointer to memory. This argument is of type void* to 321 avoid needing a cast, but is the address of the pointer (which is 322 zeroed) rather than the pointer. 323 **/ 324 int CeedFree(void *p) { 325 free(*(void **)p); 326 *(void **)p = NULL; 327 return CEED_ERROR_SUCCESS; 328 } 329 330 /** 331 @brief Register a Ceed backend 332 333 @param prefix Prefix of resources for this backend to respond to. For 334 example, the reference backend responds to "/cpu/self". 335 @param init Initialization function called by CeedInit() when the backend 336 is selected to drive the requested resource. 337 @param priority Integer priority. Lower values are preferred in case the 338 resource requested by CeedInit() has non-unique best prefix 339 match. 340 341 @return An error code: 0 - success, otherwise - failure 342 343 @ref Backend 344 **/ 345 int CeedRegister(const char *prefix, int (*init)(const char *, Ceed), 346 unsigned int priority) { 347 CeedDebugEnv("Backend Register: %s", prefix); 348 CeedRegisterImpl(prefix, init, priority); 349 return CEED_ERROR_SUCCESS; 350 } 351 352 /** 353 @brief Return debugging status flag 354 355 @param ceed Ceed context to get debugging flag 356 @param is_debug Variable to store debugging flag 357 358 @return An error code: 0 - success, otherwise - failure 359 360 @ref Backend 361 **/ 362 int CeedIsDebug(Ceed ceed, bool *is_debug) { 363 *is_debug = ceed->is_debug; 364 return CEED_ERROR_SUCCESS; 365 } 366 367 /** 368 @brief Retrieve a parent Ceed context 369 370 @param ceed Ceed context to retrieve parent of 371 @param[out] parent Address to save the parent to 372 373 @return An error code: 0 - success, otherwise - failure 374 375 @ref Backend 376 **/ 377 int CeedGetParent(Ceed ceed, Ceed *parent) { 378 int ierr; 379 if (ceed->parent) { 380 ierr = CeedGetParent(ceed->parent, parent); CeedChk(ierr); 381 return CEED_ERROR_SUCCESS; 382 } 383 *parent = ceed; 384 return CEED_ERROR_SUCCESS; 385 } 386 387 /** 388 @brief Retrieve a delegate Ceed context 389 390 @param ceed Ceed context to retrieve delegate of 391 @param[out] delegate Address to save the delegate to 392 393 @return An error code: 0 - success, otherwise - failure 394 395 @ref Backend 396 **/ 397 int CeedGetDelegate(Ceed ceed, Ceed *delegate) { 398 *delegate = ceed->delegate; 399 return CEED_ERROR_SUCCESS; 400 } 401 402 /** 403 @brief Set a delegate Ceed context 404 405 This function allows a Ceed context to set a delegate Ceed context. All 406 backend implementations default to the delegate Ceed context, unless 407 overridden. 408 409 @param ceed Ceed context to set delegate of 410 @param[out] delegate Address to set the delegate to 411 412 @return An error code: 0 - success, otherwise - failure 413 414 @ref Backend 415 **/ 416 int CeedSetDelegate(Ceed ceed, Ceed delegate) { 417 ceed->delegate = delegate; 418 delegate->parent = ceed; 419 return CEED_ERROR_SUCCESS; 420 } 421 422 /** 423 @brief Retrieve a delegate Ceed context for a specific object type 424 425 @param ceed Ceed context to retrieve delegate of 426 @param[out] delegate Address to save the delegate to 427 @param[in] obj_name Name of the object type to retrieve delegate for 428 429 @return An error code: 0 - success, otherwise - failure 430 431 @ref Backend 432 **/ 433 int CeedGetObjectDelegate(Ceed ceed, Ceed *delegate, const char *obj_name) { 434 CeedInt ierr; 435 436 // Check for object delegate 437 for (CeedInt i=0; i<ceed->obj_delegate_count; i++) 438 if (!strcmp(obj_name, ceed->obj_delegates->obj_name)) { 439 *delegate = ceed->obj_delegates->delegate; 440 return CEED_ERROR_SUCCESS; 441 } 442 443 // Use default delegate if no object delegate 444 ierr = CeedGetDelegate(ceed, delegate); CeedChk(ierr); 445 return CEED_ERROR_SUCCESS; 446 } 447 448 /** 449 @brief Set a delegate Ceed context for a specific object type 450 451 This function allows a Ceed context to set a delegate Ceed context for a 452 given type of Ceed object. All backend implementations default to the 453 delegate Ceed context for this object. For example, 454 CeedSetObjectDelegate(ceed, refceed, "Basis") 455 uses refceed implementations for all CeedBasis backend functions. 456 457 @param ceed Ceed context to set delegate of 458 @param[out] delegate Address to set the delegate to 459 @param[in] obj_name Name of the object type to set delegate for 460 461 @return An error code: 0 - success, otherwise - failure 462 463 @ref Backend 464 **/ 465 int CeedSetObjectDelegate(Ceed ceed, Ceed delegate, const char *obj_name) { 466 int ierr; 467 CeedInt count = ceed->obj_delegate_count; 468 469 // Malloc or Realloc 470 if (count) { 471 ierr = CeedRealloc(count+1, &ceed->obj_delegates); CeedChk(ierr); 472 } else { 473 ierr = CeedCalloc(1, &ceed->obj_delegates); CeedChk(ierr); 474 } 475 ceed->obj_delegate_count++; 476 477 // Set object delegate 478 ceed->obj_delegates[count].delegate = delegate; 479 ierr = CeedStringAllocCopy(obj_name, &ceed->obj_delegates[count].obj_name); 480 CeedChk(ierr); 481 482 // Set delegate parent 483 delegate->parent = ceed; 484 return CEED_ERROR_SUCCESS; 485 } 486 487 /** 488 @brief Get the fallback resource for CeedOperators 489 490 @param ceed Ceed context 491 @param[out] resource Variable to store fallback resource 492 493 @return An error code: 0 - success, otherwise - failure 494 495 @ref Backend 496 **/ 497 498 int CeedGetOperatorFallbackResource(Ceed ceed, const char **resource) { 499 *resource = (const char *)ceed->op_fallback_resource; 500 return CEED_ERROR_SUCCESS; 501 } 502 503 /** 504 @brief Set the fallback resource for CeedOperators. The current resource, if 505 any, is freed by calling this function. This string is freed upon the 506 destruction of the Ceed context. 507 508 @param[out] ceed Ceed context 509 @param resource Fallback resource to set 510 511 @return An error code: 0 - success, otherwise - failure 512 513 @ref Backend 514 **/ 515 516 int CeedSetOperatorFallbackResource(Ceed ceed, const char *resource) { 517 int ierr; 518 519 // Free old 520 ierr = CeedFree(&ceed->op_fallback_resource); CeedChk(ierr); 521 522 // Set new 523 ierr = CeedStringAllocCopy(resource, (char **)&ceed->op_fallback_resource); 524 CeedChk(ierr); 525 return CEED_ERROR_SUCCESS; 526 } 527 528 /** 529 @brief Get the parent Ceed context associated with a fallback Ceed context 530 for a CeedOperator 531 532 @param ceed Ceed context 533 @param[out] parent Variable to store parent Ceed context 534 535 @return An error code: 0 - success, otherwise - failure 536 537 @ref Backend 538 **/ 539 540 int CeedGetOperatorFallbackParentCeed(Ceed ceed, Ceed *parent) { 541 *parent = ceed->op_fallback_parent; 542 return CEED_ERROR_SUCCESS; 543 } 544 545 /** 546 @brief Flag Ceed context as deterministic 547 548 @param ceed Ceed to flag as deterministic 549 @param[out] is_deterministic Deterministic status to set 550 551 @return An error code: 0 - success, otherwise - failure 552 553 @ref Backend 554 **/ 555 556 int CeedSetDeterministic(Ceed ceed, bool is_deterministic) { 557 ceed->is_deterministic = is_deterministic; 558 return CEED_ERROR_SUCCESS; 559 } 560 561 /** 562 @brief Set a backend function 563 564 This function is used for a backend to set the function associated with 565 the Ceed objects. For example, 566 CeedSetBackendFunction(ceed, "Ceed", ceed, "VectorCreate", BackendVectorCreate) 567 sets the backend implementation of 'CeedVectorCreate' and 568 CeedSetBackendFunction(ceed, "Basis", basis, "Apply", BackendBasisApply) 569 sets the backend implementation of 'CeedBasisApply'. Note, the prefix 'Ceed' 570 is not required for the object type ("Basis" vs "CeedBasis"). 571 572 @param ceed Ceed context for error handling 573 @param type Type of Ceed object to set function for 574 @param[out] object Ceed object to set function for 575 @param func_name Name of function to set 576 @param f Function to set 577 578 @return An error code: 0 - success, otherwise - failure 579 580 @ref Backend 581 **/ 582 int CeedSetBackendFunction(Ceed ceed, const char *type, void *object, 583 const char *func_name, int (*f)()) { 584 char lookup_name[CEED_MAX_RESOURCE_LEN+1] = ""; 585 586 // Build lookup name 587 if (strcmp(type, "Ceed")) 588 strncat (lookup_name, "Ceed", CEED_MAX_RESOURCE_LEN); 589 strncat(lookup_name, type, CEED_MAX_RESOURCE_LEN); 590 strncat(lookup_name, func_name, CEED_MAX_RESOURCE_LEN); 591 592 // Find and use offset 593 for (CeedInt i = 0; ceed->f_offsets[i].func_name; i++) 594 if (!strcmp(ceed->f_offsets[i].func_name, lookup_name)) { 595 size_t offset = ceed->f_offsets[i].offset; 596 int (**fpointer)(void) = (int (**)(void))((char *)object + offset); // *NOPAD* 597 *fpointer = f; 598 return CEED_ERROR_SUCCESS; 599 } 600 601 // LCOV_EXCL_START 602 return CeedError(ceed, CEED_ERROR_UNSUPPORTED, 603 "Requested function '%s' was not found for CEED " 604 "object '%s'", func_name, type); 605 // LCOV_EXCL_STOP 606 } 607 608 /** 609 @brief Retrieve backend data for a Ceed context 610 611 @param ceed Ceed context to retrieve data of 612 @param[out] data Address to save data to 613 614 @return An error code: 0 - success, otherwise - failure 615 616 @ref Backend 617 **/ 618 int CeedGetData(Ceed ceed, void *data) { 619 *(void **)data = ceed->data; 620 return CEED_ERROR_SUCCESS; 621 } 622 623 /** 624 @brief Set backend data for a Ceed context 625 626 @param ceed Ceed context to set data of 627 @param data Address of data to set 628 629 @return An error code: 0 - success, otherwise - failure 630 631 @ref Backend 632 **/ 633 int CeedSetData(Ceed ceed, void *data) { 634 ceed->data = data; 635 return CEED_ERROR_SUCCESS; 636 } 637 638 /** 639 @brief Increment the reference counter for a Ceed context 640 641 @param ceed Ceed context to increment the reference counter 642 643 @return An error code: 0 - success, otherwise - failure 644 645 @ref Backend 646 **/ 647 int CeedReference(Ceed ceed) { 648 ceed->ref_count++; 649 return CEED_ERROR_SUCCESS; 650 } 651 652 /// @} 653 654 /// ---------------------------------------------------------------------------- 655 /// Ceed Public API 656 /// ---------------------------------------------------------------------------- 657 /// @addtogroup CeedUser 658 /// @{ 659 660 /** 661 @brief Get the list of available resource names for Ceed contexts 662 Note: The caller is responsible for `free()`ing the resources and priorities arrays, 663 but should not `free()` the contents of the resources array. 664 665 @param[out] n Number of available resources 666 @param[out] resources List of available resource names 667 @param[out] priorities Resource name prioritization values, lower is better 668 669 @return An error code: 0 - success, otherwise - failure 670 671 @ref User 672 **/ 673 // LCOV_EXCL_START 674 int CeedRegistryGetList(size_t *n, char ***const resources, 675 CeedInt **priorities) { 676 *n = 0; 677 *resources = malloc(num_backends * sizeof(**resources)); 678 if (!resources) 679 return CeedError(NULL, CEED_ERROR_MAJOR, "malloc() failure"); 680 if (priorities) { 681 *priorities = malloc(num_backends * sizeof(**priorities)); 682 if (!priorities) 683 return CeedError(NULL, CEED_ERROR_MAJOR, "malloc() failure"); 684 } 685 for (size_t i=0; i<num_backends; i++) { 686 // Only report compiled backends 687 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) { 688 *resources[i] = backends[i].prefix; 689 if (priorities) *priorities[i] = backends[i].priority; 690 *n += 1; 691 } 692 } 693 if (*n == 0) 694 // LCOV_EXCL_START 695 return CeedError(NULL, CEED_ERROR_MAJOR, "No backends installed"); 696 // LCOV_EXCL_STOP 697 *resources = realloc(*resources, *n * sizeof(**resources)); 698 if (!resources) 699 return CeedError(NULL, CEED_ERROR_MAJOR, "realloc() failure"); 700 if (priorities) { 701 *priorities = realloc(*priorities, *n * sizeof(**priorities)); 702 if (!priorities) 703 return CeedError(NULL, CEED_ERROR_MAJOR, "realloc() failure"); 704 } 705 return CEED_ERROR_SUCCESS; 706 } 707 // LCOV_EXCL_STOP 708 709 /** 710 @brief Initialize a \ref Ceed context to use the specified resource. 711 Note: Prefixing the resource with "help:" (e.g. "help:/cpu/self") 712 will result in CeedInt printing the current libCEED version number 713 and a list of current available backend resources to stderr. 714 715 @param resource Resource to use, e.g., "/cpu/self" 716 @param ceed The library context 717 @sa CeedRegister() CeedDestroy() 718 719 @return An error code: 0 - success, otherwise - failure 720 721 @ref User 722 **/ 723 int CeedInit(const char *resource, Ceed *ceed) { 724 int ierr; 725 size_t match_len = 0, match_index = UINT_MAX, 726 match_priority = CEED_MAX_BACKEND_PRIORITY, priority; 727 728 // Find matching backend 729 if (!resource) 730 // LCOV_EXCL_START 731 return CeedError(NULL, CEED_ERROR_MAJOR, "No resource provided"); 732 // LCOV_EXCL_STOP 733 ierr = CeedRegisterAll(); CeedChk(ierr); 734 735 // Check for help request 736 const char *help_prefix = "help"; 737 size_t match_help; 738 for (match_help=0; match_help<4 739 && resource[match_help] == help_prefix[match_help]; match_help++) {} 740 if (match_help == 4) { 741 fprintf(stderr, "libCEED version: %d.%d%d%s\n", CEED_VERSION_MAJOR, 742 CEED_VERSION_MINOR, CEED_VERSION_PATCH, 743 CEED_VERSION_RELEASE ? "" : "+development"); 744 fprintf(stderr, "Available backend resources:\n"); 745 for (size_t i=0; i<num_backends; i++) { 746 // Only report compiled backends 747 if (backends[i].priority < CEED_MAX_BACKEND_PRIORITY) 748 fprintf(stderr, " %s\n", backends[i].prefix); 749 } 750 fflush(stderr); 751 match_help = 5; // Delineating character expected 752 } else { 753 match_help = 0; 754 } 755 756 // Find best match, computed as number of matching characters 757 // from requested resource stem 758 size_t stem_length; 759 for (stem_length=0; resource[stem_length+match_help] 760 && resource[stem_length+match_help] != ':'; stem_length++) {} 761 for (size_t i=0; i<num_backends; i++) { 762 size_t n; 763 const char *prefix = backends[i].prefix; 764 for (n=0; prefix[n] && prefix[n] == resource[n+match_help]; n++) {} 765 priority = backends[i].priority; 766 if (n > match_len || (n == match_len && match_priority > priority)) { 767 match_len = n; 768 match_priority = priority; 769 match_index = i; 770 } 771 } 772 // Using Levenshtein distance to find closest match 773 if (match_len <= 1 || match_len != stem_length) { 774 // LCOV_EXCL_START 775 size_t lev_dis = UINT_MAX; 776 size_t lev_index = UINT_MAX, lev_priority = CEED_MAX_BACKEND_PRIORITY; 777 for (size_t i=0; i<num_backends; i++) { 778 const char *prefix = backends[i].prefix; 779 size_t prefix_length = strlen(backends[i].prefix); 780 size_t min_len = (prefix_length < stem_length) ? prefix_length : stem_length; 781 size_t column[min_len+1]; 782 for (size_t j=0; j<=min_len; j++) column[j] = j; 783 for (size_t j=1; j<=min_len; j++) { 784 column[0] = j; 785 for (size_t k=1, last_diag=j-1; k<=min_len; k++) { 786 size_t old_diag = column[k]; 787 size_t min_1 = (column[k] < column[k-1]) ? column[k]+1 : column[k-1]+1; 788 size_t min_2 = last_diag + (resource[k-1] == prefix[j-1] ? 0 : 1); 789 column[k] = (min_1 < min_2) ? min_1 : min_2; 790 last_diag = old_diag; 791 } 792 } 793 size_t n = column[min_len]; 794 priority = backends[i].priority; 795 if (n < lev_dis || (n == lev_dis 796 && lev_priority > priority)) { 797 lev_dis = n; 798 lev_priority = priority; 799 lev_index = i; 800 } 801 } 802 const char *prefix_lev = backends[lev_index].prefix; 803 size_t lev_length; 804 for (lev_length=0; prefix_lev[lev_length] 805 && prefix_lev[lev_length] != '\0'; lev_length++) {} 806 size_t m = (lev_length < stem_length) ? lev_length : stem_length; 807 if (lev_dis+1 >= m) { 808 return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s", 809 resource); 810 } else { 811 return CeedError(NULL, CEED_ERROR_MAJOR, "No suitable backend: %s\n" 812 "Closest match: %s", resource, backends[lev_index].prefix); 813 } 814 // LCOV_EXCL_STOP 815 } 816 817 // Setup Ceed 818 ierr = CeedCalloc(1, ceed); CeedChk(ierr); 819 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 820 if (!ceed_error_handler) 821 ceed_error_handler = "abort"; 822 if (!strcmp(ceed_error_handler, "exit")) 823 (*ceed)->Error = CeedErrorExit; 824 else if (!strcmp(ceed_error_handler, "store")) 825 (*ceed)->Error = CeedErrorStore; 826 else 827 (*ceed)->Error = CeedErrorAbort; 828 memcpy((*ceed)->err_msg, "No error message stored", 24); 829 (*ceed)->ref_count = 1; 830 (*ceed)->data = NULL; 831 832 // Set lookup table 833 FOffset f_offsets[] = { 834 CEED_FTABLE_ENTRY(Ceed, Error), 835 CEED_FTABLE_ENTRY(Ceed, GetPreferredMemType), 836 CEED_FTABLE_ENTRY(Ceed, Destroy), 837 CEED_FTABLE_ENTRY(Ceed, VectorCreate), 838 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreate), 839 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateOriented), 840 CEED_FTABLE_ENTRY(Ceed, ElemRestrictionCreateBlocked), 841 CEED_FTABLE_ENTRY(Ceed, BasisCreateTensorH1), 842 CEED_FTABLE_ENTRY(Ceed, BasisCreateH1), 843 CEED_FTABLE_ENTRY(Ceed, BasisCreateHdiv), 844 CEED_FTABLE_ENTRY(Ceed, TensorContractCreate), 845 CEED_FTABLE_ENTRY(Ceed, QFunctionCreate), 846 CEED_FTABLE_ENTRY(Ceed, QFunctionContextCreate), 847 CEED_FTABLE_ENTRY(Ceed, OperatorCreate), 848 CEED_FTABLE_ENTRY(Ceed, CompositeOperatorCreate), 849 CEED_FTABLE_ENTRY(CeedVector, HasValidArray), 850 CEED_FTABLE_ENTRY(CeedVector, HasBorrowedArrayOfType), 851 CEED_FTABLE_ENTRY(CeedVector, SetArray), 852 CEED_FTABLE_ENTRY(CeedVector, TakeArray), 853 CEED_FTABLE_ENTRY(CeedVector, SetValue), 854 CEED_FTABLE_ENTRY(CeedVector, GetArray), 855 CEED_FTABLE_ENTRY(CeedVector, GetArrayRead), 856 CEED_FTABLE_ENTRY(CeedVector, GetArrayWrite), 857 CEED_FTABLE_ENTRY(CeedVector, RestoreArray), 858 CEED_FTABLE_ENTRY(CeedVector, RestoreArrayRead), 859 CEED_FTABLE_ENTRY(CeedVector, Norm), 860 CEED_FTABLE_ENTRY(CeedVector, Scale), 861 CEED_FTABLE_ENTRY(CeedVector, AXPY), 862 CEED_FTABLE_ENTRY(CeedVector, PointwiseMult), 863 CEED_FTABLE_ENTRY(CeedVector, Reciprocal), 864 CEED_FTABLE_ENTRY(CeedVector, Destroy), 865 CEED_FTABLE_ENTRY(CeedElemRestriction, Apply), 866 CEED_FTABLE_ENTRY(CeedElemRestriction, ApplyBlock), 867 CEED_FTABLE_ENTRY(CeedElemRestriction, GetOffsets), 868 CEED_FTABLE_ENTRY(CeedElemRestriction, Destroy), 869 CEED_FTABLE_ENTRY(CeedBasis, Apply), 870 CEED_FTABLE_ENTRY(CeedBasis, Destroy), 871 CEED_FTABLE_ENTRY(CeedTensorContract, Apply), 872 CEED_FTABLE_ENTRY(CeedTensorContract, Destroy), 873 CEED_FTABLE_ENTRY(CeedQFunction, Apply), 874 CEED_FTABLE_ENTRY(CeedQFunction, SetCUDAUserFunction), 875 CEED_FTABLE_ENTRY(CeedQFunction, SetHIPUserFunction), 876 CEED_FTABLE_ENTRY(CeedQFunction, Destroy), 877 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasValidData), 878 CEED_FTABLE_ENTRY(CeedQFunctionContext, HasBorrowedDataOfType), 879 CEED_FTABLE_ENTRY(CeedQFunctionContext, SetData), 880 CEED_FTABLE_ENTRY(CeedQFunctionContext, TakeData), 881 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetData), 882 CEED_FTABLE_ENTRY(CeedQFunctionContext, GetDataRead), 883 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreData), 884 CEED_FTABLE_ENTRY(CeedQFunctionContext, RestoreDataRead), 885 CEED_FTABLE_ENTRY(CeedQFunctionContext, Destroy), 886 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunction), 887 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleQFunctionUpdate), 888 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleDiagonal), 889 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddDiagonal), 890 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemblePointBlockDiagonal), 891 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleAddPointBlockDiagonal), 892 CEED_FTABLE_ENTRY(CeedOperator, LinearAssembleSymbolic), 893 CEED_FTABLE_ENTRY(CeedOperator, LinearAssemble), 894 CEED_FTABLE_ENTRY(CeedOperator, CreateFDMElementInverse), 895 CEED_FTABLE_ENTRY(CeedOperator, Apply), 896 CEED_FTABLE_ENTRY(CeedOperator, ApplyComposite), 897 CEED_FTABLE_ENTRY(CeedOperator, ApplyAdd), 898 CEED_FTABLE_ENTRY(CeedOperator, ApplyAddComposite), 899 CEED_FTABLE_ENTRY(CeedOperator, ApplyJacobian), 900 CEED_FTABLE_ENTRY(CeedOperator, Destroy), 901 {NULL, 0} // End of lookup table - used in SetBackendFunction loop 902 }; 903 904 ierr = CeedCalloc(sizeof(f_offsets), &(*ceed)->f_offsets); CeedChk(ierr); 905 memcpy((*ceed)->f_offsets, f_offsets, sizeof(f_offsets)); 906 907 // Set fallback for advanced CeedOperator functions 908 const char fallbackresource[] = ""; 909 ierr = CeedSetOperatorFallbackResource(*ceed, fallbackresource); 910 CeedChk(ierr); 911 912 // Record env variables CEED_DEBUG or DBG 913 (*ceed)->is_debug = !!getenv("CEED_DEBUG") || !!getenv("DEBUG") || 914 !!getenv("DBG"); 915 916 // Backend specific setup 917 ierr = backends[match_index].init(&resource[match_help], *ceed); CeedChk(ierr); 918 919 // Copy resource prefix, if backend setup successful 920 ierr = CeedStringAllocCopy(backends[match_index].prefix, 921 (char **)&(*ceed)->resource); 922 CeedChk(ierr); 923 return CEED_ERROR_SUCCESS; 924 } 925 926 /** 927 @brief Copy the pointer to a Ceed context. Both pointers should 928 be destroyed with `CeedDestroy()`; 929 Note: If `*ceed_copy` is non-NULL, then it is assumed that 930 `*ceed_copy` is a pointer to a Ceed context. This Ceed 931 context will be destroyed if `*ceed_copy` is the only 932 reference to this Ceed context. 933 934 @param ceed Ceed context to copy reference to 935 @param[out] ceed_copy Variable to store copied reference 936 937 @return An error code: 0 - success, otherwise - failure 938 939 @ref User 940 **/ 941 int CeedReferenceCopy(Ceed ceed, Ceed *ceed_copy) { 942 int ierr; 943 944 ierr = CeedReference(ceed); CeedChk(ierr); 945 ierr = CeedDestroy(ceed_copy); CeedChk(ierr); 946 *ceed_copy = ceed; 947 return CEED_ERROR_SUCCESS; 948 } 949 950 /** 951 @brief Get the full resource name for a Ceed context 952 953 @param ceed Ceed context to get resource name of 954 @param[out] resource Variable to store resource name 955 956 @return An error code: 0 - success, otherwise - failure 957 958 @ref User 959 **/ 960 int CeedGetResource(Ceed ceed, const char **resource) { 961 *resource = (const char *)ceed->resource; 962 return CEED_ERROR_SUCCESS; 963 } 964 965 /** 966 @brief Return Ceed context preferred memory type 967 968 @param ceed Ceed context to get preferred memory type of 969 @param[out] mem_type Address to save preferred memory type to 970 971 @return An error code: 0 - success, otherwise - failure 972 973 @ref User 974 **/ 975 int CeedGetPreferredMemType(Ceed ceed, CeedMemType *mem_type) { 976 int ierr; 977 978 if (ceed->GetPreferredMemType) { 979 ierr = ceed->GetPreferredMemType(mem_type); CeedChk(ierr); 980 } else { 981 Ceed delegate; 982 ierr = CeedGetDelegate(ceed, &delegate); CeedChk(ierr); 983 984 if (delegate) { 985 ierr = CeedGetPreferredMemType(delegate, mem_type); CeedChk(ierr); 986 } else { 987 *mem_type = CEED_MEM_HOST; 988 } 989 } 990 return CEED_ERROR_SUCCESS; 991 } 992 993 /** 994 @brief Get deterministic status of Ceed 995 996 @param[in] ceed Ceed 997 @param[out] is_deterministic Variable to store deterministic status 998 999 @return An error code: 0 - success, otherwise - failure 1000 1001 @ref User 1002 **/ 1003 int CeedIsDeterministic(Ceed ceed, bool *is_deterministic) { 1004 *is_deterministic = ceed->is_deterministic; 1005 return CEED_ERROR_SUCCESS; 1006 } 1007 1008 /** 1009 @brief View a Ceed 1010 1011 @param[in] ceed Ceed to view 1012 @param[in] stream Filestream to write to 1013 1014 @return An error code: 0 - success, otherwise - failure 1015 1016 @ref User 1017 **/ 1018 int CeedView(Ceed ceed, FILE *stream) { 1019 int ierr; 1020 CeedMemType mem_type; 1021 1022 ierr = CeedGetPreferredMemType(ceed, &mem_type); CeedChk(ierr); 1023 1024 fprintf(stream, "Ceed\n" 1025 " Ceed Resource: %s\n" 1026 " Preferred MemType: %s\n", 1027 ceed->resource, CeedMemTypes[mem_type]); 1028 return CEED_ERROR_SUCCESS; 1029 } 1030 1031 /** 1032 @brief Destroy a Ceed context 1033 1034 @param ceed Address of Ceed context to destroy 1035 1036 @return An error code: 0 - success, otherwise - failure 1037 1038 @ref User 1039 **/ 1040 int CeedDestroy(Ceed *ceed) { 1041 int ierr; 1042 if (!*ceed || --(*ceed)->ref_count > 0) return CEED_ERROR_SUCCESS; 1043 if ((*ceed)->delegate) { 1044 ierr = CeedDestroy(&(*ceed)->delegate); CeedChk(ierr); 1045 } 1046 1047 if ((*ceed)->obj_delegate_count > 0) { 1048 for (int i=0; i<(*ceed)->obj_delegate_count; i++) { 1049 ierr = CeedDestroy(&((*ceed)->obj_delegates[i].delegate)); CeedChk(ierr); 1050 ierr = CeedFree(&(*ceed)->obj_delegates[i].obj_name); CeedChk(ierr); 1051 } 1052 ierr = CeedFree(&(*ceed)->obj_delegates); CeedChk(ierr); 1053 } 1054 1055 if ((*ceed)->Destroy) { 1056 ierr = (*ceed)->Destroy(*ceed); CeedChk(ierr); 1057 } 1058 1059 ierr = CeedFree(&(*ceed)->f_offsets); CeedChk(ierr); 1060 ierr = CeedFree(&(*ceed)->resource); CeedChk(ierr); 1061 ierr = CeedDestroy(&(*ceed)->op_fallback_ceed); CeedChk(ierr); 1062 ierr = CeedFree(&(*ceed)->op_fallback_resource); CeedChk(ierr); 1063 ierr = CeedFree(ceed); CeedChk(ierr); 1064 return CEED_ERROR_SUCCESS; 1065 } 1066 1067 // LCOV_EXCL_START 1068 const char *CeedErrorFormat(Ceed ceed, const char *format, va_list *args) { 1069 if (ceed->parent) 1070 return CeedErrorFormat(ceed->parent, format, args); 1071 if (ceed->op_fallback_parent) 1072 return CeedErrorFormat(ceed->op_fallback_parent, format, args); 1073 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1074 vsnprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, format, *args); // NOLINT 1075 return ceed->err_msg; 1076 } 1077 // LCOV_EXCL_STOP 1078 1079 /** 1080 @brief Error handling implementation; use \ref CeedError instead. 1081 1082 @ref Developer 1083 **/ 1084 int CeedErrorImpl(Ceed ceed, const char *filename, int lineno, const char *func, 1085 int ecode, const char *format, ...) { 1086 va_list args; 1087 int ret_val; 1088 va_start(args, format); 1089 if (ceed) { 1090 ret_val = ceed->Error(ceed, filename, lineno, func, ecode, format, &args); 1091 } else { 1092 // LCOV_EXCL_START 1093 const char *ceed_error_handler = getenv("CEED_ERROR_HANDLER"); 1094 if (!ceed_error_handler) 1095 ceed_error_handler = "abort"; 1096 if (!strcmp(ceed_error_handler, "return")) 1097 ret_val = CeedErrorReturn(ceed, filename, lineno, func, ecode, format, &args); 1098 else 1099 // This function will not return 1100 ret_val = CeedErrorAbort(ceed, filename, lineno, func, ecode, format, &args); 1101 } 1102 va_end(args); 1103 return ret_val; 1104 // LCOV_EXCL_STOP 1105 } 1106 1107 /** 1108 @brief Error handler that returns without printing anything. 1109 1110 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 1111 1112 @ref Developer 1113 **/ 1114 // LCOV_EXCL_START 1115 int CeedErrorReturn(Ceed ceed, const char *filename, int line_no, 1116 const char *func, int err_code, const char *format, 1117 va_list *args) { 1118 return err_code; 1119 } 1120 // LCOV_EXCL_STOP 1121 1122 /** 1123 @brief Error handler that stores the error message for future use and returns 1124 the error. 1125 1126 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 1127 1128 @ref Developer 1129 **/ 1130 // LCOV_EXCL_START 1131 int CeedErrorStore(Ceed ceed, const char *filename, int line_no, 1132 const char *func, int err_code, const char *format, 1133 va_list *args) { 1134 if (ceed->parent) 1135 return CeedErrorStore(ceed->parent, filename, line_no, func, err_code, format, 1136 args); 1137 if (ceed->op_fallback_parent) 1138 return CeedErrorStore(ceed->op_fallback_parent, filename, line_no, func, 1139 err_code, format, args); 1140 1141 // Build message 1142 CeedInt len; 1143 len = snprintf(ceed->err_msg, CEED_MAX_RESOURCE_LEN, "%s:%d in %s(): ", 1144 filename, line_no, func); 1145 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1146 // *INDENT-OFF* 1147 vsnprintf(ceed->err_msg + len, CEED_MAX_RESOURCE_LEN - len, format, *args); // NOLINT 1148 // *INDENT-ON* 1149 return err_code; 1150 } 1151 // LCOV_EXCL_STOP 1152 1153 /** 1154 @brief Error handler that prints to stderr and aborts 1155 1156 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 1157 1158 @ref Developer 1159 **/ 1160 // LCOV_EXCL_START 1161 int CeedErrorAbort(Ceed ceed, const char *filename, int line_no, 1162 const char *func, int err_code, const char *format, 1163 va_list *args) { 1164 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1165 vfprintf(stderr, format, *args); 1166 fprintf(stderr, "\n"); 1167 abort(); 1168 return err_code; 1169 } 1170 // LCOV_EXCL_STOP 1171 1172 /** 1173 @brief Error handler that prints to stderr and exits 1174 1175 Pass this to CeedSetErrorHandler() to obtain this error handling behavior. 1176 1177 In contrast to CeedErrorAbort(), this exits without a signal, so atexit() 1178 handlers (e.g., as used by gcov) are run. 1179 1180 @ref Developer 1181 **/ 1182 int CeedErrorExit(Ceed ceed, const char *filename, int line_no, 1183 const char *func, int err_code, const char *format, va_list *args) { 1184 fprintf(stderr, "%s:%d in %s(): ", filename, line_no, func); 1185 // Using pointer to va_list for better FFI, but clang-tidy can't verify va_list is initalized 1186 vfprintf(stderr, format, *args); // NOLINT 1187 fprintf(stderr, "\n"); 1188 exit(err_code); 1189 return err_code; 1190 } 1191 1192 /** 1193 @brief Set error handler 1194 1195 A default error handler is set in CeedInit(). Use this function to change 1196 the error handler to CeedErrorReturn(), CeedErrorAbort(), or a user-defined 1197 error handler. 1198 1199 @ref Developer 1200 **/ 1201 int CeedSetErrorHandler(Ceed ceed, CeedErrorHandler handler) { 1202 ceed->Error = handler; 1203 if (ceed->delegate) CeedSetErrorHandler(ceed->delegate, handler); 1204 for (int i=0; i<ceed->obj_delegate_count; i++) 1205 CeedSetErrorHandler(ceed->obj_delegates[i].delegate, handler); 1206 return CEED_ERROR_SUCCESS; 1207 } 1208 1209 /** 1210 @brief Get error message 1211 1212 The error message is only stored when using the error handler 1213 CeedErrorStore() 1214 1215 @param[in] ceed Ceed contex to retrieve error message 1216 @param[out] err_msg Char pointer to hold error message 1217 1218 @ref Developer 1219 **/ 1220 int CeedGetErrorMessage(Ceed ceed, const char **err_msg) { 1221 if (ceed->parent) 1222 return CeedGetErrorMessage(ceed->parent, err_msg); 1223 if (ceed->op_fallback_parent) 1224 return CeedGetErrorMessage(ceed->op_fallback_parent, err_msg); 1225 *err_msg = ceed->err_msg; 1226 return CEED_ERROR_SUCCESS; 1227 } 1228 1229 /** 1230 @brief Restore error message 1231 1232 The error message is only stored when using the error handler 1233 CeedErrorStore() 1234 1235 @param[in] ceed Ceed contex to restore error message 1236 @param[out] err_msg Char pointer that holds error message 1237 1238 @ref Developer 1239 **/ 1240 int CeedResetErrorMessage(Ceed ceed, const char **err_msg) { 1241 if (ceed->parent) 1242 return CeedResetErrorMessage(ceed->parent, err_msg); 1243 if (ceed->op_fallback_parent) 1244 return CeedResetErrorMessage(ceed->op_fallback_parent, err_msg); 1245 *err_msg = NULL; 1246 memcpy(ceed->err_msg, "No error message stored", 24); 1247 return CEED_ERROR_SUCCESS; 1248 } 1249 1250 /** 1251 @brief Get libCEED library version info 1252 1253 libCEED version numbers have the form major.minor.patch. Non-release versions 1254 may contain unstable interfaces. 1255 1256 @param[out] major Major version of the library 1257 @param[out] minor Minor version of the library 1258 @param[out] patch Patch (subminor) version of the library 1259 @param[out] release True for releases; false for development branches. 1260 1261 The caller may pass NULL for any arguments that are not needed. 1262 1263 @sa CEED_VERSION_GE() 1264 1265 @ref Developer 1266 */ 1267 int CeedGetVersion(int *major, int *minor, int *patch, bool *release) { 1268 if (major) *major = CEED_VERSION_MAJOR; 1269 if (minor) *minor = CEED_VERSION_MINOR; 1270 if (patch) *patch = CEED_VERSION_PATCH; 1271 if (release) *release = CEED_VERSION_RELEASE; 1272 return 0; 1273 } 1274 1275 int CeedGetScalarType(CeedScalarType *scalar_type) { 1276 *scalar_type = CEED_SCALAR_TYPE; 1277 return 0; 1278 } 1279 1280 1281 /// @} 1282