#ifndef FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_ #define FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_ #include #include #include #include #include #include "fesa/math/matrix.h" #include "fesa/solvers/eigen/eigen_solver.h" namespace fesa { /// @brief Identifies one requested mode without conflating equal eigenvalues. struct EigenMode { std::size_t mode_id; double eigenvalue; std::uint8_t classification; std::size_t cluster_id; std::optional frequency; }; /// @brief Records the analytic mass split at one source-ordered node. struct EigenNodalReductionRecord { std::uint8_t free_count; std::uint8_t null_count; std::uint8_t positive_count; bool rcond_available; double rcond_inf; }; /// @brief Stores independently evaluated evidence for every computed pair. struct EigenpairCheck { std::size_t sorted_rank; std::size_t solver_column; double eigenvalue; double modal_mass; double rayleigh_quotient; double eta_r; double eta_w; double rho_b; double eta_norm; double eta_energy; bool backend_res_available; double backend_res; }; /// @brief Owns a validated modal candidate independently of static state. /// @note Vectors are requested-mode by full source DOF; they are not response /// displacements. Backend work vectors are released after all-c validation. struct EigenAnalysisState { explicit EigenAnalysisState(FeastEigenResult backend_result) : backend{std::move(backend_result)} {} std::size_t full_dof_count{0U}; std::size_t free_dof_count{0U}; std::size_t mass_null_dimension{0U}; std::size_t finite_dimension{0U}; double characteristic_length{0.0}; double eta_gram{0.0}; double max_eta_orth{0.0}; bool null_checks_applicable{false}; double eta_mz{0.0}; double eta_kz{0.0}; double eta_pz{0.0}; std::vector constraint_mask; std::vector prescribed_displacement; std::vector nodal_reduction; std::vector modes; Matrix vectors{0U, 0U}; std::vector eigenpair_checks; FeastEigenResult backend; }; } // namespace fesa #endif // FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_