#include #include #include #include #include #include #include #include #include #include namespace { fesa::DofManager build_dofs( std::vector prescribed) { fesa::DomainBuilder builder; builder.add_node({ fesa::NodeId{0}, fesa::EntityOrigin{"", "", 1}, fesa::Vec3{0.0, 0.0, 0.0}, }); builder.set_step({"Load", std::move(prescribed), {}}); auto result = std::move(builder).build(); if (!result.domain.has_value()) { throw std::runtime_error{"Test Domain failed validation."}; } return fesa::DofManager::build(*result.domain); } fesa::EquationSystem hand_system() { return { { 6, {0, 3, 5, 6, 7, 8, 9}, {0, 1, 2, 1, 2, 2, 3, 4, 5}, {4.0, 1.0, 2.0, 3.0, 6.0, 5.0, 1.0, 1.0, 1.0}, }, {7.0, 8.0, 9.0, 0.0, 0.0, 0.0}, }; } TEST(EssentialBc, ZeroPrescribedValueDoesNotShiftReducedForce) { const std::vector prescribed{ {fesa::NodeId{0}, 2, 0.0}, }; const fesa::DofManager dofs = build_dofs(prescribed); const fesa::ConstraintResult result = fesa::eliminate_essential_bcs(hand_system(), dofs, prescribed); ASSERT_TRUE(result.reduced_system.has_value()); EXPECT_TRUE(result.diagnostics.empty()); EXPECT_EQ( result.reduced_system->force, (std::vector{7.0, 9.0, 0.0, 0.0, 0.0})); } TEST( ConstraintElimination, ShiftsNonzeroPrescribedValueAndPreservesOriginalSystem) { const std::vector prescribed{ {fesa::NodeId{0}, 2, 2.0}, }; const fesa::DofManager dofs = build_dofs(prescribed); const fesa::EquationSystem original = hand_system(); const fesa::EquationSystem before = original; const fesa::ConstraintResult result = fesa::eliminate_essential_bcs(original, dofs, prescribed); ASSERT_TRUE(result.reduced_system.has_value()); EXPECT_TRUE(result.diagnostics.empty()); const fesa::ReducedSystem& reduced = *result.reduced_system; EXPECT_EQ(reduced.stiffness.order, 5); EXPECT_EQ( reduced.stiffness.row_offsets, (std::vector{0, 2, 3, 4, 5, 6})); EXPECT_EQ( reduced.stiffness.column_indices, (std::vector{0, 1, 1, 2, 3, 4})); EXPECT_EQ( reduced.stiffness.values, (std::vector{4.0, 2.0, 5.0, 1.0, 1.0, 1.0})); EXPECT_EQ( reduced.force, (std::vector{5.0, -3.0, 0.0, 0.0, 0.0})); EXPECT_EQ( reduced.free_to_full, (std::vector{0, 2, 3, 4, 5})); EXPECT_EQ( reduced.prescribed_full, (std::vector{0.0, 2.0, 0.0, 0.0, 0.0, 0.0})); EXPECT_EQ(original.stiffness.order, before.stiffness.order); EXPECT_EQ( original.stiffness.row_offsets, before.stiffness.row_offsets); EXPECT_EQ( original.stiffness.column_indices, before.stiffness.column_indices); EXPECT_EQ(original.stiffness.values, before.stiffness.values); EXPECT_EQ(original.force, before.force); EXPECT_EQ( dofs.reconstruct_full( std::vector{1.0, 3.0, 0.0, 0.0, 0.0}), (std::vector{1.0, 2.0, 3.0, 0.0, 0.0, 0.0})); } TEST(ConstraintElimination, AcceptsAllDofsConstrained) { const std::vector prescribed{ {fesa::NodeId{0}, 1, 1.0}, {fesa::NodeId{0}, 2, 2.0}, {fesa::NodeId{0}, 3, 3.0}, {fesa::NodeId{0}, 4, 4.0}, {fesa::NodeId{0}, 5, 5.0}, {fesa::NodeId{0}, 6, 6.0}, }; const fesa::DofManager dofs = build_dofs(prescribed); const fesa::ConstraintResult result = fesa::eliminate_essential_bcs(hand_system(), dofs, prescribed); ASSERT_TRUE(result.reduced_system.has_value()); const fesa::ReducedSystem& reduced = *result.reduced_system; EXPECT_EQ(reduced.stiffness.order, 0); EXPECT_EQ( reduced.stiffness.row_offsets, (std::vector{0})); EXPECT_TRUE(reduced.stiffness.column_indices.empty()); EXPECT_TRUE(reduced.stiffness.values.empty()); EXPECT_TRUE(reduced.force.empty()); EXPECT_TRUE(reduced.free_to_full.empty()); EXPECT_EQ( reduced.prescribed_full, (std::vector{1.0, 2.0, 3.0, 4.0, 5.0, 6.0})); EXPECT_EQ( dofs.reconstruct_full({}), reduced.prescribed_full); } TEST(ConstraintElimination, RejectsConflictingPrescribedValues) { const fesa::DofManager dofs = build_dofs({ {fesa::NodeId{0}, 2, 2.0}, }); const std::vector conflicting{ {fesa::NodeId{0}, 2, 2.0}, {fesa::NodeId{0}, 2, 3.0}, }; const fesa::ConstraintResult result = fesa::eliminate_essential_bcs( hand_system(), dofs, conflicting); EXPECT_FALSE(result.reduced_system.has_value()); ASSERT_FALSE(result.diagnostics.empty()); EXPECT_NE( std::ranges::find( result.diagnostics, "equation.conflicting_prescribed_dof", &fesa::Diagnostic::code), result.diagnostics.end()); } TEST(Reaction, UsesOriginalFullEquilibriumEquation) { const std::vector reaction = fesa::recover_reaction( hand_system(), std::vector{1.0, 2.0, 3.0, 0.0, 0.0, 0.0}); EXPECT_EQ( reaction, (std::vector{5.0, 17.0, 20.0, 0.0, 0.0, 0.0})); } } // namespace