982 lines
47 KiB
C++
982 lines
47 KiB
C++
#include "io/hdf5/hdf5_self_check.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <exception>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "fesa/build_info.h"
|
|
#include "io/hdf5/hdf5_raii.h"
|
|
#include "io/hdf5/hdf5_primitives.h"
|
|
|
|
#include "io/hdf5/mitc4_drilling_diagnostics.h"
|
|
|
|
namespace fesa::hdf5_internal {
|
|
|
|
Hdf5Handle OpenDatasetForCheck(const hid_t file, const char* path) {
|
|
return Hdf5Handle{RequireHdf5Id(H5Dopen2(file, path, H5P_DEFAULT),
|
|
"A mandatory HDF5 dataset is missing."),
|
|
H5Dclose};
|
|
}
|
|
|
|
std::vector<hsize_t> CheckedDimensions(const hid_t dataset) {
|
|
Hdf5Handle space{RequireHdf5Id(H5Dget_space(dataset),
|
|
"Unable to inspect an HDF5 dataspace."),
|
|
H5Sclose};
|
|
const int rank = H5Sget_simple_extent_ndims(space.Get());
|
|
if (rank < 0) {
|
|
throw Hdf5Failure{"Unable to inspect an HDF5 dataset rank."};
|
|
}
|
|
std::vector<hsize_t> dimensions(static_cast<std::size_t>(rank));
|
|
if (rank > 0) {
|
|
RequireHdf5(
|
|
H5Sget_simple_extent_dims(space.Get(), dimensions.data(), nullptr),
|
|
"Unable to inspect HDF5 dataset dimensions.");
|
|
}
|
|
return dimensions;
|
|
}
|
|
|
|
void RequireStringAttribute(const hid_t object, const char* name,
|
|
const std::string& expected) {
|
|
Hdf5Handle attribute{
|
|
RequireHdf5Id(H5Aopen(object, name, H5P_DEFAULT),
|
|
"A mandatory HDF5 string attribute is missing."),
|
|
H5Aclose};
|
|
Hdf5Handle type{RequireHdf5Id(H5Aget_type(attribute.Get()),
|
|
"Unable to inspect an HDF5 attribute type."),
|
|
H5Tclose};
|
|
if (H5Tget_class(type.Get()) != H5T_STRING ||
|
|
H5Tis_variable_str(type.Get()) <= 0 ||
|
|
H5Tget_cset(type.Get()) != H5T_CSET_UTF8) {
|
|
throw Hdf5Failure{"An HDF5 string attribute is not variable-length UTF-8."};
|
|
}
|
|
char* raw = nullptr;
|
|
RequireHdf5(H5Aread(attribute.Get(), type.Get(), &raw),
|
|
"Unable to read an HDF5 string attribute.");
|
|
const std::string actual = raw == nullptr ? std::string{} : std::string{raw};
|
|
if (raw != nullptr) {
|
|
RequireHdf5(H5free_memory(raw), "Unable to release an HDF5 string value.");
|
|
}
|
|
if (actual != expected) {
|
|
throw Hdf5Failure{"HDF5 string attribute '" + std::string{name} +
|
|
"' mismatch: expected '" + expected + "', got '" +
|
|
actual + "'."};
|
|
}
|
|
}
|
|
|
|
void RequireUint64Attribute(const hid_t object, const char* name,
|
|
const std::uint64_t expected) {
|
|
Hdf5Handle attribute{
|
|
RequireHdf5Id(H5Aopen(object, name, H5P_DEFAULT),
|
|
"A mandatory HDF5 integer attribute is missing."),
|
|
H5Aclose};
|
|
Hdf5Handle type{
|
|
RequireHdf5Id(H5Aget_type(attribute.Get()),
|
|
"Unable to inspect an HDF5 integer attribute type."),
|
|
H5Tclose};
|
|
if (H5Tget_class(type.Get()) != H5T_INTEGER ||
|
|
H5Tget_size(type.Get()) != sizeof(std::uint64_t) ||
|
|
H5Tget_sign(type.Get()) != H5T_SGN_NONE ||
|
|
H5Tequal(type.Get(), H5T_STD_U64LE) <= 0) {
|
|
throw Hdf5Failure{"An HDF5 integer attribute is not portable uint64."};
|
|
}
|
|
std::uint64_t actual = 0U;
|
|
RequireHdf5(H5Aread(attribute.Get(), H5T_NATIVE_UINT64, &actual),
|
|
"Unable to read an HDF5 integer attribute.");
|
|
if (actual != expected) {
|
|
throw Hdf5Failure{"An HDF5 integer attribute has the wrong value."};
|
|
}
|
|
}
|
|
|
|
void RequirePortableCompoundMember(const hid_t compound_type,
|
|
const unsigned index,
|
|
const std::string& name) {
|
|
Hdf5Handle member_type{
|
|
RequireHdf5Id(H5Tget_member_type(compound_type, index),
|
|
"Unable to inspect a compound HDF5 field type."),
|
|
H5Tclose};
|
|
const bool is_uint64 =
|
|
name == "internal_node_id" || name == "internal_element_id" ||
|
|
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 == "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 == "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" ||
|
|
name == "name" || name == "source_file" || name == "source_elset" ||
|
|
name == "source" || name == "severity" || name == "code" ||
|
|
name == "file" || name == "keyword" || name == "entity_identity" ||
|
|
name == "message";
|
|
if (is_uint64) {
|
|
if (H5Tget_class(member_type.Get()) != H5T_INTEGER ||
|
|
H5Tget_size(member_type.Get()) != sizeof(std::uint64_t) ||
|
|
H5Tget_sign(member_type.Get()) != H5T_SGN_NONE ||
|
|
H5Tequal(member_type.Get(), H5T_STD_U64LE) <= 0) {
|
|
throw Hdf5Failure{"A compound HDF5 field is not portable uint64."};
|
|
}
|
|
return;
|
|
}
|
|
if (is_float64) {
|
|
if (H5Tget_class(member_type.Get()) != H5T_FLOAT ||
|
|
H5Tget_size(member_type.Get()) != sizeof(double) ||
|
|
H5Tequal(member_type.Get(), H5T_IEEE_F64LE) <= 0) {
|
|
throw Hdf5Failure{"A compound HDF5 field is not float64."};
|
|
}
|
|
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 ||
|
|
H5Tget_cset(member_type.Get()) != H5T_CSET_UTF8) {
|
|
throw Hdf5Failure{"A compound HDF5 field is not UTF-8."};
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (H5Tget_class(member_type.Get()) != H5T_ARRAY) {
|
|
throw Hdf5Failure{"A compound HDF5 array field has the wrong type."};
|
|
}
|
|
const int rank = H5Tget_array_ndims(member_type.Get());
|
|
if (rank <= 0) {
|
|
throw Hdf5Failure{"Unable to inspect a compound HDF5 array rank."};
|
|
}
|
|
std::vector<hsize_t> dimensions(static_cast<std::size_t>(rank));
|
|
RequireHdf5(H5Tget_array_dims2(member_type.Get(), dimensions.data()),
|
|
"Unable to inspect compound HDF5 array dimensions.");
|
|
Hdf5Handle base_type{
|
|
RequireHdf5Id(H5Tget_super(member_type.Get()),
|
|
"Unable to inspect a compound HDF5 array base type."),
|
|
H5Tclose};
|
|
if (name == "node_internal_ids") {
|
|
if ((dimensions != std::vector<hsize_t>{2U} &&
|
|
dimensions != std::vector<hsize_t>{4U}) ||
|
|
H5Tequal(base_type.Get(), H5T_STD_U64LE) <= 0) {
|
|
throw Hdf5Failure{"Element connectivity is not uint64[2] or uint64[4]."};
|
|
}
|
|
} else if (name == "coordinates") {
|
|
if (dimensions != std::vector<hsize_t>{3U} ||
|
|
H5Tequal(base_type.Get(), H5T_IEEE_F64LE) <= 0) {
|
|
throw Hdf5Failure{"Node coordinates are not float64[3]."};
|
|
}
|
|
} else if (name == "local_axes") {
|
|
if (dimensions != std::vector<hsize_t>{3U, 3U} ||
|
|
H5Tequal(base_type.Get(), H5T_IEEE_F64LE) <= 0) {
|
|
throw Hdf5Failure{"Element local axes are not float64[3,3]."};
|
|
}
|
|
} else {
|
|
throw Hdf5Failure{"A compound HDF5 field has an unknown type contract."};
|
|
}
|
|
}
|
|
|
|
void RequireResultAttributes(const hid_t dataset, const char* component_names,
|
|
const char* component_units,
|
|
const char* coordinate_system,
|
|
const char* location) {
|
|
RequireStringAttribute(dataset, "component_names", component_names);
|
|
RequireStringAttribute(dataset, "component_unit_dimensions", component_units);
|
|
RequireStringAttribute(dataset, "coordinate_system", coordinate_system);
|
|
RequireStringAttribute(dataset, "location", location);
|
|
RequireStringAttribute(dataset, "step_name", kStepName);
|
|
RequireUint64Attribute(dataset, "frame_index", 0U);
|
|
}
|
|
|
|
void RequireShellResultIdentity(const hid_t file, const char* path,
|
|
const bool uses_section_positions) {
|
|
auto dataset = OpenDatasetForCheck(file, path);
|
|
RequireStringAttribute(dataset.Get(), "source_element_type_dataset",
|
|
"/model/elements.source_element_type");
|
|
RequireStringAttribute(dataset.Get(), "internal_formulation", "FESA-MITC4");
|
|
RequireStringAttribute(dataset.Get(), "midsurface_location_dataset",
|
|
"/model/shell/midsurface_locations");
|
|
RequireStringAttribute(dataset.Get(), "local_frame_dataset",
|
|
"/steps/Step-1/frames/0/element/shell/local_frame");
|
|
if (uses_section_positions) {
|
|
RequireStringAttribute(dataset.Get(), "section_position_dataset",
|
|
"/model/shell/section_positions");
|
|
}
|
|
}
|
|
|
|
void RequireDoubleDataset(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) {
|
|
auto dataset = OpenDatasetForCheck(file, path);
|
|
if (CheckedDimensions(dataset.Get()) != expected_dimensions) {
|
|
throw Hdf5Failure{"A floating-point HDF5 dataset has the wrong shape."};
|
|
}
|
|
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
|
"Unable to inspect an HDF5 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 result dataset is not IEEE-754 float64 little-endian."};
|
|
}
|
|
RequireResultAttributes(dataset.Get(), component_names, component_units,
|
|
coordinate_system, location);
|
|
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 result dataset 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 result dataset during self-check.");
|
|
}
|
|
if (!std::all_of(values.begin(), values.end(),
|
|
[](const double value) { return std::isfinite(value); })) {
|
|
throw Hdf5Failure{"A result dataset contains a nonfinite value."};
|
|
}
|
|
}
|
|
|
|
void RequireModelDoubleDataset(
|
|
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 std::vector<double>* expected_values = nullptr) {
|
|
auto dataset = OpenDatasetForCheck(file, path);
|
|
if (CheckedDimensions(dataset.Get()) != expected_dimensions) {
|
|
throw Hdf5Failure{"A model floating-point dataset has the wrong shape."};
|
|
}
|
|
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
|
"Unable to inspect a model 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 model dataset is not IEEE-754 float64 little-endian."};
|
|
}
|
|
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);
|
|
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 model dataset 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 model dataset during self-check.");
|
|
}
|
|
if (!std::all_of(values.begin(), values.end(),
|
|
[](const double value) { return std::isfinite(value); })) {
|
|
throw Hdf5Failure{"A model dataset contains a nonfinite value."};
|
|
}
|
|
if (expected_values != nullptr && values != *expected_values) {
|
|
throw Hdf5Failure{"A fixed model dataset has the wrong value order."};
|
|
}
|
|
}
|
|
|
|
void RequireUint8Dataset(const hid_t file, const char* path,
|
|
const std::vector<hsize_t>& expected_dimensions,
|
|
const std::vector<std::uint8_t>& expected_values) {
|
|
auto dataset = OpenDatasetForCheck(file, path);
|
|
if (CheckedDimensions(dataset.Get()) != expected_dimensions) {
|
|
throw Hdf5Failure{"A uint8 HDF5 dataset has the wrong shape."};
|
|
}
|
|
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
|
"Unable to inspect a uint8 dataset 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{"A constraint mask is not portable uint8."};
|
|
}
|
|
std::vector<std::uint8_t> values(expected_values.size());
|
|
if (!values.empty()) {
|
|
RequireHdf5(H5Dread(dataset.Get(), H5T_NATIVE_UINT8, H5S_ALL, H5S_ALL,
|
|
H5P_DEFAULT, values.data()),
|
|
"Unable to read the constraint mask during self-check.");
|
|
}
|
|
if (values != expected_values) {
|
|
throw Hdf5Failure{"The constraint mask has the wrong value order."};
|
|
}
|
|
RequireStringAttribute(dataset.Get(), "component_names",
|
|
"UX,UY,UZ,URX,URY,URZ");
|
|
RequireStringAttribute(dataset.Get(), "value_meaning",
|
|
"0=free,1=constrained");
|
|
}
|
|
|
|
void RequireCompoundDataset(const hid_t file, const char* path,
|
|
const hsize_t expected_rows,
|
|
const std::vector<const char*>& expected_members) {
|
|
auto dataset = OpenDatasetForCheck(file, path);
|
|
if (CheckedDimensions(dataset.Get()) != std::vector<hsize_t>{expected_rows}) {
|
|
throw Hdf5Failure{"A compound HDF5 dataset has the wrong shape."};
|
|
}
|
|
Hdf5Handle type{RequireHdf5Id(H5Dget_type(dataset.Get()),
|
|
"Unable to inspect a compound type."),
|
|
H5Tclose};
|
|
if (H5Tget_class(type.Get()) != H5T_COMPOUND ||
|
|
H5Tget_nmembers(type.Get()) !=
|
|
static_cast<int>(expected_members.size())) {
|
|
throw Hdf5Failure{"A compound HDF5 dataset has the wrong field count."};
|
|
}
|
|
for (std::size_t index = 0U; index < expected_members.size(); ++index) {
|
|
char* raw_name =
|
|
H5Tget_member_name(type.Get(), static_cast<unsigned>(index));
|
|
if (raw_name == nullptr) {
|
|
throw Hdf5Failure{"Unable to inspect a compound HDF5 field name."};
|
|
}
|
|
const std::string actual_name{raw_name};
|
|
RequireHdf5(H5free_memory(raw_name),
|
|
"Unable to release a compound field name.");
|
|
if (actual_name != expected_members[index]) {
|
|
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,
|
|
const AnalysisState& state,
|
|
const std::size_t diagnostic_count,
|
|
const WriterModelData& model_data,
|
|
herr_t (*close_reader)(hid_t)) {
|
|
if (H5Fis_hdf5(path.string().c_str()) <= 0) {
|
|
throw Hdf5Failure{"The temporary output is not an HDF5 file."};
|
|
}
|
|
Hdf5Handle file{
|
|
RequireHdf5Id(H5Fopen(path.string().c_str(), H5F_ACC_RDONLY, H5P_DEFAULT),
|
|
"Unable to reopen the temporary HDF5 file read-only."),
|
|
close_reader};
|
|
Hdf5Handle metadata{
|
|
RequireHdf5Id(H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT),
|
|
"The HDF5 metadata group is missing."),
|
|
H5Gclose};
|
|
if (IsShellDomain(domain)) {
|
|
const auto drilling = ReadDrillingDiagnostics(file.Get());
|
|
if (state.Mitc4Drilling()) {
|
|
ValidateDrillingEvidence(state.Equilibrium(), state.Mitc4Drilling());
|
|
RequireDrilling(drilling.has_value() && drilling->formulation ==
|
|
Mitc4DrillingModeName(state.Mitc4Drilling()->mode) && drilling->frames.size() == 1U);
|
|
CheckDrillingFrame(drilling->frames.front(), 0U, state.Equilibrium(), *state.Mitc4Drilling());
|
|
} else {
|
|
RequireDrilling(!drilling.has_value());
|
|
}
|
|
}
|
|
RequireUint64Attribute(metadata.Get(), "schema_version", 0U);
|
|
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");
|
|
if (IsShellDomain(domain)) {
|
|
RequireStringAttribute(metadata.Get(), "feature_id",
|
|
"linear-static-mitc4-shell");
|
|
RequireStringAttribute(
|
|
metadata.Get(), "coordinate_convention",
|
|
"global-cartesian; shell-local=(e1,e2,e3); positive-thickness=+zeta");
|
|
RequireStringAttribute(metadata.Get(), "internal_formulation",
|
|
"FESA-MITC4");
|
|
RequireStringAttribute(metadata.Get(), "integration_rule",
|
|
"2x2x2-gauss; mitc4-edge-midpoint-shear");
|
|
} else {
|
|
RequireStringAttribute(metadata.Get(), "feature_id",
|
|
"linear-static-3d-euler-beam");
|
|
RequireStringAttribute(metadata.Get(), "coordinate_convention",
|
|
"global-cartesian; beam-local=(t,n1,t-cross-n1)");
|
|
RequireStringAttribute(metadata.Get(), "element_formulation",
|
|
"B33-3D-Euler-Bernoulli");
|
|
}
|
|
RequireStringAttribute(metadata.Get(), "step_name", kStepName);
|
|
RequireUint64Attribute(metadata.Get(), "frame_index", 0U);
|
|
|
|
RequireCompoundDataset(
|
|
file.Get(), "/model/nodes", static_cast<hsize_t>(domain.Nodes().size()),
|
|
{"internal_node_id", "instance_name", "source_label", "coordinates"});
|
|
auto nodes = OpenDatasetForCheck(file.Get(), "/model/nodes");
|
|
RequireStringAttribute(nodes.Get(), "coordinate_system", "global-cartesian");
|
|
RequireStringAttribute(nodes.Get(), "units_label", "length");
|
|
const std::vector<hsize_t> nodal_dimensions = {
|
|
static_cast<hsize_t>(domain.Nodes().size()), kDofsPerNode};
|
|
RequireDoubleDataset(file.Get(), "/steps/Step-1/frames/0/nodal/displacement",
|
|
nodal_dimensions, "UX,UY,UZ,URX,URY,URZ",
|
|
"length,length,length,radian,radian,radian",
|
|
"global-cartesian", "nodal");
|
|
RequireDoubleDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/nodal/reaction", nodal_dimensions,
|
|
"RF1,RF2,RF3,RM1,RM2,RM3",
|
|
"force,force,force,force*length,force*length,force*length",
|
|
"global-cartesian", "nodal");
|
|
|
|
if (IsShellDomain(domain)) {
|
|
RequireCompoundDataset(
|
|
file.Get(), "/model/elements",
|
|
static_cast<hsize_t>(domain.ShellElements().Size()),
|
|
{"internal_element_id", "instance_name", "source_label",
|
|
"source_element_type", "internal_formulation", "node_internal_ids",
|
|
"shell_section_internal_id", "material_internal_id"});
|
|
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
|
RequireStringAttribute(elements.Get(), "formulation", "FESA-MITC4");
|
|
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"});
|
|
|
|
std::vector<double> directors;
|
|
std::vector<double> frames;
|
|
directors.reserve(domain.Nodes().size() * 3U);
|
|
frames.reserve(domain.Nodes().size() * 9U);
|
|
hdf5_internal::AppendShellReferenceFrames(domain, directors, frames);
|
|
RequireModelDoubleDataset(file.Get(), "/model/shell/nodal_director",
|
|
{static_cast<hsize_t>(domain.Nodes().size()), 3U},
|
|
"D1,D2,D3", "1,1,1", "global-cartesian", "nodal",
|
|
&directors);
|
|
RequireModelDoubleDataset(
|
|
file.Get(), "/model/shell/nodal_frame",
|
|
{static_cast<hsize_t>(domain.Nodes().size()), 3U, 3U}, "X,Y,Z", "1,1,1",
|
|
"global-cartesian", "nodal-frame", &frames);
|
|
auto nodal_frame =
|
|
OpenDatasetForCheck(file.Get(), "/model/shell/nodal_frame");
|
|
RequireStringAttribute(nodal_frame.Get(), "axis_names", "A,B,D");
|
|
RequireUint8Dataset(file.Get(), "/model/nodal_constraint_mask",
|
|
nodal_dimensions, model_data.constraint_mask);
|
|
RequireModelDoubleDataset(file.Get(), "/model/prescribed_displacement",
|
|
nodal_dimensions, "UX,UY,UZ,URX,URY,URZ",
|
|
"length,length,length,radian,radian,radian",
|
|
"global-cartesian", "nodal-prescribed-value",
|
|
&model_data.prescribed_displacement);
|
|
const double gauss = 1.0 / std::sqrt(3.0);
|
|
const std::vector<double> locations{-gauss, -gauss, gauss, -gauss,
|
|
gauss, gauss, -gauss, gauss};
|
|
RequireModelDoubleDataset(file.Get(), "/model/shell/midsurface_locations",
|
|
{4U, 2U}, "XI,ETA", "1,1", "shell-natural",
|
|
"midsurface-location", &locations);
|
|
const std::vector<double> section_positions{-1.0, 0.0, 1.0};
|
|
RequireModelDoubleDataset(file.Get(), "/model/shell/section_positions",
|
|
{3U}, "ZETA", "1", "shell-natural",
|
|
"section-position", §ion_positions);
|
|
auto positions =
|
|
OpenDatasetForCheck(file.Get(), "/model/shell/section_positions");
|
|
RequireStringAttribute(positions.Get(), "position_names",
|
|
"BOTTOM,MIDDLE,TOP");
|
|
|
|
const hsize_t element_count =
|
|
static_cast<hsize_t>(domain.ShellElements().Size());
|
|
const std::string shell_root = std::string{kStepRoot} + "/element/shell";
|
|
RequireDoubleDataset(file.Get(), (shell_root + "/local_frame").c_str(),
|
|
{element_count, 4U, 3U, 3U}, "X,Y,Z", "1,1,1",
|
|
"global-cartesian", "shell-local-frame");
|
|
auto local_frame =
|
|
OpenDatasetForCheck(file.Get(), (shell_root + "/local_frame").c_str());
|
|
RequireStringAttribute(local_frame.Get(), "axis_names", "E1,E2,E3");
|
|
RequireStringAttribute(local_frame.Get(), "internal_formulation",
|
|
"FESA-MITC4");
|
|
RequireStringAttribute(local_frame.Get(), "source_element_type_dataset",
|
|
"/model/elements.source_element_type");
|
|
RequireStringAttribute(local_frame.Get(), "midsurface_location_dataset",
|
|
"/model/shell/midsurface_locations");
|
|
RequireDoubleDataset(
|
|
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");
|
|
RequireShellResultIdentity(
|
|
file.Get(), (shell_root + "/generalized_strain").c_str(), false);
|
|
RequireDoubleDataset(
|
|
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");
|
|
RequireShellResultIdentity(
|
|
file.Get(), (shell_root + "/section_resultant").c_str(), false);
|
|
RequireDoubleDataset(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");
|
|
RequireShellResultIdentity(file.Get(), (shell_root + "/stress").c_str(),
|
|
true);
|
|
RequireDoubleDataset(file.Get(), "/steps/Step-1/frames/0/global/energy",
|
|
{1U}, "PHYSICAL_STRAIN_ENERGY", "force*length",
|
|
"global", "global");
|
|
RequireDoubleDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/global/equilibrium", {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");
|
|
RequireDoubleDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/global/verification_metrics", {3U},
|
|
"FREE_RESIDUAL_NORMALIZED,FORCE_BALANCE_NORMALIZED,MOMENT_BALANCE_"
|
|
"NORMALIZED",
|
|
"1,1,1", "global", "verification");
|
|
auto metrics = OpenDatasetForCheck(
|
|
file.Get(), "/steps/Step-1/frames/0/global/verification_metrics");
|
|
RequireStringAttribute(
|
|
metrics.Get(), "metric_definition_ids",
|
|
"free-residual-l2-over-max-free-force-l2,force-balance-l2-over-max-"
|
|
"force-sum,moment-balance-l2-over-max-moment-sum");
|
|
RequireStringAttribute(metrics.Get(), "acceptance_thresholds",
|
|
"1e-10,1e-10,1e-10");
|
|
for (const char* forbidden :
|
|
{"/steps/Step-1/frames/0/element/shell/drilling",
|
|
"/steps/Step-1/frames/0/element/shell/drilling_energy",
|
|
"/steps/Step-1/frames/0/element/shell/S33",
|
|
"/steps/Step-1/frames/0/element/shell/S13",
|
|
"/steps/Step-1/frames/0/element/shell/S23"}) {
|
|
if (H5Lexists(file.Get(), forbidden, H5P_DEFAULT) != 0) {
|
|
throw Hdf5Failure{"A forbidden shell result path exists."};
|
|
}
|
|
}
|
|
} else {
|
|
RequireCompoundDataset(file.Get(), "/model/elements",
|
|
static_cast<hsize_t>(domain.BeamElements().Size()),
|
|
{"internal_element_id", "instance_name",
|
|
"source_label", "node_internal_ids", "local_axes"});
|
|
auto elements = OpenDatasetForCheck(file.Get(), "/model/elements");
|
|
RequireStringAttribute(elements.Get(), "formulation",
|
|
"B33-3D-Euler-Bernoulli");
|
|
const std::vector<hsize_t> end_dimensions = {
|
|
static_cast<hsize_t>(domain.BeamElements().Size()), kEndpointCount,
|
|
kEndActionComponentCount};
|
|
const std::vector<hsize_t> generalized_dimensions = {
|
|
static_cast<hsize_t>(domain.BeamElements().Size()), kGaussPointCount,
|
|
kGeneralizedComponentCount};
|
|
RequireDoubleDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/element/end_force_local",
|
|
end_dimensions, "FX,FY,FZ,MX,MY,MZ",
|
|
"force,force,force,force*length,force*length,force*length",
|
|
"beam-local", "endpoint-outward-action");
|
|
RequireDoubleDataset(file.Get(),
|
|
"/steps/Step-1/frames/0/element/section_resultant",
|
|
generalized_dimensions, "N,T,My,Mz",
|
|
"force,force*length,force*length,force*length",
|
|
"beam-local", "endpoint-positive-local-x-section-cut");
|
|
RequireDoubleDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/element/generalized_strain",
|
|
generalized_dimensions, "epsilon0,kappa_x,kappa_y,kappa_z",
|
|
"1,1/length,1/length,1/length", "beam-local", "integration-point");
|
|
RequireDoubleDataset(file.Get(),
|
|
"/steps/Step-1/frames/0/element/generalized_resultant",
|
|
generalized_dimensions, "N,T,My,Mz",
|
|
"force,force*length,force*length,force*length",
|
|
"beam-local", "integration-point");
|
|
RequireCompoundDataset(
|
|
file.Get(), "/steps/Step-1/frames/0/element/stress_s11",
|
|
static_cast<hsize_t>(state.StressResults().size()),
|
|
{"internal_element_id", "gauss_point_index", "section_point_index",
|
|
"x1", "x2", "source", "S11"});
|
|
auto stress = OpenDatasetForCheck(
|
|
file.Get(), "/steps/Step-1/frames/0/element/stress_s11");
|
|
RequireResultAttributes(stress.Get(), "S11", "force/length^2", "beam-local",
|
|
"section-point");
|
|
}
|
|
RequireCompoundDataset(file.Get(), "/diagnostics",
|
|
static_cast<hsize_t>(diagnostic_count),
|
|
{"severity", "code", "file", "line", "keyword",
|
|
"entity_identity", "message"});
|
|
RequireHdf5(file.CloseChecked(),
|
|
"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,
|
|
herr_t (*close_reader)(hid_t)) {
|
|
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."),
|
|
close_reader};
|
|
Hdf5Handle metadata{
|
|
RequireHdf5Id(H5Gopen2(file.Get(), "/metadata", H5P_DEFAULT),
|
|
"The nonlinear metadata group is missing."),
|
|
H5Gclose};
|
|
RequireDrilling(!state.AcceptedFrames().empty());
|
|
const auto drilling = ReadDrillingDiagnostics(file.Get());
|
|
const auto& expected_drilling = state.AcceptedFrames().front().mitc4_drilling;
|
|
if (expected_drilling) {
|
|
RequireDrilling(drilling.has_value() && drilling->formulation ==
|
|
Mitc4DrillingModeName(expected_drilling->mode) &&
|
|
drilling->frames.size() == state.AcceptedFrames().size());
|
|
for (std::size_t i = 0; i < drilling->frames.size(); ++i) {
|
|
const auto& frame = state.AcceptedFrames()[i];
|
|
ValidateDrillingEvidence(frame.global_equilibrium, frame.mitc4_drilling);
|
|
RequireDrilling(frame.mitc4_drilling->mode == expected_drilling->mode);
|
|
CheckDrillingFrame(drilling->frames[i], frame.frame_index, frame.global_equilibrium, *frame.mitc4_drilling);
|
|
}
|
|
} else {
|
|
RequireDrilling(!drilling.has_value());
|
|
for (const auto& frame : state.AcceptedFrames()) RequireDrilling(!frame.mitc4_drilling);
|
|
}
|
|
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-balance-error", &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,
|
|
herr_t (*close_reader)(hid_t)) {
|
|
try {
|
|
SelfCheckFile(path, domain, state, diagnostic_count, model_data, close_reader);
|
|
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());
|
|
}
|
|
}
|
|
|
|
Status NonlinearSelfCheck(const std::filesystem::path& path,
|
|
const Domain& domain,
|
|
const NonlinearAnalysisState& state,
|
|
const std::size_t diagnostic_count,
|
|
herr_t (*close_reader)(hid_t)) {
|
|
try {
|
|
NonlinearSelfCheckFile(path, domain, state, diagnostic_count, close_reader);
|
|
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
|