feat(hex8): connect Solid CLI and atomic HDF5 output
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
#include "io/hdf5/solid_hdf5_self_check.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
#include "io/hdf5/hdf5_primitives.h"
|
||||
|
||||
namespace fesa::hdf5_internal {
|
||||
namespace {
|
||||
void Check(bool condition, const char* message) {
|
||||
if (!condition) throw Hdf5Failure(message);
|
||||
}
|
||||
Hdf5Handle Dataset(hid_t file, const std::string& path,
|
||||
const std::vector<hsize_t>& dimensions) {
|
||||
Hdf5Handle dataset{RequireHdf5Id(H5Dopen2(file, path.c_str(), H5P_DEFAULT),
|
||||
"Solid required dataset is missing."),
|
||||
H5Dclose};
|
||||
Hdf5Handle space{
|
||||
RequireHdf5Id(H5Dget_space(dataset.Get()), "Solid dataset space failed."),
|
||||
H5Sclose};
|
||||
Check(H5Sget_simple_extent_ndims(space.Get()) ==
|
||||
static_cast<int>(dimensions.size()),
|
||||
"Solid dataset rank mismatch.");
|
||||
std::vector<hsize_t> actual(dimensions.size());
|
||||
RequireHdf5(H5Sget_simple_extent_dims(space.Get(), actual.data(), nullptr),
|
||||
"Solid dimensions read failed.");
|
||||
Check(actual == dimensions, "Solid dataset shape mismatch.");
|
||||
Hdf5Handle properties{RequireHdf5Id(H5Dget_create_plist(dataset.Get()),
|
||||
"Solid dataset properties failed."),
|
||||
H5Pclose};
|
||||
Check(H5Pget_layout(properties.Get()) == H5D_CONTIGUOUS &&
|
||||
H5Pget_nfilters(properties.Get()) == 0,
|
||||
"Solid dataset storage mismatch.");
|
||||
return dataset;
|
||||
}
|
||||
void String(hid_t object, const std::string& name,
|
||||
const std::string& expected) {
|
||||
Hdf5Handle attr{RequireHdf5Id(H5Aopen(object, name.c_str(), H5P_DEFAULT),
|
||||
"Solid string attribute missing."),
|
||||
H5Aclose};
|
||||
Hdf5Handle type{
|
||||
RequireHdf5Id(H5Aget_type(attr.Get()), "Solid attribute type failed."),
|
||||
H5Tclose};
|
||||
Hdf5Handle space{
|
||||
RequireHdf5Id(H5Aget_space(attr.Get()), "Solid attribute space failed."),
|
||||
H5Sclose};
|
||||
Check(H5Tget_class(type.Get()) == H5T_STRING &&
|
||||
H5Tis_variable_str(type.Get()) > 0 &&
|
||||
H5Tget_cset(type.Get()) == H5T_CSET_UTF8 &&
|
||||
H5Sget_simple_extent_type(space.Get()) == H5S_SCALAR,
|
||||
"Solid string attribute type mismatch.");
|
||||
char* value = nullptr;
|
||||
RequireHdf5(H5Aread(attr.Get(), type.Get(), &value),
|
||||
"Solid string attribute read failed.");
|
||||
const std::string actual = value ? value : "";
|
||||
RequireHdf5(H5free_memory(value), "Solid string release failed.");
|
||||
Check(IsValidUtf8(actual) && actual == expected,
|
||||
"Solid string attribute mismatch.");
|
||||
}
|
||||
template <class T>
|
||||
void Number(hid_t object, const std::string& name, T expected, hid_t ft,
|
||||
hid_t mt) {
|
||||
Hdf5Handle attr{RequireHdf5Id(H5Aopen(object, name.c_str(), H5P_DEFAULT),
|
||||
"Solid numeric attribute missing."),
|
||||
H5Aclose};
|
||||
Hdf5Handle type{
|
||||
RequireHdf5Id(H5Aget_type(attr.Get()), "Solid attribute type failed."),
|
||||
H5Tclose};
|
||||
Hdf5Handle space{
|
||||
RequireHdf5Id(H5Aget_space(attr.Get()), "Solid attribute space failed."),
|
||||
H5Sclose};
|
||||
Check(H5Tequal(type.Get(), ft) > 0 &&
|
||||
H5Sget_simple_extent_type(space.Get()) == H5S_SCALAR,
|
||||
"Solid numeric attribute type mismatch.");
|
||||
T actual{};
|
||||
RequireHdf5(H5Aread(attr.Get(), mt, &actual),
|
||||
"Solid numeric attribute read failed.");
|
||||
Check(actual == expected, "Solid numeric attribute mismatch.");
|
||||
}
|
||||
void Column(hid_t dataset, hid_t compound, std::size_t index,
|
||||
const SolidColumn& column, std::size_t rows) {
|
||||
char* name = H5Tget_member_name(compound, static_cast<unsigned>(index));
|
||||
Check(name != nullptr, "Solid member name missing.");
|
||||
const std::string actual_name = name;
|
||||
RequireHdf5(H5free_memory(name), "Solid member name release failed.");
|
||||
Check(actual_name == column.name,
|
||||
"Solid compound member order/name mismatch.");
|
||||
Hdf5Handle stored{
|
||||
RequireHdf5Id(H5Tget_member_type(compound, static_cast<unsigned>(index)),
|
||||
"Solid member type missing."),
|
||||
H5Tclose};
|
||||
const auto expected = SolidColumnType(column, false);
|
||||
Check(H5Tequal(stored.Get(), expected.Get()) > 0,
|
||||
"Solid compound member type mismatch.");
|
||||
const auto native = SolidColumnType(column, true);
|
||||
Hdf5Handle memory{
|
||||
RequireHdf5Id(H5Tcreate(H5T_COMPOUND, H5Tget_size(native.Get())),
|
||||
"Solid column memory type failed."),
|
||||
H5Tclose};
|
||||
RequireHdf5(H5Tinsert(memory.Get(), column.name.c_str(), 0, native.Get()),
|
||||
"Solid column memory member failed.");
|
||||
std::visit(
|
||||
[&](const auto& values) {
|
||||
using T = typename std::decay_t<decltype(values)>::value_type;
|
||||
if constexpr (std::is_same_v<T, std::string>) {
|
||||
std::vector<char*> actual(rows, nullptr);
|
||||
if (rows)
|
||||
RequireHdf5(H5Dread(dataset, memory.Get(), H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, actual.data()),
|
||||
"Solid string column read failed.");
|
||||
bool same = true;
|
||||
for (std::size_t i = 0; i < rows; ++i) {
|
||||
const std::string value = actual[i] ? actual[i] : "";
|
||||
same = same && IsValidUtf8(value) && value == values[i];
|
||||
}
|
||||
Hdf5Handle space{RequireHdf5Id(H5Dget_space(dataset),
|
||||
"Solid reclaim space failed."),
|
||||
H5Sclose};
|
||||
if (rows)
|
||||
RequireHdf5(H5Dvlen_reclaim(memory.Get(), space.Get(), H5P_DEFAULT,
|
||||
actual.data()),
|
||||
"Solid string column release failed.");
|
||||
Check(same, "Solid string column identity mismatch.");
|
||||
} else {
|
||||
std::vector<T> actual(values.size());
|
||||
if (rows)
|
||||
RequireHdf5(H5Dread(dataset, memory.Get(), H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, actual.data()),
|
||||
"Solid numeric column read failed.");
|
||||
Check(actual == values, "Solid numeric column identity mismatch.");
|
||||
if constexpr (std::is_same_v<T, double>)
|
||||
for (const auto value : actual)
|
||||
Check(std::isfinite(value), "Solid numeric column is nonfinite.");
|
||||
}
|
||||
},
|
||||
column.values);
|
||||
}
|
||||
} // namespace
|
||||
Status SelfCheckSolidFile(const std::filesystem::path& path,
|
||||
const SolidPlan& plan,
|
||||
herr_t (*reader_close)(hid_t)) {
|
||||
try {
|
||||
Hdf5ErrorSilencer silence;
|
||||
Hdf5Handle file{RequireHdf5Id(H5Fopen(path.string().c_str(), H5F_ACC_RDONLY,
|
||||
H5P_DEFAULT),
|
||||
"Solid candidate readback open failed."),
|
||||
reader_close};
|
||||
{
|
||||
Hdf5Handle group{
|
||||
RequireHdf5Id(H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT),
|
||||
"Solid metadata missing."),
|
||||
H5Gclose};
|
||||
Number<std::uint64_t>(group.Get(), "schema_version", 1, H5T_STD_U64LE,
|
||||
H5T_NATIVE_UINT64);
|
||||
for (const auto& item : plan.metadata)
|
||||
String(group.Get(), item.first, item.second);
|
||||
}
|
||||
for (const auto& table : plan.tables) {
|
||||
auto dataset = Dataset(file.Get(), table.path, {table.rows});
|
||||
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
||||
"Solid compound type failed."),
|
||||
H5Tclose};
|
||||
Check(H5Tget_class(type.Get()) == H5T_COMPOUND &&
|
||||
H5Tget_nmembers(type.Get()) ==
|
||||
static_cast<int>(table.columns.size()),
|
||||
"Solid compound member inventory mismatch.");
|
||||
for (std::size_t i = 0; i < table.columns.size(); ++i)
|
||||
Column(dataset.Get(), type.Get(), i, table.columns[i], table.rows);
|
||||
for (const auto& item : table.strings)
|
||||
String(dataset.Get(), item.first, item.second);
|
||||
}
|
||||
{
|
||||
auto dataset = Dataset(file.Get(), "/model/nodal_constraint_mask",
|
||||
{plan.mask.size() / 3, 3});
|
||||
Hdf5Handle type{
|
||||
RequireHdf5Id(H5Dget_type(dataset.Get()), "Solid mask type failed."),
|
||||
H5Tclose};
|
||||
Check(H5Tequal(type.Get(), H5T_STD_U8LE) > 0,
|
||||
"Solid mask dtype mismatch.");
|
||||
std::vector<std::uint8_t> mask(plan.mask.size());
|
||||
RequireHdf5(H5Dread(dataset.Get(), H5T_NATIVE_UINT8, H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, mask.data()),
|
||||
"Solid mask read failed.");
|
||||
Check(mask == plan.mask, "Solid mask identity mismatch.");
|
||||
String(dataset.Get(), "component_names", "UX,UY,UZ");
|
||||
}
|
||||
for (const auto& data : plan.datasets) {
|
||||
auto dataset = Dataset(file.Get(), data.path, data.dimensions);
|
||||
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
||||
"Solid result dtype failed."),
|
||||
H5Tclose};
|
||||
Check(H5Tequal(type.Get(), H5T_IEEE_F64LE) > 0,
|
||||
"Solid result dtype mismatch.");
|
||||
std::vector<double> values(data.values.size());
|
||||
if (!values.empty())
|
||||
RequireHdf5(H5Dread(dataset.Get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
|
||||
H5P_DEFAULT, values.data()),
|
||||
"Solid result read failed.");
|
||||
Check(values == data.values, "Solid result exact readback mismatch.");
|
||||
for (const auto value : values)
|
||||
Check(std::isfinite(value), "Solid result is nonfinite.");
|
||||
for (const auto& item : data.strings)
|
||||
String(dataset.Get(), item.first, item.second);
|
||||
for (const auto& item : data.integers)
|
||||
Number(dataset.Get(), item.first, item.second, H5T_STD_U64LE,
|
||||
H5T_NATIVE_UINT64);
|
||||
for (const auto& item : data.doubles)
|
||||
Number(dataset.Get(), item.first, item.second, H5T_IEEE_F64LE,
|
||||
H5T_NATIVE_DOUBLE);
|
||||
}
|
||||
RequireHdf5(file.CloseChecked(), "Solid readback close failed.");
|
||||
return Status::Ok();
|
||||
} catch (const std::exception& error) {
|
||||
return OutputFailure("solid-self-check-failure", error.what());
|
||||
}
|
||||
}
|
||||
} // namespace fesa::hdf5_internal
|
||||
Reference in New Issue
Block a user