From 825e03dbaf4ed5093be094f66eed8cbc8e12f74b Mon Sep 17 00:00:00 2001 From: NINI Date: Fri, 12 Jun 2026 02:38:12 +0900 Subject: [PATCH] refactor: move solver skeleton implementations to cpp --- CMakeLists.txt | 8 +- .../solver-core-skeleton.md | 18 ++- scripts/test_header_declaration_only.py | 45 ++++++ src/fesa/analysis/analysis.cpp | 30 ++++ src/fesa/analysis/analysis.hpp | 33 ++--- src/fesa/analysis/analysis_model.cpp | 60 ++++++++ src/fesa/analysis/analysis_model.hpp | 60 +------- src/fesa/analysis/analysis_state.cpp | 124 ++++++++++++++++ src/fesa/analysis/analysis_state.hpp | 129 +++-------------- src/fesa/analysis/linear_static_analysis.cpp | 48 ++++++ src/fesa/analysis/linear_static_analysis.hpp | 46 +----- src/fesa/core/status.cpp | 34 +++++ src/fesa/core/status.hpp | 31 +--- src/fesa/fem/dof_key.cpp | 10 ++ src/fesa/fem/dof_key.hpp | 5 +- src/fesa/fem/dof_manager.cpp | 133 +++++++++++++++++ src/fesa/fem/dof_manager.hpp | 137 ++---------------- src/fesa/model/analysis_step.cpp | 42 ++++++ src/fesa/model/analysis_step.hpp | 41 +----- src/fesa/model/boundary_condition.cpp | 25 ++++ src/fesa/model/boundary_condition.hpp | 22 +-- src/fesa/model/domain.cpp | 107 ++++++++++++++ src/fesa/model/domain.hpp | 113 ++------------- src/fesa/model/element.cpp | 38 +++++ src/fesa/model/element.hpp | 32 +--- src/fesa/model/load.cpp | 25 ++++ src/fesa/model/load.hpp | 22 +-- src/fesa/model/material.cpp | 22 +++ src/fesa/model/material.hpp | 17 +-- src/fesa/model/node.cpp | 20 +++ src/fesa/model/node.hpp | 16 +- src/fesa/model/property.cpp | 27 ++++ src/fesa/model/property.hpp | 23 +-- src/fesa/results/results.cpp | 70 +++++++++ src/fesa/results/results.hpp | 71 ++------- 35 files changed, 1001 insertions(+), 683 deletions(-) create mode 100644 scripts/test_header_declaration_only.py create mode 100644 src/fesa/analysis/analysis.cpp create mode 100644 src/fesa/analysis/analysis_model.cpp create mode 100644 src/fesa/analysis/analysis_state.cpp create mode 100644 src/fesa/analysis/linear_static_analysis.cpp create mode 100644 src/fesa/core/status.cpp create mode 100644 src/fesa/fem/dof_key.cpp create mode 100644 src/fesa/fem/dof_manager.cpp create mode 100644 src/fesa/model/analysis_step.cpp create mode 100644 src/fesa/model/boundary_condition.cpp create mode 100644 src/fesa/model/domain.cpp create mode 100644 src/fesa/model/element.cpp create mode 100644 src/fesa/model/load.cpp create mode 100644 src/fesa/model/material.cpp create mode 100644 src/fesa/model/node.cpp create mode 100644 src/fesa/model/property.cpp create mode 100644 src/fesa/results/results.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c98a569..59253ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,11 @@ cmake_minimum_required(VERSION 3.20) project(FESA LANGUAGES CXX) -add_library(fesa_core INTERFACE) -target_compile_features(fesa_core INTERFACE cxx_std_17) -target_include_directories(fesa_core INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/src) +file(GLOB_RECURSE FESA_CORE_SOURCES CONFIGURE_DEPENDS src/fesa/*.cpp) + +add_library(fesa_core STATIC ${FESA_CORE_SOURCES}) +target_compile_features(fesa_core PUBLIC cxx_std_17) +target_include_directories(fesa_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) enable_testing() add_subdirectory(tests) diff --git a/docs/build-test-reports/solver-core-skeleton.md b/docs/build-test-reports/solver-core-skeleton.md index 7ab6a74..3cd7012 100644 --- a/docs/build-test-reports/solver-core-skeleton.md +++ b/docs/build-test-reports/solver-core-skeleton.md @@ -7,12 +7,19 @@ ## Commands Run +```powershell +python -m unittest scripts.test_header_declaration_only +``` + +- exit_code: 0 +- summary: Solver headers contain declarations only; function bodies are implemented in `.cpp` files. + ```powershell python -m unittest discover -s scripts -p "test_*.py" ``` - exit_code: 0 -- summary: 98 Python Harness tests passed. +- summary: 99 Python Harness tests passed. ```powershell python scripts/validate_workspace.py @@ -52,6 +59,15 @@ ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R solver_core_sk - `results_containers_test` - `solver_core_skeleton_integration_test` +## Structural Tests Added + +- `scripts.test_header_declaration_only` + +## Build Structure + +- `fesa_core` now builds as a static library from `src/fesa/**/*.cpp`. +- Solver headers under `src/fesa/**/*.hpp` declare functions only; method bodies live in matching `.cpp` translation units. + ## Known Limitations - No element stiffness, residual, tangent, or stress recovery calculation is implemented. diff --git a/scripts/test_header_declaration_only.py b/scripts/test_header_declaration_only.py new file mode 100644 index 0000000..6dd311e --- /dev/null +++ b/scripts/test_header_declaration_only.py @@ -0,0 +1,45 @@ +import re +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +HEADER_ROOT = ROOT / "src" / "fesa" + + +def _looks_like_function_definition(prefix): + if "(" not in prefix or ")" not in prefix: + return False + + stripped = prefix.strip() + control_prefixes = ("if ", "for ", "while ", "switch ", "catch ") + declaration_prefixes = ("namespace ", "class ", "struct ", "enum ") + return not stripped.startswith(control_prefixes + declaration_prefixes) + + +class HeaderDeclarationOnlyTests(unittest.TestCase): + def test_solver_headers_do_not_contain_function_bodies(self): + violations = [] + + for header in sorted(HEADER_ROOT.rglob("*.hpp")): + candidate = "" + for line_number, line in enumerate(header.read_text(encoding="utf-8").splitlines(), start=1): + stripped = line.strip() + if not stripped: + continue + + candidate = f"{candidate} {stripped}".strip() + if "{" in stripped and _looks_like_function_definition(candidate.split("{", 1)[0]): + violations.append(f"{header.relative_to(ROOT)}:{line_number}: function body in header") + + if re.search(r"\([^;{}]*\)\s*=\s*(default|delete)\s*;", stripped): + violations.append(f"{header.relative_to(ROOT)}:{line_number}: function definition in header") + + if stripped.endswith(";") or stripped.endswith("}") or stripped.endswith(":"): + candidate = "" + + self.assertEqual([], violations) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/fesa/analysis/analysis.cpp b/src/fesa/analysis/analysis.cpp new file mode 100644 index 0000000..626930b --- /dev/null +++ b/src/fesa/analysis/analysis.cpp @@ -0,0 +1,30 @@ +#include + +namespace fesa::analysis { + +Analysis::~Analysis() = default; + +void Analysis::run() +{ + initialize(); + build_analysis_model(); + build_dof_map(); + build_sparse_pattern(); + assemble(); + apply_boundary_conditions(); + solve(); + update_state(); + write_results(); +} + +void Analysis::initialize() {} +void Analysis::build_analysis_model() {} +void Analysis::build_dof_map() {} +void Analysis::build_sparse_pattern() {} +void Analysis::assemble() {} +void Analysis::apply_boundary_conditions() {} +void Analysis::solve() {} +void Analysis::update_state() {} +void Analysis::write_results() {} + +} // namespace fesa::analysis diff --git a/src/fesa/analysis/analysis.hpp b/src/fesa/analysis/analysis.hpp index ed20c82..87abfaf 100644 --- a/src/fesa/analysis/analysis.hpp +++ b/src/fesa/analysis/analysis.hpp @@ -4,31 +4,20 @@ namespace fesa::analysis { class Analysis { public: - virtual ~Analysis() = default; + virtual ~Analysis(); - void run() - { - initialize(); - build_analysis_model(); - build_dof_map(); - build_sparse_pattern(); - assemble(); - apply_boundary_conditions(); - solve(); - update_state(); - write_results(); - } + void run(); 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() {} + 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 diff --git a/src/fesa/analysis/analysis_model.cpp b/src/fesa/analysis/analysis_model.cpp new file mode 100644 index 0000000..edbd8b6 --- /dev/null +++ b/src/fesa/analysis/analysis_model.cpp @@ -0,0 +1,60 @@ +#include + +#include + +namespace fesa::analysis { + +AnalysisModel::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& AnalysisModel::domain() const +{ + return domain_; +} + +const model::AnalysisStep& AnalysisModel::step() const +{ + return *step_; +} + +const std::vector& AnalysisModel::active_elements() const +{ + return active_elements_; +} + +const std::vector& AnalysisModel::active_boundary_conditions() const +{ + return active_boundary_conditions_; +} + +const std::vector& AnalysisModel::active_loads() const +{ + return active_loads_; +} + +const model::Property* AnalysisModel::property_for(const model::Element& element) const +{ + return domain_.find_property(element.property_id()); +} + +const model::Material* AnalysisModel::material_for(const model::Property& property) const +{ + return domain_.find_material(property.material_id()); +} + +} // namespace fesa::analysis diff --git a/src/fesa/analysis/analysis_model.hpp b/src/fesa/analysis/analysis_model.hpp index 4291fcd..5dee1a7 100644 --- a/src/fesa/analysis/analysis_model.hpp +++ b/src/fesa/analysis/analysis_model.hpp @@ -2,65 +2,21 @@ #include -#include #include 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"); - } + AnalysisModel(const model::Domain& domain, core::StepId step_id); - 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& active_elements() const - { - return active_elements_; - } - - const std::vector& active_boundary_conditions() const - { - return active_boundary_conditions_; - } - - const std::vector& 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()); - } + const model::Domain& domain() const; + const model::AnalysisStep& step() const; + const std::vector& active_elements() const; + const std::vector& active_boundary_conditions() const; + const std::vector& active_loads() const; + const model::Property* property_for(const model::Element& element) const; + const model::Material* material_for(const model::Property& property) const; private: const model::Domain& domain_; diff --git a/src/fesa/analysis/analysis_state.cpp b/src/fesa/analysis/analysis_state.cpp new file mode 100644 index 0000000..646004a --- /dev/null +++ b/src/fesa/analysis/analysis_state.cpp @@ -0,0 +1,124 @@ +#include + +#include +#include +#include + +namespace fesa::analysis { + +AnalysisState::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& AnalysisState::displacement() const +{ + return displacement_; +} + +const std::vector& AnalysisState::velocity() const +{ + return velocity_; +} + +const std::vector& AnalysisState::acceleration() const +{ + return acceleration_; +} + +const std::vector& AnalysisState::temperature() const +{ + return temperature_; +} + +const std::vector& AnalysisState::external_force() const +{ + return external_force_; +} + +const std::vector& AnalysisState::internal_force() const +{ + return internal_force_; +} + +const std::vector& AnalysisState::residual() const +{ + return residual_; +} + +void AnalysisState::set_displacement(std::vector values) +{ + assign_same_size(displacement_, std::move(values)); +} + +void AnalysisState::set_external_force(std::vector values) +{ + assign_same_size(external_force_, std::move(values)); +} + +void AnalysisState::set_internal_force(std::vector values) +{ + assign_same_size(internal_force_, std::move(values)); +} + +void AnalysisState::update_residual() +{ + for (std::size_t index = 0; index < residual_.size(); ++index) { + residual_[index] = external_force_[index] - internal_force_[index]; + } +} + +IterationState& AnalysisState::iteration_state() +{ + return iteration_state_; +} + +const IterationState& AnalysisState::iteration_state() const +{ + return iteration_state_; +} + +void AnalysisState::set_element_state(core::ElementId element_id, std::vector 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* AnalysisState::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; +} + +std::vector AnalysisState::vector_of(int size) +{ + if (size < 0) { + throw std::invalid_argument("negative dof count"); + } + return std::vector(static_cast(size), 0.0); +} + +void AnalysisState::assign_same_size(std::vector& target, std::vector values) +{ + if (target.size() != values.size()) { + throw std::invalid_argument("vector size mismatch"); + } + target = std::move(values); +} + +} // namespace fesa::analysis diff --git a/src/fesa/analysis/analysis_state.hpp b/src/fesa/analysis/analysis_state.hpp index 75348f6..54bfa63 100644 --- a/src/fesa/analysis/analysis_state.hpp +++ b/src/fesa/analysis/analysis_state.hpp @@ -2,8 +2,6 @@ #include -#include -#include #include namespace fesa::analysis { @@ -16,121 +14,30 @@ struct IterationState { 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)) - { - } + explicit AnalysisState(int total_dof_count); - const std::vector& displacement() const - { - return displacement_; - } + const std::vector& displacement() const; + const std::vector& velocity() const; + const std::vector& acceleration() const; + const std::vector& temperature() const; + const std::vector& external_force() const; + const std::vector& internal_force() const; + const std::vector& residual() const; - const std::vector& velocity() const - { - return velocity_; - } + void set_displacement(std::vector values); + void set_external_force(std::vector values); + void set_internal_force(std::vector values); + void update_residual(); - const std::vector& acceleration() const - { - return acceleration_; - } + IterationState& iteration_state(); + const IterationState& iteration_state() const; - const std::vector& temperature() const - { - return temperature_; - } - - const std::vector& external_force() const - { - return external_force_; - } - - const std::vector& internal_force() const - { - return internal_force_; - } - - const std::vector& residual() const - { - return residual_; - } - - void set_displacement(std::vector values) - { - assign_same_size(displacement_, std::move(values)); - } - - void set_external_force(std::vector values) - { - assign_same_size(external_force_, std::move(values)); - } - - void set_internal_force(std::vector 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 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* 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; - } + void set_element_state(core::ElementId element_id, std::vector state); + const std::vector* element_state(core::ElementId element_id) const; private: - static std::vector vector_of(int size) - { - if (size < 0) { - throw std::invalid_argument("negative dof count"); - } - return std::vector(static_cast(size), 0.0); - } - - static void assign_same_size(std::vector& target, std::vector values) - { - if (target.size() != values.size()) { - throw std::invalid_argument("vector size mismatch"); - } - target = std::move(values); - } + static std::vector vector_of(int size); + static void assign_same_size(std::vector& target, std::vector values); std::vector displacement_; std::vector velocity_; diff --git a/src/fesa/analysis/linear_static_analysis.cpp b/src/fesa/analysis/linear_static_analysis.cpp new file mode 100644 index 0000000..2d350c2 --- /dev/null +++ b/src/fesa/analysis/linear_static_analysis.cpp @@ -0,0 +1,48 @@ +#include + +namespace fesa::analysis { + +LinearStaticAnalysis::LinearStaticAnalysis(const model::Domain& domain, core::StepId step_id) + : domain_(domain), step_id_(step_id) +{ +} + +const AnalysisModel* LinearStaticAnalysis::analysis_model() const +{ + return analysis_model_.get(); +} + +const AnalysisState* LinearStaticAnalysis::state() const +{ + return state_.get(); +} + +void LinearStaticAnalysis::build_analysis_model() +{ + analysis_model_ = std::make_unique(domain_, step_id_); +} + +void LinearStaticAnalysis::build_dof_map() +{ + dof_manager_ = std::make_unique(); + 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 LinearStaticAnalysis::update_state() +{ + state_ = std::make_unique(dof_manager_->total_dof_count()); +} + +} // namespace fesa::analysis diff --git a/src/fesa/analysis/linear_static_analysis.hpp b/src/fesa/analysis/linear_static_analysis.hpp index bf87a08..6fb93a0 100644 --- a/src/fesa/analysis/linear_static_analysis.hpp +++ b/src/fesa/analysis/linear_static_analysis.hpp @@ -11,49 +11,15 @@ namespace fesa::analysis { class LinearStaticAnalysis : public Analysis { public: - LinearStaticAnalysis(const model::Domain& domain, core::StepId step_id) - : domain_(domain), step_id_(step_id) - { - } + LinearStaticAnalysis(const model::Domain& domain, core::StepId step_id); - const AnalysisModel* analysis_model() const - { - return analysis_model_.get(); - } - - const AnalysisState* state() const - { - return state_.get(); - } + const AnalysisModel* analysis_model() const; + const AnalysisState* state() const; protected: - void build_analysis_model() override - { - analysis_model_ = std::make_unique(domain_, step_id_); - } - - void build_dof_map() override - { - dof_manager_ = std::make_unique(); - 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(dof_manager_->total_dof_count()); - } + void build_analysis_model() override; + void build_dof_map() override; + void update_state() override; private: const model::Domain& domain_; diff --git a/src/fesa/core/status.cpp b/src/fesa/core/status.cpp new file mode 100644 index 0000000..c6c1b5f --- /dev/null +++ b/src/fesa/core/status.cpp @@ -0,0 +1,34 @@ +#include + +#include + +namespace fesa::core { + +Status Status::ok() +{ + return Status{}; +} + +Status Status::failure(Diagnostic diagnostic) +{ + Status status; + status.add(std::move(diagnostic)); + return status; +} + +bool Status::is_ok() const +{ + return diagnostics_.empty(); +} + +const std::vector& Status::diagnostics() const +{ + return diagnostics_; +} + +void Status::add(Diagnostic diagnostic) +{ + diagnostics_.push_back(std::move(diagnostic)); +} + +} // namespace fesa::core diff --git a/src/fesa/core/status.hpp b/src/fesa/core/status.hpp index 241f96a..f733874 100644 --- a/src/fesa/core/status.hpp +++ b/src/fesa/core/status.hpp @@ -2,39 +2,18 @@ #include -#include #include namespace fesa::core { class Status { public: - static Status ok() - { - return Status{}; - } + static Status ok(); + static Status failure(Diagnostic diagnostic); - static Status failure(Diagnostic diagnostic) - { - Status status; - status.add(std::move(diagnostic)); - return status; - } - - bool is_ok() const - { - return diagnostics_.empty(); - } - - const std::vector& diagnostics() const - { - return diagnostics_; - } - - void add(Diagnostic diagnostic) - { - diagnostics_.push_back(std::move(diagnostic)); - } + bool is_ok() const; + const std::vector& diagnostics() const; + void add(Diagnostic diagnostic); private: std::vector diagnostics_; diff --git a/src/fesa/fem/dof_key.cpp b/src/fesa/fem/dof_key.cpp new file mode 100644 index 0000000..04a2d5b --- /dev/null +++ b/src/fesa/fem/dof_key.cpp @@ -0,0 +1,10 @@ +#include + +namespace fesa::fem { + +bool operator==(const DofKey& lhs, const DofKey& rhs) +{ + return lhs.node_id.value == rhs.node_id.value && lhs.component == rhs.component; +} + +} // namespace fesa::fem diff --git a/src/fesa/fem/dof_key.hpp b/src/fesa/fem/dof_key.hpp index 2d09930..cce0d65 100644 --- a/src/fesa/fem/dof_key.hpp +++ b/src/fesa/fem/dof_key.hpp @@ -10,9 +10,6 @@ struct DofKey { model::DofComponent component; }; -inline bool operator==(const DofKey& lhs, const DofKey& rhs) -{ - return lhs.node_id.value == rhs.node_id.value && lhs.component == rhs.component; -} +bool operator==(const DofKey& lhs, const DofKey& rhs); } // namespace fesa::fem diff --git a/src/fesa/fem/dof_manager.cpp b/src/fesa/fem/dof_manager.cpp new file mode 100644 index 0000000..e8b54c5 --- /dev/null +++ b/src/fesa/fem/dof_manager.cpp @@ -0,0 +1,133 @@ +#include + +#include +#include +#include + +namespace fesa::fem { + +void DofManager::define_node_dofs(core::NodeId node_id, std::vector components) +{ + for (const auto component : components) { + DofKey key{node_id, component}; + if (find_record(key) == records_.end()) { + records_.push_back({key}); + } + } +} + +void DofManager::apply_boundary_condition(const model::BoundaryCondition& bc) +{ + auto record = find_record({bc.node_id(), bc.component()}); + if (record == records_.end()) { + throw std::invalid_argument("boundary condition references undefined dof"); + } + record->constrained = true; +} + +void DofManager::number_equations() +{ + std::sort(records_.begin(), records_.end(), [](const Record& lhs, const Record& rhs) { + if (lhs.key.node_id.value != rhs.key.node_id.value) { + return lhs.key.node_id.value < rhs.key.node_id.value; + } + return static_cast(lhs.key.component) < static_cast(rhs.key.component); + }); + + int free_id = 0; + for (int equation_id = 0; equation_id < static_cast(records_.size()); ++equation_id) { + records_[equation_id].equation_id = equation_id; + if (records_[equation_id].constrained) { + records_[equation_id].free_equation_id = std::nullopt; + } else { + records_[equation_id].free_equation_id = free_id++; + } + } + + sparse_pattern_.clear(); + for (int row = 0; row < free_id; ++row) { + for (int column = 0; column < free_id; ++column) { + sparse_pattern_.push_back({row, column}); + } + } +} + +int DofManager::total_dof_count() const +{ + return static_cast(records_.size()); +} + +int DofManager::free_dof_count() const +{ + return static_cast( + std::count_if(records_.begin(), records_.end(), [](const Record& record) { + return !record.constrained; + }) + ); +} + +int DofManager::constrained_dof_count() const +{ + return total_dof_count() - free_dof_count(); +} + +bool DofManager::is_constrained(DofKey key) const +{ + return require_record(key).constrained; +} + +int DofManager::equation_id(DofKey key) const +{ + return require_record(key).equation_id; +} + +std::optional DofManager::free_equation_id(DofKey key) const +{ + return require_record(key).free_equation_id; +} + +std::vector DofManager::expand_free_vector(const std::vector& free_values) const +{ + if (free_values.size() != static_cast(free_dof_count())) { + throw std::invalid_argument("free vector size does not match dof manager"); + } + + std::vector full(records_.size(), 0.0); + for (const auto& record : records_) { + if (record.free_equation_id.has_value()) { + full[static_cast(record.equation_id)] = + free_values[static_cast(*record.free_equation_id)]; + } + } + return full; +} + +const std::vector>& DofManager::sparse_pattern() const +{ + return sparse_pattern_; +} + +std::vector::iterator DofManager::find_record(DofKey key) +{ + return std::find_if(records_.begin(), records_.end(), [key](const Record& record) { + return record.key == key; + }); +} + +std::vector::const_iterator DofManager::find_record(DofKey key) const +{ + return std::find_if(records_.begin(), records_.end(), [key](const Record& record) { + return record.key == key; + }); +} + +const DofManager::Record& DofManager::require_record(DofKey key) const +{ + const auto record = find_record(key); + if (record == records_.end()) { + throw std::invalid_argument("dof is not defined"); + } + return *record; +} + +} // namespace fesa::fem diff --git a/src/fesa/fem/dof_manager.hpp b/src/fesa/fem/dof_manager.hpp index ccf7163..a2626bd 100644 --- a/src/fesa/fem/dof_manager.hpp +++ b/src/fesa/fem/dof_manager.hpp @@ -2,9 +2,7 @@ #include -#include #include -#include #include #include @@ -12,106 +10,18 @@ namespace fesa::fem { class DofManager { public: - void define_node_dofs(core::NodeId node_id, std::vector components) - { - for (const auto component : components) { - DofKey key{node_id, component}; - if (find_record(key) == records_.end()) { - records_.push_back({key}); - } - } - } + void define_node_dofs(core::NodeId node_id, std::vector components); + void apply_boundary_condition(const model::BoundaryCondition& bc); + void number_equations(); - void apply_boundary_condition(const model::BoundaryCondition& bc) - { - auto record = find_record({bc.node_id(), bc.component()}); - if (record == records_.end()) { - throw std::invalid_argument("boundary condition references undefined dof"); - } - record->constrained = true; - } - - void number_equations() - { - std::sort(records_.begin(), records_.end(), [](const Record& lhs, const Record& rhs) { - if (lhs.key.node_id.value != rhs.key.node_id.value) { - return lhs.key.node_id.value < rhs.key.node_id.value; - } - return static_cast(lhs.key.component) < static_cast(rhs.key.component); - }); - - int free_id = 0; - for (int equation_id = 0; equation_id < static_cast(records_.size()); ++equation_id) { - records_[equation_id].equation_id = equation_id; - if (records_[equation_id].constrained) { - records_[equation_id].free_equation_id = std::nullopt; - } else { - records_[equation_id].free_equation_id = free_id++; - } - } - - sparse_pattern_.clear(); - for (int row = 0; row < free_id; ++row) { - for (int column = 0; column < free_id; ++column) { - sparse_pattern_.push_back({row, column}); - } - } - } - - int total_dof_count() const - { - return static_cast(records_.size()); - } - - int free_dof_count() const - { - return static_cast( - std::count_if(records_.begin(), records_.end(), [](const Record& record) { - return !record.constrained; - }) - ); - } - - int constrained_dof_count() const - { - return total_dof_count() - free_dof_count(); - } - - bool is_constrained(DofKey key) const - { - return require_record(key).constrained; - } - - int equation_id(DofKey key) const - { - return require_record(key).equation_id; - } - - std::optional free_equation_id(DofKey key) const - { - return require_record(key).free_equation_id; - } - - std::vector expand_free_vector(const std::vector& free_values) const - { - if (free_values.size() != static_cast(free_dof_count())) { - throw std::invalid_argument("free vector size does not match dof manager"); - } - - std::vector full(records_.size(), 0.0); - for (const auto& record : records_) { - if (record.free_equation_id.has_value()) { - full[static_cast(record.equation_id)] = - free_values[static_cast(*record.free_equation_id)]; - } - } - return full; - } - - const std::vector>& sparse_pattern() const - { - return sparse_pattern_; - } + int total_dof_count() const; + int free_dof_count() const; + int constrained_dof_count() const; + bool is_constrained(DofKey key) const; + int equation_id(DofKey key) const; + std::optional free_equation_id(DofKey key) const; + std::vector expand_free_vector(const std::vector& free_values) const; + const std::vector>& sparse_pattern() const; private: struct Record { @@ -121,28 +31,9 @@ private: std::optional free_equation_id; }; - std::vector::iterator find_record(DofKey key) - { - return std::find_if(records_.begin(), records_.end(), [key](const Record& record) { - return record.key == key; - }); - } - - std::vector::const_iterator find_record(DofKey key) const - { - return std::find_if(records_.begin(), records_.end(), [key](const Record& record) { - return record.key == key; - }); - } - - const Record& require_record(DofKey key) const - { - const auto record = find_record(key); - if (record == records_.end()) { - throw std::invalid_argument("dof is not defined"); - } - return *record; - } + std::vector::iterator find_record(DofKey key); + std::vector::const_iterator find_record(DofKey key) const; + const Record& require_record(DofKey key) const; std::vector records_; std::vector> sparse_pattern_; diff --git a/src/fesa/model/analysis_step.cpp b/src/fesa/model/analysis_step.cpp new file mode 100644 index 0000000..7dfd87f --- /dev/null +++ b/src/fesa/model/analysis_step.cpp @@ -0,0 +1,42 @@ +#include + +#include + +namespace fesa::model { + +AnalysisStep::AnalysisStep(core::StepId id, std::string name) + : id_(id), name_(std::move(name)) +{ +} + +core::StepId AnalysisStep::id() const +{ + return id_; +} + +const std::string& AnalysisStep::name() const +{ + return name_; +} + +void AnalysisStep::add_boundary_condition(BoundaryCondition bc) +{ + boundary_conditions_.push_back(std::move(bc)); +} + +void AnalysisStep::add_load(Load load) +{ + loads_.push_back(std::move(load)); +} + +const std::vector& AnalysisStep::boundary_conditions() const +{ + return boundary_conditions_; +} + +const std::vector& AnalysisStep::loads() const +{ + return loads_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/analysis_step.hpp b/src/fesa/model/analysis_step.hpp index 3d108e7..a3d0845 100644 --- a/src/fesa/model/analysis_step.hpp +++ b/src/fesa/model/analysis_step.hpp @@ -4,47 +4,20 @@ #include #include -#include #include namespace fesa::model { class AnalysisStep { public: - AnalysisStep(core::StepId id, std::string name) - : id_(id), name_(std::move(name)) - { - } + AnalysisStep(core::StepId id, std::string name); - core::StepId id() const - { - return id_; - } - - const std::string& name() const - { - return name_; - } - - void add_boundary_condition(BoundaryCondition bc) - { - boundary_conditions_.push_back(std::move(bc)); - } - - void add_load(Load load) - { - loads_.push_back(std::move(load)); - } - - const std::vector& boundary_conditions() const - { - return boundary_conditions_; - } - - const std::vector& loads() const - { - return loads_; - } + core::StepId id() const; + const std::string& name() const; + void add_boundary_condition(BoundaryCondition bc); + void add_load(Load load); + const std::vector& boundary_conditions() const; + const std::vector& loads() const; private: core::StepId id_; diff --git a/src/fesa/model/boundary_condition.cpp b/src/fesa/model/boundary_condition.cpp new file mode 100644 index 0000000..bf910e3 --- /dev/null +++ b/src/fesa/model/boundary_condition.cpp @@ -0,0 +1,25 @@ +#include + +namespace fesa::model { + +BoundaryCondition::BoundaryCondition(core::NodeId node_id, DofComponent component, double value) + : node_id_(node_id), component_(component), value_(value) +{ +} + +core::NodeId BoundaryCondition::node_id() const +{ + return node_id_; +} + +DofComponent BoundaryCondition::component() const +{ + return component_; +} + +double BoundaryCondition::value() const +{ + return value_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/boundary_condition.hpp b/src/fesa/model/boundary_condition.hpp index 01028f8..e44aa5f 100644 --- a/src/fesa/model/boundary_condition.hpp +++ b/src/fesa/model/boundary_condition.hpp @@ -16,25 +16,11 @@ enum class DofComponent { class BoundaryCondition { public: - BoundaryCondition(core::NodeId node_id, DofComponent component, double value) - : node_id_(node_id), component_(component), value_(value) - { - } + BoundaryCondition(core::NodeId node_id, DofComponent component, double value); - core::NodeId node_id() const - { - return node_id_; - } - - DofComponent component() const - { - return component_; - } - - double value() const - { - return value_; - } + core::NodeId node_id() const; + DofComponent component() const; + double value() const; private: core::NodeId node_id_; diff --git a/src/fesa/model/domain.cpp b/src/fesa/model/domain.cpp new file mode 100644 index 0000000..161a058 --- /dev/null +++ b/src/fesa/model/domain.cpp @@ -0,0 +1,107 @@ +#include + +#include + +namespace fesa::model { + +void Domain::add_node(Node node) +{ + nodes_.push_back(std::move(node)); +} + +void Domain::add_element(Element element) +{ + elements_.push_back(std::move(element)); +} + +void Domain::add_material(Material material) +{ + materials_.push_back(std::move(material)); +} + +void Domain::add_property(Property property) +{ + properties_.push_back(std::move(property)); +} + +void Domain::add_step(AnalysisStep step) +{ + steps_.push_back(std::move(step)); +} + +const std::vector& Domain::nodes() const +{ + return nodes_; +} + +const std::vector& Domain::elements() const +{ + return elements_; +} + +const std::vector& Domain::materials() const +{ + return materials_; +} + +const std::vector& Domain::properties() const +{ + return properties_; +} + +const std::vector& Domain::steps() const +{ + return steps_; +} + +const Node* Domain::find_node(core::NodeId id) const +{ + for (const auto& node : nodes_) { + if (node.id().value == id.value) { + return &node; + } + } + return nullptr; +} + +const Element* Domain::find_element(core::ElementId id) const +{ + for (const auto& element : elements_) { + if (element.id().value == id.value) { + return &element; + } + } + return nullptr; +} + +const Material* Domain::find_material(core::MaterialId id) const +{ + for (const auto& material : materials_) { + if (material.id().value == id.value) { + return &material; + } + } + return nullptr; +} + +const Property* Domain::find_property(core::PropertyId id) const +{ + for (const auto& property : properties_) { + if (property.id().value == id.value) { + return &property; + } + } + return nullptr; +} + +const AnalysisStep* Domain::find_step(core::StepId id) const +{ + for (const auto& step : steps_) { + if (step.id().value == id.value) { + return &step; + } + } + return nullptr; +} + +} // namespace fesa::model diff --git a/src/fesa/model/domain.hpp b/src/fesa/model/domain.hpp index 2568094..dc183bd 100644 --- a/src/fesa/model/domain.hpp +++ b/src/fesa/model/domain.hpp @@ -6,112 +6,29 @@ #include #include -#include #include namespace fesa::model { class Domain { public: - void add_node(Node node) - { - nodes_.push_back(std::move(node)); - } + void add_node(Node node); + void add_element(Element element); + void add_material(Material material); + void add_property(Property property); + void add_step(AnalysisStep step); - void add_element(Element element) - { - elements_.push_back(std::move(element)); - } + const std::vector& nodes() const; + const std::vector& elements() const; + const std::vector& materials() const; + const std::vector& properties() const; + const std::vector& steps() const; - void add_material(Material material) - { - materials_.push_back(std::move(material)); - } - - void add_property(Property property) - { - properties_.push_back(std::move(property)); - } - - void add_step(AnalysisStep step) - { - steps_.push_back(std::move(step)); - } - - const std::vector& nodes() const - { - return nodes_; - } - - const std::vector& elements() const - { - return elements_; - } - - const std::vector& materials() const - { - return materials_; - } - - const std::vector& properties() const - { - return properties_; - } - - const std::vector& steps() const - { - return steps_; - } - - const Node* find_node(core::NodeId id) const - { - for (const auto& node : nodes_) { - if (node.id().value == id.value) { - return &node; - } - } - return nullptr; - } - - const Element* find_element(core::ElementId id) const - { - for (const auto& element : elements_) { - if (element.id().value == id.value) { - return &element; - } - } - return nullptr; - } - - const Material* find_material(core::MaterialId id) const - { - for (const auto& material : materials_) { - if (material.id().value == id.value) { - return &material; - } - } - return nullptr; - } - - const Property* find_property(core::PropertyId id) const - { - for (const auto& property : properties_) { - if (property.id().value == id.value) { - return &property; - } - } - return nullptr; - } - - const AnalysisStep* find_step(core::StepId id) const - { - for (const auto& step : steps_) { - if (step.id().value == id.value) { - return &step; - } - } - return nullptr; - } + const Node* find_node(core::NodeId id) const; + const Element* find_element(core::ElementId id) const; + const Material* find_material(core::MaterialId id) const; + const Property* find_property(core::PropertyId id) const; + const AnalysisStep* find_step(core::StepId id) const; private: std::vector nodes_; diff --git a/src/fesa/model/element.cpp b/src/fesa/model/element.cpp new file mode 100644 index 0000000..a4eda6b --- /dev/null +++ b/src/fesa/model/element.cpp @@ -0,0 +1,38 @@ +#include + +#include + +namespace fesa::model { + +Element::Element(core::ElementId id, + ElementTopology topology, + std::vector node_ids, + core::PropertyId property_id) + : id_(id), + topology_(topology), + node_ids_(std::move(node_ids)), + property_id_(property_id) +{ +} + +core::ElementId Element::id() const +{ + return id_; +} + +ElementTopology Element::topology() const +{ + return topology_; +} + +const std::vector& Element::node_ids() const +{ + return node_ids_; +} + +core::PropertyId Element::property_id() const +{ + return property_id_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/element.hpp b/src/fesa/model/element.hpp index 7bd24e2..ab7da3a 100644 --- a/src/fesa/model/element.hpp +++ b/src/fesa/model/element.hpp @@ -2,7 +2,6 @@ #include -#include #include namespace fesa::model { @@ -18,33 +17,12 @@ public: Element(core::ElementId id, ElementTopology topology, std::vector node_ids, - core::PropertyId property_id) - : id_(id), - topology_(topology), - node_ids_(std::move(node_ids)), - property_id_(property_id) - { - } + core::PropertyId property_id); - core::ElementId id() const - { - return id_; - } - - ElementTopology topology() const - { - return topology_; - } - - const std::vector& node_ids() const - { - return node_ids_; - } - - core::PropertyId property_id() const - { - return property_id_; - } + core::ElementId id() const; + ElementTopology topology() const; + const std::vector& node_ids() const; + core::PropertyId property_id() const; private: core::ElementId id_; diff --git a/src/fesa/model/load.cpp b/src/fesa/model/load.cpp new file mode 100644 index 0000000..d756ec1 --- /dev/null +++ b/src/fesa/model/load.cpp @@ -0,0 +1,25 @@ +#include + +namespace fesa::model { + +Load::Load(core::NodeId node_id, DofComponent component, double value) + : node_id_(node_id), component_(component), value_(value) +{ +} + +core::NodeId Load::node_id() const +{ + return node_id_; +} + +DofComponent Load::component() const +{ + return component_; +} + +double Load::value() const +{ + return value_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/load.hpp b/src/fesa/model/load.hpp index 935fd5f..75f3e15 100644 --- a/src/fesa/model/load.hpp +++ b/src/fesa/model/load.hpp @@ -6,25 +6,11 @@ namespace fesa::model { class Load { public: - Load(core::NodeId node_id, DofComponent component, double value) - : node_id_(node_id), component_(component), value_(value) - { - } + Load(core::NodeId node_id, DofComponent component, double value); - core::NodeId node_id() const - { - return node_id_; - } - - DofComponent component() const - { - return component_; - } - - double value() const - { - return value_; - } + core::NodeId node_id() const; + DofComponent component() const; + double value() const; private: core::NodeId node_id_; diff --git a/src/fesa/model/material.cpp b/src/fesa/model/material.cpp new file mode 100644 index 0000000..f7762ef --- /dev/null +++ b/src/fesa/model/material.cpp @@ -0,0 +1,22 @@ +#include + +#include + +namespace fesa::model { + +Material::Material(core::MaterialId id, std::string name) + : id_(id), name_(std::move(name)) +{ +} + +core::MaterialId Material::id() const +{ + return id_; +} + +const std::string& Material::name() const +{ + return name_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/material.hpp b/src/fesa/model/material.hpp index fd1945b..469ae08 100644 --- a/src/fesa/model/material.hpp +++ b/src/fesa/model/material.hpp @@ -3,26 +3,15 @@ #include #include -#include namespace fesa::model { class Material { public: - Material(core::MaterialId id, std::string name) - : id_(id), name_(std::move(name)) - { - } + Material(core::MaterialId id, std::string name); - core::MaterialId id() const - { - return id_; - } - - const std::string& name() const - { - return name_; - } + core::MaterialId id() const; + const std::string& name() const; private: core::MaterialId id_; diff --git a/src/fesa/model/node.cpp b/src/fesa/model/node.cpp new file mode 100644 index 0000000..3679d61 --- /dev/null +++ b/src/fesa/model/node.cpp @@ -0,0 +1,20 @@ +#include + +namespace fesa::model { + +Node::Node(core::NodeId id, std::array coordinates) + : id_(id), coordinates_(coordinates) +{ +} + +core::NodeId Node::id() const +{ + return id_; +} + +const std::array& Node::coordinates() const +{ + return coordinates_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/node.hpp b/src/fesa/model/node.hpp index 1620e45..83ae2b0 100644 --- a/src/fesa/model/node.hpp +++ b/src/fesa/model/node.hpp @@ -8,20 +8,10 @@ namespace fesa::model { class Node { public: - Node(core::NodeId id, std::array coordinates) - : id_(id), coordinates_(coordinates) - { - } + Node(core::NodeId id, std::array coordinates); - core::NodeId id() const - { - return id_; - } - - const std::array& coordinates() const - { - return coordinates_; - } + core::NodeId id() const; + const std::array& coordinates() const; private: core::NodeId id_; diff --git a/src/fesa/model/property.cpp b/src/fesa/model/property.cpp new file mode 100644 index 0000000..80cf2bf --- /dev/null +++ b/src/fesa/model/property.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace fesa::model { + +Property::Property(core::PropertyId id, std::string name, core::MaterialId material_id) + : id_(id), name_(std::move(name)), material_id_(material_id) +{ +} + +core::PropertyId Property::id() const +{ + return id_; +} + +const std::string& Property::name() const +{ + return name_; +} + +core::MaterialId Property::material_id() const +{ + return material_id_; +} + +} // namespace fesa::model diff --git a/src/fesa/model/property.hpp b/src/fesa/model/property.hpp index 1a99cc5..609623a 100644 --- a/src/fesa/model/property.hpp +++ b/src/fesa/model/property.hpp @@ -3,31 +3,16 @@ #include #include -#include namespace fesa::model { class Property { public: - Property(core::PropertyId id, std::string name, core::MaterialId material_id) - : id_(id), name_(std::move(name)), material_id_(material_id) - { - } + Property(core::PropertyId id, std::string name, core::MaterialId material_id); - core::PropertyId id() const - { - return id_; - } - - const std::string& name() const - { - return name_; - } - - core::MaterialId material_id() const - { - return material_id_; - } + core::PropertyId id() const; + const std::string& name() const; + core::MaterialId material_id() const; private: core::PropertyId id_; diff --git a/src/fesa/results/results.cpp b/src/fesa/results/results.cpp new file mode 100644 index 0000000..6b99bc7 --- /dev/null +++ b/src/fesa/results/results.cpp @@ -0,0 +1,70 @@ +#include + +#include +#include + +namespace fesa::results { + +ResultFrame::ResultFrame(int frame_id, double time) + : frame_id_(frame_id), time_(time) +{ +} + +int ResultFrame::frame_id() const +{ + return frame_id_; +} + +double ResultFrame::time() const +{ + return time_; +} + +void ResultFrame::add_field_output(FieldOutput output) +{ + if (output.components.empty()) { + throw std::invalid_argument("field output must have components"); + } + if (output.entity_ids.size() * output.components.size() != output.values.size()) { + throw std::invalid_argument("field output values do not match row shape"); + } + field_outputs_.push_back(std::move(output)); +} + +void ResultFrame::add_history_output(HistoryOutput output) +{ + history_outputs_.push_back(std::move(output)); +} + +const std::vector& ResultFrame::field_outputs() const +{ + return field_outputs_; +} + +const std::vector& ResultFrame::history_outputs() const +{ + return history_outputs_; +} + +ResultStep::ResultStep(std::string name) + : name_(std::move(name)) +{ +} + +const std::string& ResultStep::name() const +{ + return name_; +} + +ResultFrame& ResultStep::add_frame(int frame_id, double time) +{ + frames_.push_back(ResultFrame{frame_id, time}); + return frames_.back(); +} + +const std::vector& ResultStep::frames() const +{ + return frames_; +} + +} // namespace fesa::results diff --git a/src/fesa/results/results.hpp b/src/fesa/results/results.hpp index 1d352d2..d9f68c0 100644 --- a/src/fesa/results/results.hpp +++ b/src/fesa/results/results.hpp @@ -1,8 +1,6 @@ #pragma once -#include #include -#include #include namespace fesa::results { @@ -29,46 +27,14 @@ struct HistoryOutput { class ResultFrame { public: - ResultFrame(int frame_id, double time) - : frame_id_(frame_id), time_(time) - { - } + ResultFrame(int frame_id, double time); - int frame_id() const - { - return frame_id_; - } - - double time() const - { - return time_; - } - - void add_field_output(FieldOutput output) - { - if (output.components.empty()) { - throw std::invalid_argument("field output must have components"); - } - if (output.entity_ids.size() * output.components.size() != output.values.size()) { - throw std::invalid_argument("field output values do not match row shape"); - } - field_outputs_.push_back(std::move(output)); - } - - void add_history_output(HistoryOutput output) - { - history_outputs_.push_back(std::move(output)); - } - - const std::vector& field_outputs() const - { - return field_outputs_; - } - - const std::vector& history_outputs() const - { - return history_outputs_; - } + int frame_id() const; + double time() const; + void add_field_output(FieldOutput output); + void add_history_output(HistoryOutput output); + const std::vector& field_outputs() const; + const std::vector& history_outputs() const; private: int frame_id_; @@ -79,26 +45,11 @@ private: class ResultStep { public: - explicit ResultStep(std::string name) - : name_(std::move(name)) - { - } + explicit ResultStep(std::string name); - const std::string& name() const - { - return name_; - } - - ResultFrame& add_frame(int frame_id, double time) - { - frames_.push_back(ResultFrame{frame_id, time}); - return frames_.back(); - } - - const std::vector& frames() const - { - return frames_; - } + const std::string& name() const; + ResultFrame& add_frame(int frame_id, double time); + const std::vector& frames() const; private: std::string name_;