feat(hex8): preserve full high-low state in dynamic element responses

This commit is contained in:
KOKO\Mimi
2026-10-09 23:46:42 +09:00
parent d8d06ae8ae
commit 5435acdde3
21 changed files with 1152 additions and 16 deletions
+8
View File
@@ -241,6 +241,14 @@ Result<Hex8Response> Hex8Element::EvaluateLinear(const Vector& u) const {
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)
+6 -1
View File
@@ -133,7 +133,7 @@ std::pair<Coefficients, Coefficients> Split(const Coefficients& original,
} // namespace
Certificate Certify(const Hex8Element::Coordinates& coordinates,
const Vector* displacement) {
const Vector* displacement, const Vector* displacement_low) {
std::array<std::array<Interval, 3>, 8> x{};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) {
@@ -145,6 +145,11 @@ Certificate Certify(const Hex8Element::Coordinates& coordinates,
if (!std::isfinite(u)) return Certificate::kNonfinite;
x[a][i] = Add(x[a][i], {u, u});
}
if (displacement_low) {
const auto low = (*displacement_low)[3U * a + i];
if (!std::isfinite(low)) return Certificate::kNonfinite;
x[a][i] = Add(x[a][i], {low, low});
}
if (!Finite(x[a][i])) return Certificate::kNonfinite;
}
Coefficients coefficients{};
+2 -1
View File
@@ -16,7 +16,8 @@ enum class Certificate { kPositive, kNonpositive, kUncertain, kNonfinite };
/// @brief Certifies the closed natural cube with interval Bernstein bounds.
/// @note Current addition rounding is enclosed when displacement is supplied.
Certificate Certify(const Hex8Element::Coordinates& coordinates,
const Vector* displacement = nullptr);
const Vector* displacement = nullptr,
const Vector* displacement_low = nullptr);
/// @brief Evaluates trilinear shapes and natural derivatives in source order.
void Shape(const Triple& natural, std::array<double, 8>& values,
std::array<Triple, 8>& derivatives);
+206
View File
@@ -0,0 +1,206 @@
#include "fesa/elements/hex8_element.h"
#include <array>
#include <cmath>
#include <utility>
#include "../math/high_low_arithmetic.h"
#include "hex8_geometry.h"
namespace fesa {
namespace {
using math_internal::HighLow;
using Triple = std::array<HighLow, 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 math_internal::Subtract(a, b);
}
HighLow operator*(HighLow a, HighLow b) {
return math_internal::Multiply(a, b);
}
HighLow operator/(HighLow a, HighLow b) {
return math_internal::Divide(a, b);
}
HighLow& operator+=(HighLow& a, HighLow b) { return a = a + b; }
/// @brief Contracts complete pairs in fixed component order.
HighLow Dot(const Triple& a, const Triple& b) {
HighLow result;
for (std::size_t i = 0; i < 3; ++i) result += a[i] * b[i];
return result;
}
HighLow Determinant(const Tensor& a) {
return a[0][0] * (a[1][1] * a[2][2] - a[1][2] * a[2][1]) -
a[0][1] * (a[1][0] * a[2][2] - a[1][2] * a[2][0]) +
a[0][2] * (a[1][0] * a[2][1] - a[1][1] * a[2][0]);
}
bool Finite(const Tensor& a) {
for (const auto& row : a)
for (auto value : row)
if (!math_internal::IsNormalized(value)) return false;
return true;
}
std::array<double, 6> Pack(const Tensor& a) {
using math_internal::Rounded;
return {Rounded(a[0][0]), Rounded(a[1][1]), Rounded(a[2][2]),
Rounded(a[0][1]), Rounded(a[0][2]), Rounded(a[1][2])};
}
} // namespace
Result<Hex8HighLowResponse> Hex8Element::EvaluateHighLow(
const Vector& high, const Vector& low, bool nonlinear) const {
using math_internal::IsNormalized;
if (!math_internal::ArithmeticEnvironmentSupported())
return Result<Hex8HighLowResponse>::Failure(Failure(
"Hex8 high+low requires nearest rounding and gradual underflow."));
if (high.Size() != 24U || low.Size() != 24U)
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low trial requires two 24-component vectors."));
std::array<HighLow, 24> relative{};
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t i = 0; i < 3; ++i) {
const auto row = 3U * a + i;
const HighLow value{high[row], low[row]};
const HighLow origin{high[i], low[i]};
if (!IsNormalized(value) || !IsNormalized(origin))
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low translation is not finite normalized."));
relative[row] = value - origin;
if (!IsNormalized(relative[row]))
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low relative translation is unsupported."));
}
const auto certificate = hex8_internal::Certify(coordinates_, &high, &low);
if (certificate != hex8_internal::Certificate::kPositive) {
const char* message = "Hex8 full high+low current geometry is nonpositive.";
if (certificate == hex8_internal::Certificate::kUncertain)
message = "Hex8 full high+low Jacobian positivity is unresolved at "
"depth 10 or cell budget 65536.";
if (certificate == hex8_internal::Certificate::kNonfinite)
message = "Hex8 full high+low Jacobian interval arithmetic is nonfinite.";
return Result<Hex8HighLowResponse>::Failure(Failure(message));
}
Hex8HighLowResponse response;
std::array<HighLow, 24> force{};
std::array<std::array<HighLow, 24>, 24> material{}, geometric{};
HighLow energy;
for (std::size_t g = 0; g < 8; ++g) {
const auto& point = points_[g];
std::array<Triple, 8> gradients{};
Triple gradient_sum{};
Tensor h{};
for (std::size_t a = 1; a < 8; ++a)
for (std::size_t j = 0; j < 3; ++j) {
gradients[a][j] = HighLow{point.gradients[a][j]};
gradient_sum[j] += gradients[a][j];
for (std::size_t i = 0; i < 3; ++i)
h[i][j] += relative[3U * a + i] * gradients[a][j];
}
// This is the derivative of the relative displacement expression above.
// Existing rounded G0 and cached K0 are not substituted into this operator.
for (std::size_t j = 0; j < 3; ++j)
gradients[0][j] = math_internal::Negate(gradient_sum[j]);
Tensor f{};
Tensor strain{}, s{}, p{}, fft{};
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
f[i][j] = nonlinear ? h[i][j] : HighLow{};
if (i == j) f[i][j] += HighLow{1};
strain[i][j] = HighLow{0.5} * (h[i][j] + h[j][i]);
if (nonlinear)
for (std::size_t k = 0; k < 3; ++k)
strain[i][j] += HighLow{0.5} * h[k][i] * h[k][j];
}
const HighLow trace = strain[0][0] + strain[1][1] + strain[2][2];
HighLow density_energy = HighLow{0.5} * HighLow{lambda_} * trace * trace;
for (std::size_t i = 0; i < 3; ++i)
for (std::size_t j = 0; j < 3; ++j) {
s[i][j] = HighLow{2} * HighLow{mu_} * strain[i][j];
if (i == j) s[i][j] += HighLow{lambda_} * trace;
density_energy += HighLow{mu_} * strain[i][j] * strain[i][j];
}
energy += density_energy * HighLow{point.weight};
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];
}
}
const HighLow det = Determinant(f);
if (!IsNormalized(det) || math_internal::Compare(det, HighLow{}) != 1)
return Result<Hex8HighLowResponse>::Failure(Failure(
"Hex8 full deformation determinant is not finite positive."));
Tensor stress = s;
if (nonlinear) {
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];
stress[i][j] = stress[i][j] / det;
}
}
if (!Finite(h) || !Finite(f) || !Finite(strain) || !Finite(s) ||
!Finite(p) || !Finite(fft) || !Finite(stress))
return Result<Hex8HighLowResponse>::Failure(Failure(
"Hex8 high+low tensor arithmetic is outside its supported range."));
response.high.points[g] = {element_index_, static_cast<int>(g + 1U),
point.natural, point.position,
Pack(stress), Pack(strain)};
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[a]);
force[3U * a + i] += Dot(p[i], gradients[a]) * HighLow{point.weight};
}
for (std::size_t a = 0; a < 8; ++a)
for (std::size_t b = 0; b < 8; ++b) {
const auto dot = Dot(gradients[a], gradients[b]);
HighLow geo;
if (nonlinear)
for (std::size_t j = 0; j < 3; ++j)
for (std::size_t l = 0; l < 3; ++l)
geo += gradients[a][j] * s[l][j] * gradients[b][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;
const auto mat = HighLow{lambda_} * fg[a][i] * fg[b][k] +
HighLow{mu_} * fg[b][i] * fg[a][k] +
HighLow{mu_} * fft[i][k] * dot;
material[row][column] += mat * HighLow{point.weight};
if (i == k)
geometric[row][column] += geo * HighLow{point.weight};
}
}
}
if (!IsNormalized(energy))
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low energy is unsupported or nonfinite."));
response.high.strain_energy = energy.high;
response.strain_energy_low = energy.low;
for (std::size_t i = 0; i < 24; ++i) {
if (!IsNormalized(force[i]))
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low force is unsupported or nonfinite."));
response.high.internal_force[i] = force[i].high;
response.internal_force_low[i] = force[i].low;
for (std::size_t j = 0; j < 24; ++j) {
const auto tangent = material[i][j] + geometric[i][j];
if (!IsNormalized(material[i][j]) || !IsNormalized(geometric[i][j]) ||
!IsNormalized(tangent))
return Result<Hex8HighLowResponse>::Failure(
Failure("Hex8 high+low tangent is unsupported or nonfinite."));
response.high.material_tangent(i, j) = material[i][j].high;
response.high.geometric_tangent(i, j) = geometric[i][j].high;
response.high.tangent(i, j) = tangent.high;
response.tangent_low(i, j) = tangent.low;
}
}
return Result<Hex8HighLowResponse>::Success(std::move(response));
}
} // namespace fesa