#pragma once #include "fesa/Element/MITC4Kinematics.hpp" #include "fesa/Material/MITC4PlaneStressMaterial.hpp" #include #include #include namespace fesa { struct MITC4StrainTransform { MITC4MaterialMatrix matrix{}; std::vector diagnostics; bool ok() const { return !hasError(diagnostics); } }; struct MITC4IntegrationPoint { Real xi = 0.0; Real eta = 0.0; Real zeta = 0.0; Real weight = 0.0; }; struct MITC4MaterialIntegrationSample { MITC4IntegrationPoint point; MITC4IntegrationBasis basis; MITC4StrainRows strain_rows; MITC4MaterialMatrix local_material{}; MITC4MaterialMatrix strain_transform{}; MITC4MaterialMatrix convected_material{}; std::vector diagnostics; bool ok() const { return !hasError(diagnostics); } }; struct MITC4MaterialIntegrationData { std::vector samples; std::vector diagnostics; bool ok() const { return !hasError(diagnostics); } }; inline std::array mitc4GaussQuadrature2x2x2() { const Real gauss = 1.0 / std::sqrt(3.0); const std::array points = {-gauss, gauss}; std::array integration_points{}; std::size_t index = 0; for (Real xi : points) { for (Real eta : points) { for (Real zeta : points) { integration_points[index++] = {xi, eta, zeta, 1.0}; } } } return integration_points; } inline std::array, 3> mitc4TensorFromEngineeringComponent(std::size_t component) { std::array, 3> tensor{}; switch (static_cast(component)) { case MITC4StrainComponent::Eps11: tensor[0][0] = 1.0; break; case MITC4StrainComponent::Eps22: tensor[1][1] = 1.0; break; case MITC4StrainComponent::Eps33: tensor[2][2] = 1.0; break; case MITC4StrainComponent::Gamma23: tensor[1][2] = 0.5; tensor[2][1] = 0.5; break; case MITC4StrainComponent::Gamma13: tensor[0][2] = 0.5; tensor[2][0] = 0.5; break; case MITC4StrainComponent::Gamma12: tensor[0][1] = 0.5; tensor[1][0] = 0.5; break; } return tensor; } inline MITC4StrainVector mitc4EngineeringVectorFromTensor(const std::array, 3>& tensor) { MITC4StrainVector vector{}; vector[strainComponentIndex(MITC4StrainComponent::Eps11)] = tensor[0][0]; vector[strainComponentIndex(MITC4StrainComponent::Eps22)] = tensor[1][1]; vector[strainComponentIndex(MITC4StrainComponent::Eps33)] = tensor[2][2]; vector[strainComponentIndex(MITC4StrainComponent::Gamma23)] = 2.0 * tensor[1][2]; vector[strainComponentIndex(MITC4StrainComponent::Gamma13)] = 2.0 * tensor[0][2]; vector[strainComponentIndex(MITC4StrainComponent::Gamma12)] = 2.0 * tensor[0][1]; return vector; } inline MITC4StrainTransform mitc4CovariantToLocalStrainTransform(const MITC4IntegrationBasis& basis, Real tolerance = 1.0e-12) { MITC4StrainTransform result; result.diagnostics = basis.diagnostics; const Real jacobian = dot(cross(basis.g1, basis.g2), basis.g3); if (!isFinite(jacobian) || std::fabs(jacobian) <= tolerance) { result.diagnostics.push_back( mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 material transform Jacobian is near zero")); return result; } if (hasError(result.diagnostics)) { return result; } const std::array contravariant = { (1.0 / jacobian) * cross(basis.g2, basis.g3), (1.0 / jacobian) * cross(basis.g3, basis.g1), (1.0 / jacobian) * cross(basis.g1, basis.g2)}; const std::array local = {basis.local.e1, basis.local.e2, basis.local.e3}; std::array, 3> direction_cosines{}; for (std::size_t local_axis = 0; local_axis < 3; ++local_axis) { for (std::size_t convected_axis = 0; convected_axis < 3; ++convected_axis) { direction_cosines[local_axis][convected_axis] = dot(local[local_axis], contravariant[convected_axis]); } } for (std::size_t column = 0; column < 6; ++column) { const auto covariant_tensor = mitc4TensorFromEngineeringComponent(column); std::array, 3> local_tensor{}; for (std::size_t a = 0; a < 3; ++a) { for (std::size_t b = 0; b < 3; ++b) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t j = 0; j < 3; ++j) { local_tensor[a][b] += direction_cosines[a][i] * direction_cosines[b][j] * covariant_tensor[i][j]; } } } } const auto local_vector = mitc4EngineeringVectorFromTensor(local_tensor); for (std::size_t row = 0; row < 6; ++row) { result.matrix[row][column] = local_vector[row]; } } return result; } inline MITC4MaterialIntegrationData mitc4BuildMaterialIntegrationData(const MITC4Geometry& geometry, Real elastic_modulus, Real poisson_ratio, Real shear_correction = 5.0 / 6.0) { MITC4MaterialIntegrationData data; const auto material = mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, shear_correction); appendDiagnostics(data.diagnostics, material.diagnostics); if (hasError(data.diagnostics)) { return data; } for (const MITC4IntegrationPoint& point : mitc4GaussQuadrature2x2x2()) { MITC4MaterialIntegrationSample sample; sample.point = point; sample.local_material = material.matrix; sample.basis = computeMITC4IntegrationBasis(geometry, point.xi, point.eta, point.zeta); appendDiagnostics(sample.diagnostics, sample.basis.diagnostics); const auto transform = mitc4CovariantToLocalStrainTransform(sample.basis); sample.strain_transform = transform.matrix; appendDiagnostics(sample.diagnostics, transform.diagnostics); sample.strain_rows = mitc4TiedCovariantStrainRows(geometry, point.xi, point.eta, point.zeta); appendDiagnostics(sample.diagnostics, sample.strain_rows.diagnostics); if (!hasError(sample.diagnostics)) { sample.convected_material = mitc4TransformMaterialMatrix(sample.local_material, sample.strain_transform); } appendDiagnostics(data.diagnostics, sample.diagnostics); data.samples.push_back(sample); } return data; } } // namespace fesa