#ifndef FESA_ANALYSIS_SOLID_STATIC_STATE_H_ #define FESA_ANALYSIS_SOLID_STATIC_STATE_H_ #include #include #include #include #include "fesa/analysis/nonlinear_static_control.h" #include "fesa/results/solid_result_records.h" namespace fesa { /// @brief Keeps raw current-arm balance and its free-residual explanation. /// @note Internal evidence only; no additional persistent result fields. struct SolidStaticBalanceEvidence { std::array internal_force_sum{}; std::array reference_internal_moment{}; std::array current_internal_moment{}; std::array free_residual_moment{}; std::array linearization_moment{}; std::array internal_force_scale{}; std::array reference_moment_scale{}; std::array current_moment_scale{}; }; /// @brief Owns a complete accepted static frame in stable node/source order. /// @note Vectors are node-major UX/UY/UZ; reaction is constrained residual. struct SolidStaticFrame { std::uint32_t frame_index{0U}; std::uint32_t increment_index{0U}; double load_factor{0.0}; std::uint32_t iteration_count{0U}; bool is_factor_one{false}; std::vector nodal_internal_order; std::vector nodal_source_ids; Vector displacement{0U}; Vector internal_force{0U}; Vector external_force{0U}; Vector residual{0U}; Vector reaction{0U}; std::vector solid_results; double physical_strain_energy{0.0}; // Applied plus support forces, then moments about global zero at X+u. std::array global_equilibrium{}; SolidStaticBalanceEvidence balance; }; /// @brief Records two physical static channels, including failed iterations. struct SolidStaticIterationRecord { std::uint32_t increment_index{0U}; double load_factor{0.0}; std::uint32_t iteration{0U}; NewtonChannelDecision force_residual{"residual_force", 0.0, 0.0, NewtonConvergenceBranch::kZeroScale, kNewtonZeroScaleThreshold, NewtonChannelUnit::kForce, false}; NewtonChannelDecision translation_correction{ "correction_translation", 0.0, 0.0, NewtonConvergenceBranch::kZeroScale, kNewtonZeroScaleThreshold, NewtonChannelUnit::kLength, false}; bool convergence_evaluated{false}; bool accepted{false}; std::string diagnostic_code; std::string stage; std::string failure_reason; }; /// @brief Evaluates translation-only static criteria with input force/length. /// @note Exact-zero external force selects the absolute residual branch. Result EvaluateSolidStaticConvergence( const Vector& residual, const Vector& internal, const Vector& external, const Vector& correction, const Vector& increment); /// @brief Owns committed static state independently of caller trial work. class SolidStaticState { public: /// @brief Creates an uninitialized observer usable with ExecuteInto(). SolidStaticState() = default; /// @brief Copies immutable source inventory and the zero-factor baseline. static Result Create(const Domain& domain, const DofManager& dofs); /// @brief Validates complete recovery, then appends and replaces atomically. /// @note The executor certifies trial geometry before this state boundary. Status CommitAcceptedFrame(const SolidStaticFrame& frame); /// @brief Returns authoritative accepted frames; baseline is not a frame. const std::vector& AcceptedFrames() const noexcept; /// @brief Returns baseline or the most recently accepted complete state. const SolidStaticFrame& CommittedState() const noexcept; /// @brief Returns all iteration evidence, including the final failed trial. const std::vector& IterationRecords() const noexcept; private: friend class SolidStaticCore; bool initialized_{false}; bool nonlinear_{false}; std::vector reference_points_; std::vector constrained_dofs_; Vector prescribed_values_{0U}; SolidStaticFrame committed_; std::vector accepted_frames_; std::vector iterations_; }; } // namespace fesa #endif // FESA_ANALYSIS_SOLID_STATIC_STATE_H_