feat(linear-static-mitc4-shell): step 0 - shell-semantic-model

This commit is contained in:
KOKO\Mimi
2026-08-12 18:46:02 +09:00
parent 0d50625a81
commit b671759663
5 changed files with 171 additions and 0 deletions
+3
View File
@@ -17,8 +17,11 @@ public:
const std::vector<Node>& nodes() const noexcept;
const std::vector<EulerBeam3DDefinition>& elements() const noexcept;
const std::vector<Mitc4ShellDefinition>& shellElements() const noexcept;
const std::vector<LinearElasticMaterial>& materials() const noexcept;
const std::vector<GeneralBeamSection>& sections() const noexcept;
const std::vector<ShellSection>& shellSections() const noexcept;
const std::vector<ShellNodeInitialFrame>& shellNodeInitialFrames() const noexcept;
const std::vector<NodeSet>& nodeSets() const noexcept;
const std::vector<ElementSet>& elementSets() const noexcept;
const std::vector<StaticStepDefinition>& steps() const noexcept;
+34
View File
@@ -8,6 +8,7 @@
#include <filesystem>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace fesa {
@@ -40,6 +41,36 @@ struct GeneralBeamSection {
SourceLocation location;
};
enum class ShellSourceElementType {
s4,
s4r
};
inline constexpr std::string_view kMitc4InternalFormulation{"FESA-MITC4"};
struct ShellSection {
std::string name;
double thickness;
EntityIndex materialIndex;
SourceLocation location;
};
struct Mitc4ShellDefinition {
SourceEntityId sourceId;
ShellSourceElementType sourceType;
std::array<EntityIndex, 4> nodeIndices;
EntityIndex materialIndex;
EntityIndex sectionIndex;
SourceLocation location;
};
struct ShellNodeInitialFrame {
EntityIndex nodeIndex;
std::array<double, 3> director;
std::array<double, 3> tangentA;
std::array<double, 3> tangentB;
};
struct EulerBeam3DDefinition {
SourceEntityId sourceId;
std::array<EntityIndex, 2> nodeIndices;
@@ -118,8 +149,11 @@ struct ModelDefinition {
std::string heading;
std::vector<Node> nodes;
std::vector<EulerBeam3DDefinition> elements;
std::vector<Mitc4ShellDefinition> shellElements;
std::vector<LinearElasticMaterial> materials;
std::vector<GeneralBeamSection> sections;
std::vector<ShellSection> shellSections;
std::vector<ShellNodeInitialFrame> shellNodeInitialFrames;
std::vector<NodeSet> nodeSets;
std::vector<ElementSet> elementSets;
std::vector<PartDefinition> parts;
+12
View File
@@ -16,6 +16,10 @@ const std::vector<EulerBeam3DDefinition>& Domain::elements() const noexcept {
return definition_.elements;
}
const std::vector<Mitc4ShellDefinition>& Domain::shellElements() const noexcept {
return definition_.shellElements;
}
const std::vector<LinearElasticMaterial>& Domain::materials() const noexcept {
return definition_.materials;
}
@@ -24,6 +28,14 @@ const std::vector<GeneralBeamSection>& Domain::sections() const noexcept {
return definition_.sections;
}
const std::vector<ShellSection>& Domain::shellSections() const noexcept {
return definition_.shellSections;
}
const std::vector<ShellNodeInitialFrame>& Domain::shellNodeInitialFrames() const noexcept {
return definition_.shellNodeInitialFrames;
}
const std::vector<NodeSet>& Domain::nodeSets() const noexcept {
return definition_.nodeSets;
}
+79
View File
@@ -182,3 +182,82 @@ TEST(DomainModel, MultipleIdentityInstancesDoNotMerge) {
EXPECT_EQ(domain.elements()[0].nodeIndices[0], 0U);
EXPECT_EQ(domain.elements()[1].nodeIndices[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}}};
auto result = fesa::Domain::create(definition);
ASSERT_TRUE(result.hasValue());
definition.shellSections[0].thickness = -1.0;
definition.shellElements[0].sourceId.sourceLabelText = "mutated";
definition.shellNodeInitialFrames[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>&>);
ASSERT_EQ(domain.shellElements().size(), 2U);
EXPECT_EQ(domain.shellElements()[0].sourceId.sourceLabel, 20);
EXPECT_EQ(domain.shellElements()[0].sourceId.sourceLabelText, "0020");
EXPECT_EQ(domain.shellElements()[1].sourceId.sourceLabel, 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.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.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}));
auto noFramesResult = fesa::Domain::create(fesa::ModelDefinition{});
ASSERT_TRUE(noFramesResult.hasValue());
EXPECT_TRUE(noFramesResult.value().shellNodeInitialFrames().empty());
}
+43
View File
@@ -6,6 +6,7 @@
#include <cstdint>
#include <filesystem>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
@@ -142,3 +143,45 @@ TEST(DomainModel, SourceAndInternalIdentityRemainDistinct) {
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}};
EXPECT_EQ(s4Element.sourceId.instanceName, "Shell-Instance");
EXPECT_EQ(s4Element.sourceId.sourceLabel, 41);
EXPECT_EQ(s4Element.sourceId.sourceLabelText, "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);
static_assert(!HasEquationId<fesa::Mitc4ShellDefinition>::value);
static_assert(!HasEquationIds<fesa::Mitc4ShellDefinition>::value);
}