589 lines
23 KiB
C++
589 lines
23 KiB
C++
#include "fesa/analysis/analysis_model.h"
|
|
#include "fesa/core/diagnostic.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());
|
|
}
|
|
|
|
fesa::Result<fesa::ParsedInput> ReadText(const std::string& stem,
|
|
const std::string& content) {
|
|
const TemporaryInputFile input{stem, content};
|
|
return fesa::AbaqusInputReader{}.Read(input.Path());
|
|
}
|
|
|
|
std::string ReplaceOnce(std::string text, const std::string& from,
|
|
const std::string& to) {
|
|
const std::size_t position = text.find(from);
|
|
if (position == std::string::npos) {
|
|
throw std::runtime_error{"Expected fixture text was not found."};
|
|
}
|
|
text.replace(position, from.size(), to);
|
|
return text;
|
|
}
|
|
|
|
void ExpectSingleFailure(const fesa::Result<fesa::Domain>& result,
|
|
const std::string& code, std::size_t line,
|
|
const std::string& keyword) {
|
|
ASSERT_FALSE(result.HasValue());
|
|
ASSERT_EQ(result.GetStatus().Diagnostics().size(), 1U);
|
|
const fesa::Diagnostic& diagnostic = result.GetStatus().Diagnostics().front();
|
|
EXPECT_EQ(diagnostic.severity, fesa::Severity::kError);
|
|
EXPECT_EQ(diagnostic.code, code);
|
|
EXPECT_EQ(diagnostic.location.line, line);
|
|
EXPECT_EQ(diagnostic.keyword, keyword);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsNonAsciiOverlengthOrIllegalContinuationAtStableLocation) {
|
|
const auto non_ascii = ReadText("non-ascii", "*Heading\n\xFF\n");
|
|
ASSERT_FALSE(non_ascii.HasValue());
|
|
ASSERT_EQ(non_ascii.GetStatus().Diagnostics().size(), 1U);
|
|
EXPECT_EQ(non_ascii.GetStatus().Diagnostics().front().code,
|
|
"invalid-input-character");
|
|
EXPECT_EQ(non_ascii.GetStatus().Diagnostics().front().location.line, 2U);
|
|
|
|
const auto overlength = ReadText("overlength", std::string(257U, 'x'));
|
|
ASSERT_FALSE(overlength.HasValue());
|
|
EXPECT_EQ(overlength.GetStatus().Diagnostics().front().code,
|
|
"input-line-too-long");
|
|
EXPECT_EQ(overlength.GetStatus().Diagnostics().front().location.line, 1U);
|
|
|
|
const auto continuation = ReadText(
|
|
"continuation",
|
|
"*Surface, TYPE=ELEMENT, NAME=Pressure-Surface,\n** interruption\n");
|
|
ASSERT_FALSE(continuation.HasValue());
|
|
EXPECT_EQ(continuation.GetStatus().Diagnostics().front().code,
|
|
"malformed-keyword");
|
|
EXPECT_EQ(continuation.GetStatus().Diagnostics().front().location.line, 2U);
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsUnsupportedSurfaceParametersTypesSidesAndTargetKinds) {
|
|
ExpectSingleFailure(
|
|
MapText("surface-parameter",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface, "
|
|
"TRIM=YES\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"unsupported-parameter", 27U, "SURFACE");
|
|
ExpectSingleFailure(
|
|
MapText("surface-type",
|
|
ShellDeck("*SURFACE, TYPE=NODE, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"unsupported-surface-type", 27U, "SURFACE");
|
|
ExpectSingleFailure(
|
|
MapText("surface-side",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, S1\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"unsupported-surface-side", 28U, "SURFACE");
|
|
ExpectSingleFailure(
|
|
MapText("surface-target-kind",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Shell-Part.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"unsupported-surface-target", 28U, "SURFACE");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsDsloadParametersLabelsDistributionsAndCardinality) {
|
|
const std::string parameter_deck = ReplaceOnce(
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n"),
|
|
"*Dsload", "*Dsload, AMPLITUDE=Ramp");
|
|
ExpectSingleFailure(MapText("dsload-parameter", parameter_deck),
|
|
"unsupported-dsload-parameter", 36U, "DSLOAD");
|
|
ExpectSingleFailure(
|
|
MapText("dsload-label",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, PNU, 1.0\n")),
|
|
"unsupported-load-type", 37U, "DSLOAD");
|
|
ExpectSingleFailure(
|
|
MapText("dsload-cardinality",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0, 2.0\n")),
|
|
"invalid-data-arity", 37U, "DSLOAD");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsEmptyMissingAmbiguousAndCrossScopeSurfaceTargets) {
|
|
ExpectSingleFailure(
|
|
MapText("empty-surface",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"empty-surface", 27U, "SURFACE");
|
|
ExpectSingleFailure(
|
|
MapText("missing-surface",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Missing-Surface, P, 1.0\n")),
|
|
"missing-reference", 37U, "DSLOAD");
|
|
ExpectSingleFailure(
|
|
MapText("cross-scope-elset",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"S4-Set, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"cross-scope-target", 28U, "SURFACE");
|
|
ExpectSingleFailure(
|
|
MapText("ambiguous-target",
|
|
ShellDeck("*ELSET, ELSET=Shared, INSTANCE=Instance-A\n"
|
|
"10\n"
|
|
"*ELSET, ELSET=Shared, INSTANCE=Instance-B\n"
|
|
"10\n"
|
|
"*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Shared, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"duplicate-entity", 29U, "ELSET");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsImplicitInstanceNestedCyclicAndMalformedElsets) {
|
|
ExpectSingleFailure(
|
|
MapText("implicit-elset",
|
|
ShellDeck("*ELSET, ELSET=Implicit\n"
|
|
"10\n"
|
|
"*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Implicit, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"implicit-instance-elset", 27U, "ELSET");
|
|
ExpectSingleFailure(
|
|
MapText("nested-elset",
|
|
ShellDeck("*ELSET, ELSET=Inner, INSTANCE=Instance-A\n"
|
|
"10\n"
|
|
"*ELSET, ELSET=Outer, INSTANCE=Instance-A\n"
|
|
"Inner\n"
|
|
"*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Outer, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"nested-elset", 30U, "ELSET");
|
|
ExpectSingleFailure(
|
|
MapText("cyclic-elset",
|
|
ShellDeck("*ELSET, ELSET=Loop, INSTANCE=Instance-A\n"
|
|
"Loop\n"
|
|
"*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Loop, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"cyclic-elset", 28U, "ELSET");
|
|
ExpectSingleFailure(
|
|
MapText("malformed-elset",
|
|
ShellDeck("*ELSET, ELSET=Malformed, INSTANCE=Instance-A\n"
|
|
"10, , 20\n"
|
|
"*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Malformed, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"invalid-data-arity", 28U, "ELSET");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
NormalizesExactSameSideDuplicatesWithOneStableWarning) {
|
|
const auto result = MapText(
|
|
"same-side-duplicate",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n"
|
|
"INSTANCE-A.0010, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n"));
|
|
|
|
ASSERT_TRUE(result.HasValue());
|
|
ASSERT_EQ(OnlySurface(result.Value()).facets.size(), 1U);
|
|
ASSERT_EQ(result.Value().Warnings().size(), 1U);
|
|
const auto& warning = result.Value().Warnings().front();
|
|
EXPECT_EQ(warning.severity, fesa::Severity::kWarning);
|
|
EXPECT_EQ(warning.code, "duplicate-surface-member-normalized");
|
|
EXPECT_EQ(warning.location.line, 29U);
|
|
EXPECT_EQ(warning.keyword, "SURFACE");
|
|
EXPECT_EQ(warning.entity_identity, "Pressure-Surface");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput, RejectsConflictingSidesForOneElement) {
|
|
ExpectSingleFailure(
|
|
MapText("conflicting-sides",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n"
|
|
"Instance-A.10, SNEG\n",
|
|
"Pressure-Surface, P, 1.0\n")),
|
|
"conflicting-surface-membership", 29U, "SURFACE");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
RejectsNonfiniteDExponentAndLocaleDependentMagnitudes) {
|
|
ExpectSingleFailure(
|
|
MapText("dsload-nan",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, NaN\n")),
|
|
"invalid-numeric-value", 37U, "DSLOAD");
|
|
ExpectSingleFailure(
|
|
MapText("dsload-d-exponent",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0D+03\n")),
|
|
"invalid-numeric-value", 37U, "DSLOAD");
|
|
ExpectSingleFailure(
|
|
MapText("dsload-locale",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1,25\n")),
|
|
"invalid-data-arity", 37U, "DSLOAD");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
PreservesAdditiveRepeatedPressureRowsWithoutDeduplication) {
|
|
const auto result = MapText(
|
|
"repeated-dsload",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n"
|
|
"pressure-surface, p, 1.0\n"));
|
|
|
|
ASSERT_TRUE(result.HasValue());
|
|
const auto& loads = result.Value().Steps()[0U].SurfacePressureLoads();
|
|
ASSERT_EQ(loads.size(), 2U);
|
|
EXPECT_EQ(loads[0].load_ordinal, 0U);
|
|
EXPECT_EQ(loads[1].load_ordinal, 1U);
|
|
EXPECT_EQ(loads[0].data_ordinal, 0U);
|
|
EXPECT_EQ(loads[1].data_ordinal, 1U);
|
|
EXPECT_DOUBLE_EQ(loads[0].magnitude, loads[1].magnitude);
|
|
}
|
|
|
|
TEST(ShellSurfacePressureDomain,
|
|
MappingFailureCommitsNoPartialSemanticCandidate) {
|
|
const auto result = MapText(
|
|
"atomic-failure",
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n"
|
|
"Instance-A.10, SNEG\n",
|
|
"Pressure-Surface, P, 1.0\n"));
|
|
|
|
ASSERT_FALSE(result.HasValue());
|
|
EXPECT_EQ(result.GetStatus().Diagnostics().front().code,
|
|
"conflicting-surface-membership");
|
|
}
|
|
|
|
TEST(ShellSurfacePressureInput,
|
|
EmitsDiagnosticsInStableSourceOrderAcrossRepeatedRuns) {
|
|
const std::string deck =
|
|
ShellDeck("*SURFACE, TYPE=ELEMENT, NAME=Pressure-Surface\n"
|
|
"Instance-A.10, SPOS\n"
|
|
"INSTANCE-A.0010, SPOS\n",
|
|
"Pressure-Surface, P, 1.0\n");
|
|
const auto first = MapText("stable", deck);
|
|
const auto second = MapText("stable", deck);
|
|
|
|
ASSERT_TRUE(first.HasValue());
|
|
ASSERT_TRUE(second.HasValue());
|
|
ASSERT_EQ(first.Value().Warnings().size(), 1U);
|
|
ASSERT_EQ(second.Value().Warnings().size(), 1U);
|
|
const auto& left = first.Value().Warnings().front();
|
|
const auto& right = second.Value().Warnings().front();
|
|
EXPECT_EQ(left.severity, right.severity);
|
|
EXPECT_EQ(left.code, right.code);
|
|
EXPECT_EQ(left.location.file, right.location.file);
|
|
EXPECT_EQ(left.location.line, right.location.line);
|
|
EXPECT_EQ(left.keyword, right.keyword);
|
|
EXPECT_EQ(left.entity_identity, right.entity_identity);
|
|
EXPECT_EQ(left.message, right.message);
|
|
}
|