#include "io/hdf5/hdf5_model_writer.h" #include #include #include #include #include #include "fesa/build_info.h" namespace fesa::hdf5_internal { struct NodeWriteRow { std::uint64_t internal_node_id; const char* instance_name; const char* source_label; double coordinates[3]; }; struct ElementWriteRow { std::uint64_t internal_element_id; const char* instance_name; const char* source_label; std::uint64_t node_internal_ids[2]; double local_axes[9]; }; struct ShellElementWriteRow { std::uint64_t internal_element_id; const char* instance_name; const char* source_label; const char* source_element_type; const char* internal_formulation; std::uint64_t node_internal_ids[4]; std::uint64_t shell_section_internal_id; std::uint64_t material_internal_id; }; struct ShellMaterialWriteRow { std::uint64_t internal_material_id; const char* name; double youngs_modulus; double poisson_ratio; }; struct ShellSectionWriteRow { std::uint64_t internal_section_id; const char* source_file; std::uint64_t source_line; const char* source_elset; std::uint64_t material_internal_id; double thickness; }; void WriteMetadata(const hid_t file, const Domain& domain) { auto metadata = CreateGroup(file, "/metadata"); WriteUint64Attribute(metadata.Get(), "schema_version", 0U); WriteStringAttribute(metadata.Get(), "solver_version", std::string{SolverVersion()}); WriteStringAttribute(metadata.Get(), "source_input_identity", SourceInputIdentity(domain)); WriteStringAttribute(metadata.Get(), "unit_system_label", "user-consistent-unspecified"); if (IsShellDomain(domain)) { WriteStringAttribute(metadata.Get(), "feature_id", "linear-static-mitc4-shell"); WriteStringAttribute( metadata.Get(), "coordinate_convention", "global-cartesian; shell-local=(e1,e2,e3); positive-thickness=+zeta"); WriteStringAttribute(metadata.Get(), "internal_formulation", "FESA-MITC4"); WriteStringAttribute(metadata.Get(), "integration_rule", "2x2x2-gauss; mitc4-edge-midpoint-shear"); } else { WriteStringAttribute(metadata.Get(), "feature_id", "linear-static-3d-euler-beam"); WriteStringAttribute(metadata.Get(), "coordinate_convention", "global-cartesian; beam-local=(t,n1,t-cross-n1)"); WriteStringAttribute(metadata.Get(), "element_formulation", "B33-3D-Euler-Bernoulli"); } WriteStringAttribute(metadata.Get(), "step_name", kStepName); WriteUint64Attribute(metadata.Get(), "frame_index", 0U); } void WriteNodes(const hid_t file, const Domain& domain) { std::vector rows; rows.reserve(domain.Nodes().size()); for (std::size_t index = 0U; index < domain.Nodes().size(); ++index) { const Node& node = domain.Nodes()[index]; rows.push_back( {static_cast(index), node.source_id.instance_name.c_str(), node.source_id.source_label_text.c_str(), {node.coordinates[0U], node.coordinates[1U], node.coordinates[2U]}}); } auto string_type = MakeUtf8StringType(); const hsize_t coordinate_dimensions[] = {3U}; Hdf5Handle file_coordinates{ RequireHdf5Id(H5Tarray_create2(H5T_IEEE_F64LE, 1, coordinate_dimensions), "Unable to create the node coordinate file type."), H5Tclose}; Hdf5Handle memory_coordinates{ RequireHdf5Id( H5Tarray_create2(H5T_NATIVE_DOUBLE, 1, coordinate_dimensions), "Unable to create the node coordinate memory type."), H5Tclose}; Hdf5Handle file_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)), "Unable to create the node file type."), H5Tclose}; Hdf5Handle memory_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)), "Unable to create the node memory type."), H5Tclose}; RequireHdf5(H5Tinsert(file_type.Get(), "internal_node_id", HOFFSET(NodeWriteRow, internal_node_id), H5T_STD_U64LE), "Unable to define the node internal ID field."); RequireHdf5( H5Tinsert(file_type.Get(), "instance_name", HOFFSET(NodeWriteRow, instance_name), string_type.Get()), "Unable to define the node instance field."); RequireHdf5(H5Tinsert(file_type.Get(), "source_label", HOFFSET(NodeWriteRow, source_label), string_type.Get()), "Unable to define the node source-label field."); RequireHdf5( H5Tinsert(file_type.Get(), "coordinates", HOFFSET(NodeWriteRow, coordinates), file_coordinates.Get()), "Unable to define the node coordinate field."); RequireHdf5( H5Tinsert(memory_type.Get(), "internal_node_id", HOFFSET(NodeWriteRow, internal_node_id), H5T_NATIVE_UINT64), "Unable to define the node internal ID memory field."); RequireHdf5( H5Tinsert(memory_type.Get(), "instance_name", HOFFSET(NodeWriteRow, instance_name), string_type.Get()), "Unable to define the node instance memory field."); RequireHdf5(H5Tinsert(memory_type.Get(), "source_label", HOFFSET(NodeWriteRow, source_label), string_type.Get()), "Unable to define the node source-label memory field."); RequireHdf5( H5Tinsert(memory_type.Get(), "coordinates", HOFFSET(NodeWriteRow, coordinates), memory_coordinates.Get()), "Unable to define the node coordinate memory field."); auto dataset = WriteCompoundDataset(file, "/model/nodes", rows.size(), file_type.Get(), memory_type.Get(), rows.data()); WriteStringAttribute(dataset.Get(), "coordinate_system", "global-cartesian"); WriteStringAttribute(dataset.Get(), "units_label", "length"); } void WriteBeamElements(const hid_t file, const Domain& domain, const std::vector& axes) { std::vector rows; rows.reserve(domain.BeamElements().Size()); for (std::size_t index = 0U; index < domain.BeamElements().Size(); ++index) { const auto& element = domain.BeamElements()[index]; ElementWriteRow row{static_cast(index), element.source_id.instance_name.c_str(), element.source_id.source_label_text.c_str(), {static_cast(element.node_indices[0U]), static_cast(element.node_indices[1U])}, {}}; std::copy(axes[index].begin(), axes[index].end(), row.local_axes); rows.push_back(row); } auto string_type = MakeUtf8StringType(); const hsize_t node_dimensions[] = {2U}; const hsize_t axis_dimensions[] = {3U, 3U}; Hdf5Handle file_nodes{ RequireHdf5Id(H5Tarray_create2(H5T_STD_U64LE, 1, node_dimensions), "Unable to create the element connectivity file type."), H5Tclose}; Hdf5Handle memory_nodes{ RequireHdf5Id(H5Tarray_create2(H5T_NATIVE_UINT64, 1, node_dimensions), "Unable to create the element connectivity memory type."), H5Tclose}; Hdf5Handle file_axes{ RequireHdf5Id(H5Tarray_create2(H5T_IEEE_F64LE, 2, axis_dimensions), "Unable to create the local-axis file type."), H5Tclose}; Hdf5Handle memory_axes{ RequireHdf5Id(H5Tarray_create2(H5T_NATIVE_DOUBLE, 2, axis_dimensions), "Unable to create the local-axis memory type."), H5Tclose}; Hdf5Handle file_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)), "Unable to create the element file type."), H5Tclose}; Hdf5Handle memory_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)), "Unable to create the element memory type."), H5Tclose}; const auto insert_fields = [&](const hid_t type, const hid_t integer_type, const hid_t node_type, const hid_t axes_type) { RequireHdf5( H5Tinsert(type, "internal_element_id", HOFFSET(ElementWriteRow, internal_element_id), integer_type), "Unable to define the element internal ID field."); RequireHdf5( H5Tinsert(type, "instance_name", HOFFSET(ElementWriteRow, instance_name), string_type.Get()), "Unable to define the element instance field."); RequireHdf5( H5Tinsert(type, "source_label", HOFFSET(ElementWriteRow, source_label), string_type.Get()), "Unable to define the element source-label field."); RequireHdf5( H5Tinsert(type, "node_internal_ids", HOFFSET(ElementWriteRow, node_internal_ids), node_type), "Unable to define the element connectivity field."); RequireHdf5(H5Tinsert(type, "local_axes", HOFFSET(ElementWriteRow, local_axes), axes_type), "Unable to define the element local-axis field."); }; insert_fields(file_type.Get(), H5T_STD_U64LE, file_nodes.Get(), file_axes.Get()); insert_fields(memory_type.Get(), H5T_NATIVE_UINT64, memory_nodes.Get(), memory_axes.Get()); auto dataset = WriteCompoundDataset(file, "/model/elements", rows.size(), file_type.Get(), memory_type.Get(), rows.data()); WriteStringAttribute(dataset.Get(), "formulation", "B33-3D-Euler-Bernoulli"); } void WriteShellElements(const hid_t file, const Domain& domain) { std::vector rows; rows.reserve(domain.ShellElements().Size()); for (std::size_t index = 0U; index < domain.ShellElements().Size(); ++index) { const auto& element = domain.ShellElements()[index]; rows.push_back({static_cast(index), element.source_id.instance_name.c_str(), element.source_id.source_label_text.c_str(), ShellSourceTypeName(element.source_type), kMitc4InternalFormulation.data(), {static_cast(element.node_indices[0U]), static_cast(element.node_indices[1U]), static_cast(element.node_indices[2U]), static_cast(element.node_indices[3U])}, static_cast(element.section_index), static_cast(element.material_index)}); } auto string_type = MakeUtf8StringType(); const hsize_t node_dimensions[] = {kShellNodeCount}; Hdf5Handle file_nodes{ RequireHdf5Id(H5Tarray_create2(H5T_STD_U64LE, 1, node_dimensions), "Unable to create shell connectivity file type."), H5Tclose}; Hdf5Handle memory_nodes{ RequireHdf5Id(H5Tarray_create2(H5T_NATIVE_UINT64, 1, node_dimensions), "Unable to create shell connectivity memory type."), H5Tclose}; Hdf5Handle file_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellElementWriteRow)), "Unable to create shell element file type."), H5Tclose}; Hdf5Handle memory_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellElementWriteRow)), "Unable to create shell element memory type."), H5Tclose}; const auto insert_fields = [&](const hid_t type, const hid_t integer_type, const hid_t node_type) { RequireHdf5(H5Tinsert(type, "internal_element_id", HOFFSET(ShellElementWriteRow, internal_element_id), integer_type), "Unable to define shell element ID field."); RequireHdf5(H5Tinsert(type, "instance_name", HOFFSET(ShellElementWriteRow, instance_name), string_type.Get()), "Unable to define shell element instance field."); RequireHdf5(H5Tinsert(type, "source_label", HOFFSET(ShellElementWriteRow, source_label), string_type.Get()), "Unable to define shell element source-label field."); RequireHdf5(H5Tinsert(type, "source_element_type", HOFFSET(ShellElementWriteRow, source_element_type), string_type.Get()), "Unable to define shell source-type field."); RequireHdf5(H5Tinsert(type, "internal_formulation", HOFFSET(ShellElementWriteRow, internal_formulation), string_type.Get()), "Unable to define shell formulation field."); RequireHdf5( H5Tinsert(type, "node_internal_ids", HOFFSET(ShellElementWriteRow, node_internal_ids), node_type), "Unable to define shell connectivity field."); RequireHdf5( H5Tinsert(type, "shell_section_internal_id", HOFFSET(ShellElementWriteRow, shell_section_internal_id), integer_type), "Unable to define shell section ID field."); RequireHdf5(H5Tinsert(type, "material_internal_id", HOFFSET(ShellElementWriteRow, material_internal_id), integer_type), "Unable to define shell material ID field."); }; insert_fields(file_type.Get(), H5T_STD_U64LE, file_nodes.Get()); insert_fields(memory_type.Get(), H5T_NATIVE_UINT64, memory_nodes.Get()); auto dataset = WriteCompoundDataset(file, "/model/elements", rows.size(), file_type.Get(), memory_type.Get(), rows.data()); WriteStringAttribute(dataset.Get(), "formulation", "FESA-MITC4"); } void WriteShellMaterials(const hid_t file, const Domain& domain) { std::vector rows; rows.reserve(domain.LinearElasticMaterials().Size()); for (std::size_t index = 0U; index < domain.LinearElasticMaterials().Size(); ++index) { const auto& material = domain.LinearElasticMaterials()[index]; rows.push_back({static_cast(index), material.name.c_str(), material.youngs_modulus, material.poisson_ratio}); } auto string_type = MakeUtf8StringType(); Hdf5Handle file_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellMaterialWriteRow)), "Unable to create shell material file type."), H5Tclose}; Hdf5Handle memory_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellMaterialWriteRow)), "Unable to create shell material memory type."), H5Tclose}; const auto insert_fields = [&](const hid_t type, const hid_t integer_type, const hid_t double_type) { RequireHdf5(H5Tinsert(type, "internal_material_id", HOFFSET(ShellMaterialWriteRow, internal_material_id), integer_type), "Unable to define shell material ID field."); RequireHdf5(H5Tinsert(type, "name", HOFFSET(ShellMaterialWriteRow, name), string_type.Get()), "Unable to define shell material name field."); RequireHdf5( H5Tinsert(type, "E", HOFFSET(ShellMaterialWriteRow, youngs_modulus), double_type), "Unable to define shell material E field."); RequireHdf5( H5Tinsert(type, "nu", HOFFSET(ShellMaterialWriteRow, poisson_ratio), double_type), "Unable to define shell material nu field."); }; insert_fields(file_type.Get(), H5T_STD_U64LE, H5T_IEEE_F64LE); insert_fields(memory_type.Get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE); auto dataset = WriteCompoundDataset(file, "/model/shell/materials", rows.size(), file_type.Get(), memory_type.Get(), rows.data()); WriteStringAttribute(dataset.Get(), "component_unit_dimensions", "force/length^2,1"); } void WriteShellSections(const hid_t file, const Domain& domain) { std::vector source_files; source_files.reserve(domain.ShellSections().Size()); for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) { const auto& section = domain.ShellSections()[index]; source_files.push_back(NormalizedPathString(section.location.file)); } std::vector rows; rows.reserve(domain.ShellSections().Size()); for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) { const auto& section = domain.ShellSections()[index]; rows.push_back({static_cast(index), source_files[index].c_str(), static_cast(section.location.line), section.name.c_str(), static_cast(section.material_index), section.thickness}); } auto string_type = MakeUtf8StringType(); Hdf5Handle file_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellSectionWriteRow)), "Unable to create shell section file type."), H5Tclose}; Hdf5Handle memory_type{ RequireHdf5Id(H5Tcreate(H5T_COMPOUND, sizeof(ShellSectionWriteRow)), "Unable to create shell section memory type."), H5Tclose}; const auto insert_fields = [&](const hid_t type, const hid_t integer_type, const hid_t double_type) { RequireHdf5(H5Tinsert(type, "internal_section_id", HOFFSET(ShellSectionWriteRow, internal_section_id), integer_type), "Unable to define shell section ID field."); RequireHdf5(H5Tinsert(type, "source_file", HOFFSET(ShellSectionWriteRow, source_file), string_type.Get()), "Unable to define shell section source-file field."); RequireHdf5( H5Tinsert(type, "source_line", HOFFSET(ShellSectionWriteRow, source_line), integer_type), "Unable to define shell section source-line field."); RequireHdf5(H5Tinsert(type, "source_elset", HOFFSET(ShellSectionWriteRow, source_elset), string_type.Get()), "Unable to define shell section ELSET field."); RequireHdf5(H5Tinsert(type, "material_internal_id", HOFFSET(ShellSectionWriteRow, material_internal_id), integer_type), "Unable to define shell section material ID field."); RequireHdf5( H5Tinsert(type, "thickness", HOFFSET(ShellSectionWriteRow, thickness), double_type), "Unable to define shell section thickness field."); }; insert_fields(file_type.Get(), H5T_STD_U64LE, H5T_IEEE_F64LE); insert_fields(memory_type.Get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE); auto dataset = WriteCompoundDataset(file, "/model/shell/sections", rows.size(), file_type.Get(), memory_type.Get(), rows.data()); WriteStringAttribute(dataset.Get(), "thickness_unit_dimension", "length"); WriteStringAttribute(dataset.Get(), "layering", "centered-single-layer"); } void WriteShellModelData(const hid_t file, const Domain& domain, const WriterModelData& model_data) { std::vector directors; directors.reserve(domain.Nodes().size() * 3U); std::vector frames; frames.reserve(domain.Nodes().size() * 9U); 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()); } WriteModelDoubleDataset(file, "/model/shell/nodal_director", {static_cast(domain.Nodes().size()), 3U}, directors.data(), directors.size(), "D1,D2,D3", "1,1,1", "global-cartesian", "nodal"); WriteModelDoubleDataset(file, "/model/shell/nodal_frame", {static_cast(domain.Nodes().size()), 3U, 3U}, frames.data(), frames.size(), "X,Y,Z", "1,1,1", "global-cartesian", "nodal-frame"); { Hdf5Handle dataset{ RequireHdf5Id(H5Dopen2(file, "/model/shell/nodal_frame", H5P_DEFAULT), "Unable to reopen the nodal-frame dataset."), H5Dclose}; WriteStringAttribute(dataset.Get(), "axis_names", "A,B,D"); } WriteShellMaterials(file, domain); WriteShellSections(file, domain); const std::vector nodal_dimensions{ static_cast(domain.Nodes().size()), kDofsPerNode}; WriteUint8Dataset(file, "/model/nodal_constraint_mask", nodal_dimensions, model_data.constraint_mask.data(), model_data.constraint_mask.size()); WriteModelDoubleDataset( file, "/model/prescribed_displacement", nodal_dimensions, model_data.prescribed_displacement.data(), model_data.prescribed_displacement.size(), "UX,UY,UZ,URX,URY,URZ", "length,length,length,radian,radian,radian", "global-cartesian", "nodal-prescribed-value"); const double gauss = 1.0 / std::sqrt(3.0); const std::array locations{-gauss, -gauss, gauss, -gauss, gauss, gauss, -gauss, gauss}; WriteModelDoubleDataset(file, "/model/shell/midsurface_locations", {4U, 2U}, locations.data(), locations.size(), "XI,ETA", "1,1", "shell-natural", "midsurface-location"); const std::array section_positions{-1.0, 0.0, 1.0}; WriteModelDoubleDataset(file, "/model/shell/section_positions", {3U}, section_positions.data(), section_positions.size(), "ZETA", "1", "shell-natural", "section-position"); { Hdf5Handle dataset{ RequireHdf5Id( H5Dopen2(file, "/model/shell/section_positions", H5P_DEFAULT), "Unable to reopen shell section positions."), H5Dclose}; WriteStringAttribute(dataset.Get(), "position_names", "BOTTOM,MIDDLE,TOP"); } } void WriteModel(const hid_t file, const Domain& domain, const WriterModelData& model_data) { WriteMetadata(file, domain); WriteNodes(file, domain); if (IsShellDomain(domain)) { WriteShellElements(file, domain); WriteShellModelData(file, domain, model_data); } else { WriteBeamElements(file, domain, model_data.beam_local_axes); } } } // namespace fesa::hdf5_internal