feat: rebuild mitc4 stiffness drilling

This commit is contained in:
NINI
2026-05-04 22:55:52 +09:00
parent 2e1bd99d05
commit 5465ea61d8
5 changed files with 344 additions and 176 deletions
+198 -170
View File
@@ -1885,189 +1885,217 @@ inline LocalBasis computeLocalBasis(const std::array<Vec3, 4>& coordinates) {
return {frame.v1, frame.v2, frame.vn};
}
struct NaturalDerivatives {
ShapeData shape;
std::array<Real, 4> dx{};
std::array<Real, 4> dy{};
Real det_j = 0.0;
struct ElementStiffnessOptions {
Real drilling_stiffness_scale = 1.0e-3;
};
inline NaturalDerivatives naturalDerivatives(const std::array<std::array<Real, 2>, 4>& xy, Real r, Real s) {
NaturalDerivatives data;
data.shape = shapeFunctions(r, s);
Real j00 = 0.0;
Real j01 = 0.0;
Real j10 = 0.0;
Real j11 = 0.0;
for (std::size_t i = 0; i < 4; ++i) {
j00 += data.shape.dr[i] * xy[i][0];
j01 += data.shape.ds[i] * xy[i][0];
j10 += data.shape.dr[i] * xy[i][1];
j11 += data.shape.ds[i] * xy[i][1];
struct MITC4DrillingStabilizationResult {
DenseMatrix local_with_drilling;
Real reference_diagonal = 0.0;
Real drilling_stiffness = 0.0;
Real drilling_stiffness_scale = 0.0;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
data.det_j = j00 * j11 - j01 * j10;
if (std::fabs(data.det_j) < 1.0e-14) {
throw std::runtime_error("invalid shell element jacobian");
};
struct MITC4ElementStiffnessResult {
DenseMatrix local_without_drilling;
DenseMatrix local_with_drilling;
DenseMatrix global;
std::size_t integration_point_count = 0;
Real drilling_reference_diagonal = 0.0;
Real drilling_stiffness = 0.0;
Real drilling_stiffness_scale = 0.0;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
const Real inv00 = j11 / data.det_j;
const Real inv01 = -j01 / data.det_j;
const Real inv10 = -j10 / data.det_j;
const Real inv11 = j00 / data.det_j;
for (std::size_t i = 0; i < 4; ++i) {
data.dx[i] = inv00 * data.shape.dr[i] + inv01 * data.shape.ds[i];
data.dy[i] = inv10 * data.shape.dr[i] + inv11 * data.shape.ds[i];
};
inline DenseMatrix mitc4LocalDofTransform(const MITC4Geometry& geometry) {
DenseMatrix transform(24, 24);
for (LocalIndex node = 0; node < 4; ++node) {
const LocalIndex base = 6 * node;
const auto& frame = geometry.nodal_frames[static_cast<std::size_t>(node)];
const std::array<Vec3, 3> axes = {frame.v1, frame.v2, frame.vn};
for (LocalIndex local_axis = 0; local_axis < 3; ++local_axis) {
const Vec3 axis = axes[static_cast<std::size_t>(local_axis)];
transform(base + local_axis, base + 0) = axis.x;
transform(base + local_axis, base + 1) = axis.y;
transform(base + local_axis, base + 2) = axis.z;
transform(base + 3 + local_axis, base + 3) = axis.x;
transform(base + 3 + local_axis, base + 4) = axis.y;
transform(base + 3 + local_axis, base + 5) = axis.z;
}
}
return data;
return transform;
}
struct ElementStiffnessOptions {
Real drilling_stiffness_scale = 1.0e-6;
};
inline MITC4StrainRows mitc4TransformStrainRowsToLocalDofs(const MITC4StrainRows& global_rows,
const DenseMatrix& local_dof_transform) {
MITC4StrainRows local_rows;
local_rows.diagnostics = global_rows.diagnostics;
if (hasError(local_rows.diagnostics)) {
return local_rows;
}
for (std::size_t component = 0; component < 6; ++component) {
for (LocalIndex local_dof = 0; local_dof < 24; ++local_dof) {
Real value = 0.0;
for (LocalIndex global_dof = 0; global_dof < 24; ++global_dof) {
value += global_rows.rows[component][static_cast<std::size_t>(global_dof)] *
local_dof_transform(local_dof, global_dof);
}
local_rows.rows[component][static_cast<std::size_t>(local_dof)] = value;
}
}
return local_rows;
}
inline void accumulateMITC4BtDB(DenseMatrix& stiffness, const MITC4StrainRows& rows,
const MITC4MaterialMatrix& material, Real factor) {
for (LocalIndex i = 0; i < 24; ++i) {
for (LocalIndex j = 0; j < 24; ++j) {
Real value = 0.0;
for (std::size_t a = 0; a < 6; ++a) {
for (std::size_t b = 0; b < 6; ++b) {
value += rows.rows[a][static_cast<std::size_t>(i)] * material[a][b] *
rows.rows[b][static_cast<std::size_t>(j)];
}
}
stiffness.add(i, j, value * factor);
}
}
}
inline Real mitc4MinimumPositivePhysicalLocalDiagonal(const DenseMatrix& local_without_drilling,
Real tolerance = 1.0e-12) {
Real minimum = std::numeric_limits<Real>::infinity();
for (LocalIndex node = 0; node < 4; ++node) {
for (LocalIndex local_dof = 0; local_dof < 5; ++local_dof) {
const Real diagonal = local_without_drilling(6 * node + local_dof, 6 * node + local_dof);
if (isFinite(diagonal) && diagonal > tolerance) {
minimum = std::min(minimum, diagonal);
}
}
}
return minimum;
}
inline MITC4DrillingStabilizationResult mitc4ApplyDrillingStabilization(const DenseMatrix& local_without_drilling,
Real drilling_stiffness_scale,
Real tolerance = 1.0e-12) {
MITC4DrillingStabilizationResult result;
result.local_with_drilling = local_without_drilling;
result.drilling_stiffness_scale = drilling_stiffness_scale;
if (!isFinite(drilling_stiffness_scale) || drilling_stiffness_scale < 0.0) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-DRILLING-SCALE", "MITC4 drilling stiffness scale must be non-negative and finite"));
return result;
}
result.reference_diagonal = mitc4MinimumPositivePhysicalLocalDiagonal(local_without_drilling, tolerance);
if (!isFinite(result.reference_diagonal)) {
result.diagnostics.push_back(mitc4Diagnostic("FESA-MITC4-DRILLING-REFERENCE",
"MITC4 drilling stiffness reference diagonal is not positive"));
return result;
}
result.drilling_stiffness = drilling_stiffness_scale * result.reference_diagonal;
for (LocalIndex node = 0; node < 4; ++node) {
const LocalIndex gamma = 6 * node + 5;
result.local_with_drilling.add(gamma, gamma, result.drilling_stiffness);
}
return result;
}
inline DenseMatrix mitc4TransformLocalStiffnessToGlobal(const DenseMatrix& local_stiffness,
const DenseMatrix& local_dof_transform) {
DenseMatrix global(24, 24);
for (LocalIndex i = 0; i < 24; ++i) {
for (LocalIndex j = 0; j < 24; ++j) {
Real value = 0.0;
for (LocalIndex a = 0; a < 24; ++a) {
for (LocalIndex b = 0; b < 24; ++b) {
value += local_dof_transform(a, i) * local_stiffness(a, b) * local_dof_transform(b, j);
}
}
global(i, j) = value;
}
}
return global;
}
inline MITC4ElementStiffnessResult mitc4ElementStiffness(const std::array<Vec3, 4>& coordinates,
Real elastic_modulus, Real poisson_ratio, Real thickness,
ElementStiffnessOptions options = {}) {
MITC4ElementStiffnessResult result;
result.local_without_drilling = DenseMatrix(24, 24);
result.local_with_drilling = DenseMatrix(24, 24);
result.global = DenseMatrix(24, 24);
result.drilling_stiffness_scale = options.drilling_stiffness_scale;
const MITC4Geometry geometry = buildMITC4Geometry(coordinates, thickness);
appendDiagnostics(result.diagnostics, geometry.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
const MITC4MaterialIntegrationData integration =
mitc4BuildMaterialIntegrationData(geometry, elastic_modulus, poisson_ratio);
appendDiagnostics(result.diagnostics, integration.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
result.integration_point_count = integration.samples.size();
const DenseMatrix local_dof_transform = mitc4LocalDofTransform(geometry);
for (const MITC4MaterialIntegrationSample& sample : integration.samples) {
const MITC4StrainRows local_rows =
mitc4TransformStrainRowsToLocalDofs(sample.strain_rows, local_dof_transform);
appendDiagnostics(result.diagnostics, local_rows.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
const Real factor = std::fabs(sample.basis.jacobian) * sample.point.weight;
accumulateMITC4BtDB(result.local_without_drilling, local_rows, sample.convected_material, factor);
}
const auto drilling = mitc4ApplyDrillingStabilization(result.local_without_drilling, options.drilling_stiffness_scale);
appendDiagnostics(result.diagnostics, drilling.diagnostics);
result.local_with_drilling = drilling.local_with_drilling;
result.drilling_reference_diagonal = drilling.reference_diagonal;
result.drilling_stiffness = drilling.drilling_stiffness;
result.drilling_stiffness_scale = drilling.drilling_stiffness_scale;
if (hasError(result.diagnostics)) {
return result;
}
result.global = mitc4TransformLocalStiffnessToGlobal(result.local_with_drilling, local_dof_transform);
return result;
}
inline std::vector<Real> mitc4ElementInternalForce(const MITC4ElementStiffnessResult& stiffness,
const std::vector<Real>& element_displacement) {
if (stiffness.global.rows() != static_cast<LocalIndex>(element_displacement.size())) {
throw std::runtime_error("MITC4 internal force size mismatch");
}
return stiffness.global.multiply(element_displacement);
}
class MITC4ElementKernel {
public:
DenseMatrix stiffness(const std::array<Vec3, 4>& coordinates, Real elastic_modulus, Real poisson_ratio, Real thickness,
ElementStiffnessOptions options = {}) const {
const LocalBasis basis = computeLocalBasis(coordinates);
std::array<std::array<Real, 2>, 4> xy{};
for (std::size_t i = 0; i < 4; ++i) {
const Vec3 relative = coordinates[i] - coordinates[0];
xy[i] = {dot(relative, basis.e1), dot(relative, basis.e2)};
const auto result = mitc4ElementStiffness(coordinates, elastic_modulus, poisson_ratio, thickness, options);
if (!result.ok()) {
throw std::runtime_error(result.diagnostics.empty() ? "invalid MITC4 stiffness"
: result.diagnostics.front().message);
}
DenseMatrix local(24, 24);
const Real membrane_factor = elastic_modulus * thickness / (1.0 - poisson_ratio * poisson_ratio);
const Real bending_factor = elastic_modulus * thickness * thickness * thickness / (12.0 * (1.0 - poisson_ratio * poisson_ratio));
const Real shear_factor = (5.0 / 6.0) * elastic_modulus * thickness / (2.0 * (1.0 + poisson_ratio));
const Real gauss = 1.0 / std::sqrt(3.0);
const std::array<Real, 2> points = {-gauss, gauss};
for (Real r : points) {
for (Real s : points) {
const NaturalDerivatives d = naturalDerivatives(xy, r, s);
DenseMatrix b(8, 24);
addMembraneB(b, d);
addBendingB(b, d);
addMitcShearB(b, xy, r, s);
accumulateBtDB(local, b, membrane_factor, bending_factor, shear_factor, poisson_ratio, std::fabs(d.det_j));
addDrilling(local, d, elastic_modulus * thickness * options.drilling_stiffness_scale, std::fabs(d.det_j));
}
}
return transformToGlobal(local, basis);
}
private:
static void addMembraneB(DenseMatrix& b, const NaturalDerivatives& d) {
for (LocalIndex i = 0; i < 4; ++i) {
const LocalIndex c = 6 * i;
b(0, c + 0) = d.dx[static_cast<std::size_t>(i)];
b(1, c + 1) = d.dy[static_cast<std::size_t>(i)];
b(2, c + 0) = d.dy[static_cast<std::size_t>(i)];
b(2, c + 1) = d.dx[static_cast<std::size_t>(i)];
}
}
static void addBendingB(DenseMatrix& b, const NaturalDerivatives& d) {
for (LocalIndex i = 0; i < 4; ++i) {
const LocalIndex c = 6 * i;
b(3, c + 4) = -d.dx[static_cast<std::size_t>(i)];
b(4, c + 3) = d.dy[static_cast<std::size_t>(i)];
b(5, c + 3) = d.dx[static_cast<std::size_t>(i)];
b(5, c + 4) = -d.dy[static_cast<std::size_t>(i)];
}
}
static void addStandardShearRow(DenseMatrix& b, LocalIndex row, const NaturalDerivatives& d, bool gamma_xz, Real scale) {
for (LocalIndex i = 0; i < 4; ++i) {
const LocalIndex c = 6 * i;
if (gamma_xz) {
b(row, c + 2) += scale * d.dx[static_cast<std::size_t>(i)];
b(row, c + 4) += scale * d.shape.n[static_cast<std::size_t>(i)];
} else {
b(row, c + 2) += scale * d.dy[static_cast<std::size_t>(i)];
b(row, c + 3) -= scale * d.shape.n[static_cast<std::size_t>(i)];
}
}
}
static void addMitcShearB(DenseMatrix& b, const std::array<std::array<Real, 2>, 4>& xy, Real r, Real s) {
addStandardShearRow(b, 6, naturalDerivatives(xy, 0.0, -1.0), true, 0.5 * (1.0 - s));
addStandardShearRow(b, 6, naturalDerivatives(xy, 0.0, 1.0), true, 0.5 * (1.0 + s));
addStandardShearRow(b, 7, naturalDerivatives(xy, -1.0, 0.0), false, 0.5 * (1.0 - r));
addStandardShearRow(b, 7, naturalDerivatives(xy, 1.0, 0.0), false, 0.5 * (1.0 + r));
}
static void accumulateBtDB(DenseMatrix& k, const DenseMatrix& b, Real membrane_factor, Real bending_factor, Real shear_factor, Real poisson_ratio, Real det_j) {
std::array<std::array<Real, 8>, 8> d{};
d[0][0] = membrane_factor;
d[0][1] = poisson_ratio * membrane_factor;
d[1][0] = poisson_ratio * membrane_factor;
d[1][1] = membrane_factor;
d[2][2] = membrane_factor * (1.0 - poisson_ratio) / 2.0;
d[3][3] = bending_factor;
d[3][4] = poisson_ratio * bending_factor;
d[4][3] = poisson_ratio * bending_factor;
d[4][4] = bending_factor;
d[5][5] = bending_factor * (1.0 - poisson_ratio) / 2.0;
d[6][6] = shear_factor;
d[7][7] = shear_factor;
for (LocalIndex i = 0; i < 24; ++i) {
for (LocalIndex j = 0; j < 24; ++j) {
Real value = 0.0;
for (LocalIndex row = 0; row < 8; ++row) {
for (LocalIndex col = 0; col < 8; ++col) {
value += b(row, i) * d[static_cast<std::size_t>(row)][static_cast<std::size_t>(col)] * b(col, j);
}
}
k.add(i, j, value * det_j);
}
}
}
static void addDrilling(DenseMatrix& k, const NaturalDerivatives& d, Real drill_factor, Real det_j) {
std::array<Real, 24> b{};
for (LocalIndex i = 0; i < 4; ++i) {
const LocalIndex c = 6 * i;
b[static_cast<std::size_t>(c + 0)] = -0.5 * d.dy[static_cast<std::size_t>(i)];
b[static_cast<std::size_t>(c + 1)] = 0.5 * d.dx[static_cast<std::size_t>(i)];
b[static_cast<std::size_t>(c + 5)] = -d.shape.n[static_cast<std::size_t>(i)];
}
for (LocalIndex i = 0; i < 24; ++i) {
for (LocalIndex j = 0; j < 24; ++j) {
k.add(i, j, b[static_cast<std::size_t>(i)] * drill_factor * b[static_cast<std::size_t>(j)] * det_j);
}
}
}
static DenseMatrix transformToGlobal(const DenseMatrix& local, const LocalBasis& basis) {
DenseMatrix transform(24, 24);
const std::array<Vec3, 3> axes = {basis.e1, basis.e2, basis.e3};
for (LocalIndex node = 0; node < 4; ++node) {
for (LocalIndex local_axis = 0; local_axis < 3; ++local_axis) {
const Vec3 axis = axes[static_cast<std::size_t>(local_axis)];
const LocalIndex base = 6 * node;
transform(base + local_axis, base + 0) = axis.x;
transform(base + local_axis, base + 1) = axis.y;
transform(base + local_axis, base + 2) = axis.z;
transform(base + 3 + local_axis, base + 3) = axis.x;
transform(base + 3 + local_axis, base + 4) = axis.y;
transform(base + 3 + local_axis, base + 5) = axis.z;
}
}
DenseMatrix global(24, 24);
for (LocalIndex i = 0; i < 24; ++i) {
for (LocalIndex j = 0; j < 24; ++j) {
Real value = 0.0;
for (LocalIndex a = 0; a < 24; ++a) {
for (LocalIndex b = 0; b < 24; ++b) {
value += transform(a, i) * local(a, b) * transform(b, j);
}
}
global(i, j) = value;
}
}
return global;
return result.global;
}
};