refactor: extract mitc4 geometry strain helpers

This commit is contained in:
NINI
2026-05-05 23:11:23 +09:00
parent 421ad5a707
commit 150653c3c7
9 changed files with 691 additions and 434 deletions
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include "fesa/Element/MITC4Geometry.hpp"
#include "fesa/Element/MITC4Kinematics.hpp"
#include "fesa/ModuleInfo.hpp"
namespace fesa::module {
+265
View File
@@ -0,0 +1,265 @@
#pragma once
#include "fesa/Math/Math.hpp"
#include "fesa/Util/Util.hpp"
#include <array>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace fesa {
struct ShapeData {
std::array<Real, 4> n{};
std::array<Real, 4> dr{};
std::array<Real, 4> ds{};
};
inline ShapeData shapeFunctions(Real r, Real s) {
return { {
0.25 * (1.0 - r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 + s),
0.25 * (1.0 - r) * (1.0 + s),
},
{
-0.25 * (1.0 - s),
0.25 * (1.0 - s),
0.25 * (1.0 + s),
-0.25 * (1.0 + s),
},
{
-0.25 * (1.0 - r),
-0.25 * (1.0 + r),
0.25 * (1.0 + r),
0.25 * (1.0 - r),
} };
}
struct LocalBasis {
Vec3 e1;
Vec3 e2;
Vec3 e3;
};
struct MITC4NaturalPoint {
Real xi = 0.0;
Real eta = 0.0;
};
struct MITC4TyingPoint {
std::string label;
MITC4NaturalPoint natural;
std::array<LocalIndex, 2> edge_node_indices{};
};
inline std::array<MITC4NaturalPoint, 4> mitc4NodeNaturalCoordinates() {
return {{{-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0}}};
}
inline std::array<MITC4TyingPoint, 4> mitc4TyingPoints() {
return {{{"A", {0.0, -1.0}, {0, 1}},
{"B", {-1.0, 0.0}, {0, 3}},
{"C", {0.0, 1.0}, {3, 2}},
{"D", {1.0, 0.0}, {1, 2}}}};
}
struct MITC4DirectorFrame {
Vec3 v1;
Vec3 v2;
Vec3 vn;
};
struct MITC4MidsurfaceDerivatives {
ShapeData shape;
Vec3 g1;
Vec3 g2;
};
struct MITC4Geometry {
std::array<Vec3, 4> coordinates{};
Real thickness = 0.0;
ShapeData center_shape;
Vec3 g1_center;
Vec3 g2_center;
Vec3 center_normal;
std::array<MITC4DirectorFrame, 4> nodal_frames{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4IntegrationBasis {
ShapeData shape;
Vec3 g1;
Vec3 g2;
Vec3 g3;
Real jacobian = 0.0;
LocalBasis local;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
inline Vec3 globalEX() {
return {1.0, 0.0, 0.0};
}
inline Vec3 globalEY() {
return {0.0, 1.0, 0.0};
}
inline Vec3 globalEZ() {
return {0.0, 0.0, 1.0};
}
inline Diagnostic mitc4Diagnostic(std::string code, std::string message) {
return makeDiagnostic(Severity::Error, std::move(code), std::move(message), "mitc4", "<element>", 0);
}
inline void appendDiagnostics(std::vector<Diagnostic>& target, const std::vector<Diagnostic>& source) {
target.insert(target.end(), source.begin(), source.end());
}
inline MITC4MidsurfaceDerivatives mitc4MidsurfaceDerivatives(const std::array<Vec3, 4>& coordinates,
Real xi,
Real eta) {
MITC4MidsurfaceDerivatives result;
result.shape = shapeFunctions(xi, eta);
for (std::size_t i = 0; i < 4; ++i) {
result.g1 = result.g1 + result.shape.dr[i] * coordinates[i];
result.g2 = result.g2 + result.shape.ds[i] * coordinates[i];
}
return result;
}
inline std::optional<Vec3> firstNormalizedCross(const std::array<Vec3, 3>& axes, const Vec3& vector, Real tolerance) {
for (const Vec3& axis : axes) {
auto candidate = normalizedIfValid(cross(axis, vector), tolerance);
if (candidate) {
return candidate;
}
}
return std::nullopt;
}
inline std::optional<MITC4DirectorFrame> buildMITC4DirectorFrame(const Vec3& normal, Real tolerance) {
auto v1 = normalizedIfValid(cross(globalEY(), normal), tolerance);
if (!v1) {
v1 = firstNormalizedCross({globalEZ(), globalEX(), globalEY()}, normal, tolerance);
}
if (!v1) {
return std::nullopt;
}
auto v2 = normalizedIfValid(cross(normal, *v1), tolerance);
if (!v2) {
return std::nullopt;
}
return MITC4DirectorFrame{*v1, *v2, normal};
}
inline MITC4Geometry buildMITC4Geometry(const std::array<Vec3, 4>& coordinates,
Real thickness,
Real tolerance = 1.0e-12) {
MITC4Geometry geometry;
geometry.coordinates = coordinates;
geometry.thickness = thickness;
if (!isFinite(thickness) || thickness <= tolerance) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-THICKNESS", "MITC4 shell thickness must be positive and finite"));
}
for (const Vec3& coordinate : coordinates) {
if (!isFinite(coordinate)) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-COORDINATE", "MITC4 element coordinates must be finite"));
break;
}
}
const auto center = mitc4MidsurfaceDerivatives(coordinates, 0.0, 0.0);
geometry.center_shape = center.shape;
geometry.g1_center = center.g1;
geometry.g2_center = center.g2;
const auto normal = normalizedIfValid(cross(center.g1, center.g2), tolerance);
if (!normal) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-NORMAL", "MITC4 element center normal is near zero"));
return geometry;
}
geometry.center_normal = *normal;
const auto frame = buildMITC4DirectorFrame(*normal, tolerance);
if (!frame) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 nodal director basis could not be constructed"));
return geometry;
}
geometry.nodal_frames.fill(*frame);
return geometry;
}
inline MITC4IntegrationBasis computeMITC4IntegrationBasis(const MITC4Geometry& geometry,
Real xi,
Real eta,
Real zeta,
Real tolerance = 1.0e-12) {
MITC4IntegrationBasis result;
result.diagnostics = geometry.diagnostics;
result.shape = shapeFunctions(xi, eta);
for (std::size_t i = 0; i < 4; ++i) {
const Vec3& coordinate = geometry.coordinates[i];
const Vec3& normal = geometry.nodal_frames[i].vn;
result.g1 = result.g1 + result.shape.dr[i] * coordinate +
(0.5 * zeta * geometry.thickness * result.shape.dr[i]) * normal;
result.g2 = result.g2 + result.shape.ds[i] * coordinate +
(0.5 * zeta * geometry.thickness * result.shape.ds[i]) * normal;
result.g3 = result.g3 + (0.5 * geometry.thickness * result.shape.n[i]) * normal;
}
result.jacobian = dot(cross(result.g1, result.g2), result.g3);
if (!isFinite(result.jacobian) || std::fabs(result.jacobian) <= tolerance) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 element Jacobian is near zero"));
}
const auto e3 = normalizedIfValid(result.g3, tolerance);
if (!e3) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis normal is near zero"));
return result;
}
auto e1 = normalizedIfValid(cross(result.g2, *e3), tolerance);
if (!e1) {
e1 = firstNormalizedCross({globalEY(), globalEZ(), globalEX()}, *e3, tolerance);
}
if (!e1) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis tangent could not be constructed"));
return result;
}
const auto e2 = normalizedIfValid(cross(*e3, *e1), tolerance);
if (!e2) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis is not right-handed"));
return result;
}
result.local = {*e1, *e2, *e3};
return result;
}
inline LocalBasis computeLocalBasis(const std::array<Vec3, 4>& coordinates) {
const MITC4Geometry geometry = buildMITC4Geometry(coordinates, 1.0);
if (!geometry.ok()) {
throw std::runtime_error("invalid MITC4 geometry");
}
const MITC4DirectorFrame& frame = geometry.nodal_frames[0];
return {frame.v1, frame.v2, frame.vn};
}
} // namespace fesa
+205
View File
@@ -0,0 +1,205 @@
#pragma once
#include "fesa/Element/MITC4Geometry.hpp"
#include <array>
#include <cstddef>
#include <string>
#include <vector>
namespace fesa {
using MITC4ElementDofVector = std::array<Real, 24>;
using MITC4StrainVector = std::array<Real, 6>;
using MITC4StrainRow = std::array<Real, 24>;
using MITC4MaterialMatrix = std::array<std::array<Real, 6>, 6>;
enum class MITC4StrainComponent {
Eps11 = 0,
Eps22 = 1,
Eps33 = 2,
Gamma23 = 3,
Gamma13 = 4,
Gamma12 = 5
};
inline std::size_t strainComponentIndex(MITC4StrainComponent component) {
return static_cast<std::size_t>(component);
}
inline std::array<std::string, 6> mitc4StrainComponentLabels() {
return {"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"};
}
struct MITC4LocalRotations {
Real alpha = 0.0;
Real beta = 0.0;
Real gamma = 0.0;
};
struct MITC4DisplacementDerivatives {
ShapeData shape;
Vec3 displacement;
Vec3 du_dxi;
Vec3 du_deta;
Vec3 du_dzeta;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4StrainEvaluation {
MITC4StrainVector values{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4StrainRows {
std::array<MITC4StrainRow, 6> rows{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
inline Vec3 mitc4NodalTranslation(const MITC4ElementDofVector& values, std::size_t node) {
const std::size_t base = 6 * node;
return {values[base + 0], values[base + 1], values[base + 2]};
}
inline Vec3 mitc4NodalRotation(const MITC4ElementDofVector& values, std::size_t node) {
const std::size_t base = 6 * node;
return {values[base + 3], values[base + 4], values[base + 5]};
}
inline MITC4LocalRotations mitc4LocalRotations(const MITC4DirectorFrame& frame, const Vec3& global_rotation) {
return {dot(global_rotation, frame.v1), dot(global_rotation, frame.v2), dot(global_rotation, frame.vn)};
}
inline Vec3 mitc4DirectorIncrement(const MITC4DirectorFrame& frame, const Vec3& global_rotation) {
const MITC4LocalRotations rotations = mitc4LocalRotations(frame, global_rotation);
return (-rotations.alpha) * frame.v2 + rotations.beta * frame.v1;
}
inline MITC4DisplacementDerivatives mitc4DisplacementDerivatives(const MITC4Geometry& geometry,
const MITC4ElementDofVector& values,
Real xi,
Real eta,
Real zeta) {
MITC4DisplacementDerivatives result;
result.diagnostics = geometry.diagnostics;
result.shape = shapeFunctions(xi, eta);
for (std::size_t node = 0; node < 4; ++node) {
const Vec3 translation = mitc4NodalTranslation(values, node);
const Vec3 rotation = mitc4NodalRotation(values, node);
const Vec3 q = mitc4DirectorIncrement(geometry.nodal_frames[node], rotation);
const Real n = result.shape.n[node];
const Real dn_dxi = result.shape.dr[node];
const Real dn_deta = result.shape.ds[node];
result.displacement = result.displacement + n * translation + (0.5 * zeta * geometry.thickness * n) * q;
result.du_dxi = result.du_dxi + dn_dxi * translation + (0.5 * zeta * geometry.thickness * dn_dxi) * q;
result.du_deta = result.du_deta + dn_deta * translation + (0.5 * zeta * geometry.thickness * dn_deta) * q;
result.du_dzeta = result.du_dzeta + (0.5 * geometry.thickness * n) * q;
}
return result;
}
inline void assignMITC4CovariantStrain(MITC4StrainVector& values,
const MITC4IntegrationBasis& basis,
const MITC4DisplacementDerivatives& derivatives) {
values[strainComponentIndex(MITC4StrainComponent::Eps11)] = dot(derivatives.du_dxi, basis.g1);
values[strainComponentIndex(MITC4StrainComponent::Eps22)] = dot(derivatives.du_deta, basis.g2);
values[strainComponentIndex(MITC4StrainComponent::Eps33)] = 0.0;
values[strainComponentIndex(MITC4StrainComponent::Gamma23)] = dot(derivatives.du_deta, basis.g3) + dot(derivatives.du_dzeta, basis.g2);
values[strainComponentIndex(MITC4StrainComponent::Gamma13)] = dot(derivatives.du_dxi, basis.g3) + dot(derivatives.du_dzeta, basis.g1);
values[strainComponentIndex(MITC4StrainComponent::Gamma12)] = dot(derivatives.du_dxi, basis.g2) + dot(derivatives.du_deta, basis.g1);
}
inline MITC4StrainEvaluation mitc4DirectCovariantStrain(const MITC4Geometry& geometry,
const MITC4ElementDofVector& values,
Real xi,
Real eta,
Real zeta) {
MITC4StrainEvaluation result;
const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta);
appendDiagnostics(result.diagnostics, basis.diagnostics);
const auto derivatives = mitc4DisplacementDerivatives(geometry, values, xi, eta, zeta);
appendDiagnostics(result.diagnostics, derivatives.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
assignMITC4CovariantStrain(result.values, basis, derivatives);
return result;
}
inline MITC4StrainRows mitc4DirectCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) {
MITC4StrainRows result;
const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta);
appendDiagnostics(result.diagnostics, basis.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
for (std::size_t dof = 0; dof < 24; ++dof) {
MITC4ElementDofVector unit{};
unit.fill(0.0);
unit[dof] = 1.0;
const auto derivatives = mitc4DisplacementDerivatives(geometry, unit, xi, eta, zeta);
appendDiagnostics(result.diagnostics, derivatives.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
MITC4StrainVector values{};
assignMITC4CovariantStrain(values, basis, derivatives);
for (std::size_t component = 0; component < 6; ++component) {
result.rows[component][dof] = values[component];
}
}
return result;
}
inline MITC4StrainEvaluation evaluateMITC4StrainRows(const MITC4StrainRows& rows,
const MITC4ElementDofVector& values) {
MITC4StrainEvaluation result;
result.diagnostics = rows.diagnostics;
if (hasError(result.diagnostics)) {
return result;
}
for (std::size_t component = 0; component < 6; ++component) {
for (std::size_t dof = 0; dof < 24; ++dof) {
result.values[component] += rows.rows[component][dof] * values[dof];
}
}
return result;
}
inline MITC4StrainRows mitc4TiedCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) {
MITC4StrainRows result = mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta);
const auto direct_a = mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, zeta);
const auto direct_b = mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, zeta);
const auto direct_c = mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, zeta);
const auto direct_d = mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, zeta);
appendDiagnostics(result.diagnostics, direct_a.diagnostics);
appendDiagnostics(result.diagnostics, direct_b.diagnostics);
appendDiagnostics(result.diagnostics, direct_c.diagnostics);
appendDiagnostics(result.diagnostics, direct_d.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
const std::size_t gamma23 = strainComponentIndex(MITC4StrainComponent::Gamma23);
const std::size_t gamma13 = strainComponentIndex(MITC4StrainComponent::Gamma13);
for (std::size_t dof = 0; dof < 24; ++dof) {
result.rows[gamma13][dof] = 0.5 * (1.0 - eta) * direct_a.rows[gamma13][dof] +
0.5 * (1.0 + eta) * direct_c.rows[gamma13][dof];
result.rows[gamma23][dof] = 0.5 * (1.0 - xi) * direct_b.rows[gamma23][dof] +
0.5 * (1.0 + xi) * direct_d.rows[gamma23][dof];
}
return result;
}
} // namespace fesa
+1 -429
View File
@@ -2,6 +2,7 @@
#include "fesa/Boundary/Boundary.hpp"
#include "fesa/Core/Core.hpp"
#include "fesa/Element/Element.hpp"
#include "fesa/IO/IO.hpp"
#include "fesa/Load/Load.hpp"
#include "fesa/Math/Math.hpp"
@@ -68,161 +69,6 @@ inline std::vector<Real> recoverFullReaction(const DenseMatrix& k_full, const st
return reaction;
}
struct ShapeData {
std::array<Real, 4> n{};
std::array<Real, 4> dr{};
std::array<Real, 4> ds{};
};
inline ShapeData shapeFunctions(Real r, Real s) {
return {{
0.25 * (1.0 - r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 - s),
0.25 * (1.0 + r) * (1.0 + s),
0.25 * (1.0 - r) * (1.0 + s),
},
{
-0.25 * (1.0 - s),
0.25 * (1.0 - s),
0.25 * (1.0 + s),
-0.25 * (1.0 + s),
},
{
-0.25 * (1.0 - r),
-0.25 * (1.0 + r),
0.25 * (1.0 + r),
0.25 * (1.0 - r),
}};
}
struct LocalBasis {
Vec3 e1;
Vec3 e2;
Vec3 e3;
};
struct MITC4NaturalPoint {
Real xi = 0.0;
Real eta = 0.0;
};
struct MITC4TyingPoint {
std::string label;
MITC4NaturalPoint natural;
std::array<LocalIndex, 2> edge_node_indices{};
};
inline std::array<MITC4NaturalPoint, 4> mitc4NodeNaturalCoordinates() {
return {{{-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0}}};
}
inline std::array<MITC4TyingPoint, 4> mitc4TyingPoints() {
return {{{"A", {0.0, -1.0}, {0, 1}},
{"B", {-1.0, 0.0}, {0, 3}},
{"C", {0.0, 1.0}, {3, 2}},
{"D", {1.0, 0.0}, {1, 2}}}};
}
struct MITC4DirectorFrame {
Vec3 v1;
Vec3 v2;
Vec3 vn;
};
struct MITC4MidsurfaceDerivatives {
ShapeData shape;
Vec3 g1;
Vec3 g2;
};
struct MITC4Geometry {
std::array<Vec3, 4> coordinates{};
Real thickness = 0.0;
ShapeData center_shape;
Vec3 g1_center;
Vec3 g2_center;
Vec3 center_normal;
std::array<MITC4DirectorFrame, 4> nodal_frames{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4IntegrationBasis {
ShapeData shape;
Vec3 g1;
Vec3 g2;
Vec3 g3;
Real jacobian = 0.0;
LocalBasis local;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
using MITC4ElementDofVector = std::array<Real, 24>;
using MITC4StrainVector = std::array<Real, 6>;
using MITC4StrainRow = std::array<Real, 24>;
using MITC4MaterialMatrix = std::array<std::array<Real, 6>, 6>;
enum class MITC4StrainComponent {
Eps11 = 0,
Eps22 = 1,
Eps33 = 2,
Gamma23 = 3,
Gamma13 = 4,
Gamma12 = 5
};
inline std::size_t strainComponentIndex(MITC4StrainComponent component) {
return static_cast<std::size_t>(component);
}
inline std::array<std::string, 6> mitc4StrainComponentLabels() {
return {"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"};
}
struct MITC4LocalRotations {
Real alpha = 0.0;
Real beta = 0.0;
Real gamma = 0.0;
};
struct MITC4DisplacementDerivatives {
ShapeData shape;
Vec3 displacement;
Vec3 du_dxi;
Vec3 du_deta;
Vec3 du_dzeta;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4StrainEvaluation {
MITC4StrainVector values{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4StrainRows {
std::array<MITC4StrainRow, 6> rows{};
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
struct MITC4MaterialMatrixEvaluation {
MITC4MaterialMatrix matrix{};
std::vector<Diagnostic> diagnostics;
@@ -271,271 +117,6 @@ struct MITC4MaterialIntegrationData {
}
};
inline Vec3 globalEX() {
return {1.0, 0.0, 0.0};
}
inline Vec3 globalEY() {
return {0.0, 1.0, 0.0};
}
inline Vec3 globalEZ() {
return {0.0, 0.0, 1.0};
}
inline Diagnostic mitc4Diagnostic(std::string code, std::string message) {
return makeDiagnostic(Severity::Error, std::move(code), std::move(message), "mitc4", "<element>", 0);
}
inline void appendDiagnostics(std::vector<Diagnostic>& target, const std::vector<Diagnostic>& source) {
target.insert(target.end(), source.begin(), source.end());
}
inline MITC4MidsurfaceDerivatives mitc4MidsurfaceDerivatives(const std::array<Vec3, 4>& coordinates, Real xi, Real eta) {
MITC4MidsurfaceDerivatives result;
result.shape = shapeFunctions(xi, eta);
for (std::size_t i = 0; i < 4; ++i) {
result.g1 = result.g1 + result.shape.dr[i] * coordinates[i];
result.g2 = result.g2 + result.shape.ds[i] * coordinates[i];
}
return result;
}
inline std::optional<Vec3> firstNormalizedCross(const std::array<Vec3, 3>& axes, const Vec3& vector, Real tolerance) {
for (const Vec3& axis : axes) {
auto candidate = normalizedIfValid(cross(axis, vector), tolerance);
if (candidate) {
return candidate;
}
}
return std::nullopt;
}
inline std::optional<MITC4DirectorFrame> buildMITC4DirectorFrame(const Vec3& normal, Real tolerance) {
auto v1 = normalizedIfValid(cross(globalEY(), normal), tolerance);
if (!v1) {
v1 = firstNormalizedCross({globalEZ(), globalEX(), globalEY()}, normal, tolerance);
}
if (!v1) {
return std::nullopt;
}
auto v2 = normalizedIfValid(cross(normal, *v1), tolerance);
if (!v2) {
return std::nullopt;
}
return MITC4DirectorFrame{*v1, *v2, normal};
}
inline MITC4Geometry buildMITC4Geometry(const std::array<Vec3, 4>& coordinates, Real thickness, Real tolerance = 1.0e-12) {
MITC4Geometry geometry;
geometry.coordinates = coordinates;
geometry.thickness = thickness;
if (!isFinite(thickness) || thickness <= tolerance) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-THICKNESS", "MITC4 shell thickness must be positive and finite"));
}
for (const Vec3& coordinate : coordinates) {
if (!isFinite(coordinate)) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-COORDINATE", "MITC4 element coordinates must be finite"));
break;
}
}
const auto center = mitc4MidsurfaceDerivatives(coordinates, 0.0, 0.0);
geometry.center_shape = center.shape;
geometry.g1_center = center.g1;
geometry.g2_center = center.g2;
const auto normal = normalizedIfValid(cross(center.g1, center.g2), tolerance);
if (!normal) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-NORMAL", "MITC4 element center normal is near zero"));
return geometry;
}
geometry.center_normal = *normal;
const auto frame = buildMITC4DirectorFrame(*normal, tolerance);
if (!frame) {
geometry.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 nodal director basis could not be constructed"));
return geometry;
}
geometry.nodal_frames.fill(*frame);
return geometry;
}
inline MITC4IntegrationBasis computeMITC4IntegrationBasis(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta,
Real tolerance = 1.0e-12) {
MITC4IntegrationBasis result;
result.diagnostics = geometry.diagnostics;
result.shape = shapeFunctions(xi, eta);
for (std::size_t i = 0; i < 4; ++i) {
const Vec3& coordinate = geometry.coordinates[i];
const Vec3& normal = geometry.nodal_frames[i].vn;
result.g1 = result.g1 + result.shape.dr[i] * coordinate +
(0.5 * zeta * geometry.thickness * result.shape.dr[i]) * normal;
result.g2 = result.g2 + result.shape.ds[i] * coordinate +
(0.5 * zeta * geometry.thickness * result.shape.ds[i]) * normal;
result.g3 = result.g3 + (0.5 * geometry.thickness * result.shape.n[i]) * normal;
}
result.jacobian = dot(cross(result.g1, result.g2), result.g3);
if (!isFinite(result.jacobian) || std::fabs(result.jacobian) <= tolerance) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 element Jacobian is near zero"));
}
const auto e3 = normalizedIfValid(result.g3, tolerance);
if (!e3) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis normal is near zero"));
return result;
}
auto e1 = normalizedIfValid(cross(result.g2, *e3), tolerance);
if (!e1) {
e1 = firstNormalizedCross({globalEY(), globalEZ(), globalEX()}, *e3, tolerance);
}
if (!e1) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis tangent could not be constructed"));
return result;
}
const auto e2 = normalizedIfValid(cross(*e3, *e1), tolerance);
if (!e2) {
result.diagnostics.push_back(
mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis is not right-handed"));
return result;
}
result.local = {*e1, *e2, *e3};
return result;
}
inline Vec3 mitc4NodalTranslation(const MITC4ElementDofVector& values, std::size_t node) {
const std::size_t base = 6 * node;
return {values[base + 0], values[base + 1], values[base + 2]};
}
inline Vec3 mitc4NodalRotation(const MITC4ElementDofVector& values, std::size_t node) {
const std::size_t base = 6 * node;
return {values[base + 3], values[base + 4], values[base + 5]};
}
inline MITC4LocalRotations mitc4LocalRotations(const MITC4DirectorFrame& frame, const Vec3& global_rotation) {
return {dot(global_rotation, frame.v1), dot(global_rotation, frame.v2), dot(global_rotation, frame.vn)};
}
inline Vec3 mitc4DirectorIncrement(const MITC4DirectorFrame& frame, const Vec3& global_rotation) {
const MITC4LocalRotations rotations = mitc4LocalRotations(frame, global_rotation);
return (-rotations.alpha) * frame.v2 + rotations.beta * frame.v1;
}
inline MITC4DisplacementDerivatives mitc4DisplacementDerivatives(const MITC4Geometry& geometry, const MITC4ElementDofVector& values,
Real xi, Real eta, Real zeta) {
MITC4DisplacementDerivatives result;
result.diagnostics = geometry.diagnostics;
result.shape = shapeFunctions(xi, eta);
for (std::size_t node = 0; node < 4; ++node) {
const Vec3 translation = mitc4NodalTranslation(values, node);
const Vec3 rotation = mitc4NodalRotation(values, node);
const Vec3 q = mitc4DirectorIncrement(geometry.nodal_frames[node], rotation);
const Real n = result.shape.n[node];
const Real dn_dxi = result.shape.dr[node];
const Real dn_deta = result.shape.ds[node];
result.displacement = result.displacement + n * translation + (0.5 * zeta * geometry.thickness * n) * q;
result.du_dxi = result.du_dxi + dn_dxi * translation + (0.5 * zeta * geometry.thickness * dn_dxi) * q;
result.du_deta = result.du_deta + dn_deta * translation + (0.5 * zeta * geometry.thickness * dn_deta) * q;
result.du_dzeta = result.du_dzeta + (0.5 * geometry.thickness * n) * q;
}
return result;
}
inline void assignMITC4CovariantStrain(MITC4StrainVector& values, const MITC4IntegrationBasis& basis,
const MITC4DisplacementDerivatives& derivatives) {
values[strainComponentIndex(MITC4StrainComponent::Eps11)] = dot(derivatives.du_dxi, basis.g1);
values[strainComponentIndex(MITC4StrainComponent::Eps22)] = dot(derivatives.du_deta, basis.g2);
values[strainComponentIndex(MITC4StrainComponent::Eps33)] = 0.0;
values[strainComponentIndex(MITC4StrainComponent::Gamma23)] = dot(derivatives.du_deta, basis.g3) + dot(derivatives.du_dzeta, basis.g2);
values[strainComponentIndex(MITC4StrainComponent::Gamma13)] = dot(derivatives.du_dxi, basis.g3) + dot(derivatives.du_dzeta, basis.g1);
values[strainComponentIndex(MITC4StrainComponent::Gamma12)] = dot(derivatives.du_dxi, basis.g2) + dot(derivatives.du_deta, basis.g1);
}
inline MITC4StrainEvaluation mitc4DirectCovariantStrain(const MITC4Geometry& geometry, const MITC4ElementDofVector& values,
Real xi, Real eta, Real zeta) {
MITC4StrainEvaluation result;
const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta);
appendDiagnostics(result.diagnostics, basis.diagnostics);
const auto derivatives = mitc4DisplacementDerivatives(geometry, values, xi, eta, zeta);
appendDiagnostics(result.diagnostics, derivatives.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
assignMITC4CovariantStrain(result.values, basis, derivatives);
return result;
}
inline MITC4StrainRows mitc4DirectCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) {
MITC4StrainRows result;
const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta);
appendDiagnostics(result.diagnostics, basis.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
for (std::size_t dof = 0; dof < 24; ++dof) {
MITC4ElementDofVector unit{};
unit.fill(0.0);
unit[dof] = 1.0;
const auto derivatives = mitc4DisplacementDerivatives(geometry, unit, xi, eta, zeta);
appendDiagnostics(result.diagnostics, derivatives.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
MITC4StrainVector values{};
assignMITC4CovariantStrain(values, basis, derivatives);
for (std::size_t component = 0; component < 6; ++component) {
result.rows[component][dof] = values[component];
}
}
return result;
}
inline MITC4StrainEvaluation evaluateMITC4StrainRows(const MITC4StrainRows& rows, const MITC4ElementDofVector& values) {
MITC4StrainEvaluation result;
result.diagnostics = rows.diagnostics;
if (hasError(result.diagnostics)) {
return result;
}
for (std::size_t component = 0; component < 6; ++component) {
for (std::size_t dof = 0; dof < 24; ++dof) {
result.values[component] += rows.rows[component][dof] * values[dof];
}
}
return result;
}
inline MITC4StrainRows mitc4TiedCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) {
MITC4StrainRows result = mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta);
const auto direct_a = mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, zeta);
const auto direct_b = mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, zeta);
const auto direct_c = mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, zeta);
const auto direct_d = mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, zeta);
appendDiagnostics(result.diagnostics, direct_a.diagnostics);
appendDiagnostics(result.diagnostics, direct_b.diagnostics);
appendDiagnostics(result.diagnostics, direct_c.diagnostics);
appendDiagnostics(result.diagnostics, direct_d.diagnostics);
if (hasError(result.diagnostics)) {
return result;
}
const std::size_t gamma23 = strainComponentIndex(MITC4StrainComponent::Gamma23);
const std::size_t gamma13 = strainComponentIndex(MITC4StrainComponent::Gamma13);
for (std::size_t dof = 0; dof < 24; ++dof) {
result.rows[gamma13][dof] = 0.5 * (1.0 - eta) * direct_a.rows[gamma13][dof] +
0.5 * (1.0 + eta) * direct_c.rows[gamma13][dof];
result.rows[gamma23][dof] = 0.5 * (1.0 - xi) * direct_b.rows[gamma23][dof] +
0.5 * (1.0 + xi) * direct_d.rows[gamma23][dof];
}
return result;
}
inline std::array<MITC4IntegrationPoint, 8> mitc4GaussQuadrature2x2x2() {
const Real gauss = 1.0 / std::sqrt(3.0);
const std::array<Real, 2> points = {-gauss, gauss};
@@ -739,15 +320,6 @@ inline MITC4MaterialIntegrationData mitc4BuildMaterialIntegrationData(const MITC
return data;
}
inline LocalBasis computeLocalBasis(const std::array<Vec3, 4>& coordinates) {
const MITC4Geometry geometry = buildMITC4Geometry(coordinates, 1.0);
if (!geometry.ok()) {
throw std::runtime_error("invalid MITC4 geometry");
}
const MITC4DirectorFrame& frame = geometry.nodal_frames[0];
return {frame.v1, frame.v2, frame.vn};
}
struct ElementStiffnessOptions {
Real drilling_stiffness_scale = 1.0e-3;
};