feat(result-contract-completion): step 0 — beam-element-end-recovery

This commit is contained in:
KOKO\Mimi
2026-08-02 01:14:40 +09:00
parent 5618636f0d
commit 5fe57cb145
7 changed files with 341 additions and 11 deletions
+115 -10
View File
@@ -4,8 +4,10 @@
#include <cmath>
#include <cstddef>
#include <optional>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <fesa/fem/gauss_rule.hpp>
#include <fesa/fem/line2_shape.hpp>
@@ -32,6 +34,20 @@ bool is_positive_finite(const double value) {
return std::isfinite(value) && value > 0.0;
}
std::array<double, 6> constitutive_values(
const Beam3D2Input& input) {
const double shear_modulus =
input.material.young / (2.0 * (1.0 + input.material.poisson));
return {
input.material.young * input.section.area,
shear_modulus * input.section.shear_area_y,
shear_modulus * input.section.shear_area_z,
shear_modulus * input.section.torsion_j,
input.material.young * input.section.iy,
input.material.young * input.section.iz,
};
}
std::optional<BeamKernelResult> validate_properties(
const Beam3D2Input& input) {
if (!std::isfinite(input.material.young) ||
@@ -149,6 +165,34 @@ Matrix12 transform_stiffness(
return global;
}
std::array<double, 12> transform_displacement(
const Matrix12& transformation,
const std::span<const double, 12> global_displacement) {
std::array<double, 12> local_displacement{};
for (std::size_t row = 0; row < transformation.size(); ++row) {
for (std::size_t column = 0;
column < transformation[row].size();
++column) {
local_displacement[row] +=
transformation[row][column] * global_displacement[column];
}
}
return local_displacement;
}
std::array<double, 6> evaluate_strain(
const StrainMatrix& strain_matrix,
const std::array<double, 12>& local_displacement) {
std::array<double, 6> strain{};
for (std::size_t component = 0; component < strain.size(); ++component) {
for (std::size_t dof = 0; dof < local_displacement.size(); ++dof) {
strain[component] +=
strain_matrix[component][dof] * local_displacement[dof];
}
}
return strain;
}
bool is_finite(const Matrix12& matrix) {
for (const auto& row : matrix) {
for (const double value : row) {
@@ -189,16 +233,8 @@ BeamKernelResult compute_beam3d2(const Beam3D2Input& input) {
"Beam kernel requires a representable positive Jacobian.");
}
const double shear_modulus =
input.material.young / (2.0 * (1.0 + input.material.poisson));
const std::array<double, 6> constitutive{
input.material.young * input.section.area,
shear_modulus * input.section.shear_area_y,
shear_modulus * input.section.shear_area_z,
shear_modulus * input.section.torsion_j,
input.material.young * input.section.iy,
input.material.young * input.section.iz,
};
const std::array<double, 6> constitutive =
constitutive_values(input);
for (const double value : constitutive) {
if (!is_positive_finite(value)) {
return error_result(
@@ -241,4 +277,73 @@ BeamKernelResult compute_beam3d2(const Beam3D2Input& input) {
};
}
std::vector<BeamSectionResult> recover_beam3d2(
const Beam3D2Input& input,
const std::span<const double, 12> element_displacement,
const std::span<const std::array<double, 2>> recovery_points) {
const BeamKernelResult kernel = compute_beam3d2(input);
if (!kernel.contribution.has_value()) {
const std::string message = kernel.diagnostics.empty()
? "Beam recovery requires a valid Beam3D2 input."
: kernel.diagnostics.front().message;
throw std::invalid_argument{message};
}
const Vec3 axis{
input.coordinates[1].x - input.coordinates[0].x,
input.coordinates[1].y - input.coordinates[0].y,
input.coordinates[1].z - input.coordinates[0].z,
};
const double jacobian = std::hypot(axis.x, axis.y, axis.z) / 2.0;
const Matrix12 transformation =
beam_transformation(kernel.contribution->frame);
const std::array<double, 12> local_displacement =
transform_displacement(transformation, element_displacement);
const std::array<double, 6> center_strain = evaluate_strain(
strain_matrix(0.0, jacobian),
local_displacement);
const std::array<double, 6> constitutive =
constitutive_values(input);
std::vector<BeamSectionResult> results;
results.reserve(input.node_ids.size());
for (std::size_t end = 0; end < input.node_ids.size(); ++end) {
const double xi = end == 0 ? -1.0 : 1.0;
std::array<double, 6> section_strain = evaluate_strain(
strain_matrix(xi, jacobian),
local_displacement);
section_strain[1] = center_strain[1];
section_strain[2] = center_strain[2];
std::array<double, 6> section_force{};
for (std::size_t component = 0;
component < section_force.size();
++component) {
section_force[component] =
constitutive[component] * section_strain[component];
}
std::vector<double> sigma_xx;
sigma_xx.reserve(recovery_points.size());
for (const auto& point : recovery_points) {
const double y = point[0];
const double z = point[1];
sigma_xx.push_back(
input.material.young *
(section_strain[0] + z * section_strain[4] -
y * section_strain[5]));
}
results.push_back({
xi,
input.node_ids[end],
section_strain,
section_force,
section_force[0] / input.section.area,
std::move(sigma_xx),
});
}
return results;
}
} // namespace fesa