feat(hex8): connect Solid CLI and atomic HDF5 output
This commit is contained in:
@@ -9,6 +9,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "fesa/analysis/solid_dynamic_analysis_state.h"
|
||||
#include "fesa/analysis/solid_static_state.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
@@ -320,6 +323,119 @@ Result<std::string> SerializeRecord(
|
||||
return Result<std::string>::Success(std::move(line));
|
||||
}
|
||||
|
||||
Result<std::string> JoinSolidFields(const std::vector<std::string>& fields) {
|
||||
std::string line;
|
||||
for (const auto& field : fields) {
|
||||
if (!line.empty()) line += '\t';
|
||||
line += field;
|
||||
}
|
||||
line += '\n';
|
||||
return Result<std::string>::Success(std::move(line));
|
||||
}
|
||||
Result<std::string> SerializeRecord(const SolidStaticIterationRecord& record,
|
||||
const std::string& step,
|
||||
const std::filesystem::path& path) {
|
||||
std::vector<std::string> fields;
|
||||
AppendField(&fields, "schema", "solid-static-log-v1");
|
||||
AppendField(&fields, "step", EscapeText(step));
|
||||
AppendField(&fields, "increment", std::to_string(record.increment_index));
|
||||
AppendField(&fields, "iteration", std::to_string(record.iteration));
|
||||
const auto factor = FormatFiniteNumber(record.load_factor, path);
|
||||
if (!factor.HasValue())
|
||||
return Result<std::string>::Failure(factor.GetStatus());
|
||||
AppendField(&fields, "load_factor", factor.Value());
|
||||
AppendField(&fields, "stage", EscapeText(record.stage));
|
||||
for (const auto& entry :
|
||||
std::array<std::pair<ChannelSpec, const NewtonChannelDecision*>, 2>{
|
||||
{{{"residual_force", "residual_force"}, &record.force_residual},
|
||||
{{"correction_translation", "correction_translation"},
|
||||
&record.translation_correction}}}) {
|
||||
const Status status = AppendChannelFields(
|
||||
&fields, entry.first, *entry.second, record.convergence_evaluated,
|
||||
"not-evaluated(" + EscapeText(record.failure_reason) + ")", path);
|
||||
if (!status.IsOk()) return Result<std::string>::Failure(status);
|
||||
}
|
||||
AppendField(&fields, "accepted", record.accepted ? "1" : "0");
|
||||
AppendField(&fields, "diagnostic_code", EscapeText(record.diagnostic_code));
|
||||
AppendField(&fields, "failure_reason", EscapeText(record.failure_reason));
|
||||
return JoinSolidFields(fields);
|
||||
}
|
||||
Result<std::string> SerializeRecord(const SolidDynamicIterationRecord& record,
|
||||
const std::filesystem::path& path) {
|
||||
std::vector<std::string> fields;
|
||||
AppendField(&fields, "schema", "solid-hht-log-v1");
|
||||
AppendField(&fields, "arithmetic",
|
||||
"normalized-high-low-physical-state;RN64-observables");
|
||||
AppendField(&fields, "increment", std::to_string(record.increment_index));
|
||||
AppendField(&fields, "iteration",
|
||||
std::to_string(record.evidence.iteration_index));
|
||||
AppendField(&fields, "stage", EscapeText(record.stage));
|
||||
AppendField(&fields, "outcome", EscapeText(record.outcome));
|
||||
AppendField(&fields, "failure_code", EscapeText(record.failure_code));
|
||||
const auto& e = record.evidence;
|
||||
for (const double metric :
|
||||
{e.residual_numerator, e.residual_scale, e.residual_metric,
|
||||
e.weighted_residual_numerator, e.recombined_residual_numerator,
|
||||
e.old_residual_numerator, e.weighted_equivalence_metric,
|
||||
e.update_metric}) {
|
||||
if (!std::isfinite(metric) || metric < 0)
|
||||
return Result<std::string>::Failure(LogOutputFailure(
|
||||
path, "A Solid HHT norm, scale or metric is invalid."));
|
||||
}
|
||||
for (const double metric : e.correction_metrics) {
|
||||
if (!std::isfinite(metric) || metric < 0)
|
||||
return Result<std::string>::Failure(
|
||||
LogOutputFailure(path, "A Solid HHT correction metric is invalid."));
|
||||
}
|
||||
std::vector<std::pair<std::string, double>> numbers{
|
||||
{"time", record.time},
|
||||
{"physical_residual_force_norm", e.residual_numerator},
|
||||
{"uncancelled_force_scale", e.residual_scale},
|
||||
{"physical_residual_max_ratio", e.residual_metric},
|
||||
{"original_weighted_force_norm", e.weighted_residual_numerator},
|
||||
{"solved_weighted_force_norm", e.recombined_residual_numerator},
|
||||
{"old_physical_force_norm", e.old_residual_numerator},
|
||||
{"weighted_equivalence_metric", e.weighted_equivalence_metric},
|
||||
{"update_metric", e.update_metric}};
|
||||
const std::array<const char*, 4> corrections{{"du_over_L", "h_dv_over_L",
|
||||
"h2_physical_da_over_L",
|
||||
"h2_algorithmic_dA_over_L"}};
|
||||
for (std::size_t i = 0; i < 4; ++i)
|
||||
numbers.emplace_back(corrections[i], e.correction_metrics[i]);
|
||||
const std::array<const char*, 5> terms{
|
||||
{"worst_r_force", "worst_Ma_force", "worst_internal_force",
|
||||
"worst_external_force", "worst_uncancelled_force_scale"}};
|
||||
for (std::size_t i = 0; i < 5; ++i)
|
||||
numbers.emplace_back(terms[i], e.worst_force_terms[i]);
|
||||
for (const auto& number : numbers) {
|
||||
const auto formatted = FormatFiniteNumber(number.second, path);
|
||||
if (!formatted.HasValue())
|
||||
return Result<std::string>::Failure(formatted.GetStatus());
|
||||
AppendField(&fields, number.first, formatted.Value());
|
||||
}
|
||||
const std::array<const char*, 4> weighted{
|
||||
{"worst_original_weighted_force", "worst_solved_weighted_force",
|
||||
"worst_old_physical_force", "worst_weighted_uncancelled_force_scale"}};
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
const auto formatted = FormatFiniteNumber(e.worst_weighted_terms[i], path);
|
||||
if (!formatted.HasValue())
|
||||
return Result<std::string>::Failure(formatted.GetStatus());
|
||||
AppendField(&fields, weighted[i], formatted.Value());
|
||||
}
|
||||
AppendField(&fields, "physical_residual_threshold", "1e-10");
|
||||
AppendField(&fields, "correction_threshold", "1e-10");
|
||||
AppendField(&fields, "update_threshold", "1e-12");
|
||||
AppendField(&fields, "weighted_equivalence_threshold", "1e-12");
|
||||
AppendField(&fields, "residual_unit", "force");
|
||||
AppendField(&fields, "correction_unit", "dimensionless");
|
||||
AppendField(&fields, "worst_full_dof", std::to_string(e.worst_full_dof));
|
||||
AppendField(&fields, "zero_scale_exact", e.zero_scale_exact ? "1" : "0");
|
||||
AppendField(&fields, "physical_residual_pass", e.residual_pass ? "1" : "0");
|
||||
AppendField(&fields, "correction_pass", e.correction_pass ? "1" : "0");
|
||||
AppendField(&fields, "update_pass", e.update_pass ? "1" : "0");
|
||||
return JoinSolidFields(fields);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status NewtonIterationLogWriter::Open(const std::filesystem::path& log_path) {
|
||||
@@ -379,6 +495,48 @@ Status NewtonIterationLogWriter::Write(
|
||||
return Status::Ok();
|
||||
}
|
||||
|
||||
Status NewtonIterationLogWriter::Write(const SolidStaticIterationRecord& record,
|
||||
const std::string& step_name) {
|
||||
if (state_ != State::kOpen) {
|
||||
state_ = State::kFailed;
|
||||
return LogOutputFailure(log_path_, "The Newton log writer is not open.");
|
||||
}
|
||||
const auto line = SerializeRecord(record, step_name, log_path_);
|
||||
if (!line.HasValue()) {
|
||||
state_ = State::kFailed;
|
||||
return line.GetStatus();
|
||||
}
|
||||
stream_.write(line.Value().data(),
|
||||
static_cast<std::streamsize>(line.Value().size()));
|
||||
if (!stream_) {
|
||||
state_ = State::kFailed;
|
||||
return LogOutputFailure(log_path_,
|
||||
"The Solid Newton log record could not write.");
|
||||
}
|
||||
return Status::Ok();
|
||||
}
|
||||
|
||||
Status NewtonIterationLogWriter::Write(
|
||||
const SolidDynamicIterationRecord& record) {
|
||||
if (state_ != State::kOpen) {
|
||||
state_ = State::kFailed;
|
||||
return LogOutputFailure(log_path_, "The Newton log writer is not open.");
|
||||
}
|
||||
const auto line = SerializeRecord(record, log_path_);
|
||||
if (!line.HasValue()) {
|
||||
state_ = State::kFailed;
|
||||
return line.GetStatus();
|
||||
}
|
||||
stream_.write(line.Value().data(),
|
||||
static_cast<std::streamsize>(line.Value().size()));
|
||||
if (!stream_) {
|
||||
state_ = State::kFailed;
|
||||
return LogOutputFailure(log_path_,
|
||||
"The Solid Newton log record could not write.");
|
||||
}
|
||||
return Status::Ok();
|
||||
}
|
||||
|
||||
Status NewtonIterationLogWriter::Flush() {
|
||||
if (state_ != State::kOpen) {
|
||||
state_ = State::kFailed;
|
||||
|
||||
Reference in New Issue
Block a user