feat(domain-and-input-skeleton): step 0 — semantic-domain-and-origin-types
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <fesa/model/entity_origin.hpp>
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct BeamElement final {
|
||||||
|
ElementId id;
|
||||||
|
EntityOrigin origin;
|
||||||
|
std::array<NodeId, 2> nodes;
|
||||||
|
MaterialId material;
|
||||||
|
SectionId section;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fesa/core/vec3.hpp>
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
enum class ShearPropertySource { input, phase1_default };
|
||||||
|
|
||||||
|
struct BeamSection final {
|
||||||
|
SectionId id;
|
||||||
|
std::string name;
|
||||||
|
double area;
|
||||||
|
double iy;
|
||||||
|
double iz;
|
||||||
|
double torsion_j;
|
||||||
|
double shear_area_y;
|
||||||
|
double shear_area_z;
|
||||||
|
ShearPropertySource shear_source;
|
||||||
|
Vec3 orientation;
|
||||||
|
std::vector<std::array<double, 2>> recovery_points;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fesa/model/beam_element.hpp>
|
||||||
|
#include <fesa/model/beam_section.hpp>
|
||||||
|
#include <fesa/model/entity_set.hpp>
|
||||||
|
#include <fesa/model/material.hpp>
|
||||||
|
#include <fesa/model/node.hpp>
|
||||||
|
#include <fesa/model/step_definition.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
class Domain final {
|
||||||
|
public:
|
||||||
|
Domain(
|
||||||
|
std::vector<Node> nodes,
|
||||||
|
std::vector<BeamElement> beam_elements,
|
||||||
|
std::vector<IsotropicElastic> materials,
|
||||||
|
std::vector<BeamSection> sections,
|
||||||
|
std::vector<NodeSet> node_sets,
|
||||||
|
std::vector<ElementSet> element_sets,
|
||||||
|
StepDefinition step)
|
||||||
|
: nodes_{std::move(nodes)},
|
||||||
|
beam_elements_{std::move(beam_elements)},
|
||||||
|
materials_{std::move(materials)},
|
||||||
|
sections_{std::move(sections)},
|
||||||
|
node_sets_{std::move(node_sets)},
|
||||||
|
element_sets_{std::move(element_sets)},
|
||||||
|
step_{std::move(step)} {}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const Node> nodes() const noexcept {
|
||||||
|
return nodes_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const BeamElement> beam_elements() const noexcept {
|
||||||
|
return beam_elements_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const IsotropicElastic> materials() const noexcept {
|
||||||
|
return materials_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const BeamSection> sections() const noexcept {
|
||||||
|
return sections_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const NodeSet> node_sets() const noexcept {
|
||||||
|
return node_sets_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::span<const ElementSet> element_sets() const noexcept {
|
||||||
|
return element_sets_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const StepDefinition& step() const noexcept {
|
||||||
|
return step_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const Node& node(const NodeId id) const {
|
||||||
|
const auto found = std::find_if(
|
||||||
|
nodes_.begin(), nodes_.end(),
|
||||||
|
[id](const Node& candidate) { return candidate.id == id; });
|
||||||
|
if (found == nodes_.end()) {
|
||||||
|
throw std::out_of_range{"Node ID is not present in the Domain."};
|
||||||
|
}
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const Node& node(const EntityOrigin& origin) const {
|
||||||
|
const auto found = std::find_if(
|
||||||
|
nodes_.begin(), nodes_.end(), [&origin](const Node& candidate) {
|
||||||
|
return candidate.origin == origin;
|
||||||
|
});
|
||||||
|
if (found == nodes_.end()) {
|
||||||
|
throw std::out_of_range{
|
||||||
|
"Node origin is not present in the Domain."};
|
||||||
|
}
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Node> nodes_;
|
||||||
|
std::vector<BeamElement> beam_elements_;
|
||||||
|
std::vector<IsotropicElastic> materials_;
|
||||||
|
std::vector<BeamSection> sections_;
|
||||||
|
std::vector<NodeSet> node_sets_;
|
||||||
|
std::vector<ElementSet> element_sets_;
|
||||||
|
StepDefinition step_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct EntityOrigin final {
|
||||||
|
std::string part_name;
|
||||||
|
std::string instance_name;
|
||||||
|
std::int64_t local_label;
|
||||||
|
|
||||||
|
bool operator==(const EntityOrigin&) const = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct NodeSet final {
|
||||||
|
std::string name;
|
||||||
|
std::vector<NodeId> members;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ElementSet final {
|
||||||
|
std::string name;
|
||||||
|
std::vector<ElementId> members;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fesa/core/entity_id.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct NodeTag;
|
||||||
|
struct ElementTag;
|
||||||
|
struct MaterialTag;
|
||||||
|
struct SectionTag;
|
||||||
|
|
||||||
|
using NodeId = EntityId<NodeTag>;
|
||||||
|
using ElementId = EntityId<ElementTag>;
|
||||||
|
using MaterialId = EntityId<MaterialTag>;
|
||||||
|
using SectionId = EntityId<SectionTag>;
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct IsotropicElastic final {
|
||||||
|
MaterialId id;
|
||||||
|
std::string name;
|
||||||
|
double young;
|
||||||
|
double poisson;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <fesa/core/vec3.hpp>
|
||||||
|
#include <fesa/model/entity_origin.hpp>
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct Node final {
|
||||||
|
NodeId id;
|
||||||
|
EntityOrigin origin;
|
||||||
|
Vec3 position;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fesa/model/ids.hpp>
|
||||||
|
|
||||||
|
namespace fesa {
|
||||||
|
|
||||||
|
struct PrescribedDof final {
|
||||||
|
NodeId node;
|
||||||
|
std::uint8_t dof;
|
||||||
|
double value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NodalLoad final {
|
||||||
|
NodeId node;
|
||||||
|
std::array<double, 6> values;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StepDefinition final {
|
||||||
|
std::string name;
|
||||||
|
std::vector<PrescribedDof> prescribed_dofs;
|
||||||
|
std::vector<NodalLoad> nodal_loads;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace fesa
|
||||||
@@ -75,3 +75,27 @@ add_test(
|
|||||||
NAME CoreValueTypes
|
NAME CoreValueTypes
|
||||||
COMMAND "$<TARGET_FILE:fesa_core_value_tests>"
|
COMMAND "$<TARGET_FILE:fesa_core_value_tests>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_executable(fesa_model_value_tests
|
||||||
|
unit/model/entity_origin_test.cpp
|
||||||
|
unit/model/model_types_test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(fesa_model_value_tests PRIVATE cxx_std_20)
|
||||||
|
target_compile_options(fesa_model_value_tests PRIVATE /W4 /permissive- /EHsc)
|
||||||
|
|
||||||
|
target_link_libraries(fesa_model_value_tests
|
||||||
|
PRIVATE
|
||||||
|
fesa_core
|
||||||
|
GTest::gtest_main
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME ModelTypes
|
||||||
|
COMMAND "$<TARGET_FILE:fesa_model_value_tests>" --gtest_filter=ModelTypes.*
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(
|
||||||
|
NAME EntityOrigin
|
||||||
|
COMMAND "$<TARGET_FILE:fesa_model_value_tests>" --gtest_filter=EntityOrigin.*
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#include <fesa/model/domain.hpp>
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
static_assert(std::is_same_v<
|
||||||
|
decltype(std::declval<const fesa::Domain&>().nodes()),
|
||||||
|
std::span<const fesa::Node>>);
|
||||||
|
static_assert(std::is_same_v<
|
||||||
|
decltype(std::declval<const fesa::Domain&>().node(
|
||||||
|
std::declval<fesa::NodeId>())),
|
||||||
|
const fesa::Node&>);
|
||||||
|
|
||||||
|
TEST(EntityOrigin, UsesEmptyScopeNamesForFlatMeshEntities) {
|
||||||
|
const fesa::EntityOrigin origin{"", "", 17};
|
||||||
|
|
||||||
|
EXPECT_TRUE(origin.part_name.empty());
|
||||||
|
EXPECT_TRUE(origin.instance_name.empty());
|
||||||
|
EXPECT_EQ(origin.local_label, 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EntityOrigin, FindsHierarchicalNodeByCompositeOrigin) {
|
||||||
|
const fesa::Node node{
|
||||||
|
fesa::NodeId{0},
|
||||||
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 101},
|
||||||
|
fesa::Vec3{1.0, 2.0, 3.0},
|
||||||
|
};
|
||||||
|
const fesa::Domain domain{
|
||||||
|
std::vector<fesa::Node>{node},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
{},
|
||||||
|
fesa::StepDefinition{"Load", {}, {}},
|
||||||
|
};
|
||||||
|
|
||||||
|
const fesa::EntityOrigin lookup{"BeamPart", "Beam-1", 101};
|
||||||
|
const fesa::Node& found_by_origin = domain.node(lookup);
|
||||||
|
const fesa::Node& found_by_id = domain.node(fesa::NodeId{0});
|
||||||
|
|
||||||
|
EXPECT_EQ(&found_by_origin, &domain.nodes().front());
|
||||||
|
EXPECT_EQ(&found_by_id, &domain.nodes().front());
|
||||||
|
EXPECT_EQ(found_by_origin.origin.part_name, "BeamPart");
|
||||||
|
EXPECT_EQ(found_by_origin.origin.instance_name, "Beam-1");
|
||||||
|
EXPECT_EQ(found_by_origin.origin.local_label, 101);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#include <fesa/model/beam_element.hpp>
|
||||||
|
#include <fesa/model/beam_section.hpp>
|
||||||
|
#include <fesa/model/entity_set.hpp>
|
||||||
|
#include <fesa/model/material.hpp>
|
||||||
|
#include <fesa/model/node.hpp>
|
||||||
|
#include <fesa/model/step_definition.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(ModelTypes, PreservesNodeAndBeamElementValues) {
|
||||||
|
const fesa::Node first_node{
|
||||||
|
fesa::NodeId{0},
|
||||||
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 101},
|
||||||
|
fesa::Vec3{1.0, 2.0, 3.0},
|
||||||
|
};
|
||||||
|
const fesa::BeamElement element{
|
||||||
|
fesa::ElementId{4},
|
||||||
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 201},
|
||||||
|
{fesa::NodeId{0}, fesa::NodeId{1}},
|
||||||
|
fesa::MaterialId{2},
|
||||||
|
fesa::SectionId{3},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(first_node.id.value(), 0);
|
||||||
|
EXPECT_EQ(first_node.origin.part_name, "BeamPart");
|
||||||
|
EXPECT_EQ(first_node.origin.instance_name, "Beam-1");
|
||||||
|
EXPECT_EQ(first_node.origin.local_label, 101);
|
||||||
|
EXPECT_DOUBLE_EQ(first_node.position.x, 1.0);
|
||||||
|
EXPECT_DOUBLE_EQ(first_node.position.y, 2.0);
|
||||||
|
EXPECT_DOUBLE_EQ(first_node.position.z, 3.0);
|
||||||
|
|
||||||
|
EXPECT_EQ(element.id.value(), 4);
|
||||||
|
EXPECT_EQ(element.origin.local_label, 201);
|
||||||
|
EXPECT_EQ(element.nodes[0].value(), 0);
|
||||||
|
EXPECT_EQ(element.nodes[1].value(), 1);
|
||||||
|
EXPECT_EQ(element.material.value(), 2);
|
||||||
|
EXPECT_EQ(element.section.value(), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ModelTypes, PreservesMaterialAndBeamSectionValues) {
|
||||||
|
const fesa::IsotropicElastic material{
|
||||||
|
fesa::MaterialId{2},
|
||||||
|
"Steel",
|
||||||
|
210.0e9,
|
||||||
|
0.3,
|
||||||
|
};
|
||||||
|
const fesa::BeamSection section{
|
||||||
|
fesa::SectionId{3},
|
||||||
|
"General",
|
||||||
|
0.04,
|
||||||
|
1.2e-4,
|
||||||
|
1.4e-4,
|
||||||
|
2.0e-4,
|
||||||
|
0.03,
|
||||||
|
0.031,
|
||||||
|
fesa::ShearPropertySource::input,
|
||||||
|
fesa::Vec3{0.0, 1.0, 0.0},
|
||||||
|
{{-0.1, 0.0}, {0.1, 0.0}},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(material.id.value(), 2);
|
||||||
|
EXPECT_EQ(material.name, "Steel");
|
||||||
|
EXPECT_DOUBLE_EQ(material.young, 210.0e9);
|
||||||
|
EXPECT_DOUBLE_EQ(material.poisson, 0.3);
|
||||||
|
|
||||||
|
EXPECT_EQ(section.id.value(), 3);
|
||||||
|
EXPECT_EQ(section.name, "General");
|
||||||
|
EXPECT_DOUBLE_EQ(section.area, 0.04);
|
||||||
|
EXPECT_DOUBLE_EQ(section.iy, 1.2e-4);
|
||||||
|
EXPECT_DOUBLE_EQ(section.iz, 1.4e-4);
|
||||||
|
EXPECT_DOUBLE_EQ(section.torsion_j, 2.0e-4);
|
||||||
|
EXPECT_DOUBLE_EQ(section.shear_area_y, 0.03);
|
||||||
|
EXPECT_DOUBLE_EQ(section.shear_area_z, 0.031);
|
||||||
|
EXPECT_EQ(section.shear_source, fesa::ShearPropertySource::input);
|
||||||
|
EXPECT_DOUBLE_EQ(section.orientation.y, 1.0);
|
||||||
|
ASSERT_EQ(section.recovery_points.size(), 2);
|
||||||
|
EXPECT_DOUBLE_EQ(section.recovery_points[0][0], -0.1);
|
||||||
|
EXPECT_DOUBLE_EQ(section.recovery_points[1][0], 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ModelTypes, PreservesSetsAndLinearStaticStepValues) {
|
||||||
|
const fesa::NodeSet node_set{
|
||||||
|
"Fixed",
|
||||||
|
{fesa::NodeId{0}, fesa::NodeId{1}},
|
||||||
|
};
|
||||||
|
const fesa::ElementSet element_set{
|
||||||
|
"Beam",
|
||||||
|
{fesa::ElementId{4}},
|
||||||
|
};
|
||||||
|
const fesa::StepDefinition step{
|
||||||
|
"Load",
|
||||||
|
{{fesa::NodeId{0}, 6, 0.25}},
|
||||||
|
{{
|
||||||
|
fesa::NodeId{1},
|
||||||
|
{10.0, 20.0, 30.0, 40.0, 50.0, 60.0},
|
||||||
|
}},
|
||||||
|
};
|
||||||
|
|
||||||
|
EXPECT_EQ(node_set.name, "Fixed");
|
||||||
|
ASSERT_EQ(node_set.members.size(), 2);
|
||||||
|
EXPECT_EQ(node_set.members[1].value(), 1);
|
||||||
|
EXPECT_EQ(element_set.name, "Beam");
|
||||||
|
ASSERT_EQ(element_set.members.size(), 1);
|
||||||
|
EXPECT_EQ(element_set.members[0].value(), 4);
|
||||||
|
|
||||||
|
EXPECT_EQ(step.name, "Load");
|
||||||
|
ASSERT_EQ(step.prescribed_dofs.size(), 1);
|
||||||
|
EXPECT_EQ(step.prescribed_dofs[0].node.value(), 0);
|
||||||
|
EXPECT_EQ(step.prescribed_dofs[0].dof, 6);
|
||||||
|
EXPECT_DOUBLE_EQ(step.prescribed_dofs[0].value, 0.25);
|
||||||
|
ASSERT_EQ(step.nodal_loads.size(), 1);
|
||||||
|
EXPECT_EQ(step.nodal_loads[0].node.value(), 1);
|
||||||
|
EXPECT_DOUBLE_EQ(step.nodal_loads[0].values[5], 60.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Reference in New Issue
Block a user