엄격 수렴 기준 실행 보존과 한정 HHT 개발 계측 기록
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
#include "analysis/hht_development_probe.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#if defined(FESA_HHT_DEVELOPMENT_PROBE)
|
||||
#include "fesa/math/sparse_matrix.h"
|
||||
#include "fesa/math/vector.h"
|
||||
#include "analysis/hht_reduced_internal.h"
|
||||
namespace fesa::hht_development_probe {
|
||||
class IdentityTangentSolver final : public TangentSolver {
|
||||
public:
|
||||
Status FactorizeFresh(const SparseMatrix&) override { return Status::Ok(); }
|
||||
Status SolveCorrection(const Vector& rhs, Vector& result) const override {
|
||||
result = rhs; return Status::Ok();
|
||||
}
|
||||
};
|
||||
TEST(HhtDevelopmentProbe, CountsActualInnerUpdatesForLinearGraphAndZeroRoot) {
|
||||
hht_reduced_internal::FixedChart chart{};
|
||||
chart.full_dof_count = 1U;
|
||||
chart.z_dofs = {0U};
|
||||
chart.z_columns = {{0U, 1U, false, {{0U, 1.0}}, {0.0}}};
|
||||
IdentityTangentSolver solver;
|
||||
const auto evaluate = [](const Vector& q) {
|
||||
hht_reduced_internal::ConstraintOperators operators;
|
||||
operators.rows.resize(1U);
|
||||
operators.rows[0U].value = q[0U];
|
||||
operators.rows[0U].gradient = {{0U, 1.0}};
|
||||
return Result<hht_reduced_internal::ConstraintOperators>::Success(
|
||||
std::move(operators));
|
||||
};
|
||||
Statistics statistics;
|
||||
active = &statistics;
|
||||
const auto first = hht_reduced_internal::SolveEmbeddingRoot(
|
||||
chart, Vector{0U}, Vector{1U, 1.0}, evaluate, solver);
|
||||
active = nullptr;
|
||||
ASSERT_TRUE(first.HasValue());
|
||||
EXPECT_EQ(statistics.counts[kInnerCorrection].load(), 1U);
|
||||
EXPECT_DOUBLE_EQ(first.Value().z[0U], 0.0);
|
||||
Statistics zero_statistics;
|
||||
active = &zero_statistics;
|
||||
const auto zero = hht_reduced_internal::SolveEmbeddingRoot(
|
||||
chart, Vector{0U}, Vector{1U, 0.0}, evaluate, solver);
|
||||
active = nullptr;
|
||||
ASSERT_TRUE(zero.HasValue());
|
||||
EXPECT_EQ(zero_statistics.counts[kInnerCorrection].load(), 0U);
|
||||
}
|
||||
class FailingSolver final : public LinearSolver, public TangentSolver {
|
||||
public:
|
||||
Status Factorize(const SparseMatrix&) override {
|
||||
++factor_calls; return Status::Failure(FailureCategory::kSolver, {});
|
||||
}
|
||||
Status FactorizeFresh(const SparseMatrix& matrix) override { return Factorize(matrix); }
|
||||
Status Solve(const Vector&, Vector&) const override {
|
||||
++solve_calls; return Status::Failure(FailureCategory::kSolver, {});
|
||||
}
|
||||
Status SolveCorrection(const Vector& rhs, Vector& result) const override {
|
||||
return Solve(rhs, result);
|
||||
}
|
||||
int factor_calls = 0;
|
||||
mutable int solve_calls = 0;
|
||||
};
|
||||
TEST(HhtDevelopmentProbe, ForwardsEveryLogicalCallAndPreservesFailure) {
|
||||
Statistics statistics;
|
||||
active = &statistics;
|
||||
FailingSolver backend;
|
||||
PositiveSolver positive{backend};
|
||||
CorrectionSolver tangent{backend};
|
||||
auto matrix = SparseMatrix::FromCoo(0U, 0U, {}, SparsePattern{{0U}, {}});
|
||||
ASSERT_TRUE(matrix.HasValue());
|
||||
const Vector rhs{0U};
|
||||
Vector result{0U};
|
||||
EXPECT_FALSE(positive.Factorize(matrix.Value()).IsOk());
|
||||
EXPECT_FALSE(positive.Solve(rhs, result).IsOk());
|
||||
EXPECT_FALSE(tangent.FactorizeFresh(matrix.Value()).IsOk());
|
||||
EXPECT_FALSE(tangent.SolveCorrection(rhs, result).IsOk());
|
||||
active = nullptr;
|
||||
EXPECT_EQ(backend.factor_calls, 2);
|
||||
EXPECT_EQ(backend.solve_calls, 2);
|
||||
EXPECT_EQ(statistics.counts[kFactorCall].load(), 2U);
|
||||
EXPECT_EQ(statistics.counts[kSolveCall].load(), 2U);
|
||||
EXPECT_EQ(statistics.counts[kRhs].load(), 2U);
|
||||
}
|
||||
TEST(HhtDevelopmentProbe, CountsOnlyWhileActiveAndPreservesMultiplicity) {
|
||||
Statistics statistics;
|
||||
active = &statistics;
|
||||
Count(kOuterCorrection);
|
||||
Count(kInternalEvaluation, 7U);
|
||||
active = nullptr;
|
||||
Count(kOuterCorrection);
|
||||
EXPECT_EQ(statistics.counts[kOuterCorrection].load(), 1U);
|
||||
EXPECT_EQ(statistics.counts[kInternalEvaluation].load(), 7U);
|
||||
EXPECT_EQ(statistics.counts[kInertiaEvaluation].load(), 0U);
|
||||
}
|
||||
|
||||
TEST(HhtDevelopmentProbe, NestedTimesHaveNoDoubleCounting) {
|
||||
Statistics statistics;
|
||||
active = &statistics;
|
||||
{
|
||||
Scope parent{kProcedure};
|
||||
{ Scope child{kAssembly}; }
|
||||
}
|
||||
active = nullptr;
|
||||
EXPECT_EQ(statistics.stage_calls[kProcedure], 1U);
|
||||
EXPECT_EQ(statistics.stage_calls[kAssembly], 1U);
|
||||
EXPECT_GE(statistics.inclusive_ns[kProcedure], 0);
|
||||
EXPECT_GE(statistics.inclusive_ns[kAssembly], 0);
|
||||
EXPECT_EQ(statistics.exclusive_ns[kAssembly],
|
||||
statistics.inclusive_ns[kAssembly]);
|
||||
EXPECT_EQ(statistics.exclusive_ns[kProcedure] +
|
||||
statistics.exclusive_ns[kAssembly],
|
||||
statistics.inclusive_ns[kProcedure]);
|
||||
}
|
||||
} // namespace fesa::hht_development_probe
|
||||
#endif
|
||||
Reference in New Issue
Block a user