feat(nonlinear-static-newton-raphson): step 0 - cli-output-lease-and-derived-names
This commit is contained in:
@@ -5,6 +5,7 @@ add_library(
|
||||
analysis/analysis_state.cpp
|
||||
analysis/linear_static_analysis.cpp
|
||||
app/fesa_application.cpp
|
||||
app/output_paths.cpp
|
||||
assembly/load_assembler.cpp
|
||||
assembly/parallel_for.cpp
|
||||
assembly/sparse_assembler.cpp
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "fesa/analysis/analysis.h"
|
||||
#include "fesa/analysis/linear_static_analysis.h"
|
||||
#include "fesa/app/output_paths.h"
|
||||
#include "fesa/assembly/parallel_for.h"
|
||||
#include "fesa/core/diagnostic.h"
|
||||
#include "fesa/io/hdf5/hdf5_results_writer.h"
|
||||
@@ -23,19 +24,6 @@ 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::kError,
|
||||
"cli-usage",
|
||||
{{}, 0U},
|
||||
"",
|
||||
"",
|
||||
"Usage: fesa.exe <model.inp> [--output <results.h5>]."};
|
||||
}
|
||||
|
||||
const char* SeverityName(const Severity severity) {
|
||||
return severity == Severity::kWarning ? "warning" : "error";
|
||||
}
|
||||
@@ -72,23 +60,23 @@ int ExitCodeFor(const Status& status) {
|
||||
} // namespace
|
||||
|
||||
int FesaApplication::Run(const std::vector<std::string>& arguments) {
|
||||
const bool default_output_form = arguments.size() == 1U &&
|
||||
!arguments[0U].empty() &&
|
||||
!StartsWithOption(arguments[0U]);
|
||||
const bool explicit_output_form =
|
||||
arguments.size() == 3U && !arguments[0U].empty() &&
|
||||
!StartsWithOption(arguments[0U]) && arguments[1U] == "--output" &&
|
||||
!arguments[2U].empty() && !StartsWithOption(arguments[2U]);
|
||||
if (!default_output_form && !explicit_output_form) {
|
||||
WriteDiagnostics({UsageDiagnostic()});
|
||||
const auto paths = DeriveSingleInputOutputPaths(
|
||||
arguments, std::filesystem::current_path());
|
||||
if (!paths.HasValue()) {
|
||||
WriteDiagnostics(paths.GetStatus().Diagnostics());
|
||||
return kUsageExitCode;
|
||||
}
|
||||
|
||||
auto lease = AcquireOutputWriteLease(paths.Value());
|
||||
if (!lease.HasValue()) {
|
||||
WriteDiagnostics(lease.GetStatus().Diagnostics());
|
||||
return kOutputExitCode;
|
||||
}
|
||||
|
||||
AnalysisRequest request;
|
||||
request.input_path = arguments[0U];
|
||||
request.output_path = explicit_output_form
|
||||
? std::filesystem::path{arguments[2U]}
|
||||
: std::filesystem::current_path() / "results.h5";
|
||||
request.input_path = paths.Value().input_path;
|
||||
request.output_path = paths.Value().results_path;
|
||||
request.log_path = paths.Value().log_path;
|
||||
|
||||
TbbParallelFor parallel_for;
|
||||
MklPardisoSolver linear_solver;
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "fesa/app/output_paths.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "fesa/core/ascii.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
Status UsageFailure(const std::string& identity, const std::string& message) {
|
||||
return Status::Failure(
|
||||
FailureCategory::kInput,
|
||||
{{Severity::kError, "cli-usage", {{}, 0U}, "", identity, message}});
|
||||
}
|
||||
|
||||
Status OutputBusyFailure(const std::filesystem::path& results_path) {
|
||||
return Status::Failure(
|
||||
FailureCategory::kOutput,
|
||||
{{Severity::kError,
|
||||
"output-busy",
|
||||
{{}, 0U},
|
||||
"",
|
||||
results_path.generic_u8string(),
|
||||
"The derived output pair is already owned by another invocation."}});
|
||||
}
|
||||
|
||||
bool StartsWithOption(const std::string& argument) {
|
||||
return !argument.empty() && argument.front() == '-';
|
||||
}
|
||||
|
||||
std::string ArgumentIdentity(const std::size_t argument_index,
|
||||
const std::string& argument) {
|
||||
return "argument_index=" + std::to_string(argument_index) +
|
||||
";argument=" + argument;
|
||||
}
|
||||
|
||||
std::filesystem::path LockPathFor(const DerivedOutputPaths& paths) {
|
||||
std::filesystem::path lock_path = paths.results_path;
|
||||
lock_path += ".fesa.lock";
|
||||
return lock_path;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class OutputWriteLease::Impl {
|
||||
public:
|
||||
explicit Impl(const HANDLE handle) noexcept : handle_{handle} {}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
~Impl() {
|
||||
if (handle_ != INVALID_HANDLE_VALUE) {
|
||||
(void)CloseHandle(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE handle_{INVALID_HANDLE_VALUE};
|
||||
};
|
||||
|
||||
OutputWriteLease::OutputWriteLease(std::unique_ptr<Impl> impl) noexcept
|
||||
: impl_{std::move(impl)} {}
|
||||
|
||||
OutputWriteLease::OutputWriteLease(OutputWriteLease&&) noexcept = default;
|
||||
|
||||
OutputWriteLease& OutputWriteLease::operator=(OutputWriteLease&&) noexcept =
|
||||
default;
|
||||
|
||||
OutputWriteLease::~OutputWriteLease() = default;
|
||||
|
||||
Result<DerivedOutputPaths> DeriveSingleInputOutputPaths(
|
||||
const std::vector<std::string>& arguments,
|
||||
const std::filesystem::path& invocation_cwd) {
|
||||
for (std::size_t index = 0U; index < arguments.size(); ++index) {
|
||||
if (arguments[index].find("--output") != std::string::npos) {
|
||||
return Result<DerivedOutputPaths>::Failure(UsageFailure(
|
||||
ArgumentIdentity(index, arguments[index]),
|
||||
"The --output option is not supported."));
|
||||
}
|
||||
}
|
||||
|
||||
if (arguments.size() != 1U || arguments.front().empty() ||
|
||||
StartsWithOption(arguments.front())) {
|
||||
return Result<DerivedOutputPaths>::Failure(
|
||||
UsageFailure("", "Usage: fesa.exe <model.inp>."));
|
||||
}
|
||||
|
||||
std::filesystem::path input_path{arguments.front()};
|
||||
if (input_path.is_relative()) {
|
||||
input_path = invocation_cwd / input_path;
|
||||
}
|
||||
input_path = input_path.lexically_normal();
|
||||
if (!AsciiCaseInsensitiveEquals(input_path.extension().string(), ".inp")) {
|
||||
return Result<DerivedOutputPaths>::Failure(
|
||||
UsageFailure(input_path.generic_u8string(),
|
||||
"The input path must end in .inp."));
|
||||
}
|
||||
|
||||
const std::filesystem::path stem = input_path.stem();
|
||||
const std::filesystem::path output_directory = input_path.parent_path();
|
||||
return Result<DerivedOutputPaths>::Success(
|
||||
{input_path, output_directory / (stem.string() + "_results.h5"),
|
||||
output_directory / (stem.string() + "_log.txt")});
|
||||
}
|
||||
|
||||
Result<OutputWriteLease> AcquireOutputWriteLease(
|
||||
const DerivedOutputPaths& paths) {
|
||||
const std::filesystem::path lock_path = LockPathFor(paths);
|
||||
const HANDLE lock_handle = CreateFileW(
|
||||
lock_path.c_str(), GENERIC_READ | GENERIC_WRITE, 0U, nullptr, CREATE_NEW,
|
||||
FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_DELETE_ON_CLOSE, nullptr);
|
||||
if (lock_handle == INVALID_HANDLE_VALUE) {
|
||||
return Result<OutputWriteLease>::Failure(
|
||||
OutputBusyFailure(paths.results_path));
|
||||
}
|
||||
|
||||
return Result<OutputWriteLease>::Success(
|
||||
OutputWriteLease{std::make_unique<OutputWriteLease::Impl>(lock_handle)});
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
Reference in New Issue
Block a user