Integrate Refactor solver sources and dual MSVC CMake builds
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
#include "fesa/analysis/solid_static_procedure.h"
|
||||
|
||||
#include "equilibrium_algorithm.h"
|
||||
#include "analysis/solid_static_core.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
@@ -21,6 +24,8 @@
|
||||
#include "fesa/solvers/linear/linear_solver.h"
|
||||
#include "fesa/solvers/linear/tangent_solver.h"
|
||||
|
||||
#include "assembly/ordered_contribution_internal.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
constexpr std::uint32_t kMaximumIterations = 100U;
|
||||
@@ -28,7 +33,7 @@ constexpr double kRoundoffBudget =
|
||||
512.0 * std::numeric_limits<double>::epsilon();
|
||||
|
||||
struct RuntimeSolid {
|
||||
const Hex8Element* element;
|
||||
std::unique_ptr<SolidTrialElement> element;
|
||||
std::vector<std::size_t> scatter;
|
||||
};
|
||||
struct SolidAssembly {
|
||||
@@ -131,9 +136,8 @@ Result<SolidAssembly> Assemble(const std::vector<RuntimeSolid>& solids,
|
||||
const auto& response = responses[e]->Value();
|
||||
for (std::size_t i = 0U; i < 24U; ++i) {
|
||||
force[solids[e].scatter[i]] += response.internal_force[i];
|
||||
for (std::size_t j = 0U; j < 24U; ++j)
|
||||
contributions.push_back({solids[e].scatter[i], solids[e].scatter[j],
|
||||
response.tangent(i, j), e, 24U * i + j});
|
||||
assembly_internal::AppendLocalMatrixRow(
|
||||
response.tangent, solids[e].scatter, i, e, contributions);
|
||||
}
|
||||
energy += response.strain_energy;
|
||||
points.insert(points.end(), response.points.begin(), response.points.end());
|
||||
@@ -283,6 +287,13 @@ SolidStaticProcedureExecutor::SolidStaticProcedureExecutor(
|
||||
|
||||
Status SolidStaticProcedureExecutor::ExecuteInto(
|
||||
const Domain& domain, SolidStaticState& state) const {
|
||||
NewtonStepAlgorithm algorithm;
|
||||
return SolidStaticCore(parallel_for_, linear_solver_, tangent_solver_, algorithm)
|
||||
.ExecuteInto(domain, state);
|
||||
}
|
||||
|
||||
Status SolidStaticCore::ExecuteInto(
|
||||
const Domain& domain, SolidStaticState& state) const {
|
||||
if (state.initialized_)
|
||||
return Failure(domain, "solid-static-observer-not-fresh",
|
||||
"Execution requires a fresh static observer.");
|
||||
@@ -309,14 +320,14 @@ Status SolidStaticProcedureExecutor::ExecuteInto(
|
||||
state = std::move(baseline.Value());
|
||||
std::vector<RuntimeSolid> solids;
|
||||
for (const auto& element : elements) {
|
||||
const auto* hex = dynamic_cast<const Hex8Element*>(element.get());
|
||||
auto hex = ElementFactory{}.BindSolidTrial(*element);
|
||||
if (hex == nullptr)
|
||||
return Failure(domain, "invalid-solid-static-element",
|
||||
"Solid STATIC requires full-integration Hex8 kernels.",
|
||||
FailureCategory::kModel);
|
||||
auto scatter = dofs.ElementScatter(hex->DofLayout());
|
||||
if (!scatter.HasValue()) return scatter.GetStatus();
|
||||
solids.push_back({hex, std::move(scatter.Value())});
|
||||
solids.push_back({std::move(hex), std::move(scatter.Value())});
|
||||
}
|
||||
auto stiffness = SparseAssembler::Assemble(view, dofs, parallel_for_);
|
||||
if (!stiffness.HasValue()) return stiffness.GetStatus();
|
||||
@@ -336,7 +347,7 @@ Status SolidStaticProcedureExecutor::ExecuteInto(
|
||||
// Preserve the required factorization-before-load-assembly lifecycle.
|
||||
auto load = LoadAssembler::AssembleFullNodalLoad(model.Value(), dofs);
|
||||
if (!load.HasValue()) return load.GetStatus();
|
||||
auto rhs = LoadAssembler::EffectiveFreeRhs(
|
||||
auto rhs = constraints.EffectiveFreeRhs(
|
||||
load.Value(), partition.Value().k_fc, dofs.PrescribedValues(), dofs);
|
||||
if (!rhs.HasValue()) return rhs.GetStatus();
|
||||
Vector free{dofs.FreeDofCount()};
|
||||
@@ -369,91 +380,128 @@ Status SolidStaticProcedureExecutor::ExecuteInto(
|
||||
for (std::size_t c = 0U; c < dofs.ConstrainedDofCount(); ++c)
|
||||
trial[dofs.ConstrainedDofs()[c]] =
|
||||
increment.load_factor * dofs.PrescribedValues()[c];
|
||||
for (std::uint32_t iteration = 1U; iteration <= kMaximumIterations;
|
||||
++iteration) {
|
||||
struct SolidStepCandidate {
|
||||
SolidStaticFrame frame;
|
||||
SolidStaticIterationRecord record;
|
||||
record.increment_index = increment.increment_index;
|
||||
record.load_factor = increment.load_factor;
|
||||
record.iteration = iteration;
|
||||
record.stage = "entered";
|
||||
const auto fail = [&](const Status& failure) {
|
||||
record.diagnostic_code = failure.Diagnostics().empty()
|
||||
? "solid-static-failure"
|
||||
: failure.Diagnostics()[0].code;
|
||||
record.failure_reason = failure.Diagnostics().empty()
|
||||
? "Static trial failed."
|
||||
: failure.Diagnostics()[0].message;
|
||||
state.iterations_.push_back(record);
|
||||
return failure;
|
||||
};
|
||||
record.stage = "trial-recovery";
|
||||
auto assembly = Assemble(solids, dofs, trial, true, parallel_for_);
|
||||
if (!assembly.HasValue()) return fail(assembly.GetStatus());
|
||||
SolidStaticFrame frame = state.committed_;
|
||||
frame.balance = {};
|
||||
frame.global_equilibrium = {};
|
||||
frame.frame_index =
|
||||
static_cast<std::uint32_t>(state.accepted_frames_.size());
|
||||
frame.increment_index = increment.increment_index;
|
||||
frame.load_factor = increment.load_factor;
|
||||
frame.iteration_count = iteration;
|
||||
frame.is_factor_one = increment.is_factor_one;
|
||||
frame.displacement = trial;
|
||||
frame.internal_force = std::move(assembly.Value().force);
|
||||
frame.external_force = base_load.Value();
|
||||
frame.external_force.Scale(increment.load_factor);
|
||||
frame.solid_results = std::move(assembly.Value().points);
|
||||
frame.physical_strain_energy = assembly.Value().energy;
|
||||
auto status = CompleteFrame(domain, dofs, true, stiffness.Value(), frame);
|
||||
if (!status.IsOk()) return fail(status);
|
||||
record.stage = "tangent-partition";
|
||||
auto partition = constraints.Partition(assembly.Value().tangent, dofs);
|
||||
if (!partition.HasValue()) return fail(partition.GetStatus());
|
||||
Vector correction{dofs.FreeDofCount()};
|
||||
// Empty free is vacuous; prescribed geometry/recovery above still runs.
|
||||
if (dofs.FreeDofCount() != 0U) {
|
||||
record.stage = "tangent-factorization";
|
||||
status = tangent_solver_.FactorizeFresh(partition.Value().k_ff);
|
||||
if (!status.IsOk()) return fail(status);
|
||||
auto rhs = constraints.GatherFree(frame.residual, dofs);
|
||||
rhs.Scale(-1.0);
|
||||
record.stage = "correction-solve";
|
||||
status = tangent_solver_.SolveCorrection(rhs, correction);
|
||||
if (!status.IsOk()) return fail(status);
|
||||
}
|
||||
Vector current_increment{dofs.FreeDofCount()};
|
||||
for (std::size_t f = 0U; f < dofs.FreeDofCount(); ++f) {
|
||||
const auto full = dofs.FreeDofs()[f];
|
||||
current_increment[f] = trial[full] - start[full];
|
||||
}
|
||||
record.stage = "convergence";
|
||||
auto convergence = EvaluateSolidStaticConvergence(
|
||||
constraints.GatherFree(frame.residual, dofs),
|
||||
constraints.GatherFree(frame.internal_force, dofs),
|
||||
constraints.GatherFree(frame.external_force, dofs), correction,
|
||||
current_increment);
|
||||
if (!convergence.HasValue()) return fail(convergence.GetStatus());
|
||||
record.force_residual = convergence.Value().force_residual;
|
||||
record.translation_correction =
|
||||
convergence.Value().translation_correction;
|
||||
record.convergence_evaluated = true;
|
||||
if (record.force_residual.passed &&
|
||||
record.translation_correction.passed) {
|
||||
record.stage = "commit";
|
||||
status = state.CommitAcceptedFrame(frame);
|
||||
if (!status.IsOk()) return fail(status);
|
||||
record.accepted = true;
|
||||
state.iterations_.push_back(record);
|
||||
break;
|
||||
}
|
||||
if (iteration == kMaximumIterations)
|
||||
return fail(Failure(domain, "solid-static-nonconvergence",
|
||||
"Solid STATIC exceeded 100 Newton iterations."));
|
||||
for (std::size_t f = 0U; f < dofs.FreeDofCount(); ++f)
|
||||
trial[dofs.FreeDofs()[f]] += correction[f];
|
||||
record.stage = "continue";
|
||||
state.iterations_.push_back(record);
|
||||
};
|
||||
const auto observe_iteration =
|
||||
[&](const SolidStaticIterationRecord& record) {
|
||||
state.iterations_.push_back(record);
|
||||
};
|
||||
SolidFullNewtonStep<SolidStepCandidate> iteration_step(
|
||||
[&](std::uint32_t iteration)
|
||||
-> Result<std::optional<SolidStepCandidate>> {
|
||||
SolidStaticIterationRecord record;
|
||||
record.increment_index = increment.increment_index;
|
||||
record.load_factor = increment.load_factor;
|
||||
record.iteration = iteration;
|
||||
record.stage = "entered";
|
||||
const auto fail = [&](const Status& failure) {
|
||||
record.diagnostic_code = failure.Diagnostics().empty()
|
||||
? "solid-static-failure"
|
||||
: failure.Diagnostics()[0].code;
|
||||
record.failure_reason = failure.Diagnostics().empty()
|
||||
? "Static trial failed."
|
||||
: failure.Diagnostics()[0].message;
|
||||
observe_iteration(record);
|
||||
return Result<std::optional<SolidStepCandidate>>::Failure(failure);
|
||||
};
|
||||
record.stage = "trial-recovery";
|
||||
auto assembly = Assemble(solids, dofs, trial, true, parallel_for_);
|
||||
if (!assembly.HasValue())
|
||||
return fail(assembly.GetStatus());
|
||||
SolidStaticFrame frame = state.committed_;
|
||||
frame.balance = {};
|
||||
frame.global_equilibrium = {};
|
||||
frame.frame_index =
|
||||
static_cast<std::uint32_t>(state.accepted_frames_.size());
|
||||
frame.increment_index = increment.increment_index;
|
||||
frame.load_factor = increment.load_factor;
|
||||
frame.iteration_count = iteration;
|
||||
frame.is_factor_one = increment.is_factor_one;
|
||||
frame.displacement = trial;
|
||||
frame.internal_force = std::move(assembly.Value().force);
|
||||
frame.external_force = base_load.Value();
|
||||
frame.external_force.Scale(increment.load_factor);
|
||||
frame.solid_results = std::move(assembly.Value().points);
|
||||
frame.physical_strain_energy = assembly.Value().energy;
|
||||
auto status =
|
||||
CompleteFrame(domain, dofs, true, stiffness.Value(), frame);
|
||||
if (!status.IsOk())
|
||||
return fail(status);
|
||||
record.stage = "tangent-partition";
|
||||
auto partition =
|
||||
constraints.Partition(assembly.Value().tangent, dofs);
|
||||
if (!partition.HasValue())
|
||||
return fail(partition.GetStatus());
|
||||
Vector correction{dofs.FreeDofCount()};
|
||||
// Empty free is vacuous; prescribed geometry/recovery above still
|
||||
// runs.
|
||||
if (dofs.FreeDofCount() != 0U) {
|
||||
record.stage = "tangent-factorization";
|
||||
status = tangent_solver_.FactorizeFresh(partition.Value().k_ff);
|
||||
if (!status.IsOk())
|
||||
return fail(status);
|
||||
auto rhs = constraints.GatherFree(frame.residual, dofs);
|
||||
rhs.Scale(-1.0);
|
||||
record.stage = "correction-solve";
|
||||
status = tangent_solver_.SolveCorrection(rhs, correction);
|
||||
if (!status.IsOk())
|
||||
return fail(status);
|
||||
}
|
||||
Vector current_increment{dofs.FreeDofCount()};
|
||||
for (std::size_t f = 0U; f < dofs.FreeDofCount(); ++f) {
|
||||
const auto full = dofs.FreeDofs()[f];
|
||||
current_increment[f] = trial[full] - start[full];
|
||||
}
|
||||
record.stage = "convergence";
|
||||
auto convergence = EvaluateSolidStaticConvergence(
|
||||
constraints.GatherFree(frame.residual, dofs),
|
||||
constraints.GatherFree(frame.internal_force, dofs),
|
||||
constraints.GatherFree(frame.external_force, dofs), correction,
|
||||
current_increment);
|
||||
if (!convergence.HasValue())
|
||||
return fail(convergence.GetStatus());
|
||||
record.force_residual = convergence.Value().force_residual;
|
||||
record.translation_correction =
|
||||
convergence.Value().translation_correction;
|
||||
record.convergence_evaluated = true;
|
||||
if (record.force_residual.passed &&
|
||||
record.translation_correction.passed) {
|
||||
record.stage = "commit";
|
||||
return Result<std::optional<SolidStepCandidate>>::Success(
|
||||
SolidStepCandidate{std::move(frame), record});
|
||||
}
|
||||
if (iteration == kMaximumIterations)
|
||||
return fail(
|
||||
Failure(domain, "solid-static-nonconvergence",
|
||||
"Solid STATIC exceeded 100 Newton iterations."));
|
||||
for (std::size_t f = 0U; f < dofs.FreeDofCount(); ++f)
|
||||
trial[dofs.FreeDofs()[f]] += correction[f];
|
||||
record.stage = "continue";
|
||||
observe_iteration(record);
|
||||
return Result<std::optional<SolidStepCandidate>>::Success(
|
||||
std::nullopt);
|
||||
},
|
||||
kMaximumIterations);
|
||||
const Status solved = iteration_step.Solve(algorithm_);
|
||||
if (!solved.IsOk())
|
||||
return solved;
|
||||
auto candidate = iteration_step.TakeCandidate();
|
||||
auto& record = candidate.record;
|
||||
const Status committed = state.CommitAcceptedFrame(candidate.frame);
|
||||
if (!committed.IsOk()) {
|
||||
record.diagnostic_code = committed.Diagnostics().empty()
|
||||
? "solid-static-failure"
|
||||
: committed.Diagnostics()[0].code;
|
||||
record.failure_reason = committed.Diagnostics().empty()
|
||||
? "Static trial failed."
|
||||
: committed.Diagnostics()[0].message;
|
||||
observe_iteration(record);
|
||||
return committed;
|
||||
}
|
||||
record.accepted = true;
|
||||
observe_iteration(record);
|
||||
}
|
||||
return Status::Ok();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user