feat(nonlinear-static-newton-raphson): step 11 - equilibrium-projection-and-reference-gate

This commit is contained in:
KOKO\Mimi
2026-08-28 09:22:33 +09:00
parent 65b56b1755
commit 6dbf1b2bcf
30 changed files with 13122 additions and 728 deletions
@@ -219,25 +219,25 @@ NewtonChannelDecision EvaluateResidualChannel(
const double numerator = InfinityNorm(input.free_residual, is_finite);
const double internal = InfinityNorm(input.free_internal, is_finite);
const double external = InfinityNorm(input.free_external, is_finite);
const double denominator = (std::max)(internal, external);
if (!is_finite) {
return {input.identity,
numerator,
denominator,
external,
NewtonConvergenceBranch::kZeroScale,
kNewtonZeroScaleThreshold,
unit,
false};
}
if (denominator == 0.0) {
if (external == 0.0) {
return {input.identity,
numerator,
denominator,
external,
NewtonConvergenceBranch::kZeroScale,
kNewtonZeroScaleThreshold,
unit,
numerator <= kNewtonZeroScaleThreshold};
}
const double denominator = (std::max)(internal, external);
return {input.identity,
numerator,
denominator,
+101 -16
View File
@@ -1,5 +1,6 @@
#include "fesa/analysis/nonlinear_static_procedure.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
@@ -501,23 +502,101 @@ Vector CurrentIncrementComponents(const Vector& current, const Vector& start,
return SelectFreeComponents(difference, dofs, rotations);
}
std::array<double, 6> GlobalEquilibrium(const Domain& domain,
const Vector& residual) {
std::array<double, 6> equilibrium{};
Vector3 SpatialMoment(const Matrix& transform,
const std::array<double, 3>& generalized) {
return {transform(0U, 0U) * generalized[0U] +
transform(0U, 1U) * generalized[1U] +
transform(0U, 2U) * generalized[2U],
transform(1U, 0U) * generalized[0U] +
transform(1U, 1U) * generalized[1U] +
transform(1U, 2U) * generalized[2U],
transform(2U, 0U) * generalized[0U] +
transform(2U, 1U) * generalized[1U] +
transform(2U, 2U) * generalized[2U]};
}
/// @brief Computes accepted-state applied-plus-reaction balance evidence.
Result<std::array<double, 6>> GlobalEquilibrium(
const Domain& domain, const DofManager& dofs,
const NonlinearTrialState& trial) {
const std::size_t full_dof_count = domain.Nodes().size() * kDofsPerNode;
if (trial.displacement.Size() != full_dof_count ||
trial.rotation_vector.Size() != full_dof_count ||
trial.external_force.Size() != full_dof_count ||
trial.residual.Size() != full_dof_count) {
return Result<std::array<double, 6>>::Failure(ProcedureFailure(
FailureCategory::kSolver, "nonfinite-newton-data",
{domain.SourcePath(), 0U}, "NONLINEAR_STATIC_PROCEDURE",
domain.SourceContentIdentity(),
"Nonlinear equilibrium vectors do not match the Domain node inventory."));
}
std::vector<bool> constrained(full_dof_count, false);
for (const std::size_t full_dof : dofs.ConstrainedDofs()) {
if (full_dof >= constrained.size()) {
return Result<std::array<double, 6>>::Failure(ProcedureFailure(
FailureCategory::kSolver, "nonfinite-newton-data",
{domain.SourcePath(), 0U}, "NONLINEAR_STATIC_PROCEDURE",
domain.SourceContentIdentity(),
"Nonlinear constraint identity is outside the full DOF range."));
}
constrained[full_dof] = true;
}
Vector3 applied_force_resultant;
Vector3 applied_moment_resultant;
Vector3 reaction_force_resultant;
Vector3 reaction_moment_resultant;
for (std::size_t node = 0U; node < domain.Nodes().size(); ++node) {
const std::size_t first = node * kDofsPerNode;
const Vector3 force{residual[first], residual[first + 1U],
residual[first + 2U]};
const Vector3 position{domain.Nodes()[node].coordinates};
const Vector3 moment = position.Cross(force);
equilibrium[0U] += force.X();
equilibrium[1U] += force.Y();
equilibrium[2U] += force.Z();
equilibrium[3U] += moment.X() + residual[first + 3U];
equilibrium[4U] += moment.Y() + residual[first + 4U];
equilibrium[5U] += moment.Z() + residual[first + 5U];
const Vector3 position = Vector3{domain.Nodes()[node].coordinates} +
Vector3{trial.displacement[first],
trial.displacement[first + 1U],
trial.displacement[first + 2U]};
const Vector3 phi{trial.rotation_vector[first + 3U],
trial.rotation_vector[first + 4U],
trial.rotation_vector[first + 5U]};
const Vector3 applied_force{trial.external_force[first],
trial.external_force[first + 1U],
trial.external_force[first + 2U]};
const Vector3 reaction_force{
constrained[first] ? trial.residual[first] : 0.0,
constrained[first + 1U] ? trial.residual[first + 1U] : 0.0,
constrained[first + 2U] ? trial.residual[first + 2U] : 0.0};
const Matrix transform = InverseTransposeLeftJacobianSO3(phi);
const std::array<double, 3> applied_generalized{
trial.external_force[first + 3U], trial.external_force[first + 4U],
trial.external_force[first + 5U]};
const std::array<double, 3> reaction_generalized{
constrained[first + 3U] ? trial.residual[first + 3U] : 0.0,
constrained[first + 4U] ? trial.residual[first + 4U] : 0.0,
constrained[first + 5U] ? trial.residual[first + 5U] : 0.0};
const Vector3 applied_moment = SpatialMoment(transform, applied_generalized);
const Vector3 reaction_moment =
SpatialMoment(transform, reaction_generalized);
applied_force_resultant = applied_force_resultant + applied_force;
applied_moment_resultant =
applied_moment_resultant + position.Cross(applied_force) + applied_moment;
reaction_force_resultant = reaction_force_resultant + reaction_force;
reaction_moment_resultant = reaction_moment_resultant +
position.Cross(reaction_force) + reaction_moment;
}
return equilibrium;
const Vector3 force_balance =
applied_force_resultant + reaction_force_resultant;
const Vector3 moment_balance =
applied_moment_resultant + reaction_moment_resultant;
const std::array<double, 6> equilibrium{
force_balance.X(), force_balance.Y(), force_balance.Z(),
moment_balance.X(), moment_balance.Y(), moment_balance.Z()};
if (!std::all_of(equilibrium.begin(), equilibrium.end(),
[](const double value) { return std::isfinite(value); })) {
return Result<std::array<double, 6>>::Failure(ProcedureFailure(
FailureCategory::kSolver, "nonfinite-newton-data",
{domain.SourcePath(), 0U}, "NONLINEAR_STATIC_PROCEDURE",
domain.SourceContentIdentity(),
"Nonlinear global equilibrium balance is nonfinite."));
}
return Result<std::array<double, 6>>::Success(equilibrium);
}
Status ApplyIncrementConstraints(const StepDefinition& step,
@@ -689,8 +768,14 @@ NonlinearStaticProcedureExecutor::Execute(
trial.Value().shell_results = std::move(internal.Value().shell_rows);
trial.Value().physical_strain_energy =
internal.Value().physical_strain_energy;
trial.Value().global_equilibrium =
GlobalEquilibrium(domain, trial.Value().residual);
Result<std::array<double, 6>> global_equilibrium =
GlobalEquilibrium(domain, dofs.Value(), trial.Value());
if (!global_equilibrium.HasValue()) {
return Result<NonlinearStaticExecutionResult>::Failure(RecordFailure(
log, &record, global_equilibrium.GetStatus(),
NewtonIterationStage::kExternalAssembled));
}
trial.Value().global_equilibrium = global_equilibrium.Value();
Result<PartitionedStiffness> partition =
constraints.Partition(tangent.Value(), dofs.Value());