Files
FESADev/src/fesa/elements/hex8_element.cpp
T

628 lines
26 KiB
C++

#include "fesa/elements/hex8_element.h"
#include "fesa/math/vector3.h"
#include <cmath>
#include <memory>
#include <set>
#include <stdexcept>
#include <utility>
#include "../math/dense_blas_internal.h"
#include "hex8_geometry.h"
#include "hex8_linear_tangent_cache.h"
namespace fesa {
namespace {
using hex8_internal::Tensor;
using hex8_internal::Triple;
/// @brief Checks every tensor component before recovery or inverse use.
bool Finite(const Tensor& value) {
for (const auto& row : value)
for (double entry : row)
if (!std::isfinite(entry)) return false;
return true;
}
/// @brief Packs actual tensor shear, never engineering shear, in schema order.
std::array<double, 6> Pack(const Tensor& value) {
return {value[0][0], value[1][1], value[2][2],
value[0][1], value[0][2], value[1][2]};
}
/// @brief Computes a Euclidean contraction in deterministic component order.
double Dot(const Triple& a, const Triple& b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
/// @brief Forms a scaled 24-component outer product through dense MKL.
Matrix Outer(const std::array<Triple, 8>& values, double coefficient) {
Matrix left{24U, 1U};
Matrix right{1U, 24U};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) {
left(3U * a + i, 0) = coefficient * values[a][i];
right(0, 3U * a + i) = values[a][i];
}
return left.Multiply(right);
}
/// @brief Uses MKL entry products and the original ordered Gram/stress sums.
Matrix GradientProduct(const std::array<Triple, 8>& gradients,
const Tensor* stress = nullptr) {
const std::size_t terms = stress == nullptr ? 3U : 9U;
Vector left{64U * terms};
Vector right{64U * terms};
Vector final_factor{64U * terms};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b)
for (std::size_t j = 0; j < 3; ++j)
for (std::size_t l = 0; l < (stress == nullptr ? 1U : 3U); ++l) {
const auto entry = (8U * a + b) * terms +
(stress == nullptr ? j : 3U * j + l);
left[entry] = gradients[a][j];
right[entry] = stress == nullptr ? gradients[b][j] : (*stress)[l][j];
final_factor[entry] = gradients[b][l];
}
auto products = dense_blas_internal::MultiplyDiagonal(left, right);
if (stress != nullptr)
products = dense_blas_internal::MultiplyDiagonal(products, final_factor);
Matrix result{8U, 8U};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b) {
const auto first = (8U * a + b) * terms;
if (stress == nullptr) {
result(a, b) = (products[first] + products[first + 1U]) +
products[first + 2U];
} else {
for (std::size_t term = 0; term < terms; ++term)
result(a, b) += products[first + term];
}
}
return result;
}
/// @brief Retains product and cancellation errors in fixed-order contractions.
void AccumulateProduct(double left, double right, double& sum,
double& correction) {
const double product = left * right;
const double next = sum + product;
correction += std::fma(left, right, -product) +
(std::abs(sum) >= std::abs(product)
? (sum - next) + product
: (product - next) + sum);
sum = next;
}
/// @brief Recovers low bits lost when subtracting unequal nodal translations.
double DifferenceCorrection(double left, double right, double difference) {
const double right_virtual = left - difference;
const double left_virtual = difference + right_virtual;
return (left - left_virtual) + (right_virtual - right);
}
/// @brief Gives the finite-control failure without accepting a geometry guess.
const char* CertificateMessage(hex8_internal::Certificate certificate) {
switch (certificate) {
case hex8_internal::Certificate::kNonpositive:
return "Whole-cell Jacobian has a nonpositive witness.";
case hex8_internal::Certificate::kUncertain:
return "Whole-cell Jacobian positivity is unresolved at depth 10 or cell "
"budget 65536.";
case hex8_internal::Certificate::kNonfinite:
return "Whole-cell Jacobian interval arithmetic is nonfinite.";
case hex8_internal::Certificate::kPositive:
break;
}
return "Invalid Jacobian certificate.";
}
} // namespace
Hex8Element::Hex8Element(EntityIndex index, ElementDofLayout layout,
Coordinates coordinates, double kappa, double mu,
std::optional<double> density, SourceLocation location)
: element_index_{index},
layout_{std::move(layout)},
coordinates_{coordinates},
kappa_{kappa},
mu_{mu},
density_{density},
location_{std::move(location)},
paired_linear_tangent_{
std::make_shared<hex8_internal::LinearTangentCache>()} {}
Status Hex8Element::Failure(const char* message) const {
return Status::Failure(FailureCategory::kModel,
{{Severity::kError, "invalid-hex8", location_, "C3D8",
layout_.source_id.instance_name + ":" +
layout_.source_id.source_label_text,
message}});
}
Result<Hex8Element> Hex8Element::Create(EntityIndex index,
ElementDofLayout layout,
Coordinates coordinates,
const LinearElasticMaterial& material,
std::optional<double> density,
SourceLocation location) {
const double e = material.YoungsModulus();
const double nu = material.PoissonsRatio();
Hex8Element candidate{index, std::move(layout), coordinates, 0.0,
0.0, density, std::move(location)};
const std::vector<DofComponent> components{
DofComponent::kUx, DofComponent::kUy, DofComponent::kUz};
const std::set<EntityIndex> nodes(candidate.layout_.node_indices.begin(),
candidate.layout_.node_indices.end());
if (candidate.layout_.node_indices.size() != 8U || nodes.size() != 8U ||
candidate.layout_.components_per_node != components)
return Result<Hex8Element>::Failure(candidate.Failure(
"Hex8 requires eight distinct nodes with exactly UX, UY, UZ."));
if (!std::isfinite(e) || e <= 0.0 || !std::isfinite(nu) || nu <= -1.0 ||
nu >= 0.5)
return Result<Hex8Element>::Failure(
candidate.Failure("Hex8 requires finite E>0 and -1<nu<0.5."));
candidate.mu_ = e / (2.0 * (1.0 + nu));
candidate.kappa_ = e / (3.0 * (1.0 - 2.0 * nu));
if (!std::isfinite(candidate.mu_) || candidate.mu_ <= 0.0 ||
!std::isfinite(candidate.kappa_) || candidate.kappa_ <= 0.0)
return Result<Hex8Element>::Failure(
candidate.Failure("Hex8 shear and direct bulk moduli must remain "
"finite and representably positive."));
if (density && (!std::isfinite(*density) || *density <= 0.0))
return Result<Hex8Element>::Failure(candidate.Failure(
"Hex8 reference density must be finite and positive."));
const auto certificate = hex8_internal::Certify(coordinates);
if (certificate != hex8_internal::Certificate::kPositive)
return Result<Hex8Element>::Failure(
candidate.Failure(CertificateMessage(certificate)));
const double gauss = 1.0 / std::sqrt(3.0);
for (std::size_t g = 0; g < 8; ++g) {
auto& point = candidate.points_[g];
point.natural = {g % 2U == 0U ? -gauss : gauss,
(g / 2U) % 2U == 0U ? -gauss : gauss,
g < 4U ? -gauss : gauss};
std::array<Triple, 8> derivatives{};
hex8_internal::Shape(point.natural, point.shape, derivatives);
const auto jacobian = hex8_internal::Jacobian(coordinates, derivatives);
point.weight = hex8_internal::Determinant(jacobian);
if (!Finite(jacobian) || !std::isfinite(point.weight) ||
point.weight <= 0.0)
return Result<Hex8Element>::Failure(candidate.Failure(
"Reference quadrature Jacobian must be finite and positive."));
const Vector3 c0{jacobian[0][0], jacobian[1][0], jacobian[2][0]};
const Vector3 c1{jacobian[0][1], jacobian[1][1], jacobian[2][1]};
const Vector3 c2{jacobian[0][2], jacobian[1][2], jacobian[2][2]};
if (!c0.Cross(c1).IsFinite() || !c1.Cross(c2).IsFinite() ||
!c2.Cross(c0).IsFinite())
return Result<Hex8Element>::Failure(
candidate.Failure("Reference Jacobian inverse is nonfinite."));
Matrix coefficients{3U, 3U};
Matrix natural_gradients{3U, 8U};
for (std::size_t i = 0; i < 3U; ++i) {
for (std::size_t d = 0; d < 3U; ++d)
coefficients(i, d) = jacobian[i][d];
for (std::size_t a = 0; a < 8U; ++a)
natural_gradients(i, a) = derivatives[a][i];
}
const auto factor = dense_blas_internal::DenseFactor::Lu(coefficients);
if (!factor.HasValue())
return Result<Hex8Element>::Failure(
candidate.Failure("Reference Jacobian inverse is nonfinite."));
// The original geometry gate required every reciprocal action to be
// finite, including entries not exposed by the smaller shape-gradient RHS.
Matrix canonical{3U, 1U};
for (std::size_t axis = 0; axis < 3U; ++axis) {
if (axis != 0U) canonical(axis - 1U, 0U) = 0.0;
canonical(axis, 0U) = 1.0;
if (!factor.Value().Solve(canonical, true).HasValue())
return Result<Hex8Element>::Failure(
candidate.Failure("Reference Jacobian inverse is nonfinite."));
}
// J has Cartesian rows and natural columns: J^T grad_x N = grad_nat N.
const auto gradients = factor.Value().Solve(natural_gradients, true);
if (!gradients.HasValue())
return Result<Hex8Element>::Failure(candidate.Failure(
"Reference gradient or point coordinate is nonfinite."));
point.position = coordinates[0];
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) {
point.position[i] +=
point.shape[a] * (coordinates[a][i] - coordinates[0][i]);
point.gradients[a][i] = gradients.Value()(i, a);
if (!std::isfinite(point.gradients[a][i]) ||
!std::isfinite(point.position[i]))
return Result<Hex8Element>::Failure(candidate.Failure(
"Reference gradient or point coordinate is nonfinite."));
}
}
// Zero Hessian of the same reference-relative mean-volume energy.
std::array<Triple, 8> integrated_gradients{};
double volume = 0.0;
bool backend_inputs_failed = false;
for (const auto& point : candidate.points_) {
auto gradients = point.gradients;
gradients[0] = {};
for (std::size_t a = 1; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) gradients[0][i] -= gradients[a][i];
volume += point.weight;
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i)
integrated_gradients[a][i] += point.weight * gradients[a][i];
Matrix deviatoric{0U, 0U};
Matrix shear{0U, 0U};
Matrix gram{0U, 0U};
bool point_products_failed = false;
try {
deviatoric = Outer(gradients, -(2.0 / 3.0) * candidate.mu_);
shear = Outer(gradients, candidate.mu_);
gram = GradientProduct(gradients);
} catch (const std::invalid_argument&) {
backend_inputs_failed = true;
point_products_failed = true;
}
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b) {
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t k = 0; k < 3; ++k) {
if (point_products_failed) {
// Diagnostic replay only: this candidate can never succeed.
const auto& ga = gradients[a];
const auto& gb = gradients[b];
candidate.stiffness_(3U * a + i, 3U * b + k) +=
point.weight *
(-(2.0 / 3.0) * candidate.mu_ * ga[i] * gb[k] +
candidate.mu_ * ga[k] * gb[i] +
(i == k ? candidate.mu_ * Dot(ga, gb) : 0.0));
} else {
candidate.stiffness_(3U * a + i, 3U * b + k) +=
point.weight * (deviatoric(3U * a + i, 3U * b + k) +
shear(3U * a + k, 3U * b + i) +
(i == k ? candidate.mu_ * gram(a, b) : 0.0));
}
}
}
}
if (!std::isfinite(volume) || volume <= 0.0)
return Result<Hex8Element>::Failure(
candidate.Failure("Hex8 reference volume is nonfinite."));
Matrix bulk{0U, 0U};
try {
bulk = Outer(integrated_gradients, candidate.kappa_ / volume);
} catch (const std::invalid_argument&) {
backend_inputs_failed = true;
bulk = Matrix{24U, 24U};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b)
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t k = 0; k < 3; ++k)
bulk(3U * a + i, 3U * b + k) =
(candidate.kappa_ / volume) * integrated_gradients[a][i] *
integrated_gradients[b][k];
}
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j)
candidate.stiffness_(i, j) += bulk(i, j);
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j)
if (!std::isfinite(candidate.stiffness_(i, j)))
return Result<Hex8Element>::Failure(
candidate.Failure("Hex8 reference stiffness is nonfinite."));
for (std::size_t i = 0; i < 24; ++i)
if (candidate.stiffness_(i, i) <= 0.0)
return Result<Hex8Element>::Failure(
candidate.Failure("Hex8 reference stiffness has an unrepresentable "
"positive diagonal."));
if (backend_inputs_failed)
return Result<Hex8Element>::Failure(candidate.Failure(
"Hex8 dense MKL operands failed finite validation."));
return Result<Hex8Element>::Success(std::move(candidate));
}
const ElementDofLayout& Hex8Element::DofLayout() const noexcept {
return layout_;
}
Result<ElementStiffnessContribution> Hex8Element::ComputeStiffness() const {
return Result<ElementStiffnessContribution>::Success({layout_, stiffness_});
}
std::array<double, 8> Hex8Element::ReferenceWeights() const noexcept {
std::array<double, 8> weights{};
for (std::size_t g = 0; g < weights.size(); ++g)
weights[g] = points_[g].weight;
return weights;
}
Result<ElementStiffnessContribution> Hex8Element::ComputeMass() const {
if (!density_)
return Result<ElementStiffnessContribution>::Failure(
Failure("Hex8 mass requires reference density."));
Matrix consistent{24U, 24U};
for (const auto& point : points_) {
Matrix left{8U, 1U};
Matrix right{1U, 8U};
for (std::size_t a = 0; a < 8; ++a) {
left(a, 0) = *density_ * point.shape[a];
right(0, a) = point.shape[a];
}
Matrix products{0U, 0U};
try {
products = left.Multiply(right);
} catch (const std::invalid_argument&) {
return Result<ElementStiffnessContribution>::Failure(
Failure("Hex8 reference mass is nonfinite."));
}
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b) {
const double value = products(a, b) * point.weight;
if (!std::isfinite(value))
return Result<ElementStiffnessContribution>::Failure(
Failure("Hex8 reference mass is nonfinite."));
for (std::size_t i = 0; i < 3; ++i)
consistent(3U * a + i, 3U * b + i) += value;
}
}
Matrix mass{24U, 24U};
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j) {
if (!std::isfinite(consistent(i, j)))
return Result<ElementStiffnessContribution>::Failure(
Failure("Hex8 accumulated reference mass is nonfinite."));
mass(i, i) += consistent(i, j);
}
for (std::size_t i = 0; i < 24; ++i)
if (!std::isfinite(mass(i, i)) || mass(i, i) <= 0.0)
return Result<ElementStiffnessContribution>::Failure(Failure(
"Hex8 reference mass has an unrepresentable positive diagonal."));
return Result<ElementStiffnessContribution>::Success(
{layout_, std::move(mass)});
}
Result<ElementResultBundle> Hex8Element::Recover(const Vector& u) const {
auto response = EvaluateLinear(u);
if (!response.HasValue())
return Result<ElementResultBundle>::Failure(response.GetStatus());
SolidElementResultRows rows;
rows.rows.assign(response.Value().points.begin(),
response.Value().points.end());
rows.physical_strain_energy = response.Value().strain_energy;
return Result<ElementResultBundle>::Success(
{layout_.source_id, std::move(rows)});
}
Result<Hex8Response> Hex8Element::EvaluateLinear(const Vector& u) const {
return Evaluate(u, false);
}
Result<Hex8Response> Hex8Element::EvaluateNonlinear(const Vector& u) const {
return Evaluate(u, true);
}
Result<Hex8HighLowResponse> Hex8Element::EvaluateLinearHighLow(
const Vector& high, const Vector& low) const {
return EvaluateHighLow(high, low, false);
}
Result<Hex8HighLowResponse> Hex8Element::EvaluateNonlinearHighLow(
const Vector& high, const Vector& low) const {
return EvaluateHighLow(high, low, true);
}
Result<Hex8Response> Hex8Element::Evaluate(const Vector& u,
bool nonlinear) const {
if (u.Size() != 24U)
return Result<Hex8Response>::Failure(
Failure("Hex8 trial requires 24 translations."));
const auto certificate = hex8_internal::Certify(coordinates_, &u);
if (certificate != hex8_internal::Certificate::kPositive)
return Result<Hex8Response>::Failure(
Failure(CertificateMessage(certificate)));
Hex8Response response;
Vector relative{24U};
Vector relative_correction{24U};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) {
const auto row = 3U * a + i;
relative[row] = u[row] - u[i];
relative_correction[row] =
DifferenceCorrection(u[row], u[i], relative[row]);
}
if (!nonlinear) {
response.material_tangent = stiffness_;
response.tangent = stiffness_;
}
std::array<Tensor, 8> deformations{}, strains{};
std::array<std::array<Triple, 8>, 8> gradients{};
double volume = 0.0, trace_integral = 0.0;
Vector force_correction{24U};
for (std::size_t g = 0; g < 8; ++g) {
const auto& point = points_[g];
gradients[g] = point.gradients;
Triple sum{};
for (std::size_t a = 1; a < 8; ++a)
for (std::size_t j = 0; j < 3; ++j) sum[j] += gradients[g][a][j];
for (std::size_t j = 0; j < 3; ++j) gradients[g][0][j] = -sum[j];
Tensor gradient{};
Tensor gradient_correction{};
for (std::size_t a = 1; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
AccumulateProduct(relative[3U * a + i], point.gradients[a][j],
gradient[i][j], gradient_correction[i][j]);
AccumulateProduct(relative_correction[3U * a + i],
point.gradients[a][j], gradient[i][j],
gradient_correction[i][j]);
}
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j)
gradient[i][j] += gradient_correction[i][j];
Tensor f = nonlinear ? gradient : Tensor{};
for (std::size_t i = 0; i < 3; ++i) f[i][i] += 1.0;
Tensor strain{};
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
strain[i][j] = 0.5 * (gradient[i][j] + gradient[j][i]);
// E = sym(H) + H^T*H/2 avoids subtracting I at tiny strain.
if (nonlinear)
for (std::size_t k = 0; k < 3; ++k)
strain[i][j] += 0.5 * gradient[k][i] * gradient[k][j];
}
deformations[g] = f;
strains[g] = strain;
volume += point.weight;
trace_integral +=
point.weight * (strain[0][0] + strain[1][1] + strain[2][2]);
if (!Finite(gradient) || !Finite(f) || !Finite(strain))
return Result<Hex8Response>::Failure(
Failure("Hex8 trial tensor is nonfinite."));
}
const double mean_trace = trace_integral / volume;
if (!std::isfinite(volume) || volume <= 0 || !std::isfinite(mean_trace))
return Result<Hex8Response>::Failure(
Failure("Hex8 mean volume is nonfinite."));
response.strain_energy = (0.5 * kappa_ * mean_trace) * (volume * mean_trace);
std::array<Triple, 8> integrated_fg{};
bool backend_inputs_failed = false;
for (std::size_t g = 0; g < 8; ++g) {
const auto& point = points_[g];
const auto& f = deformations[g];
const auto& strain = strains[g];
const double trace = strain[0][0] + strain[1][1] + strain[2][2];
Tensor s{}, p{}, fft{};
double density_energy = 0.0;
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
const double dev = strain[i][j] - (i == j ? trace / 3.0 : 0.0);
s[i][j] = 2.0 * mu_ * dev + (i == j ? kappa_ * mean_trace : 0.0);
density_energy += mu_ * dev * dev;
}
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
for (std::size_t k = 0; k < 3; ++k) {
p[i][j] += f[i][k] * s[k][j];
fft[i][j] += f[i][k] * f[j][k];
}
}
response.strain_energy += density_energy * point.weight;
Tensor stress = s;
if (nonlinear) {
const double det_f = hex8_internal::Determinant(f);
if (!std::isfinite(det_f) || det_f <= 0.0)
return Result<Hex8Response>::Failure(Failure(
"Hex8 trial deformation determinant must be finite and positive."));
stress = {};
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j)
for (std::size_t k = 0; k < 3; ++k)
stress[i][j] += p[i][k] * f[j][k] / det_f;
}
for (std::size_t a = 0; a < 8; ++a) {
for (std::size_t i = 0; i < 3; ++i)
integrated_fg[a][i] += point.weight * Dot(f[i], gradients[g][a]);
}
Matrix deviatoric{0U, 0U};
Matrix shear{0U, 0U};
Matrix gram{0U, 0U};
Matrix geometric{0U, 0U};
bool point_products_failed = false;
if (!Finite(f) || !Finite(strain) || !Finite(s) || !Finite(p) ||
!Finite(fft) || !Finite(stress))
return Result<Hex8Response>::Failure(
Failure("Hex8 trial tensor is nonfinite."));
if (nonlinear) {
std::array<Triple, 8> fg{};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i)
fg[a][i] = Dot(f[i], gradients[g][a]);
try {
deviatoric = Outer(fg, -(2.0 / 3.0) * mu_);
shear = Outer(fg, mu_);
gram = GradientProduct(gradients[g]);
geometric = GradientProduct(gradients[g], &s);
} catch (const std::invalid_argument&) {
backend_inputs_failed = true;
point_products_failed = true;
}
}
for (std::size_t a = 0; a < 8; ++a) {
const auto& ga = gradients[g][a];
for (std::size_t i = 0; i < 3; ++i) {
double contraction = 0.0;
double contraction_correction = 0.0;
for (std::size_t j = 0; j < 3; ++j)
AccumulateProduct(p[i][j], ga[j], contraction,
contraction_correction);
AccumulateProduct(contraction, point.weight,
response.internal_force[3U * a + i],
force_correction[3U * a + i]);
AccumulateProduct(contraction_correction, point.weight,
response.internal_force[3U * a + i],
force_correction[3U * a + i]);
}
if (nonlinear)
for (std::size_t b = 0; b < 8; ++b) {
const auto& gb = gradients[g][b];
double diagnostic_geometric = 0.0;
if (point_products_failed)
for (std::size_t j = 0; j < 3; ++j)
for (std::size_t l = 0; l < 3; ++l)
diagnostic_geometric += ga[j] * s[l][j] * gb[l];
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t k = 0; k < 3; ++k) {
const auto row = 3U * a + i;
const auto column = 3U * b + k;
if (point_products_failed) {
// Reproduce original diagnostic priority, never acceptance.
response.material_tangent(row, column) +=
point.weight *
(-(2.0 / 3.0) * mu_ * Dot(f[i], ga) * Dot(f[k], gb) +
mu_ * Dot(f[i], gb) * Dot(f[k], ga) +
mu_ * fft[i][k] * Dot(ga, gb));
} else {
response.material_tangent(row, column) +=
point.weight *
(deviatoric(row, column) +
shear(3U * b + i, 3U * a + k) +
mu_ * fft[i][k] * gram(a, b));
}
if (i == k)
response.geometric_tangent(row, column) +=
(point_products_failed ? diagnostic_geometric
: geometric(a, b)) * point.weight;
}
}
}
response.points[g] = {element_index_, static_cast<int>(g + 1U),
point.natural, point.position,
Pack(stress), Pack(strain)};
}
for (std::size_t i = 0; i < 24; ++i)
response.internal_force[i] += force_correction[i];
if (nonlinear) {
Matrix bulk{0U, 0U};
try {
bulk = Outer(integrated_fg, kappa_ / volume);
} catch (const std::invalid_argument&) {
backend_inputs_failed = true;
bulk = Matrix{24U, 24U};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b)
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t k = 0; k < 3; ++k)
bulk(3U * a + i, 3U * b + k) =
(kappa_ / volume) * integrated_fg[a][i] * integrated_fg[b][k];
}
for (std::size_t i = 0; i < 24; ++i)
for (std::size_t j = 0; j < 24; ++j)
response.material_tangent(i, j) += bulk(i, j);
}
if (!std::isfinite(response.strain_energy))
return Result<Hex8Response>::Failure(
Failure("Hex8 trial energy is nonfinite."));
for (std::size_t i = 0; i < 24; ++i) {
if (!std::isfinite(response.internal_force[i]))
return Result<Hex8Response>::Failure(
Failure("Hex8 trial force is nonfinite."));
for (std::size_t j = 0; j < 24; ++j) {
if (nonlinear)
response.tangent(i, j) =
response.material_tangent(i, j) + response.geometric_tangent(i, j);
if (!std::isfinite(response.tangent(i, j)) ||
!std::isfinite(response.material_tangent(i, j)) ||
!std::isfinite(response.geometric_tangent(i, j)))
return Result<Hex8Response>::Failure(
Failure("Hex8 trial tangent is nonfinite."));
}
}
if (backend_inputs_failed)
return Result<Hex8Response>::Failure(
Failure("Hex8 dense MKL operands failed finite validation."));
return Result<Hex8Response>::Success(std::move(response));
}
} // namespace fesa