Files
FESADev/src/fesa/analysis/solid_hht_force_identity.h
T

183 lines
8.3 KiB
C++

#ifndef FESA_ANALYSIS_SOLID_HHT_FORCE_IDENTITY_H_
#define FESA_ANALYSIS_SOLID_HHT_FORCE_IDENTITY_H_
#include <cmath>
#include <initializer_list>
#include <limits>
#include "fesa/analysis/solid_dynamic_analysis_state.h"
#include "fesa/math/sparse_matrix.h"
#include "solid_hht_high_low.h"
namespace fesa::detail {
struct SolidHhtForceIdentity {
double original{0};
double solved{0};
double scale{0};
double defect{0};
double metric{0};
};
struct SolidHhtPairedForceIdentity {
math_internal::HighLow original;
math_internal::HighLow solved;
math_internal::HighLow scale;
math_internal::HighLow defect;
math_internal::HighLow metric;
};
/// @brief Keeps both physical parts and the actual old residual in the approved
/// weighted identity. Its scale includes each uncancelled mass product.
inline bool EvaluateSolidHhtHighLowForceIdentity(
const SparseMatrix& mass, std::size_t row,
math_internal::HighLow algorithmic_inertia,
const SolidDynamicAcceptedFrameState& next,
const SolidDynamicAcceptedFrameState& old, double alpha,
SolidHhtPairedForceIdentity* result) {
using namespace math_internal;
if (!ArithmeticEnvironmentSupported() || row >= mass.Rows() ||
!IsFinite(algorithmic_inertia) || !IsNormalized(algorithmic_inertia) ||
!IsFinite(HighLow(alpha)))
return false;
for (const auto* frame : {&next, &old})
if (frame->acceleration.Size() != mass.Columns() ||
frame->low.acceleration.Size() != mass.Columns() ||
frame->algorithmic_acceleration.Size() != mass.Columns() ||
frame->low.algorithmic_acceleration.Size() != mass.Columns() ||
frame->internal_force.Size() != mass.Rows() ||
frame->low.internal_force.Size() != mass.Rows() ||
frame->external_force.Size() != mass.Rows() ||
frame->low.external_force.Size() != mass.Rows() ||
frame->residual.Size() != mass.Rows() ||
frame->low.residual.Size() != mass.Rows())
return false;
const HighLow a(alpha);
const auto c = Add(HighLow(1), a);
const HighLow f(next.internal_force[row], next.low.internal_force[row]);
const HighLow F(next.external_force[row], next.low.external_force[row]);
const HighLow old_f(old.internal_force[row], old.low.internal_force[row]);
const HighLow old_F(old.external_force[row], old.low.external_force[row]);
const HighLow r(next.residual[row], next.low.residual[row]);
const HighLow old_r(old.residual[row], old.low.residual[row]);
result->original =
Subtract(Add(algorithmic_inertia, Multiply(c, Subtract(f, F))),
Multiply(a, Subtract(old_f, old_F)));
result->solved = Subtract(Multiply(c, r), Multiply(a, old_r));
HighLow mass_scale;
HighLow relation_action;
for (std::size_t k = mass.RowOffsets()[row]; k < mass.RowOffsets()[row + 1];
++k) {
const auto j = mass.ColumnIndices()[k];
const HighLow coefficient(mass.Values()[k]);
const HighLow A(next.algorithmic_acceleration[j],
next.low.algorithmic_acceleration[j]);
const HighLow physical_a(next.acceleration[j], next.low.acceleration[j]);
const HighLow old_a(old.acceleration[j], old.low.acceleration[j]);
const auto relation =
Add(Subtract(A, Multiply(c, physical_a)), Multiply(a, old_a));
mass_scale =
Add(mass_scale,
Add(Add(Abs(Multiply(coefficient, A)),
Multiply(c, Abs(Multiply(coefficient, physical_a)))),
Multiply(Abs(a), Abs(Multiply(coefficient, old_a)))));
relation_action =
Add(relation_action, Abs(Multiply(coefficient, relation)));
if (!IsFinite(mass_scale) || !IsFinite(relation_action)) return false;
}
result->scale = Add(
Add(Add(mass_scale, relation_action), Multiply(c, Add(Abs(f), Abs(F)))),
Add(Add(Multiply(Abs(a), Add(Abs(old_f), Abs(old_F))),
Multiply(c, Abs(r))),
Abs(Multiply(a, old_r))));
result->defect = Abs(Subtract(result->original, result->solved));
if (!IsFinite(result->original) || !IsFinite(result->solved) ||
!IsFinite(result->scale) || !IsFinite(result->defect))
return false;
if (Compare(result->scale, HighLow(0)) == 0) {
if (Compare(result->defect, HighLow(0)) != 0) return false;
result->metric = HighLow(0);
} else {
result->metric = Divide(result->defect, result->scale);
}
return IsFinite(result->metric) && IsNormalized(result->metric);
}
/// @brief Evaluates one original/equivalent HHT row, retaining old equilibrium
/// error. Checks each scalar before it can enter an evidence maximum.
inline bool EvaluateSolidHhtForceIdentity(
const SparseMatrix& mass, std::size_t row, double algorithmic_inertia,
const SolidDynamicAcceptedFrameState& next,
const SolidDynamicAcceptedFrameState& old, double alpha,
SolidHhtForceIdentity* result) {
for (double x :
{alpha, algorithmic_inertia, next.internal_force[row],
next.external_force[row], old.internal_force[row],
old.external_force[row], next.residual[row], old.residual[row]})
if (!std::isfinite(x)) return false;
result->original =
algorithmic_inertia +
(1 + alpha) * (next.internal_force[row] - next.external_force[row]) -
alpha * (old.internal_force[row] - old.external_force[row]);
result->solved = (1 + alpha) * next.residual[row] - alpha * old.residual[row];
double mass_action_scale = 0;
double relation_defect_action = 0;
for (std::size_t entry = mass.RowOffsets()[row];
entry < mass.RowOffsets()[row + 1]; ++entry) {
const auto column = mass.ColumnIndices()[entry];
const double coefficient = mass.Values()[entry];
const double a = next.acceleration[column];
const double A = next.algorithmic_acceleration[column];
const double a_old = old.acceleration[column];
const double relation_defect = A - (1 + alpha) * a + alpha * a_old;
const double action_scale = std::abs(coefficient * A) +
(1 + alpha) * std::abs(coefficient * a) +
std::abs(alpha) * std::abs(coefficient * a_old);
const double defect_action = std::abs(coefficient * relation_defect);
for (double x : {a, A, a_old, relation_defect, action_scale, defect_action})
if (!std::isfinite(x)) return false;
mass_action_scale += action_scale;
relation_defect_action += defect_action;
}
result->scale = mass_action_scale + relation_defect_action +
(1 + alpha) * (std::abs(next.internal_force[row]) +
std::abs(next.external_force[row])) +
std::abs(alpha) * (std::abs(old.internal_force[row]) +
std::abs(old.external_force[row])) +
(1 + alpha) * std::abs(next.residual[row]) +
std::abs(alpha * old.residual[row]);
result->defect = std::abs(result->original - result->solved);
for (double x : {result->original, result->solved, mass_action_scale,
relation_defect_action, result->scale, result->defect})
if (!std::isfinite(x)) return false;
result->metric =
result->scale == 0
? (result->defect == 0 ? 0 : std::numeric_limits<double>::infinity())
: result->defect / result->scale;
return std::isfinite(result->metric);
}
/// @brief Finite checked failure-shadow accumulation, separate from physics.
inline bool AccumulateSolidHhtForceShadow(double term, double product_error,
double* sum, double* correction,
double* absolute_sum) {
if (!std::isfinite(term) || !std::isfinite(product_error) ||
!std::isfinite(*sum) || !std::isfinite(*correction) ||
!std::isfinite(*absolute_sum))
return false;
const double next = *sum + term;
const double absolute_next = *absolute_sum + std::abs(term);
const double error_next = *correction + product_error;
const double lost = std::abs(*sum) >= std::abs(term) ? (*sum - next) + term
: (term - next) + *sum;
const double correction_next = error_next + lost;
if (!std::isfinite(next) || !std::isfinite(absolute_next) ||
!std::isfinite(error_next) || !std::isfinite(lost) ||
!std::isfinite(correction_next))
return false;
*sum = next;
*correction = correction_next;
*absolute_sum = absolute_next;
return true;
}
} // namespace fesa::detail
#endif // FESA_ANALYSIS_SOLID_HHT_FORCE_IDENTITY_H_