feat(cpp-object-oriented-modular-refactoring): step 4 - model-element-google-style
This commit is contained in:
+238
-224
@@ -1,4 +1,4 @@
|
||||
#include "fesa/model/domain.hpp"
|
||||
#include "fesa/model/domain.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -10,254 +10,268 @@
|
||||
|
||||
namespace {
|
||||
|
||||
fesa::ModelDefinition makeOwnedDefinition() {
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.sourcePath = "models/owned.inp";
|
||||
definition.sourceContentIdentity = "fnv1a64:fedcba9876543210";
|
||||
definition.heading = "Stable declaration order";
|
||||
definition.nodes = {
|
||||
{{"Instance-A", 20, "0020"}, {2.0, 0.0, 0.0}, {"models/owned.inp", 12U}},
|
||||
{{"Instance-A", 10, "0010"}, {1.0, 0.0, 0.0}, {"models/owned.inp", 11U}}};
|
||||
definition.elements = {{
|
||||
{"Instance-A", 5, "0005"},
|
||||
{fesa::EntityIndex{1}, fesa::EntityIndex{0}},
|
||||
fesa::EntityIndex{0},
|
||||
fesa::EntityIndex{0},
|
||||
{"models/owned.inp", 20U}}};
|
||||
definition.materials = {{
|
||||
"Steel", 210.0e9, 0.3, {"models/owned.inp", 30U}}};
|
||||
definition.sections = {{
|
||||
"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{{-0.1, 0.0}, {0.1, 0.0}},
|
||||
{"models/owned.inp", 40U}}};
|
||||
definition.nodeSets = {{
|
||||
"Tip", std::string{"Instance-A"}, {0U}, {"models/owned.inp", 45U}}};
|
||||
definition.elementSets = {{
|
||||
"Beam", std::string{"Instance-A"}, {0U}, {"models/owned.inp", 46U}}};
|
||||
definition.parts = {{
|
||||
"BeamPart", {20, 10}, {5}, {"Tip"}, {"Beam"},
|
||||
{"models/owned.inp", 2U}}};
|
||||
definition.instances = {{
|
||||
"Instance-A",
|
||||
"BeamPart",
|
||||
{{20, 0U}, {10, 1U}},
|
||||
{{5, 0U}},
|
||||
{"models/owned.inp", 50U}}};
|
||||
definition.steps = {{
|
||||
"Step-1",
|
||||
{{"Root", 1, 6, 0.0, {"models/owned.inp", 60U}}},
|
||||
{{"Tip", 3, -1000.0, {"models/owned.inp", 61U}}},
|
||||
0.1,
|
||||
1.0,
|
||||
1.0e-5,
|
||||
1.0,
|
||||
{"models/owned.inp", 55U}}};
|
||||
definition.warnings = {{
|
||||
fesa::Severity::kWarning,
|
||||
"ignored-output-request",
|
||||
{"models/owned.inp", 70U},
|
||||
"*OUTPUT",
|
||||
"",
|
||||
"Ignored output request."}};
|
||||
return definition;
|
||||
fesa::ModelDefinition MakeOwnedDefinition() {
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = "models/owned.inp";
|
||||
definition.source_content_identity = "fnv1a64:fedcba9876543210";
|
||||
definition.heading = "Stable declaration order";
|
||||
definition.nodes = {
|
||||
{{"Instance-A", 20, "0020"}, {2.0, 0.0, 0.0}, {"models/owned.inp", 12U}},
|
||||
{{"Instance-A", 10, "0010"}, {1.0, 0.0, 0.0}, {"models/owned.inp", 11U}}};
|
||||
definition.elements = {{{"Instance-A", 5, "0005"},
|
||||
{fesa::EntityIndex{1}, fesa::EntityIndex{0}},
|
||||
fesa::EntityIndex{0},
|
||||
fesa::EntityIndex{0},
|
||||
{"models/owned.inp", 20U}}};
|
||||
definition.materials = {{"Steel", 210.0e9, 0.3, {"models/owned.inp", 30U}}};
|
||||
definition.sections = {{"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{{-0.1, 0.0}, {0.1, 0.0}},
|
||||
{"models/owned.inp", 40U}}};
|
||||
definition.node_sets = {
|
||||
{"Tip", std::string{"Instance-A"}, {0U}, {"models/owned.inp", 45U}}};
|
||||
definition.element_sets = {
|
||||
{"Beam", std::string{"Instance-A"}, {0U}, {"models/owned.inp", 46U}}};
|
||||
definition.parts = {
|
||||
{"BeamPart", {20, 10}, {5}, {"Tip"}, {"Beam"}, {"models/owned.inp", 2U}}};
|
||||
definition.instances = {{"Instance-A",
|
||||
"BeamPart",
|
||||
{{20, 0U}, {10, 1U}},
|
||||
{{5, 0U}},
|
||||
{"models/owned.inp", 50U}}};
|
||||
definition.steps = {{"Step-1",
|
||||
{{"Root", 1, 6, 0.0, {"models/owned.inp", 60U}}},
|
||||
{{"Tip", 3, -1000.0, {"models/owned.inp", 61U}}},
|
||||
0.1,
|
||||
1.0,
|
||||
1.0e-5,
|
||||
1.0,
|
||||
{"models/owned.inp", 55U}}};
|
||||
definition.warnings = {{fesa::Severity::kWarning,
|
||||
"ignored-output-request",
|
||||
{"models/owned.inp", 70U},
|
||||
"*OUTPUT",
|
||||
"",
|
||||
"Ignored output request."}};
|
||||
return definition;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
||||
auto definition = makeOwnedDefinition();
|
||||
auto result = fesa::Domain::create(definition);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
auto definition = MakeOwnedDefinition();
|
||||
auto result = fesa::Domain::Create(definition);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
|
||||
definition.sourcePath = "mutated.inp";
|
||||
definition.sourceContentIdentity = "mutated";
|
||||
definition.nodes[0].sourceId.source_label_text = "mutated";
|
||||
definition.nodes[0].coordinates[0] = -99.0;
|
||||
definition.sections[0].sectionPoints[0][0] = -99.0;
|
||||
definition.steps[0].loads[0].magnitude = 99.0;
|
||||
definition.warnings[0].code = "mutated";
|
||||
definition.source_path = "mutated.inp";
|
||||
definition.source_content_identity = "mutated";
|
||||
definition.nodes[0].source_id.source_label_text = "mutated";
|
||||
definition.nodes[0].coordinates[0] = -99.0;
|
||||
definition.sections[0].section_points[0][0] = -99.0;
|
||||
definition.steps[0].loads[0].magnitude = 99.0;
|
||||
definition.warnings[0].code = "mutated";
|
||||
|
||||
const fesa::Domain& domain = result.Value();
|
||||
const fesa::Node* const firstNodeAddress = domain.nodes().data();
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().nodes()),
|
||||
const std::vector<fesa::Node>&>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().elements()),
|
||||
const std::vector<fesa::EulerBeam3DDefinition>&>);
|
||||
const fesa::Domain& domain = result.Value();
|
||||
const fesa::Node* const first_node_address = domain.Nodes().data();
|
||||
static_assert(
|
||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>().Nodes()),
|
||||
const std::vector<fesa::Node>&>);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>().Elements()),
|
||||
const std::vector<fesa::EulerBeam3DDefinition>&>);
|
||||
|
||||
EXPECT_EQ(domain.sourcePath(), std::filesystem::path{"models/owned.inp"});
|
||||
EXPECT_EQ(domain.sourceContentIdentity(), "fnv1a64:fedcba9876543210");
|
||||
ASSERT_EQ(domain.nodes().size(), 2U);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 20);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.source_label_text, "0020");
|
||||
EXPECT_DOUBLE_EQ(domain.nodes()[0].coordinates[0], 2.0);
|
||||
EXPECT_EQ(domain.nodes()[1].sourceId.source_label, 10);
|
||||
EXPECT_EQ(domain.nodes().data(), firstNodeAddress);
|
||||
EXPECT_EQ(domain.SourcePath(), std::filesystem::path{"models/owned.inp"});
|
||||
EXPECT_EQ(domain.SourceContentIdentity(), "fnv1a64:fedcba9876543210");
|
||||
ASSERT_EQ(domain.Nodes().size(), 2U);
|
||||
EXPECT_EQ(domain.Nodes()[0].source_id.source_label, 20);
|
||||
EXPECT_EQ(domain.Nodes()[0].source_id.source_label_text, "0020");
|
||||
EXPECT_DOUBLE_EQ(domain.Nodes()[0].coordinates[0], 2.0);
|
||||
EXPECT_EQ(domain.Nodes()[1].source_id.source_label, 10);
|
||||
EXPECT_EQ(domain.Nodes().data(), first_node_address);
|
||||
|
||||
ASSERT_EQ(domain.elements().size(), 1U);
|
||||
EXPECT_EQ(domain.elements()[0].nodeIndices[0], 1U);
|
||||
EXPECT_EQ(domain.elements()[0].nodeIndices[1], 0U);
|
||||
ASSERT_EQ(domain.materials().size(), 1U);
|
||||
ASSERT_EQ(domain.sections().size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(domain.sections()[0].sectionPoints[0][0], -0.1);
|
||||
ASSERT_EQ(domain.nodeSets().size(), 1U);
|
||||
EXPECT_EQ(domain.nodeSets()[0].nodeIndices[0], 0U);
|
||||
ASSERT_EQ(domain.elementSets().size(), 1U);
|
||||
EXPECT_EQ(domain.elementSets()[0].elementIndices[0], 0U);
|
||||
ASSERT_EQ(domain.Elements().size(), 1U);
|
||||
EXPECT_EQ(domain.Elements()[0].node_indices[0], 1U);
|
||||
EXPECT_EQ(domain.Elements()[0].node_indices[1], 0U);
|
||||
ASSERT_EQ(domain.Materials().size(), 1U);
|
||||
ASSERT_EQ(domain.Sections().size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(domain.Sections()[0].section_points[0][0], -0.1);
|
||||
ASSERT_EQ(domain.NodeSets().size(), 1U);
|
||||
EXPECT_EQ(domain.NodeSets()[0].node_indices[0], 0U);
|
||||
ASSERT_EQ(domain.ElementSets().size(), 1U);
|
||||
EXPECT_EQ(domain.ElementSets()[0].element_indices[0], 0U);
|
||||
|
||||
ASSERT_EQ(domain.steps().size(), 1U);
|
||||
EXPECT_EQ(domain.steps()[0].name, "Step-1");
|
||||
EXPECT_DOUBLE_EQ(domain.steps()[0].initialIncrement, 0.1);
|
||||
EXPECT_DOUBLE_EQ(domain.steps()[0].timePeriod, 1.0);
|
||||
EXPECT_DOUBLE_EQ(domain.steps()[0].minimumIncrement, 1.0e-5);
|
||||
EXPECT_DOUBLE_EQ(domain.steps()[0].maximumIncrement, 1.0);
|
||||
ASSERT_EQ(domain.steps()[0].boundaries.size(), 1U);
|
||||
ASSERT_EQ(domain.steps()[0].loads.size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(domain.steps()[0].loads[0].magnitude, -1000.0);
|
||||
ASSERT_EQ(domain.Steps().size(), 1U);
|
||||
EXPECT_EQ(domain.Steps()[0].name, "Step-1");
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].initial_increment, 0.1);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].time_period, 1.0);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].minimum_increment, 1.0e-5);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].maximum_increment, 1.0);
|
||||
ASSERT_EQ(domain.Steps()[0].boundaries.size(), 1U);
|
||||
ASSERT_EQ(domain.Steps()[0].loads.size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(domain.Steps()[0].loads[0].magnitude, -1000.0);
|
||||
|
||||
ASSERT_EQ(domain.warnings().size(), 1U);
|
||||
EXPECT_EQ(domain.warnings()[0].code, "ignored-output-request");
|
||||
ASSERT_EQ(domain.Warnings().size(), 1U);
|
||||
EXPECT_EQ(domain.Warnings()[0].code, "ignored-output-request");
|
||||
}
|
||||
|
||||
TEST(DomainModel, MultipleIdentityInstancesDoNotMerge) {
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.sourcePath = "models/two-instances.inp";
|
||||
definition.sourceContentIdentity = "fnv1a64:0011223344556677";
|
||||
definition.nodes = {
|
||||
{{"Instance-A", 1, "1"}, {0.0, 0.0, 0.0}, {"models/two-instances.inp", 10U}},
|
||||
{{"Instance-A", 2, "2"}, {1.0, 0.0, 0.0}, {"models/two-instances.inp", 11U}},
|
||||
{{"Instance-B", 1, "1"}, {0.0, 0.0, 0.0}, {"models/two-instances.inp", 10U}},
|
||||
{{"Instance-B", 2, "2"}, {1.0, 0.0, 0.0}, {"models/two-instances.inp", 11U}}};
|
||||
definition.elements = {
|
||||
{{"Instance-A", 1, "1"}, {0U, 1U}, 0U, 0U, {"models/two-instances.inp", 20U}},
|
||||
{{"Instance-B", 1, "1"}, {2U, 3U}, 0U, 0U, {"models/two-instances.inp", 20U}}};
|
||||
definition.materials = {{
|
||||
"Steel", 210.0e9, 0.3, {"models/two-instances.inp", 30U}}};
|
||||
definition.sections = {{
|
||||
"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{},
|
||||
{"models/two-instances.inp", 40U}}};
|
||||
definition.parts = {{
|
||||
"BeamPart", {1, 2}, {1}, {}, {}, {"models/two-instances.inp", 2U}}};
|
||||
definition.instances = {
|
||||
{"Instance-A", "BeamPart", {{1, 0U}, {2, 1U}}, {{1, 0U}},
|
||||
{"models/two-instances.inp", 50U}},
|
||||
{"Instance-B", "BeamPart", {{1, 2U}, {2, 3U}}, {{1, 1U}},
|
||||
{"models/two-instances.inp", 51U}}};
|
||||
definition.steps = {{
|
||||
"Step-1", {}, {}, 0.1, 1.0, 1.0e-5, 1.0,
|
||||
{"models/two-instances.inp", 60U}}};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = "models/two-instances.inp";
|
||||
definition.source_content_identity = "fnv1a64:0011223344556677";
|
||||
definition.nodes = {{{"Instance-A", 1, "1"},
|
||||
{0.0, 0.0, 0.0},
|
||||
{"models/two-instances.inp", 10U}},
|
||||
{{"Instance-A", 2, "2"},
|
||||
{1.0, 0.0, 0.0},
|
||||
{"models/two-instances.inp", 11U}},
|
||||
{{"Instance-B", 1, "1"},
|
||||
{0.0, 0.0, 0.0},
|
||||
{"models/two-instances.inp", 10U}},
|
||||
{{"Instance-B", 2, "2"},
|
||||
{1.0, 0.0, 0.0},
|
||||
{"models/two-instances.inp", 11U}}};
|
||||
definition.elements = {{{"Instance-A", 1, "1"},
|
||||
{0U, 1U},
|
||||
0U,
|
||||
0U,
|
||||
{"models/two-instances.inp", 20U}},
|
||||
{{"Instance-B", 1, "1"},
|
||||
{2U, 3U},
|
||||
0U,
|
||||
0U,
|
||||
{"models/two-instances.inp", 20U}}};
|
||||
definition.materials = {
|
||||
{"Steel", 210.0e9, 0.3, {"models/two-instances.inp", 30U}}};
|
||||
definition.sections = {{"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{},
|
||||
{"models/two-instances.inp", 40U}}};
|
||||
definition.parts = {
|
||||
{"BeamPart", {1, 2}, {1}, {}, {}, {"models/two-instances.inp", 2U}}};
|
||||
definition.instances = {{"Instance-A",
|
||||
"BeamPart",
|
||||
{{1, 0U}, {2, 1U}},
|
||||
{{1, 0U}},
|
||||
{"models/two-instances.inp", 50U}},
|
||||
{"Instance-B",
|
||||
"BeamPart",
|
||||
{{1, 2U}, {2, 3U}},
|
||||
{{1, 1U}},
|
||||
{"models/two-instances.inp", 51U}}};
|
||||
definition.steps = {{"Step-1",
|
||||
{},
|
||||
{},
|
||||
0.1,
|
||||
1.0,
|
||||
1.0e-5,
|
||||
1.0,
|
||||
{"models/two-instances.inp", 60U}}};
|
||||
|
||||
auto result = fesa::Domain::create(std::move(definition));
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
const fesa::Domain& domain = result.Value();
|
||||
auto result = fesa::Domain::Create(std::move(definition));
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
const fesa::Domain& domain = result.Value();
|
||||
|
||||
ASSERT_EQ(domain.nodes().size(), 4U);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.source_label, 1);
|
||||
EXPECT_EQ(domain.nodes()[2].sourceId.source_label, 1);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.source_label_text, "1");
|
||||
EXPECT_EQ(domain.nodes()[2].sourceId.source_label_text, "1");
|
||||
EXPECT_NE(
|
||||
domain.nodes()[0].sourceId.instance_name,
|
||||
domain.nodes()[2].sourceId.instance_name);
|
||||
ASSERT_EQ(domain.Nodes().size(), 4U);
|
||||
EXPECT_EQ(domain.Nodes()[0].source_id.source_label, 1);
|
||||
EXPECT_EQ(domain.Nodes()[2].source_id.source_label, 1);
|
||||
EXPECT_EQ(domain.Nodes()[0].source_id.source_label_text, "1");
|
||||
EXPECT_EQ(domain.Nodes()[2].source_id.source_label_text, "1");
|
||||
EXPECT_NE(domain.Nodes()[0].source_id.instance_name,
|
||||
domain.Nodes()[2].source_id.instance_name);
|
||||
|
||||
ASSERT_EQ(domain.elements().size(), 2U);
|
||||
EXPECT_EQ(domain.elements()[0].sourceId.source_label, 1);
|
||||
EXPECT_EQ(domain.elements()[1].sourceId.source_label, 1);
|
||||
EXPECT_EQ(domain.elements()[0].sourceId.instance_name, "Instance-A");
|
||||
EXPECT_EQ(domain.elements()[1].sourceId.instance_name, "Instance-B");
|
||||
EXPECT_EQ(domain.elements()[0].nodeIndices[0], 0U);
|
||||
EXPECT_EQ(domain.elements()[1].nodeIndices[0], 2U);
|
||||
ASSERT_EQ(domain.Elements().size(), 2U);
|
||||
EXPECT_EQ(domain.Elements()[0].source_id.source_label, 1);
|
||||
EXPECT_EQ(domain.Elements()[1].source_id.source_label, 1);
|
||||
EXPECT_EQ(domain.Elements()[0].source_id.instance_name, "Instance-A");
|
||||
EXPECT_EQ(domain.Elements()[1].source_id.instance_name, "Instance-B");
|
||||
EXPECT_EQ(domain.Elements()[0].node_indices[0], 0U);
|
||||
EXPECT_EQ(domain.Elements()[1].node_indices[0], 2U);
|
||||
}
|
||||
|
||||
// MITC4-MODEL-002
|
||||
TEST(DomainModel, ShellOwnershipPreservesResolvedAssignmentsAndOptionalFrames) {
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.sourcePath = "models/shell-owned.inp";
|
||||
definition.sourceContentIdentity = "fnv1a64:1234567890abcdef";
|
||||
definition.materials = {
|
||||
{"Material-B", 70.0e9, 0.33, {"models/shell-owned.inp", 31U}},
|
||||
{"Material-A", 210.0e9, 0.3, {"models/shell-owned.inp", 30U}}};
|
||||
definition.shellSections = {
|
||||
{"Section-B", 0.02, 0U, {"models/shell-owned.inp", 41U}},
|
||||
{"Section-A", 0.01, 1U, {"models/shell-owned.inp", 40U}}};
|
||||
definition.shellElements = {
|
||||
{{"Shell-Instance", 20, "0020"},
|
||||
fesa::ShellSourceElementType::s4r,
|
||||
{4U, 5U, 6U, 7U},
|
||||
0U,
|
||||
0U,
|
||||
{"models/shell-owned.inp", 21U}},
|
||||
{{"Shell-Instance", 10, "0010"},
|
||||
fesa::ShellSourceElementType::s4,
|
||||
{0U, 1U, 2U, 3U},
|
||||
1U,
|
||||
1U,
|
||||
{"models/shell-owned.inp", 20U}}};
|
||||
definition.shellNodeInitialFrames = {
|
||||
{4U, {0.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}},
|
||||
{0U, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}}};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = "models/shell-owned.inp";
|
||||
definition.source_content_identity = "fnv1a64:1234567890abcdef";
|
||||
definition.materials = {
|
||||
{"Material-B", 70.0e9, 0.33, {"models/shell-owned.inp", 31U}},
|
||||
{"Material-A", 210.0e9, 0.3, {"models/shell-owned.inp", 30U}}};
|
||||
definition.shell_sections = {
|
||||
{"Section-B", 0.02, 0U, {"models/shell-owned.inp", 41U}},
|
||||
{"Section-A", 0.01, 1U, {"models/shell-owned.inp", 40U}}};
|
||||
definition.shell_elements = {{{"Shell-Instance", 20, "0020"},
|
||||
fesa::ShellSourceElementType::kS4r,
|
||||
{4U, 5U, 6U, 7U},
|
||||
0U,
|
||||
0U,
|
||||
{"models/shell-owned.inp", 21U}},
|
||||
{{"Shell-Instance", 10, "0010"},
|
||||
fesa::ShellSourceElementType::kS4,
|
||||
{0U, 1U, 2U, 3U},
|
||||
1U,
|
||||
1U,
|
||||
{"models/shell-owned.inp", 20U}}};
|
||||
definition.shell_node_initial_frames = {
|
||||
{4U, {0.0, 0.0, 1.0}, {1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}},
|
||||
{0U, {0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {0.0, 0.0, -1.0}}};
|
||||
|
||||
auto result = fesa::Domain::create(definition);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
auto result = fesa::Domain::Create(definition);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
|
||||
definition.shellSections[0].thickness = -1.0;
|
||||
definition.shellElements[0].sourceId.source_label_text = "mutated";
|
||||
definition.shellNodeInitialFrames[0].director[2] = -1.0;
|
||||
definition.shell_sections[0].thickness = -1.0;
|
||||
definition.shell_elements[0].source_id.source_label_text = "mutated";
|
||||
definition.shell_node_initial_frames[0].director[2] = -1.0;
|
||||
|
||||
const fesa::Domain& domain = result.Value();
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().shellElements()),
|
||||
const std::vector<fesa::Mitc4ShellDefinition>&>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().shellSections()),
|
||||
const std::vector<fesa::ShellSection>&>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().shellNodeInitialFrames()),
|
||||
const std::vector<fesa::ShellNodeInitialFrame>&>);
|
||||
const fesa::Domain& domain = result.Value();
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().ShellElements()),
|
||||
const std::vector<fesa::Mitc4ShellDefinition>&>);
|
||||
static_assert(std::is_same_v<
|
||||
decltype(std::declval<const fesa::Domain&>().ShellSections()),
|
||||
const std::vector<fesa::ShellSection>&>);
|
||||
static_assert(
|
||||
std::is_same_v<decltype(std::declval<const fesa::Domain&>()
|
||||
.ShellNodeInitialFrames()),
|
||||
const std::vector<fesa::ShellNodeInitialFrame>&>);
|
||||
|
||||
ASSERT_EQ(domain.shellElements().size(), 2U);
|
||||
EXPECT_EQ(domain.shellElements()[0].sourceId.source_label, 20);
|
||||
EXPECT_EQ(domain.shellElements()[0].sourceId.source_label_text, "0020");
|
||||
EXPECT_EQ(domain.shellElements()[1].sourceId.source_label, 10);
|
||||
EXPECT_EQ(domain.shellElements()[0].sourceType, fesa::ShellSourceElementType::s4r);
|
||||
EXPECT_EQ(domain.shellElements()[1].sourceType, fesa::ShellSourceElementType::s4);
|
||||
EXPECT_EQ(domain.shellElements()[0].nodeIndices[3], 7U);
|
||||
EXPECT_EQ(domain.shellElements()[0].materialIndex, 0U);
|
||||
EXPECT_EQ(domain.shellElements()[0].sectionIndex, 0U);
|
||||
ASSERT_EQ(domain.ShellElements().size(), 2U);
|
||||
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label, 20);
|
||||
EXPECT_EQ(domain.ShellElements()[0].source_id.source_label_text, "0020");
|
||||
EXPECT_EQ(domain.ShellElements()[1].source_id.source_label, 10);
|
||||
EXPECT_EQ(domain.ShellElements()[0].source_type,
|
||||
fesa::ShellSourceElementType::kS4r);
|
||||
EXPECT_EQ(domain.ShellElements()[1].source_type,
|
||||
fesa::ShellSourceElementType::kS4);
|
||||
EXPECT_EQ(domain.ShellElements()[0].node_indices[3], 7U);
|
||||
EXPECT_EQ(domain.ShellElements()[0].material_index, 0U);
|
||||
EXPECT_EQ(domain.ShellElements()[0].section_index, 0U);
|
||||
|
||||
ASSERT_EQ(domain.shellSections().size(), 2U);
|
||||
EXPECT_EQ(domain.shellSections()[0].name, "Section-B");
|
||||
EXPECT_DOUBLE_EQ(domain.shellSections()[0].thickness, 0.02);
|
||||
EXPECT_EQ(domain.shellSections()[0].materialIndex, 0U);
|
||||
EXPECT_EQ(domain.shellSections()[1].name, "Section-A");
|
||||
ASSERT_EQ(domain.ShellSections().size(), 2U);
|
||||
EXPECT_EQ(domain.ShellSections()[0].name, "Section-B");
|
||||
EXPECT_DOUBLE_EQ(domain.ShellSections()[0].thickness, 0.02);
|
||||
EXPECT_EQ(domain.ShellSections()[0].material_index, 0U);
|
||||
EXPECT_EQ(domain.ShellSections()[1].name, "Section-A");
|
||||
|
||||
ASSERT_EQ(domain.shellNodeInitialFrames().size(), 2U);
|
||||
EXPECT_EQ(domain.shellNodeInitialFrames()[0].nodeIndex, 4U);
|
||||
EXPECT_EQ(
|
||||
domain.shellNodeInitialFrames()[0].director,
|
||||
(std::array<double, 3>{0.0, 0.0, 1.0}));
|
||||
EXPECT_EQ(
|
||||
domain.shellNodeInitialFrames()[0].tangentA,
|
||||
(std::array<double, 3>{1.0, 0.0, 0.0}));
|
||||
EXPECT_EQ(
|
||||
domain.shellNodeInitialFrames()[0].tangentB,
|
||||
(std::array<double, 3>{0.0, 1.0, 0.0}));
|
||||
ASSERT_EQ(domain.ShellNodeInitialFrames().size(), 2U);
|
||||
EXPECT_EQ(domain.ShellNodeInitialFrames()[0].node_index, 4U);
|
||||
EXPECT_EQ(domain.ShellNodeInitialFrames()[0].director,
|
||||
(std::array<double, 3>{0.0, 0.0, 1.0}));
|
||||
EXPECT_EQ(domain.ShellNodeInitialFrames()[0].tangent_a,
|
||||
(std::array<double, 3>{1.0, 0.0, 0.0}));
|
||||
EXPECT_EQ(domain.ShellNodeInitialFrames()[0].tangent_b,
|
||||
(std::array<double, 3>{0.0, 1.0, 0.0}));
|
||||
|
||||
auto noFramesResult = fesa::Domain::create(fesa::ModelDefinition{});
|
||||
ASSERT_TRUE(noFramesResult.HasValue());
|
||||
EXPECT_TRUE(noFramesResult.Value().shellNodeInitialFrames().empty());
|
||||
auto no_frames_result = fesa::Domain::Create(fesa::ModelDefinition{});
|
||||
ASSERT_TRUE(no_frames_result.HasValue());
|
||||
EXPECT_TRUE(no_frames_result.Value().ShellNodeInitialFrames().empty());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "fesa/model/model_types.hpp"
|
||||
#include "fesa/model/model_types.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -14,174 +14,150 @@
|
||||
|
||||
namespace {
|
||||
|
||||
template<class T, class = void>
|
||||
template <class T, class = void>
|
||||
struct HasEquationId : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
template <class T>
|
||||
struct HasEquationId<T, std::void_t<decltype(std::declval<T&>().equationId)>>
|
||||
: std::true_type {};
|
||||
|
||||
template<class T, class = void>
|
||||
template <class T, class = void>
|
||||
struct HasEquationIds : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
template <class T>
|
||||
struct HasEquationIds<T, std::void_t<decltype(std::declval<T&>().equationIds)>>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) {
|
||||
const fesa::SourceLocation nodeLocation{
|
||||
std::filesystem::path{"models/beam.inp"}, 11U};
|
||||
const fesa::SourceEntityId firstIdentity{"Instance-A", 7, "0007"};
|
||||
const fesa::SourceEntityId equalIdentity{"Instance-A", 7, "0007"};
|
||||
const fesa::SourceEntityId laterIdentity{"Instance-B", 7, "0007"};
|
||||
const fesa::SourceLocation node_location{
|
||||
std::filesystem::path{"models/beam.inp"}, 11U};
|
||||
const fesa::SourceEntityId first_identity{"Instance-A", 7, "0007"};
|
||||
const fesa::SourceEntityId equal_identity{"Instance-A", 7, "0007"};
|
||||
const fesa::SourceEntityId later_identity{"Instance-B", 7, "0007"};
|
||||
|
||||
EXPECT_EQ(
|
||||
std::make_tuple(
|
||||
firstIdentity.instance_name,
|
||||
firstIdentity.source_label,
|
||||
firstIdentity.source_label_text),
|
||||
std::make_tuple(
|
||||
equalIdentity.instance_name,
|
||||
equalIdentity.source_label,
|
||||
equalIdentity.source_label_text));
|
||||
EXPECT_LT(
|
||||
std::make_tuple(
|
||||
firstIdentity.instance_name,
|
||||
firstIdentity.source_label,
|
||||
firstIdentity.source_label_text),
|
||||
std::make_tuple(
|
||||
laterIdentity.instance_name,
|
||||
laterIdentity.source_label,
|
||||
laterIdentity.source_label_text));
|
||||
EXPECT_EQ(
|
||||
std::make_tuple(first_identity.instance_name, first_identity.source_label,
|
||||
first_identity.source_label_text),
|
||||
std::make_tuple(equal_identity.instance_name, equal_identity.source_label,
|
||||
equal_identity.source_label_text));
|
||||
EXPECT_LT(
|
||||
std::make_tuple(first_identity.instance_name, first_identity.source_label,
|
||||
first_identity.source_label_text),
|
||||
std::make_tuple(later_identity.instance_name, later_identity.source_label,
|
||||
later_identity.source_label_text));
|
||||
|
||||
const fesa::Node node{firstIdentity, {1.0, 2.0, 3.0}, nodeLocation};
|
||||
const fesa::LinearElasticMaterial material{
|
||||
"Steel", 210.0e9, 0.3, {"models/beam.inp", 30U}};
|
||||
const fesa::GeneralBeamSection section{
|
||||
"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{{-0.1, 0.0}, {0.1, 0.0}},
|
||||
{"models/beam.inp", 40U}};
|
||||
const fesa::EulerBeam3DDefinition element{
|
||||
{"Instance-A", 3, "0003"},
|
||||
{fesa::EntityIndex{5}, fesa::EntityIndex{9}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{4},
|
||||
{"models/beam.inp", 20U}};
|
||||
const fesa::BoundaryCondition boundary{
|
||||
"Root", 1, 6, 0.0, {"models/beam.inp", 60U}};
|
||||
const fesa::NodalLoad load{
|
||||
"Tip", 3, -1000.0, {"models/beam.inp", 70U}};
|
||||
const fesa::StaticStepDefinition step{
|
||||
"Step-1",
|
||||
{boundary},
|
||||
{load},
|
||||
0.1,
|
||||
1.0,
|
||||
1.0e-5,
|
||||
1.0,
|
||||
{"models/beam.inp", 50U}};
|
||||
const fesa::NodeSet nodeSet{
|
||||
"Root", std::string{"Instance-A"}, {5U}, {"models/beam.inp", 25U}};
|
||||
const fesa::ElementSet elementSet{
|
||||
"Beam", std::string{"Instance-A"}, {4U}, {"models/beam.inp", 26U}};
|
||||
const fesa::PartDefinition part{
|
||||
"BeamPart", {7, 8}, {3}, {"Root", "Tip"}, {"Beam"},
|
||||
{"models/beam.inp", 2U}};
|
||||
const fesa::InstanceDefinition instance{
|
||||
"Instance-A",
|
||||
"BeamPart",
|
||||
{{7, 5U}, {8, 9U}},
|
||||
{{3, 4U}},
|
||||
{"models/beam.inp", 80U}};
|
||||
const fesa::Node node{first_identity, {1.0, 2.0, 3.0}, node_location};
|
||||
const fesa::LinearElasticMaterial material{
|
||||
"Steel", 210.0e9, 0.3, {"models/beam.inp", 30U}};
|
||||
const fesa::GeneralBeamSection section{"Section-1",
|
||||
0.02,
|
||||
1.0e-5,
|
||||
0.0,
|
||||
2.0e-5,
|
||||
5.0e-6,
|
||||
{0.0, 1.0, 0.0},
|
||||
{{-0.1, 0.0}, {0.1, 0.0}},
|
||||
{"models/beam.inp", 40U}};
|
||||
const fesa::EulerBeam3DDefinition element{
|
||||
{"Instance-A", 3, "0003"},
|
||||
{fesa::EntityIndex{5}, fesa::EntityIndex{9}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{4},
|
||||
{"models/beam.inp", 20U}};
|
||||
const fesa::BoundaryCondition boundary{
|
||||
"Root", 1, 6, 0.0, {"models/beam.inp", 60U}};
|
||||
const fesa::NodalLoad load{"Tip", 3, -1000.0, {"models/beam.inp", 70U}};
|
||||
const fesa::StaticStepDefinition step{
|
||||
"Step-1", {boundary}, {load}, 0.1,
|
||||
1.0, 1.0e-5, 1.0, {"models/beam.inp", 50U}};
|
||||
const fesa::NodeSet node_set{
|
||||
"Root", std::string{"Instance-A"}, {5U}, {"models/beam.inp", 25U}};
|
||||
const fesa::ElementSet element_set{
|
||||
"Beam", std::string{"Instance-A"}, {4U}, {"models/beam.inp", 26U}};
|
||||
const fesa::PartDefinition part{"BeamPart", {7, 8},
|
||||
{3}, {"Root", "Tip"},
|
||||
{"Beam"}, {"models/beam.inp", 2U}};
|
||||
const fesa::InstanceDefinition instance{"Instance-A",
|
||||
"BeamPart",
|
||||
{{7, 5U}, {8, 9U}},
|
||||
{{3, 4U}},
|
||||
{"models/beam.inp", 80U}};
|
||||
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.sourcePath = "models/beam.inp";
|
||||
definition.sourceContentIdentity = "fnv1a64:0123456789abcdef";
|
||||
definition.heading = "Identity mapping fixture";
|
||||
definition.nodes = {node};
|
||||
definition.elements = {element};
|
||||
definition.materials = {material};
|
||||
definition.sections = {section};
|
||||
definition.nodeSets = {nodeSet};
|
||||
definition.elementSets = {elementSet};
|
||||
definition.parts = {part};
|
||||
definition.instances = {instance};
|
||||
definition.steps = {step};
|
||||
definition.warnings = {{
|
||||
fesa::Severity::kWarning,
|
||||
"ignored-output-request",
|
||||
{"models/beam.inp", 75U},
|
||||
"*OUTPUT",
|
||||
"",
|
||||
"Output request does not alter mandatory FESA results."}};
|
||||
fesa::ModelDefinition definition{};
|
||||
definition.source_path = "models/beam.inp";
|
||||
definition.source_content_identity = "fnv1a64:0123456789abcdef";
|
||||
definition.heading = "Identity mapping fixture";
|
||||
definition.nodes = {node};
|
||||
definition.elements = {element};
|
||||
definition.materials = {material};
|
||||
definition.sections = {section};
|
||||
definition.node_sets = {node_set};
|
||||
definition.element_sets = {element_set};
|
||||
definition.parts = {part};
|
||||
definition.instances = {instance};
|
||||
definition.steps = {step};
|
||||
definition.warnings = {
|
||||
{fesa::Severity::kWarning,
|
||||
"ignored-output-request",
|
||||
{"models/beam.inp", 75U},
|
||||
"*OUTPUT",
|
||||
"",
|
||||
"Output request does not alter mandatory FESA results."}};
|
||||
|
||||
ASSERT_EQ(definition.nodes.size(), 1U);
|
||||
EXPECT_EQ(definition.nodes[0].sourceId.source_label_text, "0007");
|
||||
EXPECT_EQ(definition.nodes[0].location.line, 11U);
|
||||
ASSERT_EQ(definition.instances.size(), 1U);
|
||||
EXPECT_EQ(definition.instances[0].partName, "BeamPart");
|
||||
EXPECT_EQ(definition.instances[0].nodeMappings[0].sourceLabel, 7);
|
||||
EXPECT_EQ(definition.instances[0].nodeMappings[0].internalIndex, 5U);
|
||||
EXPECT_EQ(definition.instances[0].elementMappings[0].internalIndex, 4U);
|
||||
EXPECT_EQ(definition.steps[0].initialIncrement, 0.1);
|
||||
EXPECT_EQ(definition.steps[0].timePeriod, 1.0);
|
||||
EXPECT_EQ(definition.steps[0].minimumIncrement, 1.0e-5);
|
||||
EXPECT_EQ(definition.steps[0].maximumIncrement, 1.0);
|
||||
ASSERT_EQ(definition.warnings.size(), 1U);
|
||||
EXPECT_EQ(definition.warnings[0].location.line, 75U);
|
||||
ASSERT_EQ(definition.nodes.size(), 1U);
|
||||
EXPECT_EQ(definition.nodes[0].source_id.source_label_text, "0007");
|
||||
EXPECT_EQ(definition.nodes[0].location.line, 11U);
|
||||
ASSERT_EQ(definition.instances.size(), 1U);
|
||||
EXPECT_EQ(definition.instances[0].part_name, "BeamPart");
|
||||
EXPECT_EQ(definition.instances[0].node_mappings[0].source_label, 7);
|
||||
EXPECT_EQ(definition.instances[0].node_mappings[0].internal_index, 5U);
|
||||
EXPECT_EQ(definition.instances[0].element_mappings[0].internal_index, 4U);
|
||||
EXPECT_EQ(definition.steps[0].initial_increment, 0.1);
|
||||
EXPECT_EQ(definition.steps[0].time_period, 1.0);
|
||||
EXPECT_EQ(definition.steps[0].minimum_increment, 1.0e-5);
|
||||
EXPECT_EQ(definition.steps[0].maximum_increment, 1.0);
|
||||
ASSERT_EQ(definition.warnings.size(), 1U);
|
||||
EXPECT_EQ(definition.warnings[0].location.line, 75U);
|
||||
|
||||
static_assert(!HasEquationId<fesa::Node>::value);
|
||||
static_assert(!HasEquationIds<fesa::Node>::value);
|
||||
static_assert(!HasEquationId<fesa::EulerBeam3DDefinition>::value);
|
||||
static_assert(!HasEquationIds<fesa::EulerBeam3DDefinition>::value);
|
||||
static_assert(!HasEquationId<fesa::Node>::value);
|
||||
static_assert(!HasEquationIds<fesa::Node>::value);
|
||||
static_assert(!HasEquationId<fesa::EulerBeam3DDefinition>::value);
|
||||
static_assert(!HasEquationIds<fesa::EulerBeam3DDefinition>::value);
|
||||
}
|
||||
|
||||
// MITC4-MODEL-001
|
||||
TEST(DomainModel, Mitc4ShellRecordsPreserveSourceAndInternalIdentity) {
|
||||
const fesa::Mitc4ShellDefinition s4Element{
|
||||
{"Shell-Instance", 41, "0041"},
|
||||
fesa::ShellSourceElementType::s4,
|
||||
{fesa::EntityIndex{3},
|
||||
fesa::EntityIndex{5},
|
||||
fesa::EntityIndex{8},
|
||||
fesa::EntityIndex{13}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{7},
|
||||
{"models/shell.inp", 20U}};
|
||||
const fesa::Mitc4ShellDefinition s4rElement{
|
||||
{"Shell-Instance", 42, "0042"},
|
||||
fesa::ShellSourceElementType::s4r,
|
||||
{fesa::EntityIndex{13},
|
||||
fesa::EntityIndex{8},
|
||||
fesa::EntityIndex{5},
|
||||
fesa::EntityIndex{3}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{7},
|
||||
{"models/shell.inp", 21U}};
|
||||
const fesa::Mitc4ShellDefinition s4_element{
|
||||
{"Shell-Instance", 41, "0041"},
|
||||
fesa::ShellSourceElementType::kS4,
|
||||
{fesa::EntityIndex{3}, fesa::EntityIndex{5}, fesa::EntityIndex{8},
|
||||
fesa::EntityIndex{13}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{7},
|
||||
{"models/shell.inp", 20U}};
|
||||
const fesa::Mitc4ShellDefinition s4r_element{
|
||||
{"Shell-Instance", 42, "0042"},
|
||||
fesa::ShellSourceElementType::kS4r,
|
||||
{fesa::EntityIndex{13}, fesa::EntityIndex{8}, fesa::EntityIndex{5},
|
||||
fesa::EntityIndex{3}},
|
||||
fesa::EntityIndex{2},
|
||||
fesa::EntityIndex{7},
|
||||
{"models/shell.inp", 21U}};
|
||||
|
||||
EXPECT_EQ(s4Element.sourceId.instance_name, "Shell-Instance");
|
||||
EXPECT_EQ(s4Element.sourceId.source_label, 41);
|
||||
EXPECT_EQ(s4Element.sourceId.source_label_text, "0041");
|
||||
EXPECT_EQ(s4Element.sourceType, fesa::ShellSourceElementType::s4);
|
||||
EXPECT_EQ(s4rElement.sourceType, fesa::ShellSourceElementType::s4r);
|
||||
EXPECT_NE(s4Element.sourceType, s4rElement.sourceType);
|
||||
EXPECT_EQ(
|
||||
fesa::kMitc4InternalFormulation,
|
||||
std::string_view{"FESA-MITC4"});
|
||||
EXPECT_EQ(
|
||||
s4Element.nodeIndices,
|
||||
(std::array<fesa::EntityIndex, 4>{3U, 5U, 8U, 13U}));
|
||||
EXPECT_EQ(s4Element.materialIndex, 2U);
|
||||
EXPECT_EQ(s4Element.sectionIndex, 7U);
|
||||
EXPECT_EQ(s4_element.source_id.instance_name, "Shell-Instance");
|
||||
EXPECT_EQ(s4_element.source_id.source_label, 41);
|
||||
EXPECT_EQ(s4_element.source_id.source_label_text, "0041");
|
||||
EXPECT_EQ(s4_element.source_type, fesa::ShellSourceElementType::kS4);
|
||||
EXPECT_EQ(s4r_element.source_type, fesa::ShellSourceElementType::kS4r);
|
||||
EXPECT_NE(s4_element.source_type, s4r_element.source_type);
|
||||
EXPECT_EQ(fesa::kMitc4InternalFormulation, std::string_view{"FESA-MITC4"});
|
||||
EXPECT_EQ(s4_element.node_indices,
|
||||
(std::array<fesa::EntityIndex, 4>{3U, 5U, 8U, 13U}));
|
||||
EXPECT_EQ(s4_element.material_index, 2U);
|
||||
EXPECT_EQ(s4_element.section_index, 7U);
|
||||
|
||||
static_assert(!HasEquationId<fesa::Mitc4ShellDefinition>::value);
|
||||
static_assert(!HasEquationIds<fesa::Mitc4ShellDefinition>::value);
|
||||
static_assert(!HasEquationId<fesa::Mitc4ShellDefinition>::value);
|
||||
static_assert(!HasEquationIds<fesa::Mitc4ShellDefinition>::value);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "fesa/model/shell_geometry.hpp"
|
||||
#include "fesa/model/shell_geometry.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -14,263 +14,242 @@ namespace {
|
||||
|
||||
using Vector3 = std::array<double, 3>;
|
||||
|
||||
fesa::Node node(fesa::EntityIndex label, Vector3 coordinates) {
|
||||
return {
|
||||
{"Shell-Instance", static_cast<std::int64_t>(label),
|
||||
std::to_string(label)},
|
||||
coordinates,
|
||||
{"shell-geometry.inp", static_cast<std::size_t>(label + 1U)}};
|
||||
fesa::Node Node(fesa::EntityIndex label, Vector3 coordinates) {
|
||||
return {{"Shell-Instance", static_cast<std::int64_t>(label),
|
||||
std::to_string(label)},
|
||||
coordinates,
|
||||
{"shell-geometry.inp", static_cast<std::size_t>(label + 1U)}};
|
||||
}
|
||||
|
||||
fesa::Mitc4ShellDefinition element(
|
||||
fesa::EntityIndex label,
|
||||
std::array<fesa::EntityIndex, 4> nodeIndices) {
|
||||
return {
|
||||
{"Shell-Instance", static_cast<std::int64_t>(label),
|
||||
std::to_string(label)},
|
||||
fesa::ShellSourceElementType::s4,
|
||||
nodeIndices,
|
||||
0U,
|
||||
0U,
|
||||
{"shell-geometry.inp", static_cast<std::size_t>(100U + label)}};
|
||||
fesa::Mitc4ShellDefinition Element(
|
||||
fesa::EntityIndex label, std::array<fesa::EntityIndex, 4> node_indices) {
|
||||
return {{"Shell-Instance", static_cast<std::int64_t>(label),
|
||||
std::to_string(label)},
|
||||
fesa::ShellSourceElementType::kS4,
|
||||
node_indices,
|
||||
0U,
|
||||
0U,
|
||||
{"shell-geometry.inp", static_cast<std::size_t>(100U + label)}};
|
||||
}
|
||||
|
||||
std::vector<fesa::ShellSection> sections(double thickness = 0.2) {
|
||||
return {{"Section", thickness, 0U, {"shell-geometry.inp", 90U}}};
|
||||
std::vector<fesa::ShellSection> Sections(double thickness = 0.2) {
|
||||
return {{"Section", thickness, 0U, {"shell-geometry.inp", 90U}}};
|
||||
}
|
||||
|
||||
double dot(const Vector3& left, const Vector3& right) {
|
||||
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
||||
double Dot(const Vector3& left, const Vector3& right) {
|
||||
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
||||
}
|
||||
|
||||
Vector3 cross(const Vector3& left, const Vector3& right) {
|
||||
return {
|
||||
left[1] * right[2] - left[2] * right[1],
|
||||
left[2] * right[0] - left[0] * right[2],
|
||||
left[0] * right[1] - left[1] * right[0]};
|
||||
Vector3 Cross(const Vector3& left, const Vector3& right) {
|
||||
return {left[1] * right[2] - left[2] * right[1],
|
||||
left[2] * right[0] - left[0] * right[2],
|
||||
left[0] * right[1] - left[1] * right[0]};
|
||||
}
|
||||
|
||||
double norm(const Vector3& value) {
|
||||
return std::sqrt(dot(value, value));
|
||||
double Norm(const Vector3& value) { return std::sqrt(Dot(value, value)); }
|
||||
|
||||
void ExpectVectorNear(const Vector3& actual, const Vector3& expected,
|
||||
double tolerance = 1.0e-12) {
|
||||
for (std::size_t component = 0U; component < actual.size(); ++component) {
|
||||
EXPECT_NEAR(actual[component], expected[component], tolerance);
|
||||
}
|
||||
}
|
||||
|
||||
void expectVectorNear(
|
||||
const Vector3& actual,
|
||||
const Vector3& expected,
|
||||
double tolerance = 1.0e-12) {
|
||||
for (std::size_t component = 0U; component < actual.size(); ++component) {
|
||||
EXPECT_NEAR(actual[component], expected[component], tolerance);
|
||||
}
|
||||
void ExpectRightHandedFrame(const fesa::ShellNodeInitialFrame& frame) {
|
||||
EXPECT_NEAR(Norm(frame.director), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(Norm(frame.tangent_a), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(Norm(frame.tangent_b), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(Dot(frame.director, frame.tangent_a), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(Dot(frame.director, frame.tangent_b), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(Dot(frame.tangent_a, frame.tangent_b), 0.0, 1.0e-12);
|
||||
ExpectVectorNear(Cross(frame.tangent_a, frame.tangent_b), frame.director);
|
||||
}
|
||||
|
||||
void expectRightHandedFrame(const fesa::ShellNodeInitialFrame& frame) {
|
||||
EXPECT_NEAR(norm(frame.director), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(norm(frame.tangentA), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(norm(frame.tangentB), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(dot(frame.director, frame.tangentA), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(dot(frame.director, frame.tangentB), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(dot(frame.tangentA, frame.tangentB), 0.0, 1.0e-12);
|
||||
expectVectorNear(cross(frame.tangentA, frame.tangentB), frame.director);
|
||||
const fesa::ShellNodeInitialFrame& FrameFor(const fesa::ShellGeometry& geometry,
|
||||
fesa::EntityIndex node_index) {
|
||||
const auto found =
|
||||
std::find_if(geometry.nodal_frames.begin(), geometry.nodal_frames.end(),
|
||||
[node_index](const fesa::ShellNodeInitialFrame& frame) {
|
||||
return frame.node_index == node_index;
|
||||
});
|
||||
EXPECT_NE(found, geometry.nodal_frames.end());
|
||||
return *found;
|
||||
}
|
||||
|
||||
const fesa::ShellNodeInitialFrame& frameFor(
|
||||
const fesa::ShellGeometry& geometry,
|
||||
fesa::EntityIndex nodeIndex) {
|
||||
const auto found = std::find_if(
|
||||
geometry.nodalFrames.begin(),
|
||||
geometry.nodalFrames.end(),
|
||||
[nodeIndex](const fesa::ShellNodeInitialFrame& frame) {
|
||||
return frame.nodeIndex == nodeIndex;
|
||||
});
|
||||
EXPECT_NE(found, geometry.nodalFrames.end());
|
||||
return *found;
|
||||
void ExpectFailureCode(const fesa::Result<fesa::ShellGeometry>& result,
|
||||
const std::string& code) {
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kModel);
|
||||
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0].code, code);
|
||||
}
|
||||
|
||||
void expectFailureCode(
|
||||
const fesa::Result<fesa::ShellGeometry>& result,
|
||||
const std::string& code) {
|
||||
ASSERT_FALSE(result.HasValue());
|
||||
EXPECT_EQ(result.GetStatus().Category(), fesa::FailureCategory::kModel);
|
||||
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U);
|
||||
EXPECT_EQ(result.GetStatus().Diagnostics()[0].code, code);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
// MITC4-GEO-001
|
||||
TEST(Mitc4Geometry, BuildsDeterministicFramesForPlanarRotatedAndWarpedElements) {
|
||||
const std::vector<fesa::Node> planarNodes{
|
||||
node(0U, {0.0, 0.0, 0.0}),
|
||||
node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, 1.0, 0.0}),
|
||||
node(3U, {0.0, 1.0, 0.0})};
|
||||
auto planar = fesa::preprocessShellGeometry(
|
||||
planarNodes, {element(10U, {0U, 1U, 2U, 3U})}, sections());
|
||||
TEST(Mitc4Geometry,
|
||||
BuildsDeterministicFramesForPlanarRotatedAndWarpedElements) {
|
||||
const std::vector<fesa::Node> planar_nodes{
|
||||
Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, 1.0, 0.0}), Node(3U, {0.0, 1.0, 0.0})};
|
||||
auto planar = fesa::PreprocessShellGeometry(
|
||||
planar_nodes, {Element(10U, {0U, 1U, 2U, 3U})}, Sections());
|
||||
|
||||
ASSERT_TRUE(planar.HasValue());
|
||||
ASSERT_EQ(planar.Value().elementData.size(), 1U);
|
||||
expectVectorNear(planar.Value().elementData[0].normalCandidate, {0.0, 0.0, 1.0});
|
||||
EXPECT_NEAR(planar.Value().elementData[0].surfaceAreaWeight, 1.0, 1.0e-12);
|
||||
ASSERT_EQ(planar.Value().nodalFrames.size(), 4U);
|
||||
for (const auto& frame : planar.Value().nodalFrames) {
|
||||
expectVectorNear(frame.director, {0.0, 0.0, 1.0});
|
||||
expectVectorNear(frame.tangentA, {1.0, 0.0, 0.0});
|
||||
expectVectorNear(frame.tangentB, {0.0, 1.0, 0.0});
|
||||
expectRightHandedFrame(frame);
|
||||
}
|
||||
ASSERT_TRUE(planar.HasValue());
|
||||
ASSERT_EQ(planar.Value().element_data.size(), 1U);
|
||||
ExpectVectorNear(planar.Value().element_data[0].normal_candidate,
|
||||
{0.0, 0.0, 1.0});
|
||||
EXPECT_NEAR(planar.Value().element_data[0].surface_area_weight, 1.0, 1.0e-12);
|
||||
ASSERT_EQ(planar.Value().nodal_frames.size(), 4U);
|
||||
for (const auto& frame : planar.Value().nodal_frames) {
|
||||
ExpectVectorNear(frame.director, {0.0, 0.0, 1.0});
|
||||
ExpectVectorNear(frame.tangent_a, {1.0, 0.0, 0.0});
|
||||
ExpectVectorNear(frame.tangent_b, {0.0, 1.0, 0.0});
|
||||
ExpectRightHandedFrame(frame);
|
||||
}
|
||||
|
||||
const std::vector<fesa::Node> rotatedNodes{
|
||||
node(0U, {0.0, 0.0, 0.0}),
|
||||
node(1U, {0.0, 1.0, 0.0}),
|
||||
node(2U, {0.0, 1.0, 1.0}),
|
||||
node(3U, {0.0, 0.0, 1.0})};
|
||||
auto rotated = fesa::preprocessShellGeometry(
|
||||
rotatedNodes, {element(11U, {0U, 1U, 2U, 3U})}, sections());
|
||||
const std::vector<fesa::Node> rotated_nodes{
|
||||
Node(0U, {0.0, 0.0, 0.0}), Node(1U, {0.0, 1.0, 0.0}),
|
||||
Node(2U, {0.0, 1.0, 1.0}), Node(3U, {0.0, 0.0, 1.0})};
|
||||
auto rotated = fesa::PreprocessShellGeometry(
|
||||
rotated_nodes, {Element(11U, {0U, 1U, 2U, 3U})}, Sections());
|
||||
|
||||
ASSERT_TRUE(rotated.HasValue());
|
||||
const auto& rotatedFrame = frameFor(rotated.Value(), 0U);
|
||||
expectVectorNear(rotatedFrame.director, {1.0, 0.0, 0.0});
|
||||
expectVectorNear(rotatedFrame.tangentA, {0.0, 1.0, 0.0});
|
||||
expectVectorNear(rotatedFrame.tangentB, {0.0, 0.0, 1.0});
|
||||
expectRightHandedFrame(rotatedFrame);
|
||||
ASSERT_TRUE(rotated.HasValue());
|
||||
const auto& rotated_frame = FrameFor(rotated.Value(), 0U);
|
||||
ExpectVectorNear(rotated_frame.director, {1.0, 0.0, 0.0});
|
||||
ExpectVectorNear(rotated_frame.tangent_a, {0.0, 1.0, 0.0});
|
||||
ExpectVectorNear(rotated_frame.tangent_b, {0.0, 0.0, 1.0});
|
||||
ExpectRightHandedFrame(rotated_frame);
|
||||
|
||||
const std::vector<fesa::Node> warpedNodes{
|
||||
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})};
|
||||
auto warped = fesa::preprocessShellGeometry(
|
||||
warpedNodes, {element(12U, {0U, 1U, 2U, 3U})}, sections());
|
||||
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})};
|
||||
auto warped = fesa::PreprocessShellGeometry(
|
||||
warped_nodes, {Element(12U, {0U, 1U, 2U, 3U})}, Sections());
|
||||
|
||||
ASSERT_TRUE(warped.HasValue());
|
||||
EXPECT_GT(warped.Value().elementData[0].surfaceAreaWeight, 2.0);
|
||||
for (const auto& frame : warped.Value().nodalFrames) {
|
||||
expectRightHandedFrame(frame);
|
||||
}
|
||||
ASSERT_TRUE(warped.HasValue());
|
||||
EXPECT_GT(warped.Value().element_data[0].surface_area_weight, 2.0);
|
||||
for (const auto& frame : warped.Value().nodal_frames) {
|
||||
ExpectRightHandedFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
// MITC4-GEO-002
|
||||
TEST(Mitc4Geometry, AreaWeightsSharedDirectorsInStableSourceIdentityOrder) {
|
||||
const std::vector<fesa::Node> nodes{
|
||||
node(0U, {0.0, 0.0, 0.0}),
|
||||
node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, 1.0, 0.0}),
|
||||
node(3U, {0.0, 1.0, 0.0}),
|
||||
node(4U, {2.0, 0.0, 1.0}),
|
||||
node(5U, {2.0, 1.0, 1.0})};
|
||||
const auto flat = element(10U, {0U, 1U, 2U, 3U});
|
||||
const auto tilted = element(20U, {1U, 4U, 5U, 2U});
|
||||
const std::vector<fesa::Node> nodes{
|
||||
Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, 1.0, 0.0}), Node(3U, {0.0, 1.0, 0.0}),
|
||||
Node(4U, {2.0, 0.0, 1.0}), Node(5U, {2.0, 1.0, 1.0})};
|
||||
const auto flat = Element(10U, {0U, 1U, 2U, 3U});
|
||||
const auto tilted = Element(20U, {1U, 4U, 5U, 2U});
|
||||
|
||||
auto first = fesa::preprocessShellGeometry(
|
||||
nodes, {tilted, flat}, sections());
|
||||
auto second = fesa::preprocessShellGeometry(
|
||||
nodes, {flat, tilted}, sections());
|
||||
auto first = fesa::PreprocessShellGeometry(nodes, {tilted, flat}, Sections());
|
||||
auto second =
|
||||
fesa::PreprocessShellGeometry(nodes, {flat, tilted}, Sections());
|
||||
|
||||
ASSERT_TRUE(first.HasValue());
|
||||
ASSERT_TRUE(second.HasValue());
|
||||
const Vector3 expectedSharedDirector{
|
||||
-1.0 / std::sqrt(5.0), 0.0, 2.0 / std::sqrt(5.0)};
|
||||
for (const auto sharedNode : {1U, 2U}) {
|
||||
const auto& firstFrame = frameFor(first.Value(), sharedNode);
|
||||
const auto& secondFrame = frameFor(second.Value(), sharedNode);
|
||||
expectVectorNear(firstFrame.director, expectedSharedDirector);
|
||||
expectVectorNear(firstFrame.director, secondFrame.director, 0.0);
|
||||
expectVectorNear(firstFrame.tangentA, {0.0, 1.0, 0.0});
|
||||
expectRightHandedFrame(firstFrame);
|
||||
}
|
||||
ASSERT_TRUE(first.HasValue());
|
||||
ASSERT_TRUE(second.HasValue());
|
||||
const Vector3 expected_shared_director{-1.0 / std::sqrt(5.0), 0.0,
|
||||
2.0 / std::sqrt(5.0)};
|
||||
for (const auto sharedNode : {1U, 2U}) {
|
||||
const auto& firstFrame = FrameFor(first.Value(), sharedNode);
|
||||
const auto& secondFrame = FrameFor(second.Value(), sharedNode);
|
||||
ExpectVectorNear(firstFrame.director, expected_shared_director);
|
||||
ExpectVectorNear(firstFrame.director, secondFrame.director, 0.0);
|
||||
ExpectVectorNear(firstFrame.tangent_a, {0.0, 1.0, 0.0});
|
||||
ExpectRightHandedFrame(firstFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// MITC4-GEO-003
|
||||
TEST(Mitc4Geometry, RejectsInvalidSurfaceJacobianAndIncidentOrientationCases) {
|
||||
const auto validElement = element(10U, {0U, 1U, 2U, 3U});
|
||||
const auto valid_element = Element(10U, {0U, 1U, 2U, 3U});
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}), node(1U, {0.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, 1.0, 0.0}), node(3U, {0.0, 1.0, 0.0})},
|
||||
{validElement}, sections()),
|
||||
"invalid-shell-geometry");
|
||||
ExpectFailureCode(fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {0.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, 1.0, 0.0}), Node(3U, {0.0, 1.0, 0.0})},
|
||||
{valid_element}, Sections()),
|
||||
"invalid-shell-geometry");
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}), node(1U, {1.0, 1.0, 0.0}),
|
||||
node(2U, {0.0, 1.0, 0.0}), node(3U, {1.0, 0.0, 0.0})},
|
||||
{validElement}, sections()),
|
||||
"invalid-shell-geometry");
|
||||
ExpectFailureCode(fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 1.0, 0.0}),
|
||||
Node(2U, {0.0, 1.0, 0.0}), Node(3U, {1.0, 0.0, 0.0})},
|
||||
{valid_element}, Sections()),
|
||||
"invalid-shell-geometry");
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}), node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {2.0, 0.0, 0.0}), node(3U, {3.0, 0.0, 0.0})},
|
||||
{validElement}, sections()),
|
||||
"invalid-shell-geometry");
|
||||
ExpectFailureCode(fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {2.0, 0.0, 0.0}), Node(3U, {3.0, 0.0, 0.0})},
|
||||
{valid_element}, Sections()),
|
||||
"invalid-shell-geometry");
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}), node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {0.05, 0.05, 0.0}), node(3U, {0.0, 1.0, 0.0})},
|
||||
{validElement}, sections()),
|
||||
"invalid-shell-geometry");
|
||||
ExpectFailureCode(
|
||||
fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {0.05, 0.05, 0.0}), Node(3U, {0.0, 1.0, 0.0})},
|
||||
{valid_element}, Sections()),
|
||||
"invalid-shell-geometry");
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}),
|
||||
node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, std::numeric_limits<double>::quiet_NaN(), 0.0}),
|
||||
node(3U, {0.0, 1.0, 0.0})},
|
||||
{validElement}, sections()),
|
||||
"invalid-shell-geometry");
|
||||
ExpectFailureCode(
|
||||
fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, std::numeric_limits<double>::quiet_NaN(), 0.0}),
|
||||
Node(3U, {0.0, 1.0, 0.0})},
|
||||
{valid_element}, Sections()),
|
||||
"invalid-shell-geometry");
|
||||
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
{node(0U, {0.0, 0.0, 0.0}), node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, 1.0, 0.0}), node(3U, {0.0, 1.0, 0.0})},
|
||||
{validElement}, sections(0.0)),
|
||||
"invalid-shell-jacobian");
|
||||
ExpectFailureCode(fesa::PreprocessShellGeometry(
|
||||
{Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, 1.0, 0.0}), Node(3U, {0.0, 1.0, 0.0})},
|
||||
{valid_element}, Sections(0.0)),
|
||||
"invalid-shell-jacobian");
|
||||
|
||||
const std::vector<fesa::Node> opposedNodes{
|
||||
node(0U, {0.0, 0.0, 0.0}), node(1U, {1.0, 0.0, 0.0}),
|
||||
node(2U, {1.0, 1.0, 0.0}), node(3U, {0.0, 1.0, 0.0})};
|
||||
expectFailureCode(
|
||||
fesa::preprocessShellGeometry(
|
||||
opposedNodes,
|
||||
{element(10U, {0U, 1U, 2U, 3U}),
|
||||
element(20U, {0U, 3U, 2U, 1U})},
|
||||
sections()),
|
||||
"opposed-incident-normal");
|
||||
const std::vector<fesa::Node> opposed_nodes{
|
||||
Node(0U, {0.0, 0.0, 0.0}), Node(1U, {1.0, 0.0, 0.0}),
|
||||
Node(2U, {1.0, 1.0, 0.0}), Node(3U, {0.0, 1.0, 0.0})};
|
||||
ExpectFailureCode(
|
||||
fesa::PreprocessShellGeometry(
|
||||
opposed_nodes,
|
||||
{Element(10U, {0U, 1U, 2U, 3U}), Element(20U, {0U, 3U, 2U, 1U})},
|
||||
Sections()),
|
||||
"opposed-incident-normal");
|
||||
}
|
||||
|
||||
// MITC4-GEO-004
|
||||
TEST(Mitc4Geometry, ExposesTheCompleteRequiredValidationPointInventory) {
|
||||
const auto& points = fesa::shellGeometryValidationPoints();
|
||||
ASSERT_EQ(points.size(), 17U);
|
||||
EXPECT_EQ(
|
||||
std::count_if(points.begin(), points.end(), [](const auto& point) {
|
||||
return point.kind == fesa::ShellGeometryPointKind::center;
|
||||
}),
|
||||
1);
|
||||
EXPECT_EQ(
|
||||
std::count_if(points.begin(), points.end(), [](const auto& point) {
|
||||
return point.kind == fesa::ShellGeometryPointKind::stiffness;
|
||||
}),
|
||||
8);
|
||||
EXPECT_EQ(
|
||||
std::count_if(points.begin(), points.end(), [](const auto& point) {
|
||||
return point.kind == fesa::ShellGeometryPointKind::tying;
|
||||
}),
|
||||
4);
|
||||
EXPECT_EQ(
|
||||
std::count_if(points.begin(), points.end(), [](const auto& point) {
|
||||
return point.kind == fesa::ShellGeometryPointKind::recovery;
|
||||
}),
|
||||
4);
|
||||
const auto& points = fesa::ShellGeometryValidationPoints();
|
||||
ASSERT_EQ(points.size(), 17U);
|
||||
EXPECT_EQ(std::count_if(points.begin(), points.end(),
|
||||
[](const auto& point) {
|
||||
return point.kind ==
|
||||
fesa::ShellGeometryPointKind::kCenter;
|
||||
}),
|
||||
1);
|
||||
EXPECT_EQ(std::count_if(points.begin(), points.end(),
|
||||
[](const auto& point) {
|
||||
return point.kind ==
|
||||
fesa::ShellGeometryPointKind::kStiffness;
|
||||
}),
|
||||
8);
|
||||
EXPECT_EQ(std::count_if(points.begin(), points.end(),
|
||||
[](const auto& point) {
|
||||
return point.kind ==
|
||||
fesa::ShellGeometryPointKind::kTying;
|
||||
}),
|
||||
4);
|
||||
EXPECT_EQ(std::count_if(points.begin(), points.end(),
|
||||
[](const auto& point) {
|
||||
return point.kind ==
|
||||
fesa::ShellGeometryPointKind::kRecovery;
|
||||
}),
|
||||
4);
|
||||
|
||||
EXPECT_EQ(points.front().naturalCoordinates, (Vector3{0.0, 0.0, 0.0}));
|
||||
const double gauss = 1.0 / std::sqrt(3.0);
|
||||
EXPECT_EQ(points[1].naturalCoordinates, (Vector3{-gauss, -gauss, -gauss}));
|
||||
EXPECT_EQ(points[8].naturalCoordinates, (Vector3{-gauss, gauss, gauss}));
|
||||
EXPECT_EQ(points[9].naturalCoordinates, (Vector3{0.0, -1.0, 0.0}));
|
||||
EXPECT_EQ(points[12].naturalCoordinates, (Vector3{1.0, 0.0, 0.0}));
|
||||
EXPECT_EQ(points[13].naturalCoordinates, (Vector3{-gauss, -gauss, 0.0}));
|
||||
EXPECT_EQ(points[16].naturalCoordinates, (Vector3{-gauss, gauss, 0.0}));
|
||||
EXPECT_EQ(points.front().natural_coordinates, (Vector3{0.0, 0.0, 0.0}));
|
||||
const double gauss = 1.0 / std::sqrt(3.0);
|
||||
EXPECT_EQ(points[1].natural_coordinates, (Vector3{-gauss, -gauss, -gauss}));
|
||||
EXPECT_EQ(points[8].natural_coordinates, (Vector3{-gauss, gauss, gauss}));
|
||||
EXPECT_EQ(points[9].natural_coordinates, (Vector3{0.0, -1.0, 0.0}));
|
||||
EXPECT_EQ(points[12].natural_coordinates, (Vector3{1.0, 0.0, 0.0}));
|
||||
EXPECT_EQ(points[13].natural_coordinates, (Vector3{-gauss, -gauss, 0.0}));
|
||||
EXPECT_EQ(points[16].natural_coordinates, (Vector3{-gauss, gauss, 0.0}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user