feat(abaqus-subset-completion): step 4 — step-bc-load-and-noop-directives
This commit is contained in:
+24
-2
@@ -156,8 +156,6 @@ add_test(
|
||||
COMMAND "$<TARGET_FILE:fesa_abaqus_parser_tests>"
|
||||
--gtest_filter=AbaqusInputContract/*
|
||||
)
|
||||
# Steps 1-4 make this normative matrix pass, then remove WILL_FAIL.
|
||||
set_tests_properties(AbaqusInputContract PROPERTIES WILL_FAIL TRUE)
|
||||
|
||||
add_test(
|
||||
NAME SetResolution
|
||||
@@ -237,6 +235,30 @@ add_test(
|
||||
--gtest_filter=ActiveInstance.*
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME StepMapping
|
||||
COMMAND "$<TARGET_FILE:fesa_deck_to_domain_tests>"
|
||||
--gtest_filter=StepMapping.*
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME Boundary
|
||||
COMMAND "$<TARGET_FILE:fesa_deck_to_domain_tests>"
|
||||
--gtest_filter=Boundary.*
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME Cload
|
||||
COMMAND "$<TARGET_FILE:fesa_deck_to_domain_tests>"
|
||||
--gtest_filter=Cload.*
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME SuppliedCantilever
|
||||
COMMAND "$<TARGET_FILE:fesa_deck_to_domain_tests>"
|
||||
--gtest_filter=SuppliedCantilever.*
|
||||
)
|
||||
|
||||
add_executable(fesa_fem_primitives_tests
|
||||
unit/fem/beam_frame_test.cpp
|
||||
unit/fem/dof_manager_test.cpp
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
|
||||
@@ -64,6 +65,14 @@ bool has_diagnostic(
|
||||
});
|
||||
}
|
||||
|
||||
const fesa::Diagnostic* find_diagnostic(
|
||||
const fesa::DomainBuildResult& result,
|
||||
const std::string_view code) {
|
||||
const auto found = std::ranges::find(
|
||||
result.diagnostics, code, &fesa::Diagnostic::code);
|
||||
return found == result.diagnostics.end() ? nullptr : &*found;
|
||||
}
|
||||
|
||||
void expect_equivalent_analysis_data(
|
||||
const fesa::Domain& flat,
|
||||
const fesa::Domain& hierarchical) {
|
||||
@@ -265,4 +274,212 @@ TEST(ActiveInstance, RejectsMultipleInstances) {
|
||||
EXPECT_EQ(diagnostic->source->line, 6U);
|
||||
}
|
||||
|
||||
TEST(StepMapping, MapsOneStaticStepAndRejectsUnsupportedConfigurations) {
|
||||
const auto valid =
|
||||
parse_and_map(fixture_path("valid/noop_directives.inp"));
|
||||
|
||||
ASSERT_TRUE(valid.domain.has_value());
|
||||
EXPECT_TRUE(valid.diagnostics.empty());
|
||||
EXPECT_EQ(valid.domain->step().name, "Step-1");
|
||||
|
||||
struct InvalidCase final {
|
||||
std::string_view name;
|
||||
std::string_view contents;
|
||||
std::string_view code;
|
||||
std::size_t line;
|
||||
};
|
||||
const InvalidCase invalid_cases[]{
|
||||
{
|
||||
"fesa-step-multiple.inp",
|
||||
"*STEP, NAME=First\n"
|
||||
"*STATIC\n"
|
||||
"*END STEP\n"
|
||||
"*STEP, NAME=Second\n"
|
||||
"*STATIC\n"
|
||||
"*END STEP\n",
|
||||
"abaqus.semantic.step_count",
|
||||
4U,
|
||||
},
|
||||
{
|
||||
"fesa-step-nlgeom.inp",
|
||||
"*STEP, NLGEOM=YES\n"
|
||||
"*STATIC\n"
|
||||
"*END STEP\n",
|
||||
"abaqus.semantic.unsupported_step_option",
|
||||
1U,
|
||||
},
|
||||
{
|
||||
"fesa-static-invalid.inp",
|
||||
"*STEP\n"
|
||||
"*STATIC\n"
|
||||
"1.0, -1.0\n"
|
||||
"*END STEP\n",
|
||||
"abaqus.semantic.invalid_static_data",
|
||||
3U,
|
||||
},
|
||||
{
|
||||
"fesa-static-extra-row.inp",
|
||||
"*STEP\n"
|
||||
"*STATIC\n"
|
||||
"1.0\n"
|
||||
"2.0\n"
|
||||
"*END STEP\n",
|
||||
"abaqus.semantic.invalid_static_data",
|
||||
4U,
|
||||
},
|
||||
};
|
||||
|
||||
for (const InvalidCase& test_case : invalid_cases) {
|
||||
const TemporaryDeck input{test_case.name, test_case.contents};
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
SCOPED_TRACE(test_case.name);
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, test_case.code);
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, test_case.line);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(StepMapping, RejectsModelDataInsideStep) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-node-inside-step.inp",
|
||||
"*STEP\n"
|
||||
"*NODE\n"
|
||||
"1, 0.0, 0.0, 0.0\n"
|
||||
"*END STEP\n"};
|
||||
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, "abaqus.syntax.invalid_node_scope");
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, 2U);
|
||||
}
|
||||
|
||||
TEST(StepMapping, RejectsBoundaryAfterCompletedStep) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-boundary-after-step.inp",
|
||||
"*STEP\n"
|
||||
"*STATIC\n"
|
||||
"*END STEP\n"
|
||||
"*BOUNDARY\n"
|
||||
"1, 1\n"};
|
||||
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, "abaqus.syntax.invalid_boundary_scope");
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, 4U);
|
||||
}
|
||||
|
||||
TEST(Boundary, CanonicalizesIdenticalGlobalAndStepPrescriptions) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-boundary-canonical.inp",
|
||||
"*NODE\n"
|
||||
"1, 0.0, 0.0, 0.0\n"
|
||||
"*BOUNDARY\n"
|
||||
"1, 1, 3, 2.5\n"
|
||||
"*STEP\n"
|
||||
"*STATIC\n"
|
||||
"*BOUNDARY\n"
|
||||
"1, 1, 3, 2.5\n"
|
||||
"*END STEP\n"};
|
||||
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
ASSERT_TRUE(result.domain.has_value());
|
||||
EXPECT_TRUE(result.diagnostics.empty());
|
||||
const auto& prescribed = result.domain->step().prescribed_dofs;
|
||||
ASSERT_EQ(prescribed.size(), 3U);
|
||||
for (std::size_t index = 0; index < prescribed.size(); ++index) {
|
||||
EXPECT_EQ(prescribed[index].node, fesa::NodeId{0});
|
||||
EXPECT_EQ(prescribed[index].dof, index + 1U);
|
||||
EXPECT_DOUBLE_EQ(prescribed[index].value, 2.5);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Boundary, ReportsConflictAtTheConflictingDataRow) {
|
||||
const auto result =
|
||||
parse_and_map(fixture_path("invalid/boundary_conflict.inp"));
|
||||
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, "abaqus.semantic.conflicting_boundary");
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, 10U);
|
||||
}
|
||||
|
||||
TEST(Cload, SumsForceAndMomentComponentsInInputOrder) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-cload-sum.inp",
|
||||
"*NODE\n"
|
||||
"1, 0.0, 0.0, 0.0\n"
|
||||
"*STEP, NAME=Load\n"
|
||||
"*STATIC\n"
|
||||
"*CLOAD\n"
|
||||
"1, 1, 2.0\n"
|
||||
"1, 1, -0.5\n"
|
||||
"1, 6, 4.0\n"
|
||||
"*END STEP\n"};
|
||||
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
ASSERT_TRUE(result.domain.has_value());
|
||||
EXPECT_TRUE(result.diagnostics.empty());
|
||||
ASSERT_EQ(result.domain->step().nodal_loads.size(), 1U);
|
||||
const auto& load = result.domain->step().nodal_loads.front();
|
||||
EXPECT_EQ(load.node, fesa::NodeId{0});
|
||||
EXPECT_EQ(
|
||||
load.values,
|
||||
(std::array<double, 6>{1.5, 0.0, 0.0, 0.0, 0.0, 4.0}));
|
||||
}
|
||||
|
||||
TEST(Cload, ReportsInvalidDofAtTheDataRow) {
|
||||
const auto result =
|
||||
parse_and_map(fixture_path("invalid/cload_invalid_dof.inp"));
|
||||
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, "abaqus.semantic.invalid_dof");
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, 6U);
|
||||
}
|
||||
|
||||
TEST(SuppliedCantilever, NormalizesReferenceModelThroughPublicParserAndMapper) {
|
||||
const std::filesystem::path path =
|
||||
std::filesystem::path{FESA_TEST_SOURCE_DIR}.parent_path() /
|
||||
"reference" / "cantilever beam" / "cantilever beam.inp";
|
||||
|
||||
const auto result = parse_and_map(path);
|
||||
|
||||
ASSERT_TRUE(result.domain.has_value());
|
||||
EXPECT_TRUE(result.diagnostics.empty());
|
||||
const fesa::Domain& domain = *result.domain;
|
||||
EXPECT_EQ(domain.nodes().size(), 11U);
|
||||
EXPECT_EQ(domain.beam_elements().size(), 10U);
|
||||
EXPECT_EQ(domain.step().prescribed_dofs.size(), 6U);
|
||||
ASSERT_EQ(domain.step().nodal_loads.size(), 1U);
|
||||
|
||||
const auto node = std::ranges::find_if(
|
||||
domain.nodes(),
|
||||
[](const fesa::Node& candidate) {
|
||||
return candidate.origin.local_label == 11;
|
||||
});
|
||||
ASSERT_NE(node, domain.nodes().end());
|
||||
EXPECT_EQ(domain.step().nodal_loads.front().node, node->id);
|
||||
EXPECT_EQ(
|
||||
domain.step().nodal_loads.front().values,
|
||||
(std::array<double, 6>{0.0, 0.0, -10000.0, 0.0, 0.0, 0.0}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user