196 lines
7.4 KiB
C++
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());
|
|
}
|