472 lines
22 KiB
C++
472 lines
22 KiB
C++
#include "fesa/analysis/solid_static_procedure.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "fesa/analysis/analysis_model.h"
|
|
#include "fesa/assembly/load_assembler.h"
|
|
#include "fesa/assembly/parallel_for.h"
|
|
#include "fesa/assembly/sparse_assembler.h"
|
|
#include "fesa/constraints/essential_constraint_policy.h"
|
|
#include "fesa/elements/element_factory.h"
|
|
#include "fesa/elements/hex8_element.h"
|
|
#include "fesa/math/sparse_matrix.h"
|
|
#include "fesa/solvers/linear/linear_solver.h"
|
|
#include "fesa/solvers/linear/tangent_solver.h"
|
|
|
|
namespace fesa {
|
|
namespace {
|
|
constexpr std::uint32_t kMaximumIterations = 100U;
|
|
constexpr double kRoundoffBudget =
|
|
512.0 * std::numeric_limits<double>::epsilon();
|
|
|
|
struct RuntimeSolid {
|
|
const Hex8Element* element;
|
|
std::vector<std::size_t> scatter;
|
|
};
|
|
struct SolidAssembly {
|
|
Vector force;
|
|
SparseMatrix tangent;
|
|
std::vector<SolidIntegrationPointRow> points;
|
|
double energy;
|
|
};
|
|
|
|
/// @brief Attaches immutable source-step context to a procedure failure.
|
|
Status Failure(const Domain& domain, const char* code, const char* message,
|
|
FailureCategory category = FailureCategory::kSolver) {
|
|
return Status::Failure(category, {{Severity::kError,
|
|
code,
|
|
{domain.SourcePath(), 0U},
|
|
"STATIC",
|
|
domain.SourceContentIdentity(),
|
|
message}});
|
|
}
|
|
/// @brief Preserves kernel source/entity evidence for failed trial recovery.
|
|
Status TrialFailure(const Status& status) {
|
|
return Status::Failure(FailureCategory::kSolver, status.Diagnostics());
|
|
}
|
|
/// @brief Returns component products with uncancelled arithmetic scales.
|
|
std::array<double, 3> Cross(const std::array<double, 3>& a,
|
|
const std::array<double, 3>& b,
|
|
std::array<double, 3>* scale = nullptr) {
|
|
std::array<double, 3> result{};
|
|
for (std::size_t i = 0U; i < 3U; ++i) {
|
|
const auto j = (i + 1U) % 3U;
|
|
const auto k = (i + 2U) % 3U;
|
|
result[i] = a[j] * b[k] - a[k] * b[j];
|
|
if (scale != nullptr)
|
|
(*scale)[i] += std::abs(a[j] * b[k]) + std::abs(a[k] * b[j]);
|
|
}
|
|
return result;
|
|
}
|
|
/// @brief Tests a zero identity at its uncancelled same-unit operation scale.
|
|
bool RoundoffZero(double value, double scale) {
|
|
return std::isfinite(value) && std::isfinite(scale) &&
|
|
(scale == 0.0 ? value == 0.0
|
|
: std::abs(value) <= kRoundoffBudget * scale);
|
|
}
|
|
/// @brief Derives source DIRECT factors or the existing ordinary STATIC path.
|
|
Result<std::vector<LoadFactorIncrement>> Factors(const Domain& domain) {
|
|
const auto& step = domain.Steps()[0];
|
|
if (!step.IsDirectStatic()) return BuildFixedLoadFactorSequence(step);
|
|
const auto count = step.IncrementCount();
|
|
if (count == 0U || count > step.MaximumIncrements() ||
|
|
!std::isfinite(step.InitialIncrement()) ||
|
|
!(step.InitialIncrement() > 0.0) || !std::isfinite(step.TimePeriod()) ||
|
|
!(step.TimePeriod() > 0.0) ||
|
|
step.InitialIncrement() * static_cast<double>(count) != step.TimePeriod())
|
|
return Result<std::vector<LoadFactorIncrement>>::Failure(Failure(
|
|
domain, "invalid-solid-static-schedule",
|
|
"DIRECT static requires a finite positive integer fixed schedule.",
|
|
FailureCategory::kInput));
|
|
std::vector<LoadFactorIncrement> factors;
|
|
factors.reserve(count);
|
|
double previous = 0.0;
|
|
for (std::uint32_t n = 1U; n <= count; ++n) {
|
|
const double factor = n == count
|
|
? 1.0
|
|
: static_cast<double>(n) *
|
|
step.InitialIncrement() / step.TimePeriod();
|
|
if (!std::isfinite(factor) || !(factor > previous) || factor > 1.0)
|
|
return Result<std::vector<LoadFactorIncrement>>::Failure(
|
|
Failure(domain, "invalid-solid-static-schedule",
|
|
"DIRECT static factors must strictly increase to one.",
|
|
FailureCategory::kInput));
|
|
factors.push_back({n, factor, n == count});
|
|
previous = factor;
|
|
if (n == count) break; // Avoid unsigned wrap at the source limit.
|
|
}
|
|
return Result<std::vector<LoadFactorIncrement>>::Success(std::move(factors));
|
|
}
|
|
/// @brief Builds index-owned trials, then reduces all contributions stably.
|
|
Result<SolidAssembly> Assemble(const std::vector<RuntimeSolid>& solids,
|
|
const DofManager& dofs,
|
|
const Vector& displacement, bool nonlinear,
|
|
const ParallelFor& parallel) {
|
|
std::vector<std::optional<Result<Hex8Response>>> responses(solids.size());
|
|
parallel.Execute(solids.size(), [&](std::size_t e) {
|
|
Vector local{24U};
|
|
for (std::size_t i = 0U; i < 24U; ++i)
|
|
local[i] = displacement[solids[e].scatter[i]];
|
|
responses[e] = nonlinear ? solids[e].element->EvaluateNonlinear(local)
|
|
: solids[e].element->EvaluateLinear(local);
|
|
});
|
|
Vector force{dofs.FullDofCount()};
|
|
std::vector<CooContribution> contributions;
|
|
contributions.reserve(solids.size() * 24U * 24U);
|
|
std::vector<SolidIntegrationPointRow> points;
|
|
points.reserve(solids.size() * 8U);
|
|
double energy = 0.0;
|
|
for (std::size_t e = 0U; e < solids.size(); ++e) {
|
|
if (!responses[e]->HasValue())
|
|
return Result<SolidAssembly>::Failure(
|
|
TrialFailure(responses[e]->GetStatus()));
|
|
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});
|
|
}
|
|
energy += response.strain_energy;
|
|
points.insert(points.end(), response.points.begin(), response.points.end());
|
|
}
|
|
auto tangent =
|
|
SparseMatrix::FromCoo(dofs.FullDofCount(), dofs.FullDofCount(),
|
|
std::move(contributions), dofs.GetSparsePattern());
|
|
if (!tangent.HasValue())
|
|
return Result<SolidAssembly>::Failure(tangent.GetStatus());
|
|
return Result<SolidAssembly>::Success({std::move(force),
|
|
std::move(tangent.Value()),
|
|
std::move(points), energy});
|
|
}
|
|
/// @brief Computes physical residual, projection and both moment-arm
|
|
/// identities.
|
|
Status CompleteFrame(const Domain& domain, const DofManager& dofs,
|
|
bool nonlinear, const SparseMatrix& stiffness,
|
|
SolidStaticFrame& frame) {
|
|
const std::size_t size = dofs.FullDofCount();
|
|
std::vector<bool> constrained(size, false);
|
|
for (auto i : dofs.ConstrainedDofs()) constrained[i] = true;
|
|
frame.residual = Vector{size};
|
|
frame.reaction = Vector{size};
|
|
for (std::size_t i = 0U; i < size; ++i) {
|
|
frame.residual[i] = frame.internal_force[i] - frame.external_force[i];
|
|
frame.reaction[i] = constrained[i] ? frame.residual[i] : 0.0;
|
|
if (!std::isfinite(frame.displacement[i]) ||
|
|
!std::isfinite(frame.internal_force[i]) ||
|
|
!std::isfinite(frame.external_force[i]) ||
|
|
!std::isfinite(frame.residual[i]))
|
|
return Failure(domain, "nonfinite-solid-static-recovery",
|
|
"Static full-space quantities must remain finite.");
|
|
}
|
|
if (!std::isfinite(frame.physical_strain_energy))
|
|
return Failure(domain, "nonfinite-solid-static-recovery",
|
|
"Recovered static strain energy must be finite.");
|
|
std::array<double, 3> force_scale{};
|
|
std::array<double, 3> reference_moment_scale{};
|
|
std::array<double, 3> current_moment_scale{};
|
|
std::array<double, 3> identity_moment_scale{};
|
|
std::array<double, 3> free_force_sum{};
|
|
std::array<double, 6> balance_scale{};
|
|
Vector arithmetic_scale{size};
|
|
// Linear K0 multiplication can cancel large translations within each row.
|
|
// Use those original products, not the small resulting internal force.
|
|
if (!nonlinear) {
|
|
for (std::size_t i = 0U; i < size; ++i)
|
|
for (std::size_t k = stiffness.RowOffsets()[i];
|
|
k < stiffness.RowOffsets()[i + 1U]; ++k)
|
|
arithmetic_scale[i] +=
|
|
std::abs(stiffness.Values()[k] *
|
|
frame.displacement[stiffness.ColumnIndices()[k]]);
|
|
for (auto i : dofs.FreeDofs())
|
|
if (!RoundoffZero(
|
|
frame.residual[i],
|
|
arithmetic_scale[i] + std::abs(frame.external_force[i])))
|
|
return Failure(
|
|
domain, "solid-linear-residual-failed",
|
|
"Linear free residual failed its uncancelled roundoff check.");
|
|
}
|
|
for (std::size_t a = 0U; a < domain.Nodes().size(); ++a) {
|
|
const auto& reference = domain.Nodes()[a].coordinates;
|
|
std::array<double, 3> current{};
|
|
std::array<double, 3> u{};
|
|
std::array<double, 3> f{};
|
|
std::array<double, 3> e{};
|
|
std::array<double, 3> applied{};
|
|
for (std::size_t i = 0U; i < 3U; ++i) {
|
|
const auto full = 3U * a + i;
|
|
u[i] = frame.displacement[full];
|
|
current[i] = reference[i] + u[i];
|
|
f[i] = frame.internal_force[full];
|
|
e[i] = constrained[full] ? 0.0 : frame.residual[full];
|
|
applied[i] = frame.external_force[full] + frame.reaction[full];
|
|
frame.balance.internal_force_sum[i] += f[i];
|
|
free_force_sum[i] += e[i];
|
|
force_scale[i] += nonlinear ? std::abs(f[i]) : arithmetic_scale[full];
|
|
frame.global_equilibrium[i] += applied[i];
|
|
balance_scale[i] += std::abs(frame.external_force[full]) +
|
|
std::abs(frame.reaction[full]) + std::abs(f[i]) +
|
|
std::abs(e[i]);
|
|
}
|
|
const auto reference_moment = Cross(reference, f, &reference_moment_scale);
|
|
const auto current_moment = Cross(current, f, ¤t_moment_scale);
|
|
const auto free_moment = Cross(current, e, &identity_moment_scale);
|
|
const auto linear_moment = Cross(u, f, &identity_moment_scale);
|
|
std::array<double, 3> applied_scale{};
|
|
const auto applied_moment = Cross(current, applied, &applied_scale);
|
|
for (std::size_t i = 0U; i < 3U; ++i) {
|
|
frame.balance.reference_internal_moment[i] += reference_moment[i];
|
|
frame.balance.current_internal_moment[i] += current_moment[i];
|
|
frame.balance.free_residual_moment[i] += free_moment[i];
|
|
frame.balance.linearization_moment[i] += linear_moment[i];
|
|
frame.global_equilibrium[3U + i] += applied_moment[i];
|
|
balance_scale[3U + i] += applied_scale[i];
|
|
if (!nonlinear) {
|
|
const auto j = (i + 1U) % 3U;
|
|
const auto k = (i + 2U) % 3U;
|
|
reference_moment_scale[i] +=
|
|
std::abs(reference[j]) * arithmetic_scale[3U * a + k] +
|
|
std::abs(reference[k]) * arithmetic_scale[3U * a + j];
|
|
}
|
|
}
|
|
}
|
|
frame.balance.internal_force_scale = force_scale;
|
|
frame.balance.reference_moment_scale = reference_moment_scale;
|
|
frame.balance.current_moment_scale = current_moment_scale;
|
|
for (std::size_t i = 0U; i < 3U; ++i) {
|
|
const double physical_moment =
|
|
nonlinear ? frame.balance.current_internal_moment[i]
|
|
: frame.balance.reference_internal_moment[i];
|
|
const double moment_scale =
|
|
nonlinear ? current_moment_scale[i] : reference_moment_scale[i];
|
|
if (!RoundoffZero(frame.balance.internal_force_sum[i], force_scale[i]) ||
|
|
!RoundoffZero(physical_moment, moment_scale))
|
|
return Failure(
|
|
domain, "solid-static-internal-balance-failed",
|
|
"Internal force or appropriate moment-arm identity failed.");
|
|
const double force_identity = frame.global_equilibrium[i] -
|
|
frame.balance.internal_force_sum[i] +
|
|
free_force_sum[i];
|
|
const double moment_identity = frame.global_equilibrium[3U + i] -
|
|
frame.balance.current_internal_moment[i] +
|
|
frame.balance.free_residual_moment[i];
|
|
if (!RoundoffZero(force_identity, balance_scale[i]) ||
|
|
!RoundoffZero(moment_identity, balance_scale[3U + i] +
|
|
current_moment_scale[i] +
|
|
identity_moment_scale[i]))
|
|
return Failure(
|
|
domain, "solid-static-residual-balance-failed",
|
|
"Applied/support balance does not explain the free residual.");
|
|
if (!std::isfinite(frame.global_equilibrium[i]) ||
|
|
!std::isfinite(frame.global_equilibrium[3U + i]))
|
|
return Failure(domain, "nonfinite-solid-static-recovery",
|
|
"Static current-position balance must remain finite.");
|
|
}
|
|
return Status::Ok();
|
|
}
|
|
} // namespace
|
|
|
|
SolidStaticProcedureExecutor::SolidStaticProcedureExecutor(
|
|
const ParallelFor& parallel_for, LinearSolver& linear_solver,
|
|
TangentSolver& tangent_solver)
|
|
: parallel_for_(parallel_for),
|
|
linear_solver_(linear_solver),
|
|
tangent_solver_(tangent_solver) {}
|
|
|
|
Status SolidStaticProcedureExecutor::ExecuteInto(
|
|
const Domain& domain, SolidStaticState& state) const {
|
|
if (state.initialized_)
|
|
return Failure(domain, "solid-static-observer-not-fresh",
|
|
"Execution requires a fresh static observer.");
|
|
if (!domain.IsSolid() || domain.Frequency() != nullptr ||
|
|
domain.Dynamic() != nullptr || domain.Steps().Size() != 1U)
|
|
return Failure(domain, "invalid-solid-static-procedure",
|
|
"Solid static execution requires one Solid STATIC step.",
|
|
FailureCategory::kModel);
|
|
auto model = AnalysisModel::Create(domain);
|
|
if (!model.HasValue()) return model.GetStatus();
|
|
std::vector<std::unique_ptr<Element>> elements;
|
|
ElementView view;
|
|
for (auto e : model.Value().ActiveElements()) {
|
|
auto element = ElementFactory{}.Create(domain.Elements()[e], domain);
|
|
if (!element.HasValue()) return element.GetStatus();
|
|
view.emplace_back(*element.Value());
|
|
elements.push_back(std::move(element.Value()));
|
|
}
|
|
DofManager dofs;
|
|
const Status mapping = dofs.Build(model.Value(), view);
|
|
if (!mapping.IsOk()) return mapping;
|
|
auto baseline = SolidStaticState::Create(domain, dofs);
|
|
if (!baseline.HasValue()) return baseline.GetStatus();
|
|
state = std::move(baseline.Value());
|
|
std::vector<RuntimeSolid> solids;
|
|
for (const auto& element : elements) {
|
|
const auto* hex = dynamic_cast<const Hex8Element*>(element.get());
|
|
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())});
|
|
}
|
|
auto stiffness = SparseAssembler::Assemble(view, dofs, parallel_for_);
|
|
if (!stiffness.HasValue()) return stiffness.GetStatus();
|
|
const EssentialConstraintPolicy constraints;
|
|
const auto& step = model.Value().Step();
|
|
const bool nonlinear = step.IsNonlinearStatic();
|
|
// No support can remove any rigid mode; fail before a perturbed
|
|
// factorization.
|
|
if (dofs.ConstrainedDofCount() == 0U)
|
|
return Failure(domain, "solid-static-rigid-mechanism",
|
|
"An unsupported Solid static free system has rigid modes.");
|
|
if (!nonlinear) {
|
|
auto partition = constraints.Partition(stiffness.Value(), dofs);
|
|
if (!partition.HasValue()) return partition.GetStatus();
|
|
Status status = linear_solver_.Factorize(partition.Value().k_ff);
|
|
if (!status.IsOk()) return status;
|
|
// 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(
|
|
load.Value(), partition.Value().k_fc, dofs.PrescribedValues(), dofs);
|
|
if (!rhs.HasValue()) return rhs.GetStatus();
|
|
Vector free{dofs.FreeDofCount()};
|
|
status = linear_solver_.Solve(rhs.Value(), free);
|
|
if (!status.IsOk()) return status;
|
|
SolidStaticFrame frame = state.committed_;
|
|
frame.frame_index = 0U;
|
|
frame.increment_index = step.IsDirectStatic() ? step.IncrementCount() : 1U;
|
|
frame.load_factor = 1.0;
|
|
frame.is_factor_one = true;
|
|
frame.displacement =
|
|
constraints.ReconstructFull(free, dofs.PrescribedValues(), dofs);
|
|
auto recovery =
|
|
Assemble(solids, dofs, frame.displacement, false, parallel_for_);
|
|
if (!recovery.HasValue()) return recovery.GetStatus();
|
|
frame.internal_force = std::move(recovery.Value().force);
|
|
frame.external_force = std::move(load.Value());
|
|
frame.solid_results = std::move(recovery.Value().points);
|
|
frame.physical_strain_energy = recovery.Value().energy;
|
|
status = CompleteFrame(domain, dofs, false, stiffness.Value(), frame);
|
|
return status.IsOk() ? state.CommitAcceptedFrame(frame) : status;
|
|
}
|
|
auto factors = Factors(domain);
|
|
if (!factors.HasValue()) return factors.GetStatus();
|
|
auto base_load = LoadAssembler::AssembleFullNodalLoad(model.Value(), dofs);
|
|
if (!base_load.HasValue()) return base_load.GetStatus();
|
|
for (const auto& increment : factors.Value()) {
|
|
const Vector start = state.committed_.displacement;
|
|
Vector trial = start;
|
|
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) {
|
|
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);
|
|
}
|
|
}
|
|
return Status::Ok();
|
|
}
|
|
Result<SolidStaticExecutionResult> SolidStaticProcedureExecutor::Execute(
|
|
const Domain& domain) const {
|
|
SolidStaticState state;
|
|
const Status status = ExecuteInto(domain, state);
|
|
if (!status.IsOk())
|
|
return Result<SolidStaticExecutionResult>::Failure(status);
|
|
auto diagnostics = domain.Warnings();
|
|
SortDiagnostics(diagnostics);
|
|
return Result<SolidStaticExecutionResult>::Success(
|
|
{std::move(state), std::move(diagnostics)});
|
|
}
|
|
} // namespace fesa
|