#ifndef FESA_ANALYSIS_HHT_FIELD_CONVERGENCE_H_ #define FESA_ANALYSIS_HHT_FIELD_CONVERGENCE_H_ #include #include #include #include #include #include "fesa/core/status.h" namespace fesa::hht_field_convergence { inline constexpr const char* kPolicyId = "abaqus2025-field-on-fesa-v1"; // Ordered element rows first, then external source entries (including explicit zero). // External entries use internal=value, inertia=0. No assembled cancellation here. struct FluxOccurrence { double internal = 0.0; double inertia = 0.0; }; struct History { double nonzero_sum = 0.0; std::uint64_t nonzero_count = 0; double maximum_sum = 0.0; std::uint64_t maximum_count = 0; }; struct FieldInput { bool active = false; // At least one free full DOF of this field. std::vector flux; double residual = 0.0; // max |I+fint-F| on full free endpoint DOFs, fi=1. double increment = 0.0; // max |q_trial-q_last_accepted| in fixed full u/phi chart. std::optional correction; // max |q_trial-q_previous_selected|, i>=1. std::optional previous_residual; std::optional older_residual; }; enum class Branch { kNotEvaluated, kLinear, kZeroFlux, kNonquadratic, kNormal }; struct FieldResult { std::optional spatial_flux, maximum_flux, time_average_flux, residual_scale; std::optional estimated_correction, residual_threshold, correction_threshold; std::optional residual_pass, correction_pass, field_pass; Branch branch = Branch::kNotEvaluated; }; struct Evaluation { Status status = Status::Failure(FailureCategory::kSolver, {}); bool field_pass = false; std::array fields; // Pure pending snapshot: procedure owns exactly-once commit after every gate/output. std::array pending; }; // Index 0: force/translation; index 1: generalized moment/total rotation phi. Evaluation Evaluate(const std::array& fields, const std::array& committed, std::size_t iteration, double reference_length); } // namespace fesa::hht_field_convergence #endif // FESA_ANALYSIS_HHT_FIELD_CONVERGENCE_H_