feat(cpp-object-oriented-modular-refactoring): step 10 - dense-blas-adapter

This commit is contained in:
KOKO\Mimi
2026-08-16 07:49:57 +09:00
parent 6b0ff31db0
commit 89fc13c873
7 changed files with 146 additions and 49 deletions
+3
View File
@@ -18,6 +18,7 @@ add_library(
io/abaqus/domain_mapper.cpp io/abaqus/domain_mapper.cpp
io/abaqus/input_reader.cpp io/abaqus/input_reader.cpp
io/hdf5/hdf5_results_writer.cpp io/hdf5/hdf5_results_writer.cpp
math/dense_blas_internal.cpp
math/matrix.cpp math/matrix.cpp
math/sparse_matrix.cpp math/sparse_matrix.cpp
math/vector.cpp math/vector.cpp
@@ -31,6 +32,8 @@ target_include_directories(
fesa_solver fesa_solver
PUBLIC PUBLIC
"${PROJECT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/include"
PRIVATE
"${PROJECT_SOURCE_DIR}/src/fesa"
) )
target_link_libraries( target_link_libraries(
+34
View File
@@ -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
+26
View File
@@ -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
View File
@@ -6,6 +6,8 @@
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include "math/dense_blas_internal.h"
namespace fesa { namespace fesa {
namespace { namespace {
@@ -21,25 +23,15 @@ std::size_t CheckedStorageSize(const std::size_t rows,
return rows * columns; return rows * columns;
} }
/// @brief Converts a dense matrix dimension to the private MKL integer /// @brief Preserves the Matrix exception contract around private size
/// contract. /// conversion.
MKL_INT ToMklSize(const std::size_t size) { MKL_INT MklSizeOrThrow(const std::size_t size) {
if (size > static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)())) { const auto converted = dense_blas_internal::ToMklSize(size);
if (!converted.HasValue()) {
throw std::length_error{ throw std::length_error{
"Dense matrix dimension exceeds the MKL integer range."}; "Dense matrix dimension exceeds the MKL integer range."};
} }
return static_cast<MKL_INT>(size); return converted.Value();
}
/// @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);
} }
} // namespace } // namespace
@@ -54,7 +46,8 @@ Matrix::Matrix(const Matrix& other)
: rows_(other.rows_), : rows_(other.rows_),
columns_(other.columns_), columns_(other.columns_),
values_(other.values_.size()) { 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 Matrix::Matrix(Matrix&& other) noexcept
@@ -69,7 +62,8 @@ Matrix::Matrix(Matrix&& other) noexcept
Matrix& Matrix::operator=(const Matrix& other) { Matrix& Matrix::operator=(const Matrix& other) {
if (this != &other) { if (this != &other) {
std::vector<double> copied(other.values_.size()); 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_; rows_ = other.rows_;
columns_ = other.columns_; columns_ = other.columns_;
values_.swap(copied); 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 // The owned layout is row-major, so the leading dimension is the column
// count for the adapter call and remains invisible to public consumers. // count for the adapter call and remains invisible to public consumers.
cblas_dgemv(CblasRowMajor, CblasNoTrans, ToMklSize(rows_), cblas_dgemv(CblasRowMajor, CblasNoTrans, MklSizeOrThrow(rows_),
ToMklSize(columns_), 1.0, values_.data(), ToMklSize(columns_), MklSizeOrThrow(columns_), 1.0, values_.data(),
rhs.Data(), 1, 0.0, result.Data(), 1); MklSizeOrThrow(columns_), rhs.Data(), 1, 0.0, result.Data(), 1);
return result; return result;
} }
@@ -138,10 +132,11 @@ Matrix Matrix::Multiply(const Matrix& rhs) const {
return result; return result;
} }
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, ToMklSize(rows_), cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, MklSizeOrThrow(rows_),
ToMklSize(rhs.columns_), ToMklSize(columns_), 1.0, values_.data(), MklSizeOrThrow(rhs.columns_), MklSizeOrThrow(columns_), 1.0,
ToMklSize(columns_), rhs.values_.data(), ToMklSize(rhs.columns_), values_.data(), MklSizeOrThrow(columns_), rhs.values_.data(),
0.0, result.values_.data(), ToMklSize(rhs.columns_)); MklSizeOrThrow(rhs.columns_), 0.0, result.values_.data(),
MklSizeOrThrow(rhs.columns_));
return result; return result;
} }
+16 -24
View File
@@ -2,32 +2,22 @@
#include <mkl.h> #include <mkl.h>
#include <limits>
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
#include "math/dense_blas_internal.h"
namespace fesa { namespace fesa {
namespace { namespace {
/// @brief Converts a dense vector size to the private MKL integer contract. /// @brief Preserves the Vector exception contract around private size
MKL_INT ToMklSize(const std::size_t size) { /// conversion.
if (size > static_cast<std::size_t>((std::numeric_limits<MKL_INT>::max)())) { 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."}; throw std::length_error{"Dense vector size exceeds the MKL integer range."};
} }
return static_cast<MKL_INT>(size); return converted.Value();
}
/// @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);
} }
} // namespace } // namespace
@@ -36,7 +26,8 @@ Vector::Vector(const std::size_t size, const double value)
: values_(size, value) {} : values_(size, value) {}
Vector::Vector(const Vector& other) : values_(other.Size()) { 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_)) { 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) { Vector& Vector::operator=(const Vector& other) {
if (this != &other) { if (this != &other) {
std::vector<double> copied(other.Size()); 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); values_.swap(copied);
} }
return *this; return *this;
@@ -83,7 +75,7 @@ double Vector::Dot(const Vector& rhs) const {
return 0.0; 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 { double Vector::Norm() const {
@@ -91,7 +83,7 @@ double Vector::Norm() const {
return 0.0; return 0.0;
} }
return cblas_dnrm2(ToMklSize(Size()), Data(), 1); return cblas_dnrm2(MklSizeOrThrow(Size()), Data(), 1);
} }
void Vector::Scale(const double alpha) { void Vector::Scale(const double alpha) {
@@ -99,7 +91,7 @@ void Vector::Scale(const double alpha) {
return; return;
} }
cblas_dscal(ToMklSize(Size()), alpha, Data(), 1); cblas_dscal(MklSizeOrThrow(Size()), alpha, Data(), 1);
} }
void Vector::Axpy(const double alpha, const Vector& x) { void Vector::Axpy(const double alpha, const Vector& x) {
@@ -110,7 +102,7 @@ void Vector::Axpy(const double alpha, const Vector& x) {
return; return;
} }
cblas_daxpy(ToMklSize(Size()), alpha, x.Data(), 1, Data(), 1); cblas_daxpy(MklSizeOrThrow(Size()), alpha, x.Data(), 1, Data(), 1);
} }
} // namespace fesa } // namespace fesa
+8
View File
@@ -15,6 +15,7 @@ add_executable(
unit/elements/euler_beam_3d_test.cpp unit/elements/euler_beam_3d_test.cpp
unit/elements/mitc4_shell_test.cpp unit/elements/mitc4_shell_test.cpp
unit/fem/dof_manager_test.cpp unit/fem/dof_manager_test.cpp
unit/math/dense_blas_internal_test.cpp
unit/math/matrix_test.cpp unit/math/matrix_test.cpp
unit/math/sparse_matrix_test.cpp unit/math/sparse_matrix_test.cpp
unit/math/vector3_test.cpp unit/math/vector3_test.cpp
@@ -33,11 +34,18 @@ add_executable(
unit/solvers/linear/mkl_pardiso_solver_test.cpp unit/solvers/linear/mkl_pardiso_solver_test.cpp
) )
target_include_directories(
fesa_unit_tests
PRIVATE
"${PROJECT_SOURCE_DIR}/src/fesa"
)
target_link_libraries( target_link_libraries(
fesa_unit_tests fesa_unit_tests
PRIVATE PRIVATE
fesa_solver fesa_solver
Fesa::HDF5 Fesa::HDF5
Fesa::MKL
GTest::gtest_main 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