feat: rebuild mitc4 stiffness drilling
This commit is contained in:
@@ -147,6 +147,19 @@ std::array<fesa::Real, 24> zeroElementDofs() {
|
||||
return values;
|
||||
}
|
||||
|
||||
std::vector<fesa::Real> zeroElementVector() {
|
||||
return std::vector<fesa::Real>(24, 0.0);
|
||||
}
|
||||
|
||||
fesa::Real quadraticEnergy(const fesa::DenseMatrix& stiffness, const std::vector<fesa::Real>& values) {
|
||||
const auto internal = stiffness.multiply(values);
|
||||
fesa::Real energy = 0.0;
|
||||
for (std::size_t i = 0; i < values.size(); ++i) {
|
||||
energy += values[i] * internal[i];
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
fesa::Domain singleElementValidationDomain() {
|
||||
fesa::Domain domain;
|
||||
domain.nodes[1] = {1, {0, 0, 0}};
|
||||
@@ -1219,6 +1232,106 @@ FESA_TEST(mitc4_material_integration_samples_carry_tied_rows_basis_and_transform
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_element_stiffness_result_is_symmetric_and_uses_2x2x2_samples) {
|
||||
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||
const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2);
|
||||
FESA_CHECK(result.ok());
|
||||
FESA_CHECK(result.integration_point_count == 8);
|
||||
FESA_CHECK(result.local_without_drilling.rows() == 24);
|
||||
FESA_CHECK(result.local_with_drilling.rows() == 24);
|
||||
FESA_CHECK(result.global.rows() == 24);
|
||||
FESA_CHECK(result.global.cols() == 24);
|
||||
for (fesa::LocalIndex i = 0; i < 24; ++i) {
|
||||
for (fesa::LocalIndex j = 0; j < 24; ++j) {
|
||||
FESA_CHECK_NEAR(result.local_without_drilling(i, j), result.local_without_drilling(j, i), 1.0e-8);
|
||||
FESA_CHECK_NEAR(result.local_with_drilling(i, j), result.local_with_drilling(j, i), 1.0e-8);
|
||||
FESA_CHECK_NEAR(result.global(i, j), result.global(j, i), 1.0e-8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_drilling_stiffness_uses_minimum_positive_physical_local_diagonal) {
|
||||
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||
const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2);
|
||||
FESA_CHECK(result.ok());
|
||||
fesa::Real expected_reference = std::numeric_limits<fesa::Real>::infinity();
|
||||
for (fesa::LocalIndex node = 0; node < 4; ++node) {
|
||||
for (fesa::LocalIndex local_dof = 0; local_dof < 5; ++local_dof) {
|
||||
const fesa::Real diagonal = result.local_without_drilling(6 * node + local_dof, 6 * node + local_dof);
|
||||
if (std::isfinite(diagonal) && diagonal > 0.0) {
|
||||
expected_reference = std::min(expected_reference, diagonal);
|
||||
}
|
||||
}
|
||||
}
|
||||
FESA_CHECK(std::isfinite(expected_reference));
|
||||
FESA_CHECK_NEAR(result.drilling_reference_diagonal, expected_reference, 1.0e-10);
|
||||
FESA_CHECK_NEAR(result.drilling_stiffness_scale, 1.0e-3, 1.0e-15);
|
||||
FESA_CHECK_NEAR(result.drilling_stiffness, 1.0e-3 * expected_reference, 1.0e-10);
|
||||
for (fesa::LocalIndex node = 0; node < 4; ++node) {
|
||||
const fesa::LocalIndex gamma = 6 * node + 5;
|
||||
FESA_CHECK_NEAR(result.local_with_drilling(gamma, gamma) - result.local_without_drilling(gamma, gamma),
|
||||
result.drilling_stiffness, 1.0e-12);
|
||||
}
|
||||
|
||||
fesa::ElementStiffnessOptions options;
|
||||
options.drilling_stiffness_scale = 2.0e-3;
|
||||
const auto scaled = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2, options);
|
||||
FESA_CHECK(scaled.ok());
|
||||
FESA_CHECK_NEAR(scaled.drilling_reference_diagonal, result.drilling_reference_diagonal, 1.0e-10);
|
||||
FESA_CHECK_NEAR(scaled.drilling_stiffness, 2.0 * result.drilling_stiffness, 1.0e-10);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_drilling_reports_invalid_scale_and_missing_reference_diagonal) {
|
||||
fesa::DenseMatrix zero_physical(24, 24);
|
||||
auto missing_reference = fesa::mitc4ApplyDrillingStabilization(zero_physical, 1.0e-3);
|
||||
FESA_CHECK(!missing_reference.ok());
|
||||
FESA_CHECK(fesa::containsDiagnostic(missing_reference.diagnostics, "FESA-MITC4-DRILLING-REFERENCE"));
|
||||
|
||||
auto invalid_scale = fesa::mitc4ApplyDrillingStabilization(zero_physical, -1.0);
|
||||
FESA_CHECK(!invalid_scale.ok());
|
||||
FESA_CHECK(fesa::containsDiagnostic(invalid_scale.diagnostics, "FESA-MITC4-DRILLING-SCALE"));
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_rigid_translation_energy_is_zero_and_drilling_energy_is_documented) {
|
||||
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||
const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2);
|
||||
FESA_CHECK(result.ok());
|
||||
|
||||
auto translate_x = zeroElementVector();
|
||||
auto translate_y = zeroElementVector();
|
||||
auto translate_z = zeroElementVector();
|
||||
for (int node = 0; node < 4; ++node) {
|
||||
translate_x[static_cast<std::size_t>(6 * node + 0)] = 1.0;
|
||||
translate_y[static_cast<std::size_t>(6 * node + 1)] = 1.0;
|
||||
translate_z[static_cast<std::size_t>(6 * node + 2)] = 1.0;
|
||||
}
|
||||
FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_x)) < 1.0e-8);
|
||||
FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_y)) < 1.0e-8);
|
||||
FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_z)) < 1.0e-8);
|
||||
|
||||
auto drilling = zeroElementVector();
|
||||
for (int node = 0; node < 4; ++node) {
|
||||
drilling[static_cast<std::size_t>(6 * node + 5)] = 1.0;
|
||||
}
|
||||
FESA_CHECK_NEAR(quadraticEnergy(result.global, drilling), 4.0 * result.drilling_stiffness, 1.0e-8);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_internal_force_is_stiffness_times_displacement_for_linear_phase1) {
|
||||
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||
const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2);
|
||||
FESA_CHECK(result.ok());
|
||||
std::vector<fesa::Real> displacement(24, 0.0);
|
||||
for (std::size_t i = 0; i < displacement.size(); ++i) {
|
||||
displacement[i] = 0.001 * static_cast<fesa::Real>(i + 1);
|
||||
}
|
||||
const auto expected = result.global.multiply(displacement);
|
||||
const auto actual = fesa::mitc4ElementInternalForce(result, displacement);
|
||||
FESA_CHECK(actual.size() == expected.size());
|
||||
for (std::size_t i = 0; i < expected.size(); ++i) {
|
||||
FESA_CHECK_NEAR(actual[i], expected[i], 1.0e-12);
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_shape_functions_and_stiffness_baseline) {
|
||||
auto shape = fesa::shapeFunctions(0.25, -0.5);
|
||||
const fesa::Real sum = shape.n[0] + shape.n[1] + shape.n[2] + shape.n[3];
|
||||
|
||||
Reference in New Issue
Block a user