feat: add solver core skeleton

This commit is contained in:
NINI
2026-06-12 02:25:07 +09:00
parent 4e7fd1087d
commit cbd1a6c5d7
46 changed files with 1911 additions and 19 deletions
+34
View File
@@ -0,0 +1,34 @@
#pragma once
namespace fesa::analysis {
class Analysis {
public:
virtual ~Analysis() = default;
void run()
{
initialize();
build_analysis_model();
build_dof_map();
build_sparse_pattern();
assemble();
apply_boundary_conditions();
solve();
update_state();
write_results();
}
protected:
virtual void initialize() {}
virtual void build_analysis_model() {}
virtual void build_dof_map() {}
virtual void build_sparse_pattern() {}
virtual void assemble() {}
virtual void apply_boundary_conditions() {}
virtual void solve() {}
virtual void update_state() {}
virtual void write_results() {}
};
} // namespace fesa::analysis
+73
View File
@@ -0,0 +1,73 @@
#pragma once
#include <fesa/model/domain.hpp>
#include <stdexcept>
#include <vector>
namespace fesa::analysis {
class AnalysisModel {
public:
AnalysisModel(const model::Domain& domain, core::StepId step_id)
: domain_(domain), step_(domain.find_step(step_id))
{
if (step_ == nullptr) {
throw std::invalid_argument("analysis step not found");
}
for (const auto& element : domain_.elements()) {
active_elements_.push_back(&element);
}
for (const auto& boundary_condition : step_->boundary_conditions()) {
active_boundary_conditions_.push_back(&boundary_condition);
}
for (const auto& load : step_->loads()) {
active_loads_.push_back(&load);
}
}
const model::Domain& domain() const
{
return domain_;
}
const model::AnalysisStep& step() const
{
return *step_;
}
const std::vector<const model::Element*>& active_elements() const
{
return active_elements_;
}
const std::vector<const model::BoundaryCondition*>& active_boundary_conditions() const
{
return active_boundary_conditions_;
}
const std::vector<const model::Load*>& active_loads() const
{
return active_loads_;
}
const model::Property* property_for(const model::Element& element) const
{
return domain_.find_property(element.property_id());
}
const model::Material* material_for(const model::Property& property) const
{
return domain_.find_material(property.material_id());
}
private:
const model::Domain& domain_;
const model::AnalysisStep* step_;
std::vector<const model::Element*> active_elements_;
std::vector<const model::BoundaryCondition*> active_boundary_conditions_;
std::vector<const model::Load*> active_loads_;
};
} // namespace fesa::analysis
+146
View File
@@ -0,0 +1,146 @@
#pragma once
#include <fesa/core/ids.hpp>
#include <stdexcept>
#include <utility>
#include <vector>
namespace fesa::analysis {
struct IterationState {
double time = 0.0;
int increment = 0;
int iteration = 0;
};
class AnalysisState {
public:
explicit AnalysisState(int total_dof_count)
: displacement_(vector_of(total_dof_count)),
velocity_(vector_of(total_dof_count)),
acceleration_(vector_of(total_dof_count)),
temperature_(vector_of(total_dof_count)),
external_force_(vector_of(total_dof_count)),
internal_force_(vector_of(total_dof_count)),
residual_(vector_of(total_dof_count))
{
}
const std::vector<double>& displacement() const
{
return displacement_;
}
const std::vector<double>& velocity() const
{
return velocity_;
}
const std::vector<double>& acceleration() const
{
return acceleration_;
}
const std::vector<double>& temperature() const
{
return temperature_;
}
const std::vector<double>& external_force() const
{
return external_force_;
}
const std::vector<double>& internal_force() const
{
return internal_force_;
}
const std::vector<double>& residual() const
{
return residual_;
}
void set_displacement(std::vector<double> values)
{
assign_same_size(displacement_, std::move(values));
}
void set_external_force(std::vector<double> values)
{
assign_same_size(external_force_, std::move(values));
}
void set_internal_force(std::vector<double> values)
{
assign_same_size(internal_force_, std::move(values));
}
void update_residual()
{
for (std::size_t index = 0; index < residual_.size(); ++index) {
residual_[index] = external_force_[index] - internal_force_[index];
}
}
IterationState& iteration_state()
{
return iteration_state_;
}
const IterationState& iteration_state() const
{
return iteration_state_;
}
void set_element_state(core::ElementId element_id, std::vector<double> state)
{
for (auto& entry : element_states_) {
if (entry.first.value == element_id.value) {
entry.second = std::move(state);
return;
}
}
element_states_.push_back({element_id, std::move(state)});
}
const std::vector<double>* element_state(core::ElementId element_id) const
{
for (const auto& entry : element_states_) {
if (entry.first.value == element_id.value) {
return &entry.second;
}
}
return nullptr;
}
private:
static std::vector<double> vector_of(int size)
{
if (size < 0) {
throw std::invalid_argument("negative dof count");
}
return std::vector<double>(static_cast<std::size_t>(size), 0.0);
}
static void assign_same_size(std::vector<double>& target, std::vector<double> values)
{
if (target.size() != values.size()) {
throw std::invalid_argument("vector size mismatch");
}
target = std::move(values);
}
std::vector<double> displacement_;
std::vector<double> velocity_;
std::vector<double> acceleration_;
std::vector<double> temperature_;
std::vector<double> external_force_;
std::vector<double> internal_force_;
std::vector<double> residual_;
IterationState iteration_state_;
std::vector<std::pair<core::ElementId, std::vector<double>>> element_states_;
};
} // namespace fesa::analysis
@@ -0,0 +1,66 @@
#pragma once
#include <fesa/analysis/analysis.hpp>
#include <fesa/analysis/analysis_model.hpp>
#include <fesa/analysis/analysis_state.hpp>
#include <fesa/fem/dof_manager.hpp>
#include <memory>
namespace fesa::analysis {
class LinearStaticAnalysis : public Analysis {
public:
LinearStaticAnalysis(const model::Domain& domain, core::StepId step_id)
: domain_(domain), step_id_(step_id)
{
}
const AnalysisModel* analysis_model() const
{
return analysis_model_.get();
}
const AnalysisState* state() const
{
return state_.get();
}
protected:
void build_analysis_model() override
{
analysis_model_ = std::make_unique<AnalysisModel>(domain_, step_id_);
}
void build_dof_map() override
{
dof_manager_ = std::make_unique<fem::DofManager>();
for (const auto* element : analysis_model_->active_elements()) {
for (const auto node_id : element->node_ids()) {
dof_manager_->define_node_dofs(node_id, {
model::DofComponent::ux,
model::DofComponent::uy,
model::DofComponent::uz
});
}
}
for (const auto* boundary_condition : analysis_model_->active_boundary_conditions()) {
dof_manager_->apply_boundary_condition(*boundary_condition);
}
dof_manager_->number_equations();
}
void update_state() override
{
state_ = std::make_unique<AnalysisState>(dof_manager_->total_dof_count());
}
private:
const model::Domain& domain_;
core::StepId step_id_;
std::unique_ptr<AnalysisModel> analysis_model_;
std::unique_ptr<fem::DofManager> dof_manager_;
std::unique_ptr<AnalysisState> state_;
};
} // namespace fesa::analysis