Files
FESADev/tests/unit/assembly/surface_pressure_assembler_test.cpp
T

196 lines
7.4 KiB
C++

#include "fesa/assembly/surface_pressure_assembler.h"
#include <gtest/gtest.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/model/domain.h"
namespace {
struct PressureFixture {
std::unique_ptr<fesa::Domain> domain;
std::unique_ptr<fesa::AnalysisModel> model;
std::unique_ptr<fesa::DofManager> dofs;
};
PressureFixture MakeFixture(const std::vector<double>& magnitudes) {
const std::filesystem::path source{"surface-pressure-assembly.inp"};
fesa::ModelDefinition definition{};
definition.source_path = source;
definition.source_content_identity = "fnv1a64:surfacepressure";
definition.nodes = {
{{"Shell-1", 1, "1"}, {0.0, 0.0, 0.0}, {source, 2U}},
{{"Shell-1", 2, "2"}, {1.0, 0.0, 0.0}, {source, 3U}},
{{"Shell-1", 3, "3"}, {1.0, 1.0, 0.0}, {source, 4U}},
{{"Shell-1", 4, "4"}, {0.0, 1.0, 0.0}, {source, 5U}}};
definition.materials = {{"Steel", 1000.0, 0.25, {source, 6U}}};
definition.shell_sections = {{"Shell", 0.1, 0U, {source, 7U}}};
definition.shell_elements = {{{"Shell-1", 1, "1"},
fesa::ShellSourceElementType::kS4,
{0U, 1U, 2U, 3U},
0U,
0U,
{source, 8U}}};
for (std::size_t node = 0U; node < definition.nodes.size(); ++node) {
definition.shell_node_initial_frames.push_back(
{static_cast<fesa::EntityIndex>(node),
{0.0, 0.0, 1.0},
{1.0, 0.0, 0.0},
{0.0, 1.0, 0.0}});
}
definition.element_surfaces = {
{"Pressure-Surface",
"PRESSURE-SURFACE",
{{0U,
0U,
"ShellPart",
"SHELLPART",
"Shell-1",
"SHELL-1",
1,
"1",
"S4",
fesa::ShellSurfaceSide::kSpos,
{source, 12U},
0U,
fesa::SurfaceMemberKind::kDirect,
"",
""}},
{source, 11U},
0U}};
std::vector<fesa::SurfacePressureLoadDefinition> pressure_loads;
pressure_loads.reserve(magnitudes.size());
for (std::size_t ordinal = 0U; ordinal < magnitudes.size(); ++ordinal) {
pressure_loads.push_back({"Step-1",
"STEP-1",
0U,
"Pressure-Surface",
"PRESSURE-SURFACE",
"P",
magnitudes[ordinal],
std::to_string(magnitudes[ordinal]),
{source, 20U + ordinal},
{source, 21U + ordinal},
ordinal,
ordinal});
}
definition.steps.emplace_back(
"Step-1", std::vector<fesa::PrescribedDisplacementDefinition>{},
std::vector<fesa::NodalLoad>{}, std::move(pressure_loads),
fesa::StaticProcedureKind::kLinearSmallDisplacement, false, 0.1, 1.0,
0.01, 1.0, fesa::SourceLocation{source, 19U});
auto domain_result = fesa::Domain::Create(std::move(definition));
if (!domain_result.HasValue()) {
throw std::runtime_error{"Unable to construct pressure Domain fixture."};
}
auto domain =
std::make_unique<fesa::Domain>(std::move(domain_result.Value()));
auto model_result = fesa::AnalysisModel::Create(*domain);
if (!model_result.HasValue()) {
throw std::runtime_error{"Unable to construct pressure AnalysisModel."};
}
auto model =
std::make_unique<fesa::AnalysisModel>(std::move(model_result.Value()));
auto dofs_result = fesa::DofManager::Create(*model);
if (!dofs_result.HasValue()) {
throw std::runtime_error{"Unable to construct pressure DofManager."};
}
auto dofs =
std::make_unique<fesa::DofManager>(std::move(dofs_result.Value()));
return {std::move(domain), std::move(model), std::move(dofs)};
}
std::vector<double> Values(const fesa::Vector& vector) {
return {vector.Data(), vector.Data() + vector.Size()};
}
} // namespace
TEST(SurfacePressureAssembly, AssemblesFullDofsInPressureFacetAndRecordOrder) {
auto fixture = MakeFixture({2.0});
fesa::Vector coordinates{fixture.dofs->FullDofCount()};
coordinates[2U] = 11.0;
fesa::SerialParallelFor serial;
const auto result = fesa::AssembleSurfacePressure(
*fixture.model, *fixture.dofs, coordinates, 1.0,
fesa::SurfacePressureConfiguration::kReference, serial);
ASSERT_TRUE(result.HasValue());
EXPECT_TRUE(result.Value().diagnostics.empty());
ASSERT_EQ(result.Value().full_external_force.Size(), 24U);
for (std::size_t node = 0U; node < 4U; ++node) {
const std::size_t offset = node * 6U;
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset], 0.0);
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset + 1U], 0.0);
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset + 2U], -0.5);
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset + 3U], 0.0);
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset + 4U], 0.0);
EXPECT_DOUBLE_EQ(result.Value().full_external_force[offset + 5U], 0.0);
}
EXPECT_EQ(result.Value().full_external_tangent.Rows(), 24U);
EXPECT_EQ(result.Value().full_external_tangent.Columns(), 24U);
for (const double value : result.Value().full_external_tangent.Values()) {
EXPECT_EQ(value, 0.0);
}
}
TEST(SurfacePressureAssembly,
AddsRepeatedRecordsAndAppliesUnitFactorExactlyOnce) {
auto fixture = MakeFixture({6.0, 2.0});
fesa::Vector coordinates{fixture.dofs->FullDofCount()};
fesa::SerialParallelFor serial;
const auto result = fesa::AssembleSurfacePressure(
*fixture.model, *fixture.dofs, coordinates, 0.5,
fesa::SurfacePressureConfiguration::kReference, serial);
ASSERT_TRUE(result.HasValue());
for (std::size_t node = 0U; node < 4U; ++node) {
EXPECT_DOUBLE_EQ(result.Value().full_external_force[node * 6U + 2U],
-1.0);
}
}
TEST(SurfacePressureAssembly,
IsBitwiseStableForSerialAndParallelLocalEvaluation) {
auto fixture = MakeFixture({1.0e16, -1.0e16, 3.5});
fesa::Vector coordinates{fixture.dofs->FullDofCount()};
fesa::SerialParallelFor serial;
fesa::TbbParallelFor parallel;
const auto serial_result = fesa::AssembleSurfacePressure(
*fixture.model, *fixture.dofs, coordinates, 1.0,
fesa::SurfacePressureConfiguration::kReference, serial);
const auto parallel_result = fesa::AssembleSurfacePressure(
*fixture.model, *fixture.dofs, coordinates, 1.0,
fesa::SurfacePressureConfiguration::kReference, parallel);
ASSERT_TRUE(serial_result.HasValue());
ASSERT_TRUE(parallel_result.HasValue());
EXPECT_EQ(Values(serial_result.Value().full_external_force),
Values(parallel_result.Value().full_external_force));
EXPECT_EQ(serial_result.Value().full_external_tangent.RowOffsets(),
parallel_result.Value().full_external_tangent.RowOffsets());
EXPECT_EQ(serial_result.Value().full_external_tangent.ColumnIndices(),
parallel_result.Value().full_external_tangent.ColumnIndices());
EXPECT_EQ(serial_result.Value().full_external_tangent.Values(),
parallel_result.Value().full_external_tangent.Values());
EXPECT_TRUE(serial_result.Value().diagnostics.empty());
EXPECT_TRUE(parallel_result.Value().diagnostics.empty());
}