196 lines
5.7 KiB
C++
196 lines
5.7 KiB
C++
#include "fesa/app/fesa_application.h"
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <hdf5.h>
|
|
|
|
namespace {
|
|
|
|
class TempDirectory {
|
|
public:
|
|
explicit TempDirectory(const std::string& label) {
|
|
static std::atomic<unsigned long long> sequence{0U};
|
|
const auto tick =
|
|
std::chrono::steady_clock::now().time_since_epoch().count();
|
|
path_ = std::filesystem::temp_directory_path() /
|
|
("fesa-surface-pressure-app-" + 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 application 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<std::streamsize>(text.size()));
|
|
if (!stream) {
|
|
throw std::runtime_error{"Unable to write application input."};
|
|
}
|
|
}
|
|
|
|
std::vector<char> ReadBytes(const std::filesystem::path& path) {
|
|
std::ifstream stream{path, std::ios::binary};
|
|
return {std::istreambuf_iterator<char>{stream},
|
|
std::istreambuf_iterator<char>{}};
|
|
}
|
|
|
|
std::string ShellDeck(const bool with_pressure, const bool collapsed) {
|
|
const std::string third_node = collapsed ? "3, 1., 0., 0.\n"
|
|
: "3, 1., 1., 0.\n";
|
|
const std::string surface = with_pressure
|
|
? "*Surface, TYPE=ELEMENT, NAME=Pressure\n"
|
|
"Shell-1.1, SPOS\n"
|
|
: "";
|
|
const std::string dsload = with_pressure
|
|
? "*Dsload\nPressure, P, 4.0\n"
|
|
: "";
|
|
return std::string{R"inp(*Part, name=ShellPart
|
|
*Node
|
|
1, 0., 0., 0.
|
|
2, 1., 0., 0.
|
|
)inp"} +
|
|
third_node + R"inp(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
|
|
)inp" +
|
|
surface + R"inp(*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=Load, nlgeom=NO
|
|
*Static
|
|
0.1, 1., 0.01, 1.
|
|
)inp" +
|
|
dsload + "*End Step\n";
|
|
}
|
|
|
|
std::string NonlinearShellDeck() {
|
|
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";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST(ShellSurfacePressureApplication,
|
|
LinearFailurePreservesExistingFinalAndNoCandidate) {
|
|
TempDirectory directory{"preserve-final"};
|
|
const auto input = directory.Path() / "invalid-pressure.inp";
|
|
const auto output = directory.Path() / "invalid-pressure_results.h5";
|
|
WriteText(input, ShellDeck(true, true));
|
|
const std::vector<char> previous{'p', 'r', 'i', 'o', 'r'};
|
|
{
|
|
std::ofstream stream{output, std::ios::binary | std::ios::trunc};
|
|
stream.write(previous.data(),
|
|
static_cast<std::streamsize>(previous.size()));
|
|
}
|
|
|
|
const int exit_code = fesa::FesaApplication{}.Run({input.string()});
|
|
|
|
EXPECT_NE(exit_code, 0);
|
|
EXPECT_EQ(ReadBytes(output), previous);
|
|
}
|
|
|
|
TEST(ShellSurfacePressureApplication,
|
|
InputWithoutPressureRetainsExistingLinearBehavior) {
|
|
TempDirectory directory{"no-pressure"};
|
|
const auto input = directory.Path() / "no-pressure.inp";
|
|
const auto output = directory.Path() / "no-pressure_results.h5";
|
|
WriteText(input, ShellDeck(false, false));
|
|
|
|
EXPECT_EQ(fesa::FesaApplication{}.Run({input.string()}), 0);
|
|
ASSERT_TRUE(std::filesystem::exists(output));
|
|
ASSERT_GT(H5Fis_hdf5(output.string().c_str()), 0);
|
|
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/frames/0/nodal/displacement",
|
|
H5P_DEFAULT),
|
|
0);
|
|
EXPECT_EQ(H5Lexists(file, "/steps/Step-1/frames/0/surface_pressure",
|
|
H5P_DEFAULT),
|
|
0);
|
|
EXPECT_GE(H5Fclose(file), 0);
|
|
}
|
|
|
|
TEST(ShellSurfacePressureApplication,
|
|
InputWithoutPressureRetainsExistingNonlinearBehavior) {
|
|
TempDirectory directory{"nonlinear-no-pressure"};
|
|
const auto input = directory.Path() / "nonlinear-no-pressure.inp";
|
|
const auto output = directory.Path() / "nonlinear-no-pressure_results.h5";
|
|
WriteText(input, NonlinearShellDeck());
|
|
|
|
EXPECT_EQ(fesa::FesaApplication{}.Run({input.string()}), 0);
|
|
ASSERT_TRUE(std::filesystem::exists(output));
|
|
ASSERT_GT(H5Fis_hdf5(output.string().c_str()), 0);
|
|
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/accepted_frames", H5P_DEFAULT), 0);
|
|
EXPECT_EQ(H5Lexists(file, "/surface_pressure", H5P_DEFAULT), 0);
|
|
EXPECT_GE(H5Fclose(file), 0);
|
|
}
|