feat: add mitc4 material integration scaffolding
This commit is contained in:
@@ -1070,6 +1070,155 @@ FESA_TEST(mitc4_mitc_gauss_shear_rows_are_interpolated_from_tying_rows) {
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_plane_stress_material_matrix_uses_documented_order_and_shear_correction) {
|
||||
constexpr fesa::Real elastic_modulus = 210.0;
|
||||
constexpr fesa::Real poisson_ratio = 0.3;
|
||||
constexpr fesa::Real kappa = 5.0 / 6.0;
|
||||
const auto law = fesa::mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, kappa);
|
||||
FESA_CHECK(law.ok());
|
||||
const auto eps11 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11);
|
||||
const auto eps22 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22);
|
||||
const auto eps33 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps33);
|
||||
const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23);
|
||||
const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13);
|
||||
const auto gamma12 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12);
|
||||
const fesa::Real plane_stress_scale = elastic_modulus / (1.0 - poisson_ratio * poisson_ratio);
|
||||
const fesa::Real shear_modulus = elastic_modulus / (2.0 * (1.0 + poisson_ratio));
|
||||
|
||||
FESA_CHECK(fesa::mitc4StrainComponentLabels() ==
|
||||
(std::array<std::string, 6>{"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"}));
|
||||
FESA_CHECK_NEAR(law.matrix[eps11][eps11], plane_stress_scale, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[eps11][eps22], poisson_ratio * plane_stress_scale, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[eps22][eps11], poisson_ratio * plane_stress_scale, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[eps22][eps22], plane_stress_scale, 1.0e-12);
|
||||
for (std::size_t component = 0; component < 6; ++component) {
|
||||
FESA_CHECK_NEAR(law.matrix[eps33][component], 0.0, 1.0e-15);
|
||||
FESA_CHECK_NEAR(law.matrix[component][eps33], 0.0, 1.0e-15);
|
||||
}
|
||||
FESA_CHECK_NEAR(law.matrix[gamma23][gamma23], kappa * shear_modulus, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[gamma13][gamma13], kappa * shear_modulus, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[gamma12][gamma12], shear_modulus, 1.0e-12);
|
||||
FESA_CHECK_NEAR(law.matrix[gamma13][gamma23], 0.0, 1.0e-15);
|
||||
|
||||
fesa::MITC4StrainVector strain{};
|
||||
strain[gamma23] = 2.0;
|
||||
strain[gamma13] = 3.0;
|
||||
strain[gamma12] = 4.0;
|
||||
const auto stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, strain);
|
||||
FESA_CHECK_NEAR(stress[gamma23], 2.0 * kappa * shear_modulus, 1.0e-12);
|
||||
FESA_CHECK_NEAR(stress[gamma13], 3.0 * kappa * shear_modulus, 1.0e-12);
|
||||
FESA_CHECK_NEAR(stress[gamma12], 4.0 * shear_modulus, 1.0e-12);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_material_matrix_reports_invalid_elastic_inputs) {
|
||||
auto invalid_e = fesa::mitc4PlaneStressMaterialMatrix(-1.0, 0.3);
|
||||
FESA_CHECK(!invalid_e.ok());
|
||||
FESA_CHECK(fesa::containsDiagnostic(invalid_e.diagnostics, "FESA-MITC4-MATERIAL"));
|
||||
|
||||
auto invalid_nu = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.5);
|
||||
FESA_CHECK(!invalid_nu.ok());
|
||||
FESA_CHECK(fesa::containsDiagnostic(invalid_nu.diagnostics, "FESA-MITC4-POISSON"));
|
||||
|
||||
auto invalid_kappa = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25, 0.0);
|
||||
FESA_CHECK(!invalid_kappa.ok());
|
||||
FESA_CHECK(fesa::containsDiagnostic(invalid_kappa.diagnostics, "FESA-MITC4-SHEAR-CORRECTION"));
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_gauss_integration_uses_2x2x2_points_and_unit_weights) {
|
||||
const auto points = fesa::mitc4GaussQuadrature2x2x2();
|
||||
FESA_CHECK(points.size() == 8);
|
||||
const fesa::Real gauss = 1.0 / std::sqrt(3.0);
|
||||
fesa::Real total_weight = 0.0;
|
||||
int positive_xi = 0;
|
||||
int positive_eta = 0;
|
||||
int positive_zeta = 0;
|
||||
for (const auto& point : points) {
|
||||
FESA_CHECK_NEAR(std::fabs(point.xi), gauss, 1.0e-15);
|
||||
FESA_CHECK_NEAR(std::fabs(point.eta), gauss, 1.0e-15);
|
||||
FESA_CHECK_NEAR(std::fabs(point.zeta), gauss, 1.0e-15);
|
||||
FESA_CHECK_NEAR(point.weight, 1.0, 1.0e-15);
|
||||
total_weight += point.weight;
|
||||
positive_xi += point.xi > 0.0 ? 1 : 0;
|
||||
positive_eta += point.eta > 0.0 ? 1 : 0;
|
||||
positive_zeta += point.zeta > 0.0 ? 1 : 0;
|
||||
}
|
||||
FESA_CHECK_NEAR(total_weight, 8.0, 1.0e-15);
|
||||
FESA_CHECK(positive_xi == 4);
|
||||
FESA_CHECK(positive_eta == 4);
|
||||
FESA_CHECK(positive_zeta == 4);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_covariant_to_local_transform_is_identity_for_orthonormal_basis) {
|
||||
fesa::MITC4IntegrationBasis basis;
|
||||
basis.g1 = {1.0, 0.0, 0.0};
|
||||
basis.g2 = {0.0, 1.0, 0.0};
|
||||
basis.g3 = {0.0, 0.0, 1.0};
|
||||
basis.local = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
|
||||
basis.jacobian = 1.0;
|
||||
const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis);
|
||||
FESA_CHECK(transform.ok());
|
||||
for (std::size_t row = 0; row < 6; ++row) {
|
||||
for (std::size_t col = 0; col < 6; ++col) {
|
||||
FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? 1.0 : 0.0, 1.0e-15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_flat_element_material_transform_scales_convected_strains_to_local_frame) {
|
||||
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);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0);
|
||||
FESA_CHECK(basis.ok());
|
||||
const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis);
|
||||
FESA_CHECK(transform.ok());
|
||||
|
||||
const std::array<fesa::Real, 6> expected_diagonal = {4.0, 4.0, 100.0, 20.0, 20.0, 4.0};
|
||||
for (std::size_t row = 0; row < 6; ++row) {
|
||||
for (std::size_t col = 0; col < 6; ++col) {
|
||||
FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? expected_diagonal[row] : 0.0, 1.0e-13);
|
||||
}
|
||||
}
|
||||
|
||||
const auto law = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25);
|
||||
FESA_CHECK(law.ok());
|
||||
const auto convected = fesa::mitc4TransformMaterialMatrix(law.matrix, transform.matrix);
|
||||
fesa::MITC4StrainVector local_strain{};
|
||||
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11)] = 0.02;
|
||||
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22)] = -0.01;
|
||||
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12)] = 0.03;
|
||||
|
||||
fesa::MITC4StrainVector convected_strain{};
|
||||
for (std::size_t component = 0; component < 6; ++component) {
|
||||
convected_strain[component] = local_strain[component] / expected_diagonal[component];
|
||||
}
|
||||
const auto local_stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, local_strain);
|
||||
const auto convected_stress = fesa::multiplyMITC4MaterialMatrix(convected, convected_strain);
|
||||
const fesa::Real local_energy = fesa::dotMITC4Vector(local_strain, local_stress);
|
||||
const fesa::Real convected_energy = fesa::dotMITC4Vector(convected_strain, convected_stress);
|
||||
FESA_CHECK_NEAR(convected_energy, local_energy, 1.0e-12);
|
||||
}
|
||||
|
||||
FESA_TEST(mitc4_material_integration_samples_carry_tied_rows_basis_and_transformed_material) {
|
||||
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);
|
||||
FESA_CHECK(geometry.ok());
|
||||
const auto data = fesa::mitc4BuildMaterialIntegrationData(geometry, 1000.0, 0.3);
|
||||
FESA_CHECK(data.ok());
|
||||
FESA_CHECK(data.samples.size() == 8);
|
||||
for (const auto& sample : data.samples) {
|
||||
FESA_CHECK(sample.ok());
|
||||
FESA_CHECK_NEAR(sample.point.weight, 1.0, 1.0e-15);
|
||||
FESA_CHECK(sample.basis.ok());
|
||||
FESA_CHECK(sample.strain_rows.ok());
|
||||
for (std::size_t i = 0; i < 6; ++i) {
|
||||
for (std::size_t j = 0; j < 6; ++j) {
|
||||
FESA_CHECK_NEAR(sample.convected_material[i][j], sample.convected_material[j][i], 1.0e-10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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