Preserve empty MITC4 hidden-constraint boundary
This commit is contained in:
@@ -64,16 +64,6 @@ bool Within(const double value, const double scale, const double tolerance) {
|
|||||||
Status Validate(const Domain& domain, const DofManager& dofs,
|
Status Validate(const Domain& domain, const DofManager& dofs,
|
||||||
const DynamicAssembly& assembly, const double length) {
|
const DynamicAssembly& assembly, const double length) {
|
||||||
const std::size_t count = dofs.FullDofCount();
|
const std::size_t count = dofs.FullDofCount();
|
||||||
if (assembly.frame.mitc4_drilling &&
|
|
||||||
assembly.frame.mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring) {
|
|
||||||
if (assembly.hidden_constraint.Size() != assembly.rotations.size())
|
|
||||||
return Failure(domain, "dynamic-invalid-algebra-state",
|
|
||||||
"Canonical hidden values must cover every node.");
|
|
||||||
for (std::size_t i = 0U; i < assembly.hidden_constraint.Size(); ++i)
|
|
||||||
if (!std::isfinite(assembly.hidden_constraint[i]))
|
|
||||||
return Failure(domain, "dynamic-invalid-algebra-state",
|
|
||||||
"Canonical hidden values must remain finite.");
|
|
||||||
}
|
|
||||||
if (!std::isfinite(length) || !(length > 0.0) || count % 6U != 0U ||
|
if (!std::isfinite(length) || !(length > 0.0) || count % 6U != 0U ||
|
||||||
assembly.rotations.size() != count / 6U) {
|
assembly.rotations.size() != count / 6U) {
|
||||||
return Failure(domain, "dynamic-invalid-algebra-state",
|
return Failure(domain, "dynamic-invalid-algebra-state",
|
||||||
@@ -559,6 +549,16 @@ Status HiddenEvidence(const Domain& domain, const DynamicAssembly& assembly,
|
|||||||
const bool require_acceleration) {
|
const bool require_acceleration) {
|
||||||
const bool direct = frame->mitc4_drilling &&
|
const bool direct = frame->mitc4_drilling &&
|
||||||
frame->mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring;
|
frame->mitc4_drilling->mode == Mitc4DrillingMode::kDirectSpring;
|
||||||
|
if (direct) {
|
||||||
|
for (const std::size_t node : nodes) {
|
||||||
|
if (node >= assembly.hidden_constraint.Size())
|
||||||
|
return Failure(domain, "dynamic-invalid-algebra-state",
|
||||||
|
"Canonical hidden values must cover every admissible null node.");
|
||||||
|
if (!std::isfinite(assembly.hidden_constraint[node]))
|
||||||
|
return Failure(domain, "dynamic-invalid-algebra-state",
|
||||||
|
"Canonical hidden values must remain finite.");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vector psi{nodes.size()};
|
Vector psi{nodes.size()};
|
||||||
Vector psi_scale{nodes.size()};
|
Vector psi_scale{nodes.size()};
|
||||||
Vector velocity_defect{nodes.size()};
|
Vector velocity_defect{nodes.size()};
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ struct Fixture {
|
|||||||
Fixture MakeFixture(const std::size_t free_nodes = 1U,
|
Fixture MakeFixture(const std::size_t free_nodes = 1U,
|
||||||
const bool partial_rotation = false,
|
const bool partial_rotation = false,
|
||||||
const bool translations_free = true,
|
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{};
|
fesa::ModelDefinition definition{};
|
||||||
definition.source_path = "hht-algebra.inp";
|
definition.source_path = "hht-algebra.inp";
|
||||||
definition.nodes = {
|
definition.nodes = {
|
||||||
@@ -66,7 +67,10 @@ Fixture MakeFixture(const std::size_t free_nodes = 1U,
|
|||||||
step.boundaries.push_back(
|
step.boundaries.push_back(
|
||||||
{name, 1, 6, 0.0, {definition.source_path, 10U}});
|
{name, 1, 6, 0.0, {definition.source_path, 10U}});
|
||||||
} else {
|
} 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(
|
step.boundaries.push_back(
|
||||||
{name, 4, 5, 0.0, {definition.source_path, 10U}});
|
{name, 4, 5, 0.0, {definition.source_path, 10U}});
|
||||||
if (!translations_free)
|
if (!translations_free)
|
||||||
@@ -705,3 +709,57 @@ TEST(HhtEndpointFields, MeasuresActualFullChartAcrossNonlinearRootAndPartialBoun
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1938,4 +1938,24 @@ TEST(Mitc4DrillingEvidence, DirectHiddenCertificateUsesCanonicalPrimitive) {
|
|||||||
EXPECT_DOUBLE_EQ(frame.hidden_velocity_defect[row], 0.0);
|
EXPECT_DOUBLE_EQ(frame.hidden_velocity_defect[row], 0.0);
|
||||||
EXPECT_DOUBLE_EQ(frame.hidden_acceleration_defect[row], 0.0);
|
EXPECT_DOUBLE_EQ(frame.hidden_acceleration_defect[row], 0.0);
|
||||||
}
|
}
|
||||||
|
const auto accepted = frame;
|
||||||
|
for (const bool missing : {true, false}) {
|
||||||
|
auto invalid = assembly.Value();
|
||||||
|
if (missing)
|
||||||
|
invalid.hidden_constraint = fesa::Vector(0U);
|
||||||
|
else
|
||||||
|
invalid.hidden_constraint[0U] = std::numeric_limits<double>::infinity();
|
||||||
|
const auto rejected = fesa::hht_internal::CompleteConstraintEvidence(
|
||||||
|
*fixture.domain, fixture.dofs, invalid, length.Value(), null_nodes,
|
||||||
|
positive, tangent, &frame);
|
||||||
|
EXPECT_FALSE(rejected.IsOk());
|
||||||
|
for (const auto& pair : {
|
||||||
|
std::make_pair(&frame.hidden_constraint, &accepted.hidden_constraint),
|
||||||
|
std::make_pair(&frame.hidden_velocity_defect, &accepted.hidden_velocity_defect),
|
||||||
|
std::make_pair(&frame.hidden_acceleration_defect, &accepted.hidden_acceleration_defect)}) {
|
||||||
|
ASSERT_EQ(pair.first->Size(), pair.second->Size());
|
||||||
|
for (std::size_t row = 0U; row < pair.first->Size(); ++row)
|
||||||
|
ExpectSameBits((*pair.first)[row], (*pair.second)[row]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user