feat(result-contract-completion): step 1 — complete-result-contract

This commit is contained in:
KOKO\Mimi
2026-08-02 01:28:37 +09:00
parent 218cfa9d50
commit e0a6a6fa70
8 changed files with 454 additions and 5 deletions
@@ -4,6 +4,7 @@
#include <array>
#include <cstddef>
#include <exception>
#include <stdexcept>
#include <optional>
#include <string>
#include <utility>
@@ -12,6 +13,7 @@
#include <fesa/assembly/equation_system.hpp>
#include <fesa/assembly/serial_assembler.hpp>
#include <fesa/constraints/essential_bc.hpp>
#include <fesa/elements/beam/beam3d2.hpp>
#include <fesa/fem/dof_manager.hpp>
#include <fesa/solvers/linear/pardiso_linear_solver.hpp>
@@ -34,6 +36,18 @@ AnalysisRunResult equation_failure(
}});
}
AnalysisRunResult results_failure(
std::string code,
std::string message) {
return failure({{
DiagnosticStage::results,
Severity::error,
std::move(code),
std::move(message),
std::nullopt,
}});
}
NodalFrame build_nodal_frame(
const Domain& domain,
const DofManager& dofs,
@@ -48,9 +62,11 @@ NodalFrame build_nodal_frame(
NodalFrame nodal;
nodal.node_ids = std::move(node_ids);
nodal.origins.reserve(nodal.node_ids.size());
nodal.displacement.reserve(nodal.node_ids.size());
nodal.reaction.reserve(nodal.node_ids.size());
for (const NodeId node_id : nodal.node_ids) {
nodal.origins.push_back(domain.node(node_id).origin);
std::array<double, 6> node_displacement{};
std::array<double, 6> node_reaction{};
for (std::size_t component = 0; component < 6; ++component) {
@@ -67,6 +83,70 @@ NodalFrame build_nodal_frame(
return nodal;
}
ElementFrame build_element_frame(
const Domain& domain,
const DofManager& dofs,
const std::vector<double>& displacement) {
std::vector<const BeamElement*> elements;
elements.reserve(domain.beam_elements().size());
for (const BeamElement& element : domain.beam_elements()) {
elements.push_back(&element);
}
std::ranges::sort(
elements,
{},
[](const BeamElement* element) {
return element->id.value();
});
ElementFrame frame;
frame.beams.reserve(elements.size());
for (const BeamElement* element : elements) {
const Beam3D2Input input{
{
domain.node(element->nodes[0]).position,
domain.node(element->nodes[1]).position,
},
element->nodes,
domain.material(element->material),
domain.section(element->section),
};
const BeamKernelResult kernel = compute_beam3d2(input);
if (!kernel.contribution.has_value()) {
const std::string message = kernel.diagnostics.empty()
? "Beam recovery requires a valid element input."
: kernel.diagnostics.front().message;
throw std::runtime_error{message};
}
std::array<double, 12> element_displacement{};
const std::array<std::size_t, 12> full_dofs =
dofs.element_full_dofs(*element);
for (std::size_t local = 0; local < full_dofs.size(); ++local) {
element_displacement[local] = displacement[full_dofs[local]];
}
std::vector<BeamSectionResult> recovered = recover_beam3d2(
input,
element_displacement,
input.section.recovery_points);
if (recovered.size() != 2U) {
throw std::logic_error{
"Beam recovery must return exactly two end results."};
}
frame.beams.push_back({
element->id,
element->origin,
kernel.contribution->frame,
{
std::move(recovered[0]),
std::move(recovered[1]),
},
});
}
return frame;
}
} // namespace
AnalysisRunResult LinearStaticAnalysis::run(const Domain& domain) const {
@@ -114,6 +194,14 @@ AnalysisRunResult LinearStaticAnalysis::run(const Domain& domain) const {
"equation.reaction_recovery_failed", error.what());
}
ElementFrame element;
try {
element = build_element_frame(domain, dofs, displacement);
} catch (const std::exception& error) {
return results_failure(
"results.element_recovery_failed", error.what());
}
ResultDatabase database{
"1.0.0",
{{
@@ -122,6 +210,7 @@ AnalysisRunResult LinearStaticAnalysis::run(const Domain& domain) const {
1.0,
build_nodal_frame(
domain, dofs, displacement, reaction),
std::move(element),
{},
}},
}},