feat(cpp-object-oriented-modular-refactoring): step 8 - element-geometry-vector3
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/math/vector3.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
@@ -720,6 +722,40 @@ TEST(EulerBeam3D, RotatedTransformPreservesWorkAndEnergy) {
|
||||
(local_displacement[6U] - local_displacement[0U]) / 3.0, 1.0e-14);
|
||||
}
|
||||
|
||||
TEST(EulerBeam3D, PreservesExactRotatedResultsAcrossVector3Migration) {
|
||||
const auto section = MakeSection({-2.0, 2.0, 0.0});
|
||||
const auto material = MakeMaterial();
|
||||
const Node first_node = MakeNode({1.0, -2.0, 0.5}, 1U);
|
||||
const Node second_node = MakeNode({3.0, 0.0, 1.5}, 2U);
|
||||
const Vector3 delta =
|
||||
Vector3(second_node.coordinates) - Vector3(first_node.coordinates);
|
||||
EXPECT_DOUBLE_EQ(delta.Norm(), 3.0);
|
||||
|
||||
const auto beam = RequireBeam(first_node, second_node, section, material);
|
||||
const Matrix global = beam.GlobalStiffness();
|
||||
Vector displacement{kElementDofCount};
|
||||
for (std::size_t index = 0U; index < displacement.Size(); ++index) {
|
||||
displacement[index] = 0.01 * static_cast<double>(index + 1U) - 0.04;
|
||||
}
|
||||
const BeamRecovery recovery = beam.Recover(displacement);
|
||||
|
||||
EXPECT_DOUBLE_EQ(global(0U, 0U), 0x1.65f135da12f68p+28);
|
||||
EXPECT_DOUBLE_EQ(global(0U, 1U), 0x1.6261c084bda12p+28);
|
||||
EXPECT_DOUBLE_EQ(global(0U, 2U), 0x1.630ca684bda13p+27);
|
||||
EXPECT_DOUBLE_EQ(global(4U, 4U), 0x1.068e359b59b58p+22);
|
||||
EXPECT_DOUBLE_EQ(global(5U, 11U), 0x1.2d14a7ee7ee7bp+22);
|
||||
EXPECT_DOUBLE_EQ(recovery.gauss_generalized_strains[0U][0U],
|
||||
0x1.1111111111110p-5);
|
||||
EXPECT_DOUBLE_EQ(recovery.gauss_generalized_strains[0U][1U],
|
||||
0x1.1111111111111p-5);
|
||||
EXPECT_DOUBLE_EQ(recovery.gauss_generalized_strains[0U][2U],
|
||||
-0x1.382425a7d2473p-6);
|
||||
EXPECT_DOUBLE_EQ(recovery.gauss_generalized_strains[0U][3U],
|
||||
-0x1.a9389137b051dp-6);
|
||||
EXPECT_DOUBLE_EQ(recovery.endpoint_section_resultants[1U][2U],
|
||||
0x1.525c94a87359fp+17);
|
||||
}
|
||||
|
||||
TEST(EulerBeam3D, ConstantLineLoadMatchesAllSignedComponents) {
|
||||
const double length = 4.0;
|
||||
const ConstantLocalLineLoad load{2.5, -3.0, 5.5, -7.0};
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/math/vector3.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using Vector3 = std::array<double, 3>;
|
||||
@@ -588,6 +590,64 @@ TEST(Mitc4ShellKernel,
|
||||
EXPECT_DOUBLE_EQ(repeated.drilling_stiffness, stiffness.drilling_stiffness);
|
||||
}
|
||||
|
||||
TEST(Mitc4ShellKernel,
|
||||
PreservesExactStiffnessRecoveryAndPatchAcrossVector3Migration) {
|
||||
const auto nodes = PlanarNodes();
|
||||
const auto shell_candidate = fesa::Mitc4Shell::Create(
|
||||
NodePointers(nodes), Directors(), Section(), Material());
|
||||
ASSERT_TRUE(shell_candidate.HasValue());
|
||||
const auto& shell = shell_candidate.Value();
|
||||
const auto stiffness_candidate = shell.Stiffness();
|
||||
ASSERT_TRUE(stiffness_candidate.HasValue());
|
||||
const auto& stiffness = stiffness_candidate.Value();
|
||||
const fesa::Vector3 frame_e3{shell.LocalFrame(0.0, 0.0).e3};
|
||||
EXPECT_DOUBLE_EQ(frame_e3.X(), 0.0);
|
||||
EXPECT_DOUBLE_EQ(frame_e3.Y(), 0.0);
|
||||
EXPECT_DOUBLE_EQ(frame_e3.Z(), 1.0);
|
||||
|
||||
constexpr std::array<double, 8> kGeneralized{0.1, -0.05, 0.2, 0.3,
|
||||
-0.15, 0.25, 0.4, -0.3};
|
||||
fesa::Vector global_field{24U};
|
||||
for (std::size_t node_index = 0U; node_index < nodes.size(); ++node_index) {
|
||||
const double x = nodes[node_index].coordinates[0];
|
||||
const double y = nodes[node_index].coordinates[1];
|
||||
const std::size_t offset = 6U * node_index;
|
||||
global_field[offset] = kGeneralized[0] * x + 0.5 * kGeneralized[2] * y;
|
||||
global_field[offset + 1U] = kGeneralized[1] * y + 0.5 * kGeneralized[2] * x;
|
||||
global_field[offset + 2U] = kGeneralized[6] * x + kGeneralized[7] * y -
|
||||
0.5 * kGeneralized[5] * x * y;
|
||||
global_field[offset + 3U] =
|
||||
-kGeneralized[4] * y - 0.5 * kGeneralized[5] * x;
|
||||
global_field[offset + 4U] = kGeneralized[3] * x + 0.5 * kGeneralized[5] * y;
|
||||
}
|
||||
const auto recovery_candidate = shell.RecoverPhysical(global_field);
|
||||
ASSERT_TRUE(recovery_candidate.HasValue());
|
||||
const auto& recovery = recovery_candidate.Value();
|
||||
|
||||
std::array<std::array<double, 5>, 4> e11_values{};
|
||||
for (std::size_t node_index = 0U; node_index < nodes.size(); ++node_index) {
|
||||
e11_values[node_index][0U] = 0.2 * nodes[node_index].coordinates[0U];
|
||||
}
|
||||
const double gauss = 1.0 / std::sqrt(3.0);
|
||||
const fesa::Vector patch = shell.StrainDisplacement20(gauss, -gauss, gauss)
|
||||
.Multiply(PhysicalField(e11_values));
|
||||
|
||||
EXPECT_DOUBLE_EQ(stiffness.physical_local20(0U, 0U), 0x1.d555555555554p+6);
|
||||
EXPECT_DOUBLE_EQ(stiffness.physical_local20(0U, 4U), 0.0);
|
||||
EXPECT_DOUBLE_EQ(stiffness.physical_global24(2U, 2U), 0x1.aaaaaaaaaaaadp+5);
|
||||
EXPECT_DOUBLE_EQ(stiffness.drilling_stiffness, 0x1.0d6cffc5beeb5p-4);
|
||||
EXPECT_DOUBLE_EQ(recovery.strain_energy, 0x1.20a3d70a3d70bp+6);
|
||||
constexpr std::array<double, 8> kExpectedStrain{
|
||||
0x1.999999999999ap-4, -0x1.999999999999bp-5, 0x1.9999999999998p-3,
|
||||
0x1.3333333333335p-2, -0x1.3333333333335p-3, 0x1.0000000000000p-2,
|
||||
0x1.999999999999cp-2, -0x1.3333333333334p-2};
|
||||
EXPECT_EQ(recovery.points[0U].generalized_strain, kExpectedStrain);
|
||||
EXPECT_DOUBLE_EQ(patch[0U], 0x1.9999999999999p-3);
|
||||
for (std::size_t component = 1U; component < patch.Size(); ++component) {
|
||||
EXPECT_DOUBLE_EQ(patch[component], 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// MITC4-KERNEL-002
|
||||
TEST(Mitc4ShellKernel,
|
||||
PreservesPhysicalEnergyUnderTwentyToTwentyFourCongruence) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
@@ -19,6 +21,7 @@ TEST(Vector3, StoresComponentsWithExactArithmetic) {
|
||||
constexpr Vector3 sum = lhs + rhs;
|
||||
constexpr Vector3 difference = lhs - rhs;
|
||||
constexpr Vector3 scaled = lhs * -2.0;
|
||||
constexpr Vector3 divided = lhs / -2.0;
|
||||
|
||||
EXPECT_DOUBLE_EQ(lhs.X(), 1.0);
|
||||
EXPECT_DOUBLE_EQ(lhs.Y(), -2.0);
|
||||
@@ -35,6 +38,22 @@ TEST(Vector3, StoresComponentsWithExactArithmetic) {
|
||||
EXPECT_DOUBLE_EQ(scaled[0], -2.0);
|
||||
EXPECT_DOUBLE_EQ(scaled[1], 4.0);
|
||||
EXPECT_DOUBLE_EQ(scaled[2], -6.0);
|
||||
EXPECT_DOUBLE_EQ(divided[0], -0.5);
|
||||
EXPECT_DOUBLE_EQ(divided[1], 1.0);
|
||||
EXPECT_DOUBLE_EQ(divided[2], -1.5);
|
||||
}
|
||||
|
||||
TEST(Vector3, ConvertsArrayAndPreservesScalarLeftEvaluationOrder) {
|
||||
constexpr std::array<double, 3> kComponents{1.25, -2.5, 5.0};
|
||||
constexpr Vector3 value{kComponents};
|
||||
constexpr Vector3 scaled = -2.0 * value;
|
||||
|
||||
EXPECT_TRUE(value == Vector3(1.25, -2.5, 5.0));
|
||||
EXPECT_FALSE(value == Vector3(1.25, -2.5, 4.0));
|
||||
EXPECT_EQ(value.Components(), kComponents);
|
||||
EXPECT_DOUBLE_EQ(scaled.X(), -2.5);
|
||||
EXPECT_DOUBLE_EQ(scaled.Y(), 5.0);
|
||||
EXPECT_DOUBLE_EQ(scaled.Z(), -10.0);
|
||||
}
|
||||
|
||||
TEST(Vector3, ComputesDotProduct) {
|
||||
@@ -61,6 +80,9 @@ TEST(Vector3, UsesRightHandedCrossProductOrientation) {
|
||||
|
||||
TEST(Vector3, ComputesEuclideanNorm) {
|
||||
EXPECT_DOUBLE_EQ(Vector3(2.0, -3.0, 6.0).Norm(), 7.0);
|
||||
|
||||
const double expected = std::hypot(1.0e308, 1.0e308, 0.0);
|
||||
EXPECT_DOUBLE_EQ(Vector3(1.0e308, 1.0e308, 0.0).Norm(), expected);
|
||||
}
|
||||
|
||||
TEST(Vector3, NormalizesNonzeroVectors) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/math/vector3.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using Vector3 = std::array<double, 3>;
|
||||
@@ -134,6 +136,34 @@ TEST(Mitc4Geometry,
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Mitc4Geometry, PreservesExactWarpedResultsAcrossVector3Migration) {
|
||||
const std::vector<fesa::Node> warped_nodes{
|
||||
Node(0U, {0.0, 0.0, 0.0}), Node(1U, {2.0, 0.0, 0.0}),
|
||||
Node(2U, {2.0, 1.0, 0.2}), Node(3U, {0.0, 1.0, 0.0})};
|
||||
const auto warped = fesa::PreprocessShellGeometry(
|
||||
warped_nodes, {Element(12U, {0U, 1U, 2U, 3U})}, Sections());
|
||||
ASSERT_TRUE(warped.HasValue());
|
||||
const auto& element = warped.Value().element_data[0U];
|
||||
const auto& frame = FrameFor(warped.Value(), 0U);
|
||||
const fesa::Vector3 normal{element.normal_candidate};
|
||||
const fesa::Vector3 director{frame.director};
|
||||
const fesa::Vector3 tangent_a{frame.tangent_a};
|
||||
const fesa::Vector3 tangent_b{frame.tangent_b};
|
||||
EXPECT_DOUBLE_EQ(normal.X(), -0x1.97105218e28c2p-5);
|
||||
EXPECT_DOUBLE_EQ(normal.Y(), -0x1.97105218e28c2p-4);
|
||||
EXPECT_DOUBLE_EQ(normal.Z(), 0x1.fcd4669f1b2f2p-1);
|
||||
EXPECT_DOUBLE_EQ(element.surface_area_weight, 0x1.021ebe8f40622p+1);
|
||||
EXPECT_DOUBLE_EQ(director.X(), -0x1.97105218e28c2p-5);
|
||||
EXPECT_DOUBLE_EQ(director.Y(), -0x1.97105218e28c2p-4);
|
||||
EXPECT_DOUBLE_EQ(director.Z(), 0x1.fcd4669f1b2f2p-1);
|
||||
EXPECT_DOUBLE_EQ(tangent_a.X(), 0x1.ff5e152c2d2abp-1);
|
||||
EXPECT_DOUBLE_EQ(tangent_a.Y(), -0x1.4408ec773d925p-8);
|
||||
EXPECT_DOUBLE_EQ(tangent_a.Z(), 0x1.950b27950cf6dp-5);
|
||||
EXPECT_DOUBLE_EQ(tangent_b.X(), 0.0);
|
||||
EXPECT_DOUBLE_EQ(tangent_b.Y(), 0x1.fd7583bc82e28p-1);
|
||||
EXPECT_DOUBLE_EQ(tangent_b.Z(), 0x1.9791363068b53p-4);
|
||||
}
|
||||
|
||||
// MITC4-GEO-002
|
||||
TEST(Mitc4Geometry, AreaWeightsSharedDirectorsInStableSourceIdentityOrder) {
|
||||
const std::vector<fesa::Node> nodes{
|
||||
|
||||
Reference in New Issue
Block a user