perf(math): route ordinary products through MKL and preserve arithmetic
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/fem/dof_manager.h"
|
||||
#include "math/dense_blas_internal.h"
|
||||
|
||||
namespace fesa::eigen_internal {
|
||||
namespace {
|
||||
@@ -117,7 +118,8 @@ Result<NodalLift> MakeLift(const Matrix& mass,
|
||||
if (positive == 0U) return Result<NodalLift>::Success(std::move(result));
|
||||
|
||||
const Matrix scaled = ScaledMass(mass, length);
|
||||
Matrix mp(positive, positive);
|
||||
Matrix mp = dense_blas_internal::MultiplyTransposeLeft(basis, scaled)
|
||||
.Multiply(basis);
|
||||
double scale = 0.0;
|
||||
double basis_error = 0.0;
|
||||
for (std::size_t i = 0; i < positive; ++i) {
|
||||
@@ -125,9 +127,6 @@ Result<NodalLift> MakeLift(const Matrix& mass,
|
||||
double gram = 0.0;
|
||||
for (std::size_t a = 0; a < 6U; ++a) {
|
||||
gram += basis(a, i) * basis(a, j);
|
||||
for (std::size_t b = 0; b < 6U; ++b) {
|
||||
mp(i, j) += basis(a, i) * scaled(a, b) * basis(b, j);
|
||||
}
|
||||
}
|
||||
basis_error = std::hypot(basis_error, gram - (i == j ? 1.0 : 0.0));
|
||||
scale = (std::max)(scale, std::abs(mp(i, j)));
|
||||
@@ -343,17 +342,18 @@ Result<EigenReduction> BuildEigenReduction(
|
||||
kz_error = std::hypot(kz_error, item.second);
|
||||
}
|
||||
const auto& left = lifts[node];
|
||||
std::map<std::size_t, Matrix> projected_blocks;
|
||||
for (const auto& entry : blocks) {
|
||||
projected_blocks.emplace(
|
||||
entry.first, dense_blas_internal::MultiplyTransposeLeft(
|
||||
left.scaled_lift, entry.second)
|
||||
.Multiply(lifts[entry.first].scaled_lift));
|
||||
}
|
||||
for (std::size_t i = 0; i < left.scaled_lift.Columns(); ++i) {
|
||||
for (const auto& entry : blocks) {
|
||||
for (const auto& entry : projected_blocks) {
|
||||
const auto& right = lifts[entry.first];
|
||||
for (std::size_t j = 0; j < right.scaled_lift.Columns(); ++j) {
|
||||
double value = 0.0;
|
||||
for (std::size_t a = 0; a < 6U; ++a) {
|
||||
for (std::size_t b = 0; b < 6U; ++b) {
|
||||
value += left.scaled_lift(a, i) * entry.second(a, b) *
|
||||
right.scaled_lift(b, j);
|
||||
}
|
||||
}
|
||||
const double value = entry.second(i, j);
|
||||
pattern.column_indices.push_back(right.offset + j);
|
||||
contributions.push_back({left.offset + i, right.offset + j, value,
|
||||
node, pattern.column_indices.size() - 1U});
|
||||
@@ -385,11 +385,12 @@ Vector RestoreScaledMode(const EigenReduction& reduction, const Matrix& vectors,
|
||||
Vector result(reduction.nodes.size() * 6U);
|
||||
for (std::size_t node = 0; node < reduction.nodes.size(); ++node) {
|
||||
const auto& lift = reduction.nodes[node];
|
||||
Vector reduced{lift.scaled_lift.Columns()};
|
||||
for (std::size_t j = 0; j < reduced.Size(); ++j)
|
||||
reduced[j] = vectors(lift.offset + j, column);
|
||||
const auto restored = lift.scaled_lift.Multiply(reduced);
|
||||
for (std::size_t a = 0; a < 6U; ++a) {
|
||||
for (std::size_t j = 0; j < lift.scaled_lift.Columns(); ++j) {
|
||||
result[6U * node + a] +=
|
||||
lift.scaled_lift(a, j) * vectors(lift.offset + j, column);
|
||||
}
|
||||
result[6U * node + a] = restored[a];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -399,10 +400,11 @@ Vector MultiplyNodalMass(const std::vector<Matrix>& mass,
|
||||
const Vector& vector) {
|
||||
Vector result(vector.Size());
|
||||
for (std::size_t node = 0; node < mass.size(); ++node) {
|
||||
Vector local{6U};
|
||||
for (std::size_t b = 0; b < 6U; ++b) local[b] = vector[6U * node + b];
|
||||
const auto product = mass[node].Multiply(local);
|
||||
for (std::size_t a = 0; a < 6U; ++a) {
|
||||
for (std::size_t b = 0; b < 6U; ++b) {
|
||||
result[6U * node + a] += mass[node](a, b) * vector[6U * node + b];
|
||||
}
|
||||
result[6U * node + a] = product[a];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -32,11 +32,7 @@ Status Failure(const std::string& code, const std::string& identity,
|
||||
}
|
||||
|
||||
double Dot(const Vector& left, const Vector& right) {
|
||||
double result = 0.0;
|
||||
for (std::size_t index = 0U; index < left.Size(); ++index) {
|
||||
result += left[index] * right[index];
|
||||
}
|
||||
return result;
|
||||
return left.Dot(right);
|
||||
}
|
||||
|
||||
double Norm(const Vector& vector) {
|
||||
@@ -318,8 +314,13 @@ Result<EigenAnalysisState> ExecuteCandidate(const Domain& domain) {
|
||||
(pencil_scale * y_norm) * y_norm);
|
||||
const double rayleigh = energy / modal_mass;
|
||||
Vector x(a.Rows());
|
||||
for (std::size_t row = 0; row < x.Size(); ++row)
|
||||
for (std::size_t row = 0; row < x.Size(); ++row) {
|
||||
x[row] = state.backend.eigenvectors(row, column) * normalization;
|
||||
if (!std::isfinite(x[row]))
|
||||
return Result<EigenAnalysisState>::Failure(
|
||||
Failure("invalid-eigenvalue-result", std::to_string(column),
|
||||
"Recovered mode is nonfinite or violates constraints."));
|
||||
}
|
||||
Vector ax = a.Multiply(x);
|
||||
Vector whitened_residual = ax;
|
||||
whitened_residual.Axpy(-lambda, x);
|
||||
@@ -421,6 +422,10 @@ Result<EigenAnalysisState> LinearFrequencyProcedureExecutor::Execute(
|
||||
return Result<EigenAnalysisState>::Failure(
|
||||
Failure("eigen-index-overflow", "modal-workspace",
|
||||
"Requested workspace exceeds container capacity."));
|
||||
} catch (const std::runtime_error&) {
|
||||
return Result<EigenAnalysisState>::Failure(
|
||||
Failure("eigen-multiplication-failed", "modal-workspace",
|
||||
"Modal sparse multiplication failed."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,8 +248,13 @@ Result<SolidFrequencyState> ExecuteCandidate(const Domain& domain) {
|
||||
mass_phi.reserve(count);
|
||||
for (std::size_t c = 0; c < count; ++c) {
|
||||
Vector vector(k.Rows());
|
||||
for (std::size_t row = 0; row < k.Rows(); ++row)
|
||||
for (std::size_t row = 0; row < k.Rows(); ++row) {
|
||||
vector[row] = state.backend.eigenvectors(row, c);
|
||||
if (!std::isfinite(vector[row]))
|
||||
return Result<SolidFrequencyState>::Failure(
|
||||
Failure("solid-eigenpair-validation-failed",
|
||||
"Modal mass is not finite positive."));
|
||||
}
|
||||
Vector mv = m.Multiply(vector);
|
||||
const double raw_mass = vector.Dot(mv);
|
||||
if (!(raw_mass > 0.0) || !std::isfinite(raw_mass))
|
||||
@@ -257,6 +262,12 @@ Result<SolidFrequencyState> ExecuteCandidate(const Domain& domain) {
|
||||
Failure("solid-eigenpair-validation-failed",
|
||||
"Modal mass is not finite positive."));
|
||||
vector.Scale(1.0 / std::sqrt(raw_mass));
|
||||
for (std::size_t row = 0; row < vector.Size(); ++row) {
|
||||
if (!std::isfinite(vector[row]))
|
||||
return Result<SolidFrequencyState>::Failure(
|
||||
Failure("solid-eigenpair-validation-failed",
|
||||
"Normalized modal vector is not finite."));
|
||||
}
|
||||
mv = m.Multiply(vector);
|
||||
const Vector kv = k.Multiply(vector);
|
||||
const double lambda = state.backend.eigenvalues[c];
|
||||
@@ -362,6 +373,10 @@ Result<SolidFrequencyState> SolidFrequencyProcedureExecutor::Execute(
|
||||
return Result<SolidFrequencyState>::Failure(
|
||||
Failure("eigen-allocation-failed",
|
||||
"Solid modal workspace exceeds addressable storage."));
|
||||
} catch (const std::runtime_error&) {
|
||||
return Result<SolidFrequencyState>::Failure(
|
||||
Failure("eigen-multiplication-failed",
|
||||
"Solid modal sparse multiplication failed."));
|
||||
}
|
||||
}
|
||||
} // namespace fesa
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -116,6 +117,14 @@ Result<Equations> Build(const ThermalSystem& s, const ThermalPartition& p,
|
||||
constrained[p.constrained[i]] = i;
|
||||
SparsePattern pattern{{0}, {}};
|
||||
std::vector<CooContribution> coo;
|
||||
SparsePattern rhs_pattern{{0U}, {}};
|
||||
std::vector<CooContribution> capacity_rhs, conductivity_rhs;
|
||||
Vector capacity_input = old;
|
||||
Vector prescribed_full{m};
|
||||
std::vector<bool> capacity_column_used(m, false);
|
||||
for (std::size_t i = 0; i < p.constrained.size(); ++i) {
|
||||
prescribed_full[p.constrained[i]] = p.prescribed[i];
|
||||
}
|
||||
Vector b{n};
|
||||
std::vector<I> exact_a, exact_b(n);
|
||||
for (std::size_t f = 0; f < n; ++f) {
|
||||
@@ -126,22 +135,43 @@ Result<Equations> Build(const ThermalSystem& s, const ThermalPartition& p,
|
||||
const auto col = s.c.ColumnIndices()[j];
|
||||
const double c = s.c.Values()[j], k = s.k.Values()[j];
|
||||
const I cd = Div(I{c}, I{delta});
|
||||
rhs_pattern.column_indices.push_back(col);
|
||||
capacity_column_used[col] = true;
|
||||
capacity_rhs.push_back({f, col, c / delta, 0U, j});
|
||||
conductivity_rhs.push_back({f, col, k, 0U, j});
|
||||
if (free[col] != n) {
|
||||
coo.push_back({f, free[col], c / delta + k, 0, coo.size()});
|
||||
pattern.column_indices.push_back(free[col]);
|
||||
exact_a.push_back(Add(cd, I{k}));
|
||||
b[f] += (c / delta) * old[col];
|
||||
exact_b[f] = Add(exact_b[f], Mul(cd, I{old[col]}));
|
||||
} else {
|
||||
const double g = p.prescribed[constrained[col]];
|
||||
// Preserve raw old constrained capacity; never project frame zero.
|
||||
b[f] += (c / delta) * (old[col] - g) - k * g;
|
||||
exact_b[f] = Add(exact_b[f],
|
||||
Sub(Mul(cd, Sub(I{old[col]}, I{g})), Mul(I{k}, I{g})));
|
||||
}
|
||||
}
|
||||
pattern.row_offsets.push_back(coo.size());
|
||||
rhs_pattern.row_offsets.push_back(capacity_rhs.size());
|
||||
}
|
||||
// Do not create overflow in an absent constrained column: the original
|
||||
// free-row RHS never evaluated that subtraction. Referenced structural
|
||||
// zeros still evaluate it and reject nonfinite intermediate values.
|
||||
for (std::size_t i = 0; i < p.constrained.size(); ++i) {
|
||||
const auto column = p.constrained[i];
|
||||
if (capacity_column_used[column]) capacity_input[column] -= p.prescribed[i];
|
||||
}
|
||||
if (!Finite(capacity_input))
|
||||
return Result<Equations>::Failure(Failure("Nonfinite backward Euler RHS."));
|
||||
auto c_rhs = SparseMatrix::FromCoo(n, m, std::move(capacity_rhs), rhs_pattern);
|
||||
auto k_rhs = SparseMatrix::FromCoo(n, m, std::move(conductivity_rhs),
|
||||
rhs_pattern);
|
||||
if (!c_rhs.HasValue() || !k_rhs.HasValue())
|
||||
return Result<Equations>::Failure(Failure("Nonfinite backward Euler RHS."));
|
||||
const auto capacity_action = c_rhs.Value().Multiply(capacity_input);
|
||||
const auto conductivity_action = k_rhs.Value().Multiply(prescribed_full);
|
||||
for (std::size_t f = 0; f < n; ++f)
|
||||
b[f] += capacity_action[f] - conductivity_action[f];
|
||||
auto a = SparseMatrix::FromCoo(n, n, std::move(coo), pattern);
|
||||
if (!a.HasValue()) return Result<Equations>::Failure(a.GetStatus());
|
||||
if (!Finite(b))
|
||||
@@ -178,13 +208,15 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
I a_norm, t_norm, b_norm = Norm(eq.b);
|
||||
Vector red{n}, red_error{n};
|
||||
std::vector<I> construction(n);
|
||||
Vector free_temperature{n};
|
||||
for (std::size_t f = 0; f < n; ++f)
|
||||
free_temperature[f] = candidate.temperature[p.free[f]];
|
||||
const auto reduced_action = eq.a.Multiply(free_temperature);
|
||||
for (std::size_t f = 0; f < n; ++f) {
|
||||
I row_norm, scale, ea, identity;
|
||||
double dot = 0;
|
||||
for (auto j = eq.a.RowOffsets()[f]; j < eq.a.RowOffsets()[f + 1]; ++j) {
|
||||
const double a = eq.a.Values()[j],
|
||||
t = candidate.temperature[p.free[eq.a.ColumnIndices()[j]]];
|
||||
dot += a * t;
|
||||
row_norm = Add(row_norm, Magnitude(a));
|
||||
scale = Add(scale, Mul(Magnitude(a), Magnitude(t)));
|
||||
const I error{Error(eq.exact_a[j], a)};
|
||||
@@ -193,7 +225,7 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
}
|
||||
a_norm = Max(a_norm, row_norm);
|
||||
t_norm = Max(t_norm, Magnitude(candidate.temperature[p.free[f]]));
|
||||
red[f] = dot - eq.b[f];
|
||||
red[f] = reduced_action[f] - eq.b[f];
|
||||
scale = Add(scale, Magnitude(eq.b[f]));
|
||||
const double nu =
|
||||
static_cast<double>(eq.a.RowOffsets()[f + 1] - eq.a.RowOffsets()[f]);
|
||||
@@ -216,18 +248,19 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
for (std::size_t i = 0; i < m; ++i)
|
||||
change[i] = candidate.temperature[i] - old[i];
|
||||
if (!Finite(change)) return Failure("Nonfinite temperature change.");
|
||||
const auto capacity_change = s.c.Multiply(change);
|
||||
const auto conductivity_new = s.k.Multiply(candidate.temperature);
|
||||
I c_change, k_new, q_abs, c_temperatures, underflow_sum;
|
||||
std::vector<I> k_columns(m);
|
||||
double energy = 0;
|
||||
for (std::size_t i = 0; i < m; ++i) {
|
||||
double cd = 0, kt = 0, ksum = 0;
|
||||
const double cd = capacity_change[i], kt = conductivity_new[i];
|
||||
double ksum = 0;
|
||||
I cs, ks, ts, cabs, nullscale, nullerror, coefficient;
|
||||
for (auto j = s.c.RowOffsets()[i]; j < s.c.RowOffsets()[i + 1]; ++j) {
|
||||
const auto col = s.c.ColumnIndices()[j];
|
||||
const double c = s.c.Values()[j], k = s.k.Values()[j],
|
||||
t = candidate.temperature[col];
|
||||
cd += c * change[col];
|
||||
kt += k * t;
|
||||
ksum += k;
|
||||
cs = Add(cs, Mul(Magnitude(c), Magnitude(change[col])));
|
||||
ks = Add(ks, Mul(Magnitude(k), Magnitude(t)));
|
||||
@@ -334,10 +367,13 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
if (p.constrained.empty() && Zero(q_abs)) {
|
||||
ev.dissipation_checked = true;
|
||||
double cn = 0, co = 0, cd = 0, kn = 0;
|
||||
const auto capacity_new = s.c.Multiply(candidate.temperature);
|
||||
const auto capacity_old = s.c.Multiply(old);
|
||||
I sc, subscale, t_one, d_square, o_square, skew_square, quadratic_underflow;
|
||||
for (std::size_t i = 0; i < m; ++i) {
|
||||
const double t = candidate.temperature[i];
|
||||
double row_cn = 0, row_co = 0, row_cd = 0, row_kn = 0;
|
||||
const double row_cn = capacity_new[i], row_co = capacity_old[i],
|
||||
row_cd = capacity_change[i], row_kn = conductivity_new[i];
|
||||
I scale_cn, scale_co, scale_cd, scale_kn;
|
||||
t_one = Add(t_one, Magnitude(t));
|
||||
d_square = Add(d_square, Mul(Magnitude(change[i]), Magnitude(change[i])));
|
||||
@@ -345,10 +381,6 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
for (auto j = s.c.RowOffsets()[i]; j < s.c.RowOffsets()[i + 1]; ++j) {
|
||||
const auto col = s.c.ColumnIndices()[j];
|
||||
const double c = s.c.Values()[j], k = s.k.Values()[j];
|
||||
row_cn += c * candidate.temperature[col];
|
||||
row_co += c * old[col];
|
||||
row_cd += c * change[col];
|
||||
row_kn += k * candidate.temperature[col];
|
||||
scale_cn = Add(
|
||||
scale_cn, Mul(Magnitude(c), Magnitude(candidate.temperature[col])));
|
||||
scale_co = Add(scale_co, Mul(Magnitude(c), Magnitude(old[col])));
|
||||
@@ -421,7 +453,7 @@ Status Check(const ThermalSystem& s, const ThermalPartition& p,
|
||||
|
||||
Result<ThermalStepCandidate> SolveThermalIncrement(
|
||||
const ThermalSystem& s, const ThermalPartition& p, const Vector& old,
|
||||
double delta, LinearSolver& solver, ThermalFactorization& cache) {
|
||||
double delta, LinearSolver& solver, ThermalFactorization& cache) try {
|
||||
auto status = Validate(s, p, old, delta);
|
||||
if (!status.IsOk()) return Result<ThermalStepCandidate>::Failure(status);
|
||||
auto equations = Build(s, p, old, delta);
|
||||
@@ -461,6 +493,12 @@ Result<ThermalStepCandidate> SolveThermalIncrement(
|
||||
status = Check(s, p, old, delta, equations.Value(), candidate);
|
||||
if (!status.IsOk()) return Result<ThermalStepCandidate>::Failure(status);
|
||||
return Result<ThermalStepCandidate>::Success(std::move(candidate));
|
||||
} catch (const std::length_error&) {
|
||||
return Result<ThermalStepCandidate>::Failure(
|
||||
Failure("Thermal sparse action exceeds the backend integer range."));
|
||||
} catch (const std::runtime_error&) {
|
||||
return Result<ThermalStepCandidate>::Failure(
|
||||
Failure("Thermal sparse action failed."));
|
||||
}
|
||||
|
||||
Status RunThermalHistory(const ThermalAssembly& assembly,
|
||||
|
||||
Reference in New Issue
Block a user