Files
FESA/src/fesa/analysis/linear_static_analysis.cpp
T

138 lines
4.0 KiB
C++

#include <fesa/analysis/linear_static_analysis.hpp>
#include <algorithm>
#include <array>
#include <cstddef>
#include <exception>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <fesa/assembly/equation_system.hpp>
#include <fesa/assembly/serial_assembler.hpp>
#include <fesa/constraints/essential_bc.hpp>
#include <fesa/fem/dof_manager.hpp>
#include <fesa/solvers/linear/pardiso_linear_solver.hpp>
namespace fesa {
namespace {
AnalysisRunResult failure(std::vector<Diagnostic> 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,
}});
}
NodalFrame build_nodal_frame(
const Domain& domain,
const DofManager& dofs,
const std::vector<double>& displacement,
const std::vector<double>& reaction) {
std::vector<NodeId> 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.displacement.reserve(nodal.node_ids.size());
nodal.reaction.reserve(nodal.node_ids.size());
for (const NodeId node_id : nodal.node_ids) {
std::array<double, 6> node_displacement{};
std::array<double, 6> node_reaction{};
for (std::size_t component = 0; component < 6; ++component) {
const std::size_t full_dof = dofs.full_dof({
node_id,
static_cast<NodeDof>(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;
}
} // namespace
AnalysisRunResult LinearStaticAnalysis::run(const Domain& domain) const {
const DofManager dofs = DofManager::build(domain);
std::optional<EquationSystem> 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<double> 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<double> displacement;
try {
displacement = dofs.reconstruct_full(reduced_solution);
} catch (const std::exception& error) {
return equation_failure(
"equation.solution_reconstruction_failed", error.what());
}
std::vector<double> reaction;
try {
reaction = recover_reaction(*original, displacement);
} catch (const std::exception& error) {
return equation_failure(
"equation.reaction_recovery_failed", error.what());
}
ResultDatabase database{
"1.0.0",
{{
domain.step().name,
{{
1.0,
build_nodal_frame(
domain, dofs, displacement, reaction),
{},
}},
}},
};
Status status = validate_result_database(database);
if (!status.succeeded) {
return failure(std::move(status.diagnostics));
}
return {true, std::move(database), {}};
}
} // namespace fesa