292 lines
8.8 KiB
C++
292 lines
8.8 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <limits>
|
|
#include <string_view>
|
|
#include <utility>
|
|
|
|
#include <fesa/results/result_database.hpp>
|
|
|
|
namespace {
|
|
|
|
std::array<double, 6> zeros() {
|
|
return {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
|
}
|
|
|
|
fesa::ResultDatabase valid_database() {
|
|
fesa::NodalFrame nodal{
|
|
{fesa::NodeId{0}, fesa::NodeId{1}},
|
|
{
|
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 10},
|
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 20},
|
|
},
|
|
{
|
|
zeros(),
|
|
{1.0, 2.0, 3.0, 0.1, 0.2, 0.3},
|
|
},
|
|
{
|
|
{-10.0, -20.0, -30.0, -1.0, -2.0, -3.0},
|
|
zeros(),
|
|
},
|
|
};
|
|
fesa::BeamElementFrame beam{
|
|
fesa::ElementId{3},
|
|
fesa::EntityOrigin{"BeamPart", "Beam-1", 30},
|
|
{
|
|
fesa::Vec3{1.0, 0.0, 0.0},
|
|
fesa::Vec3{0.0, 1.0, 0.0},
|
|
fesa::Vec3{0.0, 0.0, 1.0},
|
|
},
|
|
{{
|
|
{
|
|
-1.0,
|
|
fesa::NodeId{0},
|
|
zeros(),
|
|
zeros(),
|
|
0.0,
|
|
{1.0},
|
|
},
|
|
{
|
|
1.0,
|
|
fesa::NodeId{1},
|
|
zeros(),
|
|
zeros(),
|
|
0.0,
|
|
{2.0},
|
|
},
|
|
}},
|
|
};
|
|
fesa::ResultFrame frame{
|
|
1.0,
|
|
std::move(nodal),
|
|
{{std::move(beam)}},
|
|
{},
|
|
};
|
|
fesa::ResultStep step{"Load", {std::move(frame)}};
|
|
return {"2.0.0", {std::move(step)}};
|
|
}
|
|
|
|
bool has_diagnostic(
|
|
const fesa::Status& status,
|
|
const std::string_view code) {
|
|
return std::ranges::any_of(
|
|
status.diagnostics,
|
|
[code](const fesa::Diagnostic& diagnostic) {
|
|
return diagnostic.code == code &&
|
|
diagnostic.stage == fesa::DiagnosticStage::results &&
|
|
diagnostic.severity == fesa::Severity::error;
|
|
});
|
|
}
|
|
|
|
TEST(NodalFrame, AcceptsFiniteSixComponentFieldsWithMatchingNodeIds) {
|
|
const auto database = valid_database();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_TRUE(status.succeeded);
|
|
EXPECT_TRUE(status.diagnostics.empty());
|
|
}
|
|
|
|
TEST(NodalFrame, RejectsDisplacementSizeMismatch) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.displacement.pop_back();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nodal_size_mismatch"));
|
|
}
|
|
|
|
TEST(NodalFrame, RejectsReactionSizeMismatch) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.reaction.pop_back();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nodal_size_mismatch"));
|
|
}
|
|
|
|
TEST(CompleteResultContract, RejectsNodeOriginSizeMismatch) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.origins.pop_back();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(
|
|
status, "results.nodal_origin_size_mismatch"));
|
|
}
|
|
|
|
TEST(NodalFrame, RejectsDuplicateNodeId) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.node_ids[1] = fesa::NodeId{0};
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.duplicate_node_id"));
|
|
}
|
|
|
|
TEST(NodalFrame, RejectsNonfiniteDisplacement) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.displacement[1][2] =
|
|
std::numeric_limits<double>::quiet_NaN();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value"));
|
|
}
|
|
|
|
TEST(NodalFrame, RejectsNonfiniteReaction) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].nodal.reaction[0][4] =
|
|
std::numeric_limits<double>::infinity();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value"));
|
|
}
|
|
|
|
TEST(ResultDatabase, RejectsDuplicateStepName) {
|
|
auto database = valid_database();
|
|
database.steps.push_back(database.steps[0]);
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.duplicate_step_name"));
|
|
}
|
|
|
|
TEST(ResultDatabase, RejectsDuplicateFrameTimeWithinStep) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames.push_back(database.steps[0].frames[0]);
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.duplicate_frame_time"));
|
|
}
|
|
|
|
TEST(ResultDatabase, RejectsNonfiniteFrameTime) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0].step_time =
|
|
std::numeric_limits<double>::quiet_NaN();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value"));
|
|
}
|
|
|
|
TEST(CompleteResultContract, DeclaresCoordinatesAndComponentOrdering) {
|
|
EXPECT_EQ(
|
|
fesa::NodalFrame::coordinate_system,
|
|
fesa::FieldCoordinateSystem::global);
|
|
EXPECT_EQ(
|
|
fesa::NodalFrame::displacement_components,
|
|
(std::array<std::string_view, 6>{
|
|
"Ux", "Uy", "Uz", "Rx", "Ry", "Rz"}));
|
|
EXPECT_EQ(
|
|
fesa::NodalFrame::reaction_components,
|
|
(std::array<std::string_view, 6>{
|
|
"RFx", "RFy", "RFz", "RMx", "RMy", "RMz"}));
|
|
EXPECT_EQ(
|
|
fesa::BeamElementFrame::coordinate_system,
|
|
fesa::FieldCoordinateSystem::element_local);
|
|
EXPECT_EQ(
|
|
fesa::BeamElementFrame::section_strain_components,
|
|
(std::array<std::string_view, 6>{
|
|
"epsilon", "gamma_y", "gamma_z", "kappa_x", "kappa_y",
|
|
"kappa_z"}));
|
|
EXPECT_EQ(
|
|
fesa::BeamElementFrame::section_force_components,
|
|
(std::array<std::string_view, 6>{
|
|
"N", "Vy", "Vz", "T", "My", "Mz"}));
|
|
EXPECT_EQ(
|
|
fesa::BeamElementFrame::axial_stress_component,
|
|
std::string_view{"sigma_xx"});
|
|
}
|
|
|
|
TEST(ElementFrame, AcceptsFiniteConnectedBeamResults) {
|
|
const auto status = fesa::validate_result_database(valid_database());
|
|
|
|
EXPECT_TRUE(status.succeeded);
|
|
EXPECT_TRUE(status.diagnostics.empty());
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsDuplicateElementId) {
|
|
auto database = valid_database();
|
|
auto& beams = database.steps[0].frames[0].element.beams;
|
|
beams.push_back(beams[0]);
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.duplicate_element_id"));
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsDuplicateEndNode) {
|
|
auto database = valid_database();
|
|
auto& ends = database.steps[0].frames[0]
|
|
.element.beams[0].end_results;
|
|
ends[1].end_node = ends[0].end_node;
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.duplicate_beam_end_node"));
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsWrongEndConnectivity) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0]
|
|
.element.beams[0].end_results[0].xi = 1.0;
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.invalid_beam_connectivity"));
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsEndNodeAbsentFromNodalFrame) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0]
|
|
.element.beams[0].end_results[1].end_node = fesa::NodeId{99};
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.invalid_beam_connectivity"));
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsNonfiniteLocalFrameAndSectionValue) {
|
|
auto database = valid_database();
|
|
auto& beam = database.steps[0].frames[0].element.beams[0];
|
|
beam.local_frame.ey.y = std::numeric_limits<double>::infinity();
|
|
beam.end_results[1].section_force[4] =
|
|
std::numeric_limits<double>::quiet_NaN();
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(status, "results.nonfinite_value"));
|
|
}
|
|
|
|
TEST(ElementFrame, RejectsMismatchedRecoveryPointCount) {
|
|
auto database = valid_database();
|
|
database.steps[0].frames[0]
|
|
.element.beams[0].end_results[1].sigma_xx.push_back(3.0);
|
|
|
|
const auto status = fesa::validate_result_database(database);
|
|
|
|
EXPECT_FALSE(status.succeeded);
|
|
EXPECT_TRUE(has_diagnostic(
|
|
status, "results.recovery_point_count_mismatch"));
|
|
}
|
|
|
|
} // namespace
|