174 lines
9.0 KiB
C++
174 lines
9.0 KiB
C++
#include "fesa/Element/Element.hpp"
|
|
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <stdexcept>
|
|
|
|
namespace {
|
|
|
|
void check(bool value, const char* message) {
|
|
if (!value) {
|
|
throw std::runtime_error(message);
|
|
}
|
|
}
|
|
|
|
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
|
if (std::fabs(actual - expected) > tolerance) {
|
|
throw std::runtime_error(message);
|
|
}
|
|
}
|
|
|
|
void checkVecNear(const fesa::Vec3& actual, const fesa::Vec3& expected, fesa::Real tolerance, const char* message) {
|
|
checkNear(actual.x, expected.x, tolerance, message);
|
|
checkNear(actual.y, expected.y, tolerance, message);
|
|
checkNear(actual.z, expected.z, tolerance, message);
|
|
}
|
|
|
|
void checkRightHandedOrthonormal(const fesa::Vec3& e1, const fesa::Vec3& e2, const fesa::Vec3& e3) {
|
|
checkNear(fesa::norm(e1), 1.0, 1.0e-14, "e1 norm changed");
|
|
checkNear(fesa::norm(e2), 1.0, 1.0e-14, "e2 norm changed");
|
|
checkNear(fesa::norm(e3), 1.0, 1.0e-14, "e3 norm changed");
|
|
checkNear(fesa::dot(e1, e2), 0.0, 1.0e-14, "e1/e2 orthogonality changed");
|
|
checkNear(fesa::dot(e1, e3), 0.0, 1.0e-14, "e1/e3 orthogonality changed");
|
|
checkNear(fesa::dot(e2, e3), 0.0, 1.0e-14, "e2/e3 orthogonality changed");
|
|
checkNear(fesa::dot(fesa::cross(e1, e2), e3), 1.0, 1.0e-14, "basis handedness changed");
|
|
}
|
|
|
|
fesa::MITC4ElementDofVector zeroElementDofs() {
|
|
fesa::MITC4ElementDofVector values{};
|
|
values.fill(0.0);
|
|
return values;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
const auto center = fesa::shapeFunctions(0.0, 0.0);
|
|
checkNear(center.n[0] + center.n[1] + center.n[2] + center.n[3], 1.0, 1.0e-15, "shape sum changed");
|
|
checkNear(center.dr[0], -0.25, 1.0e-15, "center dN1/dxi changed");
|
|
checkNear(center.ds[2], 0.25, 1.0e-15, "center dN3/deta changed");
|
|
|
|
const auto nodes = fesa::mitc4NodeNaturalCoordinates();
|
|
checkNear(nodes[0].xi, -1.0, 1.0e-15, "node 1 xi changed");
|
|
checkNear(nodes[0].eta, -1.0, 1.0e-15, "node 1 eta changed");
|
|
checkNear(nodes[2].xi, 1.0, 1.0e-15, "node 3 xi changed");
|
|
checkNear(nodes[2].eta, 1.0, 1.0e-15, "node 3 eta changed");
|
|
for (std::size_t i = 0; i < 4; ++i) {
|
|
const auto corner = fesa::shapeFunctions(nodes[i].xi, nodes[i].eta);
|
|
checkNear(corner.n[i], 1.0, 1.0e-15, "corner interpolation changed");
|
|
}
|
|
|
|
const auto tying = fesa::mitc4TyingPoints();
|
|
check(tying[0].label == "A" && tying[0].natural.xi == 0.0 && tying[0].natural.eta == -1.0,
|
|
"tying point A changed");
|
|
check((tying[0].edge_node_indices == std::array<fesa::LocalIndex, 2>{0, 1}), "tying edge A changed");
|
|
check(tying[1].label == "B" && tying[1].natural.xi == -1.0 && tying[1].natural.eta == 0.0,
|
|
"tying point B changed");
|
|
check((tying[1].edge_node_indices == std::array<fesa::LocalIndex, 2>{0, 3}), "tying edge B changed");
|
|
check(tying[2].label == "C" && tying[2].natural.xi == 0.0 && tying[2].natural.eta == 1.0,
|
|
"tying point C changed");
|
|
check((tying[2].edge_node_indices == std::array<fesa::LocalIndex, 2>{3, 2}), "tying edge C changed");
|
|
check(tying[3].label == "D" && tying[3].natural.xi == 1.0 && tying[3].natural.eta == 0.0,
|
|
"tying point D changed");
|
|
check((tying[3].edge_node_indices == std::array<fesa::LocalIndex, 2>{1, 2}), "tying edge D changed");
|
|
|
|
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
|
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
|
check(geometry.ok(), "flat geometry should remain valid");
|
|
checkVecNear(geometry.center_normal, {0, 0, 1}, 1.0e-14, "center normal changed");
|
|
for (const auto& frame : geometry.nodal_frames) {
|
|
checkVecNear(frame.v1, {1, 0, 0}, 1.0e-14, "director v1 changed");
|
|
checkVecNear(frame.v2, {0, 1, 0}, 1.0e-14, "director v2 changed");
|
|
checkVecNear(frame.vn, {0, 0, 1}, 1.0e-14, "director normal changed");
|
|
checkRightHandedOrthonormal(frame.v1, frame.v2, frame.vn);
|
|
}
|
|
|
|
const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0);
|
|
check(basis.ok(), "flat integration basis should remain valid");
|
|
checkVecNear(basis.g1, {0.5, 0, 0}, 1.0e-14, "g1 changed");
|
|
checkVecNear(basis.g2, {0, 0.5, 0}, 1.0e-14, "g2 changed");
|
|
checkVecNear(basis.g3, {0, 0, 0.1}, 1.0e-14, "g3 changed");
|
|
checkRightHandedOrthonormal(basis.local.e1, basis.local.e2, basis.local.e3);
|
|
checkNear(basis.jacobian, 0.025, 1.0e-14, "Jacobian changed");
|
|
|
|
const auto invalid_thickness = fesa::buildMITC4Geometry(coords, 0.0);
|
|
check(!invalid_thickness.ok(), "invalid thickness should remain diagnosed");
|
|
check(fesa::containsDiagnostic(invalid_thickness.diagnostics, "FESA-MITC4-THICKNESS"), "thickness diagnostic changed");
|
|
const std::array<fesa::Vec3, 4> collinear = {{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {3, 0, 0}}};
|
|
const auto singular = fesa::buildMITC4Geometry(collinear, 0.1);
|
|
check(!singular.ok(), "singular normal should remain diagnosed");
|
|
check(fesa::containsDiagnostic(singular.diagnostics, "FESA-MITC4-SINGULAR-NORMAL"), "normal diagnostic changed");
|
|
|
|
const auto rotations = fesa::mitc4LocalRotations(geometry.nodal_frames[0], {1.0, 2.0, 3.0});
|
|
checkNear(rotations.alpha, 1.0, 1.0e-14, "alpha rotation changed");
|
|
checkNear(rotations.beta, 2.0, 1.0e-14, "beta rotation changed");
|
|
checkNear(rotations.gamma, 3.0, 1.0e-14, "gamma rotation changed");
|
|
checkVecNear(fesa::mitc4DirectorIncrement(geometry.nodal_frames[0], {0.0, 2.0, 5.0}), {2, 0, 0}, 1.0e-14,
|
|
"director increment changed");
|
|
|
|
auto dofs = zeroElementDofs();
|
|
for (std::size_t node = 0; node < 4; ++node) {
|
|
dofs[6 * node + 0] = 1.0;
|
|
dofs[6 * node + 1] = 0.5;
|
|
dofs[6 * node + 4] = 2.0;
|
|
dofs[6 * node + 5] = 99.0;
|
|
}
|
|
const auto mid = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 0.0);
|
|
check(mid.ok(), "midsurface displacement interpolation changed");
|
|
checkVecNear(mid.displacement, {1.0, 0.5, 0.0}, 1.0e-14, "midsurface displacement value changed");
|
|
const auto top = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 1.0);
|
|
check(top.ok(), "top displacement interpolation changed");
|
|
checkVecNear(top.displacement, {1.2, 0.5, 0.0}, 1.0e-14, "top displacement value changed");
|
|
|
|
constexpr fesa::Real xi = 0.2;
|
|
constexpr fesa::Real eta = -0.3;
|
|
constexpr fesa::Real zeta = 0.4;
|
|
const auto rows = fesa::mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta);
|
|
check(rows.ok(), "direct covariant strain rows should remain valid");
|
|
check(fesa::mitc4StrainComponentLabels()[0] == "eps11", "strain label eps11 changed");
|
|
check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23) == 3, "gamma23 index changed");
|
|
check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13) == 4, "gamma13 index changed");
|
|
|
|
const fesa::Real h = 1.0e-6;
|
|
const std::array<std::size_t, 4> checked_dofs = {0, 7, 10, 11};
|
|
for (std::size_t dof : checked_dofs) {
|
|
auto plus = zeroElementDofs();
|
|
auto minus = zeroElementDofs();
|
|
plus[dof] = h;
|
|
minus[dof] = -h;
|
|
const auto plus_strain = fesa::mitc4DirectCovariantStrain(geometry, plus, xi, eta, zeta);
|
|
const auto minus_strain = fesa::mitc4DirectCovariantStrain(geometry, minus, xi, eta, zeta);
|
|
check(plus_strain.ok() && minus_strain.ok(), "direct strain perturbation changed");
|
|
for (std::size_t component = 0; component < 6; ++component) {
|
|
const fesa::Real finite_difference = (plus_strain.values[component] - minus_strain.values[component]) / (2.0 * h);
|
|
checkNear(rows.rows[component][dof], finite_difference, 1.0e-8, "direct strain row finite difference changed");
|
|
}
|
|
}
|
|
|
|
const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23);
|
|
const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13);
|
|
const auto direct_at_point = fesa::mitc4DirectCovariantStrainRows(geometry, 0.5, 0.25, 0.0);
|
|
const auto direct_a = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, 0.0);
|
|
const auto direct_b = fesa::mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, 0.0);
|
|
const auto direct_c = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, 0.0);
|
|
const auto direct_d = fesa::mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, 0.0);
|
|
const auto tied = fesa::mitc4TiedCovariantStrainRows(geometry, 0.5, 0.25, 0.0);
|
|
check(direct_at_point.ok() && tied.ok(), "MITC tied rows should remain valid");
|
|
|
|
const std::size_t node2_ry = 6 + 4;
|
|
const fesa::Real expected_gamma13 =
|
|
0.5 * (1.0 - 0.25) * direct_a.rows[gamma13][node2_ry] + 0.5 * (1.0 + 0.25) * direct_c.rows[gamma13][node2_ry];
|
|
checkNear(tied.rows[gamma13][node2_ry], expected_gamma13, 1.0e-14, "MITC gamma13 tying interpolation changed");
|
|
check(std::fabs(tied.rows[gamma13][node2_ry] - direct_at_point.rows[gamma13][node2_ry]) > 1.0e-4,
|
|
"MITC gamma13 should not be direct Gauss shear");
|
|
|
|
const std::size_t node4_rx = 3 * 6 + 3;
|
|
const fesa::Real expected_gamma23 =
|
|
0.5 * (1.0 - 0.5) * direct_b.rows[gamma23][node4_rx] + 0.5 * (1.0 + 0.5) * direct_d.rows[gamma23][node4_rx];
|
|
checkNear(tied.rows[gamma23][node4_rx], expected_gamma23, 1.0e-14, "MITC gamma23 tying interpolation changed");
|
|
check(std::fabs(tied.rows[gamma23][node4_rx] - direct_at_point.rows[gamma23][node4_rx]) > 1.0e-4,
|
|
"MITC gamma23 should not be direct Gauss shear");
|
|
|
|
return 0;
|
|
}
|