perf(shell): route local products through MKL and reduce Hessian storage work

This commit is contained in:
KOKO\Mimi
2026-10-14 18:07:16 +09:00
parent 8c7c41729e
commit f9e0df66dc
9 changed files with 758 additions and 179 deletions
+124 -45
View File
@@ -2,9 +2,11 @@
#include <array>
#include <cmath>
#include <cstddef>
#include <exception>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
@@ -13,6 +15,7 @@
#include "fesa/math/matrix.h"
#include "fesa/solvers/linear/linear_solver.h"
#include "fesa/solvers/linear/tangent_solver.h"
#include "math/dense_blas_internal.h"
namespace fesa {
namespace hht_internal {
@@ -132,13 +135,13 @@ Status CheckLocalMass(const Domain& domain, const SparseMatrix& matrix,
const std::size_t begin, const std::size_t end) {
if (begin == end) return Status::Ok();
const std::size_t size = end - begin;
std::array<std::array<double, 6>, 6> block{};
std::array<std::array<double, 6>, 6> lower{};
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
Matrix block{size, size};
double scale = 0.0;
for (std::size_t i = 0; i < size; ++i) {
for (std::size_t j = 0; j < size; ++j) {
block[i][j] = Entry(matrix, begin + i, begin + j);
scale = std::max(scale, std::abs(block[i][j]));
block(i, j) = Entry(matrix, begin + i, begin + j);
scale = std::max(scale, std::abs(block(i, j)));
}
}
if (!std::isfinite(scale) || !(scale > 0.0)) {
@@ -149,36 +152,34 @@ Status CheckLocalMass(const Domain& domain, const SparseMatrix& matrix,
for (std::size_t i = 0; i < size; ++i) {
double sum = 0.0;
for (std::size_t j = 0; j < size; ++j) {
block[i][j] /= scale;
sum += std::abs(block[i][j]);
block(i, j) /= scale;
sum += std::abs(block(i, j));
}
norm = std::max(norm, sum);
for (std::size_t j = 0; j <= i; ++j) {
double value = block[i][j];
for (std::size_t k = 0; k < j; ++k) value -= lower[i][k] * lower[j][k];
if (!std::isfinite(value) || (i == j && !(value > 0.0))) {
return Failure(
domain, "dynamic-positive-mass",
"Physical mass requires strictly positive Cholesky pivots.");
}
lower[i][j] = i == j ? std::sqrt(value) : value / lower[j][j];
}
}
Matrix lower{size, size};
Matrix inverse_lower{size, size};
auto factor_failure = dense_blas_internal::CholeskyFailure::kNone;
try {
if (!dense_blas_internal::CholeskyLowerAndInverse(
block, lower, inverse_lower, &factor_failure)) {
if (factor_failure == dense_blas_internal::CholeskyFailure::kInverse)
return Failure(
domain, "dynamic-positive-mass-conditioning",
"A nodal free physical block reciprocal condition is below 2^-32.");
return Failure(
domain, "dynamic-positive-mass",
"Physical mass requires strictly positive Cholesky pivots.");
}
} catch (const std::exception& error) {
return Failure(domain, "dynamic-positive-mass", error.what());
}
const Matrix inverse =
dense_blas_internal::MultiplyTransposeLeft(inverse_lower, inverse_lower);
std::array<double, 6> sums{};
for (std::size_t column = 0; column < size; ++column) {
std::array<double, 6> solution{};
for (std::size_t i = 0; i < size; ++i) {
double value = i == column ? 1.0 : 0.0;
for (std::size_t k = 0; k < i; ++k) value -= lower[i][k] * solution[k];
solution[i] = value / lower[i][i];
}
for (std::size_t reverse = size; reverse > 0U; --reverse) {
const std::size_t i = reverse - 1U;
double value = solution[i];
for (std::size_t k = i + 1U; k < size; ++k)
value -= lower[k][i] * solution[k];
solution[i] = value / lower[i][i];
sums[i] += std::abs(solution[i]);
sums[i] += std::abs(inverse(i, column));
}
}
const double inverse_norm = *std::max_element(sums.begin(), sums.end());
@@ -212,19 +213,33 @@ Result<PhysicalSpace> BuildPhysicalSpace(
double symmetry_error = 0.0;
double null_error = 0.0;
if (mass_scale > 0.0) {
Vector normalized_mass{mass.Values().size()};
Vector null_coefficients{mass.Values().size()};
for (std::size_t index = 0U; index < mass.Values().size(); ++index) {
normalized_mass[index] = mass.Values()[index] / mass_scale;
const auto full = dofs.FreeDofs()[mass.ColumnIndices()[index]];
if (full % 6U >= 3U && node_null[full / 6U] != no_null)
null_coefficients[index] =
assembly.rotations[full / 6U].drill_null[full % 6U - 3U];
}
Vector null_products{0U};
{
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
null_products = dense_blas_internal::MultiplyDiagonal(normalized_mass,
null_coefficients);
}
for (std::size_t row = 0; row < mass.Rows(); ++row) {
std::vector<double> action(null_nodes.size(), 0.0);
for (std::size_t index = mass.RowOffsets()[row];
index < mass.RowOffsets()[row + 1U]; ++index) {
const std::size_t column = mass.ColumnIndices()[index];
const double entry = mass.Values()[index] / mass_scale;
const double entry = normalized_mass[index];
mass_norm = std::hypot(mass_norm, entry);
symmetry_error = std::hypot(
symmetry_error, entry - Entry(mass, column, row) / mass_scale);
const std::size_t full = dofs.FreeDofs()[column];
if (full % 6U >= 3U && node_null[full / 6U] != no_null) {
action[node_null[full / 6U]] +=
entry * assembly.rotations[full / 6U].drill_null[full % 6U - 3U];
action[node_null[full / 6U]] += null_products[index];
}
}
for (const double value : action)
@@ -291,6 +306,7 @@ Result<PhysicalSpace> BuildPhysicalSpace(
}
}
std::vector<CooContribution> entries;
std::vector<std::pair<double, double>> mass_factors;
node_starts.push_back(dimension);
double complement_gram_error = 0.0;
double null_gram_error = 0.0;
@@ -350,8 +366,10 @@ Result<PhysicalSpace> BuildPhysicalSpace(
domain, "dynamic-invalid-analytic-null",
"The scaled physical and analytic null Gram identities failed."));
}
for (std::size_t i = 0; i < dimension; ++i)
for (std::size_t i = 0; i < dimension; ++i) {
entries.push_back({i, i, 0.0, 0U, entries.size()});
mass_factors.push_back({0.0, 0.0});
}
for (std::size_t row = 0; row < mass.Rows(); ++row) {
const auto& left = rows[dofs.FreeDofs()[row]];
for (std::size_t index = mass.RowOffsets()[row];
@@ -359,13 +377,54 @@ Result<PhysicalSpace> BuildPhysicalSpace(
const auto& right = rows[dofs.FreeDofs()[mass.ColumnIndices()[index]]];
for (const auto& a : left) {
for (const auto& b : right) {
entries.push_back({a.first, b.first,
a.second * mass.Values()[index] * b.second, 0U,
entries.size()});
entries.push_back({a.first, b.first, a.second, 0U, entries.size()});
mass_factors.push_back({mass.Values()[index], b.second});
}
}
}
}
Vector coefficients{entries.size()};
Vector mass_values{entries.size()};
Vector right_values{entries.size()};
for (std::size_t i = 0U; i < entries.size(); ++i) {
coefficients[i] = entries[i].value;
mass_values[i] = mass_factors[i].first;
right_values[i] = mass_factors[i].second;
}
Vector projected{0U};
{
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
projected =
dense_blas_internal::MultiplyDiagonal(coefficients, mass_values);
bool finite = true;
for (std::size_t i = 0U; i < projected.Size(); ++i) {
entries[i].value = projected[i];
finite = finite && std::isfinite(projected[i]);
}
// A rejected intermediate cannot produce a finite original contribution.
// Still compute the remaining finite entries so an earlier final-product
// overflow keeps its diagnostic priority. No invalid operand reaches MKL.
if (!finite) {
std::vector<std::size_t> valid_positions;
for (std::size_t i = 0U; i < projected.Size(); ++i)
if (std::isfinite(projected[i])) valid_positions.push_back(i);
Vector valid_left{valid_positions.size()};
Vector valid_right{valid_positions.size()};
for (std::size_t i = 0U; i < valid_positions.size(); ++i) {
valid_left[i] = projected[valid_positions[i]];
valid_right[i] = right_values[valid_positions[i]];
}
const Vector valid_products =
dense_blas_internal::MultiplyDiagonal(valid_left, valid_right);
for (std::size_t i = 0U; i < valid_positions.size(); ++i)
entries[valid_positions[i]].value = valid_products[i];
auto rejected = MakeSparse(dimension, dimension, std::move(entries));
return Result<PhysicalSpace>::Failure(rejected.GetStatus());
}
projected = dense_blas_internal::MultiplyDiagonal(projected, right_values);
}
for (std::size_t i = 0U; i < entries.size(); ++i)
entries[i].value = projected[i];
auto block = MakeSparse(dimension, dimension, std::move(entries));
if (!block.HasValue())
return Result<PhysicalSpace>::Failure(block.GetStatus());
@@ -436,15 +495,20 @@ Result<Regularity> CheckRegularity(const Domain& domain,
for (std::size_t i = 0; i < nodes.size(); ++i) {
node_null[nodes[i]] = i;
const auto& n = assembly.rotations[nodes[i]].drill_null;
double stiffness = 0.0;
Matrix metric{3U, 3U};
Vector direction{3U};
for (std::size_t a = 0; a < 3; ++a) {
direction[a] = n[a];
for (std::size_t b = 0; b < 3; ++b) {
stiffness += n[a] *
Entry(assembly.drilling_metric, 6U * nodes[i] + 3U + a,
6U * nodes[i] + 3U + b) *
n[b];
metric(a, b) = Entry(assembly.drilling_metric, 6U * nodes[i] + 3U + a,
6U * nodes[i] + 3U + b);
}
}
double stiffness = 0.0;
{
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
stiffness = direction.Dot(metric.Multiply(direction));
}
if (!std::isfinite(stiffness) || !(stiffness > 0.0)) {
return Result<Regularity>::Failure(
Failure(domain, "dynamic-algebraic-regularity",
@@ -454,22 +518,37 @@ Result<Regularity> CheckRegularity(const Domain& domain,
weights[i] = std::sqrt(stiffness);
}
std::vector<CooContribution> entries;
std::vector<double> null_coefficients;
for (std::size_t row = 0; row < nodes.size(); ++row) {
entries.push_back({row, row, 0.0, 0U, entries.size()});
null_coefficients.push_back(0.0);
for (std::size_t index = gradient.RowOffsets()[row];
index < gradient.RowOffsets()[row + 1U]; ++index) {
const std::size_t full = gradient.ColumnIndices()[index];
if (full % 6U >= 3U && node_null[full / 6U] != no_null) {
const std::size_t column = node_null[full / 6U];
entries.push_back(
{row, column,
gradient.Values()[index] *
assembly.rotations[full / 6U].drill_null[full % 6U - 3U] /
weights[row] / weights[column],
0U, entries.size()});
{row, column, gradient.Values()[index], 0U, entries.size()});
null_coefficients.push_back(
assembly.rotations[full / 6U].drill_null[full % 6U - 3U]);
}
}
}
Vector gradient_values{entries.size()};
Vector null_values{entries.size()};
for (std::size_t i = 0U; i < entries.size(); ++i) {
gradient_values[i] = entries[i].value;
null_values[i] = null_coefficients[i];
}
Vector products{0U};
{
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
products =
dense_blas_internal::MultiplyDiagonal(gradient_values, null_values);
}
for (std::size_t i = 0U; i < entries.size(); ++i)
entries[i].value =
products[i] / weights[entries[i].row] / weights[entries[i].column];
auto matrix = MakeSparse(nodes.size(), nodes.size(), std::move(entries));
if (!matrix.HasValue())
return Result<Regularity>::Failure(matrix.GetStatus());