Files
FESADev/src/fesa/io/hdf5/solid_hdf5_self_check.cpp
T

218 lines
9.8 KiB
C++

#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