feat(nonlinear-static-newton-raphson): step 9 - nonlinear-hdf5-schema-v1-writer

This commit is contained in:
KOKO\Mimi
2026-08-24 15:19:07 +09:00
parent 0978b019ce
commit dd3c6191c8
10 changed files with 2146 additions and 4 deletions
+1
View File
@@ -33,6 +33,7 @@ add_library(
io/abaqus/topology_mapper.cpp
io/hdf5/hdf5_atomic_file.cpp
io/hdf5/hdf5_model_writer.cpp
io/hdf5/nonlinear_hdf5_results_writer.cpp
io/hdf5/hdf5_primitives.cpp
io/hdf5/hdf5_result_writer.cpp
io/hdf5/hdf5_results_writer.cpp
+28
View File
@@ -138,6 +138,34 @@ void WriteUint64Attribute(const hid_t object, const char* name,
"Unable to write an HDF5 integer attribute.");
}
void WriteUint8Attribute(const hid_t object, const char* name,
const std::uint8_t value) {
Hdf5Handle space{RequireHdf5Id(H5Screate(H5S_SCALAR),
"Unable to create an attribute space."),
H5Sclose};
Hdf5Handle attribute{
RequireHdf5Id(H5Acreate2(object, name, H5T_STD_U8LE, space.Get(),
H5P_DEFAULT, H5P_DEFAULT),
"Unable to create an HDF5 uint8 attribute."),
H5Aclose};
RequireHdf5(H5Awrite(attribute.Get(), H5T_NATIVE_UINT8, &value),
"Unable to write an HDF5 uint8 attribute.");
}
void WriteDoubleAttribute(const hid_t object, const char* name,
const double value) {
Hdf5Handle space{RequireHdf5Id(H5Screate(H5S_SCALAR),
"Unable to create an attribute space."),
H5Sclose};
Hdf5Handle attribute{
RequireHdf5Id(H5Acreate2(object, name, H5T_IEEE_F64LE, space.Get(),
H5P_DEFAULT, H5P_DEFAULT),
"Unable to create an HDF5 float64 attribute."),
H5Aclose};
RequireHdf5(H5Awrite(attribute.Get(), H5T_NATIVE_DOUBLE, &value),
"Unable to write an HDF5 float64 attribute.");
}
Hdf5Handle CreateGroup(const hid_t file, const char* path) {
Hdf5Handle link_properties{RequireHdf5Id(H5Pcreate(H5P_LINK_CREATE),
"Unable to create link properties."),
+2
View File
@@ -97,6 +97,8 @@ Hdf5Handle MakeUtf8StringType();
void WriteStringAttribute(hid_t object, const char* name,
const std::string& value);
void WriteUint64Attribute(hid_t object, const char* name, std::uint64_t value);
void WriteUint8Attribute(hid_t object, const char* name, std::uint8_t value);
void WriteDoubleAttribute(hid_t object, const char* name, double value);
Hdf5Handle CreateGroup(hid_t file, const char* path);
Hdf5Handle CreateDatasetSpace(const std::vector<hsize_t>& dimensions);
void WriteResultAttributes(hid_t dataset, const std::string& component_names,
+348 -4
View File
@@ -59,7 +59,9 @@ void RequireStringAttribute(const hid_t object, const char* name,
RequireHdf5(H5free_memory(raw), "Unable to release an HDF5 string value.");
}
if (actual != expected) {
throw Hdf5Failure{"An HDF5 string attribute has the wrong value."};
throw Hdf5Failure{"HDF5 string attribute '" + std::string{name} +
"' mismatch: expected '" + expected + "', got '" +
actual + "'."};
}
}
@@ -99,9 +101,13 @@ void RequirePortableCompoundMember(const hid_t compound_type,
name == "internal_material_id" || name == "internal_section_id" ||
name == "shell_section_internal_id" || name == "material_internal_id" ||
name == "source_line" || name == "gauss_point_index" ||
name == "section_point_index" || name == "line";
name == "section_point_index" || name == "line" ||
name == "frame_index" || name == "increment_index" ||
name == "iteration_count";
const bool is_float64 = name == "x1" || name == "x2" || name == "S11" ||
name == "E" || name == "nu" || name == "thickness";
name == "E" || name == "nu" || name == "thickness" ||
name == "load_factor";
const bool is_uint8 = name == "is_factor_one";
const bool is_string =
name == "instance_name" || name == "source_label" ||
name == "source_element_type" || name == "internal_formulation" ||
@@ -126,6 +132,15 @@ void RequirePortableCompoundMember(const hid_t compound_type,
}
return;
}
if (is_uint8) {
if (H5Tget_class(member_type.Get()) != H5T_INTEGER ||
H5Tget_size(member_type.Get()) != sizeof(std::uint8_t) ||
H5Tget_sign(member_type.Get()) != H5T_SGN_NONE ||
H5Tequal(member_type.Get(), H5T_STD_U8LE) <= 0) {
throw Hdf5Failure{"A compound HDF5 field is not portable uint8."};
}
return;
}
if (is_string) {
if (H5Tget_class(member_type.Get()) != H5T_STRING ||
H5Tis_variable_str(member_type.Get()) <= 0 ||
@@ -342,13 +357,159 @@ void RequireCompoundDataset(const hid_t file, const char* path,
RequireHdf5(H5free_memory(raw_name),
"Unable to release a compound field name.");
if (actual_name != expected_members[index]) {
throw Hdf5Failure{"A compound HDF5 field has the wrong identity."};
throw Hdf5Failure{"Compound HDF5 field identity mismatch: expected '" +
std::string{expected_members[index]} + "', got '" +
actual_name + "'."};
}
RequirePortableCompoundMember(type.Get(), static_cast<unsigned>(index),
expected_members[index]);
}
}
void RequireUint8Attribute(const hid_t object, const char* name,
const std::uint8_t expected) {
Hdf5Handle attribute{
RequireHdf5Id(H5Aopen(object, name, H5P_DEFAULT),
"A mandatory HDF5 uint8 attribute is missing."),
H5Aclose};
Hdf5Handle type{
RequireHdf5Id(H5Aget_type(attribute.Get()),
"Unable to inspect an HDF5 uint8 attribute type."),
H5Tclose};
if (H5Tget_class(type.Get()) != H5T_INTEGER ||
H5Tget_size(type.Get()) != sizeof(std::uint8_t) ||
H5Tget_sign(type.Get()) != H5T_SGN_NONE ||
H5Tequal(type.Get(), H5T_STD_U8LE) <= 0) {
throw Hdf5Failure{"An HDF5 attribute is not portable uint8."};
}
std::uint8_t actual = 0U;
RequireHdf5(H5Aread(attribute.Get(), H5T_NATIVE_UINT8, &actual),
"Unable to read an HDF5 uint8 attribute.");
if (actual != expected) {
throw Hdf5Failure{"An HDF5 uint8 attribute has the wrong value."};
}
}
void RequireDoubleAttribute(const hid_t object, const char* name,
const double expected) {
Hdf5Handle attribute{
RequireHdf5Id(H5Aopen(object, name, H5P_DEFAULT),
"A mandatory HDF5 float64 attribute is missing."),
H5Aclose};
Hdf5Handle type{
RequireHdf5Id(H5Aget_type(attribute.Get()),
"Unable to inspect an HDF5 float64 attribute type."),
H5Tclose};
if (H5Tget_class(type.Get()) != H5T_FLOAT ||
H5Tget_size(type.Get()) != sizeof(double) ||
H5Tequal(type.Get(), H5T_IEEE_F64LE) <= 0) {
throw Hdf5Failure{"An HDF5 attribute is not portable float64."};
}
double actual = 0.0;
RequireHdf5(H5Aread(attribute.Get(), H5T_NATIVE_DOUBLE, &actual),
"Unable to read an HDF5 float64 attribute.");
if (actual != expected) {
throw Hdf5Failure{"An HDF5 float64 attribute has the wrong value."};
}
}
void RequireSchemaV1NumericDataset(
const hid_t file, const char* path,
const std::vector<hsize_t>& expected_dimensions,
const char* component_names, const char* component_units,
const char* coordinate_system, const char* location,
const AcceptedFrameState* frame) {
auto dataset = OpenDatasetForCheck(file, path);
if (CheckedDimensions(dataset.Get()) != expected_dimensions) {
throw Hdf5Failure{"A schema-v1 numeric dataset has the wrong shape."};
}
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
"Unable to inspect a schema-v1 dataset type."),
H5Tclose};
if (H5Tget_class(type.Get()) != H5T_FLOAT ||
H5Tget_size(type.Get()) != sizeof(double) ||
H5Tequal(type.Get(), H5T_IEEE_F64LE) <= 0) {
throw Hdf5Failure{"A schema-v1 numeric dataset is not float64."};
}
RequireUint64Attribute(dataset.Get(), "schema_version", 1U);
RequireStringAttribute(dataset.Get(), "component_names", component_names);
RequireStringAttribute(dataset.Get(), "component_unit_dimensions",
component_units);
RequireStringAttribute(dataset.Get(), "coordinate_system",
coordinate_system);
RequireStringAttribute(dataset.Get(), "location", location);
if (frame != nullptr) {
RequireStringAttribute(dataset.Get(), "step_name", "Step-1");
RequireUint64Attribute(dataset.Get(), "frame_index", frame->frame_index);
RequireUint64Attribute(dataset.Get(), "increment_index",
frame->increment_index);
RequireDoubleAttribute(dataset.Get(), "load_factor", frame->load_factor);
RequireUint8Attribute(dataset.Get(), "is_factor_one",
frame->is_factor_one ? 1U : 0U);
}
std::size_t value_count = 1U;
for (const hsize_t dimension : expected_dimensions) {
std::size_t next = 0U;
if (!SizeProductFits(value_count, static_cast<std::size_t>(dimension),
next)) {
throw Hdf5Failure{"A schema-v1 numeric shape overflows size_t."};
}
value_count = next;
}
std::vector<double> values(value_count);
if (!values.empty()) {
RequireHdf5(H5Dread(dataset.Get(), H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
H5P_DEFAULT, values.data()),
"Unable to read a schema-v1 numeric dataset.");
}
if (!std::all_of(values.begin(), values.end(),
[](const double value) { return std::isfinite(value); })) {
throw Hdf5Failure{"A schema-v1 numeric dataset contains a nonfinite value."};
}
}
void RequireSchemaV1MaskDataset(const hid_t file,
const std::vector<hsize_t>& dimensions) {
auto dataset = OpenDatasetForCheck(file, "/model/nodal_constraint_mask");
if (CheckedDimensions(dataset.Get()) != dimensions) {
throw Hdf5Failure{"The schema-v1 constraint mask has the wrong shape."};
}
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
"Unable to inspect the constraint mask type."),
H5Tclose};
if (H5Tget_class(type.Get()) != H5T_INTEGER ||
H5Tget_size(type.Get()) != sizeof(std::uint8_t) ||
H5Tget_sign(type.Get()) != H5T_SGN_NONE ||
H5Tequal(type.Get(), H5T_STD_U8LE) <= 0) {
throw Hdf5Failure{"The schema-v1 constraint mask is not uint8."};
}
RequireUint64Attribute(dataset.Get(), "schema_version", 1U);
RequireStringAttribute(dataset.Get(), "component_names", "UX,UY,UZ,URX,URY,URZ");
RequireStringAttribute(dataset.Get(), "component_unit_dimensions",
"1,1,1,1,1,1");
RequireStringAttribute(dataset.Get(), "coordinate_system", "global-cartesian");
RequireStringAttribute(dataset.Get(), "location", "nodal-constraint");
RequireStringAttribute(dataset.Get(), "value_meaning", "0=free,1=constrained");
}
void RequireSchemaV1ShellIdentity(const hid_t file, const char* path,
const bool has_section_positions,
const std::string& frame_root) {
auto dataset = OpenDatasetForCheck(file, path);
RequireStringAttribute(dataset.Get(), "source_element_type_dataset",
"/model/elements.source_element_type");
RequireStringAttribute(dataset.Get(), "internal_formulation",
"FESA-MITC4-total-lagrangian");
RequireStringAttribute(dataset.Get(), "midsurface_location_dataset",
"/model/shell/midsurface_locations");
RequireStringAttribute(dataset.Get(), "local_frame_dataset",
frame_root + "/element/shell/local_frame");
if (has_section_positions) {
RequireStringAttribute(dataset.Get(), "section_position_dataset",
"/model/shell/section_positions");
}
}
/// @brief Reopens the closed candidate and verifies its required external
/// schema.
void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
@@ -594,6 +755,175 @@ void SelfCheckFile(const std::filesystem::path& path, const Domain& domain,
"Unable to close the read-only HDF5 schema self-check handle.");
}
void NonlinearSelfCheckFile(const std::filesystem::path& path,
const Domain& domain,
const NonlinearAnalysisState& state,
const std::size_t diagnostic_count) {
if (H5Fis_hdf5(path.string().c_str()) <= 0) {
throw Hdf5Failure{"The nonlinear candidate is not an HDF5 file."};
}
Hdf5Handle file{
RequireHdf5Id(H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT),
"Unable to reopen nonlinear candidate read-only."),
H5Fclose};
Hdf5Handle metadata{
RequireHdf5Id(H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT),
"The nonlinear metadata group is missing."),
H5Gclose};
RequireUint64Attribute(metadata.Get(), "schema_version", 1U);
RequireStringAttribute(metadata.Get(), "feature_id",
"nonlinear-static-newton-raphson");
RequireStringAttribute(metadata.Get(), "solver_version",
std::string{SolverVersion()});
RequireStringAttribute(metadata.Get(), "source_input_identity",
SourceInputIdentity(domain));
RequireStringAttribute(metadata.Get(), "unit_system_label",
"user-consistent-unspecified");
RequireStringAttribute(
metadata.Get(), "coordinate_convention",
"global-cartesian; shell-local=fixed-initial-reference; "
"positive-thickness=+zeta");
RequireStringAttribute(metadata.Get(), "analysis_procedure",
"nonlinear-static-full-newton");
RequireStringAttribute(metadata.Get(), "internal_formulation",
"FESA-MITC4-total-lagrangian");
RequireStringAttribute(metadata.Get(), "integration_rule",
"2x2x2-gauss; mitc4-edge-midpoint-shear");
RequireStringAttribute(metadata.Get(), "step_name", "Step-1");
RequireStringAttribute(metadata.Get(), "frame_indexing",
"accepted-increment-order-zero-based");
RequireUint8Attribute(metadata.Get(), "factor_zero_is_frame", 0U);
RequireDoubleAttribute(metadata.Get(), "factor_zero_load_factor", 0.0);
const hsize_t node_count = static_cast<hsize_t>(domain.Nodes().size());
const hsize_t element_count =
static_cast<hsize_t>(domain.ShellElements().Size());
const std::vector<hsize_t> nodal_dimensions{node_count, kDofsPerNode};
RequireCompoundDataset(file.Get(), "/model/nodes", node_count,
{"internal_node_id", "instance_name", "source_label",
"coordinates"});
RequireCompoundDataset(
file.Get(), "/model/elements", element_count,
{"internal_element_id", "instance_name", "source_label",
"source_element_type", "internal_formulation", "node_internal_ids",
"shell_section_internal_id", "material_internal_id"});
RequireCompoundDataset(file.Get(), "/model/shell/materials",
static_cast<hsize_t>(domain.LinearElasticMaterials().Size()),
{"internal_material_id", "name", "E", "nu"});
RequireCompoundDataset(
file.Get(), "/model/shell/sections",
static_cast<hsize_t>(domain.ShellSections().Size()),
{"internal_section_id", "source_file", "source_line", "source_elset",
"material_internal_id", "thickness"});
RequireSchemaV1NumericDataset(
file.Get(), "/model/shell/nodal_director", {node_count, 3U}, "D1,D2,D3",
"1,1,1", "global-cartesian", "nodal-initial-director", nullptr);
RequireSchemaV1NumericDataset(
file.Get(), "/model/shell/nodal_frame", {node_count, 3U, 3U}, "X,Y,Z",
"1,1,1", "global-cartesian", "nodal-initial-frame", nullptr);
RequireSchemaV1MaskDataset(file.Get(), nodal_dimensions);
RequireSchemaV1NumericDataset(
file.Get(), "/model/prescribed_displacement_start", nodal_dimensions,
"UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal-prescribed-start", nullptr);
RequireSchemaV1NumericDataset(
file.Get(), "/model/prescribed_displacement_target", nodal_dimensions,
"UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal-prescribed-target", nullptr);
RequireSchemaV1NumericDataset(file.Get(), "/model/shell/midsurface_locations",
{4U, 2U}, "XI,ETA", "1,1", "shell-natural",
"midsurface-location", nullptr);
RequireSchemaV1NumericDataset(file.Get(), "/model/shell/section_positions",
{3U}, "ZETA", "1", "shell-natural",
"section-position", nullptr);
const auto& frames = state.AcceptedFrames();
RequireCompoundDataset(file.Get(), "/steps/Step-1/accepted_frames",
static_cast<hsize_t>(frames.size()),
{"frame_index", "increment_index", "load_factor",
"iteration_count", "is_factor_one"});
if (H5Lexists(file.Get(), "/steps/Step-1/trial_history", H5P_DEFAULT) != 0) {
throw Hdf5Failure{"Schema v1 must not contain nonlinear trial history."};
}
for (const auto& frame : frames) {
const std::string root = "/steps/Step-1/frames/" +
std::to_string(frame.frame_index);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/displacement").c_str(), nodal_dimensions,
"UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/triad").c_str(), {node_count, 3U, 3U},
"X,Y,Z", "1,1,1", "global-cartesian", "nodal-triad", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/director").c_str(), {node_count, 3U},
"D1,D2,D3", "1,1,1", "global-cartesian", "nodal-director", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/generalized_internal_force").c_str(),
nodal_dimensions, "FI1,FI2,FI3,MI_PHI1,MI_PHI2,MI_PHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/generalized_external_force").c_str(),
nodal_dimensions, "FE1,FE2,FE3,ME_PHI1,ME_PHI2,ME_PHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/generalized_residual").c_str(),
nodal_dimensions, "RU1,RU2,RU3,RPHI1,RPHI2,RPHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/nodal/reaction").c_str(), nodal_dimensions,
"RF1,RF2,RF3,RM1,RM2,RM3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", &frame);
const std::string shell_root = root + "/element/shell";
RequireSchemaV1NumericDataset(
file.Get(), (shell_root + "/local_frame").c_str(),
{element_count, 4U, 3U, 3U}, "X,Y,Z", "1,1,1", "global-cartesian",
"shell-local-frame", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (shell_root + "/generalized_strain").c_str(),
{element_count, 4U, 8U}, "E11,E22,G12,K11,K22,K12,G13,G23",
"1,1,1,1/length,1/length,1/length,1,1", "shell-local", "midsurface",
&frame);
RequireSchemaV1ShellIdentity(file.Get(),
(shell_root + "/generalized_strain").c_str(),
false, root);
RequireSchemaV1NumericDataset(
file.Get(), (shell_root + "/section_resultant").c_str(),
{element_count, 4U, 8U}, "N11,N22,N12,M11,M22,M12,Q13,Q23",
"force/length,force/length,force/length,force,force,force,force/length,"
"force/length",
"shell-local", "midsurface", &frame);
RequireSchemaV1ShellIdentity(file.Get(),
(shell_root + "/section_resultant").c_str(),
false, root);
RequireSchemaV1NumericDataset(
file.Get(), (shell_root + "/stress").c_str(),
{element_count, 4U, 3U, 3U}, "S11,S22,S12",
"force/length^2,force/length^2,force/length^2", "shell-local",
"section-position", &frame);
RequireSchemaV1ShellIdentity(file.Get(), (shell_root + "/stress").c_str(),
true, root);
RequireSchemaV1NumericDataset(file.Get(), (root + "/global/energy").c_str(),
{1U}, "PHYSICAL_STRAIN_ENERGY", "force*length",
"global", "global", &frame);
RequireSchemaV1NumericDataset(
file.Get(), (root + "/global/equilibrium").c_str(), {6U},
"FORCE_1,FORCE_2,FORCE_3,MOMENT_1,MOMENT_2,MOMENT_3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "global-origin", &frame);
}
RequireCompoundDataset(file.Get(), "/diagnostics",
static_cast<hsize_t>(diagnostic_count),
{"severity", "code", "file", "line", "keyword",
"entity_identity", "message"});
RequireHdf5(file.CloseChecked(),
"Unable to close nonlinear read-only self-check file.");
}
Status SelfCheck(const std::filesystem::path& path, const Domain& domain,
const AnalysisState& state, const std::size_t diagnostic_count,
const WriterModelData& model_data) {
@@ -607,4 +937,18 @@ Status SelfCheck(const std::filesystem::path& path, const Domain& domain,
}
}
Status NonlinearSelfCheck(const std::filesystem::path& path,
const Domain& domain,
const NonlinearAnalysisState& state,
const std::size_t diagnostic_count) {
try {
NonlinearSelfCheckFile(path, domain, state, diagnostic_count);
return Status::Ok();
} catch (const Hdf5Failure& failure) {
return OutputFailure("hdf5-write-failure", failure.what());
} catch (const std::exception& failure) {
return OutputFailure("hdf5-write-failure", failure.what());
}
}
} // namespace fesa::hdf5_internal
+7
View File
@@ -5,6 +5,7 @@
#include <filesystem>
#include "fesa/analysis/analysis_state.h"
#include "fesa/analysis/nonlinear_analysis_state.h"
#include "fesa/core/status.h"
#include "fesa/model/domain.h"
#include "io/hdf5/hdf5_primitives.h"
@@ -16,6 +17,12 @@ Status SelfCheck(const std::filesystem::path& path, const Domain& domain,
const AnalysisState& state, std::size_t diagnostic_count,
const WriterModelData& model_data);
/// @brief Reopens and validates the complete closed schema-v1 candidate.
Status NonlinearSelfCheck(const std::filesystem::path& path,
const Domain& domain,
const NonlinearAnalysisState& state,
std::size_t diagnostic_count);
} // namespace fesa::hdf5_internal
#endif // FESA_IO_HDF5_HDF5_SELF_CHECK_H_
File diff suppressed because it is too large Load Diff