#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fesa { namespace { AnalysisRunResult failure(std::vector diagnostics) { return {false, std::nullopt, std::move(diagnostics)}; } AnalysisRunResult equation_failure( std::string code, std::string message) { return failure({{ DiagnosticStage::equation, Severity::error, std::move(code), std::move(message), std::nullopt, }}); } 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, const std::vector& displacement, const std::vector& reaction) { std::vector node_ids; node_ids.reserve(domain.nodes().size()); for (const Node& node : domain.nodes()) { node_ids.push_back(node.id); } std::ranges::sort(node_ids); 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 node_displacement{}; std::array node_reaction{}; for (std::size_t component = 0; component < 6; ++component) { const std::size_t full_dof = dofs.full_dof({ node_id, static_cast(component), }); node_displacement[component] = displacement[full_dof]; node_reaction[component] = reaction[full_dof]; } nodal.displacement.push_back(node_displacement); nodal.reaction.push_back(node_reaction); } return nodal; } ElementFrame build_element_frame( const Domain& domain, const DofManager& dofs, const std::vector& displacement) { std::vector 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 element_displacement{}; const std::array 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 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 { const DofManager dofs = DofManager::build(domain); std::optional original; try { original = assemble_serial(domain, dofs); } catch (const std::exception& error) { return equation_failure( "equation.assembly_failed", error.what()); } ConstraintResult constrained = eliminate_essential_bcs(*original, dofs); if (!constrained.reduced_system.has_value()) { return failure(std::move(constrained.diagnostics)); } std::vector reduced_solution; if (dofs.free_equation_count() != 0) { PardisoLinearSolver solver; LinearSolveResult solved = solver.solve( constrained.reduced_system->stiffness, constrained.reduced_system->force); if (!solved.diagnostics.empty()) { return failure(std::move(solved.diagnostics)); } reduced_solution = std::move(solved.solution); } std::vector displacement; try { displacement = dofs.reconstruct_full(reduced_solution); } catch (const std::exception& error) { return equation_failure( "equation.solution_reconstruction_failed", error.what()); } std::vector reaction; try { reaction = recover_reaction(*original, displacement); } catch (const std::exception& error) { return equation_failure( "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{ "2.0.0", {{ domain.step().name, {{ 1.0, build_nodal_frame( domain, dofs, displacement, reaction), std::move(element), {}, }}, }}, }; Status status = validate_result_database(database); if (!status.succeeded) { return failure(std::move(status.diagnostics)); } return {true, std::move(database), {}}; } } // namespace fesa