#include "io/hdf5/eigen_hdf5_self_check.h" #include #include #include #include #include #include #include #include #include #include "fesa/build_info.h" #include "io/hdf5/hdf5_primitives.h" #include "io/hdf5/hdf5_raii.h" namespace fesa::hdf5_internal { namespace { Status Failure(const std::string& message) { return OutputFailure("invalid-eigen-hdf5-candidate", message); } bool HasPath(hid_t file, const char* path) { return H5Lexists(file, path, H5P_DEFAULT) > 0; } bool HasNoStaticResults(hid_t file) { for (const char* path : {"/steps/Step-1/frames", "/steps/Step-1/global", "/steps/Step-1/element"}) { if (HasPath(file, path)) return false; } return true; } bool ReadUint64Attribute(hid_t object, const char* name, std::uint64_t expected) { if (H5Aexists(object, name) <= 0) return false; Hdf5Handle attribute{H5Aopen(object, name, H5P_DEFAULT), H5Aclose}; if (attribute.Get() < 0) return false; Hdf5Handle type{H5Aget_type(attribute.Get()), H5Tclose}; Hdf5Handle space{H5Aget_space(attribute.Get()), H5Sclose}; if (type.Get() < 0 || H5Tequal(type.Get(), H5T_STD_U64LE) <= 0 || space.Get() < 0 || H5Sget_simple_extent_type(space.Get()) != H5S_SCALAR) return false; std::uint64_t value = 0U; return H5Aread(attribute.Get(), H5T_NATIVE_UINT64, &value) >= 0 && value == expected; } bool ReadStringAttribute(hid_t object, const char* name, const char* expected) { if (H5Aexists(object, name) <= 0) return false; Hdf5Handle attribute{H5Aopen(object, name, H5P_DEFAULT), H5Aclose}; if (attribute.Get() < 0) return false; Hdf5Handle type{H5Aget_type(attribute.Get()), H5Tclose}; Hdf5Handle space{H5Aget_space(attribute.Get()), H5Sclose}; if (type.Get() < 0 || H5Tget_class(type.Get()) != H5T_STRING || H5Tis_variable_str(type.Get()) <= 0 || H5Tget_cset(type.Get()) != H5T_CSET_UTF8 || space.Get() < 0 || H5Sget_simple_extent_type(space.Get()) != H5S_SCALAR) return false; char* value = nullptr; if (H5Aread(attribute.Get(), type.Get(), &value) < 0 || value == nullptr) return false; const bool matches = std::string{value} == expected; H5free_memory(value); return matches; } bool DatasetShape(hid_t file, const char* path, const std::vector& expected_dimensions) { Hdf5Handle dataset{H5Dopen2(file, path, H5P_DEFAULT), H5Dclose}; if (dataset.Get() < 0) return false; Hdf5Handle space{H5Dget_space(dataset.Get()), H5Sclose}; if (space.Get() < 0 || H5Sget_simple_extent_ndims(space.Get()) != static_cast(expected_dimensions.size())) return false; std::vector dimensions(expected_dimensions.size()); if (H5Sget_simple_extent_dims(space.Get(), dimensions.data(), nullptr) < 0) return false; return dimensions == expected_dimensions; } bool VerifyVectors(hid_t file, const Domain& domain, const EigenAnalysisState& state) { const std::size_t count = state.vectors.Rows() * state.vectors.Columns(); if (!DatasetShape(file, "/steps/Step-1/eigen/vectors", {static_cast(state.modes.size()), static_cast(domain.Nodes().size()), 6U})) return false; std::vector values(count); Hdf5Handle dataset{H5Dopen2(file, "/steps/Step-1/eigen/vectors", H5P_DEFAULT), H5Dclose}; Hdf5Handle type{H5Dget_type(dataset.Get()), H5Tclose}; if (dataset.Get() < 0 || H5Tequal(type.Get(), H5T_IEEE_F64LE) <= 0 || (count != 0U && H5Dread(dataset.Get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, values.data()) < 0)) return false; for (std::size_t row = 0U; row < state.vectors.Rows(); ++row) { for (std::size_t column = 0U; column < state.vectors.Columns(); ++column) { const double value = values[row * state.vectors.Columns() + column]; if (!std::isfinite(value) || value != state.vectors(row, column)) return false; if (state.constraint_mask[column] != 0U && value != 0.0) return false; } } return true; } template bool CheckMember(hid_t file, const char* path, const char* name, hid_t file_type, hid_t memory_type, const Rows& rows, Getter get) { if (!DatasetShape(file, path, {static_cast(rows.size())})) return false; Hdf5Handle dataset{H5Dopen2(file, path, H5P_DEFAULT), H5Dclose}; Hdf5Handle type{H5Dget_type(dataset.Get()), H5Tclose}; if (type.Get() < 0 || H5Tget_class(type.Get()) != H5T_COMPOUND) return false; const int member = H5Tget_member_index(type.Get(), name); if (member < 0) return false; Hdf5Handle actual_type{ H5Tget_member_type(type.Get(), static_cast(member)), H5Tclose}; if (H5Tequal(actual_type.Get(), file_type) <= 0) return false; Hdf5Handle memory{H5Tcreate(H5T_COMPOUND, sizeof(T)), H5Tclose}; if (memory.Get() < 0 || H5Tinsert(memory.Get(), name, 0U, memory_type) < 0) return false; std::vector values(rows.size()); if (!values.empty() && H5Dread(dataset.Get(), memory.Get(), H5S_ALL, H5S_ALL, H5P_DEFAULT, values.data()) < 0) return false; for (std::size_t i = 0; i < values.size(); ++i) if (values[i] != static_cast(get(rows[i]))) return false; return true; } template bool CheckTextMember(hid_t file, const char* path, const char* name, const Rows& rows, Getter get) { if (!DatasetShape(file, path, {static_cast(rows.size())})) return false; Hdf5Handle dataset{H5Dopen2(file, path, H5P_DEFAULT), H5Dclose}; Hdf5Handle type{H5Dget_type(dataset.Get()), H5Tclose}; if (type.Get() < 0 || H5Tget_class(type.Get()) != H5T_COMPOUND) return false; const int member = H5Tget_member_index(type.Get(), name); if (member < 0) return false; Hdf5Handle member_type{ H5Tget_member_type(type.Get(), static_cast(member)), H5Tclose}; if (H5Tget_class(member_type.Get()) != H5T_STRING || H5Tis_variable_str(member_type.Get()) <= 0 || H5Tget_cset(member_type.Get()) != H5T_CSET_UTF8) return false; Hdf5Handle memory{H5Tcreate(H5T_COMPOUND, sizeof(char*)), H5Tclose}; if (H5Tinsert(memory.Get(), name, 0U, member_type.Get()) < 0) return false; std::vector values(rows.size(), nullptr); if (!values.empty() && H5Dread(dataset.Get(), memory.Get(), H5S_ALL, H5S_ALL, H5P_DEFAULT, values.data()) < 0) return false; bool matches = true; for (std::size_t i = 0; i < values.size(); ++i) { if (values[i] == nullptr || std::string(values[i]) != get(rows[i])) matches = false; if (values[i] != nullptr && H5free_memory(values[i]) < 0) matches = false; } return matches; } template bool CheckScalar(hid_t object, const char* name, hid_t file_type, hid_t memory_type, T expected) { Hdf5Handle attribute{H5Aopen(object, name, H5P_DEFAULT), H5Aclose}; if (attribute.Get() < 0) return false; Hdf5Handle type{H5Aget_type(attribute.Get()), H5Tclose}; Hdf5Handle space{H5Aget_space(attribute.Get()), H5Sclose}; if (H5Tequal(type.Get(), file_type) <= 0 || H5Sget_simple_extent_type(space.Get()) != H5S_SCALAR) return false; T value{}; return H5Aread(attribute.Get(), memory_type, &value) >= 0 && value == expected; } template bool CheckArray(hid_t file, const char* path, hid_t file_type, hid_t memory_type, const std::vector& shape, const Values& expected) { if (!DatasetShape(file, path, shape)) return false; Hdf5Handle dataset{H5Dopen2(file, path, H5P_DEFAULT), H5Dclose}; Hdf5Handle type{H5Dget_type(dataset.Get()), H5Tclose}; if (H5Tequal(type.Get(), file_type) <= 0) return false; std::vector actual(expected.size()); if (!actual.empty() && H5Dread(dataset.Get(), memory_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, actual.data()) < 0) return false; for (std::size_t i = 0U; i < actual.size(); ++i) if (actual[i] != expected[i]) return false; return true; } bool CheckTextAttributes( hid_t object, std::initializer_list> attributes) { for (const auto& attribute : attributes) if (!ReadStringAttribute(object, attribute.first, attribute.second.c_str())) return false; return true; } bool CheckAttributes(hid_t file, const Domain& domain, const EigenAnalysisState& state) { Hdf5Handle metadata{H5Gopen2(file, "/metadata", H5P_DEFAULT), H5Gclose}; if (!CheckTextAttributes( metadata.Get(), {{"solver_version", std::string{SolverVersion()}}, {"source_input_identity", SourceInputIdentity(domain)}, {"unit_system_label", "user-consistent-unspecified"}, {"coordinate_convention", "global-cartesian; shell-local=initial-reference; " "positive-thickness=+zeta"}, {"internal_formulation", "FESA-MITC4"}, {"integration_rule", "2x2x2-gauss; mitc4-edge-midpoint-shear"}, {"mass_integration_rule", "3x3x3-gauss"}, {"stabilization_rule", "initial-physical-rotary-trace-mean"}, {"mass_variant", "derived-lumped"}, {"mass_origin", "consistent-moment-row-sum"}, {"normalization", "mass"}, {"eigensolver_backend", "MKL FEAST"}, {"eigensolver_routine", "dfeast_scsrgv"}, {"backend_problem", "scaled-whitened-lumped-with-identity-mass"}, {"numerical_contract", "mitc4-shell-feast-eigen2:r1"}})) return false; const auto& source = *domain.Frequency(); Hdf5Handle step{H5Gopen2(file, "/steps/Step-1", H5P_DEFAULT), H5Gclose}; if (!CheckTextAttributes( step.Get(), {{"source_step_name", source.source_step_name_raw}, {"source_step_name_canonical", source.source_step_name_canonical}, {"source_eigensolver", "LANCZOS"}, {"source_sim", "YES"}, {"source_acoustic_coupling", "ON"}, {"source_normalization", "MASS"}, {"frame_indexing", "mode-rank-one-based"}}) || !ReadUint64Attribute(step.Get(), "step_source_line", source.step_location.line) || !ReadUint64Attribute(step.Get(), "frequency_source_line", source.frequency_location.line) || !ReadUint64Attribute(step.Get(), "frequency_data_line", source.data_location.line) || !CheckScalar(step.Get(), "source_nlgeom", H5T_STD_U8LE, H5T_NATIVE_UINT8, 0U) || !CheckScalar( step.Get(), "source_perturbation_present", H5T_STD_U8LE, H5T_NATIVE_UINT8, static_cast(source.perturbation_explicit))) return false; Hdf5Handle presence{ H5Aopen(step.Get(), "source_option_presence", H5P_DEFAULT), H5Aclose}; Hdf5Handle presence_type{H5Aget_type(presence.Get()), H5Tclose}; Hdf5Handle presence_space{H5Aget_space(presence.Get()), H5Sclose}; hsize_t extent = 0U; std::array actual_presence{}; const std::array expected_presence{ static_cast(source.eigensolver_explicit), static_cast(source.sim_explicit), static_cast(source.acoustic_coupling_explicit), static_cast(source.normalization_explicit)}; if (presence.Get() < 0 || H5Tequal(presence_type.Get(), H5T_STD_U8LE) <= 0 || H5Sget_simple_extent_ndims(presence_space.Get()) != 1 || H5Sget_simple_extent_dims(presence_space.Get(), &extent, nullptr) < 0 || extent != 4U || H5Aread(presence.Get(), H5T_NATIVE_UINT8, actual_presence.data()) < 0 || actual_presence != expected_presence) return false; for (const char* leaf : {"modes", "frequencies", "vectors"}) { const std::string path = std::string{"/steps/Step-1/eigen/"} + leaf; Hdf5Handle dataset{H5Dopen2(file, path.c_str(), H5P_DEFAULT), H5Dclose}; if (!ReadUint64Attribute(dataset.Get(), "eigen_schema_version", 1U) || !CheckTextAttributes(dataset.Get(), {{"source_step_name", source.source_step_name_raw}, {"domain", "mode"}})) return false; if (std::string{leaf} == "vectors") { if (!CheckTextAttributes( dataset.Get(), {{"component_names", "UX,UY,UZ,URX,URY,URZ"}, {"component_unit_dimensions", "mass^-1/2,mass^-1/2,mass^-1/2,mass^-1/2*length^-1,mass^-1/" "2*length^-1,mass^-1/2*length^-1"}, {"coordinate_system", "global-cartesian"}, {"location", "nodal-modal-vector"}, {"normalization", "mass"}, {"mass_variant", "derived-lumped"}, {"interpretation", "normalized-mode-not-response-displacement"}})) return false; } else { const bool modes = std::string{leaf} == "modes"; if (!CheckTextAttributes( dataset.Get(), {{"quantity", modes ? "eigenvalue" : "cycle-frequency"}, {"component_names", modes ? "Value" : "Freq"}, {"component_unit_dimensions", modes ? "time^-2" : "time^-1"}, {"coordinate_system", "scalar"}, {"location", "modal-frame"}})) return false; if (!modes && !ReadStringAttribute(dataset.Get(), "unavailable_rule", "negative-raw-eigenvalue")) return false; } } Hdf5Handle verification{ H5Gopen2(file, "/steps/Step-1/eigen/verification", H5P_DEFAULT), H5Gclose}; for (const auto& metric : std::initializer_list>{ {"characteristic_length", state.characteristic_length}, {"operator_scale", state.backend.operator_scale}, {"eta_gram", state.eta_gram}, {"max_eta_orth", state.max_eta_orth}, {"eta_mz", state.eta_mz}, {"eta_kz", state.eta_kz}, {"eta_pz", state.eta_pz}}) if (!CheckScalar(verification.Get(), metric.first, H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, metric.second)) return false; if (!ReadStringAttribute(verification.Get(), "validation_scope", "all-computed-c-columns") || !CheckScalar( verification.Get(), "null_checks_applicable", H5T_STD_U8LE, H5T_NATIVE_UINT8, static_cast(state.null_checks_applicable))) return false; Hdf5Handle backend{H5Gopen2(file, "/steps/Step-1/eigen/backend", H5P_DEFAULT), H5Gclose}; const auto& b = state.backend; if (!CheckTextAttributes( backend.Get(), {{"runtime_version", b.runtime_version}, {"integer_abi", b.integer_abi}, {"upper_search_termination", b.upper_search_termination}}) || !ReadUint64Attribute(backend.Get(), "thread_count", b.thread_count) || !ReadUint64Attribute(backend.Get(), "seed", b.seed) || !ReadUint64Attribute(backend.Get(), "final_count", state.eigenpair_checks.size())) return false; for (const auto& bound : std::initializer_list>{ {"enclosure_lower", b.enclosure_lower}, {"enclosure_upper", b.enclosure_upper}, {"solve_lower", b.solve_lower}, {"solve_upper", b.solve_upper}}) if (!CheckScalar(backend.Get(), bound.first, H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, bound.second)) return false; std::size_t counts = 0U, solves = 0U, final_ordinal = 0U; for (const auto& call : b.backend_calls) { if (call.kind == "count") ++counts; if (call.kind == "solve") { ++solves; final_ordinal = call.ordinal; } } return ReadUint64Attribute(backend.Get(), "count_calls", counts) && ReadUint64Attribute(backend.Get(), "solve_calls", solves) && ReadUint64Attribute(backend.Get(), "final_solve_ordinal", final_ordinal) && CheckArray(file, "/steps/Step-1/eigen/backend/base_fpm", H5T_STD_I64LE, H5T_NATIVE_INT64, {128U}, b.base_fpm) && CheckArray( file, "/model/nodal_constraint_mask", H5T_STD_U8LE, H5T_NATIVE_UINT8, {static_cast(domain.Nodes().size()), 6U}, state.constraint_mask) && CheckArray(file, "/model/prescribed_displacement", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, {static_cast(domain.Nodes().size()), 6U}, state.prescribed_displacement); } bool CheckModelRows(hid_t file, const Domain& domain) { std::vector indices(domain.Nodes().size()); std::iota(indices.begin(), indices.end(), 0U); const char* path = "/model/nodes"; const hsize_t three = 3U, four = 4U; Hdf5Handle coordinates_file{H5Tarray_create2(H5T_IEEE_F64LE, 1, &three), H5Tclose}; Hdf5Handle coordinates_memory{H5Tarray_create2(H5T_NATIVE_DOUBLE, 1, &three), H5Tclose}; if (!CheckMember(file, path, "internal_node_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [](auto i) { return i; }) || !CheckTextMember( file, path, "instance_name", domain.Nodes(), [](const auto& row) { return row.source_id.instance_name; }) || !CheckTextMember( file, path, "source_label", domain.Nodes(), [](const auto& row) { return row.source_id.source_label_text; }) || !CheckMember>( file, path, "coordinates", coordinates_file.Get(), coordinates_memory.Get(), domain.Nodes(), [](const auto& row) { return row.coordinates; })) return false; indices.resize(domain.ShellElements().Size()); std::iota(indices.begin(), indices.end(), 0U); Hdf5Handle nodes_file{H5Tarray_create2(H5T_STD_U64LE, 1, &four), H5Tclose}; Hdf5Handle nodes_memory{H5Tarray_create2(H5T_NATIVE_UINT64, 1, &four), H5Tclose}; path = "/model/elements"; if (!CheckMember(file, path, "internal_element_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [](auto i) { return i; }) || !CheckTextMember( file, path, "instance_name", indices, [&](auto i) { return domain.ShellElements()[i].source_id.instance_name; }) || !CheckTextMember( file, path, "source_label", indices, [&](auto i) { return domain.ShellElements()[i].source_id.source_label_text; }) || !CheckTextMember(file, path, "source_element_type", indices, [&](auto i) { return ShellSourceTypeName( domain.ShellElements()[i].source_type); }) || !CheckTextMember(file, path, "internal_formulation", indices, [](auto) { return std::string{"FESA-MITC4"}; }) || !CheckMember( file, path, "material_internal_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [&](auto i) { return domain.ShellElements()[i].material_index; }) || !CheckMember( file, path, "shell_section_internal_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [&](auto i) { return domain.ShellElements()[i].section_index; }) || !CheckMember>( file, path, "node_internal_ids", nodes_file.Get(), nodes_memory.Get(), indices, [&](auto i) { const auto& nodes = domain.ShellElements()[i].node_indices; return std::array{nodes[0], nodes[1], nodes[2], nodes[3]}; })) return false; indices.resize(domain.LinearElasticMaterials().Size()); std::iota(indices.begin(), indices.end(), 0U); path = "/model/shell/materials"; if (!CheckMember(file, path, "internal_material_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [](auto i) { return i; }) || !CheckTextMember( file, path, "name", indices, [&](auto i) { return domain.LinearElasticMaterials()[i].name; }) || !CheckMember( file, path, "E", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, indices, [&](auto i) { return domain.LinearElasticMaterials()[i].youngs_modulus; }) || !CheckMember( file, path, "nu", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, indices, [&](auto i) { return domain.LinearElasticMaterials()[i].poisson_ratio; })) return false; indices.resize(domain.ShellSections().Size()); std::iota(indices.begin(), indices.end(), 0U); path = "/model/shell/sections"; if (!CheckMember(file, path, "internal_section_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [](auto i) { return i; }) || !CheckMember( file, path, "material_internal_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [&](auto i) { return domain.ShellSections()[i].material_index; }) || !CheckMember( file, path, "source_line", H5T_STD_U64LE, H5T_NATIVE_UINT64, indices, [&](auto i) { return domain.ShellSections()[i].location.line; }) || !CheckTextMember(file, path, "source_file", indices, [&](auto i) { return NormalizedPathString( domain.ShellSections()[i].location.file); }) || !CheckTextMember( file, path, "source_elset", indices, [&](auto i) { return domain.ShellSections()[i].name; }) || !CheckMember( file, path, "thickness", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, indices, [&](auto i) { return domain.ShellSections()[i].thickness; })) return false; std::vector directors, frames; for (const auto& frame : domain.ShellNodeInitialFrames()) { directors.insert(directors.end(), frame.director.begin(), frame.director.end()); frames.insert(frames.end(), frame.tangent_a.begin(), frame.tangent_a.end()); frames.insert(frames.end(), frame.tangent_b.begin(), frame.tangent_b.end()); frames.insert(frames.end(), frame.director.begin(), frame.director.end()); } return CheckArray(file, "/model/shell/nodal_director", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, {static_cast(domain.Nodes().size()), 3U}, directors) && CheckArray( file, "/model/shell/nodal_frame", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, {static_cast(domain.Nodes().size()), 3U, 3U}, frames); } bool CheckModalRows(hid_t file, const Domain& domain, const EigenAnalysisState& state, const std::vector& diagnostics) { const char* path = "/steps/Step-1/eigen/modes"; if (!CheckMember(file, path, "mode_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.modes, [](const auto& row) { return row.mode_id; })) return false; if (!CheckMember(file, path, "eigenvalue", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.modes, [](const auto& row) { return row.eigenvalue; })) return false; if (!CheckMember( file, path, "classification", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.modes, [](const auto& row) { return row.classification; })) return false; if (!CheckMember( file, path, "cluster_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.modes, [](const auto& row) { return row.cluster_id; })) return false; std::vector frequencies; for (const auto& mode : state.modes) if (mode.frequency) frequencies.push_back(mode); path = "/steps/Step-1/eigen/frequencies"; if (!CheckMember(file, path, "mode_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, frequencies, [](const auto& row) { return row.mode_id; })) return false; if (!CheckMember(file, path, "frequency", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, frequencies, [](const auto& row) { return *row.frequency; })) return false; path = "/steps/Step-1/eigen/verification/nodal_reduction"; if (!CheckMember( file, path, "free_count", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.nodal_reduction, [](const auto& row) { return row.free_count; })) return false; if (!CheckMember( file, path, "null_count", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.nodal_reduction, [](const auto& row) { return row.null_count; })) return false; if (!CheckMember( file, path, "positive_count", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.nodal_reduction, [](const auto& row) { return row.positive_count; })) return false; if (!CheckMember( file, path, "rcond_available", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.nodal_reduction, [](const auto& row) { return row.rcond_available; })) return false; if (!CheckMember(file, path, "rcond_inf", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.nodal_reduction, [](const auto& row) { return row.rcond_inf; })) return false; path = "/steps/Step-1/eigen/verification/eigenpair_checks"; if (!CheckMember( file, path, "sorted_rank", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.eigenpair_checks, [](const auto& row) { return row.sorted_rank; })) return false; if (!CheckMember( file, path, "solver_column", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.eigenpair_checks, [](const auto& row) { return row.solver_column; })) return false; if (!CheckMember(file, path, "eigenvalue", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.eigenvalue; })) return false; if (!CheckMember(file, path, "modal_mass", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.modal_mass; })) return false; if (!CheckMember( file, path, "rayleigh_quotient", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.rayleigh_quotient; })) return false; if (!CheckMember(file, path, "eta_r", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.eta_r; })) return false; if (!CheckMember(file, path, "eta_w", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.eta_w; })) return false; if (!CheckMember(file, path, "rho_b", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.rho_b; })) return false; if (!CheckMember(file, path, "eta_norm", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.eta_norm; })) return false; if (!CheckMember(file, path, "eta_energy", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.eta_energy; })) return false; if (!CheckMember( file, path, "backend_res_available", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.eigenpair_checks, [](const auto& row) { return row.backend_res_available; })) return false; if (!CheckMember(file, path, "backend_res", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.eigenpair_checks, [](const auto& row) { return row.backend_res; })) return false; path = "/steps/Step-1/eigen/backend/calls"; if (!CheckMember(file, path, "ordinal", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.backend.backend_calls, [](const auto& row) { return row.ordinal; })) return false; if (!CheckMember(file, path, "lower", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.backend.backend_calls, [](const auto& row) { return row.lower; })) return false; if (!CheckMember(file, path, "upper", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.backend.backend_calls, [](const auto& row) { return row.upper; })) return false; if (!CheckMember( file, path, "allocated_m0", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.backend.backend_calls, [](const auto& row) { return row.allocated_m0; })) return false; if (!CheckMember(file, path, "info", H5T_STD_I64LE, H5T_NATIVE_INT64, state.backend.backend_calls, [](const auto& row) { return row.info; })) return false; if (!CheckMember( file, path, "count_available", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.backend.backend_calls, [](const auto& row) { return row.count_available; })) return false; if (!CheckMember(file, path, "count", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.backend.backend_calls, [](const auto& row) { return row.count; })) return false; if (!CheckMember( file, path, "loop_available", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.backend.backend_calls, [](const auto& row) { return row.loop_available; })) return false; if (!CheckMember(file, path, "loop", H5T_STD_U64LE, H5T_NATIVE_UINT64, state.backend.backend_calls, [](const auto& row) { return row.loop; })) return false; if (!CheckMember( file, path, "epsout_available", H5T_STD_U8LE, H5T_NATIVE_UINT8, state.backend.backend_calls, [](const auto& row) { return row.epsout_available; })) return false; if (!CheckMember(file, path, "epsout", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, state.backend.backend_calls, [](const auto& row) { return row.epsout; })) return false; if (!CheckTextMember(file, path, "kind", state.backend.backend_calls, [](const auto& row) { return row.kind; })) return false; if (!CheckTextMember(file, path, "purpose", state.backend.backend_calls, [](const auto& row) { return row.purpose; })) return false; if (!CheckTextMember(file, path, "outcome", state.backend.backend_calls, [](const auto& row) { return row.outcome; })) return false; if (!CheckTextMember(file, path, "decision_reason", state.backend.backend_calls, [](const auto& row) { return row.decision_reason; })) return false; path = "/model/shell/densities"; if (!CheckMember( file, path, "material_internal_id", H5T_STD_U64LE, H5T_NATIVE_UINT64, domain.Densities(), [](const auto& row) { return row.material_index; })) return false; if (!CheckMember(file, path, "rho", H5T_IEEE_F64LE, H5T_NATIVE_DOUBLE, domain.Densities(), [](const auto& row) { return row.rho; })) return false; if (!CheckMember( file, path, "source_line", H5T_STD_U64LE, H5T_NATIVE_UINT64, domain.Densities(), [](const auto& row) { return row.location.line; })) return false; if (!CheckTextMember(file, path, "source_file", domain.Densities(), [](const auto& row) { return NormalizedPathString(row.location.file); })) return false; path = "/diagnostics"; if (!CheckMember( file, path, "line", H5T_STD_U64LE, H5T_NATIVE_UINT64, diagnostics, [](const auto& row) { return row.location.line; })) return false; if (!CheckTextMember(file, path, "severity", diagnostics, [](const auto&) { return std::string{"warning"}; })) return false; if (!CheckTextMember(file, path, "file", diagnostics, [](const auto& row) { return NormalizedPathString(row.location.file); })) return false; if (!CheckTextMember(file, path, "code", diagnostics, [](const auto& row) { return row.code; })) return false; if (!CheckTextMember(file, path, "keyword", diagnostics, [](const auto& row) { return row.keyword; })) return false; if (!CheckTextMember(file, path, "entity_identity", diagnostics, [](const auto& row) { return row.entity_identity; })) return false; if (!CheckTextMember(file, path, "message", diagnostics, [](const auto& row) { return row.message; })) return false; return true; } } // namespace Status SelfCheckEigenFile(const std::filesystem::path& path, const Domain& domain, const EigenAnalysisState& state, const std::vector& diagnostics) { try { Hdf5ErrorSilencer silence; Hdf5Handle file{RequireHdf5Id(H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT), "Unable to reopen eigen candidate."), H5Fclose}; for (const char* required : {"/metadata", "/model/nodes", "/model/elements", "/model/shell/densities", "/diagnostics", "/steps/Step-1/eigen/modes", "/steps/Step-1/eigen/frequencies", "/steps/Step-1/eigen/vectors", "/steps/Step-1/eigen/verification/nodal_reduction", "/steps/Step-1/eigen/verification/eigenpair_checks", "/steps/Step-1/eigen/backend/base_fpm", "/steps/Step-1/eigen/backend/calls"}) { if (!HasPath(file.Get(), required)) return Failure("Mandatory eigen path is missing."); } if (!HasNoStaticResults(file.Get())) return Failure("Eigen output contains static result paths."); Hdf5Handle metadata{H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT), H5Gclose}; Hdf5Handle step{H5Gopen2(file.Get(), "/steps/Step-1", H5P_DEFAULT), H5Gclose}; if (metadata.Get() < 0 || step.Get() < 0 || !ReadUint64Attribute(metadata.Get(), "schema_version", 0U) || !ReadUint64Attribute(metadata.Get(), "eigen_schema_version", 1U) || !ReadStringAttribute(metadata.Get(), "feature_id", "mitc4-shell-feast-eigen2") || !ReadStringAttribute(metadata.Get(), "analysis_procedure", "linear-frequency") || !ReadUint64Attribute(step.Get(), "requested_mode_count", state.modes.size()) || !ReadUint64Attribute(step.Get(), "returned_mode_count", state.modes.size()) || !ReadUint64Attribute(step.Get(), "full_dof_count", state.full_dof_count) || !ReadUint64Attribute(step.Get(), "free_dof_count", state.free_dof_count) || !ReadUint64Attribute(step.Get(), "mass_null_dimension", state.mass_null_dimension) || !ReadUint64Attribute(step.Get(), "finite_dimension", state.finite_dimension) || !ReadUint64Attribute(step.Get(), "validated_eigenpair_count", state.eigenpair_checks.size())) { return Failure("Eigen metadata or step inventory differs from state."); } if (!DatasetShape(file.Get(), "/steps/Step-1/eigen/modes", {static_cast(state.modes.size())}) || !DatasetShape(file.Get(), "/steps/Step-1/eigen/frequencies", {static_cast([&state] { std::size_t count = 0U; for (const auto& mode : state.modes) if (mode.frequency) ++count; return count; }())}) || !DatasetShape(file.Get(), "/steps/Step-1/eigen/verification/nodal_reduction", {static_cast(domain.Nodes().size())}) || !DatasetShape(file.Get(), "/steps/Step-1/eigen/verification/eigenpair_checks", {static_cast(state.eigenpair_checks.size())}) || !DatasetShape(file.Get(), "/steps/Step-1/eigen/backend/base_fpm", {128U}) || !DatasetShape( file.Get(), "/steps/Step-1/eigen/backend/calls", {static_cast(state.backend.backend_calls.size())}) || !VerifyVectors(file.Get(), domain, state)) { return Failure("Eigen dataset shape or vector data differs from state."); } if (!CheckModalRows(file.Get(), domain, state, diagnostics)) return Failure( "Compound type, source identity or value differs from candidate " "state."); if (!CheckAttributes(file.Get(), domain, state)) return Failure( "Mandatory attributes, controls or boundary data differ from " "candidate state."); if (!CheckModelRows(file.Get(), domain)) return Failure( "Model source identities, connectivity or physical data differ from " "Domain."); if (metadata.CloseChecked() < 0 || step.CloseChecked() < 0) return Failure("Unable to close candidate groups."); if (file.CloseChecked() < 0) return Failure("Unable to close checked eigen candidate."); return Status::Ok(); } catch (const Hdf5Failure& failure) { return Failure(failure.what()); } } } // namespace fesa::hdf5_internal