feat(linear-static-3d-euler-beam): step 10 - domain-model
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
#include "fesa/model/domain.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
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::warning,
|
||||
"ignored-output-request",
|
||||
{"models/owned.inp", 70U},
|
||||
"*OUTPUT",
|
||||
"",
|
||||
"Ignored output request."}};
|
||||
return definition;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(DomainModel, ImmutableOwnershipPreservesStableOrder) {
|
||||
auto definition = makeOwnedDefinition();
|
||||
auto result = fesa::Domain::create(definition);
|
||||
ASSERT_TRUE(result.hasValue());
|
||||
|
||||
definition.sourcePath = "mutated.inp";
|
||||
definition.sourceContentIdentity = "mutated";
|
||||
definition.nodes[0].sourceId.sourceLabelText = "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";
|
||||
|
||||
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>&>);
|
||||
|
||||
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.sourceLabel, 20);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabelText, "0020");
|
||||
EXPECT_DOUBLE_EQ(domain.nodes()[0].coordinates[0], 2.0);
|
||||
EXPECT_EQ(domain.nodes()[1].sourceId.sourceLabel, 10);
|
||||
EXPECT_EQ(domain.nodes().data(), firstNodeAddress);
|
||||
|
||||
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.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.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}}};
|
||||
|
||||
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.sourceLabel, 1);
|
||||
EXPECT_EQ(domain.nodes()[2].sourceId.sourceLabel, 1);
|
||||
EXPECT_EQ(domain.nodes()[0].sourceId.sourceLabelText, "1");
|
||||
EXPECT_EQ(domain.nodes()[2].sourceId.sourceLabelText, "1");
|
||||
EXPECT_NE(
|
||||
domain.nodes()[0].sourceId.instanceName,
|
||||
domain.nodes()[2].sourceId.instanceName);
|
||||
|
||||
ASSERT_EQ(domain.elements().size(), 2U);
|
||||
EXPECT_EQ(domain.elements()[0].sourceId.sourceLabel, 1);
|
||||
EXPECT_EQ(domain.elements()[1].sourceId.sourceLabel, 1);
|
||||
EXPECT_EQ(domain.elements()[0].sourceId.instanceName, "Instance-A");
|
||||
EXPECT_EQ(domain.elements()[1].sourceId.instanceName, "Instance-B");
|
||||
EXPECT_EQ(domain.elements()[0].nodeIndices[0], 0U);
|
||||
EXPECT_EQ(domain.elements()[1].nodeIndices[0], 2U);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include "fesa/model/model_types.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
template<class T, class = void>
|
||||
struct HasEquationId : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct HasEquationId<T, std::void_t<decltype(std::declval<T&>().equationId)>>
|
||||
: std::true_type {};
|
||||
|
||||
template<class T, class = void>
|
||||
struct HasEquationIds : std::false_type {};
|
||||
|
||||
template<class T>
|
||||
struct HasEquationIds<T, std::void_t<decltype(std::declval<T&>().equationIds)>>
|
||||
: std::true_type {};
|
||||
|
||||
} // 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"};
|
||||
|
||||
EXPECT_EQ(
|
||||
std::make_tuple(
|
||||
firstIdentity.instanceName,
|
||||
firstIdentity.sourceLabel,
|
||||
firstIdentity.sourceLabelText),
|
||||
std::make_tuple(
|
||||
equalIdentity.instanceName,
|
||||
equalIdentity.sourceLabel,
|
||||
equalIdentity.sourceLabelText));
|
||||
EXPECT_LT(
|
||||
std::make_tuple(
|
||||
firstIdentity.instanceName,
|
||||
firstIdentity.sourceLabel,
|
||||
firstIdentity.sourceLabelText),
|
||||
std::make_tuple(
|
||||
laterIdentity.instanceName,
|
||||
laterIdentity.sourceLabel,
|
||||
laterIdentity.sourceLabelText));
|
||||
|
||||
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}};
|
||||
|
||||
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::warning,
|
||||
"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.sourceLabelText, "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);
|
||||
|
||||
static_assert(!HasEquationId<fesa::Node>::value);
|
||||
static_assert(!HasEquationIds<fesa::Node>::value);
|
||||
static_assert(!HasEquationId<fesa::EulerBeam3DDefinition>::value);
|
||||
static_assert(!HasEquationIds<fesa::EulerBeam3DDefinition>::value);
|
||||
}
|
||||
Reference in New Issue
Block a user