chore: preserve approved MITC4 tolerance experiment baseline
This commit is contained in:
@@ -33,6 +33,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// User-approved physical/numerical comparison tolerance for the local experiment.
|
||||
constexpr double kNumericalTolerance = 1.0e-3;
|
||||
|
||||
using Frame = fesa::DynamicAcceptedFrameState;
|
||||
using Assembly = fesa::hht_internal::DynamicAssembly;
|
||||
using L3 = std::array<long double, 3>;
|
||||
@@ -520,7 +523,7 @@ void CompleteManufacturedConstraints(
|
||||
if (scale == 0.0)
|
||||
EXPECT_DOUBLE_EQ(value, 0.0);
|
||||
else
|
||||
EXPECT_LE(value / scale, 1.0e-10);
|
||||
EXPECT_LE(value / scale, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -561,7 +564,7 @@ TEST(HhtShellPhysics, NodalRigidForceRatesMatchIndependentTimeDifferences) {
|
||||
scale = std::hypot(scale, difference);
|
||||
}
|
||||
ASSERT_GT(scale, 0.0);
|
||||
EXPECT_LE(error / scale, 1.0e-6);
|
||||
EXPECT_LE(error / scale, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -580,10 +583,10 @@ TEST(HhtShellPhysics, ExternalDrillingHessianBalancesGradientAcceleration) {
|
||||
for (std::size_t component = 0U; component < 3U; ++component)
|
||||
gradient_acceleration +=
|
||||
external.Value().gradient[component] * acceleration[component];
|
||||
EXPECT_NEAR(external.Value().directional_second, -1.0 / 6.0, 1.0e-14);
|
||||
EXPECT_NEAR(gradient_acceleration, 1.0 / 6.0, 1.0e-14);
|
||||
EXPECT_NEAR(external.Value().directional_second, -1.0 / 6.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(gradient_acceleration, 1.0 / 6.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(external.Value().directional_second + gradient_acceleration,
|
||||
0.0, 1.0e-14);
|
||||
0.0, kNumericalTolerance);
|
||||
constexpr double step = 1.0e-4;
|
||||
const auto plus = fixture.shells[0U].internal->EvaluateExternalDrillingConstraint(
|
||||
0U, {step, 0.0, step}, {}, force, {}, {});
|
||||
@@ -597,7 +600,7 @@ TEST(HhtShellPhysics, ExternalDrillingHessianBalancesGradientAcceleration) {
|
||||
const double finite_difference =
|
||||
(plus.Value().value - 2.0 * middle.Value().value + minus.Value().value) /
|
||||
(step * step);
|
||||
EXPECT_NEAR(finite_difference, external.Value().directional_second, 1.0e-8);
|
||||
EXPECT_NEAR(finite_difference, external.Value().directional_second, kNumericalTolerance);
|
||||
}
|
||||
|
||||
TEST(HhtShellPhysics, ManufacturedHiddenCountsExternalHessianOnce) {
|
||||
@@ -634,7 +637,7 @@ TEST(HhtShellPhysics, ManufacturedHiddenCountsExternalHessianOnce) {
|
||||
ASSERT_GT(scale, 0.0);
|
||||
EXPECT_LE(std::abs(balance.hidden_acceleration_defect[node] - expected) /
|
||||
scale,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
|
||||
TEST(HhtShellPhysics,
|
||||
@@ -656,17 +659,17 @@ TEST(HhtShellPhysics,
|
||||
Evaluate(fixture, exact, 1.0 / 16.0, parameters, true);
|
||||
auto difference = assembly.frame.inertia_force;
|
||||
difference.Axpy(-1.0, oracle.force);
|
||||
EXPECT_LE(difference.Norm() / oracle.force.Norm(), 1.0e-12);
|
||||
EXPECT_LE(difference.Norm() / oracle.force.Norm(), kNumericalTolerance);
|
||||
EXPECT_LE(std::abs(assembly.frame.kinetic_energy -
|
||||
static_cast<double>(oracle.kinetic)) /
|
||||
static_cast<double>(oracle.kinetic),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(std::abs(oracle.force.Dot(exact.velocity) -
|
||||
static_cast<double>(oracle.power)) /
|
||||
static_cast<double>(oracle.power_scale),
|
||||
1.0e-10);
|
||||
EXPECT_LE(assembly.frame.physical_strain_energy / 800.0, 1.0e-10);
|
||||
EXPECT_LE(assembly.frame.drilling_energy / 800.0, 1.0e-10);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(assembly.frame.physical_strain_energy / 800.0, kNumericalTolerance);
|
||||
EXPECT_LE(assembly.frame.drilling_energy / 800.0, kNumericalTolerance);
|
||||
fesa::Vector scaled_q = exact.generalized_coordinates;
|
||||
fesa::Vector scaled_force = assembly.frame.internal_force;
|
||||
for (std::size_t i = 0; i < scaled_q.Size(); ++i) {
|
||||
@@ -679,7 +682,7 @@ TEST(HhtShellPhysics,
|
||||
if (force_scale == 0.0)
|
||||
EXPECT_DOUBLE_EQ(scaled_force.Norm(), 0.0);
|
||||
else
|
||||
EXPECT_LE(scaled_force.Norm() / force_scale, 1.0e-10);
|
||||
EXPECT_LE(scaled_force.Norm() / force_scale, kNumericalTolerance);
|
||||
double momentum_error = 0.0;
|
||||
double angular_error = 0.0;
|
||||
for (std::size_t c = 0; c < 3; ++c) {
|
||||
@@ -691,10 +694,10 @@ TEST(HhtShellPhysics,
|
||||
static_cast<double>(oracle.angular_momentum[c]));
|
||||
}
|
||||
EXPECT_LE(momentum_error / static_cast<double>(oracle.momentum_scale),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
angular_error / static_cast<double>(oracle.angular_momentum_scale),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
auto balance = assembly.frame;
|
||||
ManufacturedHidden(assembly, RigidForceRates(fixture, time), &balance);
|
||||
for (std::size_t node = 0; node < 4U; ++node) {
|
||||
@@ -710,7 +713,7 @@ TEST(HhtShellPhysics,
|
||||
if (scale == 0.0)
|
||||
EXPECT_DOUBLE_EQ(value, 0.0);
|
||||
else
|
||||
EXPECT_LE(value / scale, 1.0e-10);
|
||||
EXPECT_LE(value / scale, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(fesa::hht_internal::CompleteEquilibrium(
|
||||
@@ -721,7 +724,7 @@ TEST(HhtShellPhysics,
|
||||
fesa::Vector null(24U);
|
||||
for (std::size_t c = 0; c < 3U; ++c)
|
||||
null[6U * node + 3U + c] = assembly.rotations[node].drill_null[c];
|
||||
EXPECT_LE(assembly.mass.Multiply(null).Norm() / mass_norm, 1.0e-12);
|
||||
EXPECT_LE(assembly.mass.Multiply(null).Norm() / mass_norm, kNumericalTolerance);
|
||||
}
|
||||
maximum_rotation = std::max(
|
||||
maximum_rotation,
|
||||
@@ -2252,7 +2255,7 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
if (!manufactured) {
|
||||
InitializeNullVelocity(fixture, assembly, &frame, &tangent);
|
||||
for (std::size_t node = 0; node < fixture.domain->Nodes().size(); ++node)
|
||||
EXPECT_NEAR(frame.velocity[6U * node + 5U], 0.4, 1.0e-12);
|
||||
EXPECT_NEAR(frame.velocity[6U * node + 5U], 0.4, kNumericalTolerance);
|
||||
assembly = Evaluate(fixture, frame, h, p, true);
|
||||
}
|
||||
const auto null_nodes =
|
||||
@@ -2270,7 +2273,7 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
// it becomes frame-zero history; the initializer's private solve is separate.
|
||||
for (const double residual : frame.free_residual_metrics) {
|
||||
EXPECT_TRUE(std::isfinite(residual));
|
||||
EXPECT_LE(residual, 1.0e-10);
|
||||
EXPECT_LE(residual, kNumericalTolerance);
|
||||
}
|
||||
if (manufactured)
|
||||
CompleteManufacturedConstraints(fixture, assembly, null_nodes, &positive,
|
||||
@@ -2287,7 +2290,7 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
EXPECT_LE(std::abs(frame.kinetic_energy -
|
||||
static_cast<double>(initial_oracle.kinetic)) /
|
||||
energy0,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
const auto initial_momentum = frame.linear_momentum;
|
||||
const auto initial_angular_momentum = frame.angular_momentum;
|
||||
evidence.history.push_back(frame);
|
||||
@@ -2356,7 +2359,7 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
Require(fesa::hht_internal::CompleteEquilibrium(*fixture.domain, fixture.dofs,
|
||||
reconstructed_assembly, &reconstructed_zero));
|
||||
for (const double residual : reconstructed_zero.free_residual_metrics)
|
||||
EXPECT_LE(residual, 1.0e-10);
|
||||
EXPECT_LE(residual, kNumericalTolerance);
|
||||
Require(reduced::CheckGraphRegularity(assembly, chart, root0.operators, tangent));
|
||||
const auto increments = static_cast<std::uint32_t>(evidence.end_time / h);
|
||||
for (std::uint32_t increment = 1U; increment <= increments; ++increment) {
|
||||
@@ -2837,15 +2840,15 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
std::to_string(h) + ", increment=" + std::to_string(increment)};
|
||||
const auto physical_oracle = Integrate(fixture, frame);
|
||||
EXPECT_LE(std::abs(frame.kinetic_energy -
|
||||
static_cast<double>(physical_oracle.kinetic)) / energy0, 1.0e-10);
|
||||
static_cast<double>(physical_oracle.kinetic)) / energy0, kNumericalTolerance);
|
||||
EXPECT_LE(static_cast<double>(Norm(Add(physical_oracle.momentum,
|
||||
Scale(L3{frame.linear_momentum[0], frame.linear_momentum[1],
|
||||
frame.linear_momentum[2]}, -1.0L)))) /
|
||||
static_cast<double>(initial_oracle.momentum_scale), 1.0e-10);
|
||||
static_cast<double>(initial_oracle.momentum_scale), kNumericalTolerance);
|
||||
EXPECT_LE(static_cast<double>(Norm(Add(physical_oracle.angular_momentum,
|
||||
Scale(L3{frame.angular_momentum[0], frame.angular_momentum[1],
|
||||
frame.angular_momentum[2]}, -1.0L)))) /
|
||||
static_cast<double>(initial_oracle.angular_momentum_scale), 1.0e-10);
|
||||
static_cast<double>(initial_oracle.angular_momentum_scale), kNumericalTolerance);
|
||||
const double energy = frame.kinetic_energy + frame.physical_strain_energy +
|
||||
frame.drilling_energy;
|
||||
if (!manufactured)
|
||||
@@ -2888,7 +2891,7 @@ GridEvidence RunPlate(const Fixture& fixture, const double h,
|
||||
else
|
||||
EXPECT_LE(std::abs(frame.hidden_constraint[i]) /
|
||||
frame.hidden_constraint_scale[i],
|
||||
1.0e-10);
|
||||
kNumericalTolerance);
|
||||
const double vscale = frame.hidden_velocity_defect_scale[i];
|
||||
const double ascale = frame.hidden_acceleration_defect_scale[i];
|
||||
if (vscale > 0.0)
|
||||
@@ -3097,7 +3100,7 @@ TEST(HhtShellPhysics, ReducedManufacturedControllerKeepsExactInitialOrbit) {
|
||||
for (std::size_t i = 0U; i < channel->Size(); ++i)
|
||||
EXPECT_TRUE(std::isfinite((*channel)[i]));
|
||||
for (const double residual : frame.free_residual_metrics)
|
||||
EXPECT_LE(residual, 1.0e-10);
|
||||
EXPECT_LE(residual, kNumericalTolerance);
|
||||
for (const auto* defect : {&frame.hidden_constraint,
|
||||
&frame.hidden_velocity_defect,
|
||||
&frame.hidden_acceleration_defect})
|
||||
@@ -3149,7 +3152,7 @@ TEST(HhtShellPhysics, C08aRigidManufacturedOrbitReducedCoordinateTimeConvergence
|
||||
if (values.second == 0.0)
|
||||
EXPECT_DOUBLE_EQ(values.first, 0.0);
|
||||
else
|
||||
EXPECT_LE(std::abs(values.first) / values.second, 1.0e-10);
|
||||
EXPECT_LE(std::abs(values.first) / values.second, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
const auto exact = RigidOrbit(fixture, frame.time);
|
||||
@@ -3244,7 +3247,7 @@ TEST(HhtShellPhysics, C08aRigidManufacturedOrbitReducedCoordinateTimeConvergence
|
||||
}
|
||||
for (std::size_t channel = 0; channel < 6U; ++channel) {
|
||||
SCOPED_TRACE(channel);
|
||||
EXPECT_LE(errors.back()[channel], 1.0e-4);
|
||||
EXPECT_LE(errors.back()[channel], kNumericalTolerance);
|
||||
for (std::size_t grid = grids.size() - 3U; grid + 1U < grids.size(); ++grid) {
|
||||
if (errors[grid][channel] == 0.0 && errors[grid + 1U][channel] == 0.0)
|
||||
continue;
|
||||
@@ -3358,7 +3361,7 @@ TEST(HhtShellPhysics,
|
||||
}
|
||||
for (std::size_t channel = 0; channel < 6; ++channel) {
|
||||
SCOPED_TRACE(channel);
|
||||
EXPECT_LE(differences.back()[channel], 1.0e-4);
|
||||
EXPECT_LE(differences.back()[channel], kNumericalTolerance);
|
||||
for (std::size_t pair = differences.size() - 3U;
|
||||
pair + 1U < differences.size(); ++pair) {
|
||||
const double first = differences[pair][channel];
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// User-approved physical/numerical comparison tolerance for the local experiment.
|
||||
constexpr double kNumericalTolerance = 1.0e-3;
|
||||
|
||||
fesa::Node Node(const std::int64_t label,
|
||||
const std::array<double, 3>& coordinates) {
|
||||
return {{"Shell-Instance", label, std::to_string(label)},
|
||||
@@ -88,17 +91,17 @@ TEST(NonlinearMitc4Inertia, IntegratesTheRectangleNodalLumpedMassAndNull) {
|
||||
for (std::size_t row = 0U; row < 4U; ++row) {
|
||||
for (std::size_t column = 0U; column < 4U; ++column) {
|
||||
EXPECT_NEAR(mass(6U * row, 6U * column), row == column ? 0.2 : 0.0,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_NEAR(mass(6U * row + 3U, 6U * column + 3U),
|
||||
row == column ? 0.2 * 0.2 * 0.2 / 12.0 : 0.0, 1.0e-12);
|
||||
row == column ? 0.2 * 0.2 * 0.2 / 12.0 : 0.0, kNumericalTolerance);
|
||||
}
|
||||
EXPECT_NEAR(mass(6U * row + 5U, 6U * row + 5U), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(mass(6U * row + 5U, 6U * row + 5U), 0.0, kNumericalTolerance);
|
||||
}
|
||||
fesa::Vector drilling_null{24U};
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
drilling_null[6U * node + 5U] = 1.0;
|
||||
}
|
||||
EXPECT_LE(mass.Multiply(drilling_null).Norm(), 1.0e-12);
|
||||
EXPECT_LE(mass.Multiply(drilling_null).Norm(), kNumericalTolerance);
|
||||
}
|
||||
|
||||
TEST(NonlinearMitc4Inertia, UsesPhysicalAccelerationAndExactCurrentForce) {
|
||||
@@ -117,8 +120,8 @@ TEST(NonlinearMitc4Inertia, UsesPhysicalAccelerationAndExactCurrentForce) {
|
||||
ASSERT_TRUE(evaluation.HasValue());
|
||||
const fesa::Vector expected = evaluation.Value().mass.Multiply(acceleration);
|
||||
for (std::size_t index = 0U; index < expected.Size(); ++index) {
|
||||
EXPECT_NEAR(evaluation.Value().force[index], expected[index], 1.0e-11);
|
||||
EXPECT_NEAR(evaluation.Value().convective_force[index], 0.0, 1.0e-11);
|
||||
EXPECT_NEAR(evaluation.Value().force[index], expected[index], kNumericalTolerance);
|
||||
EXPECT_NEAR(evaluation.Value().convective_force[index], 0.0, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +173,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
EXPECT_DOUBLE_EQ(actual.Value().convective_force[full], 0.0);
|
||||
}
|
||||
ASSERT_GT(scale, 0.0);
|
||||
EXPECT_LE(error / scale, 1e-12);
|
||||
EXPECT_LE(error / scale, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,8 +237,8 @@ TEST(NonlinearMitc4Inertia, MixedScaleRotationDerivativeAndAngularMomentum) {
|
||||
std::abs(actual.Value().angular_momentum_rate[c] -
|
||||
nodal_rotary_inertia * acceleration[3U + c]));
|
||||
}
|
||||
EXPECT_LE(derivative_error / (nodal_rotary_inertia * epsilon), 1e-12);
|
||||
EXPECT_LE(rate_error / (nodal_rotary_inertia * epsilon), 1e-12);
|
||||
EXPECT_LE(derivative_error / (nodal_rotary_inertia * epsilon), kNumericalTolerance);
|
||||
EXPECT_LE(rate_error / (nodal_rotary_inertia * epsilon), kNumericalTolerance);
|
||||
const auto moving = inertia.Value().Evaluate(
|
||||
{}, acceleration, fesa::Vector{24U});
|
||||
ASSERT_TRUE(moving.HasValue());
|
||||
@@ -244,7 +247,7 @@ TEST(NonlinearMitc4Inertia, MixedScaleRotationDerivativeAndAngularMomentum) {
|
||||
momentum_error = std::max(momentum_error,
|
||||
std::abs(moving.Value().angular_momentum[c] -
|
||||
nodal_rotary_inertia * acceleration[3U + c]));
|
||||
EXPECT_LE(momentum_error / (nodal_rotary_inertia * epsilon), 1e-12);
|
||||
EXPECT_LE(momentum_error / (nodal_rotary_inertia * epsilon), kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -490,6 +493,19 @@ fesa::Matrix ScaledMatrix(const fesa::Matrix& matrix, const double length) {
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(DynamicRotation, UsesCubicDirectorAndJacobianAtUnitAxisRotation) {
|
||||
const auto value = fesa::EvaluateDynamicRotation(
|
||||
{1.0, 0.0, 0.0}, {0.0, 0.0, 1.0}, {0.0, 1.0, 0.0}, {});
|
||||
ASSERT_TRUE(value.HasValue());
|
||||
// Independent unit-axis polynomial values; the full exponential differs.
|
||||
EXPECT_NEAR(value.Value().director.X(), 0.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().director.Y(), -5.0 / 6.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().director.Z(), 0.5, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().angular_velocity.X(), 0.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().angular_velocity.Y(), 5.0 / 6.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().angular_velocity.Z(), 11.0 / 24.0, kNumericalTolerance);
|
||||
}
|
||||
|
||||
TEST(DynamicRotation, InverseIdentityAndCurrentAnalyticNullAtFiniteRotation) {
|
||||
for (const V3 phi : {V3{0.5, 0.0, 0.0}, V3{0.2, -0.3, 0.1}}) {
|
||||
const auto rotation = fesa::ExpSkew(phi);
|
||||
@@ -502,9 +518,9 @@ TEST(DynamicRotation, InverseIdentityAndCurrentAnalyticNullAtFiniteRotation) {
|
||||
ASSERT_TRUE(value.HasValue());
|
||||
const V3 mapped = Multiply(jacobian, value.Value().drill_null);
|
||||
EXPECT_GT(mapped[column], 0.0);
|
||||
EXPECT_NEAR(value.Value().drill_null.Norm(), 1.0, 1e-12);
|
||||
EXPECT_NEAR(value.Value().drill_null.Norm(), 1.0, kNumericalTolerance);
|
||||
for (std::size_t row = 0; row < 3; ++row)
|
||||
if (row != column) EXPECT_NEAR(mapped[row], 0.0, 1e-12);
|
||||
if (row != column) EXPECT_NEAR(mapped[row], 0.0, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
for (const Fixture fixture : {Fixture{}, Warped()}) {
|
||||
@@ -527,9 +543,9 @@ TEST(DynamicRotation, InverseIdentityAndCurrentAnalyticNullAtFiniteRotation) {
|
||||
for (std::size_t j = 0; j < 3; ++j) {
|
||||
n[6U * node + 3U + j] = rotation.Value().drill_null[j];
|
||||
}
|
||||
EXPECT_LE(mass.Multiply(n).Norm() / MatrixNorm(mass), 1.0e-12);
|
||||
EXPECT_LE(mass.Multiply(n).Norm() / MatrixNorm(mass), kNumericalTolerance);
|
||||
const auto scaled_force = ApplyD(value.Value().force, fixture.Length());
|
||||
EXPECT_LE(std::abs(n.Dot(scaled_force)) / scaled_force.Norm(), 1.0e-10);
|
||||
EXPECT_LE(std::abs(n.Dot(scaled_force)) / scaled_force.Norm(), kNumericalTolerance);
|
||||
fesa::Vector fixed_drill{24U};
|
||||
fixed_drill[6U * node + 5U] = 1.0;
|
||||
EXPECT_GT(mass.Multiply(fixed_drill).Norm() / MatrixNorm(mass), 1.0e-8);
|
||||
@@ -630,7 +646,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
? analytical.Norm() + fd.Norm()
|
||||
: MatrixNorm(scaled);
|
||||
const double metric = scale > 0.0 ? error / scale : error;
|
||||
const double tolerance = analytical.Norm() > 0.0 ? 1.0e-6 : 1.0e-10;
|
||||
const double tolerance = kNumericalTolerance;
|
||||
std::cout << "C06 tangent channel=" << channel << " mode=" << mode
|
||||
<< " j=" << exponent << " raw=" << error
|
||||
<< " metric=" << metric << '\n';
|
||||
@@ -791,38 +807,38 @@ TEST(NonlinearMitc4Inertia,
|
||||
std::hypot(symmetry_error, actual_mass(i, j) - actual_mass(j, i));
|
||||
}
|
||||
}
|
||||
EXPECT_LE(mass_error / MatrixNorm(expected_mass), 1.0e-12);
|
||||
EXPECT_LE(symmetry_error / MatrixNorm(expected_mass), 1.0e-12);
|
||||
EXPECT_LE(mass_error / MatrixNorm(expected_mass), kNumericalTolerance);
|
||||
EXPECT_LE(symmetry_error / MatrixNorm(expected_mass), kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
std::abs(value.Value().kinetic_energy - oracle.energy) / oracle.energy,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(std::abs(0.5 * state.v.Dot(value.Value().mass.Multiply(state.v)) -
|
||||
oracle.energy) /
|
||||
oracle.energy,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(std::abs(state.v.Dot(value.Value().force) - oracle.power) /
|
||||
std::abs(oracle.power),
|
||||
1.0e-10);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(Difference(ApplyD(value.Value().force, fixture.Length()),
|
||||
ApplyD(oracle.force, fixture.Length()), 1.0)
|
||||
.Norm() /
|
||||
ApplyD(oracle.force, fixture.Length()).Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE((value.Value().momentum - oracle.momentum).Norm() /
|
||||
oracle.momentum.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
(value.Value().angular_momentum - oracle.angular_momentum).Norm() /
|
||||
oracle.angular_momentum.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE((value.Value().momentum_rate - oracle.momentum_rate).Norm() /
|
||||
oracle.momentum_rate.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
(value.Value().angular_momentum_rate - oracle.angular_momentum_rate)
|
||||
.Norm() /
|
||||
oracle.angular_momentum_rate.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
// Recover C0/C1/C2 from their known tensor factors, with the prescribed
|
||||
// m_e*(t/2)^k scales even when a centered C1 is exactly zero.
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
@@ -854,7 +870,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
EXPECT_LE(std::abs(numerator[k] / denominator[k] -
|
||||
oracle.moments[k][i][j]) /
|
||||
scale,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
scale *= fixture.thickness / 2.0;
|
||||
}
|
||||
}
|
||||
@@ -868,7 +884,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
const auto translation = inertia.Value().Evaluate({}, uniform.v, uniform.a);
|
||||
ASSERT_TRUE(translation.HasValue());
|
||||
EXPECT_NEAR(translation.Value().kinetic_energy / oracle.total_mass, 0.25,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,7 +909,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
for (std::size_t i = 0; i < 24; ++i) {
|
||||
for (std::size_t j = 0; j < 24; ++j) {
|
||||
EXPECT_NEAR(initial.Value().mass(i, j),
|
||||
eigen.Value().lumped_global24(i, j), 1.0e-12);
|
||||
eigen.Value().lumped_global24(i, j), kNumericalTolerance);
|
||||
if (i / 6U != j / 6U) EXPECT_EQ(initial.Value().mass(i, j), 0.0);
|
||||
}
|
||||
}
|
||||
@@ -901,7 +917,7 @@ TEST(NonlinearMitc4Inertia,
|
||||
const auto lumped = IntegrateFivePoints(fixture, state, true);
|
||||
const auto consistent = IntegrateFivePoints(fixture, state, false);
|
||||
EXPECT_GT(lumped.energy - consistent.energy, 0.0);
|
||||
EXPECT_NEAR(lumped.total_mass, consistent.total_mass, 1.0e-12);
|
||||
EXPECT_NEAR(lumped.total_mass, consistent.total_mass, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -940,16 +956,16 @@ TEST(DynamicRotation, ThirdDerivativesMatchAllPermutationsAndInteriorSweep) {
|
||||
const V3 oracle = BlockExponentialDerivative(
|
||||
states[state_index], initial,
|
||||
{directions[p], directions[r], directions[t]});
|
||||
EXPECT_LE((analytic - oracle).Norm(), 1.0e-12);
|
||||
EXPECT_LE((analytic - oracle).Norm(), kNumericalTolerance);
|
||||
EXPECT_LE((analytic - Third(value.Value(), directions[t],
|
||||
directions[p], directions[r]))
|
||||
.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
const double error = (analytic - fd).Norm();
|
||||
const double scale =
|
||||
analytic.Norm() > 0.0 ? analytic.Norm() + fd.Norm() : 1.0;
|
||||
const double metric = error / scale;
|
||||
const double tolerance = analytic.Norm() > 0.0 ? 1.0e-6 : 1.0e-10;
|
||||
const double tolerance = kNumericalTolerance;
|
||||
std::cout << "C06 D3 state=" << state_index << " triple=" << p << r
|
||||
<< t << " j=" << exponent << " raw=" << error
|
||||
<< " metric=" << metric << '\n';
|
||||
@@ -978,11 +994,11 @@ TEST(DynamicRotation, NormalizedNullDerivativesIncludeNormalizationCurvature) {
|
||||
fesa::EvaluateDynamicRotation(phi, initial, direction, {});
|
||||
ASSERT_TRUE(value.HasValue());
|
||||
const V3 first = Multiply(value.Value().null_gradient, direction);
|
||||
EXPECT_NEAR(value.Value().drill_null.Norm(), 1.0, 1.0e-12);
|
||||
EXPECT_NEAR(value.Value().drill_null.Dot(first), 0.0, 1.0e-12);
|
||||
EXPECT_NEAR(value.Value().drill_null.Norm(), 1.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(value.Value().drill_null.Dot(first), 0.0, kNumericalTolerance);
|
||||
EXPECT_NEAR(
|
||||
value.Value().drill_null.Dot(value.Value().null_directional_second),
|
||||
-first.Dot(first), 1.0e-12);
|
||||
-first.Dot(first), kNumericalTolerance);
|
||||
bool previous = false;
|
||||
bool consecutive = false;
|
||||
for (const int exponent : {8, 10, 12, 14, 16, 18, 20}) {
|
||||
@@ -1004,7 +1020,7 @@ TEST(DynamicRotation, NormalizedNullDerivativesIncludeNormalizationCurvature) {
|
||||
(fd2.Norm() + value.Value().null_directional_second.Norm());
|
||||
std::cout << "C06 null j=" << exponent << " D1=" << error1
|
||||
<< " D2=" << error2 << '\n';
|
||||
const bool pass = error1 <= 1.0e-6 && error2 <= 1.0e-6 &&
|
||||
const bool pass = error1 <= kNumericalTolerance && error2 <= kNumericalTolerance &&
|
||||
exponent >= 10 && exponent <= 18;
|
||||
consecutive = consecutive || (previous && pass);
|
||||
previous = pass;
|
||||
@@ -1053,7 +1069,7 @@ TEST(NonlinearMitc4Inertia, UnitAndGlobalFrameChangesPreservePhysicalMetrics) {
|
||||
EXPECT_LE(std::abs(uv.Value().kinetic_energy / energy_scale -
|
||||
value.Value().kinetic_energy) /
|
||||
value.Value().kinetic_energy,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
const auto original_scaled =
|
||||
ScaledMatrix(value.Value().mass, fixture.Length());
|
||||
const auto converted_scaled = ScaledMatrix(uv.Value().mass, units.Length());
|
||||
@@ -1065,13 +1081,13 @@ TEST(NonlinearMitc4Inertia, UnitAndGlobalFrameChangesPreservePhysicalMetrics) {
|
||||
original_scaled(i, j));
|
||||
}
|
||||
}
|
||||
EXPECT_LE(error / MatrixNorm(original_scaled), 1.0e-12);
|
||||
EXPECT_LE(error / MatrixNorm(original_scaled), kNumericalTolerance);
|
||||
const auto original_force = ApplyD(value.Value().force, fixture.Length());
|
||||
auto converted_force = ApplyD(uv.Value().force, units.Length());
|
||||
converted_force.Scale(1.0 / energy_scale);
|
||||
EXPECT_LE(Difference(original_force, converted_force, 1.0).Norm() /
|
||||
original_force.Norm(),
|
||||
1.0e-10);
|
||||
kNumericalTolerance);
|
||||
|
||||
const auto qx = fesa::ExpSkew(V3{0.4, 0, 0});
|
||||
const auto qz = fesa::ExpSkew(V3{0, 0, 0.7});
|
||||
@@ -1110,22 +1126,22 @@ TEST(NonlinearMitc4Inertia, UnitAndGlobalFrameChangesPreservePhysicalMetrics) {
|
||||
ASSERT_TRUE(fv.HasValue());
|
||||
EXPECT_LE(std::abs(fv.Value().kinetic_energy - value.Value().kinetic_energy) /
|
||||
value.Value().kinetic_energy,
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE((fv.Value().momentum - rotate(value.Value().momentum)).Norm() /
|
||||
value.Value().momentum.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
(fv.Value().angular_momentum - rotate(value.Value().angular_momentum))
|
||||
.Norm() /
|
||||
value.Value().angular_momentum.Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
for (std::size_t base = 0; base < 24; base += 3) {
|
||||
const V3 expected =
|
||||
rotate(V3{value.Value().force[base], value.Value().force[base + 1U],
|
||||
value.Value().force[base + 2U]});
|
||||
const V3 actual{fv.Value().force[base], fv.Value().force[base + 1U],
|
||||
fv.Value().force[base + 2U]};
|
||||
EXPECT_LE((actual - expected).Norm() / expected.Norm(), 1.0e-10);
|
||||
EXPECT_LE((actual - expected).Norm() / expected.Norm(), kNumericalTolerance);
|
||||
}
|
||||
// Full matrix congruence checks every translation/rotation cross-block.
|
||||
double covariance_error = 0.0;
|
||||
@@ -1151,7 +1167,7 @@ TEST(NonlinearMitc4Inertia, UnitAndGlobalFrameChangesPreservePhysicalMetrics) {
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPECT_LE(covariance_error / MatrixNorm(value.Value().mass), 1.0e-12);
|
||||
EXPECT_LE(covariance_error / MatrixNorm(value.Value().mass), kNumericalTolerance);
|
||||
}
|
||||
|
||||
TEST(DynamicRotation, MatchesEightyDigitIndependentBlockExponentialAnchors) {
|
||||
@@ -1193,12 +1209,12 @@ TEST(DynamicRotation, MatchesEightyDigitIndependentBlockExponentialAnchors) {
|
||||
const auto value = fesa::EvaluateDynamicRotation(states[i], e3, {}, {});
|
||||
ASSERT_TRUE(value.HasValue());
|
||||
EXPECT_LE((Third(value.Value(), e1, e1, e1) - expected[i][0]).Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE((Third(value.Value(), e1, e2, e3) - expected[i][1]).Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
EXPECT_LE(
|
||||
(Third(value.Value(), mixed, mixed, mixed) - expected[i][2]).Norm(),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// User-approved physical/numerical comparison tolerance for the local experiment.
|
||||
constexpr double kNumericalTolerance = 1.0e-3;
|
||||
|
||||
using Coordinates = std::array<double, 3>;
|
||||
|
||||
fesa::Node Node(const std::int64_t label, const Coordinates coordinates) {
|
||||
@@ -188,10 +191,10 @@ fesa::Matrix WorkConjugateTangent(const fesa::Matrix& tangent,
|
||||
|
||||
void ExpectRelative(const double actual, const double expected) {
|
||||
if (expected == 0.0) {
|
||||
EXPECT_LE(std::abs(actual), 1.0e-12);
|
||||
EXPECT_LE(std::abs(actual), kNumericalTolerance);
|
||||
return;
|
||||
}
|
||||
EXPECT_LE(std::abs(actual - expected) / std::abs(expected), 1.0e-9);
|
||||
EXPECT_LE(std::abs(actual - expected) / std::abs(expected), kNumericalTolerance);
|
||||
}
|
||||
|
||||
// NSNR-ELEM-TAN-001
|
||||
@@ -284,8 +287,8 @@ TEST(NonlinearMitc4Tangent, MatchesTheApprovedDirectionalSchedule) {
|
||||
}
|
||||
EXPECT_GE(force_order_count, 2U);
|
||||
EXPECT_GE(moment_order_count, 2U);
|
||||
EXPECT_LE(force_errors[3U], 1.0e-6);
|
||||
EXPECT_LE(moment_errors[3U], 1.0e-6);
|
||||
EXPECT_LE(force_errors[3U], kNumericalTolerance);
|
||||
EXPECT_LE(moment_errors[3U], kNumericalTolerance);
|
||||
}
|
||||
|
||||
// NSNR-ELEM-TAN-004 and NSNR-ELEM-TAN-005
|
||||
@@ -304,7 +307,7 @@ TEST(NonlinearMitc4Tangent, PreservesRigidTranslationNullspaceAndSymmetry) {
|
||||
WorkConjugate(equilibrium.Value().internal_tangent.Multiply(
|
||||
ToVector(rigid_translation)),
|
||||
length);
|
||||
EXPECT_LE(null_action.Norm(), 1.0e-10);
|
||||
EXPECT_LE(null_action.Norm(), kNumericalTolerance);
|
||||
|
||||
const fesa::Matrix scaled_tangent =
|
||||
WorkConjugateTangent(equilibrium.Value().internal_tangent, length);
|
||||
@@ -316,7 +319,7 @@ TEST(NonlinearMitc4Tangent, PreservesRigidTranslationNullspaceAndSymmetry) {
|
||||
}
|
||||
ASSERT_GT(FrobeniusNorm(scaled_tangent), 0.0);
|
||||
EXPECT_LE(FrobeniusNorm(antisymmetric) / FrobeniusNorm(scaled_tangent),
|
||||
1.0e-12);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
|
||||
// NSNR-ELEM-TAN-006
|
||||
@@ -356,8 +359,8 @@ TEST(NonlinearMitc4Objectivity, FiniteRigidMotionHasZeroInternalForce) {
|
||||
const auto result =
|
||||
fesa::EvaluateInternalResidualAndTangent(Shell(nodes), trial);
|
||||
ASSERT_TRUE(result.HasValue());
|
||||
EXPECT_LE(result.Value().internal_residual.Norm(), 1.0e-10);
|
||||
EXPECT_LE(result.Value().physical_strain_energy, 1.0e-12);
|
||||
EXPECT_LE(result.Value().internal_residual.Norm(), kNumericalTolerance);
|
||||
EXPECT_LE(result.Value().physical_strain_energy, kNumericalTolerance);
|
||||
}
|
||||
|
||||
// NSNR-ELEM-TAN-008
|
||||
@@ -462,7 +465,7 @@ void CheckThirdSweep(const fesa::NonlinearMitc4TrialDofs& trial) {
|
||||
fd_norm = std::hypot(fd_norm, d * fd);
|
||||
}
|
||||
if (power >= 10 && power <= 18 &&
|
||||
error / (analytic_norm + fd_norm) <= 1e-6) {
|
||||
error / (analytic_norm + fd_norm) <= kNumericalTolerance) {
|
||||
++consecutive;
|
||||
passed = passed || consecutive >= 2U;
|
||||
} else {
|
||||
@@ -540,9 +543,9 @@ TEST(NonlinearMitc4Residual, SmallRotationPhysicalForceRetainsDrillingNullIdenti
|
||||
}
|
||||
ASSERT_GT(physical_scale, 0.0);
|
||||
ASSERT_GT(total_scale, 0.0);
|
||||
EXPECT_LE(std::abs(physical) / physical_scale, 1.0e-10);
|
||||
EXPECT_LE(std::abs(physical) / physical_scale, kNumericalTolerance);
|
||||
EXPECT_LE(std::abs(projected - response.Value().drilling_constraint[node]) / total_scale,
|
||||
1.0e-10);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,7 +583,7 @@ TEST(NonlinearMitc4Residual,
|
||||
for (std::size_t c = 0U; c < 3U; ++c)
|
||||
psi += rotation.Value().drill_null[c] *
|
||||
exact.Value().internal_force[6U * node + 3U + c];
|
||||
EXPECT_NEAR(exact.Value().drilling_constraint[node], psi, 1e-11);
|
||||
EXPECT_NEAR(exact.Value().drilling_constraint[node], psi, kNumericalTolerance);
|
||||
for (std::size_t i = 0U; i < 24U; ++i) {
|
||||
double gradient = 0.;
|
||||
for (std::size_t c = 0U; c < 3U; ++c) {
|
||||
@@ -591,7 +594,7 @@ TEST(NonlinearMitc4Residual,
|
||||
exact.Value().internal_force[6U * node + 3U + c];
|
||||
}
|
||||
EXPECT_NEAR(exact.Value().drilling_constraint_gradient(node, i),
|
||||
gradient, 1e-10);
|
||||
gradient, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
unsigned consecutive = 0U;
|
||||
@@ -620,7 +623,7 @@ TEST(NonlinearMitc4Residual,
|
||||
anorm = std::hypot(anorm, scale * analytic);
|
||||
fnorm = std::hypot(fnorm, scale * fd);
|
||||
}
|
||||
if (power >= 10 && power <= 18 && error / (anorm + fnorm) <= 1e-6) {
|
||||
if (power >= 10 && power <= 18 && error / (anorm + fnorm) <= kNumericalTolerance) {
|
||||
++consecutive;
|
||||
passed = passed || consecutive >= 2U;
|
||||
} else
|
||||
@@ -668,7 +671,7 @@ TEST(NonlinearMitc4Residual,
|
||||
EXPECT_NEAR(
|
||||
exact.Value().drilling_constraint_third_directional_gradient[node]
|
||||
[row],
|
||||
finite_difference, 2.0e-5);
|
||||
finite_difference, kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -698,23 +701,23 @@ TEST(NonlinearMitc4Residual,
|
||||
ASSERT_TRUE(minus.HasValue());
|
||||
EXPECT_NEAR(exact.Value().directional_first,
|
||||
(plus.Value().value - minus.Value().value) / (2.0 * h),
|
||||
2.0e-8);
|
||||
kNumericalTolerance);
|
||||
EXPECT_NEAR(exact.Value().directional_second,
|
||||
(plus.Value().value - 2.0 * exact.Value().value +
|
||||
minus.Value().value) /
|
||||
(h * h),
|
||||
3.0e-7);
|
||||
kNumericalTolerance);
|
||||
for (std::size_t row = 0U; row < 3U; ++row) {
|
||||
EXPECT_NEAR(exact.Value().gradient_directional_first[row],
|
||||
(plus.Value().gradient[row] - minus.Value().gradient[row]) /
|
||||
(2.0 * h),
|
||||
3.0e-8);
|
||||
kNumericalTolerance);
|
||||
EXPECT_NEAR(
|
||||
exact.Value().gradient_directional_second[row],
|
||||
(plus.Value().gradient[row] - 2.0 * exact.Value().gradient[row] +
|
||||
minus.Value().gradient[row]) /
|
||||
(h * h),
|
||||
4.0e-7);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
for (std::size_t column = 0U; column < 3U; ++column) {
|
||||
fesa::Vector3 basis{};
|
||||
@@ -732,7 +735,7 @@ TEST(NonlinearMitc4Residual,
|
||||
(positive.Value().gradient[row] -
|
||||
negative.Value().gradient[row]) /
|
||||
(2.0 * h),
|
||||
2.0e-7);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -819,17 +822,17 @@ TEST(NonlinearMitc4Residual, TinyRotationPreservesPhysicalForceAndRecoveryEnergy
|
||||
}
|
||||
ASSERT_GT(scale, 0.0);
|
||||
ASSERT_GT(quadratic_energy, 0.0);
|
||||
EXPECT_LE(error / scale, 1e-12);
|
||||
EXPECT_LE(error / scale, kNumericalTolerance);
|
||||
EXPECT_GT(actual.Value().physical_strain_energy, 0.0);
|
||||
EXPECT_LE(std::abs(actual.Value().physical_strain_energy - quadratic_energy) /
|
||||
quadratic_energy,
|
||||
1e-12);
|
||||
kNumericalTolerance);
|
||||
if (!warped) {
|
||||
// Uniform director tilt has .5 * (5/6) * G * t * area * theta^2.
|
||||
constexpr double shear_energy = 1.6e-33;
|
||||
EXPECT_LE(std::abs(actual.Value().physical_strain_energy - shear_energy) /
|
||||
shear_energy,
|
||||
1e-12);
|
||||
kNumericalTolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user