Preserve empty MITC4 hidden-constraint boundary

This commit is contained in:
KOKO\Mimi
2026-10-20 09:27:58 +09:00
parent c6188b3ad7
commit df1bf94369
3 changed files with 90 additions and 12 deletions
@@ -28,7 +28,8 @@ struct Fixture {
Fixture MakeFixture(const std::size_t free_nodes = 1U,
const bool partial_rotation = false,
const bool translations_free = true,
const double youngs_modulus = 1000.0) {
const double youngs_modulus = 1000.0,
const bool all_rotations_constrained = false) {
fesa::ModelDefinition definition{};
definition.source_path = "hht-algebra.inp";
definition.nodes = {
@@ -66,7 +67,10 @@ Fixture MakeFixture(const std::size_t free_nodes = 1U,
step.boundaries.push_back(
{name, 1, 6, 0.0, {definition.source_path, 10U}});
} else {
if (partial_rotation)
if (all_rotations_constrained)
step.boundaries.push_back(
{name, 4, 6, 0.0, {definition.source_path, 10U}});
else if (partial_rotation)
step.boundaries.push_back(
{name, 4, 5, 0.0, {definition.source_path, 10U}});
if (!translations_free)
@@ -705,3 +709,57 @@ TEST(HhtEndpointFields, MeasuresActualFullChartAcrossNonlinearRootAndPartialBoun
}
} // namespace
TEST(HhtDynamicAlgebra, NonDirectionalAssemblyRetainsValidEmptyNullBoundary) {
for (const std::size_t free_nodes : {4U, 0U}) {
SCOPED_TRACE(free_nodes);
const auto fixture = MakeFixture(free_nodes, false, true, 1000.0, true);
const auto shells = fesa::hht_internal::BuildRuntimeShells(*fixture.domain,
fixture.dofs);
ASSERT_TRUE(shells.HasValue());
auto trial = EmptyTrial(fixture);
trial.external_force[0U] = 0.4;
fesa::Vector load_scale(24U);
load_scale[0U] = 0.4;
fesa::SerialParallelFor serial;
const auto parameters = fesa::MakeHhtParameters(-0.05);
ASSERT_TRUE(parameters.HasValue());
const auto assembly = fesa::hht_internal::EvaluateAssembly(
*fixture.domain, fixture.dofs, shells.Value(), trial, load_scale,
0.1, parameters.Value(), serial, false);
ASSERT_TRUE(assembly.HasValue());
ASSERT_TRUE(assembly.Value().frame.mitc4_drilling);
EXPECT_EQ(assembly.Value().frame.mitc4_drilling->mode,
fesa::Mitc4DrillingMode::kDirectSpring);
EXPECT_EQ(assembly.Value().hidden_constraint.Size(), 0U);
EXPECT_FALSE(assembly.Value().hidden_gradient.has_value());
const auto nodes = fesa::hht_internal::NullNodes(fixture.dofs,
assembly.Value().rotations);
ASSERT_TRUE(nodes.empty());
fesa::MklPardisoSolver positive;
fesa::MklPardisoTangentSolver tangent;
const auto acceleration = fesa::hht_internal::InitializeAcceleration(
*fixture.domain, fixture.dofs, assembly.Value(), 2.0, positive, tangent);
EXPECT_TRUE(acceleration.HasValue())
<< (acceleration.GetStatus().Diagnostics().empty() ? "" :
acceleration.GetStatus().Diagnostics()[0U].message);
auto frame = assembly.Value().frame;
if (acceleration.HasValue()) {
frame.acceleration = acceleration.Value();
// rho*t*area/4 = .2 per node; the .4 force gives acceleration 2.
EXPECT_NEAR(acceleration.Value()[0U], free_nodes == 0U ? 0.0 : 2.0, 1e-12);
for (const auto full : fixture.dofs.ConstrainedDofs())
EXPECT_DOUBLE_EQ(acceleration.Value()[full], 0.0);
}
const auto complete = fesa::hht_internal::CompleteConstraintEvidence(
*fixture.domain, fixture.dofs, assembly.Value(), 2.0, nodes,
positive, tangent, &frame);
EXPECT_TRUE(complete.IsOk())
<< (complete.Diagnostics().empty() ? "" : complete.Diagnostics()[0U].message);
if (complete.IsOk()) {
EXPECT_EQ(frame.hidden_constraint.Size(), 0U);
EXPECT_EQ(frame.hidden_velocity_defect.Size(), 0U);
EXPECT_EQ(frame.hidden_acceleration_defect.Size(), 0U);
}
}
}