feat(cpp-object-oriented-modular-refactoring): step 5 - solver-workflow-google-style

This commit is contained in:
KOKO\Mimi
2026-08-16 06:20:08 +09:00
parent e1c0e357dd
commit 24f006fe4a
52 changed files with 5817 additions and 6369 deletions
+62 -68
View File
@@ -1,96 +1,90 @@
#include "fesa/analysis/analysis_model.hpp"
#include "fesa/analysis/analysis_model.h"
#include <string>
#include <vector>
namespace fesa {
Result<AnalysisModel> AnalysisModel::create(const Domain& domain) {
if (domain.Steps().empty()) {
return Result<AnalysisModel>::Failure(Status::Failure(
FailureCategory::kInput,
{{Severity::kError,
"invalid-model-cardinality",
{domain.SourcePath(), 0U},
"STEP",
"0",
"AnalysisModel requires exactly one static step."}}));
}
if (domain.Steps().size() > 1U) {
const auto& secondStep = domain.Steps()[1];
return Result<AnalysisModel>::Failure(Status::Failure(
FailureCategory::kInput,
{{Severity::kError,
"unsupported-multiple-step",
secondStep.location,
"STEP",
secondStep.name,
"AnalysisModel does not support multiple steps."}}));
}
return Result<AnalysisModel>::Success(AnalysisModel{domain});
Result<AnalysisModel> AnalysisModel::Create(const Domain& domain) {
if (domain.Steps().empty()) {
return Result<AnalysisModel>::Failure(
Status::Failure(FailureCategory::kInput,
{{Severity::kError,
"invalid-model-cardinality",
{domain.SourcePath(), 0U},
"STEP",
"0",
"AnalysisModel requires exactly one static step."}}));
}
if (domain.Steps().size() > 1U) {
const auto& second_step = domain.Steps()[1];
return Result<AnalysisModel>::Failure(
Status::Failure(FailureCategory::kInput,
{{Severity::kError, "unsupported-multiple-step",
second_step.location, "STEP", second_step.name,
"AnalysisModel does not support multiple steps."}}));
}
return Result<AnalysisModel>::Success(AnalysisModel{domain});
}
const Domain& AnalysisModel::domain() const noexcept {
return *domain_;
const Domain& AnalysisModel::GetDomain() const noexcept { return *domain_; }
const StaticStepDefinition& AnalysisModel::Step() const noexcept {
return domain_->Steps().front();
}
const StaticStepDefinition& AnalysisModel::step() const noexcept {
return domain_->Steps().front();
const std::vector<EntityIndex>& AnalysisModel::ActiveElements() const noexcept {
return active_elements_;
}
const std::vector<EntityIndex>& AnalysisModel::activeElements() const noexcept {
return activeElements_;
const std::vector<EntityIndex>& AnalysisModel::ActiveMaterials()
const noexcept {
return active_materials_;
}
const std::vector<EntityIndex>& AnalysisModel::activeMaterials() const noexcept {
return activeMaterials_;
const std::vector<EntityIndex>& AnalysisModel::ActiveSections() const noexcept {
return active_sections_;
}
const std::vector<EntityIndex>& AnalysisModel::activeSections() const noexcept {
return activeSections_;
const std::vector<EntityIndex>& AnalysisModel::ActiveBoundaryConditions()
const noexcept {
return active_boundary_conditions_;
}
const std::vector<EntityIndex>&
AnalysisModel::activeBoundaryConditions() const noexcept {
return activeBoundaryConditions_;
}
const std::vector<EntityIndex>& AnalysisModel::activeLoads() const noexcept {
return activeLoads_;
const std::vector<EntityIndex>& AnalysisModel::ActiveLoads() const noexcept {
return active_loads_;
}
AnalysisModel::AnalysisModel(const Domain& domain) : domain_{&domain} {
std::vector<bool> reachableMaterials(domain.Materials().size(), false);
std::vector<bool> reachableSections(domain.Sections().size(), false);
std::vector<bool> reachable_materials(domain.Materials().size(), false);
std::vector<bool> reachable_sections(domain.Sections().size(), false);
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
const auto& element = domain.Elements()[index];
activeElements_.push_back(static_cast<EntityIndex>(index));
reachableMaterials[element.material_index] = true;
reachableSections[element.section_index] = true;
}
for (std::size_t index = 0U; index < domain.Elements().size(); ++index) {
const auto& element = domain.Elements()[index];
active_elements_.push_back(static_cast<EntityIndex>(index));
reachable_materials[element.material_index] = true;
reachable_sections[element.section_index] = true;
}
// Ascending vector positions are the stable internal order, independent
// of first reachability or duplicate element assignments.
for (std::size_t index = 0U; index < reachableMaterials.size(); ++index) {
if (reachableMaterials[index]) {
activeMaterials_.push_back(static_cast<EntityIndex>(index));
}
// Ascending vector positions are the stable internal order, independent
// of first reachability or duplicate element assignments.
for (std::size_t index = 0U; index < reachable_materials.size(); ++index) {
if (reachable_materials[index]) {
active_materials_.push_back(static_cast<EntityIndex>(index));
}
for (std::size_t index = 0U; index < reachableSections.size(); ++index) {
if (reachableSections[index]) {
activeSections_.push_back(static_cast<EntityIndex>(index));
}
}
for (std::size_t index = 0U; index < reachable_sections.size(); ++index) {
if (reachable_sections[index]) {
active_sections_.push_back(static_cast<EntityIndex>(index));
}
}
for (std::size_t index = 0U;
index < step().boundaries.size();
++index) {
activeBoundaryConditions_.push_back(static_cast<EntityIndex>(index));
}
for (std::size_t index = 0U; index < step().loads.size(); ++index) {
activeLoads_.push_back(static_cast<EntityIndex>(index));
}
for (std::size_t index = 0U; index < Step().boundaries.size(); ++index) {
active_boundary_conditions_.push_back(static_cast<EntityIndex>(index));
}
for (std::size_t index = 0U; index < Step().loads.size(); ++index) {
active_loads_.push_back(static_cast<EntityIndex>(index));
}
}
} // namespace fesa
} // namespace fesa
+162 -194
View File
@@ -1,4 +1,4 @@
#include "fesa/analysis/analysis_state.hpp"
#include "fesa/analysis/analysis_state.h"
#include <algorithm>
#include <array>
@@ -12,244 +12,212 @@ namespace {
constexpr std::size_t kShellLocationsPerElement = 4U;
Status shellCandidateFailure(
const std::string& code,
const std::string& identity,
const std::string& message) {
return Status::Failure(
FailureCategory::kModel,
{{Severity::kError,
code,
{},
"ANALYSIS_STATE",
identity,
message}});
/// @brief Creates a structured failure without mutating existing state.
Status ShellCandidateFailure(const std::string& code,
const std::string& identity,
const std::string& message) {
return Status::Failure(
FailureCategory::kModel,
{{Severity::kError, code, {}, "ANALYSIS_STATE", identity, message}});
}
template<std::size_t Size>
bool finite(const std::array<double, Size>& values) {
return std::all_of(
values.begin(), values.end(),
[](const double value) { return std::isfinite(value); });
template <std::size_t Size>
/// @brief Tests one fixed-size candidate component array for finite values.
bool IsFinite(const std::array<double, Size>& values) {
return std::all_of(values.begin(), values.end(),
[](const double value) { return std::isfinite(value); });
}
bool finite(const ShellResultRow& row) {
if (!finite(row.naturalCoordinates) ||
!finite(row.generalizedStrain) ||
!finite(row.sectionResultant)) {
return false;
/// @brief Tests every physical shell row component for finite values.
bool IsFinite(const ShellResultRow& row) {
if (!IsFinite(row.natural_coordinates) || !IsFinite(row.generalized_strain) ||
!IsFinite(row.section_resultant)) {
return false;
}
for (const auto& axis : row.local_frame) {
if (!IsFinite(axis)) {
return false;
}
for (const auto& axis : row.localFrame) {
if (!finite(axis)) {
return false;
}
}
return std::all_of(
row.stress.begin(), row.stress.end(),
[](const ShellSectionStressRow& stress) {
return std::isfinite(stress.zeta) && finite(stress.components);
});
}
return std::all_of(row.stress.begin(), row.stress.end(),
[](const ShellSectionStressRow& stress) {
return std::isfinite(stress.zeta) &&
IsFinite(stress.components);
});
}
} // namespace
} // namespace
AnalysisState AnalysisState::create(
const DofManager& dofs, StepFrameIdentity identity) {
return AnalysisState{dofs.fullDofCount(), std::move(identity)};
AnalysisState AnalysisState::Create(const DofManager& dofs,
StepFrameIdentity identity) {
return AnalysisState{dofs.FullDofCount(), std::move(identity)};
}
Vector& AnalysisState::displacement() noexcept {
return displacement_;
Vector& AnalysisState::Displacement() noexcept { return displacement_; }
const Vector& AnalysisState::Displacement() const noexcept {
return displacement_;
}
const Vector& AnalysisState::displacement() const noexcept {
return displacement_;
Vector& AnalysisState::ExternalForce() noexcept { return external_force_; }
const Vector& AnalysisState::ExternalForce() const noexcept {
return external_force_;
}
Vector& AnalysisState::externalForce() noexcept {
return externalForce_;
Vector& AnalysisState::InternalForce() noexcept { return internal_force_; }
const Vector& AnalysisState::InternalForce() const noexcept {
return internal_force_;
}
const Vector& AnalysisState::externalForce() const noexcept {
return externalForce_;
Vector& AnalysisState::Residual() noexcept { return residual_; }
const Vector& AnalysisState::Residual() const noexcept { return residual_; }
Vector& AnalysisState::Reaction() noexcept { return reaction_; }
const Vector& AnalysisState::Reaction() const noexcept { return reaction_; }
const StepFrameIdentity& AnalysisState::Identity() const noexcept {
return identity_;
}
Vector& AnalysisState::internalForce() noexcept {
return internalForce_;
std::vector<EndpointResultRow>& AnalysisState::EndpointResults() noexcept {
return endpoint_results_;
}
const Vector& AnalysisState::internalForce() const noexcept {
return internalForce_;
const std::vector<EndpointResultRow>& AnalysisState::EndpointResults()
const noexcept {
return endpoint_results_;
}
Vector& AnalysisState::residual() noexcept {
return residual_;
std::vector<GaussResultRow>& AnalysisState::GaussResults() noexcept {
return gauss_results_;
}
const Vector& AnalysisState::residual() const noexcept {
return residual_;
const std::vector<GaussResultRow>& AnalysisState::GaussResults()
const noexcept {
return gauss_results_;
}
Vector& AnalysisState::reaction() noexcept {
return reaction_;
std::vector<StressS11Row>& AnalysisState::StressResults() noexcept {
return stress_results_;
}
const Vector& AnalysisState::reaction() const noexcept {
return reaction_;
const std::vector<StressS11Row>& AnalysisState::StressResults() const noexcept {
return stress_results_;
}
const StepFrameIdentity& AnalysisState::identity() const noexcept {
return identity_;
}
std::vector<EndpointResultRow>& AnalysisState::endpointResults() noexcept {
return endpointResults_;
}
const std::vector<EndpointResultRow>& AnalysisState::endpointResults() const noexcept {
return endpointResults_;
}
std::vector<GaussResultRow>& AnalysisState::gaussResults() noexcept {
return gaussResults_;
}
const std::vector<GaussResultRow>& AnalysisState::gaussResults() const noexcept {
return gaussResults_;
}
std::vector<StressS11Row>& AnalysisState::stressResults() noexcept {
return stressResults_;
}
const std::vector<StressS11Row>& AnalysisState::stressResults() const noexcept {
return stressResults_;
}
Status AnalysisState::commitShellResults(
const std::vector<EntityIndex>& expectedElementOrder,
Status AnalysisState::CommitShellResults(
const std::vector<EntityIndex>& expected_element_order,
ShellStateCandidate candidate) {
if (expectedElementOrder.size() >
(std::numeric_limits<std::size_t>::max)() /
kShellLocationsPerElement) {
return shellCandidateFailure(
"invalid-shell-state-inventory",
identity_.stepName,
"The expected shell result inventory is too large.");
}
const std::size_t expectedRowCount =
expectedElementOrder.size() * kShellLocationsPerElement;
if (candidate.rows.size() != expectedRowCount) {
return shellCandidateFailure(
"invalid-shell-state-inventory",
identity_.stepName,
"Shell results require exactly four rows per expected element.");
}
if (std::adjacent_find(
expectedElementOrder.begin(), expectedElementOrder.end(),
[](const EntityIndex left, const EntityIndex right) {
return left >= right;
}) != expectedElementOrder.end()) {
return shellCandidateFailure(
"invalid-shell-state-inventory",
identity_.stepName,
"Expected shell elements must be unique and in stable index order.");
}
if (expected_element_order.size() >
(std::numeric_limits<std::size_t>::max)() / kShellLocationsPerElement) {
return ShellCandidateFailure(
"invalid-shell-state-inventory", identity_.step_name,
"The expected shell result inventory is too large.");
}
const std::size_t expected_row_count =
expected_element_order.size() * kShellLocationsPerElement;
if (candidate.rows.size() != expected_row_count) {
return ShellCandidateFailure(
"invalid-shell-state-inventory", identity_.step_name,
"Shell results require exactly four rows per expected element.");
}
if (std::adjacent_find(expected_element_order.begin(),
expected_element_order.end(),
[](const EntityIndex left, const EntityIndex right) {
return left >= right;
}) != expected_element_order.end()) {
return ShellCandidateFailure(
"invalid-shell-state-inventory", identity_.step_name,
"Expected shell elements must be unique and in stable index order.");
}
const std::array<ShellMidsurfaceLocation, kShellLocationsPerElement>
expectedLocations{
ShellMidsurfaceLocation::gp1,
ShellMidsurfaceLocation::gp2,
ShellMidsurfaceLocation::gp3,
ShellMidsurfaceLocation::gp4};
const double gauss = 1.0 / std::sqrt(3.0);
const std::array<std::array<double, 2>, kShellLocationsPerElement>
expectedCoordinates{
std::array<double, 2>{-gauss, -gauss},
std::array<double, 2>{gauss, -gauss},
std::array<double, 2>{gauss, gauss},
std::array<double, 2>{-gauss, gauss}};
const std::array<ShellSectionPosition, 3> expectedPositions{
ShellSectionPosition::bottom,
ShellSectionPosition::middle,
ShellSectionPosition::top};
constexpr std::array<double, 3> expectedZeta{-1.0, 0.0, 1.0};
for (std::size_t elementOrder = 0U;
elementOrder < expectedElementOrder.size();
++elementOrder) {
for (std::size_t point = 0U;
point < kShellLocationsPerElement;
++point) {
const auto& row = candidate.rows[
elementOrder * kShellLocationsPerElement + point];
if (row.element != expectedElementOrder[elementOrder] ||
row.location != expectedLocations[point] ||
row.naturalCoordinates != expectedCoordinates[point]) {
return shellCandidateFailure(
"invalid-shell-state-inventory",
std::to_string(row.element),
"Shell rows must preserve element and GP1 through GP4 identity.");
}
for (std::size_t position = 0U;
position < expectedPositions.size();
++position) {
if (row.stress[position].position !=
expectedPositions[position] ||
row.stress[position].zeta != expectedZeta[position]) {
return shellCandidateFailure(
"invalid-shell-state-inventory",
std::to_string(row.element),
"Shell stress rows require BOTTOM, MIDDLE, TOP identity.");
}
}
if (!finite(row)) {
return shellCandidateFailure(
"nonfinite-shell-state-value",
std::to_string(row.element),
"Shell result rows must contain only finite values.");
}
const std::array<ShellMidsurfaceLocation, kShellLocationsPerElement>
expected_locations{
ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
const double gauss = 1.0 / std::sqrt(3.0);
const std::array<std::array<double, 2>, kShellLocationsPerElement>
expected_coordinates{std::array<double, 2>{-gauss, -gauss},
std::array<double, 2>{gauss, -gauss},
std::array<double, 2>{gauss, gauss},
std::array<double, 2>{-gauss, gauss}};
const std::array<ShellSectionPosition, 3> expected_positions{
ShellSectionPosition::kBottom, ShellSectionPosition::kMiddle,
ShellSectionPosition::kTop};
constexpr std::array<double, 3> expected_zeta{-1.0, 0.0, 1.0};
for (std::size_t element_order = 0U;
element_order < expected_element_order.size(); ++element_order) {
for (std::size_t point = 0U; point < kShellLocationsPerElement; ++point) {
const auto& row =
candidate.rows[element_order * kShellLocationsPerElement + point];
if (row.element != expected_element_order[element_order] ||
row.location != expected_locations[point] ||
row.natural_coordinates != expected_coordinates[point]) {
return ShellCandidateFailure(
"invalid-shell-state-inventory", std::to_string(row.element),
"Shell rows must preserve element and GP1 through GP4 identity.");
}
for (std::size_t position = 0U; position < expected_positions.size();
++position) {
if (row.stress[position].position != expected_positions[position] ||
row.stress[position].zeta != expected_zeta[position]) {
return ShellCandidateFailure(
"invalid-shell-state-inventory", std::to_string(row.element),
"Shell stress rows require BOTTOM, MIDDLE, TOP identity.");
}
}
if (!IsFinite(row)) {
return ShellCandidateFailure(
"nonfinite-shell-state-value", std::to_string(row.element),
"Shell result rows must contain only finite values.");
}
}
}
if (!std::isfinite(candidate.physicalStrainEnergy) ||
!finite(candidate.equilibrium) ||
!finite(candidate.verificationMetrics)) {
return shellCandidateFailure(
"nonfinite-shell-state-value",
identity_.stepName,
"Shell energy, equilibrium, and normalized metrics must be finite.");
}
if (!std::isfinite(candidate.physical_strain_energy) ||
!IsFinite(candidate.equilibrium) ||
!IsFinite(candidate.verification_metrics)) {
return ShellCandidateFailure(
"nonfinite-shell-state-value", identity_.step_name,
"Shell energy, equilibrium, and normalized metrics must be finite.");
}
shellResults_ = std::move(candidate.rows);
physicalStrainEnergy_ = candidate.physicalStrainEnergy;
equilibrium_ = candidate.equilibrium;
verificationMetrics_ = candidate.verificationMetrics;
return Status::Ok();
shell_results_ = std::move(candidate.rows);
physical_strain_energy_ = candidate.physical_strain_energy;
equilibrium_ = candidate.equilibrium;
verification_metrics_ = candidate.verification_metrics;
return Status::Ok();
}
const std::vector<ShellResultRow>& AnalysisState::shellResults() const noexcept {
return shellResults_;
const std::vector<ShellResultRow>& AnalysisState::ShellResults()
const noexcept {
return shell_results_;
}
double AnalysisState::physicalStrainEnergy() const noexcept {
return physicalStrainEnergy_;
double AnalysisState::PhysicalStrainEnergy() const noexcept {
return physical_strain_energy_;
}
const std::array<double, 6>& AnalysisState::equilibrium() const noexcept {
return equilibrium_;
const std::array<double, 6>& AnalysisState::Equilibrium() const noexcept {
return equilibrium_;
}
const std::array<double, 3>& AnalysisState::verificationMetrics() const noexcept {
return verificationMetrics_;
const std::array<double, 3>& AnalysisState::VerificationMetrics()
const noexcept {
return verification_metrics_;
}
AnalysisState::AnalysisState(
std::size_t fullDofCount, StepFrameIdentity identity)
AnalysisState::AnalysisState(std::size_t full_dof_count,
StepFrameIdentity identity)
: identity_{std::move(identity)},
displacement_{fullDofCount},
externalForce_{fullDofCount},
internalForce_{fullDofCount},
residual_{fullDofCount},
reaction_{fullDofCount} {}
displacement_{full_dof_count},
external_force_{full_dof_count},
internal_force_{full_dof_count},
residual_{full_dof_count},
reaction_{full_dof_count} {}
} // namespace fesa
} // namespace fesa
+137 -141
View File
@@ -1,170 +1,166 @@
#include "fesa/analysis/linear_static_analysis.hpp"
#include "fesa/assembly/load_assembler.hpp"
#include "fesa/assembly/parallel_for.hpp"
#include "fesa/assembly/sparse_assembler.hpp"
#include "fesa/io/abaqus/domain_mapper.hpp"
#include "fesa/io/abaqus/input_reader.hpp"
#include "fesa/results/result_recovery.hpp"
#include "fesa/results/results_writer.hpp"
#include "fesa/solvers/linear/linear_solver.h"
#include "fesa/analysis/linear_static_analysis.h"
#include <utility>
#include "fesa/assembly/load_assembler.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/assembly/sparse_assembler.h"
#include "fesa/io/abaqus/domain_mapper.hpp"
#include "fesa/io/abaqus/input_reader.hpp"
#include "fesa/results/result_recovery.h"
#include "fesa/results/results_writer.h"
#include "fesa/solvers/linear/linear_solver.h"
namespace fesa {
Status Analysis::run(const AnalysisRequest& request) {
Status status = initialize(request);
if (!status.IsOk()) {
return status;
}
status = buildAnalysisModel();
if (!status.IsOk()) {
return status;
}
status = buildDofMapAndSparsePattern();
if (!status.IsOk()) {
return status;
}
status = assembleAndPartitionStiffness();
if (!status.IsOk()) {
return status;
}
status = factorize();
if (!status.IsOk()) {
return status;
}
status = assembleLoadsAndEffectiveRhs();
if (!status.IsOk()) {
return status;
}
status = substituteAndReconstruct();
if (!status.IsOk()) {
return status;
}
return recoverAndWriteResults();
Status Analysis::Run(const AnalysisRequest& request) {
Status status = Initialize(request);
if (!status.IsOk()) {
return status;
}
status = BuildAnalysisModel();
if (!status.IsOk()) {
return status;
}
status = BuildDofMapAndSparsePattern();
if (!status.IsOk()) {
return status;
}
status = AssembleAndPartitionStiffness();
if (!status.IsOk()) {
return status;
}
status = Factorize();
if (!status.IsOk()) {
return status;
}
status = AssembleLoadsAndEffectiveRhs();
if (!status.IsOk()) {
return status;
}
status = SubstituteAndReconstruct();
if (!status.IsOk()) {
return status;
}
return RecoverAndWriteResults();
}
LinearStaticAnalysis::LinearStaticAnalysis(
const ParallelFor& parallelFor,
LinearSolver& linearSolver,
ResultsWriter& resultsWriter)
: parallelFor_{parallelFor},
linearSolver_{linearSolver},
resultsWriter_{resultsWriter} {}
LinearStaticAnalysis::LinearStaticAnalysis(const ParallelFor& parallel_for,
LinearSolver& linear_solver,
ResultsWriter& results_writer)
: parallel_for_{parallel_for},
linear_solver_{linear_solver},
results_writer_{results_writer} {}
Status LinearStaticAnalysis::initialize(const AnalysisRequest& request) {
// Clear dependent objects in reverse ownership order so a reused analysis
// never exposes a view into a Domain from an earlier run.
effectiveRhs_.reset();
partitionedStiffness_.reset();
fullStiffness_.reset();
state_.reset();
dofs_.reset();
model_.reset();
domain_.reset();
diagnostics_.clear();
request_ = request;
Status LinearStaticAnalysis::Initialize(const AnalysisRequest& request) {
// Clear dependent objects in reverse ownership order so a reused analysis
// never exposes a view into a Domain from an earlier run.
effective_rhs_.reset();
partitioned_stiffness_.reset();
full_stiffness_.reset();
state_.reset();
dofs_.reset();
model_.reset();
domain_.reset();
diagnostics_.clear();
request_ = request;
const auto parsed = AbaqusInputReader{}.read(request_.inputPath);
if (!parsed.HasValue()) {
return parsed.GetStatus();
}
auto domain = AbaqusDomainMapper{}.map(parsed.Value());
if (!domain.HasValue()) {
return domain.GetStatus();
}
const auto parsed = AbaqusInputReader{}.read(request_.input_path);
if (!parsed.HasValue()) {
return parsed.GetStatus();
}
auto domain = AbaqusDomainMapper{}.map(parsed.Value());
if (!domain.HasValue()) {
return domain.GetStatus();
}
domain_ = std::make_unique<Domain>(std::move(domain.Value()));
diagnostics_ = domain_->Warnings();
SortDiagnostics(diagnostics_);
return Status::Ok();
domain_ = std::make_unique<Domain>(std::move(domain.Value()));
diagnostics_ = domain_->Warnings();
SortDiagnostics(diagnostics_);
return Status::Ok();
}
Status LinearStaticAnalysis::buildAnalysisModel() {
auto model = AnalysisModel::create(*domain_);
if (!model.HasValue()) {
return model.GetStatus();
}
model_ = std::make_unique<AnalysisModel>(std::move(model.Value()));
return Status::Ok();
Status LinearStaticAnalysis::BuildAnalysisModel() {
auto model = AnalysisModel::Create(*domain_);
if (!model.HasValue()) {
return model.GetStatus();
}
model_ = std::make_unique<AnalysisModel>(std::move(model.Value()));
return Status::Ok();
}
Status LinearStaticAnalysis::buildDofMapAndSparsePattern() {
auto dofs = DofManager::create(*model_);
if (!dofs.HasValue()) {
return dofs.GetStatus();
}
dofs_ = std::make_unique<DofManager>(std::move(dofs.Value()));
state_ = std::make_unique<AnalysisState>(
AnalysisState::create(*dofs_, {"Step-1", 0U}));
return Status::Ok();
Status LinearStaticAnalysis::BuildDofMapAndSparsePattern() {
auto dofs = DofManager::Create(*model_);
if (!dofs.HasValue()) {
return dofs.GetStatus();
}
dofs_ = std::make_unique<DofManager>(std::move(dofs.Value()));
state_ = std::make_unique<AnalysisState>(
AnalysisState::Create(*dofs_, {"Step-1", 0U}));
return Status::Ok();
}
Status LinearStaticAnalysis::assembleAndPartitionStiffness() {
auto stiffness = SparseAssembler::assembleStiffness(
*model_, *dofs_, parallelFor_);
if (!stiffness.HasValue()) {
return stiffness.GetStatus();
}
fullStiffness_ =
std::make_unique<SparseMatrix>(std::move(stiffness.Value()));
Status LinearStaticAnalysis::AssembleAndPartitionStiffness() {
auto stiffness =
SparseAssembler::AssembleStiffness(*model_, *dofs_, parallel_for_);
if (!stiffness.HasValue()) {
return stiffness.GetStatus();
}
full_stiffness_ =
std::make_unique<SparseMatrix>(std::move(stiffness.Value()));
auto partitioned = EssentialConstraints::partition(
*fullStiffness_, *dofs_);
if (!partitioned.HasValue()) {
return partitioned.GetStatus();
}
partitionedStiffness_ = std::make_unique<PartitionedStiffness>(
std::move(partitioned.Value()));
return Status::Ok();
auto partitioned = EssentialConstraints::Partition(*full_stiffness_, *dofs_);
if (!partitioned.HasValue()) {
return partitioned.GetStatus();
}
partitioned_stiffness_ =
std::make_unique<PartitionedStiffness>(std::move(partitioned.Value()));
return Status::Ok();
}
Status LinearStaticAnalysis::factorize() {
// This call intentionally precedes all load assembly in Analysis::run.
return linearSolver_.Factorize(partitionedStiffness_->kff);
Status LinearStaticAnalysis::Factorize() {
// This call intentionally precedes all load assembly in Analysis::Run.
return linear_solver_.Factorize(partitioned_stiffness_->kff);
}
Status LinearStaticAnalysis::assembleLoadsAndEffectiveRhs() {
auto fullLoad = LoadAssembler::assembleFullNodalLoad(*model_, *dofs_);
if (!fullLoad.HasValue()) {
return fullLoad.GetStatus();
}
state_->externalForce() = std::move(fullLoad.Value());
Status LinearStaticAnalysis::AssembleLoadsAndEffectiveRhs() {
auto full_load = LoadAssembler::AssembleFullNodalLoad(*model_, *dofs_);
if (!full_load.HasValue()) {
return full_load.GetStatus();
}
state_->ExternalForce() = std::move(full_load.Value());
auto rhs = LoadAssembler::effectiveFreeRhs(
state_->externalForce(),
partitionedStiffness_->kfc,
dofs_->prescribedValues(),
*dofs_);
if (!rhs.HasValue()) {
return rhs.GetStatus();
}
effectiveRhs_ = std::make_unique<Vector>(std::move(rhs.Value()));
return Status::Ok();
auto rhs = LoadAssembler::EffectiveFreeRhs(state_->ExternalForce(),
partitioned_stiffness_->kfc,
dofs_->PrescribedValues(), *dofs_);
if (!rhs.HasValue()) {
return rhs.GetStatus();
}
effective_rhs_ = std::make_unique<Vector>(std::move(rhs.Value()));
return Status::Ok();
}
Status LinearStaticAnalysis::substituteAndReconstruct() {
Vector freeDisplacement{dofs_->freeDofCount()};
const Status solveStatus =
linearSolver_.Solve(*effectiveRhs_, freeDisplacement);
if (!solveStatus.IsOk()) {
return solveStatus;
}
Status LinearStaticAnalysis::SubstituteAndReconstruct() {
Vector free_displacement{dofs_->FreeDofCount()};
const Status solve_status =
linear_solver_.Solve(*effective_rhs_, free_displacement);
if (!solve_status.IsOk()) {
return solve_status;
}
state_->displacement() = EssentialConstraints::reconstructFull(
freeDisplacement, dofs_->prescribedValues(), *dofs_);
return Status::Ok();
state_->Displacement() = EssentialConstraints::ReconstructFull(
free_displacement, dofs_->PrescribedValues(), *dofs_);
return Status::Ok();
}
Status LinearStaticAnalysis::recoverAndWriteResults() {
const Status recoveryStatus = ResultRecovery::recover(
*model_, *dofs_, *fullStiffness_, *state_);
if (!recoveryStatus.IsOk()) {
return recoveryStatus;
}
return resultsWriter_.write(
request_.outputPath, *domain_, *state_, diagnostics_);
Status LinearStaticAnalysis::RecoverAndWriteResults() {
const Status recovery_status =
ResultRecovery::Recover(*model_, *dofs_, *full_stiffness_, *state_);
if (!recovery_status.IsOk()) {
return recovery_status;
}
return results_writer_.Write(request_.output_path, *domain_, *state_,
diagnostics_);
}
} // namespace fesa
} // namespace fesa