#include "fesa/analysis/prepared_analysis.h" #include #include #include "analysis_procedures.h" #include "fesa/analysis/analysis_model.h" #include "fesa/analysis/hht_dynamic_procedure.h" #include "fesa/analysis/linear_frequency_procedure.h" #include "fesa/analysis/linear_static_procedure.h" #include "fesa/analysis/newton_iteration_log.h" #include "fesa/analysis/nonlinear_static_procedure.h" #include "fesa/analysis/solid_frequency_procedure.h" #include "fesa/analysis/solid_hht_dynamic_procedure.h" #include "fesa/analysis/solid_static_procedure.h" #include "fesa/analysis/transient_heat_procedure.h" #include "fesa/app/output_paths.h" #include "fesa/io/abaqus/domain_mapper.h" #include "fesa/io/abaqus/input_reader.h" #include "fesa/results/prepared_results_writer.h" namespace fesa { Result PrepareAnalysisInput( const std::filesystem::path& input_path) { const Result syntax = AbaqusInputReader{}.Read(input_path); if (!syntax.HasValue()) { return Result::Failure(syntax.GetStatus()); } Result mapped = AbaqusDomainMapper{}.Map(syntax.Value()); if (!mapped.HasValue()) { return Result::Failure(mapped.GetStatus()); } PreparedAnalysisInput prepared{std::move(mapped.Value()), {}}; prepared.diagnostics = prepared.domain.Warnings(); SortDiagnostics(prepared.diagnostics); return Result::Success(std::move(prepared)); } namespace { enum class ProcedureKind { kLinearStatic, kNonlinearStatic, kLinearFrequency, kHhtDynamic, kSolidStatic, kSolidFrequency, kSolidHhtDynamic, kTransientHeat }; // Keep procedure precedence and step-count diagnostics common to both APIs. Result SelectProcedure(const Domain& domain) { if (domain.ThermalStep() != nullptr) return Result::Success(ProcedureKind::kTransientHeat); if (domain.Steps().Size() != 1U) { return Result::Failure(Status::Failure( FailureCategory::kInput, {{Severity::kError, "invalid-analysis-step-count", {}, "*STEP", {}, "Prepared analysis requires exactly one analysis step."}})); } if (domain.IsSolid()) { if (domain.Dynamic() != nullptr) return Result::Success(ProcedureKind::kSolidHhtDynamic); if (domain.Steps()[0U].IsFrequency()) return Result::Success(ProcedureKind::kSolidFrequency); return Result::Success(ProcedureKind::kSolidStatic); } if (domain.Dynamic() != nullptr) return Result::Success(ProcedureKind::kHhtDynamic); if (domain.Steps()[0U].IsFrequency()) return Result::Success(ProcedureKind::kLinearFrequency); if (domain.Steps()[0U].IsNonlinearStatic()) return Result::Success(ProcedureKind::kNonlinearStatic); return Result::Success(ProcedureKind::kLinearStatic); } template Result Completed(Result result) { if (!result.HasValue()) return Result::Failure(result.GetStatus()); return Result::Success( PreparedAnalysisOutcome{std::move(result.Value())}); } Result MissingExecutor(const char* code, const char* keyword, const char* message) { return Result::Failure( Status::Failure(FailureCategory::kSolver, {{Severity::kError, code, {}, keyword, {}, message}})); } } // namespace Result LinearStaticProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain)); } Result NonlinearStaticProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain, log_)); } Result LinearFrequencyProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain)); } Result HhtDynamicProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain, log_)); } Result SolidStaticProcedure::Execute(const Domain& domain) { SolidStaticState state; const Status execution = executor_.ExecuteInto(domain, state); for (const auto& record : state.IterationRecords()) { const Status written = log_.Write(record, domain.Steps()[0U].Name()); if (!written.IsOk()) return Result::Failure(written); } if (!execution.IsOk()) return Result::Failure(execution); return Result::Success(PreparedAnalysisOutcome{ SolidStaticExecutionResult{std::move(state), domain.Warnings()}}); } Result SolidFrequencyProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain)); } Result SolidHhtDynamicProcedure::Execute(const Domain& domain) { auto model = AnalysisModel::Create(domain); if (!model.HasValue()) return Result::Failure(model.GetStatus()); auto dofs = DofManager::Create(model.Value()); if (!dofs.HasValue()) return Result::Failure(dofs.GetStatus()); auto state = SolidDynamicAnalysisState::Create(domain, dofs.Value()); if (!state.HasValue()) return Result::Failure(state.GetStatus()); const Status execution = executor_.ExecuteInto(domain, state.Value()); for (const auto& record : state.Value().IterationRecords()) { const Status written = log_.Write(record); if (!written.IsOk()) return Result::Failure(written); } if (!execution.IsOk()) return Result::Failure(execution); return Result::Success( PreparedAnalysisOutcome{SolidDynamicExecutionResult{ std::move(dofs.Value()), std::move(state.Value())}}); } Result TransientHeatProcedure::Execute(const Domain& domain) { return Completed(executor_.Execute(domain)); } Result> CreateAnalysisProcedure( const Domain& domain, const ParallelFor& parallel_for, LinearSolver& linear_solver, TangentSolver& tangent_solver, const EigenSolver& eigen_solver, NewtonIterationLogWriter& log) { const auto kind = SelectProcedure(domain); if (!kind.HasValue()) return Result>::Failure( kind.GetStatus()); std::unique_ptr procedure; switch (kind.Value()) { case ProcedureKind::kLinearStatic: procedure = std::make_unique(parallel_for, linear_solver); break; case ProcedureKind::kNonlinearStatic: procedure = std::make_unique(parallel_for, tangent_solver, log); break; case ProcedureKind::kLinearFrequency: procedure = std::make_unique(eigen_solver); break; case ProcedureKind::kHhtDynamic: procedure = std::make_unique( parallel_for, linear_solver, tangent_solver, log); break; case ProcedureKind::kSolidStatic: procedure = std::make_unique( parallel_for, linear_solver, tangent_solver, log); break; case ProcedureKind::kSolidFrequency: procedure = std::make_unique(eigen_solver); break; case ProcedureKind::kSolidHhtDynamic: procedure = std::make_unique( parallel_for, linear_solver, tangent_solver, log); break; case ProcedureKind::kTransientHeat: procedure = std::make_unique(parallel_for, linear_solver); break; } return Result>::Success( std::move(procedure)); } Result ExecutePreparedAnalysis(const Domain& domain, AnalysisProcedure& procedure) { return procedure.Execute(domain); } Result ExecutePreparedAnalysis( const Domain& domain, LinearStaticProcedureExecutor& linear_executor, NonlinearStaticProcedureExecutor& nonlinear_executor, const LinearFrequencyProcedureExecutor& frequency_executor, NewtonIterationLogWriter& log, const HhtDynamicProcedureExecutor* dynamic_executor, const TransientHeatProcedureExecutor* thermal_executor, const SolidStaticProcedureExecutor* solid_static_executor, const SolidFrequencyProcedureExecutor* solid_frequency_executor, const SolidHhtDynamicProcedureExecutor* solid_dynamic_executor) { const auto kind = SelectProcedure(domain); if (!kind.HasValue()) return Result::Failure(kind.GetStatus()); std::unique_ptr procedure; switch (kind.Value()) { case ProcedureKind::kLinearStatic: procedure = std::make_unique(linear_executor); break; case ProcedureKind::kNonlinearStatic: procedure = std::make_unique(nonlinear_executor, log); break; case ProcedureKind::kLinearFrequency: procedure = std::make_unique(frequency_executor); break; case ProcedureKind::kHhtDynamic: if (dynamic_executor == nullptr) return MissingExecutor("missing-dynamic-executor", "DYNAMIC", "Dynamic execution adapter is required."); procedure = std::make_unique(*dynamic_executor, log); break; case ProcedureKind::kSolidStatic: if (solid_static_executor == nullptr) return MissingExecutor( "missing-solid-executor", "", "The selected Solid execution adapter is required."); procedure = std::make_unique(*solid_static_executor, log); break; case ProcedureKind::kSolidFrequency: if (solid_frequency_executor == nullptr) return MissingExecutor( "missing-solid-executor", "", "The selected Solid execution adapter is required."); procedure = std::make_unique(*solid_frequency_executor); break; case ProcedureKind::kSolidHhtDynamic: if (solid_dynamic_executor == nullptr) return MissingExecutor( "missing-solid-executor", "", "The selected Solid execution adapter is required."); procedure = std::make_unique( *solid_dynamic_executor, log); break; case ProcedureKind::kTransientHeat: if (thermal_executor == nullptr) return MissingExecutor("missing-thermal-executor", "HEAT TRANSFER", "Thermal execution adapter is required."); procedure = std::make_unique(*thermal_executor); break; } return ExecutePreparedAnalysis(domain, *procedure); } Status FinalizePreparedAnalysisResult( const DerivedOutputPaths& paths, const Domain& domain, const PreparedAnalysisOutcome& outcome, const std::vector& diagnostics, ResultsWriter& linear_writer, NonlinearResultsWriter& nonlinear_writer, EigenResultsWriter& eigen_writer, DynamicResultsWriter* dynamic_writer, ThermalResultsWriter* thermal_writer, SolidResultsWriter* solid_writer) { return WritePreparedAnalysisResult( paths.results_path, domain, outcome, diagnostics, linear_writer, nonlinear_writer, eigen_writer, dynamic_writer, thermal_writer, solid_writer); } } // namespace fesa