feat(linear-static-3d-euler-beam): step 24 - linear-static-cli

This commit is contained in:
KOKO\Mimi
2026-08-09 22:54:59 +09:00
parent 286424bfde
commit a9d93bb206
14 changed files with 3919 additions and 31 deletions
+112
View File
@@ -0,0 +1,112 @@
#include "fesa/app/fesa_application.hpp"
#include "fesa/analysis/linear_static_analysis.hpp"
#include "fesa/assembly/parallel_for.hpp"
#include "fesa/core/diagnostic.hpp"
#include "fesa/io/hdf5/hdf5_results_writer.hpp"
#include "fesa/solvers/linear/mkl_pardiso_solver.hpp"
#include <filesystem>
#include <iostream>
#include <string>
#include <vector>
namespace fesa {
namespace {
constexpr int kSuccessExitCode = 0;
constexpr int kUsageExitCode = 2;
constexpr int kInputExitCode = 3;
constexpr int kModelExitCode = 4;
constexpr int kSolverExitCode = 5;
constexpr int kOutputExitCode = 6;
bool startsWithOption(const std::string& argument) {
return !argument.empty() && argument.front() == '-';
}
Diagnostic usageDiagnostic() {
return {
Severity::error,
"cli-usage",
{{}, 0U},
"",
"",
"Usage: fesa.exe <model.inp> [--output <results.h5>]."};
}
const char* severityName(const Severity severity) {
return severity == Severity::warning ? "warning" : "error";
}
void writeDiagnostics(std::vector<Diagnostic> diagnostics) {
sortDiagnostics(diagnostics);
for (const auto& diagnostic : diagnostics) {
// Stable field labels and tab separators keep empty source fields
// explicit without depending on locale-specific formatting.
std::cerr
<< "severity=" << severityName(diagnostic.severity)
<< '\t' << "code=" << diagnostic.code
<< '\t' << "file="
<< diagnostic.location.file.generic_u8string()
<< '\t' << "line=" << diagnostic.location.line
<< '\t' << "keyword=" << diagnostic.keyword
<< '\t' << "entity_identity=" << diagnostic.entityIdentity
<< '\t' << "message=" << diagnostic.message
<< '\n';
}
}
int exitCodeFor(const Status& status) {
switch (status.failureCategory().value_or(FailureCategory::input)) {
case FailureCategory::input:
return kInputExitCode;
case FailureCategory::model:
return kModelExitCode;
case FailureCategory::solver:
return kSolverExitCode;
case FailureCategory::output:
return kOutputExitCode;
}
return kInputExitCode;
}
} // namespace
int FesaApplication::run(const std::vector<std::string>& arguments) {
const bool defaultOutputForm =
arguments.size() == 1U &&
!arguments[0U].empty() &&
!startsWithOption(arguments[0U]);
const bool explicitOutputForm =
arguments.size() == 3U &&
!arguments[0U].empty() &&
!startsWithOption(arguments[0U]) &&
arguments[1U] == "--output" &&
!arguments[2U].empty();
if (!defaultOutputForm && !explicitOutputForm) {
writeDiagnostics({usageDiagnostic()});
return kUsageExitCode;
}
AnalysisRequest request;
request.inputPath = arguments[0U];
request.outputPath = explicitOutputForm
? std::filesystem::path{arguments[2U]}
: std::filesystem::current_path() / "results.h5";
TbbParallelFor parallelFor;
MklPardisoSolver linearSolver;
Hdf5ResultsWriter resultsWriter;
LinearStaticAnalysis analysis{
parallelFor, linearSolver, resultsWriter};
const Status status = analysis.run(request);
if (status.isOk()) {
return kSuccessExitCode;
}
writeDiagnostics(status.diagnostics());
return exitCodeFor(status);
}
} // namespace fesa
+17
View File
@@ -0,0 +1,17 @@
#include "fesa/app/fesa_application.hpp"
#include <cstddef>
#include <string>
#include <vector>
int main(const int argc, char* argv[]) {
std::vector<std::string> arguments;
if (argc > 1) {
arguments.reserve(static_cast<std::size_t>(argc - 1));
}
// The application boundary receives only operands and options, not argv[0].
for (int index = 1; index < argc; ++index) {
arguments.emplace_back(argv[index]);
}
return fesa::FesaApplication{}.run(arguments);
}