perf(hex8): use MKL Hessian products and exact zero pair addition
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "hex8_geometry.h"
|
#include "hex8_geometry.h"
|
||||||
@@ -26,6 +27,36 @@ std::array<double, 6> Pack(const Tensor& value) {
|
|||||||
double Dot(const Triple& a, const Triple& b) {
|
double Dot(const Triple& a, const Triple& b) {
|
||||||
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
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 Forms node-gradient Gram and stress congruence through dense MKL.
|
||||||
|
Matrix GradientProduct(const std::array<Triple, 8>& gradients,
|
||||||
|
const Tensor* stress = nullptr) {
|
||||||
|
Matrix left{8U, 3U};
|
||||||
|
Matrix transpose{3U, 8U};
|
||||||
|
for (std::size_t a = 0; a < 8; ++a)
|
||||||
|
for (std::size_t i = 0; i < 3; ++i) {
|
||||||
|
left(a, i) = gradients[a][i];
|
||||||
|
transpose(i, a) = gradients[a][i];
|
||||||
|
}
|
||||||
|
if (stress != nullptr) {
|
||||||
|
Matrix stress_transpose{3U, 3U};
|
||||||
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
|
for (std::size_t j = 0; j < 3; ++j)
|
||||||
|
stress_transpose(i, j) = (*stress)[j][i];
|
||||||
|
left = left.Multiply(stress_transpose);
|
||||||
|
}
|
||||||
|
return left.Multiply(transpose);
|
||||||
|
}
|
||||||
/// @brief Retains product and cancellation errors in fixed-order contractions.
|
/// @brief Retains product and cancellation errors in fixed-order contractions.
|
||||||
void AccumulateProduct(double left, double right, double& sum,
|
void AccumulateProduct(double left, double right, double& sum,
|
||||||
double& correction) {
|
double& correction) {
|
||||||
@@ -158,30 +189,41 @@ Result<Hex8Element> Hex8Element::Create(EntityIndex index,
|
|||||||
for (std::size_t a = 0; a < 8; ++a)
|
for (std::size_t a = 0; a < 8; ++a)
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
integrated_gradients[a][i] += point.weight * gradients[a][i];
|
integrated_gradients[a][i] += point.weight * gradients[a][i];
|
||||||
|
Matrix deviatoric{0U, 0U};
|
||||||
|
Matrix shear{0U, 0U};
|
||||||
|
Matrix gram{0U, 0U};
|
||||||
|
try {
|
||||||
|
deviatoric = Outer(gradients, -(2.0 / 3.0) * candidate.mu_);
|
||||||
|
shear = Outer(gradients, candidate.mu_);
|
||||||
|
gram = GradientProduct(gradients);
|
||||||
|
} catch (const std::invalid_argument&) {
|
||||||
|
return Result<Hex8Element>::Failure(
|
||||||
|
candidate.Failure("Hex8 reference stiffness is nonfinite."));
|
||||||
|
}
|
||||||
for (std::size_t a = 0; a < 8; ++a)
|
for (std::size_t a = 0; a < 8; ++a)
|
||||||
for (std::size_t b = 0; b < 8; ++b) {
|
for (std::size_t b = 0; b < 8; ++b) {
|
||||||
const auto& ga = gradients[a];
|
|
||||||
const auto& gb = gradients[b];
|
|
||||||
const double dot = Dot(ga, gb);
|
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
for (std::size_t k = 0; k < 3; ++k) {
|
for (std::size_t k = 0; k < 3; ++k) {
|
||||||
candidate.stiffness_(3U * a + i, 3U * b + k) +=
|
candidate.stiffness_(3U * a + i, 3U * b + k) +=
|
||||||
point.weight * (-(2.0 / 3.0) * candidate.mu_ * ga[i] * gb[k] +
|
point.weight * (deviatoric(3U * a + i, 3U * b + k) +
|
||||||
candidate.mu_ * ga[k] * gb[i] +
|
shear(3U * a + k, 3U * b + i) +
|
||||||
(i == k ? candidate.mu_ * dot : 0.0));
|
(i == k ? candidate.mu_ * gram(a, b) : 0.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!std::isfinite(volume) || volume <= 0.0)
|
if (!std::isfinite(volume) || volume <= 0.0)
|
||||||
return Result<Hex8Element>::Failure(
|
return Result<Hex8Element>::Failure(
|
||||||
candidate.Failure("Hex8 reference volume is nonfinite."));
|
candidate.Failure("Hex8 reference volume is nonfinite."));
|
||||||
for (std::size_t a = 0; a < 8; ++a)
|
Matrix bulk{0U, 0U};
|
||||||
for (std::size_t b = 0; b < 8; ++b)
|
try {
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
bulk = Outer(integrated_gradients, candidate.kappa_ / volume);
|
||||||
for (std::size_t k = 0; k < 3; ++k)
|
} catch (const std::invalid_argument&) {
|
||||||
candidate.stiffness_(3U * a + i, 3U * b + k) +=
|
return Result<Hex8Element>::Failure(
|
||||||
(candidate.kappa_ / volume) * integrated_gradients[a][i] *
|
candidate.Failure("Hex8 reference stiffness is nonfinite."));
|
||||||
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 i = 0; i < 24; ++i)
|
||||||
for (std::size_t j = 0; j < 24; ++j)
|
for (std::size_t j = 0; j < 24; ++j)
|
||||||
if (!std::isfinite(candidate.stiffness_(i, j)))
|
if (!std::isfinite(candidate.stiffness_(i, j)))
|
||||||
@@ -382,6 +424,25 @@ Result<Hex8Response> Hex8Element::Evaluate(const Vector& u,
|
|||||||
for (std::size_t i = 0; i < 3; ++i)
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
integrated_fg[a][i] += point.weight * Dot(f[i], gradients[g][a]);
|
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};
|
||||||
|
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&) {
|
||||||
|
return Result<Hex8Response>::Failure(
|
||||||
|
Failure("Hex8 trial tangent is nonfinite."));
|
||||||
|
}
|
||||||
|
}
|
||||||
for (std::size_t a = 0; a < 8; ++a) {
|
for (std::size_t a = 0; a < 8; ++a) {
|
||||||
const auto& ga = gradients[g][a];
|
const auto& ga = gradients[g][a];
|
||||||
for (std::size_t i = 0; i < 3; ++i) {
|
for (std::size_t i = 0; i < 3; ++i) {
|
||||||
@@ -399,23 +460,18 @@ Result<Hex8Response> Hex8Element::Evaluate(const Vector& u,
|
|||||||
}
|
}
|
||||||
if (nonlinear)
|
if (nonlinear)
|
||||||
for (std::size_t b = 0; b < 8; ++b) {
|
for (std::size_t b = 0; b < 8; ++b) {
|
||||||
const auto& gb = gradients[g][b];
|
|
||||||
double geometric = 0.0;
|
|
||||||
for (std::size_t j = 0; j < 3; ++j)
|
|
||||||
for (std::size_t l = 0; l < 3; ++l)
|
|
||||||
geometric += ga[j] * s[l][j] * gb[l];
|
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
for (std::size_t i = 0; i < 3; ++i)
|
||||||
for (std::size_t k = 0; k < 3; ++k) {
|
for (std::size_t k = 0; k < 3; ++k) {
|
||||||
const auto row = 3U * a + i;
|
const auto row = 3U * a + i;
|
||||||
const auto column = 3U * b + k;
|
const auto column = 3U * b + k;
|
||||||
response.material_tangent(row, column) +=
|
response.material_tangent(row, column) +=
|
||||||
point.weight *
|
point.weight *
|
||||||
(-(2.0 / 3.0) * mu_ * Dot(f[i], ga) * Dot(f[k], gb) +
|
(deviatoric(row, column) +
|
||||||
mu_ * Dot(f[i], gb) * Dot(f[k], ga) +
|
shear(3U * b + i, 3U * a + k) +
|
||||||
mu_ * fft[i][k] * Dot(ga, gb));
|
mu_ * fft[i][k] * gram(a, b));
|
||||||
if (i == k)
|
if (i == k)
|
||||||
response.geometric_tangent(row, column) +=
|
response.geometric_tangent(row, column) +=
|
||||||
geometric * point.weight;
|
geometric(a, b) * point.weight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,13 +485,18 @@ Result<Hex8Response> Hex8Element::Evaluate(const Vector& u,
|
|||||||
}
|
}
|
||||||
for (std::size_t i = 0; i < 24; ++i)
|
for (std::size_t i = 0; i < 24; ++i)
|
||||||
response.internal_force[i] += force_correction[i];
|
response.internal_force[i] += force_correction[i];
|
||||||
if (nonlinear)
|
if (nonlinear) {
|
||||||
for (std::size_t a = 0; a < 8; ++a)
|
Matrix bulk{0U, 0U};
|
||||||
for (std::size_t b = 0; b < 8; ++b)
|
try {
|
||||||
for (std::size_t i = 0; i < 3; ++i)
|
bulk = Outer(integrated_fg, kappa_ / volume);
|
||||||
for (std::size_t k = 0; k < 3; ++k)
|
} catch (const std::invalid_argument&) {
|
||||||
response.material_tangent(3 * a + i, 3 * b + k) +=
|
return Result<Hex8Response>::Failure(
|
||||||
(kappa_ / volume) * integrated_fg[a][i] * integrated_fg[b][k];
|
Failure("Hex8 trial tangent is nonfinite."));
|
||||||
|
}
|
||||||
|
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))
|
if (!std::isfinite(response.strain_energy))
|
||||||
return Result<Hex8Response>::Failure(
|
return Result<Hex8Response>::Failure(
|
||||||
Failure("Hex8 trial energy is nonfinite."));
|
Failure("Hex8 trial energy is nonfinite."));
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ using math_internal::HighLow;
|
|||||||
using Triple = std::array<HighLow, 3>;
|
using Triple = std::array<HighLow, 3>;
|
||||||
using Tensor = std::array<Triple, 3>;
|
using Tensor = std::array<Triple, 3>;
|
||||||
|
|
||||||
HighLow operator+(HighLow a, HighLow b) { return math_internal::Add(a, b); }
|
HighLow operator+(HighLow a, HighLow b) {
|
||||||
|
return hex8_internal::AddPair(a, b);
|
||||||
|
}
|
||||||
HighLow operator-(HighLow a, HighLow b) {
|
HighLow operator-(HighLow a, HighLow b) {
|
||||||
return math_internal::Subtract(a, b);
|
return math_internal::Subtract(a, b);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,18 @@
|
|||||||
#include "../math/high_low_arithmetic.h"
|
#include "../math/high_low_arithmetic.h"
|
||||||
|
|
||||||
namespace fesa::hex8_internal {
|
namespace fesa::hex8_internal {
|
||||||
|
/// @brief Preserves original zero-add normalization without redundant TwoSums.
|
||||||
|
/// @note Valid nonzero terms retain the original addition association.
|
||||||
|
inline math_internal::HighLow AddPair(math_internal::HighLow left,
|
||||||
|
math_internal::HighLow right) {
|
||||||
|
using namespace math_internal;
|
||||||
|
if (!IsNormalized(left) || !IsNormalized(right)) return InvalidHighLow();
|
||||||
|
if (left.high == 0 && left.low == 0)
|
||||||
|
return {right.high, right.low == 0 ? 0.0 : right.low};
|
||||||
|
if (right.high == 0 && right.low == 0)
|
||||||
|
return {left.high, left.low == 0 ? 0.0 : left.low};
|
||||||
|
return Add(left, right);
|
||||||
|
}
|
||||||
/// @brief Omits exact-zero cross products without changing nonzero association.
|
/// @brief Omits exact-zero cross products without changing nonzero association.
|
||||||
/// @note A retained zero addition performs the original canonicalization and
|
/// @note A retained zero addition performs the original canonicalization and
|
||||||
/// invalid-result check; its repeated application is idempotent.
|
/// invalid-result check; its repeated application is idempotent.
|
||||||
@@ -14,14 +26,14 @@ inline math_internal::HighLow MultiplyPair(math_internal::HighLow left,
|
|||||||
if (left.low != 0 && right.low != 0) return Multiply(left, right);
|
if (left.low != 0 && right.low != 0) return Multiply(left, right);
|
||||||
auto product = TwoProduct(left.high, right.high);
|
auto product = TwoProduct(left.high, right.high);
|
||||||
if (right.low != 0) {
|
if (right.low != 0) {
|
||||||
product = Add(product, TwoProduct(left.high, right.low));
|
product = AddPair(product, TwoProduct(left.high, right.low));
|
||||||
} else if (left.low != 0) {
|
} else if (left.low != 0) {
|
||||||
// The original zero addition precedes the nonzero low-high term.
|
// The original zero addition precedes the nonzero low-high term.
|
||||||
product = Add(product, HighLow{});
|
product = AddPair(product, HighLow{});
|
||||||
}
|
}
|
||||||
if (left.low != 0)
|
if (left.low != 0)
|
||||||
product = Add(product, TwoProduct(left.low, right.high));
|
product = AddPair(product, TwoProduct(left.low, right.high));
|
||||||
return Add(product, HighLow{});
|
return AddPair(product, HighLow{});
|
||||||
}
|
}
|
||||||
} // namespace fesa::hex8_internal
|
} // namespace fesa::hex8_internal
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ void ExpectOriginalProduct(HighLow left, HighLow right) {
|
|||||||
{left.high, left.low}, {right.high, right.low});
|
{left.high, left.low}, {right.high, right.low});
|
||||||
EXPECT_EQ(PairBits(actual.high), PairBits(expected.high));
|
EXPECT_EQ(PairBits(actual.high), PairBits(expected.high));
|
||||||
EXPECT_EQ(PairBits(actual.low), PairBits(expected.low));
|
EXPECT_EQ(PairBits(actual.low), PairBits(expected.low));
|
||||||
|
const auto actual_sum = fesa::hex8_internal::AddPair(left, right);
|
||||||
|
const auto expected_sum = fesa::high_low_original::Add(
|
||||||
|
{left.high, left.low}, {right.high, right.low});
|
||||||
|
EXPECT_EQ(PairBits(actual_sum.high), PairBits(expected_sum.high));
|
||||||
|
EXPECT_EQ(PairBits(actual_sum.low), PairBits(expected_sum.low));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Hex8HighLow, ScalarCrossProductShortcutMatchesOriginalBits) {
|
TEST(Hex8HighLow, ScalarCrossProductShortcutMatchesOriginalBits) {
|
||||||
@@ -65,6 +70,8 @@ TEST(Hex8HighLow, ScalarCrossProductShortcutMatchesOriginalBits) {
|
|||||||
std::memcpy(&left, &left_bits, sizeof(left));
|
std::memcpy(&left, &left_bits, sizeof(left));
|
||||||
std::memcpy(&right, &right_bits, sizeof(right));
|
std::memcpy(&right, &right_bits, sizeof(right));
|
||||||
const auto pair = fesa::math_internal::TwoSum(left, right);
|
const auto pair = fesa::math_internal::TwoSum(left, right);
|
||||||
|
ExpectOriginalProduct(pair, HighLow{});
|
||||||
|
ExpectOriginalProduct(HighLow{}, pair);
|
||||||
ExpectOriginalProduct(pair, HighLow{right});
|
ExpectOriginalProduct(pair, HighLow{right});
|
||||||
ExpectOriginalProduct(HighLow{right}, pair);
|
ExpectOriginalProduct(HighLow{right}, pair);
|
||||||
ExpectOriginalProduct(pair, pair);
|
ExpectOriginalProduct(pair, pair);
|
||||||
|
|||||||
Reference in New Issue
Block a user