feat: add mitc4 material integration scaffolding

This commit is contained in:
NINI
2026-05-04 22:34:53 +09:00
parent d550e13e77
commit 2e1bd99d05
5 changed files with 433 additions and 6 deletions
+149
View File
@@ -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];