feat(cpp-object-oriented-modular-refactoring): step 10 - dense-blas-adapter
This commit is contained in:
@@ -18,6 +18,7 @@ add_library(
|
||||
io/abaqus/domain_mapper.cpp
|
||||
io/abaqus/input_reader.cpp
|
||||
io/hdf5/hdf5_results_writer.cpp
|
||||
math/dense_blas_internal.cpp
|
||||
math/matrix.cpp
|
||||
math/sparse_matrix.cpp
|
||||
math/vector.cpp
|
||||
@@ -31,6 +32,8 @@ target_include_directories(
|
||||
fesa_solver
|
||||
PUBLIC
|
||||
"${PROJECT_SOURCE_DIR}/include"
|
||||
PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/src/fesa"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace fesa::dense_blas_internal {
|
||||
|
||||
Result<MKL_INT> ToMklSize(const std::size_t size) {
|
||||
if (size > static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)())) {
|
||||
return Result<MKL_INT>::Failure(Status::Failure(
|
||||
{{Severity::kError,
|
||||
"dense-blas-size-overflow",
|
||||
{},
|
||||
"",
|
||||
"",
|
||||
"Dense operation size exceeds the MKL integer range."}}));
|
||||
}
|
||||
return Result<MKL_INT>::Success(static_cast<MKL_INT>(size));
|
||||
}
|
||||
|
||||
void CopyValues(const double* source, const std::size_t size,
|
||||
double* destination) {
|
||||
if (size == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mkl_size = ToMklSize(size);
|
||||
if (!mkl_size.HasValue()) {
|
||||
throw std::length_error{"Dense copy size exceeds the MKL integer range."};
|
||||
}
|
||||
cblas_dcopy(mkl_size.Value(), source, 1, destination, 1);
|
||||
}
|
||||
|
||||
} // namespace fesa::dense_blas_internal
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef FESA_SRC_FESA_MATH_DENSE_BLAS_INTERNAL_H_
|
||||
#define FESA_SRC_FESA_MATH_DENSE_BLAS_INTERNAL_H_
|
||||
|
||||
#include <mkl.h>
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "fesa/core/status.h"
|
||||
|
||||
namespace fesa::dense_blas_internal {
|
||||
|
||||
/// @brief Converts a dense storage length to the private MKL integer type.
|
||||
/// @return The converted length or an uncategorized overflow failure.
|
||||
Result<MKL_INT> ToMklSize(std::size_t size);
|
||||
|
||||
/// @brief Copies contiguous double values through the private BLAS backend.
|
||||
/// @param source Readable storage for size values when size is nonzero.
|
||||
/// @param size Number of contiguous values to copy.
|
||||
/// @param destination Writable storage for size values when size is nonzero.
|
||||
/// @throws std::length_error if size exceeds the MKL integer range.
|
||||
/// @note A zero-length copy accepts null pointers and does not call BLAS.
|
||||
void CopyValues(const double* source, std::size_t size, double* destination);
|
||||
|
||||
} // namespace fesa::dense_blas_internal
|
||||
|
||||
#endif // FESA_SRC_FESA_MATH_DENSE_BLAS_INTERNAL_H_
|
||||
+20
-25
@@ -6,6 +6,8 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
@@ -21,25 +23,15 @@ std::size_t CheckedStorageSize(const std::size_t rows,
|
||||
return rows * columns;
|
||||
}
|
||||
|
||||
/// @brief Converts a dense matrix dimension to the private MKL integer
|
||||
/// contract.
|
||||
MKL_INT ToMklSize(const std::size_t size) {
|
||||
if (size > static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)())) {
|
||||
/// @brief Preserves the Matrix exception contract around private size
|
||||
/// conversion.
|
||||
MKL_INT MklSizeOrThrow(const std::size_t size) {
|
||||
const auto converted = dense_blas_internal::ToMklSize(size);
|
||||
if (!converted.HasValue()) {
|
||||
throw std::length_error{
|
||||
"Dense matrix dimension exceeds the MKL integer range."};
|
||||
}
|
||||
return static_cast<MKL_INT>(size);
|
||||
}
|
||||
|
||||
/// @brief Copies owned values without exposing the dense backend publicly.
|
||||
void CopyValues(const std::vector<double>& source,
|
||||
std::vector<double>& destination) {
|
||||
if (source.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cblas_dcopy(ToMklSize(source.size()), source.data(), 1, destination.data(),
|
||||
1);
|
||||
return converted.Value();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -54,7 +46,8 @@ Matrix::Matrix(const Matrix& other)
|
||||
: rows_(other.rows_),
|
||||
columns_(other.columns_),
|
||||
values_(other.values_.size()) {
|
||||
CopyValues(other.values_, values_);
|
||||
dense_blas_internal::CopyValues(other.values_.data(), other.values_.size(),
|
||||
values_.data());
|
||||
}
|
||||
|
||||
Matrix::Matrix(Matrix&& other) noexcept
|
||||
@@ -69,7 +62,8 @@ Matrix::Matrix(Matrix&& other) noexcept
|
||||
Matrix& Matrix::operator=(const Matrix& other) {
|
||||
if (this != &other) {
|
||||
std::vector<double> copied(other.values_.size());
|
||||
CopyValues(other.values_, copied);
|
||||
dense_blas_internal::CopyValues(other.values_.data(), other.values_.size(),
|
||||
copied.data());
|
||||
rows_ = other.rows_;
|
||||
columns_ = other.columns_;
|
||||
values_.swap(copied);
|
||||
@@ -121,9 +115,9 @@ Vector Matrix::Multiply(const Vector& rhs) const {
|
||||
|
||||
// The owned layout is row-major, so the leading dimension is the column
|
||||
// count for the adapter call and remains invisible to public consumers.
|
||||
cblas_dgemv(CblasRowMajor, CblasNoTrans, ToMklSize(rows_),
|
||||
ToMklSize(columns_), 1.0, values_.data(), ToMklSize(columns_),
|
||||
rhs.Data(), 1, 0.0, result.Data(), 1);
|
||||
cblas_dgemv(CblasRowMajor, CblasNoTrans, MklSizeOrThrow(rows_),
|
||||
MklSizeOrThrow(columns_), 1.0, values_.data(),
|
||||
MklSizeOrThrow(columns_), rhs.Data(), 1, 0.0, result.Data(), 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -138,10 +132,11 @@ Matrix Matrix::Multiply(const Matrix& rhs) const {
|
||||
return result;
|
||||
}
|
||||
|
||||
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, ToMklSize(rows_),
|
||||
ToMklSize(rhs.columns_), ToMklSize(columns_), 1.0, values_.data(),
|
||||
ToMklSize(columns_), rhs.values_.data(), ToMklSize(rhs.columns_),
|
||||
0.0, result.values_.data(), ToMklSize(rhs.columns_));
|
||||
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, MklSizeOrThrow(rows_),
|
||||
MklSizeOrThrow(rhs.columns_), MklSizeOrThrow(columns_), 1.0,
|
||||
values_.data(), MklSizeOrThrow(columns_), rhs.values_.data(),
|
||||
MklSizeOrThrow(rhs.columns_), 0.0, result.values_.data(),
|
||||
MklSizeOrThrow(rhs.columns_));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+16
-24
@@ -2,32 +2,22 @@
|
||||
|
||||
#include <mkl.h>
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
/// @brief Converts a dense vector size to the private MKL integer contract.
|
||||
MKL_INT ToMklSize(const std::size_t size) {
|
||||
if (size > static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)())) {
|
||||
/// @brief Preserves the Vector exception contract around private size
|
||||
/// conversion.
|
||||
MKL_INT MklSizeOrThrow(const std::size_t size) {
|
||||
const auto converted = dense_blas_internal::ToMklSize(size);
|
||||
if (!converted.HasValue()) {
|
||||
throw std::length_error{"Dense vector size exceeds the MKL integer range."};
|
||||
}
|
||||
return static_cast<MKL_INT>(size);
|
||||
}
|
||||
|
||||
/// @brief Copies owned values without exposing the dense backend publicly.
|
||||
void CopyValues(const std::vector<double>& source,
|
||||
std::vector<double>& destination) {
|
||||
if (source.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep the backend operation in this translation unit so public ownership
|
||||
// remains independent of MKL headers and integer types.
|
||||
cblas_dcopy(ToMklSize(source.size()), source.data(), 1, destination.data(),
|
||||
1);
|
||||
return converted.Value();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -36,7 +26,8 @@ Vector::Vector(const std::size_t size, const double value)
|
||||
: values_(size, value) {}
|
||||
|
||||
Vector::Vector(const Vector& other) : values_(other.Size()) {
|
||||
CopyValues(other.values_, values_);
|
||||
dense_blas_internal::CopyValues(other.values_.data(), other.values_.size(),
|
||||
values_.data());
|
||||
}
|
||||
|
||||
Vector::Vector(Vector&& other) noexcept : values_(std::move(other.values_)) {
|
||||
@@ -46,7 +37,8 @@ Vector::Vector(Vector&& other) noexcept : values_(std::move(other.values_)) {
|
||||
Vector& Vector::operator=(const Vector& other) {
|
||||
if (this != &other) {
|
||||
std::vector<double> copied(other.Size());
|
||||
CopyValues(other.values_, copied);
|
||||
dense_blas_internal::CopyValues(other.values_.data(), other.values_.size(),
|
||||
copied.data());
|
||||
values_.swap(copied);
|
||||
}
|
||||
return *this;
|
||||
@@ -83,7 +75,7 @@ double Vector::Dot(const Vector& rhs) const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return cblas_ddot(ToMklSize(Size()), Data(), 1, rhs.Data(), 1);
|
||||
return cblas_ddot(MklSizeOrThrow(Size()), Data(), 1, rhs.Data(), 1);
|
||||
}
|
||||
|
||||
double Vector::Norm() const {
|
||||
@@ -91,7 +83,7 @@ double Vector::Norm() const {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return cblas_dnrm2(ToMklSize(Size()), Data(), 1);
|
||||
return cblas_dnrm2(MklSizeOrThrow(Size()), Data(), 1);
|
||||
}
|
||||
|
||||
void Vector::Scale(const double alpha) {
|
||||
@@ -99,7 +91,7 @@ void Vector::Scale(const double alpha) {
|
||||
return;
|
||||
}
|
||||
|
||||
cblas_dscal(ToMklSize(Size()), alpha, Data(), 1);
|
||||
cblas_dscal(MklSizeOrThrow(Size()), alpha, Data(), 1);
|
||||
}
|
||||
|
||||
void Vector::Axpy(const double alpha, const Vector& x) {
|
||||
@@ -110,7 +102,7 @@ void Vector::Axpy(const double alpha, const Vector& x) {
|
||||
return;
|
||||
}
|
||||
|
||||
cblas_daxpy(ToMklSize(Size()), alpha, x.Data(), 1, Data(), 1);
|
||||
cblas_daxpy(MklSizeOrThrow(Size()), alpha, x.Data(), 1, Data(), 1);
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
@@ -15,6 +15,7 @@ add_executable(
|
||||
unit/elements/euler_beam_3d_test.cpp
|
||||
unit/elements/mitc4_shell_test.cpp
|
||||
unit/fem/dof_manager_test.cpp
|
||||
unit/math/dense_blas_internal_test.cpp
|
||||
unit/math/matrix_test.cpp
|
||||
unit/math/sparse_matrix_test.cpp
|
||||
unit/math/vector3_test.cpp
|
||||
@@ -33,11 +34,18 @@ add_executable(
|
||||
unit/solvers/linear/mkl_pardiso_solver_test.cpp
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
fesa_unit_tests
|
||||
PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/src/fesa"
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
fesa_unit_tests
|
||||
PRIVATE
|
||||
fesa_solver
|
||||
Fesa::HDF5
|
||||
Fesa::MKL
|
||||
GTest::gtest_main
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
|
||||
namespace fesa::dense_blas_internal {
|
||||
namespace {
|
||||
|
||||
TEST(DenseBlasInternal, CDup003ConvertsZeroNormalAndOverflowLengths) {
|
||||
const auto zero = ToMklSize(0U);
|
||||
ASSERT_TRUE(zero.HasValue());
|
||||
EXPECT_EQ(zero.Value(), 0);
|
||||
|
||||
const auto normal = ToMklSize(3U);
|
||||
ASSERT_TRUE(normal.HasValue());
|
||||
EXPECT_EQ(normal.Value(), 3);
|
||||
|
||||
const std::size_t overflow =
|
||||
static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)()) + 1U;
|
||||
const auto rejected = ToMklSize(overflow);
|
||||
EXPECT_FALSE(rejected.HasValue());
|
||||
EXPECT_FALSE(rejected.GetStatus().IsOk());
|
||||
}
|
||||
|
||||
TEST(DenseBlasInternal, CDup003CopiesZeroAndNonzeroContiguousValues) {
|
||||
EXPECT_NO_THROW(CopyValues(nullptr, 0U, nullptr));
|
||||
|
||||
const std::array<double, 3U> source{1.25, -2.5, 4.0};
|
||||
std::array<double, 3U> destination{};
|
||||
CopyValues(source.data(), source.size(), destination.data());
|
||||
|
||||
EXPECT_EQ(destination, source);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace fesa::dense_blas_internal
|
||||
Reference in New Issue
Block a user