#include "fesa/app/fesa_application.h" #include #include #include #include #include #include #include #include #include namespace { class TempDirectory { public: explicit TempDirectory(const std::string& label) { static std::atomic sequence{0U}; const auto tick = std::chrono::steady_clock::now().time_since_epoch().count(); path_ = std::filesystem::temp_directory_path() / ("fesa-step10-nonlinear-" + label + "-" + std::to_string(tick) + "-" + std::to_string(sequence.fetch_add(1U))); std::error_code error; if (!std::filesystem::create_directory(path_, error) || error) { throw std::runtime_error{"Unable to create nonlinear procedure fixture."}; } } TempDirectory(const TempDirectory&) = delete; TempDirectory& operator=(const TempDirectory&) = delete; ~TempDirectory() { std::error_code ignored; std::filesystem::remove_all(path_, ignored); } const std::filesystem::path& Path() const noexcept { return path_; } private: std::filesystem::path path_; }; void WriteText(const std::filesystem::path& path, const std::string& text) { std::ofstream stream{path, std::ios::binary | std::ios::trunc}; stream.write(text.data(), static_cast(text.size())); if (!stream) { throw std::runtime_error{"Unable to write nonlinear procedure input."}; } } std::string AllConstrainedNonlinearShellDeck() { return R"inp(*Part, name=ShellPart *Node 1, 0., 0., 0. 2, 1., 0., 0. 3, 1., 1., 0. 4, 0., 1., 0. *Element, type=S4 1, 1, 2, 3, 4 *Elset, elset=ShellSet 1 *Shell Section, elset=ShellSet, material=Steel 0.1 *End Part *Assembly, name=Assembly *Instance, name=Shell-1, part=ShellPart *End Instance *Nset, nset=All, instance=Shell-1 1, 2, 3, 4 *End Assembly *Material, name=Steel *Elastic 1000., 0.25 *Boundary All, 1, 6 *Step, name=Step-1, nlgeom=YES *Static 0.1, 1., 0.001, 0.1 *End Step )inp"; } class Hdf5File { public: explicit Hdf5File(const std::filesystem::path& path) : handle_{H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)} { if (handle_ < 0) { throw std::runtime_error{"Unable to open nonlinear results HDF5."}; } } Hdf5File(const Hdf5File&) = delete; Hdf5File& operator=(const Hdf5File&) = delete; ~Hdf5File() { (void)H5Fclose(handle_); } hid_t Get() const noexcept { return handle_; } private: hid_t handle_; }; std::string ReadStringAttribute(const hid_t object, const char* name) { const hid_t attribute = H5Aopen(object, name, H5P_DEFAULT); const hid_t type = attribute < 0 ? -1 : H5Aget_type(attribute); if (attribute < 0 || type < 0) { if (type >= 0) { (void)H5Tclose(type); } if (attribute >= 0) { (void)H5Aclose(attribute); } throw std::runtime_error{"Unable to read nonlinear metadata attribute."}; } char* value = nullptr; const herr_t read = H5Aread(attribute, type, &value); const std::string result = read >= 0 && value != nullptr ? value : ""; if (value != nullptr) { (void)H5free_memory(value); } (void)H5Tclose(type); (void)H5Aclose(attribute); if (read < 0) { throw std::runtime_error{"Unable to read nonlinear metadata text."}; } return result; } // NSNR-PROC-004, NSNR-PROC-007, and NSNR-PROC-010. TEST(NonlinearStaticProcedure, AllConstrainedNlgeomRunWritesSchemaV1AfterAcceptedFrames) { TempDirectory directory{"schema-v1"}; const auto input = directory.Path() / "shellnl.inp"; const auto output = directory.Path() / "shellnl_results.h5"; WriteText(input, AllConstrainedNonlinearShellDeck()); testing::internal::CaptureStderr(); const int exit_code = fesa::FesaApplication{}.Run({input.string()}); const std::string standard_error = testing::internal::GetCapturedStderr(); ASSERT_EQ(exit_code, 0) << standard_error; ASSERT_TRUE(std::filesystem::exists(output)); ASSERT_GT(H5Fis_hdf5(output.string().c_str()), 0); const Hdf5File file{output}; const hid_t metadata = H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT); ASSERT_GE(metadata, 0); EXPECT_EQ(ReadStringAttribute(metadata, "feature_id"), "nonlinear-static-newton-raphson"); (void)H5Gclose(metadata); EXPECT_GT(H5Lexists(file.Get(), "/steps/Step-1/accepted_frames", H5P_DEFAULT), 0); } } // namespace