feat: rebuild mitc4 stiffness drilling
This commit is contained in:
+198
-170
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user