fix(hht): preserve metric association and complete MKL actions

This commit is contained in:
KOKO\Mimi
2026-10-14 18:38:00 +09:00
parent 2853051896
commit c58d499727
6 changed files with 155 additions and 43 deletions
+29 -14
View File
@@ -495,20 +495,8 @@ 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;
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) {
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));
}
const double stiffness = OrderedNullMetric(
assembly.drilling_metric, nodes[i], n);
if (!std::isfinite(stiffness) || !(stiffness > 0.0)) {
return Result<Regularity>::Failure(
Failure(domain, "dynamic-algebraic-regularity",
@@ -674,6 +662,33 @@ Status HiddenEvidence(const Domain& domain, const DynamicAssembly& assembly,
} // namespace
double OrderedNullMetric(const SparseMatrix& metric, const std::size_t node,
const Vector3& direction) {
Vector left{9U};
Vector entries{9U};
Vector right{9U};
for (std::size_t i = 0U; i < 3U; ++i)
for (std::size_t j = 0U; j < 3U; ++j) {
const auto index = 3U * i + j;
left[index] = direction[i];
entries[index] = Entry(metric, 6U * node + 3U + i,
6U * node + 3U + j);
right[index] = direction[j];
if (!std::isfinite(left[index]) || !std::isfinite(entries[index]) ||
!std::isfinite(right[index]))
return std::numeric_limits<double>::quiet_NaN();
}
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
const Vector first = dense_blas_internal::MultiplyDiagonal(left, entries);
for (std::size_t i = 0U; i < first.Size(); ++i)
if (!std::isfinite(first[i]))
return std::numeric_limits<double>::quiet_NaN();
const Vector products = dense_blas_internal::MultiplyDiagonal(first, right);
double value = 0.0;
for (std::size_t i = 0U; i < products.Size(); ++i) value += products[i];
return value;
}
std::vector<std::size_t> NullNodes(
const DofManager& dofs,
const std::vector<DynamicRotationKinematics>& rotations) {
+45 -3
View File
@@ -7,6 +7,7 @@
#include <utility>
#include "analysis/hht_dynamic_internal.h"
#include "math/dense_blas_internal.h"
#include "fesa/assembly/parallel_for.h"
#include "fesa/elements/finite_rotation.h"
#include "fesa/loads/load.h"
@@ -54,6 +55,27 @@ Vector Gather(const Vector& full, const RuntimeShell& shell) {
return local;
}
/// @brief Preserves rejected entries while batching finite products in MKL.
Vector EntryProducts(const Vector& left, const Vector& right) {
Vector result(left.Size(), std::numeric_limits<double>::quiet_NaN());
std::vector<std::size_t> positions;
positions.reserve(left.Size());
for (std::size_t i = 0U; i < left.Size(); ++i)
if (std::isfinite(left[i]) && std::isfinite(right[i])) positions.push_back(i);
Vector valid_left{positions.size()};
Vector valid_right{positions.size()};
for (std::size_t i = 0U; i < positions.size(); ++i) {
valid_left[i] = left[positions[i]];
valid_right[i] = right[positions[i]];
}
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
const Vector products =
dense_blas_internal::MultiplyDiagonal(valid_left, valid_right);
for (std::size_t i = 0U; i < positions.size(); ++i)
result[positions[i]] = products[i];
return result;
}
} // namespace
Result<std::vector<RuntimeShell>> BuildRuntimeShells(const Domain& domain,
@@ -455,6 +477,15 @@ Result<DynamicAssembly> EvaluateAssembly(
}
}
const auto a = Gather(frame.acceleration, shells[e]);
Vector mass_values{24U * 24U};
Vector acceleration_values{24U * 24U};
for (std::size_t row = 0U; row < 24U; ++row)
for (std::size_t column = 0U; column < 24U; ++column) {
const auto index = 24U * row + column;
mass_values[index] = m.mass(row, column);
acceleration_values[index] = a[column];
}
const Vector mass_action = EntryProducts(mass_values, acceleration_values);
for (std::size_t row = 0U; row < 24U; ++row) {
const auto global = shells[e].scatter[row];
frame.internal_force[global] += f.internal_force[row];
@@ -466,7 +497,7 @@ Result<DynamicAssembly> EvaluateAssembly(
std::abs(f.drilling_force[row]);
for (std::size_t column = 0U; column < 24U; ++column) {
frame.residual_scale[global] +=
std::abs(m.mass(row, column) * a[column]);
std::abs(mass_action[24U * row + column]);
const std::array<double, 6> values{
m.mass(row, column), m.position_tangent(row, column),
m.velocity_tangent(row, column), f.internal_tangent(row, column),
@@ -543,9 +574,20 @@ Result<DynamicAssembly> EvaluateAssembly(
Vector hidden_second(domain.Nodes().size());
Vector hidden_second_scale(domain.Nodes().size());
for (std::size_t node = 0U; node < constraint_hessians.size(); ++node) {
Vector coefficients{constraint_hessians[node].size()};
Vector first_velocity{coefficients.Size()};
Vector second_velocity{coefficients.Size()};
std::size_t index = 0U;
for (const auto& entry : constraint_hessians[node]) {
const double term = entry.second * frame.velocity[entry.first.first] *
frame.velocity[entry.first.second];
coefficients[index] = entry.second;
first_velocity[index] = frame.velocity[entry.first.first];
second_velocity[index] = frame.velocity[entry.first.second];
++index;
}
const Vector products = EntryProducts(
EntryProducts(coefficients, first_velocity), second_velocity);
for (std::size_t i = 0U; i < products.Size(); ++i) {
const double term = products[i];
hidden_second[node] += term;
hidden_second_scale[node] += std::abs(term);
}
+5
View File
@@ -95,6 +95,11 @@ std::vector<std::size_t> NullNodes(
const DofManager& dofs,
const std::vector<DynamicRotationKinematics>& rotations);
/// @brief Contracts a nodal metric with original left-associated products.
/// @note Nonfinite products remain for the caller's existing validity gate.
double OrderedNullMetric(const SparseMatrix& metric, std::size_t node,
const Vector3& direction);
/// @brief Solves the positive-mass and differentiated-null initial equations.
///
/// The returned candidate is finite and exact-zero at constrained DOFs. Its
+2 -26
View File
@@ -87,19 +87,6 @@ Result<SparseMatrix> DenseSparse(const Matrix& matrix) {
std::move(entries));
}
double SparseEntry(const SparseMatrix& matrix, const std::size_t row,
const std::size_t column) {
const auto first = matrix.RowOffsets()[row];
const auto last = matrix.RowOffsets()[row + 1U];
const auto found = std::lower_bound(matrix.ColumnIndices().begin() + first,
matrix.ColumnIndices().begin() + last,
column);
if (found == matrix.ColumnIndices().begin() + last || *found != column)
return 0.0;
return matrix.Values()[static_cast<std::size_t>(
found - matrix.ColumnIndices().begin())];
}
bool ValidBasis(const FixedChart& chart) {
if (chart.p_columns.size() != chart.p_dofs.size() ||
chart.z_columns.size() != chart.z_dofs.size()) return false;
@@ -1211,19 +1198,8 @@ Result<double> CheckGraphRegularity(
Failure("Reduced graph regularity node is invalid.",
"dynamic-mass-regularity"));
const auto& null = assembly.rotations[node].drill_null;
Matrix metric_block{3U, 3U};
Vector direction{3U};
for (std::size_t i = 0U; i < 3U; ++i) {
direction[i] = null[i];
for (std::size_t j = 0U; j < 3U; ++j)
metric_block(i, j) = SparseEntry(
assembly.drilling_metric, 6U * node + 3U + i, 6U * node + 3U + j);
}
double metric = 0.0;
{
const dense_blas_internal::ScopedLocalDenseThreads local_threads{};
metric = direction.Dot(metric_block.Multiply(direction));
}
const double metric = hht_internal::OrderedNullMetric(
assembly.drilling_metric, node, null);
if (!std::isfinite(metric) || !(metric > 0.0))
return Result<double>::Failure(Failure(
"Reduced graph requires a positive finite null drilling metric.",