fix(math): preserve certified sums and complete MKL algebra

This commit is contained in:
KOKO\Mimi
2026-10-14 15:40:52 +09:00
parent 59c8e93b07
commit 590a93793f
9 changed files with 236 additions and 95 deletions
+25 -30
View File
@@ -8,6 +8,7 @@
#include <utility>
#include "thermal_transient_bounds.h"
#include "math/dense_blas_internal.h"
namespace fesa::thermal_transient_internal {
namespace {
@@ -117,14 +118,9 @@ 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];
}
const auto entries = s.c.Values().size();
Vector capacity_diagonal{entries}, conductivity_diagonal{entries};
Vector capacity_input{entries}, conductivity_input{entries};
Vector b{n};
std::vector<I> exact_a, exact_b(n);
for (std::size_t f = 0; f < n; ++f) {
@@ -135,43 +131,39 @@ 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});
capacity_diagonal[j] = c / delta;
if (free[col] != n) {
capacity_input[j] = old[col];
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}));
exact_b[f] = Add(exact_b[f], Mul(cd, I{old[col]}));
} else {
const double g = p.prescribed[constrained[col]];
capacity_input[j] = old[col] - g;
conductivity_diagonal[j] = k;
conductivity_input[j] = g;
// Preserve raw old constrained capacity; never project frame zero.
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];
const auto capacity_action = dense_blas_internal::MultiplyDiagonal(
capacity_diagonal, capacity_input);
const auto conductivity_action = dense_blas_internal::MultiplyDiagonal(
conductivity_diagonal, conductivity_input);
for (std::size_t f = 0; f < n; ++f) {
const auto row = p.free[f];
for (auto j = s.c.RowOffsets()[row]; j < s.c.RowOffsets()[row + 1]; ++j) {
const auto col = s.c.ColumnIndices()[j];
// Preserve the original per-entry subtraction before the row sum.
// Separately summing C*delta and K*g loses small stable RHS terms.
b[f] += free[col] != n ? capacity_action[j]
: capacity_action[j] - conductivity_action[j];
}
}
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))
@@ -493,6 +485,9 @@ 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::invalid_argument&) {
return Result<ThermalStepCandidate>::Failure(
Failure("Nonfinite backward Euler RHS."));
} catch (const std::length_error&) {
return Result<ThermalStepCandidate>::Failure(
Failure("Thermal sparse action exceeds the backend integer range."));