feat: add eigen2 frequency pipeline and fail-closed validation
Implement MITC4 mass, constrained whitening, MKL FEAST, modal HDF5, and deterministic Value comparison. MSVC Debug builds; 394 of 395 tests pass. Original-model all-c residual and the documented S3 static contract conflict remain unresolved; this is an implementation checkpoint, not feature acceptance.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
#ifndef FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_
|
||||
#define FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/math/matrix.h"
|
||||
#include "fesa/solvers/eigen/mkl_feast_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<double> 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<std::uint8_t> constraint_mask;
|
||||
std::vector<double> prescribed_displacement;
|
||||
std::vector<EigenNodalReductionRecord> nodal_reduction;
|
||||
std::vector<EigenMode> modes;
|
||||
Matrix vectors{0U, 0U};
|
||||
std::vector<EigenpairCheck> eigenpair_checks;
|
||||
FeastEigenResult backend;
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
#endif // FESA_ANALYSIS_EIGEN_ANALYSIS_STATE_H_
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef FESA_ANALYSIS_LINEAR_FREQUENCY_PROCEDURE_H_
|
||||
#define FESA_ANALYSIS_LINEAR_FREQUENCY_PROCEDURE_H_
|
||||
|
||||
#include "fesa/analysis/eigen_analysis_state.h"
|
||||
|
||||
namespace fesa {
|
||||
|
||||
class Domain;
|
||||
|
||||
/// @brief Executes the supported linear MITC4 frequency procedure.
|
||||
class LinearFrequencyProcedureExecutor final {
|
||||
public:
|
||||
/// @brief Builds, solves, and validates one frequency modal candidate.
|
||||
/// @return Requested physical modes plus all-c validation evidence.
|
||||
Result<EigenAnalysisState> Execute(const Domain& domain) const;
|
||||
};
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
#endif // FESA_ANALYSIS_LINEAR_FREQUENCY_PROCEDURE_H_
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/analysis/analysis_state.h"
|
||||
#include "fesa/analysis/eigen_analysis_state.h"
|
||||
#include "fesa/analysis/nonlinear_analysis_state.h"
|
||||
#include "fesa/core/diagnostic.h"
|
||||
#include "fesa/core/status.h"
|
||||
@@ -14,6 +15,8 @@
|
||||
namespace fesa {
|
||||
|
||||
class LinearStaticProcedureExecutor;
|
||||
class LinearFrequencyProcedureExecutor;
|
||||
class EigenResultsWriter;
|
||||
class NewtonIterationLogWriter;
|
||||
class NonlinearResultsWriter;
|
||||
class NonlinearStaticProcedureExecutor;
|
||||
@@ -47,7 +50,8 @@ struct NonlinearStaticExecutionResult {
|
||||
};
|
||||
|
||||
using PreparedAnalysisOutcome =
|
||||
std::variant<LinearStaticExecutionResult, NonlinearStaticExecutionResult>;
|
||||
std::variant<LinearStaticExecutionResult, NonlinearStaticExecutionResult,
|
||||
EigenAnalysisState>;
|
||||
|
||||
/// @brief Selects the approved procedure from a prepared Domain.
|
||||
/// @pre The executors and log outlive this call.
|
||||
@@ -55,6 +59,7 @@ using PreparedAnalysisOutcome =
|
||||
Result<PreparedAnalysisOutcome> ExecutePreparedAnalysis(
|
||||
const Domain& domain, LinearStaticProcedureExecutor& linear_executor,
|
||||
NonlinearStaticProcedureExecutor& nonlinear_executor,
|
||||
const LinearFrequencyProcedureExecutor& frequency_executor,
|
||||
NewtonIterationLogWriter& log);
|
||||
|
||||
/// @brief Writes an already computed outcome to its authoritative result file.
|
||||
@@ -64,7 +69,7 @@ Status FinalizePreparedAnalysisResult(
|
||||
const DerivedOutputPaths& paths, const Domain& domain,
|
||||
const PreparedAnalysisOutcome& outcome,
|
||||
const std::vector<Diagnostic>& diagnostics, ResultsWriter& linear_writer,
|
||||
NonlinearResultsWriter& nonlinear_writer);
|
||||
NonlinearResultsWriter& nonlinear_writer, EigenResultsWriter& eigen_writer);
|
||||
|
||||
} // namespace fesa
|
||||
|
||||
|
||||
Reference in New Issue
Block a user