From aba87d69ae8be95ca512bfa311d9c8f428196f21 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 28 May 2026 22:32:01 -0400 Subject: [PATCH 1/9] Add a vector class to GridKit --- GridKit/LinearAlgebra/CMakeLists.txt | 1 + GridKit/LinearAlgebra/Vector/CMakeLists.txt | 7 + GridKit/LinearAlgebra/Vector/Vector.cpp | 1039 +++++++++++++++++ GridKit/LinearAlgebra/Vector/Vector.hpp | 81 ++ tests/UnitTests/LinearAlgebra/CMakeLists.txt | 1 + .../LinearAlgebra/Vector/CMakeLists.txt | 7 + .../LinearAlgebra/Vector/VectorTests.hpp | 427 +++++++ .../LinearAlgebra/Vector/runVectorTests.cpp | 52 + 8 files changed, 1615 insertions(+) create mode 100644 GridKit/LinearAlgebra/Vector/CMakeLists.txt create mode 100644 GridKit/LinearAlgebra/Vector/Vector.cpp create mode 100644 GridKit/LinearAlgebra/Vector/Vector.hpp create mode 100644 tests/UnitTests/LinearAlgebra/Vector/CMakeLists.txt create mode 100644 tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp create mode 100644 tests/UnitTests/LinearAlgebra/Vector/runVectorTests.cpp diff --git a/GridKit/LinearAlgebra/CMakeLists.txt b/GridKit/LinearAlgebra/CMakeLists.txt index 4586c24e9..98d64e05a 100644 --- a/GridKit/LinearAlgebra/CMakeLists.txt +++ b/GridKit/LinearAlgebra/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(SparseMatrix) add_subdirectory(DenseMatrix) +add_subdirectory(Vector) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp DESTINATION include/GridKit/LinearAlgebra) diff --git a/GridKit/LinearAlgebra/Vector/CMakeLists.txt b/GridKit/LinearAlgebra/Vector/CMakeLists.txt new file mode 100644 index 000000000..d4384dc15 --- /dev/null +++ b/GridKit/LinearAlgebra/Vector/CMakeLists.txt @@ -0,0 +1,7 @@ +gridkit_add_library(dense_vector + SOURCES + Vector.cpp + HEADERS + Vector.hpp + LINK_LIBRARIES + GridKit::utilities_logger) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp new file mode 100644 index 000000000..664a1e210 --- /dev/null +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -0,0 +1,1039 @@ +#include +#include + +#include +#include + +namespace GridKit +{ + namespace LinearAlgebra + { + + using out = GridKit::Utilities::Logger; + + /** + * @brief Single vector constructor. + * + * @param[in] n - Number of elements in the vector + */ + template + Vector::Vector(IdxT n) + : n_capacity_(n), + k_(1), + n_size_(n), + gpu_updated_(new bool[1]), + cpu_updated_(new bool[1]) + { + gpu_updated_[0] = false; + cpu_updated_[0] = false; + } + + /** + * @brief Multivector constructor. + * + * @param[in] n - Number of elements in the vector + * @param[in] k - Number of vectors in multivector + */ + template + Vector::Vector(IdxT n, IdxT k) + : n_capacity_(n), + k_(k), + n_size_(n), + gpu_updated_(new bool[k]), + cpu_updated_(new bool[k]) + { + setHostUpdated(false); + setDeviceUpdated(false); + } + + /** + * @brief destructor. + * + */ + template + Vector::~Vector() + { + if (owns_cpu_data_ && h_data_) + mem_.deleteOnHost(h_data_); + if (owns_gpu_data_ && d_data_) + mem_.deleteOnDevice(d_data_); + delete[] gpu_updated_; + delete[] cpu_updated_; + } + + /** + * @brief Get capacity of a single vector. + * + * Vector memory is allocated to `n_capacity_*k_`. This is the maximum + * number of elements that the (multi)vector can hold. + * + * @return `n_capacity_` the maximum number of elements in the vector. + */ + template + IdxT Vector::getCapacity() const + { + return n_capacity_; + } + + /** + * @brief get the number of elements in a single vector. + * + * For vectors with changing sizes, set the vector capacity to + * the maximum expected size. + * + * @return `n_size_` number of elements currently in the vector. + */ + template + IdxT Vector::getSize() const + { + return n_size_; + } + + /** + * @brief Get the number of vectors in multivector. + * + * @return _k_, number of vectors in the multivector, + * or 1 if the vector is not a multivector. + */ + template + IdxT Vector::getNumVectors() const + { + return k_; + } + + /** + * @brief Set the vector data pointer (HOST or DEVICE) to an external data. + * + * @param[in] data - Pointer to data + * @param[in] memspace - Memory space (HOST or DEVICE) + * + * @warning This function DOES NOT ALLOCATE any data, it only assigns the + * pointer. + * + * @warning This is an expert level method. Use only if you know what + * you are doing. + */ + template + int Vector::setData(ScalarT* data, memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + if (owns_cpu_data_ && h_data_) + { + out::error() << "Trying to set vector host values, but the values already set!\n"; + out::error() << "Ignoring setData function call ...\n"; + return 1; + } + h_data_ = data; + setHostUpdated(true); + setDeviceUpdated(false); + owns_cpu_data_ = false; + break; + case DEVICE: + if (owns_gpu_data_ && d_data_) + { + out::error() << "Trying to set vector device values, but the values already set!\n"; + out::error() << "Ignoring setData function call ...\n"; + return 1; + } + d_data_ = data; + setHostUpdated(false); + setDeviceUpdated(true); + owns_gpu_data_ = false; + break; + } + return 0; + } + + /** + * @brief Set the flag to indicate that the data (HOST or DEVICE) has been + * updated. + * + * Use this function if you update vector elements by accessing the raw data + * pointer. + * + * @param[in] memspace - Memory space (HOST or DEVICE) + * + * @warning This is an expert level method. Use only if you know what + * you are doing. + */ + template + int Vector::setDataUpdated(memory::MemorySpace memspace) + { + assert(cpu_updated_ && gpu_updated_ && "Update flags not allocated"); + + using namespace memory; + switch (memspace) + { + case HOST: + setHostUpdated(true); + setDeviceUpdated(false); + break; + case DEVICE: + setHostUpdated(false); + setDeviceUpdated(true); + break; + } + return 0; + } + + /** + * @brief Set the flag to indicate that the data (HOST or DEVICE) for + * vector `j` in the multivector has been updated. + * + * Use this function if you update vector elements by accessing the raw data + * pointer. + * + * @param[in] memspace - Memory space (HOST or DEVICE) + * + * @warning This is an expert level method. Use only if you know what + * you are doing. + */ + template + int Vector::setDataUpdated(IdxT j, memory::MemorySpace memspace) + { + assert(cpu_updated_ && gpu_updated_ && "Update flags not allocated"); + + using namespace memory; + switch (memspace) + { + case HOST: + cpu_updated_[j] = true; + gpu_updated_[j] = false; + break; + case DEVICE: + gpu_updated_[j] = true; + cpu_updated_[j] = false; + break; + } + return 0; + } + + /** + * @brief Copy data from another vector. + * + * @param[in] v - Vector, which data will be copied + * @param[in] memspaceIn - Memory space of the incoming data (HOST or DEVICE) + * @param[in] memspaceOut - Memory space the data will be copied to (HOST or DEVICE) + * + * @pre size of _v_ is equal or larger than the current vector size. + */ + template + int Vector::copyFromExternal(Vector* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut) + { + ScalarT* source_data = source->getData(memspaceIn); + return copyFromExternal(source_data, memspaceIn, memspaceOut); + } + + /** + * @brief Copy vector data from input array. + * + * This function allocates (if necessary) and copies the data. + * + * @param[in] data - Data that is to be copied + * @param[in] memspaceIn - Memory space of the incoming data (HOST or DEVICE) + * @param[in] memspaceOut - Memory space the data will be copied to (HOST or DEVICE) + * + * @return 0 if successful, -1 otherwise. + */ + template + int Vector::copyFromExternal(const ScalarT* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut) + { + int control = -1; + if ((memspaceIn == memory::HOST) && (memspaceOut == memory::HOST)) + { + control = 0; + } + if ((memspaceIn == memory::HOST) && (memspaceOut == memory::DEVICE)) + { + control = 1; + } + if ((memspaceIn == memory::DEVICE) && (memspaceOut == memory::HOST)) + { + control = 2; + } + if ((memspaceIn == memory::DEVICE) && (memspaceOut == memory::DEVICE)) + { + control = 3; + } + + if ((memspaceOut == memory::HOST) && (h_data_ == nullptr)) + { + // allocate first + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + if ((memspaceOut == memory::DEVICE) && (d_data_ == nullptr)) + { + // allocate first + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + + switch (control) + { + case 0: // cpu->cpu + mem_.copyArrayHostToHost(h_data_, source, n_size_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case 2: // gpu->cpu + mem_.copyArrayDeviceToHost(h_data_, source, n_size_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case 1: // cpu->gpu + mem_.copyArrayHostToDevice(d_data_, source, n_size_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + case 3: // gpu->gpu + mem_.copyArrayDeviceToDevice(d_data_, source, n_size_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + default: + return -1; + } + return 0; + } + + /** + * @brief get a pointer to HOST or DEVICE vector data. + * + * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) + * + * @return pointer to the vector data (HOST or DEVICE). In case of multivectors, + * vectors are stored column-wise. + * + * @note This function gives you access to the pointer, not to a copy. + * If you change the values using the pointer, the vector values will + * change too. Make sure to use setDataUpdated function to set the update + * flags correctly after changing the values. + */ + template + ScalarT* Vector::getData(memory::MemorySpace memspace) + { + using memory::DEVICE; + using memory::HOST; + + switch (memspace) + { + case HOST: + if ((cpu_updated_[0] == false) && (gpu_updated_[0] == true)) + { + syncData(memspace); + } + return h_data_; + case DEVICE: + if ((gpu_updated_[0] == false) && (cpu_updated_[0] == true)) + { + syncData(memspace); + } + return d_data_; + default: + return nullptr; + } + } + + /** + * @brief get a pointer to HOST or DEVICE vector data. + * + * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) + * + * @return pointer to the vector data (HOST or DEVICE). In case of multivectors, + * vectors are stored column-wise. + */ + template + const ScalarT* Vector::getData(memory::MemorySpace memspace) const + { + using memory::DEVICE; + using memory::HOST; + + switch (memspace) + { + case HOST: + if (cpu_updated_[0] == false) + { + out::error() << "Trying to get data on the host, but host data is out of date!\n" + << "Use syncData function to sync host data with the device data!\n"; + return nullptr; + } + return h_data_; + case DEVICE: + if (gpu_updated_[0] == false) + { + out::error() << "Trying to get data on the device, but device data is out of date!\n" + << "Use syncData function to sync device data with the host data!\n"; + return nullptr; + } + return d_data_; + default: + return nullptr; + } + } + + /** + * @brief get a pointer to HOST or DEVICE data of a particular vector in a multivector. + * + * @param[in] j - Index of a vector in multivector + * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) + * + * @return pointer to the _i_th vector data (HOST or DEVICE) within a multivector. + * + * @pre `j` < `k_` i.e, `j` is smaller than the total number of vectors in multivector. + * + * @note This function gives you access to the pointer, not to a copy. + * If you change the values using the pointer, the vector values will + * change too. Make sure to use setDataUpdated function to set the update + * flags correctly after changing the values. + */ + template + ScalarT* Vector::getData(IdxT j, memory::MemorySpace memspace) + { + using memory::DEVICE; + using memory::HOST; + + if (k_ <= j) + { + out::error() << "Trying to get data for vector " << j << " in multivector" + << " but there are only " << k_ << " vectors!\n"; + return nullptr; + } + + switch (memspace) + { + case HOST: + if ((cpu_updated_[j] == false) && (gpu_updated_[j] == true)) + { + syncData(j, memspace); + } + return &h_data_[j * n_size_]; + case DEVICE: + if ((gpu_updated_[j] == false) && (cpu_updated_[j] == true)) + { + syncData(j, memspace); + } + return &d_data_[j * n_size_]; + default: + return nullptr; + } + } + + /** + * @brief get a const pointer to HOST or DEVICE data of a particular + * vector in a multivector. + * + * @param[in] j - Index of a vector in multivector + * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) + * + * @return pointer to the _i_th vector data (HOST or DEVICE) within a multivector. + * + * @pre `j` < `k_` i.e, `j` is smaller than the total number of vectors in multivector. + * + */ + template + const ScalarT* Vector::getData(IdxT j, memory::MemorySpace memspace) const + { + using memory::DEVICE; + using memory::HOST; + + if (k_ <= j) + { + out::error() << "Trying to get data for vector " << j << " in multivector" + << " but there are only " << k_ << " vectors!\n"; + return nullptr; + } + + switch (memspace) + { + case HOST: + if (cpu_updated_[j] == false) + { + out::error() << "Trying to get data for vector " << j << " on the host, " + << "but host data is out of date!\n" + << "Use syncData function to sync host data with the device data!\n"; + return nullptr; + } + return &h_data_[j * n_size_]; + case DEVICE: + if (gpu_updated_[j] == false) + { + out::error() << "Trying to get data for vector " << j << " on the device, " + << "but device data is out of date!\n" + << "Use syncData function to sync device data with the host data!\n"; + return nullptr; + } + return &d_data_[j * n_size_]; + default: + return nullptr; + } + } + + /** + * @brief Sync out of date memory space with the updated one. + * + * syncData is the only function that can set data on both HOST and DEVICE + * to the same values. + * + * @param[in] memspaceOut - Memory space to sync + * + * @return 0 if successful, 1 otherwise. + * + * @warning This function can be called only when all vectors in a + * multivector have the same update status. Otherwise, you need to sync + * vectors in a multivector individually. + * + */ + template + int Vector::syncData(memory::MemorySpace memspaceOut) + { + using namespace memory; + + bool all_cpu_updated = cpu_updated_[0]; + bool all_gpu_updated = gpu_updated_[0]; + + // Verify that all vectors in multivector have the same update status. + for (IdxT i = 1; i < k_; ++i) + { + if (gpu_updated_[i] != all_gpu_updated) + { + out::error() << "Trying to sync all multivector data on the device," + << " but individual vectors were updated differently.\n" + << "Use syncData function for individual vectors instead!\n"; + assert(false); + return 1; + } + if (cpu_updated_[i] != all_cpu_updated) + { + out::error() << "Trying to sync all multivector data on the host," + << " but individual vectors were updated differently.\n" + << "Use syncData function for individual vectors instead!\n"; + assert(false); + return 1; + } + } + + switch (memspaceOut) + { + case DEVICE: // cpu -> gpu + if (gpu_updated_[0]) + { + out::error() << "Trying to sync device, but device already up to date!\n"; + assert(!gpu_updated_[0]); + return 1; + } + if (!cpu_updated_[0]) + { + out::error() << "Trying to sync device with host, but host is out of date!\n"; + } + if (d_data_ == nullptr) + { + // allocate first + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + mem_.copyArrayHostToDevice(d_data_, h_data_, n_size_ * k_); + setDeviceUpdated(true); + break; + case HOST: // gpu -> cpu + if (cpu_updated_[0]) + { + out::error() << "Trying to sync host, but host already up to date!\n"; + assert(!cpu_updated_[0]); + return 1; + } + if (!gpu_updated_[0]) + { + out::error() << "Trying to sync host with device, but device is out of date!\n"; + } + if (h_data_ == nullptr) + { + // allocate first + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.copyArrayDeviceToHost(h_data_, d_data_, n_size_ * k_); + setHostUpdated(true); + break; + default: + return 1; + } + return 0; + } + + /** + * @brief Sync out of date memory space with the updated one. + * + * syncData is the only function that can set data on both HOST and DEVICE + * to the same values. + * + * @param[in] memspaceOut - Memory space to sync + * + * @return 0 if successful, 1 otherwise. + * + * @warning This function can be called only when all vectors in a + * multivector have the same update status. Otherwise, you need to sync + * vectors in a multivector individually. + * + */ + template + int Vector::syncData(IdxT j, memory::MemorySpace memspaceOut) + { + using namespace memory; + + switch (memspaceOut) + { + case DEVICE: // cpu->gpu + if (gpu_updated_[j]) + { + out::error() << "Trying to sync device, but device already up to date!\n"; + assert(!gpu_updated_[j]); + return 1; + } + if (!cpu_updated_[j]) + { + out::error() << "Trying to sync device with host, but host is out of date!\n"; + return 1; + } + if (d_data_ == nullptr) + { + // allocate first + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + mem_.copyArrayHostToDevice(&d_data_[j * n_size_], &h_data_[j * n_size_], n_size_); + gpu_updated_[j] = true; + break; + case HOST: // cuda->cpu + if (cpu_updated_[j]) + { + out::error() << "Trying to sync host, but host already up to date!\n"; + assert(!cpu_updated_[j]); + return 1; + } + if (!gpu_updated_[j]) + { + out::error() << "Trying to sync host with device, but device is out of date!\n"; + return 1; + } + if (h_data_ == nullptr) + { + // allocate first + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.copyArrayDeviceToHost(&h_data_[j * n_size_], &d_data_[j * n_size_], n_size_); + cpu_updated_[j] = true; + break; + default: + return 1; + } + return 0; + } + + /** + * @brief Allocate vector data for HOST or DEVICE + * + * @param[in] memspace - Memory space of the data to be allocated + * + */ + template + int Vector::allocate(memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + delete[] h_data_; + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + if (gpu_updated_[0]) + { + cpu_updated_[0] = false; + } + else + { + cpu_updated_[0] = true; + } + break; + case DEVICE: + mem_.deleteOnDevice(d_data_); + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + if (cpu_updated_[0]) + { + gpu_updated_[0] = false; + } + else + { + gpu_updated_[0] = true; + } + break; + } + return 0; + } + + /** + * @brief set vector data to zero. In case of multivectors, entire multivector is set to zero. + * + * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * + */ + template + int Vector::setToZero(memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + if (h_data_ == nullptr) + { + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.setZeroArrayOnHost(h_data_, n_capacity_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case DEVICE: + if (d_data_ == nullptr) + { + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + mem_.setZeroArrayOnDevice(d_data_, n_capacity_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + } + return 0; + } + + /** + * @brief set the data of a single vector in a multivector to zero. + * + * @param[in] i - Index of a vector in a multivector + * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * + * @pre _i_ < _k_ i.e,, _i_ is smaller than the total number of vectors in multivector. + */ + template + int Vector::setToZero(IdxT j, memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + if (h_data_ == nullptr) + { + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.setZeroArrayOnHost(&h_data_[j * n_size_], n_size_); + cpu_updated_[j] = true; + gpu_updated_[j] = false; + break; + case DEVICE: + if (d_data_ == nullptr) + { + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + // TODO: We should not need to access raw data in this class + mem_.setZeroArrayOnDevice(&d_data_[j * n_size_], n_size_); + cpu_updated_[j] = false; + gpu_updated_[j] = true; + break; + } + return 0; + } + + /** + * @brief set vector data to a given constant. + * + * In case of multivectors, entire multivector is set to the constant. + * + * @param[in] C - Constant (real number) + * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * + */ + template + int Vector::setToConst(ScalarT C, memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + if (h_data_ == nullptr) + { + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.setArrayToConstOnHost(h_data_, C, n_size_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case DEVICE: + if (d_data_ == nullptr) + { + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + mem_.setArrayToConstOnDevice(d_data_, C, n_size_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + } + return 0; + } + + /** + * @brief set the data of a single vector in a multivector to a given constant. + * + * @param[in] j - Index of a vector in a multivector + * @param[in] C - Constant (real number) + * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * + * @pre _j_ < _k_ i.e,, _j_ is smaller than the total number of vectors in multivector. + */ + template + int Vector::setToConst(IdxT j, ScalarT C, memory::MemorySpace memspace) + { + using namespace memory; + switch (memspace) + { + case HOST: + if (h_data_ == nullptr) + { + h_data_ = new ScalarT[n_capacity_ * k_]; + owns_cpu_data_ = true; + } + mem_.setArrayToConstOnHost(&h_data_[n_size_ * j], C, n_size_); + cpu_updated_[j] = true; + gpu_updated_[j] = false; + break; + case DEVICE: + if (d_data_ == nullptr) + { + mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + owns_gpu_data_ = true; + } + mem_.setArrayToConstOnDevice(&d_data_[n_size_ * j], C, n_size_); + cpu_updated_[j] = false; + gpu_updated_[j] = true; + break; + } + return 0; + } + + /** + * @brief Resize vector to `new_n_size`. + * + * Use for vectors and multivectors that change size throughout computation. + * + * @note Vector needs to have capacity set to maximum expected size. + * @warning This method is not to be used in vectors who do not own their + * data. + * + * @param[in] new_n_size - New vector length + * + * @return 0 if successful, -1 otherwise. + * + * @pre `new_n_size` <= `n_capacity_` + */ + template + int Vector::resize(IdxT new_n_size) + { + assert(owns_cpu_data_ && owns_gpu_data_ + && "Cannot resize if vector is not owning the data."); + + if (new_n_size > n_capacity_) + { + out::error() << "Trying to resize vector to " << new_n_size + << " elements but memory allocated only for " << n_capacity_ + << " elements.\n"; + return 1; + } + else + { + n_size_ = new_n_size; + return 0; + } + } + + /** + * @brief copy HOST or DEVICE data of a specified vector in a multivector to _dest_. + * + * This function allows to copy data between different memory spaces in one call. + * For example, you can copy data of vector _i_ from HOST to DEVICE, or from DEVICE to HOST. + * + * @param[out] dest - Pointer to the memory to which data is copied + * @param[in] i - Index of a vector in a multivector + * @param[in] memspaceInSrc - Memory space (HOST or DEVICE) of the data to be copied + * @param[in] memspaceOutDst - Memory space (HOST or DEVICE) to which data is copied + * + * @return 0 if successful, -1 otherwise. + * + * @pre _i_ < _k_ i.e,, _i_ is smaller than the total number of vectors in multivector. + * @pre _dest_ is allocated, and the size of _dest_ is at least _n_ (length of a single vector in the multivector). + * @pre _dest_ is allocated in memspaceOutDst memory space. + * @post All elements of the vector _i_ are copied to the array _dest_. + */ + template + int Vector::copyToExternal(ScalarT* dest, IdxT i, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) + { + using namespace memory; + ScalarT* data = getData(i, memspaceSrc); + // Check that the source data is not null and up to date + if (data == nullptr) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not allocated in the source memory space!\n"; + return -1; + } + // Check that the destination memory space is allocated + if (dest == nullptr) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the destination pointer is not allocated!\n"; + return -1; + } + if (i > this->k_) + { + return -1; + } + else + { + switch (memspaceSrc) + { + case HOST: + if (!cpu_updated_[i]) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; + return -1; + } + switch (memspaceDst) + { + case HOST: + mem_.copyArrayHostToHost(dest, data, n_size_); + break; + case DEVICE: + mem_.copyArrayHostToDevice(dest, data, n_size_); + break; + } + break; + case DEVICE: + if (!gpu_updated_[i]) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; + return -1; + } + switch (memspaceDst) + { + case HOST: + mem_.copyArrayDeviceToHost(dest, data, n_size_); + break; + case DEVICE: + mem_.copyArrayDeviceToDevice(dest, data, n_size_); + break; + } + break; + } + } + return 0; + } + + /** + * @brief copy HOST or DEVICE data of multivector to _dest_. + * + * This function allows to copy data between different memory spaces in one call. + * For example, you can copy data of multivector from HOST to DEVICE, or from DEVICE to HOST. + * + * @param[out] dest - Pointer to the memory to which data is copied + * @param[in] memspaceSrc - Memory space (HOST or DEVICE) of the data to be copied + * @param[in] memspaceDst - Memory space (HOST or DEVICE) to which data is copied + * + * @return 0 if successful, -1 otherwise. + * + * @pre _dest_ is allocated, and the size of _dest_ is at least _n_ * _k_ (total length of all vectors in the multivector). + * @pre _dest_ is allocated in memspaceOutDst memory space. + * @post All elements of all vectors in multivector are copied to the array _dest_. + */ + template + int Vector::copyToExternal(ScalarT* dest, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) + { + using namespace memory; + ScalarT* data = this->getData(memspaceSrc); + // Check that the source data is not null and up to date + if (data == nullptr) + { + out::error() << "Trying to copy data for multivector but the data is not allocated in the source memory space!\n"; + return -1; + } + // Check that the destination memory space is allocated + if (dest == nullptr) + { + out::error() << "Trying to copy data for multivector but the destination pointer is not allocated!\n"; + return -1; + } + switch (memspaceSrc) + { + case HOST: + if (!cpu_updated_[0]) + { + out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; + return -1; + } + switch (memspaceDst) + { + case HOST: + mem_.copyArrayHostToHost(dest, data, n_size_ * k_); + break; + case DEVICE: + mem_.copyArrayHostToDevice(dest, data, n_size_ * k_); + break; + } + break; + case DEVICE: + if (!gpu_updated_[0]) + { + out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; + return -1; + } + switch (memspaceDst) + { + case HOST: + mem_.copyArrayDeviceToHost(dest, data, n_size_ * k_); + break; + case DEVICE: + mem_.copyArrayDeviceToDevice(dest, data, n_size_ * k_); + break; + } + break; + } + return 0; + } + + // + // Private methods + // + + template + void Vector::setHostUpdated(bool is_updated) + { + std::fill(cpu_updated_, cpu_updated_ + k_, is_updated); + } + + template + void Vector::setDeviceUpdated(bool is_updated) + { + std::fill(gpu_updated_, gpu_updated_ + k_, is_updated); + } + + // template class Vector; + template class Vector; + // template class Vector; + + } // namespace LinearAlgebra +} // namespace GridKit diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp new file mode 100644 index 000000000..901f7c1dc --- /dev/null +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -0,0 +1,81 @@ +#pragma once +#include + +#include + +namespace GridKit +{ + namespace LinearAlgebra + { + /** + * @brief This class implements vectors (dense arrays) and multivectors and + * some basic utilities (get size, allocate, set data, get data, etc). + * + * + * What you need to know: + * - Multivectors are stored in one array, organized column-wise. A vector + * is a multivector of size 1. + * - There is a mirroring memory approach: the class has DEVICE and HOST + * data pointers. If needed, only one (or none) can be used or allocated. + * Unless triggered directly or by other function, the data is NOT + * automatically updated between HOST and DEVICE. + * - Constructor DOES NOT allocate memory. This has to be done separately. + * - You can get (and set) "raw" data easily, if needed. + * - There is memory ownership utility - vector can own memory (separate + * flags for HOST and DEVICE) or not, depending on how it is used. + * + * @author Kasia Swirydowicz + * @author Slaven Peles + */ + template + class Vector + { + public: + Vector(IdxT n); + Vector(IdxT n, IdxT k); + ~Vector(); + + int copyFromExternal(const ScalarT* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); + int copyFromExternal(Vector* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); + ScalarT* getData(memory::MemorySpace memspace); + ScalarT* getData(IdxT i, memory::MemorySpace memspace); + const ScalarT* getData(memory::MemorySpace memspace) const; + const ScalarT* getData(IdxT i, memory::MemorySpace memspace) const; + + IdxT getCapacity() const; + IdxT getSize() const; + IdxT getNumVectors() const; + + int setDataUpdated(memory::MemorySpace memspace); + int setDataUpdated(IdxT j, memory::MemorySpace memspace); + int setData(ScalarT* data, memory::MemorySpace memspace); + int allocate(memory::MemorySpace memspace); + int setToZero(memory::MemorySpace memspace); + int setToZero(IdxT i, memory::MemorySpace memspace); + int setToConst(ScalarT C, memory::MemorySpace memspace); + int setToConst(IdxT i, ScalarT C, memory::MemorySpace memspace); + int syncData(memory::MemorySpace memspaceOut); + int syncData(IdxT j, memory::MemorySpace memspaceOut); + int resize(IdxT new_n_current); + int copyToExternal(ScalarT* dest, IdxT i, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst); + int copyToExternal(ScalarT* dest, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst); + + private: + void setHostUpdated(bool is_updated); + void setDeviceUpdated(bool is_updated); + + IdxT n_capacity_{0}; ///< vector capacity + IdxT k_{0}; ///< number of vectors in multivector + IdxT n_size_{0}; ///< actual size of the vector + ScalarT* d_data_{nullptr}; ///< DEVICE data array + ScalarT* h_data_{nullptr}; ///< HOST data array + bool* gpu_updated_{nullptr}; ///< DEVICE data flags (updated or not) + bool* cpu_updated_{nullptr}; ///< HOST data flags (updated or not) + + bool owns_gpu_data_{true}; ///< data owneship flag for DEVICE data + bool owns_cpu_data_{true}; ///< data ownership flag for HOST data + + MemoryHandler mem_; ///< Device memory manager object + }; + } // namespace LinearAlgebra +} // namespace GridKit diff --git a/tests/UnitTests/LinearAlgebra/CMakeLists.txt b/tests/UnitTests/LinearAlgebra/CMakeLists.txt index 58e28ddcb..801a7b3b7 100644 --- a/tests/UnitTests/LinearAlgebra/CMakeLists.txt +++ b/tests/UnitTests/LinearAlgebra/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(SparseMatrix) +add_subdirectory(Vector) diff --git a/tests/UnitTests/LinearAlgebra/Vector/CMakeLists.txt b/tests/UnitTests/LinearAlgebra/Vector/CMakeLists.txt new file mode 100644 index 000000000..c97de6e54 --- /dev/null +++ b/tests/UnitTests/LinearAlgebra/Vector/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(test_vector runVectorTests.cpp) +target_link_libraries(test_vector GridKit::dense_vector GridKit::testing) + +add_test(NAME VectorTest COMMAND $) + +install(TARGETS test_vector + RUNTIME DESTINATION bin) diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp new file mode 100644 index 000000000..700e07b3d --- /dev/null +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -0,0 +1,427 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +// #include +// #include +// #include +#include +#include + +namespace GridKit +{ + namespace Testing + { + using namespace LinearAlgebra; + + /** + * @class Tests for vector operations. + */ + template + class VectorTests + { + public: + VectorTests(memory::MemorySpace memspace = memory::HOST) + : memspace_(memspace) + { + } + + virtual ~VectorTests() + { + } + + /** + * @brief Test vector constructor with specified size and number of vectors. + * + * @param[in] N Number of elements in the vector. + * @param[in] k Number of vectors in the multivector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome vectorConstructor(IdxT N, IdxT k) + { + TestStatus status; + status = true; + + Vector x(N, k); + + if (x.getCapacity() != N) + { + std::cout << "The capacity of the vector is " << x.getCapacity() + << ", expected: " << N << "\n"; + status *= false; + } + + if (x.getSize() != N) + { + std::cout << "The size of the vector is " << x.getSize() + << ", expected: " << N << "\n"; + status *= false; + } + + if (x.getNumVectors() != k) + { + std::cout << "The number of vectors in the multivector is " << x.getNumVectors() + << ", expected: " << k << "\n"; + status *= false; + } + + return status.report(__func__); + } + + /** + * @brief Test vector constructor with specified size and default number of vectors (1). + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome vectorConstructor(IdxT N) + { + return vectorConstructor(N, 1); + } + + /** + * @brief Test resizing a vector to a new size. + * + * @param[in] N Current size of the vector. + * @param[in] new_N New size to which the vector should be resized. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome resize(IdxT N, IdxT new_N) + { + TestStatus status; + status = true; + + Vector x(N); + x.allocate(memspace_); + + x.resize(new_N); + + if (x.getSize() != new_N) + { + std::cout << "The size of the vector after resizing is " << x.getSize() + << ", expected: " << new_N << "\n"; + status *= false; + } + + return status.report(__func__); + } + + /** + * @brief Test setting data in a vector from array. + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome setData(IdxT N) + { + TestStatus status; + status = true; + + Vector x(N); + + ScalarT* data = new ScalarT[N]; + for (IdxT i = 0; i < N; ++i) + { + data[i] = 0.1 * (ScalarT) i; + } + x.setData(data, memspace_); + + const ScalarT* x_data = x.getData(memspace_); + + if (x_data == nullptr) + { + std::cout << "The data pointer is null after setting.\n"; + status *= false; + } + else + { + for (IdxT i = 0; i < N; ++i) + { + if (!isEqual(x_data[i], data[i])) + { + std::cout << "The data in the vector is incorrect at index " << i + << ", expected: " << data[i] + << ", got: " << x_data[i] << "\n"; + status *= false; + break; + } + } + } + + delete[] data; + return status.report(__func__); + } + + /** + * @brief Test copying data between vector-array and vector-vector. + * + * This creates an array, copies it to a vector in the current memory space, then + * copies it to another vector in the same memory space, and finally back to a third on the + * HOST. Then, it verifies the content of the final vector. This test only passes if all + * copies are successful. + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome copyFromExternal(IdxT N) + { + TestStatus status; + status = true; + + Vector x(N); + ScalarT* data = new ScalarT[N]; + for (IdxT i = 0; i < N; ++i) + { + data[i] = 0.1 * (ScalarT) i; + } + + // array -> memspace + x.copyFromExternal(data, memory::HOST, memspace_); + + // memspace -> memspace + Vector y(N); + y.copyFromExternal(&x, memspace_, memspace_); + + // memspace -> host + Vector z(N); + z.copyFromExternal(&y, memspace_, memory::HOST); + + const ScalarT* z_data = z.getData(memory::HOST); + + if (z_data == nullptr) + { + std::cout << "The data pointer is null after copying from vector.\n"; + status *= false; + } + else + { + for (IdxT i = 0; i < N; ++i) + { + if (!isEqual(z_data[i], data[i])) + { + std::cout << "The data in the copied vector is incorrect at index " << i + << ", expected: " << data[i] + << ", got: " << z_data[i] << "\n"; + status *= false; + break; + } + } + } + + delete[] data; + return status.report(__func__); + } + + /** + * @brief Test copying data from vector to an array. + * + * This creates a vector, copies data to it, and then copies the data to an array + * in the current memory space. Finally, it uses the MemoryHandler to copy the data + * to HOST for verification. + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome copyToExternal(IdxT N) + { + TestStatus status = true; + + Vector x(N); + ScalarT* data = new ScalarT[N]; + for (int i = 0; i < N; ++i) + { + data[i] = 0.1 * (ScalarT) i; + } + + x.copyFromExternal(data, memory::HOST, memspace_); + + // Copy data to an array on current memspace + ScalarT* dest = new ScalarT[N]; + // second argument is source, third is destination + x.copyToExternal(dest, memspace_, memspace_); + + // Copy to host to verify + ScalarT* dest_h = new ScalarT[N]; + if (memspace_ == memory::DEVICE) + { + mh_.copyArrayDeviceToHost(dest_h, dest, N); + dest = dest_h; + } + else + { + // If we are on HOST, we can use dest directly + delete[] dest_h; + dest_h = dest; + } + + // Verify the copied data + for (int i = 0; i < N; ++i) + { + if (!isEqual(dest_h[i], data[i])) + { + std::cout << "The data in the destination array is incorrect at index " << i + << ", expected: " << data[i] + << ", got: " << dest_h[i] << "\n"; + status *= false; + break; + } + } + + delete[] data; + delete[] dest; + return status.report(__func__); + } + + /** + * @brief Test setting all elements of a vector to a constant value. + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome indicating success or failure of the test. + */ + TestOutcome setToConst(IdxT N) + { + constexpr ScalarT ONE = 1.0; + constexpr ScalarT ZERO = 0.0; + + TestStatus success = true; + + IdxT vector_size = N; + IdxT number_vectors = 3; + + Vector x(vector_size, number_vectors); + + x.setToZero(memspace_); + success *= verifyAnswer(x, ZERO); + + x.setToConst(1, ONE, memspace_); // set vector 1 to ones + success *= verifyAnswer(vector_size, x.getData(0, memspace_), ZERO); + success *= verifyAnswer(vector_size, x.getData(1, memspace_), ONE); + success *= verifyAnswer(vector_size, x.getData(2, memspace_), ZERO); + + x.setToConst(ONE, memspace_); + success *= verifyAnswer(x, ONE); + + x.setToZero(1, memspace_); // set vector 1 to zeros + success *= verifyAnswer(vector_size, x.getData(0, memspace_), ONE); + success *= verifyAnswer(vector_size, x.getData(1, memspace_), ZERO); + success *= verifyAnswer(vector_size, x.getData(2, memspace_), ONE); + + return success.report(__func__); + } + + /** + * @brief Test syncing data between HOST and DEVICE memory spaces. + * + * Creates a vector allocated in the specified memory space, then sync + * to the other memory space and verify that the data is synced correctly. + * + * @param[in] N Number of elements in the vector. + * @return TestOutcome returns a report on the test. + */ + TestOutcome syncData(IdxT N = 4) + { + constexpr ScalarT ONE = 1.0; + constexpr ScalarT ZERO = 0.0; + + TestStatus success; + success = true; + + if (memspace_ == memory::HOST) + { + return success.report(__func__); + } + + IdxT vector_size = N; + IdxT number_vectors = 3; + + Vector x(vector_size, number_vectors); + + // Set all vectors in x on device to ones + x.setToConst(ONE, memspace_); + // Sync host (all ones on the host, as well) + x.syncData(memory::HOST); + // Set vector 1 to all zeros on host + x.setToZero(1, memory::HOST); + // Sync vector 1 on device + x.syncData(1, memspace_); + + // Check what we have on device now is correct + success *= verifyAnswer(vector_size, x.getData(0, memspace_), ONE); + success *= verifyAnswer(vector_size, x.getData(1, memspace_), ZERO); + success *= verifyAnswer(vector_size, x.getData(2, memspace_), ONE); + + return success.report(__func__); + } + + private: + memory::MemorySpace memspace_{memory::HOST}; + MemoryHandler mh_; + + /// Check if vector elements are set to the same number + bool verifyAnswer(Vector& x, ScalarT answer) + { + bool success = true; + + if (memspace_ == memory::DEVICE) + { + x.syncData(memory::HOST); + } + + for (IdxT i = 0; i < x.getSize(); ++i) + { + // std::cout << x->getData("cpu")[i] << "\n"; + if (!isEqual(x.getData(memory::HOST)[i], answer)) + { + std::cout << std::setprecision(16); + success = false; + std::cout << "Solution vector element x[" << i << "] = " << x.getData(memory::HOST)[i] + << ", expected: " << answer << "\n"; + break; + } + } + return success; + } + + /// Check if an array elements are set to the same number + bool verifyAnswer(IdxT size, const ScalarT* data, ScalarT answer) + { + bool success = true; + ScalarT* x = nullptr; + + // If the data is on device copy it to the host + if (memspace_ == memory::DEVICE) + { + mh_.allocateArrayOnHost(&x, size); + mh_.copyArrayDeviceToHost(x, data, size); + // Set `data` to point to the host copy + data = x; + } + + for (size_t i = 0; i < static_cast(size); ++i) + { + if (!isEqual(data[i], answer)) + { + std::cout << std::setprecision(16); + success = false; + std::cout << "Solution vector element x[" << i << "] = " << data[i] + << ", expected: " << answer << "\n"; + break; + } + } + + if (memspace_ == memory::DEVICE) + { + mh_.deleteOnHost(x); + data = nullptr; + } + + return success; + } + }; // class + } // namespace Testing +} // namespace GridKit diff --git a/tests/UnitTests/LinearAlgebra/Vector/runVectorTests.cpp b/tests/UnitTests/LinearAlgebra/Vector/runVectorTests.cpp new file mode 100644 index 000000000..06336eb2d --- /dev/null +++ b/tests/UnitTests/LinearAlgebra/Vector/runVectorTests.cpp @@ -0,0 +1,52 @@ +#include +#include +#include + +#include "VectorTests.hpp" + +int main(int, char**) +{ + GridKit::Testing::TestingResults result; + + { + GridKit::Testing::VectorTests test; + + std::cout << "Running vector tests on CPU:\n"; + result += test.vectorConstructor(50, 5); + result += test.vectorConstructor(50); + + result += test.setData(50); + + result += test.copyFromExternal(50); + // result += test.copyToExternal(50); + + result += test.resize(100, 50); + + result += test.setToConst(50); + result += test.setToConst(50); + } + +#ifdef GRIDKIT_USE_GPU + { + GridKit::Testing::VectorTests test(GridKit::memory::DEVICE); + + std::cout << "Running Testing on GPU:\n"; + result += test.vectorConstructor(50, 5); + result += test.vectorConstructor(50); + + result += test.setData(50); + + result += test.copyFromExternal(50); + // result += test.copyToExternal(50); + + result += test.resize(100, 50); + + result += test.setToConst(50); + result += test.setToConst(50); + result += test.syncData(50); + result += test.syncData(50); + } +#endif + + return result.summary(); +} From 21d8dcdc7c319d18770bda16ef03fc4504b0cff3 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 20:29:30 -0400 Subject: [PATCH 2/9] Refactor Vector class. --- GridKit/LinearAlgebra/Vector/Vector.cpp | 205 ++++++++++-------- GridKit/LinearAlgebra/Vector/Vector.hpp | 24 +- .../LinearAlgebra/Vector/VectorTests.hpp | 4 + 3 files changed, 135 insertions(+), 98 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index 664a1e210..c9495c608 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -107,6 +107,9 @@ namespace GridKit * @param[in] data - Pointer to data * @param[in] memspace - Memory space (HOST or DEVICE) * + * @pre Vector data pointers must be null. If the vector data already + * exists this function returns error message. + * * @warning This function DOES NOT ALLOCATE any data, it only assigns the * pointer. * @@ -117,27 +120,22 @@ namespace GridKit int Vector::setData(ScalarT* data, memory::MemorySpace memspace) { using namespace memory; + if (d_data_ || h_data_) + { + out::error() << "Trying to set vector data, but the data already exists!\n"; + out::error() << "Ignoring setData function call ...\n"; + return 1; + } + switch (memspace) { case HOST: - if (owns_cpu_data_ && h_data_) - { - out::error() << "Trying to set vector host values, but the values already set!\n"; - out::error() << "Ignoring setData function call ...\n"; - return 1; - } h_data_ = data; setHostUpdated(true); setDeviceUpdated(false); owns_cpu_data_ = false; break; case DEVICE: - if (owns_gpu_data_ && d_data_) - { - out::error() << "Trying to set vector device values, but the values already set!\n"; - out::error() << "Ignoring setData function call ...\n"; - return 1; - } d_data_ = data; setHostUpdated(false); setDeviceUpdated(true); @@ -215,16 +213,16 @@ namespace GridKit * @brief Copy data from another vector. * * @param[in] v - Vector, which data will be copied - * @param[in] memspaceIn - Memory space of the incoming data (HOST or DEVICE) - * @param[in] memspaceOut - Memory space the data will be copied to (HOST or DEVICE) + * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) + * @param[in] memspaceDst - Memory space the data will be copied to (HOST or DEVICE) * * @pre size of _v_ is equal or larger than the current vector size. */ template - int Vector::copyFromExternal(Vector* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut) + int Vector::copyFromExternal(Vector* source, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) { - ScalarT* source_data = source->getData(memspaceIn); - return copyFromExternal(source_data, memspaceIn, memspaceOut); + ScalarT* source_data = source->getData(memspaceSrc); + return copyFromExternal(source_data, memspaceSrc, memspaceDst); } /** @@ -233,69 +231,74 @@ namespace GridKit * This function allocates (if necessary) and copies the data. * * @param[in] data - Data that is to be copied - * @param[in] memspaceIn - Memory space of the incoming data (HOST or DEVICE) - * @param[in] memspaceOut - Memory space the data will be copied to (HOST or DEVICE) + * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) + * @param[in] memspaceDst - Memory space the data will be copied to (HOST or DEVICE) * * @return 0 if successful, -1 otherwise. */ template - int Vector::copyFromExternal(const ScalarT* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut) + int Vector::copyFromExternal(const ScalarT* source, + memory::MemorySpace memspaceSrc, + memory::MemorySpace memspaceDst) { - int control = -1; - if ((memspaceIn == memory::HOST) && (memspaceOut == memory::HOST)) - { - control = 0; - } - if ((memspaceIn == memory::HOST) && (memspaceOut == memory::DEVICE)) - { - control = 1; - } - if ((memspaceIn == memory::DEVICE) && (memspaceOut == memory::HOST)) - { - control = 2; - } - if ((memspaceIn == memory::DEVICE) && (memspaceOut == memory::DEVICE)) - { - control = 3; - } - - if ((memspaceOut == memory::HOST) && (h_data_ == nullptr)) + switch (memspaceDst) { - // allocate first - h_data_ = new ScalarT[n_capacity_ * k_]; - owns_cpu_data_ = true; - } - if ((memspaceOut == memory::DEVICE) && (d_data_ == nullptr)) - { - // allocate first - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + case memory::HOST: + if (h_data_ == nullptr) + { + out::error() << "Vector::copyFromExternal - destination memory space" + << " on host not allocated\n"; + return 1; + } + break; + case memory::DEVICE: + if (d_data_ == nullptr) + { + out::error() << "Vector::copyFromExternal - destination memory space" + << " on device not allocated\n"; + return 1; + } + break; } - switch (control) + switch (memspaceSrc) { - case 0: // cpu->cpu - mem_.copyArrayHostToHost(h_data_, source, n_size_ * k_); - setHostUpdated(true); - setDeviceUpdated(false); - break; - case 2: // gpu->cpu - mem_.copyArrayDeviceToHost(h_data_, source, n_size_ * k_); - setHostUpdated(true); - setDeviceUpdated(false); - break; - case 1: // cpu->gpu - mem_.copyArrayHostToDevice(d_data_, source, n_size_ * k_); - setHostUpdated(false); - setDeviceUpdated(true); + case memory::HOST: + switch (memspaceDst) + { + case memory::HOST: + mem_.copyArrayHostToHost(h_data_, source, n_size_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case memory::DEVICE: + mem_.copyArrayHostToDevice(d_data_, source, n_size_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + default: + return -1; + } break; - case 3: // gpu->gpu - mem_.copyArrayDeviceToDevice(d_data_, source, n_size_ * k_); - setHostUpdated(false); - setDeviceUpdated(true); + case memory::DEVICE: + switch (memspaceDst) + { + case memory::HOST: + mem_.copyArrayDeviceToHost(h_data_, source, n_size_ * k_); + setHostUpdated(true); + setDeviceUpdated(false); + break; + case memory::DEVICE: + mem_.copyArrayDeviceToDevice(d_data_, source, n_size_ * k_); + setHostUpdated(false); + setDeviceUpdated(true); + break; + default: + return 1; + } break; default: - return -1; + return 1; } return 0; } @@ -322,15 +325,19 @@ namespace GridKit switch (memspace) { case HOST: - if ((cpu_updated_[0] == false) && (gpu_updated_[0] == true)) + if (cpu_updated_[0] == false) { - syncData(memspace); + out::error() << "Vector::getData - requesting data from host" + << " but host data is stale. Returning nullptr ...\n"; + return nullptr; } return h_data_; case DEVICE: - if ((gpu_updated_[0] == false) && (cpu_updated_[0] == true)) + if (gpu_updated_[0] == false) { - syncData(memspace); + out::error() << "Vector::getData - requesting data from device" + << " but device data is stale. Returning nullptr ...\n"; + return nullptr; } return d_data_; default: @@ -408,13 +415,19 @@ namespace GridKit case HOST: if ((cpu_updated_[j] == false) && (gpu_updated_[j] == true)) { - syncData(j, memspace); + out::error() << "Trying to get data for vector " << j << " on the host, " + << "but host data is out of date!\n" + << "Use syncData function to sync host data with the device data!\n"; + return nullptr; } return &h_data_[j * n_size_]; case DEVICE: if ((gpu_updated_[j] == false) && (cpu_updated_[j] == true)) { - syncData(j, memspace); + out::error() << "Trying to get data for vector " << j << " on the device, " + << "but device data is out of date!\n" + << "Use syncData function to sync device data with the host data!\n"; + return nullptr; } return &d_data_[j * n_size_]; default: @@ -478,7 +491,7 @@ namespace GridKit * syncData is the only function that can set data on both HOST and DEVICE * to the same values. * - * @param[in] memspaceOut - Memory space to sync + * @param[in] memspaceDst - Memory space to sync * * @return 0 if successful, 1 otherwise. * @@ -488,7 +501,7 @@ namespace GridKit * */ template - int Vector::syncData(memory::MemorySpace memspaceOut) + int Vector::syncData(memory::MemorySpace memspaceDst) { using namespace memory; @@ -503,7 +516,6 @@ namespace GridKit out::error() << "Trying to sync all multivector data on the device," << " but individual vectors were updated differently.\n" << "Use syncData function for individual vectors instead!\n"; - assert(false); return 1; } if (cpu_updated_[i] != all_cpu_updated) @@ -511,18 +523,16 @@ namespace GridKit out::error() << "Trying to sync all multivector data on the host," << " but individual vectors were updated differently.\n" << "Use syncData function for individual vectors instead!\n"; - assert(false); return 1; } } - switch (memspaceOut) + switch (memspaceDst) { case DEVICE: // cpu -> gpu if (gpu_updated_[0]) { out::error() << "Trying to sync device, but device already up to date!\n"; - assert(!gpu_updated_[0]); return 1; } if (!cpu_updated_[0]) @@ -542,7 +552,6 @@ namespace GridKit if (cpu_updated_[0]) { out::error() << "Trying to sync host, but host already up to date!\n"; - assert(!cpu_updated_[0]); return 1; } if (!gpu_updated_[0]) @@ -552,7 +561,7 @@ namespace GridKit if (h_data_ == nullptr) { // allocate first - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.copyArrayDeviceToHost(h_data_, d_data_, n_size_ * k_); @@ -570,7 +579,7 @@ namespace GridKit * syncData is the only function that can set data on both HOST and DEVICE * to the same values. * - * @param[in] memspaceOut - Memory space to sync + * @param[in] memspaceDst - Memory space to sync * * @return 0 if successful, 1 otherwise. * @@ -580,17 +589,16 @@ namespace GridKit * */ template - int Vector::syncData(IdxT j, memory::MemorySpace memspaceOut) + int Vector::syncData(IdxT j, memory::MemorySpace memspaceDst) { using namespace memory; - switch (memspaceOut) + switch (memspaceDst) { case DEVICE: // cpu->gpu if (gpu_updated_[j]) { out::error() << "Trying to sync device, but device already up to date!\n"; - assert(!gpu_updated_[j]); return 1; } if (!cpu_updated_[j]) @@ -611,7 +619,6 @@ namespace GridKit if (cpu_updated_[j]) { out::error() << "Trying to sync host, but host already up to date!\n"; - assert(!cpu_updated_[j]); return 1; } if (!gpu_updated_[j]) @@ -622,7 +629,7 @@ namespace GridKit if (h_data_ == nullptr) { // allocate first - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.copyArrayDeviceToHost(&h_data_[j * n_size_], &d_data_[j * n_size_], n_size_); @@ -647,8 +654,13 @@ namespace GridKit switch (memspace) { case HOST: - delete[] h_data_; - h_data_ = new ScalarT[n_capacity_ * k_]; + if (!owns_cpu_data_) + { + out::error() << "Vector::allocate - cannot allocate host data, vector does not own it\n"; + return 1; + } + mem_.deleteOnHost(h_data_); + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; if (gpu_updated_[0]) { @@ -660,6 +672,11 @@ namespace GridKit } break; case DEVICE: + if (!owns_gpu_data_) + { + out::error() << "Vector::allocate - cannot allocate device data, vector does not own it\n"; + return 1; + } mem_.deleteOnDevice(d_data_); mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); owns_gpu_data_ = true; @@ -691,7 +708,7 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.setZeroArrayOnHost(h_data_, n_capacity_ * k_); @@ -729,7 +746,7 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.setZeroArrayOnHost(&h_data_[j * n_size_], n_size_); @@ -769,7 +786,7 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.setArrayToConstOnHost(h_data_, C, n_size_ * k_); @@ -808,7 +825,7 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - h_data_ = new ScalarT[n_capacity_ * k_]; + mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; } mem_.setArrayToConstOnHost(&h_data_[n_size_ * j], C, n_size_); diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index 901f7c1dc..2d033cff2 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -31,12 +31,23 @@ namespace GridKit class Vector { public: + Vector() = default; Vector(IdxT n); Vector(IdxT n, IdxT k); ~Vector(); - int copyFromExternal(const ScalarT* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); - int copyFromExternal(Vector* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); + Vector(const Vector&) = delete; + Vector(Vector&&) = delete; + Vector& operator=(const Vector&) = delete; + Vector& operator=(Vector&&) = delete; + + int copyFromExternal(const ScalarT* source, + memory::MemorySpace memspaceIn, + memory::MemorySpace memspaceOut); + int copyFromExternal(Vector* source, + memory::MemorySpace memspaceIn, + memory::MemorySpace memspaceOut); + ScalarT* getData(memory::MemorySpace memspace); ScalarT* getData(IdxT i, memory::MemorySpace memspace); const ScalarT* getData(memory::MemorySpace memspace) const; @@ -57,8 +68,13 @@ namespace GridKit int syncData(memory::MemorySpace memspaceOut); int syncData(IdxT j, memory::MemorySpace memspaceOut); int resize(IdxT new_n_current); - int copyToExternal(ScalarT* dest, IdxT i, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst); - int copyToExternal(ScalarT* dest, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst); + int copyToExternal(ScalarT* dest, + IdxT i, + memory::MemorySpace memspaceSrc, + memory::MemorySpace memspaceDst); + int copyToExternal(ScalarT* dest, + memory::MemorySpace memspaceSrc, + memory::MemorySpace memspaceDst); private: void setHostUpdated(bool is_updated); diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp index 700e07b3d..85c89a5e2 100644 --- a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -181,14 +181,17 @@ namespace GridKit } // array -> memspace + x.allocate(memspace_); x.copyFromExternal(data, memory::HOST, memspace_); // memspace -> memspace Vector y(N); + y.allocate(memspace_); y.copyFromExternal(&x, memspace_, memspace_); // memspace -> host Vector z(N); + z.allocate(memory::HOST); z.copyFromExternal(&y, memspace_, memory::HOST); const ScalarT* z_data = z.getData(memory::HOST); @@ -238,6 +241,7 @@ namespace GridKit data[i] = 0.1 * (ScalarT) i; } + x.allocate(memspace_); x.copyFromExternal(data, memory::HOST, memspace_); // Copy data to an array on current memspace From 4373fa86e59230e89323b5b87f9644ff6a08401b Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 20:53:24 -0400 Subject: [PATCH 3/9] Further cleanup of Vector class. --- GridKit/LinearAlgebra/Vector/Vector.cpp | 116 ++++++++---------- GridKit/LinearAlgebra/Vector/Vector.hpp | 2 +- .../LinearAlgebra/Vector/VectorTests.hpp | 4 +- 3 files changed, 53 insertions(+), 69 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index c9495c608..c7a38db03 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -219,9 +219,9 @@ namespace GridKit * @pre size of _v_ is equal or larger than the current vector size. */ template - int Vector::copyFromExternal(Vector* source, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) + int Vector::copyFromExternal(const Vector& source, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) { - ScalarT* source_data = source->getData(memspaceSrc); + const ScalarT* source_data = source.getData(memspaceSrc); return copyFromExternal(source_data, memspaceSrc, memspaceDst); } @@ -234,7 +234,7 @@ namespace GridKit * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) * @param[in] memspaceDst - Memory space the data will be copied to (HOST or DEVICE) * - * @return 0 if successful, -1 otherwise. + * @return 0 if successful, 1 otherwise. */ template int Vector::copyFromExternal(const ScalarT* source, @@ -277,7 +277,7 @@ namespace GridKit setDeviceUpdated(true); break; default: - return -1; + return 1; } break; case memory::DEVICE: @@ -538,6 +538,7 @@ namespace GridKit if (!cpu_updated_[0]) { out::error() << "Trying to sync device with host, but host is out of date!\n"; + return 1; } if (d_data_ == nullptr) { @@ -557,6 +558,7 @@ namespace GridKit if (!gpu_updated_[0]) { out::error() << "Trying to sync host with device, but device is out of date!\n"; + return 1; } if (h_data_ == nullptr) { @@ -662,14 +664,7 @@ namespace GridKit mem_.deleteOnHost(h_data_); mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); owns_cpu_data_ = true; - if (gpu_updated_[0]) - { - cpu_updated_[0] = false; - } - else - { - cpu_updated_[0] = true; - } + setHostUpdated(false); break; case DEVICE: if (!owns_gpu_data_) @@ -680,14 +675,7 @@ namespace GridKit mem_.deleteOnDevice(d_data_); mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); owns_gpu_data_ = true; - if (cpu_updated_[0]) - { - gpu_updated_[0] = false; - } - else - { - gpu_updated_[0] = true; - } + setDeviceUpdated(false); break; } return 0; @@ -857,7 +845,7 @@ namespace GridKit * * @param[in] new_n_size - New vector length * - * @return 0 if successful, -1 otherwise. + * @return 0 if successful, 1 otherwise. * * @pre `new_n_size` <= `n_capacity_` */ @@ -892,7 +880,7 @@ namespace GridKit * @param[in] memspaceInSrc - Memory space (HOST or DEVICE) of the data to be copied * @param[in] memspaceOutDst - Memory space (HOST or DEVICE) to which data is copied * - * @return 0 if successful, -1 otherwise. + * @return 0 if successful, 1 otherwise. * * @pre _i_ < _k_ i.e,, _i_ is smaller than the total number of vectors in multivector. * @pre _dest_ is allocated, and the size of _dest_ is at least _n_ (length of a single vector in the multivector). @@ -903,60 +891,56 @@ namespace GridKit int Vector::copyToExternal(ScalarT* dest, IdxT i, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) { using namespace memory; - ScalarT* data = getData(i, memspaceSrc); - // Check that the source data is not null and up to date - if (data == nullptr) + if (i >= k_) { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not allocated in the source memory space!\n"; - return -1; + out::error() << "Trying to copy data for vector " << i << " in multivector but there are only " << k_ << " vectors!\n"; + return 1; } - // Check that the destination memory space is allocated if (dest == nullptr) { out::error() << "Trying to copy data for vector " << i << " in multivector but the destination pointer is not allocated!\n"; - return -1; + return 1; } - if (i > this->k_) + ScalarT* data = getData(i, memspaceSrc); + if (data == nullptr) { - return -1; + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not allocated in the source memory space!\n"; + return 1; } - else + switch (memspaceSrc) { - switch (memspaceSrc) + case HOST: + if (!cpu_updated_[i]) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; + return 1; + } + switch (memspaceDst) { case HOST: - if (!cpu_updated_[i]) - { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; - return -1; - } - switch (memspaceDst) - { - case HOST: - mem_.copyArrayHostToHost(dest, data, n_size_); - break; - case DEVICE: - mem_.copyArrayHostToDevice(dest, data, n_size_); - break; - } + mem_.copyArrayHostToHost(dest, data, n_size_); break; case DEVICE: - if (!gpu_updated_[i]) - { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; - return -1; - } - switch (memspaceDst) - { - case HOST: - mem_.copyArrayDeviceToHost(dest, data, n_size_); - break; - case DEVICE: - mem_.copyArrayDeviceToDevice(dest, data, n_size_); - break; - } + mem_.copyArrayHostToDevice(dest, data, n_size_); break; } + break; + case DEVICE: + if (!gpu_updated_[i]) + { + out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; + return 1; + } + switch (memspaceDst) + { + case HOST: + mem_.copyArrayDeviceToHost(dest, data, n_size_); + break; + case DEVICE: + mem_.copyArrayDeviceToDevice(dest, data, n_size_); + break; + } + break; } return 0; } @@ -971,7 +955,7 @@ namespace GridKit * @param[in] memspaceSrc - Memory space (HOST or DEVICE) of the data to be copied * @param[in] memspaceDst - Memory space (HOST or DEVICE) to which data is copied * - * @return 0 if successful, -1 otherwise. + * @return 0 if successful, 1 otherwise. * * @pre _dest_ is allocated, and the size of _dest_ is at least _n_ * _k_ (total length of all vectors in the multivector). * @pre _dest_ is allocated in memspaceOutDst memory space. @@ -986,13 +970,13 @@ namespace GridKit if (data == nullptr) { out::error() << "Trying to copy data for multivector but the data is not allocated in the source memory space!\n"; - return -1; + return 1; } // Check that the destination memory space is allocated if (dest == nullptr) { out::error() << "Trying to copy data for multivector but the destination pointer is not allocated!\n"; - return -1; + return 1; } switch (memspaceSrc) { @@ -1000,7 +984,7 @@ namespace GridKit if (!cpu_updated_[0]) { out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; - return -1; + return 1; } switch (memspaceDst) { @@ -1016,7 +1000,7 @@ namespace GridKit if (!gpu_updated_[0]) { out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; - return -1; + return 1; } switch (memspaceDst) { diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index 2d033cff2..ac08d2ca0 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -44,7 +44,7 @@ namespace GridKit int copyFromExternal(const ScalarT* source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); - int copyFromExternal(Vector* source, + int copyFromExternal(const Vector& source, memory::MemorySpace memspaceIn, memory::MemorySpace memspaceOut); diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp index 85c89a5e2..00a61c18f 100644 --- a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -187,12 +187,12 @@ namespace GridKit // memspace -> memspace Vector y(N); y.allocate(memspace_); - y.copyFromExternal(&x, memspace_, memspace_); + y.copyFromExternal(x, memspace_, memspace_); // memspace -> host Vector z(N); z.allocate(memory::HOST); - z.copyFromExternal(&y, memspace_, memory::HOST); + z.copyFromExternal(y, memspace_, memory::HOST); const ScalarT* z_data = z.getData(memory::HOST); From c008d68d1c3c4ec638b899036d073123bef952f7 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:00:01 -0400 Subject: [PATCH 4/9] Remove lazy allocations --- GridKit/LinearAlgebra/Vector/Vector.cpp | 52 +++++++++---------- .../LinearAlgebra/Vector/VectorTests.hpp | 5 ++ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index c7a38db03..299784250 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -542,9 +542,8 @@ namespace GridKit } if (d_data_ == nullptr) { - // allocate first - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::syncData - device data not allocated\n"; + return 1; } mem_.copyArrayHostToDevice(d_data_, h_data_, n_size_ * k_); setDeviceUpdated(true); @@ -562,9 +561,8 @@ namespace GridKit } if (h_data_ == nullptr) { - // allocate first - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::syncData - host data not allocated\n"; + return 1; } mem_.copyArrayDeviceToHost(h_data_, d_data_, n_size_ * k_); setHostUpdated(true); @@ -610,9 +608,8 @@ namespace GridKit } if (d_data_ == nullptr) { - // allocate first - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::syncData - device data not allocated\n"; + return 1; } mem_.copyArrayHostToDevice(&d_data_[j * n_size_], &h_data_[j * n_size_], n_size_); gpu_updated_[j] = true; @@ -630,9 +627,8 @@ namespace GridKit } if (h_data_ == nullptr) { - // allocate first - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::syncData - host data not allocated\n"; + return 1; } mem_.copyArrayDeviceToHost(&h_data_[j * n_size_], &d_data_[j * n_size_], n_size_); cpu_updated_[j] = true; @@ -696,8 +692,8 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::setToZero - host data not allocated\n"; + return 1; } mem_.setZeroArrayOnHost(h_data_, n_capacity_ * k_); setHostUpdated(true); @@ -706,8 +702,8 @@ namespace GridKit case DEVICE: if (d_data_ == nullptr) { - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::setToZero - device data not allocated\n"; + return 1; } mem_.setZeroArrayOnDevice(d_data_, n_capacity_ * k_); setHostUpdated(false); @@ -734,8 +730,8 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::setToZero - host data not allocated\n"; + return 1; } mem_.setZeroArrayOnHost(&h_data_[j * n_size_], n_size_); cpu_updated_[j] = true; @@ -744,8 +740,8 @@ namespace GridKit case DEVICE: if (d_data_ == nullptr) { - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::setToZero - device data not allocated\n"; + return 1; } // TODO: We should not need to access raw data in this class mem_.setZeroArrayOnDevice(&d_data_[j * n_size_], n_size_); @@ -774,8 +770,8 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::setToConst - host data not allocated\n"; + return 1; } mem_.setArrayToConstOnHost(h_data_, C, n_size_ * k_); setHostUpdated(true); @@ -784,8 +780,8 @@ namespace GridKit case DEVICE: if (d_data_ == nullptr) { - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::setToConst - device data not allocated\n"; + return 1; } mem_.setArrayToConstOnDevice(d_data_, C, n_size_ * k_); setHostUpdated(false); @@ -813,8 +809,8 @@ namespace GridKit case HOST: if (h_data_ == nullptr) { - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); - owns_cpu_data_ = true; + out::error() << "Vector::setToConst - host data not allocated\n"; + return 1; } mem_.setArrayToConstOnHost(&h_data_[n_size_ * j], C, n_size_); cpu_updated_[j] = true; @@ -823,8 +819,8 @@ namespace GridKit case DEVICE: if (d_data_ == nullptr) { - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); - owns_gpu_data_ = true; + out::error() << "Vector::setToConst - device data not allocated\n"; + return 1; } mem_.setArrayToConstOnDevice(&d_data_[n_size_ * j], C, n_size_); cpu_updated_[j] = false; diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp index 00a61c18f..87d0e7c50 100644 --- a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -298,6 +298,9 @@ namespace GridKit IdxT number_vectors = 3; Vector x(vector_size, number_vectors); + x.allocate(memspace_); + if (memspace_ == memory::DEVICE) + x.allocate(memory::HOST); x.setToZero(memspace_); success *= verifyAnswer(x, ZERO); @@ -344,6 +347,8 @@ namespace GridKit IdxT number_vectors = 3; Vector x(vector_size, number_vectors); + x.allocate(memspace_); + x.allocate(memory::HOST); // Set all vectors in x on device to ones x.setToConst(ONE, memspace_); From 37daa26ffa3816d163d74230ead763dae00e26d6 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:10:50 -0400 Subject: [PATCH 5/9] Remove redundant checks. --- GridKit/LinearAlgebra/Vector/Vector.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index 299784250..03e811e47 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -413,7 +413,7 @@ namespace GridKit switch (memspace) { case HOST: - if ((cpu_updated_[j] == false) && (gpu_updated_[j] == true)) + if (cpu_updated_[j] == false) { out::error() << "Trying to get data for vector " << j << " on the host, " << "but host data is out of date!\n" @@ -422,7 +422,7 @@ namespace GridKit } return &h_data_[j * n_size_]; case DEVICE: - if ((gpu_updated_[j] == false) && (cpu_updated_[j] == true)) + if (gpu_updated_[j] == false) { out::error() << "Trying to get data for vector " << j << " on the device, " << "but device data is out of date!\n" @@ -906,11 +906,6 @@ namespace GridKit switch (memspaceSrc) { case HOST: - if (!cpu_updated_[i]) - { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; - return 1; - } switch (memspaceDst) { case HOST: @@ -922,11 +917,6 @@ namespace GridKit } break; case DEVICE: - if (!gpu_updated_[i]) - { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not up to date in the source memory space!\n"; - return 1; - } switch (memspaceDst) { case HOST: From 2aaa65b36f5834929388d9c7f7328e017c03d70b Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:23:22 -0400 Subject: [PATCH 6/9] Make error messages consistent. --- GridKit/LinearAlgebra/Vector/Vector.cpp | 89 ++++++++++--------------- 1 file changed, 35 insertions(+), 54 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index 03e811e47..7abfdf784 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -122,8 +122,7 @@ namespace GridKit using namespace memory; if (d_data_ || h_data_) { - out::error() << "Trying to set vector data, but the data already exists!\n"; - out::error() << "Ignoring setData function call ...\n"; + out::error() << "Vector::setData - data already exists, ignoring call\n"; return 1; } @@ -246,16 +245,14 @@ namespace GridKit case memory::HOST: if (h_data_ == nullptr) { - out::error() << "Vector::copyFromExternal - destination memory space" - << " on host not allocated\n"; + out::error() << "Vector::copyFromExternal - host destination not allocated\n"; return 1; } break; case memory::DEVICE: if (d_data_ == nullptr) { - out::error() << "Vector::copyFromExternal - destination memory space" - << " on device not allocated\n"; + out::error() << "Vector::copyFromExternal - device destination not allocated\n"; return 1; } break; @@ -327,16 +324,14 @@ namespace GridKit case HOST: if (cpu_updated_[0] == false) { - out::error() << "Vector::getData - requesting data from host" - << " but host data is stale. Returning nullptr ...\n"; + out::error() << "Vector::getData - host data is stale. Perhaps you need to call syncData?\n"; return nullptr; } return h_data_; case DEVICE: if (gpu_updated_[0] == false) { - out::error() << "Vector::getData - requesting data from device" - << " but device data is stale. Returning nullptr ...\n"; + out::error() << "Vector::getData - device data is stale. Perhaps you need to call syncData?\n"; return nullptr; } return d_data_; @@ -364,16 +359,14 @@ namespace GridKit case HOST: if (cpu_updated_[0] == false) { - out::error() << "Trying to get data on the host, but host data is out of date!\n" - << "Use syncData function to sync host data with the device data!\n"; + out::error() << "Vector::getData - host data is stale. Perhaps you need to call syncData?\n"; return nullptr; } return h_data_; case DEVICE: if (gpu_updated_[0] == false) { - out::error() << "Trying to get data on the device, but device data is out of date!\n" - << "Use syncData function to sync device data with the host data!\n"; + out::error() << "Vector::getData - device data is stale. Perhaps you need to call syncData?\n"; return nullptr; } return d_data_; @@ -405,8 +398,7 @@ namespace GridKit if (k_ <= j) { - out::error() << "Trying to get data for vector " << j << " in multivector" - << " but there are only " << k_ << " vectors!\n"; + out::error() << "Vector::getData - vector index " << j << " out of range, multivector has only " << k_ << " vectors\n"; return nullptr; } @@ -415,18 +407,14 @@ namespace GridKit case HOST: if (cpu_updated_[j] == false) { - out::error() << "Trying to get data for vector " << j << " on the host, " - << "but host data is out of date!\n" - << "Use syncData function to sync host data with the device data!\n"; + out::error() << "Vector::getData - host data for vector " << j << " is stale. Perhaps you need to call syncData?\n"; return nullptr; } return &h_data_[j * n_size_]; case DEVICE: if (gpu_updated_[j] == false) { - out::error() << "Trying to get data for vector " << j << " on the device, " - << "but device data is out of date!\n" - << "Use syncData function to sync device data with the host data!\n"; + out::error() << "Vector::getData - device data for vector " << j << " is stale. Perhaps you need to call syncData?\n"; return nullptr; } return &d_data_[j * n_size_]; @@ -455,8 +443,7 @@ namespace GridKit if (k_ <= j) { - out::error() << "Trying to get data for vector " << j << " in multivector" - << " but there are only " << k_ << " vectors!\n"; + out::error() << "Vector::getData - vector index " << j << " out of range, multivector has only " << k_ << " vectors\n"; return nullptr; } @@ -465,18 +452,14 @@ namespace GridKit case HOST: if (cpu_updated_[j] == false) { - out::error() << "Trying to get data for vector " << j << " on the host, " - << "but host data is out of date!\n" - << "Use syncData function to sync host data with the device data!\n"; + out::error() << "Vector::getData - host data for vector " << j << " is stale. Perhaps you need to call syncData?\n"; return nullptr; } return &h_data_[j * n_size_]; case DEVICE: if (gpu_updated_[j] == false) { - out::error() << "Trying to get data for vector " << j << " on the device, " - << "but device data is out of date!\n" - << "Use syncData function to sync device data with the host data!\n"; + out::error() << "Vector::getData - device data for vector " << j << " is stale. Perhaps you need to call syncData?\n"; return nullptr; } return &d_data_[j * n_size_]; @@ -513,16 +496,14 @@ namespace GridKit { if (gpu_updated_[i] != all_gpu_updated) { - out::error() << "Trying to sync all multivector data on the device," - << " but individual vectors were updated differently.\n" - << "Use syncData function for individual vectors instead!\n"; + out::error() << "Vector::syncData - inconsistent update state across device columns.\n" + << "Use syncData(j, memspace) for individual vectors\n"; return 1; } if (cpu_updated_[i] != all_cpu_updated) { - out::error() << "Trying to sync all multivector data on the host," - << " but individual vectors were updated differently.\n" - << "Use syncData function for individual vectors instead!\n"; + out::error() << "Vector::syncData - inconsistent update state across host columns.\n" + << "Use syncData(j, memspace) for individual vectors\n"; return 1; } } @@ -532,12 +513,12 @@ namespace GridKit case DEVICE: // cpu -> gpu if (gpu_updated_[0]) { - out::error() << "Trying to sync device, but device already up to date!\n"; + out::error() << "Vector::syncData - device already up to date\n"; return 1; } if (!cpu_updated_[0]) { - out::error() << "Trying to sync device with host, but host is out of date!\n"; + out::error() << "Vector::syncData - host data is stale, cannot sync to device\n"; return 1; } if (d_data_ == nullptr) @@ -551,12 +532,12 @@ namespace GridKit case HOST: // gpu -> cpu if (cpu_updated_[0]) { - out::error() << "Trying to sync host, but host already up to date!\n"; + out::error() << "Vector::syncData - host already up to date\n"; return 1; } if (!gpu_updated_[0]) { - out::error() << "Trying to sync host with device, but device is out of date!\n"; + out::error() << "Vector::syncData - device data is stale, cannot sync to host\n"; return 1; } if (h_data_ == nullptr) @@ -598,12 +579,12 @@ namespace GridKit case DEVICE: // cpu->gpu if (gpu_updated_[j]) { - out::error() << "Trying to sync device, but device already up to date!\n"; + out::error() << "Vector::syncData - device already up to date\n"; return 1; } if (!cpu_updated_[j]) { - out::error() << "Trying to sync device with host, but host is out of date!\n"; + out::error() << "Vector::syncData - host data is stale, cannot sync to device\n"; return 1; } if (d_data_ == nullptr) @@ -617,12 +598,12 @@ namespace GridKit case HOST: // cuda->cpu if (cpu_updated_[j]) { - out::error() << "Trying to sync host, but host already up to date!\n"; + out::error() << "Vector::syncData - host already up to date\n"; return 1; } if (!gpu_updated_[j]) { - out::error() << "Trying to sync host with device, but device is out of date!\n"; + out::error() << "Vector::syncData - device data is stale, cannot sync to host\n"; return 1; } if (h_data_ == nullptr) @@ -853,9 +834,8 @@ namespace GridKit if (new_n_size > n_capacity_) { - out::error() << "Trying to resize vector to " << new_n_size - << " elements but memory allocated only for " << n_capacity_ - << " elements.\n"; + out::error() << "Vector::resize - requested size " << new_n_size + << " exceeds capacity " << n_capacity_ << "\n"; return 1; } else @@ -889,18 +869,19 @@ namespace GridKit using namespace memory; if (i >= k_) { - out::error() << "Trying to copy data for vector " << i << " in multivector but there are only " << k_ << " vectors!\n"; + out::error() << "Vector::copyToExternal - vector index " << i + << " out of range, multivector has only " << k_ << " vectors\n"; return 1; } if (dest == nullptr) { - out::error() << "Trying to copy data for vector " << i << " in multivector but the destination pointer is not allocated!\n"; + out::error() << "Vector::copyToExternal - destination pointer for vector " << i << " is null\n"; return 1; } ScalarT* data = getData(i, memspaceSrc); if (data == nullptr) { - out::error() << "Trying to copy data for vector " << i << " in multivector but the data is not allocated in the source memory space!\n"; + out::error() << "Vector::copyToExternal - source data for vector " << i << " is null or stale\n"; return 1; } switch (memspaceSrc) @@ -955,13 +936,13 @@ namespace GridKit // Check that the source data is not null and up to date if (data == nullptr) { - out::error() << "Trying to copy data for multivector but the data is not allocated in the source memory space!\n"; + out::error() << "Vector::copyToExternal - source data is null or stale\n"; return 1; } // Check that the destination memory space is allocated if (dest == nullptr) { - out::error() << "Trying to copy data for multivector but the destination pointer is not allocated!\n"; + out::error() << "Vector::copyToExternal - destination pointer is null\n"; return 1; } switch (memspaceSrc) @@ -969,7 +950,7 @@ namespace GridKit case HOST: if (!cpu_updated_[0]) { - out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; + out::error() << "Vector::copyToExternal - source data is stale\n"; return 1; } switch (memspaceDst) @@ -985,7 +966,7 @@ namespace GridKit case DEVICE: if (!gpu_updated_[0]) { - out::error() << "Trying to copy data for multivector but the data is not up to date in the source memory space!\n"; + out::error() << "Vector::copyToExternal - source data is stale\n"; return 1; } switch (memspaceDst) From 795014227f7068e39876bec394e995d534259cc0 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:36:16 -0400 Subject: [PATCH 7/9] Update comments in Vector class. --- GridKit/LinearAlgebra/Vector/Vector.cpp | 90 ++++++++++++------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index 7abfdf784..abb77228e 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -76,7 +76,7 @@ namespace GridKit } /** - * @brief get the number of elements in a single vector. + * @brief Get the number of elements in a single vector. * * For vectors with changing sizes, set the vector capacity to * the maximum expected size. @@ -211,11 +211,11 @@ namespace GridKit /** * @brief Copy data from another vector. * - * @param[in] v - Vector, which data will be copied - * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) - * @param[in] memspaceDst - Memory space the data will be copied to (HOST or DEVICE) + * @param[in] source - Vector whose data will be copied + * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) + * @param[in] memspaceDst - Memory space to copy data to (HOST or DEVICE) * - * @pre size of _v_ is equal or larger than the current vector size. + * @pre Size of _source_ is greater than or equal to the current vector size. */ template int Vector::copyFromExternal(const Vector& source, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) @@ -225,13 +225,14 @@ namespace GridKit } /** - * @brief Copy vector data from input array. + * @brief Copy vector data from an input array. * - * This function allocates (if necessary) and copies the data. + * Destination memory must be pre-allocated via allocate() before calling + * this function. * - * @param[in] data - Data that is to be copied - * @param[in] memspaceSrc - Memory space of the data source (HOST or DEVICE) - * @param[in] memspaceDst - Memory space the data will be copied to (HOST or DEVICE) + * @param[in] source - Array to copy from + * @param[in] memspaceSrc - Memory space of the source array (HOST or DEVICE) + * @param[in] memspaceDst - Memory space to copy data to (HOST or DEVICE) * * @return 0 if successful, 1 otherwise. */ @@ -376,19 +377,18 @@ namespace GridKit } /** - * @brief get a pointer to HOST or DEVICE data of a particular vector in a multivector. + * @brief Get a pointer to HOST or DEVICE data of a vector in a multivector. * * @param[in] j - Index of a vector in multivector * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) * - * @return pointer to the _i_th vector data (HOST or DEVICE) within a multivector. + * @return Pointer to the _j_th vector data (HOST or DEVICE). * - * @pre `j` < `k_` i.e, `j` is smaller than the total number of vectors in multivector. + * @pre `j` < `k_`, i.e., `j` is smaller than the number of vectors. * * @note This function gives you access to the pointer, not to a copy. * If you change the values using the pointer, the vector values will - * change too. Make sure to use setDataUpdated function to set the update - * flags correctly after changing the values. + * change too. Call setDataUpdated() to update the staleness flags. */ template ScalarT* Vector::getData(IdxT j, memory::MemorySpace memspace) @@ -424,16 +424,14 @@ namespace GridKit } /** - * @brief get a const pointer to HOST or DEVICE data of a particular - * vector in a multivector. + * @brief Get a const pointer to HOST or DEVICE data of a vector in a multivector. * * @param[in] j - Index of a vector in multivector * @param[in] memspace - Memory space of the pointer (HOST or DEVICE) * - * @return pointer to the _i_th vector data (HOST or DEVICE) within a multivector. - * - * @pre `j` < `k_` i.e, `j` is smaller than the total number of vectors in multivector. + * @return Const pointer to the _j_th vector data (HOST or DEVICE). * + * @pre `j` < `k_`, i.e., `j` is smaller than the number of vectors. */ template const ScalarT* Vector::getData(IdxT j, memory::MemorySpace memspace) const @@ -595,7 +593,7 @@ namespace GridKit mem_.copyArrayHostToDevice(&d_data_[j * n_size_], &h_data_[j * n_size_], n_size_); gpu_updated_[j] = true; break; - case HOST: // cuda->cpu + case HOST: // gpu -> cpu if (cpu_updated_[j]) { out::error() << "Vector::syncData - host already up to date\n"; @@ -635,7 +633,8 @@ namespace GridKit case HOST: if (!owns_cpu_data_) { - out::error() << "Vector::allocate - cannot allocate host data, vector does not own it\n"; + out::error() << "Vector::allocate - cannot reallocate host data," + << " vector does not own it\n"; return 1; } mem_.deleteOnHost(h_data_); @@ -646,7 +645,8 @@ namespace GridKit case DEVICE: if (!owns_gpu_data_) { - out::error() << "Vector::allocate - cannot allocate device data, vector does not own it\n"; + out::error() << "Vector::allocate - cannot reallocate device data," + << " vector does not own it\n"; return 1; } mem_.deleteOnDevice(d_data_); @@ -659,9 +659,11 @@ namespace GridKit } /** - * @brief set vector data to zero. In case of multivectors, entire multivector is set to zero. + * @brief Set vector data to zero. + * + * In case of multivectors, the entire multivector is set to zero. * - * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * @param[in] memspace - Memory space of the data to be zeroed (HOST or DEVICE) * */ template @@ -697,10 +699,10 @@ namespace GridKit /** * @brief set the data of a single vector in a multivector to zero. * - * @param[in] i - Index of a vector in a multivector - * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * @param[in] j - Index of a vector in the multivector + * @param[in] memspace - Memory space of the data to be zeroed (HOST or DEVICE) * - * @pre _i_ < _k_ i.e,, _i_ is smaller than the total number of vectors in multivector. + * @pre `j` < `k_`, i.e., `j` is smaller than the number of vectors. */ template int Vector::setToZero(IdxT j, memory::MemorySpace memspace) @@ -738,8 +740,8 @@ namespace GridKit * * In case of multivectors, entire multivector is set to the constant. * - * @param[in] C - Constant (real number) - * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * @param[in] C - Constant value to set + * @param[in] memspace - Memory space of the data to be set (HOST or DEVICE) * */ template @@ -775,11 +777,11 @@ namespace GridKit /** * @brief set the data of a single vector in a multivector to a given constant. * - * @param[in] j - Index of a vector in a multivector - * @param[in] C - Constant (real number) - * @param[in] memspace - Memory space of the data to be set to 0 (HOST or DEVICE) + * @param[in] j - Index of a vector in the multivector + * @param[in] C - Constant value to set + * @param[in] memspace - Memory space of the data to be set (HOST or DEVICE) * - * @pre _j_ < _k_ i.e,, _j_ is smaller than the total number of vectors in multivector. + * @pre `j` < `k_`, i.e., `j` is smaller than the number of vectors. */ template int Vector::setToConst(IdxT j, ScalarT C, memory::MemorySpace memspace) @@ -846,22 +848,20 @@ namespace GridKit } /** - * @brief copy HOST or DEVICE data of a specified vector in a multivector to _dest_. + * @brief Copy HOST or DEVICE data of a single vector in a multivector to _dest_. * - * This function allows to copy data between different memory spaces in one call. - * For example, you can copy data of vector _i_ from HOST to DEVICE, or from DEVICE to HOST. + * Supports cross-space copies, e.g. vector _i_ from HOST to DEVICE. * - * @param[out] dest - Pointer to the memory to which data is copied - * @param[in] i - Index of a vector in a multivector - * @param[in] memspaceInSrc - Memory space (HOST or DEVICE) of the data to be copied - * @param[in] memspaceOutDst - Memory space (HOST or DEVICE) to which data is copied + * @param[out] dest - Destination array + * @param[in] i - Index of a vector in the multivector + * @param[in] memspaceSrc - Memory space of the source data (HOST or DEVICE) + * @param[in] memspaceDst - Memory space of the destination (HOST or DEVICE) * * @return 0 if successful, 1 otherwise. * - * @pre _i_ < _k_ i.e,, _i_ is smaller than the total number of vectors in multivector. - * @pre _dest_ is allocated, and the size of _dest_ is at least _n_ (length of a single vector in the multivector). - * @pre _dest_ is allocated in memspaceOutDst memory space. - * @post All elements of the vector _i_ are copied to the array _dest_. + * @pre `i` < `k_`, i.e., `i` is smaller than the number of vectors. + * @pre _dest_ is allocated with at least _n_ elements in _memspaceDst_. + * @post All elements of vector _i_ are copied to _dest_. */ template int Vector::copyToExternal(ScalarT* dest, IdxT i, memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) From f1581f0c2bb17f4a27eb44a50956ebd6e8f51929 Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:53:04 -0400 Subject: [PATCH 8/9] Memory space in Vector defaults to HOST --- GridKit/LinearAlgebra/Vector/Vector.hpp | 44 ++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index ac08d2ca0..2690fb0d9 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -42,39 +42,39 @@ namespace GridKit Vector& operator=(Vector&&) = delete; int copyFromExternal(const ScalarT* source, - memory::MemorySpace memspaceIn, - memory::MemorySpace memspaceOut); + memory::MemorySpace memspaceIn = memory::HOST, + memory::MemorySpace memspaceOut = memory::HOST); int copyFromExternal(const Vector& source, - memory::MemorySpace memspaceIn, - memory::MemorySpace memspaceOut); + memory::MemorySpace memspaceIn = memory::HOST, + memory::MemorySpace memspaceOut = memory::HOST); - ScalarT* getData(memory::MemorySpace memspace); - ScalarT* getData(IdxT i, memory::MemorySpace memspace); - const ScalarT* getData(memory::MemorySpace memspace) const; - const ScalarT* getData(IdxT i, memory::MemorySpace memspace) const; + ScalarT* getData(memory::MemorySpace memspace = memory::HOST); + ScalarT* getData(IdxT i, memory::MemorySpace memspace = memory::HOST); + const ScalarT* getData(memory::MemorySpace memspace = memory::HOST) const; + const ScalarT* getData(IdxT i, memory::MemorySpace memspace = memory::HOST) const; IdxT getCapacity() const; IdxT getSize() const; IdxT getNumVectors() const; - int setDataUpdated(memory::MemorySpace memspace); - int setDataUpdated(IdxT j, memory::MemorySpace memspace); - int setData(ScalarT* data, memory::MemorySpace memspace); - int allocate(memory::MemorySpace memspace); - int setToZero(memory::MemorySpace memspace); - int setToZero(IdxT i, memory::MemorySpace memspace); - int setToConst(ScalarT C, memory::MemorySpace memspace); - int setToConst(IdxT i, ScalarT C, memory::MemorySpace memspace); - int syncData(memory::MemorySpace memspaceOut); - int syncData(IdxT j, memory::MemorySpace memspaceOut); + int setDataUpdated(memory::MemorySpace memspace = memory::HOST); + int setDataUpdated(IdxT j, memory::MemorySpace memspace = memory::HOST); + int setData(ScalarT* data, memory::MemorySpace memspace = memory::HOST); + int allocate(memory::MemorySpace memspace = memory::HOST); + int setToZero(memory::MemorySpace memspace = memory::HOST); + int setToZero(IdxT i, memory::MemorySpace memspace = memory::HOST); + int setToConst(ScalarT C, memory::MemorySpace memspace = memory::HOST); + int setToConst(IdxT i, ScalarT C, memory::MemorySpace memspace = memory::HOST); + int syncData(memory::MemorySpace memspaceOut = memory::HOST); + int syncData(IdxT j, memory::MemorySpace memspaceOut = memory::HOST); int resize(IdxT new_n_current); int copyToExternal(ScalarT* dest, IdxT i, - memory::MemorySpace memspaceSrc, - memory::MemorySpace memspaceDst); + memory::MemorySpace memspaceSrc = memory::HOST, + memory::MemorySpace memspaceDst = memory::HOST); int copyToExternal(ScalarT* dest, - memory::MemorySpace memspaceSrc, - memory::MemorySpace memspaceDst); + memory::MemorySpace memspaceSrc = memory::HOST, + memory::MemorySpace memspaceDst = memory::HOST); private: void setHostUpdated(bool is_updated); From b727a04afb02e91255a1834293b197bece42175d Mon Sep 17 00:00:00 2001 From: pelesh Date: Thu, 4 Jun 2026 21:55:19 -0400 Subject: [PATCH 9/9] [skip ci] update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c31a6e41..066786e09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ - Added `BusToSignalAdapter` component for communicating bus voltages and injection currents. - Added cmake-format hooks, including in pre-commit. - Added off-nominal tap ratio and phase shift support to the PhasorDynamics `Branch` model. +- Added portable Vector class to GridKit ## v0.1