feat(cpp-object-oriented-modular-refactoring): step 16 - generic-sparse-assembler

This commit is contained in:
KOKO\Mimi
2026-08-16 10:24:28 +09:00
parent a21b991ef9
commit 6b10f8a7d2
3 changed files with 219 additions and 259 deletions
@@ -6,11 +6,13 @@
#include <array>
#include <cstring>
#include <filesystem>
#include <functional>
#include <utility>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/elements/element.h"
#include "fesa/elements/mitc4_shell.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/model/domain.h"
@@ -180,6 +182,42 @@ class ReverseParallelFor final : public fesa::ParallelFor {
mutable std::size_t observed_count_{0U};
};
class FakeStiffnessElement final : public fesa::Element {
public:
FakeStiffnessElement(fesa::ElementDofLayout layout, fesa::Matrix stiffness)
: layout_{std::move(layout)}, stiffness_{std::move(stiffness)} {}
const fesa::ElementDofLayout& DofLayout() const noexcept override {
return layout_;
}
fesa::Result<fesa::ElementStiffnessContribution> ComputeStiffness()
const override {
return fesa::Result<fesa::ElementStiffnessContribution>::Success(
{layout_, stiffness_});
}
fesa::Result<fesa::ElementResultBundle> Recover(
const fesa::Vector&) const override {
return fesa::Result<fesa::ElementResultBundle>::Success(
{layout_.source_id, fesa::BeamElementResultRows{}});
}
private:
fesa::ElementDofLayout layout_;
fesa::Matrix stiffness_;
};
fesa::Matrix MatrixFromRows(const std::vector<std::vector<double>>& rows) {
fesa::Matrix matrix{rows.size(), rows.empty() ? 0U : rows.front().size()};
for (std::size_t row = 0U; row < rows.size(); ++row) {
for (std::size_t column = 0U; column < rows[row].size(); ++column) {
matrix(row, column) = rows[row][column];
}
}
return matrix;
}
void ExpectByteIdentical(const fesa::SparseMatrix& actual,
const fesa::SparseMatrix& expected) {
EXPECT_TRUE(ByteIdentical(actual.RowOffsets(), expected.RowOffsets()));
@@ -187,6 +225,66 @@ void ExpectByteIdentical(const fesa::SparseMatrix& actual,
EXPECT_TRUE(ByteIdentical(actual.Values(), expected.Values()));
}
// C-ASSEMBLY-001
TEST(SparseAssembly,
AssemblesFakeRuntimeContributionsIntoExactDeterministicCsr) {
auto domain = fesa::Domain::Create(MakeDefinition());
ASSERT_TRUE(domain.HasValue());
auto model = fesa::AnalysisModel::Create(domain.Value());
ASSERT_TRUE(model.HasValue());
fesa::ElementDofLayout first_layout{
{"Beam-1", 10, "10"}, {0U, 2U}, {fesa::DofComponent::kUx}};
FakeStiffnessElement first{std::move(first_layout),
MatrixFromRows({{1.0, 2.0}, {3.0, 4.0}})};
fesa::ElementDofLayout second_layout{
{"Beam-1", 20, "20"},
{2U},
{fesa::DofComponent::kUx, fesa::DofComponent::kUz,
fesa::DofComponent::kUrz}};
FakeStiffnessElement second{
std::move(second_layout),
MatrixFromRows({{5.0, 6.0, 7.0}, {8.0, 9.0, 10.0}, {11.0, 12.0, 13.0}})};
const fesa::ElementView elements{std::cref(first), std::cref(second)};
fesa::DofManager dofs;
ASSERT_TRUE(dofs.Build(model.Value(), elements).IsOk());
fesa::SerialParallelFor serial_executor;
fesa::TbbParallelFor tbb_executor;
ReverseParallelFor reverse_executor;
auto serial =
fesa::SparseAssembler::Assemble(elements, dofs, serial_executor);
auto tbb = fesa::SparseAssembler::Assemble(elements, dofs, tbb_executor);
auto reversed =
fesa::SparseAssembler::Assemble(elements, dofs, reverse_executor);
ASSERT_TRUE(serial.HasValue());
ASSERT_TRUE(tbb.HasValue());
ASSERT_TRUE(reversed.HasValue());
EXPECT_EQ(reverse_executor.Calls(), 1U);
EXPECT_EQ(reverse_executor.ObservedCount(), elements.size());
EXPECT_EQ(serial.Value().Rows(), 18U);
EXPECT_EQ(serial.Value().Columns(), 18U);
EXPECT_EQ(serial.Value().RowOffsets(),
(std::vector<std::size_t>{0U, 2U, 2U, 2U, 2U, 2U, 2U, 2U, 2U, 2U,
2U, 2U, 2U, 6U, 6U, 9U, 9U, 9U, 12U}));
EXPECT_EQ(serial.Value().ColumnIndices(),
(std::vector<std::size_t>{0U, 12U, 0U, 12U, 14U, 17U, 12U, 14U, 17U,
12U, 14U, 17U}));
EXPECT_EQ(serial.Value().Values(),
(std::vector<double>{1.0, 2.0, 3.0, 9.0, 6.0, 7.0, 8.0, 9.0, 10.0,
11.0, 12.0, 13.0}));
ExpectByteIdentical(tbb.Value(), serial.Value());
ExpectByteIdentical(reversed.Value(), serial.Value());
for (std::size_t repetition = 0U; repetition < 8U; ++repetition) {
auto repeated =
fesa::SparseAssembler::Assemble(elements, dofs, tbb_executor);
ASSERT_TRUE(repeated.HasValue());
ExpectByteIdentical(repeated.Value(), serial.Value());
}
}
TEST(SparseAssembly, SerialTbbAndRepeatedRunsAreByteIdentical) {
auto domain_result = fesa::Domain::Create(MakeDefinition());
ASSERT_TRUE(domain_result.HasValue());