엄격 수렴 기준 실행 보존과 한정 HHT 개발 계측 기록

This commit is contained in:
KOKO\Mimi
2026-10-19 02:09:22 +09:00
parent 78cb339207
commit adb809bc01
12 changed files with 450 additions and 0 deletions
+4
View File
@@ -7,6 +7,7 @@ add_library(
analysis/eigen_reduction.cpp
analysis/hht_time_integration.cpp
analysis/hht_dynamic_algebra.cpp
analysis/hht_development_probe.h
analysis/hht_dynamic_assembly.cpp
analysis/hht_reduced_operators.cpp
analysis/hht_reduced_procedure.cpp
@@ -126,6 +127,9 @@ target_compile_options(
PRIVATE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX /EHsc>
)
if(FESA_HHT_DEVELOPMENT_PROBE)
target_compile_definitions(fesa_solver PRIVATE FESA_HHT_DEVELOPMENT_PROBE=1)
endif()
# Keep the CPU/OS dispatch on the baseline ISA; only its private kernel uses AVX2.
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND
+141
View File
@@ -0,0 +1,141 @@
#ifndef FESA_ANALYSIS_HHT_DEVELOPMENT_PROBE_H_
#define FESA_ANALYSIS_HHT_DEVELOPMENT_PROBE_H_
// This header is compiled out of normal builds, including environment/file I/O.
#if defined(FESA_HHT_DEVELOPMENT_PROBE)
#include <array>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <string>
#include "fesa/solvers/linear/linear_solver.h"
#include "fesa/solvers/linear/tangent_solver.h"
namespace fesa::hht_development_probe {
enum Stage { kProcedure, kInner, kAssembly, kOuterSolve, kRates,
kFactor, kSolve, kStageCount };
enum Counter { kOuterCorrection, kInnerCorrection, kInternalEvaluation,
kInertiaEvaluation, kFactorCall, kSolveCall, kRhs,
kConstraintInternalEvaluation, kInternalFailure, kCounterCount };
struct Statistics {
std::array<std::atomic<std::uint64_t>, kCounterCount> counts{};
// Timers/stage attribution are serial procedure-thread data. Only the three
// element counters below and internal failures may be touched by workers.
std::array<std::int64_t, kStageCount> inclusive_ns{};
std::array<std::int64_t, kStageCount> exclusive_ns{};
std::array<std::uint64_t, kStageCount> stage_calls{};
std::array<std::array<std::uint64_t, kCounterCount>, kStageCount> by_stage{};
};
inline Statistics* active = nullptr;
class Scope {
public:
explicit Scope(Stage stage) : stage_(stage), parent_(current_),
start_(std::chrono::steady_clock::now()) {
current_ = this;
if (active != nullptr) ++active->stage_calls[stage_];
}
~Scope() {
const auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now() - start_).count();
if (active != nullptr) {
active->inclusive_ns[stage_] += elapsed;
active->exclusive_ns[stage_] += elapsed - children_;
}
if (parent_ != nullptr) parent_->children_ += elapsed;
current_ = parent_;
}
static Stage Current() { return current_ == nullptr ? kProcedure : current_->stage_; }
private:
Stage stage_;
Scope* parent_;
std::chrono::steady_clock::time_point start_;
std::int64_t children_ = 0;
inline static thread_local Scope* current_ = nullptr;
};
inline void Count(Counter counter, std::uint64_t count = 1U) {
if (active == nullptr) return;
active->counts[counter].fetch_add(count, std::memory_order_relaxed);
// Element callbacks are parallel; their totals use only the atomics above.
if (counter != kInternalEvaluation && counter != kInertiaEvaluation &&
counter != kConstraintInternalEvaluation && counter != kInternalFailure)
active->by_stage[Scope::Current()][counter] += count;
}
class Session {
public:
Session() : previous_(active) { active = &statistics_; }
~Session() {
active = previous_;
#if defined(_WIN32)
char* environment = nullptr;
std::size_t size = 0U;
if (_dupenv_s(&environment, &size, "FESA_HHT_PROBE_OUTPUT") != 0) return;
const std::string path = environment == nullptr ? "" : environment;
std::free(environment);
#else
const char* environment = std::getenv("FESA_HHT_PROBE_OUTPUT");
const std::string path = environment == nullptr ? "" : environment;
#endif
if (path.empty()) return;
std::ofstream output(path, std::ios::out | std::ios::binary);
constexpr const char* counters[] = {"outer_applied_correction", "inner_applied_correction",
"assembly_internal_evaluation", "assembly_inertia_evaluation",
"logical_factor_call", "logical_solve_call", "logical_rhs",
"constraint_internal_evaluation", "assembly_internal_failure"};
constexpr const char* stages[] = {"procedure_other", "inner_graph", "assembly",
"outer_linear", "physical_rates", "factor", "solve"};
for (int c = 0; c < kCounterCount; ++c)
output << counters[c] << '=' << statistics_.counts[c].load() << '\n';
output << "procedure_inclusive_ns=" << statistics_.inclusive_ns[kProcedure] << '\n';
for (int s = 0; s < kStageCount; ++s) {
output << stages[s] << "_calls=" << statistics_.stage_calls[s] << '\n';
output << stages[s] << "_exclusive_ns=" << statistics_.exclusive_ns[s] << '\n';
for (int c = 0; c < kCounterCount; ++c)
if (statistics_.by_stage[s][c] != 0U)
output << stages[s] << '_' << counters[c] << '='
<< statistics_.by_stage[s][c] << '\n';
}
}
private:
Statistics statistics_;
Statistics* previous_;
};
class PositiveSolver final : public LinearSolver {
public:
explicit PositiveSolver(LinearSolver& solver) : solver_(solver) {}
Status Factorize(const SparseMatrix& matrix) override {
Count(kFactorCall); Scope scope{kFactor}; return solver_.Factorize(matrix);
}
Status Solve(const Vector& rhs, Vector& solution) const override {
Count(kSolveCall); Count(kRhs); Scope scope{kSolve};
return solver_.Solve(rhs, solution);
}
private:
LinearSolver& solver_;
};
class CorrectionSolver final : public TangentSolver {
public:
explicit CorrectionSolver(TangentSolver& solver) : solver_(solver) {}
Status FactorizeFresh(const SparseMatrix& matrix) override {
Count(kFactorCall); Scope scope{kFactor}; return solver_.FactorizeFresh(matrix);
}
Status SolveCorrection(const Vector& rhs, Vector& solution) const override {
Count(kSolveCall); Count(kRhs); Scope scope{kSolve};
return solver_.SolveCorrection(rhs, solution);
}
private:
TangentSolver& solver_;
};
} // namespace fesa::hht_development_probe
#define FESA_HHT_PROBE_COUNT(counter) \
::fesa::hht_development_probe::Count(::fesa::hht_development_probe::counter)
#define FESA_HHT_PROBE_SCOPE(stage) \
::fesa::hht_development_probe::Scope probe_scope{ \
::fesa::hht_development_probe::stage}
#else
#define FESA_HHT_PROBE_COUNT(counter) ((void)0)
#define FESA_HHT_PROBE_SCOPE(stage) ((void)0)
#endif
#endif // FESA_ANALYSIS_HHT_DEVELOPMENT_PROBE_H_
@@ -12,6 +12,7 @@
#endif
#include "analysis/hht_dynamic_internal.h"
#include "analysis/hht_development_probe.h"
#include "math/dense_blas_internal.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/elements/finite_rotation.h"
@@ -382,6 +383,7 @@ Result<DynamicAssembly> EvaluateAssembly(
const Vector& load_scale, const double time_increment,
const HhtParameters& parameters, const ParallelFor& parallel_for,
const bool directional_third) {
FESA_HHT_PROBE_SCOPE(kAssembly);
const std::size_t count = dofs.FullDofCount();
for (const auto* values : std::array<const Vector*, 6>{
&frame.generalized_coordinates, &frame.velocity, &frame.acceleration,
@@ -446,9 +448,11 @@ Result<DynamicAssembly> EvaluateAssembly(
}
const auto before = retain_constraints ? ReadTrialEvaluationContext()
: TrialEvaluationContext{};
FESA_HHT_PROBE_COUNT(kInternalEvaluation);
auto f = shells[e].internal->EvaluateTrial(
trial, directional_third ? &v : nullptr);
if (!f.HasValue()) {
FESA_HHT_PROBE_COUNT(kInternalFailure);
failures[e] = f.GetStatus();
return;
}
@@ -456,6 +460,7 @@ Result<DynamicAssembly> EvaluateAssembly(
const auto after = ReadTrialEvaluationContext();
if (ReusableTrialContext(before, after)) evaluation_contexts[e] = after;
}
FESA_HHT_PROBE_COUNT(kInertiaEvaluation);
auto m = shells[e].inertia->Evaluate(trial, v, a);
if (!m.HasValue()) {
failures[e] = m.GetStatus();
@@ -1,4 +1,5 @@
#include "analysis/hht_reduced_internal.h"
#include "analysis/hht_development_probe.h"
#include <algorithm>
#include <cmath>
@@ -513,6 +514,7 @@ Result<ConstraintOperators> EvaluateConstraintOperators(
shell_index++, shells.size(), q, v);
std::optional<NonlinearMitc4InternalEquilibrium> fresh;
if (saved == nullptr) {
FESA_HHT_PROBE_COUNT(kConstraintInternalEvaluation);
auto internal =
shell.internal->EvaluateTrial(Trial(q), &v);
if (!internal.HasValue())
@@ -713,6 +715,7 @@ Result<EmbeddingRoot> SolveEmbeddingRoot(const FixedChart& chart,
const Vector& p, const Vector& seed,
const ConstraintEvaluator& evaluate,
TangentSolver& solver) {
FESA_HHT_PROBE_SCOPE(kInner);
if (!evaluate || p.Size() != chart.p_dofs.size() ||
seed.Size() != chart.z_dofs.size())
return Result<EmbeddingRoot>::Failure(
@@ -830,6 +833,7 @@ Result<EmbeddingRoot> SolveEmbeddingRoot(const FixedChart& chart,
prior_q = q.Value();
has_prior_z = true;
z = std::move(next_z);
FESA_HHT_PROBE_COUNT(kInnerCorrection);
}
return Result<EmbeddingRoot>::Failure(Failure("Unreachable graph loop."));
}
@@ -876,6 +880,7 @@ Result<EmbeddingFirstDerivatives> EvaluateEmbeddingFirstDerivatives(
Result<ReconstructedRates> ReconstructPhysicalRates(
const FixedChart& chart, const EmbeddingRoot& root, const Vector& vp,
const Vector& ap, TangentSolver& solver) {
FESA_HHT_PROBE_SCOPE(kRates);
if (!ValidBasis(chart) || root.q.Size() != chart.full_dof_count ||
root.operators.rows.size() != chart.z_dofs.size() ||
vp.Size() != chart.p_dofs.size() || ap.Size() != chart.p_dofs.size() ||
@@ -1,4 +1,5 @@
#include "analysis/hht_reduced_procedure.h"
#include "analysis/hht_development_probe.h"
#include "analysis/hht_reduced_integrator.h"
#include "equilibrium_algorithm.h"
@@ -396,8 +397,16 @@ Result<DynamicExecutionResult> ExecuteReducedHht(
TangentSolver& tangent_solver) {
const ReducedCoordinateShellHhtIntegrator integrator;
NewtonStepAlgorithm algorithm;
#if defined(FESA_HHT_DEVELOPMENT_PROBE)
hht_development_probe::Session probe_session;
hht_development_probe::PositiveSolver measured_positive{positive_solver};
hht_development_probe::CorrectionSolver measured_tangent{tangent_solver};
return ExecuteReducedHht(domain, log, parallel_for, measured_positive,
measured_tangent, integrator, algorithm);
#else
return ExecuteReducedHht(domain, log, parallel_for, positive_solver,
tangent_solver, integrator, algorithm);
#endif
}
Result<DynamicExecutionResult> ExecuteReducedHht(
@@ -405,6 +414,7 @@ Result<DynamicExecutionResult> ExecuteReducedHht(
const ParallelFor& parallel_for, LinearSolver& positive_solver,
TangentSolver& tangent_solver, const ShellHhtIntegrator& integrator,
EquilibriumAlgorithm& algorithm) {
FESA_HHT_PROBE_SCOPE(kProcedure);
if (domain.Dynamic() == nullptr)
return Result<DynamicExecutionResult>::Failure(Status::Failure(
FailureCategory::kModel, {{Severity::kError, "invalid-dynamic-step", {},
@@ -1016,6 +1026,7 @@ Result<DynamicExecutionResult> ExecuteReducedHht(
};
Vector selected_solution{selected.Value().rhs.Size()};
if (selected_solution.Size() > 0U) {
FESA_HHT_PROBE_SCOPE(kOuterSolve);
status = tangent_solver.FactorizeFresh(selected.Value().tangent);
if (!status.IsOk())
return linear_failure(status);
@@ -1091,6 +1102,7 @@ Result<DynamicExecutionResult> ExecuteReducedHht(
seed = trial.Value().root.z;
for (std::size_t i = 0U; i < seed.Size(); ++i)
seed[i] += correction.Value().z[i];
FESA_HHT_PROBE_COUNT(kOuterCorrection);
return StepResult::Success(std::nullopt);
},
kMaximumOuterCorrections);