#include "io/hdf5/solid_hdf5_self_check.h" #include #include #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& 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(dimensions.size()), "Solid dataset rank mismatch."); std::vector 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 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(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(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::value_type; if constexpr (std::is_same_v) { std::vector 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 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) 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(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(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 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 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