#include "fesa/assembly/surface_pressure_assembler.h" #include #include #include #include #include #include #include #include #include #include #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 domain; std::unique_ptr model; std::unique_ptr dofs; }; PressureFixture MakeFixture(const std::vector& 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(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 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{}, std::vector{}, 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(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(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(std::move(dofs_result.Value())); return {std::move(domain), std::move(model), std::move(dofs)}; } std::vector 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()); }