feat: add eigen2 frequency pipeline and fail-closed validation
Implement MITC4 mass, constrained whitening, MKL FEAST, modal HDF5, and deterministic Value comparison. MSVC Debug builds; 394 of 395 tests pass. Original-model all-c residual and the documented S3 static contract conflict remain unresolved; this is an implementation checkpoint, not feature acceptance.
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <hdf5.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "fesa/app/fesa_application.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class EigenApplicationTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
static std::atomic<unsigned long long> sequence{0U};
|
||||
folder_ =
|
||||
std::filesystem::temp_directory_path() /
|
||||
("fesa-eigen-app-" +
|
||||
std::to_string(
|
||||
std::chrono::steady_clock::now().time_since_epoch().count()) +
|
||||
"-" + std::to_string(sequence.fetch_add(1U)));
|
||||
ASSERT_TRUE(std::filesystem::create_directory(folder_));
|
||||
input_ = folder_ / "Panel.Modes.inp";
|
||||
output_ = folder_ / "Panel.Modes_results.h5";
|
||||
log_ = folder_ / "Panel.Modes_log.txt";
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
std::error_code ignored;
|
||||
std::filesystem::remove_all(folder_, ignored);
|
||||
}
|
||||
|
||||
void WriteDeck(const bool empty = false) {
|
||||
std::ofstream file{input_};
|
||||
file << R"inp(*Part, name=Panel
|
||||
*Node
|
||||
11,0,0,0
|
||||
22,1,0,0
|
||||
33,1,1,0
|
||||
44,0,1,0
|
||||
*Element,type=S4
|
||||
71,11,22,33,44
|
||||
*Elset,elset=Shell
|
||||
71
|
||||
*Shell Section,elset=Shell,material=Steel
|
||||
0.5
|
||||
*End Part
|
||||
*Assembly,name=Assembly
|
||||
*Instance,name=Panel-1,part=Panel
|
||||
*End Instance
|
||||
*Nset,nset=Fixed,instance=Panel-1
|
||||
11,22,33
|
||||
*Nset,nset=Tip,instance=Panel-1
|
||||
44
|
||||
*End Assembly
|
||||
*Material,name=Steel
|
||||
*Density
|
||||
1.
|
||||
*Elastic
|
||||
12.,0.
|
||||
*Boundary
|
||||
Fixed,1,6
|
||||
Tip,1,2
|
||||
Tip,4,6
|
||||
)inp";
|
||||
if (empty) file << "Tip,3,3\n";
|
||||
file << R"inp(*Step,name=Step-2,nlgeom=NO,perturbation
|
||||
*Frequency,eigensolver=Lanczos,normalization=mass
|
||||
1
|
||||
*End Step
|
||||
)inp";
|
||||
ASSERT_TRUE(file.good());
|
||||
}
|
||||
|
||||
std::filesystem::path folder_, input_, output_, log_;
|
||||
};
|
||||
|
||||
TEST_F(EigenApplicationTest, DispatchesFrequencyAndWritesModalSibling) {
|
||||
WriteDeck();
|
||||
ASSERT_EQ(fesa::FesaApplication{}.Run({input_.string()}), 0);
|
||||
ASSERT_TRUE(std::filesystem::exists(output_));
|
||||
ASSERT_TRUE(std::filesystem::exists(log_));
|
||||
EXPECT_EQ(std::filesystem::file_size(log_), 0U);
|
||||
const hid_t file =
|
||||
H5Fopen(output_.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
|
||||
ASSERT_GE(file, 0);
|
||||
EXPECT_GT(H5Lexists(file, "/steps/Step-1/eigen/modes", H5P_DEFAULT), 0);
|
||||
EXPECT_GT(H5Lexists(file, "/steps/Step-1/eigen/vectors", H5P_DEFAULT), 0);
|
||||
EXPECT_EQ(H5Lexists(file, "/steps/Step-1/frames", H5P_DEFAULT), 0);
|
||||
EXPECT_GE(H5Fclose(file), 0);
|
||||
}
|
||||
|
||||
TEST_F(EigenApplicationTest, EmptyPencilPreservesPreviousFinalAndEmptyLog) {
|
||||
WriteDeck(true);
|
||||
const std::string original = "previous authoritative file";
|
||||
{
|
||||
std::ofstream previous{output_, std::ios::binary};
|
||||
previous << original;
|
||||
}
|
||||
EXPECT_EQ(fesa::FesaApplication{}.Run({input_.string()}), 4);
|
||||
std::ifstream previous{output_, std::ios::binary};
|
||||
EXPECT_TRUE(std::string(std::istreambuf_iterator<char>{previous}, {}) ==
|
||||
original);
|
||||
EXPECT_EQ(std::filesystem::file_size(log_), 0U);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user