feat: implement consistent-mass HHT shell dynamics

This commit is contained in:
KOKO\Mimi
2026-09-22 21:38:42 +09:00
parent fec8cf09c2
commit f0dc42a280
41 changed files with 9294 additions and 125 deletions
@@ -258,6 +258,68 @@ Result<std::string> SerializeRecord(const NewtonIterationLogRecord& record,
return Result<std::string>::Success(std::move(line));
}
/// @brief Serializes dynamic evidence without static load-factor semantics.
Result<std::string> SerializeRecord(
const DynamicNewtonIterationLogRecord& record,
const std::filesystem::path& log_path) {
const auto time = FormatFiniteNumber(record.time, log_path);
if (!time.HasValue()) return Result<std::string>::Failure(time.GetStatus());
if (record.time < 0.0 || record.stage.empty() || record.outcome.empty()) {
return Result<std::string>::Failure(LogOutputFailure(
log_path,
"A dynamic Newton log requires nonnegative time, stage, and outcome."));
}
std::vector<std::string> fields;
fields.reserve(24U);
AppendField(&fields, "record", "dynamic-newton-iteration");
AppendField(&fields, "schema", "hht-log-v1");
AppendField(&fields, "increment", std::to_string(record.increment_index));
AppendField(&fields, "time", time.Value());
AppendField(&fields, "iteration", std::to_string(record.iteration_index));
AppendField(&fields, "stage", EscapeText(record.stage));
const std::array<const char*, 2U> channels{"u", "phi"};
for (std::size_t channel = 0U; channel < channels.size(); ++channel) {
const std::string suffix{channels[channel]};
const std::array<std::pair<std::string, double>, 6U> metrics{{
{"residual_" + suffix + "_numerator",
record.residual_numerator[channel]},
{"residual_" + suffix + "_scale", record.residual_scale[channel]},
{"coordinate_correction_" + suffix,
record.coordinate_correction[channel]},
{"velocity_correction_" + suffix, record.velocity_correction[channel]},
{"acceleration_correction_" + suffix,
record.acceleration_correction[channel]},
{"algorithmic_correction_" + suffix,
record.algorithmic_correction[channel]},
}};
for (const auto& metric : metrics) {
const auto formatted = FormatFiniteNumber(metric.second, log_path);
if (!formatted.HasValue()) {
return Result<std::string>::Failure(formatted.GetStatus());
}
if (metric.second < 0.0) {
return Result<std::string>::Failure(LogOutputFailure(
log_path, "A dynamic Newton log norm or scale is negative."));
}
AppendField(&fields, metric.first, formatted.Value());
}
AppendField(
&fields, "residual_" + suffix + "_branch",
record.residual_scale[channel] == 0.0 ? "zero-scale" : "relative");
}
AppendField(&fields, "residual_pass", record.residual_pass ? "1" : "0");
AppendField(&fields, "correction_pass", record.correction_pass ? "1" : "0");
AppendField(&fields, "outcome", EscapeText(record.outcome));
AppendField(&fields, "failure_code", EscapeText(record.failure_code));
std::string line;
for (std::size_t index = 0U; index < fields.size(); ++index) {
if (index != 0U) line += '\t';
line += fields[index];
}
line += '\n';
return Result<std::string>::Success(std::move(line));
}
} // namespace
Status NewtonIterationLogWriter::Open(const std::filesystem::path& log_path) {
@@ -296,6 +358,27 @@ Status NewtonIterationLogWriter::Write(const NewtonIterationLogRecord& record) {
return Status::Ok();
}
Status NewtonIterationLogWriter::Write(
const DynamicNewtonIterationLogRecord& 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 dynamic Newton log record could not write.");
}
return Status::Ok();
}
Status NewtonIterationLogWriter::Flush() {
if (state_ != State::kOpen) {
state_ = State::kFailed;