fix(hht): preserve metric association and complete MKL actions
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.",
|
||||
|
||||
Reference in New Issue
Block a user