chore: preserve approved MITC4 tolerance experiment baseline

This commit is contained in:
KOKO\Mimi
2026-10-18 23:42:54 +09:00
parent 43867d0b3d
commit d7f6d0ce74
13 changed files with 630 additions and 110 deletions
@@ -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);
}
}
}