Integrate Refactor solver sources and dual MSVC CMake builds

This commit is contained in:
KOKO\Mimi
2026-10-18 02:10:51 +09:00
parent 7ee32e5837
commit 12d51480de
218 changed files with 17842 additions and 9689 deletions
+204 -164
View File
@@ -1,5 +1,9 @@
#include "fesa/analysis/solid_hht_dynamic_procedure.h"
#include "equilibrium_algorithm.h"
#include "analysis/solid_hht_core.h"
#include "solid_hht_integrator.h"
#include <algorithm>
#include <array>
#include <cmath>
@@ -85,7 +89,7 @@ Status CheckArithmeticEnvironment(const Domain& domain) {
/// @brief Owns immutable kernels and the stable full-space translation scatter.
struct RuntimeSolid {
std::unique_ptr<Element> owner;
const Hex8Element* solid;
std::unique_ptr<SolidTrialElement> solid;
std::vector<std::size_t> scatter;
};
/// @brief Creates checked kernels in declaration order, with no mutable element
@@ -98,7 +102,7 @@ Result<std::vector<RuntimeSolid>> BuildSolids(const Domain& domain,
auto owner = factory.Create(domain.Elements()[e], domain);
if (!owner.HasValue())
return Result<std::vector<RuntimeSolid>>::Failure(owner.GetStatus());
const auto* solid = dynamic_cast<const Hex8Element*>(owner.Value().get());
auto solid = factory.BindSolidTrial(*owner.Value());
if (solid == nullptr)
return Result<std::vector<RuntimeSolid>>::Failure(
Failure(domain, "solid-dynamic-element-family",
@@ -107,7 +111,7 @@ Result<std::vector<RuntimeSolid>> BuildSolids(const Domain& domain,
if (!scatter.HasValue())
return Result<std::vector<RuntimeSolid>>::Failure(scatter.GetStatus());
solids.push_back(
{std::move(owner.Value()), solid, std::move(scatter.Value())});
{std::move(owner.Value()), std::move(solid), std::move(scatter.Value())});
}
return Result<std::vector<RuntimeSolid>>::Success(std::move(solids));
}
@@ -239,7 +243,7 @@ Result<double> ReferenceLength(const Domain& domain) {
"Reference length is not finite positive."));
return Result<double>::Success(length);
}
/// @brief Forms M/(beta*h²)+(1+alpha)*Kt in the unchanged full sparse pattern.
/// @brief Forms M/(beta*h^2)+(1+alpha)*Kt in the unchanged full sparse pattern.
Result<SparseMatrix> EffectiveTangent(const SparseMatrix& m,
const PairedTangent& k,
const DofManager& dofs, double h,
@@ -549,24 +553,6 @@ bool CorrectionPass(const std::array<HighLow, 4>& metrics) {
Compare(x, HighLow(1e-10)) <= 0;
});
}
Status SetRates(const SolidDynamicAcceptedFrameState& old,
const detail::SolidHhtPairedPredictor& predictor, double h,
const HhtParameters& p,
SolidDynamicAcceptedFrameState* candidate) {
auto rates = detail::EvaluateSolidHhtHighLowRates(
candidate->displacement, candidate->low.displacement, predictor,
old.acceleration, old.low.acceleration, h, p);
if (!rates.HasValue()) return rates.GetStatus();
candidate->velocity = std::move(rates.Value().velocity);
candidate->low.velocity = std::move(rates.Value().velocity_low);
candidate->acceleration = std::move(rates.Value().acceleration);
candidate->low.acceleration = std::move(rates.Value().acceleration_low);
candidate->algorithmic_acceleration =
std::move(rates.Value().algorithmic_acceleration);
candidate->low.algorithmic_acceleration =
std::move(rates.Value().algorithmic_acceleration_low);
return Status::Ok();
}
} // namespace
SolidHhtDynamicProcedureExecutor::SolidHhtDynamicProcedureExecutor(
@@ -600,6 +586,17 @@ Status SolidHhtDynamicProcedureExecutor::ExecuteInto(
Status SolidHhtDynamicProcedureExecutor::ExecuteIntoForVerification(
const Domain& domain, SolidDynamicAnalysisState& state,
double alpha) const {
const detail::PairedSolidHhtIntegrator integrator;
NewtonStepAlgorithm algorithm;
return ExecuteSolidHhtCore(domain, state, alpha, parallel_for_, mass_solver_,
tangent_solver_, integrator, algorithm);
}
Status ExecuteSolidHhtCore(
const Domain& domain, SolidDynamicAnalysisState& state, double alpha,
const ParallelFor& parallel_for, LinearSolver& mass_solver,
TangentSolver& tangent_solver, const detail::SolidHhtIntegrator& integrator,
EquilibriumAlgorithm& algorithm) {
SolidDynamicIterationRecord record;
auto fail = [&](const Status& failure) {
record.stage = "failed";
@@ -630,7 +627,7 @@ Status SolidHhtDynamicProcedureExecutor::ExecuteIntoForVerification(
auto mass_partition = partition.Partition(mass.Value(), dofs);
if (!mass_partition.HasValue()) return fail(mass_partition.GetStatus());
if (dofs.FreeDofCount() != 0) {
const auto status = mass_solver_.Factorize(mass_partition.Value().k_ff);
const auto status = mass_solver.Factorize(mass_partition.Value().k_ff);
if (!status.IsOk()) return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk()) return fail(environment);
@@ -655,7 +652,7 @@ Status SolidHhtDynamicProcedureExecutor::ExecuteIntoForVerification(
if (!force.HasValue()) return fail(force.GetStatus());
initial.external_force = std::move(force.Value());
auto tangent =
EvaluateSolids(domain, solids.Value(), dofs, parallel_for_, &initial);
EvaluateSolids(domain, solids.Value(), dofs, parallel_for, &initial);
if (!tangent.HasValue()) return fail(tangent.GetStatus());
SolidDynamicIterationEvidence evidence;
bool initial_converged = false;
@@ -682,7 +679,7 @@ Status SolidHhtDynamicProcedureExecutor::ExecuteIntoForVerification(
const auto i = dofs.FreeDofs()[j];
rhs[j] = Rounded(Negate(Pair(initial.residual, initial.low.residual, i)));
}
status = mass_solver_.Solve(rhs, a);
status = mass_solver.Solve(rhs, a);
if (!status.IsOk()) return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk()) return fail(environment);
@@ -726,145 +723,188 @@ Status SolidHhtDynamicProcedureExecutor::ExecuteIntoForVerification(
record = {};
record.increment_index = increment;
record.time = static_cast<double>(increment) * h;
auto predictor = detail::PredictSolidHhtHighLow(old, h, p);
if (!predictor.HasValue()) return fail(predictor.GetStatus());
auto candidate = old;
candidate.frame_index = candidate.increment_index = increment;
candidate.iteration_count = 0;
candidate.time = record.time;
candidate.is_initial = false;
candidate.iterations.clear();
// Begin from certified accepted geometry, not an unchecked predictor.
force = LoadAssembler::AssembleFullNodalLoad(model.Value(), dofs,
candidate.time);
if (!force.HasValue()) return fail(force.GetStatus());
candidate.external_force = std::move(force.Value());
candidate.low.external_force = Vector(dofs.FullDofCount());
std::array<HighLow, 4> correction{}, fresh_correction{};
bool accepted = false;
for (std::uint32_t iteration = 0; iteration <= 100; ++iteration) {
record.stage = "evaluation";
record.outcome = "paired-candidate";
record.evidence = {};
record.evidence.iteration_index = iteration;
status = SetRates(old, predictor.Value(), h, p, &candidate);
if (!status.IsOk()) return fail(status);
tangent = EvaluateSolids(domain, solids.Value(), dofs, parallel_for_,
&candidate);
if (!tangent.HasValue()) return fail(tangent.GetStatus());
status = RecoverEndpoint(domain, dofs, mass.Value(), &old, initial_energy,
&candidate, &record.evidence);
if (!status.IsOk()) return fail(status);
auto MA = detail::MultiplySolidHhtMassHighLow(
mass.Value(), candidate.algorithmic_acceleration,
candidate.low.algorithmic_acceleration);
if (!MA.HasValue()) return fail(MA.GetStatus());
Vector rhs(dofs.FreeDofCount());
HighLow identity_metric, original_norm, solved_norm, old_norm;
for (std::size_t j = 0; j < dofs.FreeDofCount(); ++j) {
const auto i = dofs.FreeDofs()[j];
detail::SolidHhtPairedForceIdentity identity;
if (!detail::EvaluateSolidHhtHighLowForceIdentity(
mass.Value(), i, Pair(MA.Value().high, MA.Value().low, i),
candidate, old, p.alpha, &identity))
return fail(
Failure(domain, "solid-dynamic-nonfinite-residual",
"Paired weighted force arithmetic is unsupported."));
rhs[j] = Rounded(Negate(identity.solved));
identity_metric = Maximum(identity_metric, identity.metric);
original_norm = Maximum(original_norm, Abs(identity.original));
solved_norm = Maximum(solved_norm, Abs(identity.solved));
old_norm =
Maximum(old_norm, Abs(Pair(old.residual, old.low.residual, i)));
if (i == record.evidence.worst_full_dof)
record.evidence.worst_weighted_terms = {
Rounded(identity.original), Rounded(identity.solved),
Rounded(Pair(old.residual, old.low.residual, i)),
Rounded(identity.scale)};
}
if (!Valid(identity_metric) || !Valid(original_norm) ||
!Valid(solved_norm) || !Valid(old_norm) ||
Compare(identity_metric, HighLow(1e-12)) > 0)
return fail(Failure(domain,
"solid-dynamic-weighted-algebra-inconsistency",
"Paired original and solved forces disagree beyond "
"their uncancelled scale."));
record.evidence.weighted_equivalence_metric = Rounded(identity_metric);
record.evidence.weighted_residual_numerator = Rounded(original_norm);
record.evidence.recombined_residual_numerator = Rounded(solved_norm);
record.evidence.old_residual_numerator = Rounded(old_norm);
for (std::size_t c = 0; c < 4; ++c) {
if (!Valid(correction[c]) || !Valid(fresh_correction[c]))
return fail(detail::SolidHhtPairedKinematicsFailure());
record.evidence.correction_metrics[c] = Rounded(correction[c]);
}
record.evidence.correction_pass =
CorrectionPass(correction) && CorrectionPass(fresh_correction);
const auto update = CheckUpdates(old, candidate, p, h);
if (!Valid(update))
return fail(detail::SolidHhtPairedKinematicsFailure());
record.evidence.update_metric = Rounded(update);
record.evidence.update_pass = Compare(update, HighLow(1e-12)) <= 0;
candidate.iterations.push_back(record.evidence);
candidate.iteration_count = iteration;
if (record.evidence.residual_pass && record.evidence.correction_pass &&
record.evidence.update_pass) {
status = state.CommitAcceptedFrame(candidate);
if (!status.IsOk()) return fail(status);
record.stage = "accepted";
record.outcome = "increment";
state.RecordIteration(record);
accepted = true;
break;
}
state.RecordIteration(record);
if (iteration == 100)
return fail(Failure(
domain, "solid-dynamic-nonconvergence",
"Paired HHT endpoint did not converge in100 Newton solves."));
auto effective =
EffectiveTangent(mass.Value(), tangent.Value(), dofs, h, p);
if (!effective.HasValue()) return fail(effective.GetStatus());
auto reduced = partition.Partition(effective.Value(), dofs);
if (!reduced.HasValue()) return fail(reduced.GetStatus());
Vector du(dofs.FreeDofCount());
if (dofs.FreeDofCount() != 0) {
status = tangent_solver_.FactorizeFresh(reduced.Value().k_ff);
if (!status.IsOk()) return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk()) return fail(environment);
status = tangent_solver_.SolveCorrection(rhs, du);
if (!status.IsOk()) return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk()) return fail(environment);
}
if (du.Size() != dofs.FreeDofCount())
return fail(Failure(domain, "solid-dynamic-invalid-backend-result",
"Tangent solve returned incompatible correction."));
for (std::size_t j = 0; j < du.Size(); ++j)
if (!std::isfinite(du[j]))
return fail(Failure(domain, "solid-dynamic-invalid-backend-result",
"Tangent solve returned nonfinite correction."));
fresh_correction = FreshCorrectionMetrics(du, length.Value(), p);
const auto before = candidate;
for (std::size_t j = 0; j < du.Size(); ++j) {
const auto i = dofs.FreeDofs()[j];
const auto value =
Add(Pair(candidate.displacement, candidate.low.displacement, i),
HighLow(du[j] == 0 ? 0.0 : du[j]));
if (!Valid(value))
return fail(detail::SolidHhtPairedKinematicsFailure());
Store(value, &candidate.displacement, &candidate.low.displacement, i);
}
status = SetRates(old, predictor.Value(), h, p, &candidate);
if (!status.IsOk()) return fail(status);
correction =
ObservedCorrectionMetrics(before, candidate, h, length.Value());
}
if (!accepted)
return fail(Failure(domain, "solid-dynamic-nonconvergence",
"No complete paired endpoint was accepted."));
status = detail::RunSolidHhtStep(
integrator, old, h, p, fail,
[&](const detail::SolidHhtIntegrator& step_integrator,
const detail::SolidHhtPairedPredictor& predictor)
-> Result<SolidDynamicAcceptedFrameState> {
auto candidate = old;
candidate.frame_index = candidate.increment_index = increment;
candidate.iteration_count = 0;
candidate.time = record.time;
candidate.is_initial = false;
candidate.iterations.clear();
// Begin from certified accepted geometry, not an unchecked predictor.
force = LoadAssembler::AssembleFullNodalLoad(model.Value(), dofs,
candidate.time);
if (!force.HasValue())
return Result<SolidDynamicAcceptedFrameState>::Failure(fail(force.GetStatus()));
candidate.external_force = std::move(force.Value());
candidate.low.external_force = Vector(dofs.FullDofCount());
std::array<HighLow, 4> correction{}, fresh_correction{};
using StepResult = Result<std::optional<SolidDynamicAcceptedFrameState>>;
const auto report_failure = fail;
const auto record_iteration = [&](const auto& value) {
state.RecordIteration(value);
};
SolidHhtStep<SolidDynamicAcceptedFrameState> iteration_step(
[&](std::uint32_t iteration) -> StepResult {
const auto fail = [&](const Status& failure) {
return StepResult::Failure(report_failure(failure));
};
record.stage = "evaluation";
record.outcome = "paired-candidate";
record.evidence = {};
record.evidence.iteration_index = iteration;
status =
detail::SetSolidHhtRates(step_integrator, old, predictor, h, p, &candidate);
if (!status.IsOk())
return fail(status);
tangent = EvaluateSolids(domain, solids.Value(), dofs, parallel_for,
&candidate);
if (!tangent.HasValue())
return fail(tangent.GetStatus());
status =
RecoverEndpoint(domain, dofs, mass.Value(), &old, initial_energy,
&candidate, &record.evidence);
if (!status.IsOk())
return fail(status);
auto MA = detail::MultiplySolidHhtMassHighLow(
mass.Value(), candidate.algorithmic_acceleration,
candidate.low.algorithmic_acceleration);
if (!MA.HasValue())
return fail(MA.GetStatus());
Vector rhs(dofs.FreeDofCount());
HighLow identity_metric, original_norm, solved_norm, old_norm;
for (std::size_t j = 0; j < dofs.FreeDofCount(); ++j) {
const auto i = dofs.FreeDofs()[j];
detail::SolidHhtPairedForceIdentity identity;
if (!detail::EvaluateSolidHhtHighLowForceIdentity(
mass.Value(), i, Pair(MA.Value().high, MA.Value().low, i),
candidate, old, p.alpha, &identity))
return fail(
Failure(domain, "solid-dynamic-nonfinite-residual",
"Paired weighted force arithmetic is unsupported."));
rhs[j] = Rounded(Negate(identity.solved));
identity_metric = Maximum(identity_metric, identity.metric);
original_norm = Maximum(original_norm, Abs(identity.original));
solved_norm = Maximum(solved_norm, Abs(identity.solved));
old_norm =
Maximum(old_norm, Abs(Pair(old.residual, old.low.residual, i)));
if (i == record.evidence.worst_full_dof)
record.evidence.worst_weighted_terms = {
Rounded(identity.original), Rounded(identity.solved),
Rounded(Pair(old.residual, old.low.residual, i)),
Rounded(identity.scale)};
}
if (!Valid(identity_metric) || !Valid(original_norm) ||
!Valid(solved_norm) || !Valid(old_norm) ||
Compare(identity_metric, HighLow(1e-12)) > 0)
return fail(
Failure(domain, "solid-dynamic-weighted-algebra-inconsistency",
"Paired original and solved forces disagree beyond "
"their uncancelled scale."));
record.evidence.weighted_equivalence_metric =
Rounded(identity_metric);
record.evidence.weighted_residual_numerator = Rounded(original_norm);
record.evidence.recombined_residual_numerator = Rounded(solved_norm);
record.evidence.old_residual_numerator = Rounded(old_norm);
for (std::size_t c = 0; c < 4; ++c) {
if (!Valid(correction[c]) || !Valid(fresh_correction[c]))
return fail(detail::SolidHhtPairedKinematicsFailure());
record.evidence.correction_metrics[c] = Rounded(correction[c]);
}
record.evidence.correction_pass =
CorrectionPass(correction) && CorrectionPass(fresh_correction);
const auto update = CheckUpdates(old, candidate, p, h);
if (!Valid(update))
return fail(detail::SolidHhtPairedKinematicsFailure());
record.evidence.update_metric = Rounded(update);
record.evidence.update_pass = Compare(update, HighLow(1e-12)) <= 0;
candidate.iterations.push_back(record.evidence);
candidate.iteration_count = iteration;
if (record.evidence.residual_pass &&
record.evidence.correction_pass && record.evidence.update_pass) {
return StepResult::Success(candidate);
}
record_iteration(record);
if (iteration == 100)
return fail(Failure(
domain, "solid-dynamic-nonconvergence",
"Paired HHT endpoint did not converge in100 Newton solves."));
auto effective =
EffectiveTangent(mass.Value(), tangent.Value(), dofs, h, p);
if (!effective.HasValue())
return fail(effective.GetStatus());
auto reduced = partition.Partition(effective.Value(), dofs);
if (!reduced.HasValue())
return fail(reduced.GetStatus());
Vector du(dofs.FreeDofCount());
if (dofs.FreeDofCount() != 0) {
status = tangent_solver.FactorizeFresh(reduced.Value().k_ff);
if (!status.IsOk())
return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk())
return fail(environment);
status = tangent_solver.SolveCorrection(rhs, du);
if (!status.IsOk())
return fail(status);
environment = CheckArithmeticEnvironment(domain);
if (!environment.IsOk())
return fail(environment);
}
if (du.Size() != dofs.FreeDofCount())
return fail(
Failure(domain, "solid-dynamic-invalid-backend-result",
"Tangent solve returned incompatible correction."));
for (std::size_t j = 0; j < du.Size(); ++j)
if (!std::isfinite(du[j]))
return fail(
Failure(domain, "solid-dynamic-invalid-backend-result",
"Tangent solve returned nonfinite correction."));
fresh_correction = FreshCorrectionMetrics(du, length.Value(), p);
const auto before = candidate;
for (std::size_t j = 0; j < du.Size(); ++j) {
const auto i = dofs.FreeDofs()[j];
const auto value =
Add(Pair(candidate.displacement, candidate.low.displacement, i),
HighLow(du[j] == 0 ? 0.0 : du[j]));
if (!Valid(value))
return fail(detail::SolidHhtPairedKinematicsFailure());
Store(value, &candidate.displacement, &candidate.low.displacement,
i);
}
status =
detail::SetSolidHhtRates(step_integrator, old, predictor, h, p, &candidate);
if (!status.IsOk())
return fail(status);
correction =
ObservedCorrectionMetrics(before, candidate, h, length.Value());
return StepResult::Success(std::nullopt);
},
100U);
const Status solved = iteration_step.Solve(algorithm);
if (!solved.IsOk())
return Result<SolidDynamicAcceptedFrameState>::Failure(solved);
if (!iteration_step.HasCandidate())
return Result<SolidDynamicAcceptedFrameState>::Failure(
fail(Failure(domain, "solid-dynamic-nonconvergence",
"No complete paired endpoint was accepted.")));
return Result<SolidDynamicAcceptedFrameState>::Success(
iteration_step.TakeCandidate());
},
[&](SolidDynamicAcceptedFrameState&& accepted_candidate) -> Status {
status = state.CommitAcceptedFrame(accepted_candidate);
if (!status.IsOk())
return fail(status);
record.stage = "accepted";
record.outcome = "increment";
state.RecordIteration(record);
return Status::Ok();
});
if (!status.IsOk()) return status;
}
return Status::Ok();
}
} // namespace fesa
} // namespace fesa