feat(linear-static-mitc4-shell): step 9 - shell-analysis-state

This commit is contained in:
KOKO\Mimi
2026-08-12 20:46:40 +09:00
parent 0a1ef59b41
commit 8c776fe6e2
4 changed files with 457 additions and 0 deletions
+166
View File
@@ -1,8 +1,57 @@
#include "fesa/analysis/analysis_state.hpp"
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <string>
#include <utility>
namespace fesa {
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::model,
{{Severity::error,
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); });
}
bool finite(const ShellResultRow& row) {
if (!finite(row.naturalCoordinates) ||
!finite(row.generalizedStrain) ||
!finite(row.sectionResultant)) {
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);
});
}
} // namespace
AnalysisState AnalysisState::create(
const DofManager& dofs, StepFrameIdentity identity) {
@@ -77,6 +126,123 @@ const std::vector<StressS11Row>& AnalysisState::stressResults() const noexcept {
return stressResults_;
}
Status AnalysisState::commitShellResults(
const std::vector<EntityIndex>& expectedElementOrder,
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.");
}
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.");
}
}
}
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.");
}
shellResults_ = std::move(candidate.rows);
physicalStrainEnergy_ = candidate.physicalStrainEnergy;
equilibrium_ = candidate.equilibrium;
verificationMetrics_ = candidate.verificationMetrics;
return Status::ok();
}
const std::vector<ShellResultRow>& AnalysisState::shellResults() const noexcept {
return shellResults_;
}
double AnalysisState::physicalStrainEnergy() const noexcept {
return physicalStrainEnergy_;
}
const std::array<double, 6>& AnalysisState::equilibrium() const noexcept {
return equilibrium_;
}
const std::array<double, 3>& AnalysisState::verificationMetrics() const noexcept {
return verificationMetrics_;
}
AnalysisState::AnalysisState(
std::size_t fullDofCount, StepFrameIdentity identity)
: identity_{std::move(identity)},