1 // Copyright (c) 2017-2024, Lawrence Livermore National Security, LLC and other CEED contributors. 2 // All Rights Reserved. See the top-level LICENSE and NOTICE files for details. 3 // 4 // SPDX-License-Identifier: BSD-2-Clause 5 // 6 // This file is part of CEED: http://github.com/ceed 7 8 #include <ceed-impl.h> 9 #include <ceed.h> 10 #include <ceed/backend.h> 11 #include <assert.h> 12 #include <math.h> 13 #include <stdbool.h> 14 #include <stdint.h> 15 #include <stdio.h> 16 17 /// @file 18 /// Implementation of public CeedVector interfaces 19 20 /// @cond DOXYGEN_SKIP 21 static struct CeedVector_private ceed_vector_active; 22 static struct CeedVector_private ceed_vector_none; 23 /// @endcond 24 25 /// @addtogroup CeedVectorUser 26 /// @{ 27 28 /// Indicate that vector will be provided as an explicit argument to @ref CeedOperatorApply(). 29 const CeedVector CEED_VECTOR_ACTIVE = &ceed_vector_active; 30 31 /// Indicate that no vector is applicable (i.e., for @ref CEED_EVAL_WEIGHT). 32 const CeedVector CEED_VECTOR_NONE = &ceed_vector_none; 33 34 /// @} 35 36 /// ---------------------------------------------------------------------------- 37 /// CeedVector Backend API 38 /// ---------------------------------------------------------------------------- 39 /// @addtogroup CeedVectorBackend 40 /// @{ 41 42 /** 43 @brief Check for valid data in a `CeedVector` 44 45 @param[in] vec `CeedVector` to check validity 46 @param[out] has_valid_array Variable to store validity 47 48 @return An error code: 0 - success, otherwise - failure 49 50 @ref Backend 51 **/ 52 int CeedVectorHasValidArray(CeedVector vec, bool *has_valid_array) { 53 CeedSize length; 54 55 CeedCheck(vec->HasValidArray, CeedVectorReturnCeed(vec), CEED_ERROR_UNSUPPORTED, "Backend does not support CeedVectorHasValidArray"); 56 CeedCall(CeedVectorGetLength(vec, &length)); 57 if (length == 0) { 58 *has_valid_array = true; 59 return CEED_ERROR_SUCCESS; 60 } 61 CeedCall(vec->HasValidArray(vec, has_valid_array)); 62 return CEED_ERROR_SUCCESS; 63 } 64 65 /** 66 @brief Check for borrowed array of a specific @ref CeedMemType in a `CeedVector` 67 68 @param[in] vec `CeedVector` to check 69 @param[in] mem_type Memory type to check 70 @param[out] has_borrowed_array_of_type Variable to store result 71 72 @return An error code: 0 - success, otherwise - failure 73 74 @ref Backend 75 **/ 76 int CeedVectorHasBorrowedArrayOfType(CeedVector vec, CeedMemType mem_type, bool *has_borrowed_array_of_type) { 77 CeedCheck(vec->HasBorrowedArrayOfType, CeedVectorReturnCeed(vec), CEED_ERROR_UNSUPPORTED, 78 "Backend does not support CeedVectorHasBorrowedArrayOfType"); 79 CeedCall(vec->HasBorrowedArrayOfType(vec, mem_type, has_borrowed_array_of_type)); 80 return CEED_ERROR_SUCCESS; 81 } 82 83 /** 84 @brief Get the state of a `CeedVector` 85 86 @param[in] vec `CeedVector` to retrieve state 87 @param[out] state Variable to store state 88 89 @return An error code: 0 - success, otherwise - failure 90 91 @ref Backend 92 **/ 93 int CeedVectorGetState(CeedVector vec, uint64_t *state) { 94 *state = vec->state; 95 return CEED_ERROR_SUCCESS; 96 } 97 98 /** 99 @brief Get the backend data of a `CeedVector` 100 101 @param[in] vec `CeedVector` to retrieve state 102 @param[out] data Variable to store data 103 104 @return An error code: 0 - success, otherwise - failure 105 106 @ref Backend 107 **/ 108 int CeedVectorGetData(CeedVector vec, void *data) { 109 *(void **)data = vec->data; 110 return CEED_ERROR_SUCCESS; 111 } 112 113 /** 114 @brief Set the backend data of a `CeedVector` 115 116 @param[in,out] vec `CeedVector` to retrieve state 117 @param[in] data Data to set 118 119 @return An error code: 0 - success, otherwise - failure 120 121 @ref Backend 122 **/ 123 int CeedVectorSetData(CeedVector vec, void *data) { 124 vec->data = data; 125 return CEED_ERROR_SUCCESS; 126 } 127 128 /** 129 @brief Increment the reference counter for a `CeedVector` 130 131 @param[in,out] vec `CeedVector` to increment the reference counter 132 133 @return An error code: 0 - success, otherwise - failure 134 135 @ref Backend 136 **/ 137 int CeedVectorReference(CeedVector vec) { 138 vec->ref_count++; 139 return CEED_ERROR_SUCCESS; 140 } 141 142 /// @} 143 144 /// ---------------------------------------------------------------------------- 145 /// CeedVector Public API 146 /// ---------------------------------------------------------------------------- 147 /// @addtogroup CeedVectorUser 148 /// @{ 149 150 /** 151 @brief Create a `CeedVector` of the specified length (does not allocate memory) 152 153 @param[in] ceed `Ceed` object used to create the `CeedVector` 154 @param[in] length Length of vector 155 @param[out] vec Address of the variable where the newly created `CeedVector` will be stored 156 157 @return An error code: 0 - success, otherwise - failure 158 159 @ref User 160 **/ 161 int CeedVectorCreate(Ceed ceed, CeedSize length, CeedVector *vec) { 162 if (!ceed->VectorCreate) { 163 Ceed delegate; 164 165 CeedCall(CeedGetObjectDelegate(ceed, &delegate, "Vector")); 166 CeedCheck(delegate, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not implement VectorCreate"); 167 CeedCall(CeedVectorCreate(delegate, length, vec)); 168 return CEED_ERROR_SUCCESS; 169 } 170 171 CeedCall(CeedCalloc(1, vec)); 172 CeedCall(CeedReferenceCopy(ceed, &(*vec)->ceed)); 173 (*vec)->ref_count = 1; 174 (*vec)->length = length; 175 (*vec)->state = 0; 176 CeedCall(ceed->VectorCreate(length, *vec)); 177 return CEED_ERROR_SUCCESS; 178 } 179 180 /** 181 @brief Copy the pointer to a `CeedVector`. 182 183 Both pointers should be destroyed with @ref CeedVectorDestroy(). 184 185 Note: If the value of `*vec_copy` passed to this function is non-`NULL`, then it is assumed that `*vec_copy` is a pointer to a `CeedVector`. 186 This `CeedVector` will be destroyed if `*vec_copy` is the only reference to this `CeedVector`. 187 188 @param[in] vec `CeedVector` to copy reference to 189 @param[in,out] vec_copy Variable to store copied reference 190 191 @return An error code: 0 - success, otherwise - failure 192 193 @ref User 194 **/ 195 int CeedVectorReferenceCopy(CeedVector vec, CeedVector *vec_copy) { 196 if (vec != CEED_VECTOR_ACTIVE && vec != CEED_VECTOR_NONE) CeedCall(CeedVectorReference(vec)); 197 CeedCall(CeedVectorDestroy(vec_copy)); 198 *vec_copy = vec; 199 return CEED_ERROR_SUCCESS; 200 } 201 202 /** 203 @brief Copy a `CeedVector` into a different `CeedVector`. 204 205 @param[in] vec `CeedVector` to copy 206 @param[in,out] vec_copy `CeedVector` to copy array into 207 208 @return An error code: 0 - success, otherwise - failure 209 210 @ref User 211 **/ 212 int CeedVectorCopy(CeedVector vec, CeedVector vec_copy) { 213 Ceed ceed; 214 CeedMemType mem_type, mem_type_copy; 215 CeedScalar *array; 216 217 // Get the preferred memory type 218 CeedCall(CeedVectorGetCeed(vec, &ceed)); 219 CeedCall(CeedGetPreferredMemType(ceed, &mem_type)); 220 221 // Get the preferred memory type 222 CeedCall(CeedVectorGetCeed(vec_copy, &ceed)); 223 CeedCall(CeedGetPreferredMemType(ceed, &mem_type_copy)); 224 225 // Check that both have same memory type 226 if (mem_type != mem_type_copy) mem_type = CEED_MEM_HOST; 227 228 // Check compatible lengths 229 { 230 CeedSize length_vec, length_copy; 231 232 CeedCall(CeedVectorGetLength(vec, &length_vec)); 233 CeedCall(CeedVectorGetLength(vec_copy, &length_copy)); 234 CeedCheck(length_vec == length_copy, ceed, CEED_ERROR_INCOMPATIBLE, "CeedVectors must have the same length to copy"); 235 } 236 237 // Copy the values from vec to vec_copy 238 CeedCall(CeedVectorGetArray(vec, mem_type, &array)); 239 CeedCall(CeedVectorSetArray(vec_copy, mem_type, CEED_COPY_VALUES, array)); 240 241 CeedCall(CeedVectorRestoreArray(vec, &array)); 242 return CEED_ERROR_SUCCESS; 243 } 244 245 /** 246 @brief Copy a strided portion of `CeedVector` contents into a different `CeedVector` 247 248 @param[in] vec `CeedVector` to copy 249 @param[in] start First index to copy 250 @param[in] step Stride between indices to copy 251 @param[in,out] vec_copy `CeedVector` to copy values to 252 253 @return An error code: 0 - success, otherwise - failure 254 255 @ref User 256 **/ 257 int CeedVectorCopyStrided(CeedVector vec, CeedSize start, CeedInt step, CeedVector vec_copy) { 258 CeedSize length; 259 const CeedScalar *array = NULL; 260 CeedScalar *array_copy = NULL; 261 262 // Backend version 263 if (vec->CopyStrided && vec_copy->CopyStrided) { 264 CeedCall(vec->CopyStrided(vec, start, step, vec_copy)); 265 vec_copy->state += 2; 266 return CEED_ERROR_SUCCESS; 267 } 268 269 // Get length 270 { 271 CeedSize length_vec, length_copy; 272 273 CeedCall(CeedVectorGetLength(vec, &length_vec)); 274 CeedCall(CeedVectorGetLength(vec_copy, &length_copy)); 275 if (length_vec <= 0 || length_copy <= 0) return CEED_ERROR_SUCCESS; 276 length = length_vec < length_copy ? length_vec : length_copy; 277 } 278 279 // Copy 280 CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &array)); 281 CeedCall(CeedVectorGetArray(vec_copy, CEED_MEM_HOST, &array_copy)); 282 for (CeedSize i = start; i < length; i += step) array_copy[i] = array[i]; 283 284 // Cleanup 285 CeedCall(CeedVectorRestoreArrayRead(vec, &array)); 286 CeedCall(CeedVectorRestoreArray(vec_copy, &array_copy)); 287 return CEED_ERROR_SUCCESS; 288 } 289 290 /** 291 @brief Set the array used by a `CeedVector`, freeing any previously allocated array if applicable. 292 293 The backend may copy values to a different @ref CeedMemType, such as during @ref CeedOperatorApply(). 294 See also @ref CeedVectorSyncArray() and @ref CeedVectorTakeArray(). 295 296 @param[in,out] vec `CeedVector` 297 @param[in] mem_type Memory type of the array being passed 298 @param[in] copy_mode Copy mode for the array 299 @param[in] array Array to be used, or `NULL` with @ref CEED_COPY_VALUES to have the library allocate 300 301 @return An error code: 0 - success, otherwise - failure 302 303 @ref User 304 **/ 305 int CeedVectorSetArray(CeedVector vec, CeedMemType mem_type, CeedCopyMode copy_mode, CeedScalar *array) { 306 CeedSize length; 307 Ceed ceed; 308 309 CeedCall(CeedVectorGetCeed(vec, &ceed)); 310 311 CeedCheck(vec->SetArray, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support VectorSetArray"); 312 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use"); 313 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access"); 314 315 CeedCall(CeedVectorGetLength(vec, &length)); 316 if (length > 0) CeedCall(vec->SetArray(vec, mem_type, copy_mode, array)); 317 vec->state += 2; 318 return CEED_ERROR_SUCCESS; 319 } 320 321 /** 322 @brief Set the `CeedVector` to a constant value 323 324 @param[in,out] vec `CeedVector` 325 @param[in] value Value to be used 326 327 @return An error code: 0 - success, otherwise - failure 328 329 @ref User 330 **/ 331 int CeedVectorSetValue(CeedVector vec, CeedScalar value) { 332 Ceed ceed; 333 334 CeedCall(CeedVectorGetCeed(vec, &ceed)); 335 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use"); 336 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access"); 337 338 if (vec->SetValue) { 339 CeedCall(vec->SetValue(vec, value)); 340 vec->state += 2; 341 } else { 342 CeedSize length; 343 CeedScalar *array; 344 345 CeedCall(CeedVectorGetArrayWrite(vec, CEED_MEM_HOST, &array)); 346 CeedCall(CeedVectorGetLength(vec, &length)); 347 for (CeedSize i = 0; i < length; i++) array[i] = value; 348 CeedCall(CeedVectorRestoreArray(vec, &array)); 349 } 350 return CEED_ERROR_SUCCESS; 351 } 352 353 /** 354 @brief Set a portion of a `CeedVector` to a constant value. 355 356 Note: The `CeedVector` must already have valid data set via @ref CeedVectorSetArray() or similar. 357 358 @param[in,out] vec `CeedVector` 359 @param[in] start First index to set 360 @param[in] step Stride between indices to set 361 @param[in] value Value to be used 362 363 @return An error code: 0 - success, otherwise - failure 364 365 @ref User 366 **/ 367 int CeedVectorSetValueStrided(CeedVector vec, CeedSize start, CeedInt step, CeedScalar value) { 368 Ceed ceed; 369 370 CeedCall(CeedVectorGetCeed(vec, &ceed)); 371 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use"); 372 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access"); 373 374 if (vec->SetValueStrided) { 375 CeedCall(vec->SetValueStrided(vec, start, step, value)); 376 vec->state += 2; 377 } else { 378 CeedSize length; 379 CeedScalar *array; 380 381 CeedCall(CeedVectorGetLength(vec, &length)); 382 if (length <= 0) return CEED_ERROR_SUCCESS; 383 CeedCall(CeedVectorGetArray(vec, CEED_MEM_HOST, &array)); 384 for (CeedSize i = start; i < length; i += step) array[i] = value; 385 CeedCall(CeedVectorRestoreArray(vec, &array)); 386 } 387 return CEED_ERROR_SUCCESS; 388 } 389 390 /** 391 @brief Sync the `CeedVector` to a specified `mem_type`. 392 393 This function is used to force synchronization of arrays set with @ref CeedVectorSetArray(). 394 If the requested `mem_type` is already synchronized, this function results in a no-op. 395 396 @param[in,out] vec `CeedVector` 397 @param[in] mem_type @ref CeedMemType to be synced 398 399 @return An error code: 0 - success, otherwise - failure 400 401 @ref User 402 **/ 403 int CeedVectorSyncArray(CeedVector vec, CeedMemType mem_type) { 404 CeedSize length; 405 406 CeedCheck(vec->state % 2 == 0, CeedVectorReturnCeed(vec), CEED_ERROR_ACCESS, "Cannot sync CeedVector, the access lock is already in use"); 407 408 // Don't sync empty array 409 CeedCall(CeedVectorGetLength(vec, &length)); 410 if (length == 0) return CEED_ERROR_SUCCESS; 411 412 if (vec->SyncArray) { 413 CeedCall(vec->SyncArray(vec, mem_type)); 414 } else { 415 const CeedScalar *array; 416 417 CeedCall(CeedVectorGetArrayRead(vec, mem_type, &array)); 418 CeedCall(CeedVectorRestoreArrayRead(vec, &array)); 419 } 420 return CEED_ERROR_SUCCESS; 421 } 422 423 /** 424 @brief Take ownership of the `CeedVector` array set by @ref CeedVectorSetArray() with @ref CEED_USE_POINTER and remove the array from the `CeedVector`. 425 426 The caller is responsible for managing and freeing the array. 427 This function will error if @ref CeedVectorSetArray() was not previously called with @ref CEED_USE_POINTER for the corresponding mem_type. 428 429 @param[in,out] vec `CeedVector` 430 @param[in] mem_type Memory type on which to take the array. 431 If the backend uses a different memory type, this will perform a copy. 432 @param[out] array Array on memory type `mem_type`, or `NULL` if array pointer is not required 433 434 @return An error code: 0 - success, otherwise - failure 435 436 @ref User 437 **/ 438 int CeedVectorTakeArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) { 439 CeedSize length; 440 CeedScalar *temp_array = NULL; 441 Ceed ceed; 442 443 CeedCall(CeedVectorGetCeed(vec, &ceed)); 444 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, the access lock is already in use"); 445 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot take CeedVector array, a process has read access"); 446 447 CeedCall(CeedVectorGetLength(vec, &length)); 448 if (length > 0) { 449 bool has_borrowed_array_of_type = true, has_valid_array = true; 450 451 CeedCall(CeedVectorHasBorrowedArrayOfType(vec, mem_type, &has_borrowed_array_of_type)); 452 CeedCheck(has_borrowed_array_of_type, ceed, CEED_ERROR_BACKEND, "CeedVector has no borrowed %s array, must set array with CeedVectorSetArray", 453 CeedMemTypes[mem_type]); 454 455 CeedCall(CeedVectorHasValidArray(vec, &has_valid_array)); 456 CeedCheck(has_valid_array, ceed, CEED_ERROR_BACKEND, 457 "CeedVector has no valid data to take, must set data with CeedVectorSetValue or CeedVectorSetArray"); 458 459 CeedCall(vec->TakeArray(vec, mem_type, &temp_array)); 460 } 461 if (array) (*array) = temp_array; 462 return CEED_ERROR_SUCCESS; 463 } 464 465 /** 466 @brief Get read/write access to a `CeedVector` via the specified memory type. 467 468 Restore access with @ref CeedVectorRestoreArray(). 469 470 @param[in,out] vec `CeedVector` to access 471 @param[in] mem_type Memory type on which to access the array. 472 If the backend uses a different memory type, this will perform a copy. 473 @param[out] array Array on memory type `mem_type` 474 475 @note The @ref CeedVectorGetArray() and @ref CeedVectorRestoreArray() functions provide access to array pointers in the desired memory space. 476 Pairing get/restore allows the `CeedVector` to track access, thus knowing if norms or other operations may need to be recomputed. 477 478 @return An error code: 0 - success, otherwise - failure 479 480 @ref User 481 **/ 482 int CeedVectorGetArray(CeedVector vec, CeedMemType mem_type, CeedScalar **array) { 483 CeedSize length; 484 Ceed ceed; 485 486 CeedCall(CeedVectorGetCeed(vec, &ceed)); 487 CeedCheck(vec->GetArray, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArray"); 488 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use"); 489 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access"); 490 491 CeedCall(CeedVectorGetLength(vec, &length)); 492 if (length > 0) { 493 bool has_valid_array = true; 494 495 CeedCall(CeedVectorHasValidArray(vec, &has_valid_array)); 496 CeedCheck(has_valid_array, ceed, CEED_ERROR_BACKEND, 497 "CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray"); 498 499 CeedCall(vec->GetArray(vec, mem_type, array)); 500 } else { 501 *array = NULL; 502 } 503 vec->state++; 504 return CEED_ERROR_SUCCESS; 505 } 506 507 /** 508 @brief Get read-only access to a `CeedVector` via the specified memory type. 509 510 Restore access with @ref CeedVectorRestoreArrayRead(). 511 512 @param[in] vec `CeedVector` to access 513 @param[in] mem_type Memory type on which to access the array. 514 If the backend uses a different memory type, this will perform a copy (possibly cached). 515 @param[out] array Array on memory type `mem_type` 516 517 @return An error code: 0 - success, otherwise - failure 518 519 @ref User 520 **/ 521 int CeedVectorGetArrayRead(CeedVector vec, CeedMemType mem_type, const CeedScalar **array) { 522 CeedSize length; 523 Ceed ceed; 524 525 CeedCall(CeedVectorGetCeed(vec, &ceed)); 526 CeedCheck(vec->GetArrayRead, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support GetArrayRead"); 527 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector read-only array access, the access lock is already in use"); 528 529 CeedCall(CeedVectorGetLength(vec, &length)); 530 if (length > 0) { 531 bool has_valid_array = true; 532 533 CeedCall(CeedVectorHasValidArray(vec, &has_valid_array)); 534 CeedCheck(has_valid_array, ceed, CEED_ERROR_BACKEND, 535 "CeedVector has no valid data to read, must set data with CeedVectorSetValue or CeedVectorSetArray"); 536 537 CeedCall(vec->GetArrayRead(vec, mem_type, array)); 538 } else { 539 *array = NULL; 540 } 541 vec->num_readers++; 542 return CEED_ERROR_SUCCESS; 543 } 544 545 /** 546 @brief Get write access to a `CeedVector` via the specified memory type. 547 548 Restore access with @ref CeedVectorRestoreArray(). 549 All old values should be assumed to be invalid. 550 551 @param[in,out] vec `CeedVector` to access 552 @param[in] mem_type Memory type on which to access the array. 553 @param[out] array Array on memory type `mem_type` 554 555 @return An error code: 0 - success, otherwise - failure 556 557 @ref User 558 **/ 559 int CeedVectorGetArrayWrite(CeedVector vec, CeedMemType mem_type, CeedScalar **array) { 560 CeedSize length; 561 Ceed ceed; 562 563 CeedCall(CeedVectorGetCeed(vec, &ceed)); 564 CeedCheck(vec->GetArrayWrite, ceed, CEED_ERROR_UNSUPPORTED, "Backend does not support CeedVectorGetArrayWrite"); 565 CeedCheck(vec->state % 2 == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, the access lock is already in use"); 566 CeedCheck(vec->num_readers == 0, ceed, CEED_ERROR_ACCESS, "Cannot grant CeedVector array access, a process has read access"); 567 568 CeedCall(CeedVectorGetLength(vec, &length)); 569 if (length > 0) { 570 CeedCall(vec->GetArrayWrite(vec, mem_type, array)); 571 } else { 572 *array = NULL; 573 } 574 vec->state++; 575 return CEED_ERROR_SUCCESS; 576 } 577 578 /** 579 @brief Restore an array obtained using @ref CeedVectorGetArray() or @ref CeedVectorGetArrayWrite() 580 581 @param[in,out] vec `CeedVector` to restore 582 @param[in,out] array Array of vector data 583 584 @return An error code: 0 - success, otherwise - failure 585 586 @ref User 587 **/ 588 int CeedVectorRestoreArray(CeedVector vec, CeedScalar **array) { 589 CeedSize length; 590 591 CeedCheck(vec->state % 2 == 1, CeedVectorReturnCeed(vec), CEED_ERROR_ACCESS, "Cannot restore CeedVector array access, access was not granted"); 592 CeedCall(CeedVectorGetLength(vec, &length)); 593 if (length > 0 && vec->RestoreArray) CeedCall(vec->RestoreArray(vec)); 594 *array = NULL; 595 vec->state++; 596 return CEED_ERROR_SUCCESS; 597 } 598 599 /** 600 @brief Restore an array obtained using @ref CeedVectorGetArrayRead() 601 602 @param[in] vec `CeedVector` to restore 603 @param[in,out] array Array of vector data 604 605 @return An error code: 0 - success, otherwise - failure 606 607 @ref User 608 **/ 609 int CeedVectorRestoreArrayRead(CeedVector vec, const CeedScalar **array) { 610 CeedSize length; 611 612 CeedCheck(vec->num_readers > 0, CeedVectorReturnCeed(vec), CEED_ERROR_ACCESS, 613 "Cannot restore CeedVector array read access, access was not granted"); 614 vec->num_readers--; 615 CeedCall(CeedVectorGetLength(vec, &length)); 616 if (length > 0 && vec->num_readers == 0 && vec->RestoreArrayRead) CeedCall(vec->RestoreArrayRead(vec)); 617 *array = NULL; 618 return CEED_ERROR_SUCCESS; 619 } 620 621 /** 622 @brief Get the norm of a `CeedVector`. 623 624 Note: This operation is local to the `CeedVector`. 625 This function will likely not provide the desired results for the norm of the libCEED portion of a parallel vector or a `CeedVector` with duplicated or hanging nodes. 626 627 @param[in] vec `CeedVector` to retrieve maximum value 628 @param[in] norm_type Norm type @ref CEED_NORM_1, @ref CEED_NORM_2, or @ref CEED_NORM_MAX 629 @param[out] norm Variable to store norm value 630 631 @return An error code: 0 - success, otherwise - failure 632 633 @ref User 634 **/ 635 int CeedVectorNorm(CeedVector vec, CeedNormType norm_type, CeedScalar *norm) { 636 bool has_valid_array = true; 637 CeedSize length; 638 639 CeedCall(CeedVectorHasValidArray(vec, &has_valid_array)); 640 CeedCheck(has_valid_array, CeedVectorReturnCeed(vec), CEED_ERROR_BACKEND, 641 "CeedVector has no valid data to compute norm, must set data with CeedVectorSetValue or CeedVectorSetArray"); 642 643 CeedCall(CeedVectorGetLength(vec, &length)); 644 if (length == 0) { 645 *norm = 0; 646 return CEED_ERROR_SUCCESS; 647 } 648 649 // Backend impl for GPU, if added 650 if (vec->Norm) { 651 CeedCall(vec->Norm(vec, norm_type, norm)); 652 return CEED_ERROR_SUCCESS; 653 } 654 655 const CeedScalar *array; 656 CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &array)); 657 assert(array); 658 659 *norm = 0.; 660 switch (norm_type) { 661 case CEED_NORM_1: 662 for (CeedSize i = 0; i < length; i++) { 663 *norm += fabs(array[i]); 664 } 665 break; 666 case CEED_NORM_2: 667 for (CeedSize i = 0; i < length; i++) { 668 *norm += fabs(array[i]) * fabs(array[i]); 669 } 670 break; 671 case CEED_NORM_MAX: 672 for (CeedSize i = 0; i < length; i++) { 673 const CeedScalar abs_v_i = fabs(array[i]); 674 *norm = *norm > abs_v_i ? *norm : abs_v_i; 675 } 676 } 677 if (norm_type == CEED_NORM_2) *norm = sqrt(*norm); 678 679 CeedCall(CeedVectorRestoreArrayRead(vec, &array)); 680 return CEED_ERROR_SUCCESS; 681 } 682 683 /** 684 @brief Compute `x = alpha x` 685 686 @param[in,out] x `CeedVector` for scaling 687 @param[in] alpha scaling factor 688 689 @return An error code: 0 - success, otherwise - failure 690 691 @ref User 692 **/ 693 int CeedVectorScale(CeedVector x, CeedScalar alpha) { 694 bool has_valid_array = true; 695 CeedSize length; 696 CeedScalar *x_array = NULL; 697 698 CeedCall(CeedVectorHasValidArray(x, &has_valid_array)); 699 CeedCheck(has_valid_array, CeedVectorReturnCeed(x), CEED_ERROR_BACKEND, 700 "CeedVector has no valid data to scale, must set data with CeedVectorSetValue or CeedVectorSetArray"); 701 702 // Return early for empty vector 703 CeedCall(CeedVectorGetLength(x, &length)); 704 if (length == 0) return CEED_ERROR_SUCCESS; 705 706 // Backend implementation 707 if (x->Scale) return x->Scale(x, alpha); 708 709 // Default implementation 710 CeedCall(CeedVectorGetArray(x, CEED_MEM_HOST, &x_array)); 711 assert(x_array); 712 for (CeedSize i = 0; i < length; i++) x_array[i] *= alpha; 713 CeedCall(CeedVectorRestoreArray(x, &x_array)); 714 return CEED_ERROR_SUCCESS; 715 } 716 717 /** 718 @brief Compute `y = alpha x + y` 719 720 @param[in,out] y target `CeedVector` for sum 721 @param[in] alpha scaling factor 722 @param[in] x second `CeedVector`, must be different than ``y` 723 724 @return An error code: 0 - success, otherwise - failure 725 726 @ref User 727 **/ 728 int CeedVectorAXPY(CeedVector y, CeedScalar alpha, CeedVector x) { 729 bool has_valid_array_x = true, has_valid_array_y = true; 730 CeedSize length_x, length_y; 731 CeedScalar *y_array = NULL; 732 CeedScalar const *x_array = NULL; 733 Ceed ceed, ceed_parent_x, ceed_parent_y; 734 735 CeedCall(CeedVectorGetCeed(y, &ceed)); 736 CeedCall(CeedVectorGetLength(y, &length_y)); 737 CeedCall(CeedVectorGetLength(x, &length_x)); 738 CeedCheck(length_x == length_y, ceed, CEED_ERROR_UNSUPPORTED, 739 "Cannot add vector of different lengths." 740 " x length: %" CeedSize_FMT " y length: %" CeedSize_FMT, 741 length_x, length_y); 742 CeedCheck(x != y, ceed, CEED_ERROR_UNSUPPORTED, "Cannot use same vector for x and y in CeedVectorAXPY"); 743 744 CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x)); 745 CeedCheck(has_valid_array_x, ceed, CEED_ERROR_BACKEND, 746 "CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 747 CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y)); 748 CeedCheck(has_valid_array_y, ceed, CEED_ERROR_BACKEND, 749 "CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 750 751 CeedCall(CeedGetParent(x->ceed, &ceed_parent_x)); 752 CeedCall(CeedGetParent(y->ceed, &ceed_parent_y)); 753 CeedCheck(ceed_parent_x == ceed_parent_y, ceed, CEED_ERROR_INCOMPATIBLE, "Vectors x and y must be created by the same Ceed context"); 754 755 // Return early for empty vectors 756 if (length_y == 0) return CEED_ERROR_SUCCESS; 757 758 // Backend implementation 759 if (y->AXPY) { 760 CeedCall(y->AXPY(y, alpha, x)); 761 return CEED_ERROR_SUCCESS; 762 } 763 764 // Default implementation 765 CeedCall(CeedVectorGetArray(y, CEED_MEM_HOST, &y_array)); 766 CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array)); 767 768 assert(x_array); 769 assert(y_array); 770 771 for (CeedSize i = 0; i < length_y; i++) y_array[i] += alpha * x_array[i]; 772 773 CeedCall(CeedVectorRestoreArray(y, &y_array)); 774 CeedCall(CeedVectorRestoreArrayRead(x, &x_array)); 775 return CEED_ERROR_SUCCESS; 776 } 777 778 /** 779 @brief Compute `y = alpha x + beta y` 780 781 @param[in,out] y target `CeedVector` for sum 782 @param[in] alpha first scaling factor 783 @param[in] beta second scaling factor 784 @param[in] x second `CeedVector`, must be different than `y` 785 786 @return An error code: 0 - success, otherwise - failure 787 788 @ref User 789 **/ 790 int CeedVectorAXPBY(CeedVector y, CeedScalar alpha, CeedScalar beta, CeedVector x) { 791 bool has_valid_array_x = true, has_valid_array_y = true; 792 CeedSize length_x, length_y; 793 CeedScalar *y_array = NULL; 794 CeedScalar const *x_array = NULL; 795 Ceed ceed, ceed_parent_x, ceed_parent_y; 796 797 CeedCall(CeedVectorGetCeed(y, &ceed)); 798 799 CeedCall(CeedVectorGetLength(y, &length_y)); 800 CeedCall(CeedVectorGetLength(x, &length_x)); 801 CeedCheck(length_x == length_y, ceed, CEED_ERROR_UNSUPPORTED, 802 "Cannot add vector of different lengths." 803 " x length: %" CeedSize_FMT " y length: %" CeedSize_FMT, 804 length_x, length_y); 805 CeedCheck(x != y, ceed, CEED_ERROR_UNSUPPORTED, "Cannot use same vector for x and y in CeedVectorAXPBY"); 806 807 CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x)); 808 CeedCheck(has_valid_array_x, ceed, CEED_ERROR_BACKEND, 809 "CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 810 CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y)); 811 CeedCheck(has_valid_array_y, ceed, CEED_ERROR_BACKEND, 812 "CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 813 814 CeedCall(CeedGetParent(x->ceed, &ceed_parent_x)); 815 CeedCall(CeedGetParent(y->ceed, &ceed_parent_y)); 816 CeedCheck(ceed_parent_x == ceed_parent_y, ceed, CEED_ERROR_INCOMPATIBLE, "Vectors x and y must be created by the same Ceed context"); 817 818 // Return early for empty vectors 819 if (length_y == 0) return CEED_ERROR_SUCCESS; 820 821 // Backend implementation 822 if (y->AXPBY) { 823 CeedCall(y->AXPBY(y, alpha, beta, x)); 824 return CEED_ERROR_SUCCESS; 825 } 826 827 // Default implementation 828 CeedCall(CeedVectorGetArray(y, CEED_MEM_HOST, &y_array)); 829 CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array)); 830 831 assert(x_array); 832 assert(y_array); 833 834 for (CeedSize i = 0; i < length_y; i++) y_array[i] = alpha * x_array[i] + beta * y_array[i]; 835 836 CeedCall(CeedVectorRestoreArray(y, &y_array)); 837 CeedCall(CeedVectorRestoreArrayRead(x, &x_array)); 838 return CEED_ERROR_SUCCESS; 839 } 840 841 /** 842 @brief Compute the pointwise multiplication \f$w = x .* y\f$. 843 844 Any subset of `x`, `y`, and `w` may be the same `CeedVector`. 845 846 @param[out] w target `CeedVector` for the product 847 @param[in] x first `CeedVector` for product 848 @param[in] y second `CeedVector` for the product 849 850 @return An error code: 0 - success, otherwise - failure 851 852 @ref User 853 **/ 854 int CeedVectorPointwiseMult(CeedVector w, CeedVector x, CeedVector y) { 855 bool has_valid_array_x = true, has_valid_array_y = true; 856 CeedScalar *w_array = NULL; 857 CeedScalar const *x_array = NULL, *y_array = NULL; 858 CeedSize length_w, length_x, length_y; 859 Ceed ceed, ceed_parent_w, ceed_parent_x, ceed_parent_y; 860 861 CeedCall(CeedVectorGetCeed(w, &ceed)); 862 CeedCall(CeedVectorGetLength(w, &length_w)); 863 CeedCall(CeedVectorGetLength(x, &length_x)); 864 CeedCall(CeedVectorGetLength(y, &length_y)); 865 CeedCheck(length_w == length_x && length_w == length_y, ceed, CEED_ERROR_UNSUPPORTED, 866 "Cannot multiply vectors of different lengths." 867 " x length: %" CeedSize_FMT " y length: %" CeedSize_FMT, 868 length_x, length_y); 869 870 CeedCall(CeedGetParent(w->ceed, &ceed_parent_w)); 871 CeedCall(CeedGetParent(x->ceed, &ceed_parent_x)); 872 CeedCall(CeedGetParent(y->ceed, &ceed_parent_y)); 873 CeedCheck(ceed_parent_w == ceed_parent_x && ceed_parent_w == ceed_parent_y, ceed, CEED_ERROR_INCOMPATIBLE, 874 "Vectors w, x, and y must be created by the same Ceed context"); 875 876 CeedCall(CeedVectorHasValidArray(x, &has_valid_array_x)); 877 CeedCheck(has_valid_array_x, ceed, CEED_ERROR_BACKEND, 878 "CeedVector x has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 879 CeedCall(CeedVectorHasValidArray(y, &has_valid_array_y)); 880 CeedCheck(has_valid_array_y, ceed, CEED_ERROR_BACKEND, 881 "CeedVector y has no valid data, must set data with CeedVectorSetValue or CeedVectorSetArray"); 882 883 // Return early for empty vectors 884 if (length_w == 0) return CEED_ERROR_SUCCESS; 885 886 // Backend implementation 887 if (w->PointwiseMult) { 888 CeedCall(w->PointwiseMult(w, x, y)); 889 return CEED_ERROR_SUCCESS; 890 } 891 892 // Default implementation 893 if (x == w || y == w) { 894 CeedCall(CeedVectorGetArray(w, CEED_MEM_HOST, &w_array)); 895 } else { 896 CeedCall(CeedVectorGetArrayWrite(w, CEED_MEM_HOST, &w_array)); 897 } 898 if (x != w) { 899 CeedCall(CeedVectorGetArrayRead(x, CEED_MEM_HOST, &x_array)); 900 } else { 901 x_array = w_array; 902 } 903 if (y != w && y != x) { 904 CeedCall(CeedVectorGetArrayRead(y, CEED_MEM_HOST, &y_array)); 905 } else if (y == x) { 906 y_array = x_array; 907 } else if (y == w) { 908 y_array = w_array; 909 } 910 911 assert(w_array); 912 assert(x_array); 913 assert(y_array); 914 915 for (CeedSize i = 0; i < length_w; i++) w_array[i] = x_array[i] * y_array[i]; 916 917 if (y != w && y != x) CeedCall(CeedVectorRestoreArrayRead(y, &y_array)); 918 if (x != w) CeedCall(CeedVectorRestoreArrayRead(x, &x_array)); 919 CeedCall(CeedVectorRestoreArray(w, &w_array)); 920 return CEED_ERROR_SUCCESS; 921 } 922 923 /** 924 @brief Take the reciprocal of a `CeedVector`. 925 926 @param[in,out] vec `CeedVector` to take reciprocal 927 928 @return An error code: 0 - success, otherwise - failure 929 930 @ref User 931 **/ 932 int CeedVectorReciprocal(CeedVector vec) { 933 bool has_valid_array = true; 934 CeedSize length; 935 CeedScalar *array; 936 Ceed ceed; 937 938 CeedCall(CeedVectorGetCeed(vec, &ceed)); 939 CeedCall(CeedVectorHasValidArray(vec, &has_valid_array)); 940 CeedCheck(has_valid_array, ceed, CEED_ERROR_BACKEND, 941 "CeedVector has no valid data to compute reciprocal, must set data with CeedVectorSetValue or CeedVectorSetArray"); 942 943 // Check if vector data set 944 CeedCheck(vec->state > 0, ceed, CEED_ERROR_INCOMPLETE, "CeedVector must have data set to take reciprocal"); 945 946 // Return early for empty vector 947 CeedCall(CeedVectorGetLength(vec, &length)); 948 if (length == 0) return CEED_ERROR_SUCCESS; 949 950 // Backend impl for GPU, if added 951 if (vec->Reciprocal) { 952 CeedCall(vec->Reciprocal(vec)); 953 return CEED_ERROR_SUCCESS; 954 } 955 956 CeedCall(CeedVectorGetArray(vec, CEED_MEM_HOST, &array)); 957 for (CeedSize i = 0; i < length; i++) { 958 if (fabs(array[i]) > CEED_EPSILON) array[i] = 1. / array[i]; 959 } 960 961 CeedCall(CeedVectorRestoreArray(vec, &array)); 962 return CEED_ERROR_SUCCESS; 963 } 964 965 /** 966 @brief View a `CeedVector` 967 968 Note: It is safe to use any unsigned values for `start` or `stop` and any nonzero integer for `step`. 969 Any portion of the provided range that is outside the range of valid indices for the `CeedVector` will be ignored. 970 971 @param[in] vec `CeedVector` to view 972 @param[in] start Index of first `CeedVector` entry to view 973 @param[in] stop Index of last `CeedVector` entry to view 974 @param[in] step Step between `CeedVector` entries to view 975 @param[in] fp_fmt Printing format 976 @param[in] stream Filestream to write to 977 978 @return An error code: 0 - success, otherwise - failure 979 980 @ref User 981 **/ 982 int CeedVectorViewRange(CeedVector vec, CeedSize start, CeedSize stop, CeedInt step, const char *fp_fmt, FILE *stream) { 983 char fmt[1024]; 984 CeedSize length; 985 const CeedScalar *x; 986 987 CeedCheck(step != 0, CeedVectorReturnCeed(vec), CEED_ERROR_MINOR, "View range 'step' must be nonzero"); 988 989 CeedCall(CeedVectorGetLength(vec, &length)); 990 fprintf(stream, "CeedVector length %" CeedSize_FMT "\n", length); 991 if (start != 0 || stop != length || step != 1) { 992 fprintf(stream, " start: %" CeedSize_FMT "\n stop: %" CeedSize_FMT "\n step: %" CeedInt_FMT "\n", start, stop, step); 993 } 994 if (start > length) start = length; 995 if (stop > length) stop = length; 996 997 snprintf(fmt, sizeof fmt, " %s\n", fp_fmt ? fp_fmt : "%g"); 998 CeedCall(CeedVectorGetArrayRead(vec, CEED_MEM_HOST, &x)); 999 for (CeedSize i = start; step > 0 ? (i < stop) : (i > stop); i += step) fprintf(stream, fmt, x[i]); 1000 CeedCall(CeedVectorRestoreArrayRead(vec, &x)); 1001 if (stop != length) fprintf(stream, " ...\n"); 1002 return CEED_ERROR_SUCCESS; 1003 } 1004 1005 /** 1006 @brief View a `CeedVector` 1007 1008 @param[in] vec `CeedVector` to view 1009 @param[in] fp_fmt Printing format 1010 @param[in] stream Filestream to write to 1011 1012 @return An error code: 0 - success, otherwise - failure 1013 1014 @ref User 1015 **/ 1016 int CeedVectorView(CeedVector vec, const char *fp_fmt, FILE *stream) { 1017 CeedSize length; 1018 1019 CeedCall(CeedVectorGetLength(vec, &length)); 1020 CeedCall(CeedVectorViewRange(vec, 0, length, 1, fp_fmt, stream)); 1021 return CEED_ERROR_SUCCESS; 1022 } 1023 1024 /** 1025 @brief Get the `Ceed` associated with a `CeedVector` 1026 1027 @param[in] vec `CeedVector` to retrieve state 1028 @param[out] ceed Variable to store `Ceed` 1029 1030 @return An error code: 0 - success, otherwise - failure 1031 1032 @ref Advanced 1033 **/ 1034 int CeedVectorGetCeed(CeedVector vec, Ceed *ceed) { 1035 *ceed = CeedVectorReturnCeed(vec); 1036 return CEED_ERROR_SUCCESS; 1037 } 1038 1039 /** 1040 @brief Return the `Ceed` associated with a `CeedVector` 1041 1042 @param[in] vec `CeedVector` to retrieve state 1043 1044 @return `Ceed` associated with the `vec` 1045 1046 @ref Advanced 1047 **/ 1048 Ceed CeedVectorReturnCeed(CeedVector vec) { return vec->ceed; } 1049 1050 /** 1051 @brief Get the length of a `CeedVector` 1052 1053 @param[in] vec `CeedVector` to retrieve length 1054 @param[out] length Variable to store length 1055 1056 @return An error code: 0 - success, otherwise - failure 1057 1058 @ref User 1059 **/ 1060 int CeedVectorGetLength(CeedVector vec, CeedSize *length) { 1061 *length = vec->length; 1062 return CEED_ERROR_SUCCESS; 1063 } 1064 1065 /** 1066 @brief Destroy a `CeedVector` 1067 1068 @param[in,out] vec `CeedVector` to destroy 1069 1070 @return An error code: 0 - success, otherwise - failure 1071 1072 @ref User 1073 **/ 1074 int CeedVectorDestroy(CeedVector *vec) { 1075 if (!*vec || *vec == CEED_VECTOR_ACTIVE || *vec == CEED_VECTOR_NONE || --(*vec)->ref_count > 0) { 1076 *vec = NULL; 1077 return CEED_ERROR_SUCCESS; 1078 } 1079 CeedCheck((*vec)->state % 2 == 0, (*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, the writable access lock is in use"); 1080 CeedCheck((*vec)->num_readers == 0, (*vec)->ceed, CEED_ERROR_ACCESS, "Cannot destroy CeedVector, a process has read access"); 1081 1082 if ((*vec)->Destroy) CeedCall((*vec)->Destroy(*vec)); 1083 1084 CeedCall(CeedDestroy(&(*vec)->ceed)); 1085 CeedCall(CeedFree(vec)); 1086 return CEED_ERROR_SUCCESS; 1087 } 1088 1089 /// @} 1090