Files
FESADev/tests/unit/io/abaqus/shell_surface_pressure_domain_test.cpp
T

305 lines
11 KiB
C++

#include "fesa/analysis/analysis_model.h"
#include "fesa/io/abaqus/domain_mapper.h"
#include "fesa/io/abaqus/input_reader.h"
#include <gtest/gtest.h>
#include <filesystem>
#include <fstream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace {
class TemporaryInputFile {
public:
TemporaryInputFile(const std::string& stem, const std::string& content)
: path_{std::filesystem::temp_directory_path() /
("fesa-shell-surface-pressure-" + stem + ".inp")} {
std::ofstream stream{path_, std::ios::binary | std::ios::trunc};
stream.write(content.data(), static_cast<std::streamsize>(content.size()));
if (!stream) {
throw std::runtime_error{"Unable to create pressure input fixture."};
}
}
~TemporaryInputFile() {
std::error_code error;
std::filesystem::remove(path_, error);
}
const std::filesystem::path& Path() const noexcept { return path_; }
private:
std::filesystem::path path_;
};
fesa::Result<fesa::Domain> MapText(const std::string& stem,
const std::string& content) {
const TemporaryInputFile input{stem, content};
const auto parsed = fesa::AbaqusInputReader{}.Read(input.Path());
if (!parsed.HasValue()) {
return fesa::Result<fesa::Domain>::Failure(parsed.GetStatus());
}
return fesa::AbaqusDomainMapper{}.Map(parsed.Value());
}
std::string ShellDeck(const std::string& assembly_content,
const std::string& dsload_rows) {
return std::string{R"inp(*Part, name=Shell-Part
*Node
1, 0., 0., 0.
2, 1., 0., 0.
3, 1., 1., 0.
4, 0., 1., 0.
5, 2., 0., 0.
6, 2., 1., 0.
*Element, type=S4
0010, 1, 2, 3, 4
*Element, type=S4R
0020, 2, 5, 6, 3
*Elset, elset=S4-Set
0010
*Elset, elset=S4R-Set
0020
*Shell Section, elset=S4-Set, material=Steel
0.1
*Shell Section, elset=S4R-Set, material=Steel
0.1
*End Part
*Assembly, name=Assembly
*Instance, name=Instance-B, part=Shell-Part
*End Instance
*Instance, name=Instance-A, part=Shell-Part
*End Instance
)inp"} +
assembly_content + R"inp(*End Assembly
*Material, name=Steel
*Elastic
210000., 0.3
*Step, name=Pressure-Step, nlgeom=NO
*Static
0.1, 1., 0.01, 1.
*Dsload
)inp" +
dsload_rows + "*End Step\n";
}
const fesa::ElementSurfaceDefinition& OnlySurface(const fesa::Domain& domain) {
EXPECT_EQ(domain.ElementSurfaces().size(), 1U);
return domain.ElementSurfaces().front();
}
} // namespace
TEST(ShellSurfacePressureInput,
MapsDirectInstanceFacetsForS4AndS4rCaseInsensitively) {
auto result = MapText(
"direct",
ShellDeck("*SuRfAcE, NAME=Direct-Surface, TYPE=ELEMENT\n"
"instance-a.0010, spos\n"
"INSTANCE-A.0020, sneg\n",
"direct-surface, P, 1.0\n"));
ASSERT_TRUE(result.HasValue());
const auto& surface = OnlySurface(result.Value());
EXPECT_EQ(surface.surface_name_raw, "Direct-Surface");
EXPECT_EQ(surface.surface_name_canonical, "DIRECT-SURFACE");
ASSERT_EQ(surface.facets.size(), 2U);
EXPECT_EQ(surface.facets[0].instance_name_raw, "Instance-A");
EXPECT_EQ(surface.facets[0].source_element_label, 10);
EXPECT_EQ(surface.facets[0].source_element_label_raw, "0010");
EXPECT_EQ(surface.facets[0].source_element_type, "S4");
EXPECT_EQ(surface.facets[0].side, fesa::ShellSurfaceSide::kSpos);
EXPECT_EQ(surface.facets[1].source_element_type, "S4R");
EXPECT_EQ(surface.facets[1].side, fesa::ShellSurfaceSide::kSneg);
}
TEST(ShellSurfacePressureInput,
MapsExplicitInstanceElsetAndResultNeutralInternalParameter) {
auto result = MapText(
"elset",
ShellDeck("*ELSET, ELSET=Flat-Members, INTERNAL, INSTANCE=Instance-A\n"
"10, 20\n"
"*SURFACE, TYPE=ELEMENT, NAME=Elset-Surface\n"
"flat-members, SPOS\n",
"Elset-Surface, P, 5.0\n"));
ASSERT_TRUE(result.HasValue());
const auto& surface = OnlySurface(result.Value());
ASSERT_EQ(surface.facets.size(), 2U);
for (const auto& facet : surface.facets) {
EXPECT_EQ(facet.member_kind, fesa::SurfaceMemberKind::kElset);
EXPECT_EQ(facet.elset_name_raw, "flat-members");
EXPECT_EQ(facet.elset_name_canonical, "FLAT-MEMBERS");
EXPECT_EQ(facet.instance_name_raw, "Instance-A");
EXPECT_EQ(facet.side, fesa::ShellSurfaceSide::kSpos);
}
}
TEST(ShellSurfacePressureInput,
MapsMixedDirectAndElsetMembersAcrossTwoExplicitInstancesWithStableDataMemberAndElsetOrder) {
auto result = MapText(
"mixed",
ShellDeck("*ELSET, ELSET=Flat-B, INSTANCE=instance-b\n"
"20\n"
"*ELSET, ELSET=Flat-A, INSTANCE=INSTANCE-A\n"
"10\n"
"*SURFACE, TYPE=ELEMENT, NAME=Mixed-Surface\n"
"instance-a.20, SNEG\n"
"flat-b, SPOS\n"
"INSTANCE-B.10, sneg\n"
"FLAT-A, spos\n",
"Mixed-Surface, P, 3.0\n"));
ASSERT_TRUE(result.HasValue());
const auto& surface = OnlySurface(result.Value());
ASSERT_EQ(surface.facets.size(), 4U);
EXPECT_EQ(surface.facets[0].instance_name_raw, "Instance-B");
EXPECT_EQ(surface.facets[0].source_element_label, 10);
EXPECT_EQ(surface.facets[0].side, fesa::ShellSurfaceSide::kSneg);
EXPECT_EQ(surface.facets[0].first_member_ordinal, 2U);
EXPECT_EQ(surface.facets[1].instance_name_raw, "Instance-B");
EXPECT_EQ(surface.facets[1].source_element_label, 20);
EXPECT_EQ(surface.facets[1].member_kind, fesa::SurfaceMemberKind::kElset);
EXPECT_EQ(surface.facets[1].first_member_ordinal, 1U);
EXPECT_EQ(surface.facets[2].instance_name_raw, "Instance-A");
EXPECT_EQ(surface.facets[2].source_element_label, 10);
EXPECT_EQ(surface.facets[2].member_kind, fesa::SurfaceMemberKind::kElset);
EXPECT_EQ(surface.facets[2].first_member_ordinal, 3U);
EXPECT_EQ(surface.facets[3].instance_name_raw, "Instance-A");
EXPECT_EQ(surface.facets[3].source_element_label, 20);
EXPECT_EQ(surface.facets[3].member_kind, fesa::SurfaceMemberKind::kDirect);
EXPECT_EQ(surface.facets[3].first_member_ordinal, 0U);
for (const auto& facet : surface.facets) {
EXPECT_EQ(facet.part_name_raw, "Shell-Part");
EXPECT_EQ(facet.part_name_canonical, "SHELL-PART");
EXPECT_EQ(facet.instance_name_canonical,
facet.instance_name_raw == "Instance-A" ? "INSTANCE-A"
: "INSTANCE-B");
}
}
TEST(ShellSurfacePressureInput,
PreservesSposSnegMagnitudeTextLocationsAndSourceOrder) {
auto result = MapText(
"provenance",
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
"Instance-A.10, SPOS\n"
"Instance-A.20, SNEG\n",
"pressure-surface, p, -0.0\n"
"PRESSURE-SURFACE, P, +2.50e+03\n"));
ASSERT_TRUE(result.HasValue());
const auto& surface = OnlySurface(result.Value());
ASSERT_EQ(surface.facets.size(), 2U);
EXPECT_EQ(surface.facets[0].facet_ordinal, 0U);
EXPECT_EQ(surface.facets[0].side, fesa::ShellSurfaceSide::kSpos);
EXPECT_EQ(surface.facets[1].facet_ordinal, 1U);
EXPECT_EQ(surface.facets[1].side, fesa::ShellSurfaceSide::kSneg);
const auto& step = result.Value().Steps()[0U];
ASSERT_EQ(step.SurfacePressureLoads().size(), 2U);
const auto& first = step.SurfacePressureLoads()[0U];
const auto& second = step.SurfacePressureLoads()[1U];
EXPECT_EQ(first.step_name_raw, "Pressure-Step");
EXPECT_EQ(first.step_name_canonical, "PRESSURE-STEP");
EXPECT_EQ(first.surface_name_raw, "pressure-surface");
EXPECT_EQ(first.surface_name_canonical, "PRESSURE-SURFACE");
EXPECT_EQ(first.load_type, "P");
EXPECT_DOUBLE_EQ(first.magnitude, 0.0);
EXPECT_EQ(first.magnitude_raw, "-0.0");
EXPECT_EQ(first.data_ordinal, 0U);
EXPECT_EQ(first.load_ordinal, 0U);
EXPECT_LT(first.keyword_location.line, first.data_location.line);
EXPECT_EQ(second.magnitude_raw, "+2.50e+03");
EXPECT_DOUBLE_EQ(second.magnitude, 2500.0);
EXPECT_EQ(second.data_ordinal, 1U);
EXPECT_EQ(second.load_ordinal, 1U);
}
TEST(ShellSurfacePressureDomain,
OwnsCanonicalFacetsAndStepOwnedAdditivePressureRecords) {
fesa::ModelDefinition definition{};
definition.element_surfaces = {
{"Surface", "SURFACE", {{0U,
0U,
"Part",
"PART",
"Instance",
"INSTANCE",
10,
"0010",
"S4",
fesa::ShellSurfaceSide::kSpos,
{"owned.inp", 3U},
0U,
fesa::SurfaceMemberKind::kDirect,
"",
""}},
{"owned.inp", 2U},
0U}};
definition.steps = {{"Step", {}, {},
{{"Step", "STEP", 0U, "Surface", "SURFACE", "P",
1.0, "1.0", {"owned.inp", 7U},
{"owned.inp", 8U}, 0U, 0U}},
fesa::StaticProcedureKind::kLinearSmallDisplacement,
false, 0.1, 1.0, 0.01, 1.0, {"owned.inp", 6U}}};
auto result = fesa::Domain::Create(definition);
ASSERT_TRUE(result.HasValue());
definition.element_surfaces[0].facets[0].source_element_label = 99;
definition.steps[0].surface_pressure_loads[0].magnitude = 99.0;
const fesa::Domain& domain = result.Value();
static_assert(std::is_same_v<
decltype(std::declval<const fesa::Domain&>().ElementSurfaces()),
const std::vector<fesa::ElementSurfaceDefinition>&>);
static_assert(std::is_same_v<
decltype(std::declval<const fesa::StepDefinition&>()
.SurfacePressureLoads()),
const std::vector<fesa::SurfacePressureLoadDefinition>&>);
EXPECT_EQ(domain.ElementSurfaces()[0].facets[0].source_element_label, 10);
EXPECT_DOUBLE_EQ(domain.Steps()[0U].SurfacePressureLoads()[0].magnitude,
1.0);
}
TEST(ShellSurfacePressureDomain,
AnalysisModelBorrowsActivePressureWithoutCopyingDomain) {
auto mapped = MapText(
"analysis-model",
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
"Instance-A.10, SPOS\n",
"Pressure-Surface, P, 1.0\n"));
ASSERT_TRUE(mapped.HasValue());
auto model = fesa::AnalysisModel::Create(mapped.Value());
ASSERT_TRUE(model.HasValue());
EXPECT_EQ(&model.Value().ActiveSurfacePressureLoads(),
&mapped.Value().Steps()[0U].SurfacePressureLoads());
EXPECT_EQ(model.Value().ActiveSurfacePressureLoads().size(), 1U);
}
TEST(ShellSurfacePressureInput,
AcceptsWhitespaceSignedZeroAndFiniteScientificNotation) {
auto result = MapText(
"numbers",
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
"Instance-A.10, SPOS\n",
" pressure-surface , p , +0 \n"
"Pressure-Surface, P, -1.25E-3\n"));
ASSERT_TRUE(result.HasValue());
const auto& loads = result.Value().Steps()[0U].SurfacePressureLoads();
ASSERT_EQ(loads.size(), 2U);
EXPECT_EQ(loads[0].magnitude_raw, "+0");
EXPECT_DOUBLE_EQ(loads[0].magnitude, 0.0);
EXPECT_EQ(loads[1].magnitude_raw, "-1.25E-3");
EXPECT_DOUBLE_EQ(loads[1].magnitude, -0.00125);
}