feat(cpp-object-oriented-modular-refactoring): step 9 - result-io-vector3

This commit is contained in:
KOKO\Mimi
2026-08-16 07:40:16 +09:00
parent 1cb1f26cdc
commit 0be8d1bd89
6 changed files with 140 additions and 148 deletions
+20 -39
View File
@@ -14,6 +14,7 @@
#include <utility>
#include <vector>
#include "fesa/math/vector3.h"
#include "fesa/model/shell_geometry.h"
namespace fesa {
@@ -1800,61 +1801,41 @@ class MappingContext {
bool ValidateGeometry(const RawElement& raw, const Node& first,
const Node& second, const GeneralBeamSection& section) {
const auto norm = [](const std::array<double, 3>& vector) {
return std::hypot(vector[0], vector[1], vector[2]);
};
const auto maximum_absolute = [](const std::array<double, 3>& vector) {
const auto maximum_absolute = [](const Vector3& vector) {
return std::max(
{std::abs(vector[0]), std::abs(vector[1]), std::abs(vector[2])});
};
const Vector3 first_position{first.coordinates};
const Vector3 second_position{second.coordinates};
// Compare both approved inequalities after a common scaling. This
// preserves the exact ratios while avoiding overflow in x*x and in
// subtraction between large finite coordinates.
const double global_coordinate_scale =
std::max({1.0, maximum_absolute(first.coordinates),
maximum_absolute(second.coordinates)});
std::array<double, 3> first_scaled{};
std::array<double, 3> second_scaled{};
std::array<double, 3> delta_scaled{};
for (std::size_t coordinate = 0U; coordinate < 3U; ++coordinate) {
first_scaled[coordinate] =
first.coordinates[coordinate] / global_coordinate_scale;
second_scaled[coordinate] =
second.coordinates[coordinate] / global_coordinate_scale;
delta_scaled[coordinate] =
second_scaled[coordinate] - first_scaled[coordinate];
}
const double length_ratio = norm(delta_scaled);
std::max({1.0, maximum_absolute(first_position),
maximum_absolute(second_position)});
const Vector3 first_scaled = first_position / global_coordinate_scale;
const Vector3 second_scaled = second_position / global_coordinate_scale;
const Vector3 delta_scaled = second_scaled - first_scaled;
const double length_ratio = delta_scaled.Norm();
const double coordinate_norm_ratio =
std::max({1.0 / global_coordinate_scale, norm(first_scaled),
norm(second_scaled)});
std::max({1.0 / global_coordinate_scale, first_scaled.Norm(),
second_scaled.Norm()});
if (!(length_ratio > 1.0e-12 * coordinate_norm_ratio)) {
return ModelFailure(
"invalid-beam-length", raw.location, "ELEMENT", raw.label_text,
"Beam length fails the approved scale-aware threshold.");
}
std::array<double, 3> tangent{delta_scaled[0] / length_ratio,
delta_scaled[1] / length_ratio,
delta_scaled[2] / length_ratio};
const Vector3 tangent = delta_scaled / length_ratio;
const double global_guide_scale =
std::max(1.0, maximum_absolute(section.first_axis));
std::array<double, 3> guide_scaled{};
for (std::size_t coordinate = 0U; coordinate < 3U; ++coordinate) {
guide_scaled[coordinate] =
section.first_axis[coordinate] / global_guide_scale;
}
const double projection = guide_scaled[0] * tangent[0] +
guide_scaled[1] * tangent[1] +
guide_scaled[2] * tangent[2];
std::array<double, 3> perpendicular{
guide_scaled[0] - projection * tangent[0],
guide_scaled[1] - projection * tangent[1],
guide_scaled[2] - projection * tangent[2]};
const Vector3 guide{section.first_axis};
const double global_guide_scale = std::max(1.0, maximum_absolute(guide));
const Vector3 guide_scaled = guide / global_guide_scale;
const double projection = guide_scaled.Dot(tangent);
const Vector3 perpendicular = guide_scaled - projection * tangent;
const double guide_norm_ratio =
std::max(1.0 / global_guide_scale, norm(guide_scaled));
if (!(norm(perpendicular) > 1.0e-12 * guide_norm_ratio)) {
std::max(1.0 / global_guide_scale, guide_scaled.Norm());
if (!(perpendicular.Norm() > 1.0e-12 * guide_norm_ratio)) {
return ModelFailure(
"invalid-beam-guide-vector", section.location, "BEAM GENERAL SECTION",
raw.label_text,
+29 -43
View File
@@ -20,6 +20,7 @@
#include "fesa/analysis/analysis_model.h"
#include "fesa/build_info.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/vector3.h"
namespace fesa {
namespace {
@@ -163,11 +164,6 @@ Status OutputFailure(const std::string& code, const std::string& message) {
{{Severity::kError, code, {}, "", "", message}});
}
bool IsFinite(const std::array<double, 3>& values) {
return std::all_of(values.begin(), values.end(),
[](const double value) { return std::isfinite(value); });
}
template <std::size_t Size>
bool IsFinite(const std::array<double, Size>& values) {
return std::all_of(values.begin(), values.end(),
@@ -244,26 +240,21 @@ const char* ShellSourceTypeName(const ShellSourceElementType type) {
bool IsOrthonormalRightHanded(
const std::array<std::array<double, 3>, 3>& frame) {
constexpr double kTolerance = 1.0e-12;
const auto dot = [](const std::array<double, 3>& left,
const std::array<double, 3>& right) {
return left[0U] * right[0U] + left[1U] * right[1U] + left[2U] * right[2U];
};
for (const auto& axis : frame) {
if (!IsFinite(axis) || std::abs(dot(axis, axis) - 1.0) > kTolerance) {
const std::array<Vector3, 3> axes = {Vector3{frame[0U]}, Vector3{frame[1U]},
Vector3{frame[2U]}};
for (const Vector3& axis : axes) {
if (!axis.IsFinite() || std::abs(axis.Dot(axis) - 1.0) > kTolerance) {
return false;
}
}
if (std::abs(dot(frame[0U], frame[1U])) > kTolerance ||
std::abs(dot(frame[0U], frame[2U])) > kTolerance ||
std::abs(dot(frame[1U], frame[2U])) > kTolerance) {
if (std::abs(axes[0U].Dot(axes[1U])) > kTolerance ||
std::abs(axes[0U].Dot(axes[2U])) > kTolerance ||
std::abs(axes[1U].Dot(axes[2U])) > kTolerance) {
return false;
}
const std::array<double, 3> cross = {
frame[0U][1U] * frame[1U][2U] - frame[0U][2U] * frame[1U][1U],
frame[0U][2U] * frame[1U][0U] - frame[0U][0U] * frame[1U][2U],
frame[0U][0U] * frame[1U][1U] - frame[0U][1U] * frame[1U][0U]};
return dot(cross, frame[2U]) > 0.0 &&
std::abs(dot(cross, frame[2U]) - 1.0) <= kTolerance;
const Vector3 cross = axes[0U].Cross(axes[1U]);
const double handedness = cross.Dot(axes[2U]);
return handedness > 0.0 && std::abs(handedness - 1.0) <= kTolerance;
}
bool ComputeLocalAxes(const Domain& domain,
@@ -275,30 +266,25 @@ bool ComputeLocalAxes(const Domain& domain,
}
const auto& first = domain.Nodes()[element.node_indices[0U]].coordinates;
const auto& second = domain.Nodes()[element.node_indices[1U]].coordinates;
const auto& guide = domain.Sections()[element.section_index].first_axis;
const std::array<double, 3> delta = {
second[0U] - first[0U], second[1U] - first[1U], second[2U] - first[2U]};
const double length = std::hypot(delta[0U], delta[1U], delta[2U]);
if (!IsFinite(first) || !IsFinite(second) || !IsFinite(guide) ||
!IsFinite(delta) || !std::isfinite(length) || !(length > 0.0)) {
const Vector3 first_position{first};
const Vector3 second_position{second};
const Vector3 guide{domain.Sections()[element.section_index].first_axis};
const Vector3 delta = second_position - first_position;
const double length = delta.Norm();
if (!first_position.IsFinite() || !second_position.IsFinite() ||
!guide.IsFinite() || !delta.IsFinite() || !std::isfinite(length) ||
!(length > 0.0)) {
return false;
}
const std::array<double, 3> x = {delta[0U] / length, delta[1U] / length,
delta[2U] / length};
const double projection =
guide[0U] * x[0U] + guide[1U] * x[1U] + guide[2U] * x[2U];
const std::array<double, 3> y_trial = {guide[0U] - projection * x[0U],
guide[1U] - projection * x[1U],
guide[2U] - projection * x[2U]};
const double y_norm = std::hypot(y_trial[0U], y_trial[1U], y_trial[2U]);
if (!IsFinite(y_trial) || !std::isfinite(y_norm) || !(y_norm > 0.0)) {
const Vector3 x = delta / length;
const double projection = guide.Dot(x);
const Vector3 y_trial = guide - projection * x;
const double y_norm = y_trial.Norm();
if (!y_trial.IsFinite() || !std::isfinite(y_norm) || !(y_norm > 0.0)) {
return false;
}
const std::array<double, 3> y = {y_trial[0U] / y_norm, y_trial[1U] / y_norm,
y_trial[2U] / y_norm};
const std::array<double, 3> z = {x[1U] * y[2U] - x[2U] * y[1U],
x[2U] * y[0U] - x[0U] * y[2U],
x[0U] * y[1U] - x[1U] * y[0U]};
const Vector3 y = y_trial / y_norm;
const Vector3 z = x.Cross(y);
axes = {x[0U], x[1U], x[2U], y[0U], y[1U], y[2U], z[0U], z[1U], z[2U]};
return IsFinite(axes);
}
@@ -417,7 +403,7 @@ Status ValidateShellWriterInput(const Domain& domain,
++position) {
if (row.stress[position].position != positions[position] ||
row.stress[position].zeta != kZeta[position] ||
!IsFinite(row.stress[position].components)) {
!Vector3{row.stress[position].components}.IsFinite()) {
return OutputFailure("invalid-result-rows",
"Shell stress rows must preserve BOTTOM, "
"MIDDLE, TOP identity.");
@@ -426,7 +412,7 @@ Status ValidateShellWriterInput(const Domain& domain,
}
if (!std::isfinite(state.PhysicalStrainEnergy()) ||
!IsFinite(state.Equilibrium()) ||
!IsFinite(state.VerificationMetrics())) {
!Vector3{state.VerificationMetrics()}.IsFinite()) {
return OutputFailure(
"invalid-result-rows",
"Shell energy, equilibrium, and verification metrics must be finite.");
@@ -493,7 +479,7 @@ Status ValidateWriterInput(const std::filesystem::path& output_path,
node.source_id.source_label_text.empty() ||
!IsValidUtf8(node.source_id.instance_name) ||
!IsValidUtf8(node.source_id.source_label_text) ||
!IsFinite(node.coordinates)) {
!Vector3{node.coordinates}.IsFinite()) {
return OutputFailure(
"invalid-result-identity",
"Every node requires finite coordinates and UTF-8 source identity.");