test: add mitc4 patch benchmark coverage
This commit is contained in:
@@ -160,6 +160,76 @@ fesa::Real quadraticEnergy(const fesa::DenseMatrix& stiffness, const std::vector
|
||||
return energy;
|
||||
}
|
||||
|
||||
std::array<fesa::Vec3, 4> flatUnitSquareCoordinates() {
|
||||
return {fesa::Vec3{0.0, 0.0, 0.0},
|
||||
fesa::Vec3{1.0, 0.0, 0.0},
|
||||
fesa::Vec3{1.0, 1.0, 0.0},
|
||||
fesa::Vec3{0.0, 1.0, 0.0}};
|
||||
}
|
||||
|
||||
using MITC4DofField = std::function<fesa::Vec3(const fesa::Vec3&)>;
|
||||
|
||||
fesa::MITC4ElementDofVector elementDofsFromFields(const std::array<fesa::Vec3, 4>& coords,
|
||||
MITC4DofField translation,
|
||||
MITC4DofField rotation) {
|
||||
fesa::MITC4ElementDofVector values{};
|
||||
for (std::size_t node = 0; node < coords.size(); ++node) {
|
||||
const auto displacement = translation(coords[node]);
|
||||
const auto rotational_dof = rotation(coords[node]);
|
||||
values[node * 6 + 0] = displacement.x;
|
||||
values[node * 6 + 1] = displacement.y;
|
||||
values[node * 6 + 2] = displacement.z;
|
||||
values[node * 6 + 3] = rotational_dof.x;
|
||||
values[node * 6 + 4] = rotational_dof.y;
|
||||
values[node * 6 + 5] = rotational_dof.z;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
std::vector<fesa::Real> toVector(const fesa::MITC4ElementDofVector& values) {
|
||||
return {values.begin(), values.end()};
|
||||
}
|
||||
|
||||
fesa::MITC4StrainVector localStrainAt(const fesa::MITC4Geometry& geometry,
|
||||
const fesa::MITC4ElementDofVector& values,
|
||||
fesa::Real xi,
|
||||
fesa::Real eta,
|
||||
fesa::Real zeta) {
|
||||
const auto rows = fesa::mitc4TiedCovariantStrainRows(geometry, xi, eta, zeta);
|
||||
FESA_CHECK(rows.ok());
|
||||
const auto convected_strain = fesa::evaluateMITC4StrainRows(rows, values);
|
||||
FESA_CHECK(convected_strain.ok());
|
||||
const auto basis = fesa::computeMITC4IntegrationBasis(geometry, xi, eta, zeta);
|
||||
FESA_CHECK(basis.ok());
|
||||
const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis);
|
||||
FESA_CHECK(transform.ok());
|
||||
return fesa::multiplyMITC4MaterialMatrix(transform.matrix, convected_strain.values);
|
||||
}
|
||||
|
||||
fesa::Real singleElementCantileverTipUz(fesa::Real thickness) {
|
||||
fesa::Domain domain;
|
||||
domain.nodes[1] = {1, {0.0, 0.0, 0.0}};
|
||||
domain.nodes[2] = {2, {4.0, 0.0, 0.0}};
|
||||
domain.nodes[3] = {3, {4.0, 1.0, 0.0}};
|
||||
domain.nodes[4] = {4, {0.0, 1.0, 0.0}};
|
||||
domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "SHELLS"};
|
||||
domain.node_sets["clamped"] = {"CLAMPED", {1, 4}};
|
||||
domain.element_sets["shells"] = {"SHELLS", {1}};
|
||||
domain.materials["shell_steel"] = {"SHELL_STEEL", 210000.0, 0.30};
|
||||
domain.shell_sections.push_back({"SHELLS", "SHELL_STEEL", thickness});
|
||||
domain.boundary_conditions.push_back({"CLAMPED", 1, 6, 0.0});
|
||||
domain.loads.push_back({"2", 3, -0.5});
|
||||
domain.loads.push_back({"3", 3, -0.5});
|
||||
|
||||
fesa::LinearStaticAnalysis analysis;
|
||||
const auto result = analysis.run(domain);
|
||||
FESA_CHECK(result.ok());
|
||||
fesa::DofManager dofs(domain);
|
||||
const auto node2_uz = result.state.u_full[static_cast<std::size_t>(dofs.fullIndex(2, fesa::Dof::UZ))];
|
||||
const auto node3_uz = result.state.u_full[static_cast<std::size_t>(dofs.fullIndex(3, fesa::Dof::UZ))];
|
||||
return 0.5 * (node2_uz + node3_uz);
|
||||
}
|
||||
|
||||
fesa::Domain singleElementValidationDomain() {
|
||||
fesa::Domain domain;
|
||||
domain.nodes[1] = {1, {0, 0, 0}};
|
||||
@@ -1316,6 +1386,143 @@ FESA_TEST(mitc4_rigid_translation_energy_is_zero_and_drilling_energy_is_document
|
||||
FESA_CHECK_NEAR(quadraticEnergy(result.global, drilling), 4.0 * result.drilling_stiffness, 1.0e-8);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_constant_membrane_patch_strain_is_uniform) {
|
||||
const auto coords = flatUnitSquareCoordinates();
|
||||
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const fesa::Real eps_x = 0.010;
|
||||
const fesa::Real eps_y = -0.004;
|
||||
const fesa::Real gamma_xy = 0.006;
|
||||
const auto dofs = elementDofsFromFields(
|
||||
coords,
|
||||
[&](const fesa::Vec3& p) {
|
||||
return fesa::Vec3{eps_x * p.x + 0.5 * gamma_xy * p.y,
|
||||
eps_y * p.y + 0.5 * gamma_xy * p.x,
|
||||
0.0};
|
||||
},
|
||||
[](const fesa::Vec3&) { return fesa::Vec3{}; });
|
||||
|
||||
for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) {
|
||||
const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta);
|
||||
FESA_CHECK_NEAR(strain[0], eps_x, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[1], eps_y, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[3], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[4], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[5], gamma_xy, 1.0e-12);
|
||||
}
|
||||
|
||||
const auto stiffness = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2);
|
||||
FESA_CHECK(stiffness.ok());
|
||||
const auto energy = quadraticEnergy(stiffness.local_without_drilling, toVector(dofs));
|
||||
FESA_CHECK(energy > 0.0);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_pure_bending_patch_is_membrane_free_and_thickness_antisymmetric) {
|
||||
const auto coords = flatUnitSquareCoordinates();
|
||||
const fesa::Real thickness = 0.2;
|
||||
const auto geometry = fesa::buildMITC4Geometry(coords, thickness);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const fesa::Real curvature_x = 0.025;
|
||||
const auto dofs = elementDofsFromFields(
|
||||
coords,
|
||||
[](const fesa::Vec3&) { return fesa::Vec3{}; },
|
||||
[&](const fesa::Vec3& p) { return fesa::Vec3{0.0, curvature_x * p.x, 0.0}; });
|
||||
|
||||
const fesa::Real xi = -0.30;
|
||||
const fesa::Real eta = 0.45;
|
||||
const fesa::Real zeta = 1.0 / std::sqrt(3.0);
|
||||
const auto top = localStrainAt(geometry, dofs, xi, eta, zeta);
|
||||
const auto mid = localStrainAt(geometry, dofs, xi, eta, 0.0);
|
||||
const auto bottom = localStrainAt(geometry, dofs, xi, eta, -zeta);
|
||||
|
||||
FESA_CHECK(std::fabs(top[0]) > 1.0e-6);
|
||||
FESA_CHECK_NEAR(top[0] + bottom[0], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(mid[0], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(top[1] + bottom[1], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(mid[1], 0.0, 1.0e-12);
|
||||
|
||||
const auto stiffness = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, thickness);
|
||||
FESA_CHECK(stiffness.ok());
|
||||
const auto energy = quadraticEnergy(stiffness.local_without_drilling, toVector(dofs));
|
||||
FESA_CHECK(energy > 0.0);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_pure_transverse_shear_patch_is_constant) {
|
||||
const auto coords = flatUnitSquareCoordinates();
|
||||
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const fesa::Real gamma_xz = 0.012;
|
||||
const auto dofs = elementDofsFromFields(
|
||||
coords,
|
||||
[&](const fesa::Vec3& p) { return fesa::Vec3{0.0, 0.0, gamma_xz * p.x}; },
|
||||
[](const fesa::Vec3&) { return fesa::Vec3{}; });
|
||||
|
||||
for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) {
|
||||
const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta);
|
||||
FESA_CHECK_NEAR(strain[0], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[1], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[3], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[4], gamma_xz, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[5], 0.0, 1.0e-12);
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_pure_twist_patch_reproduces_bilinear_transverse_shear) {
|
||||
const auto coords = flatUnitSquareCoordinates();
|
||||
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const fesa::Real twist = 0.018;
|
||||
const auto dofs = elementDofsFromFields(
|
||||
coords,
|
||||
[&](const fesa::Vec3& p) { return fesa::Vec3{0.0, 0.0, twist * p.x * p.y}; },
|
||||
[](const fesa::Vec3&) { return fesa::Vec3{}; });
|
||||
|
||||
for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) {
|
||||
const auto x = 0.5 * (sample.xi + 1.0);
|
||||
const auto y = 0.5 * (sample.eta + 1.0);
|
||||
const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta);
|
||||
FESA_CHECK_NEAR(strain[0], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[1], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[3], twist * x, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[4], twist * y, 1.0e-12);
|
||||
FESA_CHECK_NEAR(strain[5], 0.0, 1.0e-12);
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_membrane_patch_energy_is_not_hidden_by_drilling_scale) {
|
||||
const auto coords = flatUnitSquareCoordinates();
|
||||
const auto dofs = elementDofsFromFields(
|
||||
coords,
|
||||
[](const fesa::Vec3& p) { return fesa::Vec3{0.003 * p.x, -0.002 * p.y, 0.0}; },
|
||||
[](const fesa::Vec3&) { return fesa::Vec3{}; });
|
||||
|
||||
fesa::ElementStiffnessOptions no_drilling;
|
||||
no_drilling.drilling_stiffness_scale = 0.0;
|
||||
fesa::ElementStiffnessOptions strong_drilling;
|
||||
strong_drilling.drilling_stiffness_scale = 1.0e-1;
|
||||
const auto physical = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2, no_drilling);
|
||||
const auto stabilized = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2, strong_drilling);
|
||||
FESA_CHECK(physical.ok());
|
||||
FESA_CHECK(stabilized.ok());
|
||||
|
||||
const auto physical_energy = quadraticEnergy(physical.local_with_drilling, toVector(dofs));
|
||||
const auto stabilized_energy = quadraticEnergy(stabilized.local_with_drilling, toVector(dofs));
|
||||
FESA_CHECK(physical_energy > 0.0);
|
||||
FESA_CHECK_NEAR(stabilized_energy, physical_energy, physical_energy * 1.0e-12);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_single_element_cantilever_is_thickness_sensitive) {
|
||||
const auto thick_tip = singleElementCantileverTipUz(0.20);
|
||||
const auto thin_tip = singleElementCantileverTipUz(0.10);
|
||||
FESA_CHECK(thick_tip < 0.0);
|
||||
FESA_CHECK(thin_tip < thick_tip);
|
||||
const auto response_ratio = std::fabs(thin_tip / thick_tip);
|
||||
FESA_CHECK(response_ratio > 2.0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user