Files
FESADev/src/fesa/io/hdf5/nonlinear_hdf5_results_writer.cpp
T

1114 lines
53 KiB
C++

#include "fesa/io/hdf5/nonlinear_hdf5_results_writer.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <filesystem>
#include <limits>
#include <string>
#include <utility>
#include <vector>
#include "fesa/analysis/analysis_model.h"
#include "fesa/build_info.h"
#include "fesa/elements/finite_rotation.h"
#include "fesa/fem/dof_manager.h"
#include "fesa/math/vector3.h"
#include "io/hdf5/hdf5_atomic_file.h"
#include "io/hdf5/hdf5_primitives.h"
#include "io/hdf5/hdf5_raii.h"
#include "io/hdf5/hdf5_self_check.h"
namespace fesa {
namespace {
using hdf5_internal::Hdf5Failure;
using hdf5_internal::Hdf5Handle;
constexpr std::size_t kDofsPerNode = 6U;
constexpr std::size_t kShellLocationsPerElement = 4U;
constexpr std::size_t kSectionPositions = 3U;
struct NonlinearModelData {
std::vector<std::uint8_t> constraint_mask;
std::vector<double> prescribed_start;
std::vector<double> prescribed_target;
};
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;
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 MaterialWriteRow {
std::uint64_t internal_material_id;
const char* name;
double youngs_modulus;
double poisson_ratio;
};
struct SectionWriteRow {
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;
};
struct AcceptedFrameWriteRow {
std::uint64_t frame_index;
std::uint64_t increment_index;
double load_factor;
std::uint64_t iteration_count;
std::uint8_t is_factor_one;
};
struct DiagnosticWriteRow {
const char* severity;
const char* code;
const char* file;
std::uint64_t line;
const char* keyword;
const char* entity_identity;
const char* message;
};
template <std::size_t Size>
bool IsFinite(const std::array<double, Size>& values) {
return std::all_of(values.begin(), values.end(),
[](const double value) { return std::isfinite(value); });
}
bool IsFinite(const Vector& values) {
for (std::size_t index = 0U; index < values.Size(); ++index) {
if (!std::isfinite(values[index])) {
return false;
}
}
return true;
}
bool SameIdentity(const SourceEntityId& left, const SourceEntityId& right) {
return left.instance_name == right.instance_name &&
left.source_label == right.source_label &&
left.source_label_text == right.source_label_text;
}
bool IsFinite(const ShellResultRow& row) {
if (!IsFinite(row.natural_coordinates) || !IsFinite(row.generalized_strain) ||
!IsFinite(row.section_resultant)) {
return false;
}
for (const auto& axis : row.local_frame) {
if (!IsFinite(axis)) {
return false;
}
}
for (const auto& stress : row.stress) {
if (!std::isfinite(stress.zeta) || !IsFinite(stress.components)) {
return false;
}
}
return true;
}
Status Failure(const char* code, const std::string& message) {
return hdf5_internal::OutputFailure(code, message);
}
Status ValidateDomain(const Domain& domain) {
if (!domain.BeamElements().Empty() || domain.ShellElements().Empty() ||
domain.Steps().Size() != 1U || !domain.Steps()[0U].IsNonlinearStatic() ||
domain.Nodes().empty() || domain.SourcePath().empty() ||
domain.SourceContentIdentity().empty() ||
!hdf5_internal::IsValidUtf8(domain.SourceContentIdentity())) {
return Failure("invalid-result-identity",
"Schema v1 requires one nonlinear shell Domain identity.");
}
if (domain.ShellNodeInitialFrames().size() != domain.Nodes().size()) {
return Failure("invalid-result-identity",
"Every nonlinear output node requires an initial frame.");
}
for (std::size_t node = 0U; node < domain.Nodes().size(); ++node) {
const Node& source = domain.Nodes()[node];
const auto& frame = domain.ShellNodeInitialFrames()[node];
if (source.source_id.source_label <= 0 ||
source.source_id.source_label_text.empty() ||
!hdf5_internal::IsValidUtf8(source.source_id.instance_name) ||
!hdf5_internal::IsValidUtf8(source.source_id.source_label_text) ||
!Vector3{source.coordinates}.IsFinite() ||
frame.node_index != static_cast<EntityIndex>(node) ||
!IsFinite(frame.tangent_a) || !IsFinite(frame.tangent_b) ||
!IsFinite(frame.director)) {
return Failure("invalid-result-identity",
"Nonlinear node identity or initial frame is invalid.");
}
}
for (std::size_t element = 0U; element < domain.ShellElements().Size();
++element) {
const auto& source = domain.ShellElements()[element];
if (source.source_id.source_label <= 0 ||
source.source_id.source_label_text.empty() ||
!hdf5_internal::IsValidUtf8(source.source_id.instance_name) ||
!hdf5_internal::IsValidUtf8(source.source_id.source_label_text) ||
source.node_indices.size() != 4U ||
source.section_index >= domain.ShellSections().Size() ||
source.material_index >= domain.LinearElasticMaterials().Size() ||
domain.ShellSections()[source.section_index].material_index !=
source.material_index) {
return Failure("invalid-result-identity",
"Nonlinear shell element identity is invalid.");
}
for (const EntityIndex node : source.node_indices) {
if (node >= domain.Nodes().size()) {
return Failure("invalid-result-identity",
"Nonlinear shell connectivity is out of range.");
}
}
}
return Status::Ok();
}
Status BuildModelData(const Domain& domain, NonlinearModelData& model_data) {
auto model_result = AnalysisModel::Create(domain);
if (!model_result.HasValue()) {
return Failure("invalid-result-state",
"The nonlinear writer could not create an AnalysisModel.");
}
auto dofs_result = DofManager::Create(model_result.Value());
if (!dofs_result.HasValue()) {
return Failure("invalid-result-state",
"The nonlinear writer could not create a DofManager.");
}
const DofManager dofs = std::move(dofs_result.Value());
std::size_t full_dof_count = 0U;
if (!hdf5_internal::SizeProductFits(domain.Nodes().size(), kDofsPerNode,
full_dof_count) ||
dofs.FullDofCount() != full_dof_count ||
dofs.ConstrainedDofs().size() != dofs.PrescribedValues().Size()) {
return Failure("invalid-result-state",
"Nonlinear constraint identities have an invalid size.");
}
model_data.constraint_mask.assign(full_dof_count, 0U);
model_data.prescribed_start.assign(full_dof_count, 0.0);
model_data.prescribed_target.assign(full_dof_count, 0.0);
for (std::size_t index = 0U; index < dofs.ConstrainedDofs().size(); ++index) {
const std::size_t full_dof = dofs.ConstrainedDofs()[index];
const double target = dofs.PrescribedValues()[index];
if (full_dof >= full_dof_count || !std::isfinite(target)) {
return Failure("invalid-result-state",
"Nonlinear prescribed values must be finite and in range.");
}
model_data.constraint_mask[full_dof] = 1U;
model_data.prescribed_target[full_dof] = target;
}
return Status::Ok();
}
Status ValidateState(const Domain& domain, const NonlinearAnalysisState& state) {
const auto& frames = state.AcceptedFrames();
if (frames.empty()) {
return Failure("invalid-result-state",
"Schema v1 requires at least one accepted frame.");
}
std::size_t full_dof_count = 0U;
std::size_t shell_row_count = 0U;
if (!hdf5_internal::SizeProductFits(domain.Nodes().size(), kDofsPerNode,
full_dof_count) ||
!hdf5_internal::SizeProductFits(domain.ShellElements().Size(),
kShellLocationsPerElement,
shell_row_count)) {
return Failure("invalid-result-state",
"Nonlinear result dimensions overflow size_t.");
}
const double gauss = 1.0 / std::sqrt(3.0);
const std::array<ShellMidsurfaceLocation, kShellLocationsPerElement>
locations{ShellMidsurfaceLocation::kGp1, ShellMidsurfaceLocation::kGp2,
ShellMidsurfaceLocation::kGp3, ShellMidsurfaceLocation::kGp4};
const std::array<std::array<double, 2>, kShellLocationsPerElement>
coordinates{{{-gauss, -gauss}, {gauss, -gauss},
{gauss, gauss}, {-gauss, gauss}}};
for (std::size_t frame_index = 0U; frame_index < frames.size();
++frame_index) {
const AcceptedFrameState& frame = frames[frame_index];
const bool final_frame = frame_index + 1U == frames.size();
if (frame.frame_index != frame_index || frame.increment_index == 0U ||
!std::isfinite(frame.load_factor) || frame.load_factor <= 0.0 ||
frame.load_factor > 1.0 || frame.iteration_count == 0U ||
frame.is_factor_one != final_frame ||
(final_frame && frame.load_factor != 1.0) ||
(!final_frame && frame.load_factor >= 1.0) ||
(frame_index > 0U &&
(frame.increment_index <= frames[frame_index - 1U].increment_index ||
frame.load_factor <= frames[frame_index - 1U].load_factor))) {
return Failure("invalid-result-state",
"Accepted-frame ordering or factor-one identity is invalid.");
}
if (frame.nodal_internal_order.size() != domain.Nodes().size() ||
frame.nodal_source_ids.size() != domain.Nodes().size() ||
frame.nodal_triad.size() != domain.Nodes().size() ||
frame.nodal_director.size() != domain.Nodes().size() ||
frame.shell_results.size() != shell_row_count ||
!std::isfinite(frame.physical_strain_energy) ||
!IsFinite(frame.global_equilibrium)) {
return Failure("invalid-result-state",
"Accepted-frame identity or physical inventory is invalid.");
}
const std::array<const Vector*, 7U> vectors{
&frame.generalized_coordinates, &frame.displacement,
&frame.rotation_vector, &frame.internal_force,
&frame.external_force, &frame.residual,
&frame.reaction};
for (const Vector* values : vectors) {
if (values->Size() != full_dof_count || !IsFinite(*values)) {
return Failure("invalid-result-state",
"Accepted-frame vectors must be finite node-by-six data.");
}
}
for (std::size_t node = 0U; node < domain.Nodes().size(); ++node) {
if (frame.nodal_internal_order[node] != node ||
!SameIdentity(frame.nodal_source_ids[node],
domain.Nodes()[node].source_id) ||
!IsFinite(frame.nodal_triad[node][0U]) ||
!IsFinite(frame.nodal_triad[node][1U]) ||
!IsFinite(frame.nodal_triad[node][2U]) ||
!IsFinite(frame.nodal_director[node])) {
return Failure("invalid-result-state",
"Accepted-frame nodal identity is invalid.");
}
}
for (std::size_t row_index = 0U; row_index < shell_row_count;
++row_index) {
const ShellResultRow& row = frame.shell_results[row_index];
const std::size_t element = row_index / kShellLocationsPerElement;
const std::size_t location = row_index % kShellLocationsPerElement;
if (row.element != element || row.location != locations[location] ||
row.natural_coordinates != coordinates[location] || !IsFinite(row)) {
return Failure("invalid-result-state",
"Accepted shell rows must preserve element and GP order.");
}
}
}
return Status::Ok();
}
void WriteCommonNumericAttributes(const hid_t dataset,
const std::string& component_names,
const std::string& component_units,
const std::string& coordinate_system,
const std::string& location) {
hdf5_internal::WriteUint64Attribute(dataset, "schema_version", 1U);
hdf5_internal::WriteStringAttribute(dataset, "component_names",
component_names);
hdf5_internal::WriteStringAttribute(dataset, "component_unit_dimensions",
component_units);
hdf5_internal::WriteStringAttribute(dataset, "coordinate_system",
coordinate_system);
hdf5_internal::WriteStringAttribute(dataset, "location", location);
}
void WriteFrameAttributes(const hid_t dataset, const AcceptedFrameState& frame) {
hdf5_internal::WriteStringAttribute(dataset, "step_name", "Step-1");
hdf5_internal::WriteUint64Attribute(dataset, "frame_index", frame.frame_index);
hdf5_internal::WriteUint64Attribute(dataset, "increment_index",
frame.increment_index);
hdf5_internal::WriteDoubleAttribute(dataset, "load_factor",
frame.load_factor);
hdf5_internal::WriteUint8Attribute(dataset, "is_factor_one",
frame.is_factor_one ? 1U : 0U);
}
Hdf5Handle WriteModelDoubleDataset(
const hid_t file, const std::string& path,
const std::vector<hsize_t>& dimensions, const double* values,
const std::size_t value_count, const std::string& component_names,
const std::string& component_units, const std::string& coordinate_system,
const std::string& location) {
auto dataset = hdf5_internal::WriteDoubleValues(file, path, dimensions, values,
value_count);
WriteCommonNumericAttributes(dataset.Get(), component_names, component_units,
coordinate_system, location);
return dataset;
}
Hdf5Handle WriteFrameDoubleDataset(
const hid_t file, const std::string& path,
const std::vector<hsize_t>& dimensions, const double* values,
const std::size_t value_count, const std::string& component_names,
const std::string& component_units, const std::string& coordinate_system,
const std::string& location, const AcceptedFrameState& frame) {
auto dataset = WriteModelDoubleDataset(file, path, dimensions, values,
value_count, component_names,
component_units, coordinate_system,
location);
WriteFrameAttributes(dataset.Get(), frame);
return dataset;
}
void WriteMetadata(const hid_t file, const Domain& domain) {
auto metadata = hdf5_internal::CreateGroup(file, "/metadata");
hdf5_internal::WriteUint64Attribute(metadata.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(metadata.Get(), "feature_id",
"nonlinear-static-newton-raphson");
hdf5_internal::WriteStringAttribute(metadata.Get(), "solver_version",
std::string{SolverVersion()});
hdf5_internal::WriteStringAttribute(metadata.Get(), "source_input_identity",
hdf5_internal::SourceInputIdentity(domain));
hdf5_internal::WriteStringAttribute(metadata.Get(), "unit_system_label",
"user-consistent-unspecified");
hdf5_internal::WriteStringAttribute(
metadata.Get(), "coordinate_convention",
"global-cartesian; shell-local=fixed-initial-reference; "
"positive-thickness=+zeta");
hdf5_internal::WriteStringAttribute(metadata.Get(), "analysis_procedure",
"nonlinear-static-full-newton");
hdf5_internal::WriteStringAttribute(metadata.Get(), "internal_formulation",
"FESA-MITC4-total-lagrangian");
hdf5_internal::WriteStringAttribute(metadata.Get(), "integration_rule",
"2x2x2-gauss; mitc4-edge-midpoint-shear");
hdf5_internal::WriteStringAttribute(metadata.Get(), "step_name", "Step-1");
hdf5_internal::WriteStringAttribute(metadata.Get(), "frame_indexing",
"accepted-increment-order-zero-based");
hdf5_internal::WriteUint8Attribute(metadata.Get(), "factor_zero_is_frame",
0U);
hdf5_internal::WriteDoubleAttribute(metadata.Get(), "factor_zero_load_factor",
0.0);
}
void WriteNodes(const hid_t file, const Domain& domain) {
std::vector<NodeWriteRow> 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<std::uint64_t>(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 = hdf5_internal::MakeUtf8StringType();
const hsize_t coordinate_dimensions[] = {3U};
Hdf5Handle file_coordinates{hdf5_internal::RequireHdf5Id(
H5Tarray_create2(H5T_IEEE_F64LE, 1,
coordinate_dimensions),
"Unable to create node coordinate type."),
H5Tclose};
Hdf5Handle memory_coordinates{hdf5_internal::RequireHdf5Id(
H5Tarray_create2(H5T_NATIVE_DOUBLE, 1,
coordinate_dimensions),
"Unable to create node memory type."),
H5Tclose};
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)),
"Unable to create nonlinear node file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(NodeWriteRow)),
"Unable to create nonlinear node memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type,
const hid_t coordinate_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "internal_node_id", HOFFSET(NodeWriteRow, internal_node_id),
integer_type),
"Unable to define nonlinear node ID.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "instance_name", HOFFSET(NodeWriteRow, instance_name),
string_type.Get()),
"Unable to define nonlinear node instance.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_label", HOFFSET(NodeWriteRow, source_label),
string_type.Get()),
"Unable to define nonlinear node source label.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "coordinates", HOFFSET(NodeWriteRow, coordinates),
coordinate_type),
"Unable to define nonlinear node coordinates.");
};
insert(file_type.Get(), H5T_STD_U64LE, file_coordinates.Get());
insert(memory_type.Get(), H5T_NATIVE_UINT64, memory_coordinates.Get());
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/model/nodes", rows.size(), file_type.Get(), memory_type.Get(),
rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(dataset.Get(), "coordinate_system",
"global-cartesian");
hdf5_internal::WriteStringAttribute(dataset.Get(), "component_names", "X,Y,Z");
hdf5_internal::WriteStringAttribute(dataset.Get(), "component_unit_dimensions",
"length,length,length");
hdf5_internal::WriteStringAttribute(dataset.Get(), "location",
"nodal-reference");
}
void WriteElements(const hid_t file, const Domain& domain) {
std::vector<ElementWriteRow> 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<std::uint64_t>(index),
element.source_id.instance_name.c_str(),
element.source_id.source_label_text.c_str(),
hdf5_internal::ShellSourceTypeName(element.source_type),
"FESA-MITC4-total-lagrangian",
{static_cast<std::uint64_t>(element.node_indices[0U]),
static_cast<std::uint64_t>(element.node_indices[1U]),
static_cast<std::uint64_t>(element.node_indices[2U]),
static_cast<std::uint64_t>(element.node_indices[3U])},
static_cast<std::uint64_t>(element.section_index),
static_cast<std::uint64_t>(element.material_index)});
}
auto string_type = hdf5_internal::MakeUtf8StringType();
const hsize_t nodes[] = {4U};
Hdf5Handle file_nodes{hdf5_internal::RequireHdf5Id(
H5Tarray_create2(H5T_STD_U64LE, 1, nodes),
"Unable to create shell connectivity type."),
H5Tclose};
Hdf5Handle memory_nodes{hdf5_internal::RequireHdf5Id(
H5Tarray_create2(H5T_NATIVE_UINT64, 1, nodes),
"Unable to create shell connectivity memory type."),
H5Tclose};
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)),
"Unable to create shell element file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(ElementWriteRow)),
"Unable to create shell element memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type,
const hid_t node_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "internal_element_id",
HOFFSET(ElementWriteRow, internal_element_id), integer_type),
"Unable to define shell element ID.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "instance_name", HOFFSET(ElementWriteRow, instance_name),
string_type.Get()),
"Unable to define shell instance identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_label", HOFFSET(ElementWriteRow, source_label),
string_type.Get()),
"Unable to define shell source label identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_element_type",
HOFFSET(ElementWriteRow, source_element_type),
string_type.Get()),
"Unable to define shell source type identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "internal_formulation",
HOFFSET(ElementWriteRow, internal_formulation),
string_type.Get()),
"Unable to define shell formulation identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "node_internal_ids", HOFFSET(ElementWriteRow, node_internal_ids),
node_type),
"Unable to define shell connectivity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "shell_section_internal_id",
HOFFSET(ElementWriteRow, shell_section_internal_id), integer_type),
"Unable to define shell section identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "material_internal_id",
HOFFSET(ElementWriteRow, material_internal_id), integer_type),
"Unable to define shell material identity.");
};
insert(file_type.Get(), H5T_STD_U64LE, file_nodes.Get());
insert(memory_type.Get(), H5T_NATIVE_UINT64, memory_nodes.Get());
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/model/elements", rows.size(), file_type.Get(), memory_type.Get(),
rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(dataset.Get(), "formulation",
"FESA-MITC4-total-lagrangian");
}
void WriteMaterials(const hid_t file, const Domain& domain) {
std::vector<MaterialWriteRow> 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<std::uint64_t>(index), material.name.c_str(),
material.youngs_modulus, material.poisson_ratio});
}
auto string_type = hdf5_internal::MakeUtf8StringType();
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(MaterialWriteRow)),
"Unable to create shell material file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(MaterialWriteRow)),
"Unable to create shell material memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type,
const hid_t double_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "internal_material_id",
HOFFSET(MaterialWriteRow, internal_material_id), integer_type),
"Unable to define shell material ID.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "name", HOFFSET(MaterialWriteRow, name), string_type.Get()),
"Unable to define shell material name.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "E", HOFFSET(MaterialWriteRow, youngs_modulus),
double_type),
"Unable to define shell material E.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "nu", HOFFSET(MaterialWriteRow, poisson_ratio),
double_type),
"Unable to define shell material nu.");
};
insert(file_type.Get(), H5T_STD_U64LE, H5T_IEEE_F64LE);
insert(memory_type.Get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE);
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/model/shell/materials", rows.size(), file_type.Get(),
memory_type.Get(), rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(dataset.Get(), "component_unit_dimensions",
"force/length^2,1");
hdf5_internal::WriteStringAttribute(dataset.Get(), "location",
"model-material");
}
void WriteSections(const hid_t file, const Domain& domain) {
std::vector<std::string> source_files;
source_files.reserve(domain.ShellSections().Size());
for (std::size_t index = 0U; index < domain.ShellSections().Size(); ++index) {
source_files.push_back(hdf5_internal::NormalizedPathString(
domain.ShellSections()[index].location.file));
}
std::vector<SectionWriteRow> 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<std::uint64_t>(index), source_files[index].c_str(),
static_cast<std::uint64_t>(section.location.line),
section.name.c_str(),
static_cast<std::uint64_t>(section.material_index),
section.thickness});
}
auto string_type = hdf5_internal::MakeUtf8StringType();
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(SectionWriteRow)),
"Unable to create shell section file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(SectionWriteRow)),
"Unable to create shell section memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type,
const hid_t double_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "internal_section_id",
HOFFSET(SectionWriteRow, internal_section_id), integer_type),
"Unable to define shell section ID.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_file", HOFFSET(SectionWriteRow, source_file),
string_type.Get()),
"Unable to define shell section source file.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_line", HOFFSET(SectionWriteRow, source_line),
integer_type),
"Unable to define shell section source line.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "source_elset", HOFFSET(SectionWriteRow, source_elset),
string_type.Get()),
"Unable to define shell section source elset.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "material_internal_id",
HOFFSET(SectionWriteRow, material_internal_id), integer_type),
"Unable to define shell section material identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "thickness", HOFFSET(SectionWriteRow, thickness),
double_type),
"Unable to define shell section thickness.");
};
insert(file_type.Get(), H5T_STD_U64LE, H5T_IEEE_F64LE);
insert(memory_type.Get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE);
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/model/shell/sections", rows.size(), file_type.Get(),
memory_type.Get(), rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(dataset.Get(), "component_unit_dimensions",
"length");
hdf5_internal::WriteStringAttribute(dataset.Get(), "location",
"model-shell-section");
hdf5_internal::WriteStringAttribute(dataset.Get(), "layering",
"centered-single-layer");
}
void WriteModelData(const hid_t file, const Domain& domain,
const NonlinearModelData& model_data) {
std::vector<double> directors;
std::vector<double> frames;
directors.reserve(domain.Nodes().size() * 3U);
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());
}
const hsize_t node_count = static_cast<hsize_t>(domain.Nodes().size());
(void)WriteModelDoubleDataset(file, "/model/shell/nodal_director",
{node_count, 3U}, directors.data(),
directors.size(), "D1,D2,D3", "1,1,1",
"global-cartesian", "nodal-initial-director");
auto initial_frame = WriteModelDoubleDataset(
file, "/model/shell/nodal_frame", {node_count, 3U, 3U}, frames.data(),
frames.size(), "X,Y,Z", "1,1,1", "global-cartesian",
"nodal-initial-frame");
hdf5_internal::WriteStringAttribute(initial_frame.Get(), "axis_names", "A,B,D");
WriteMaterials(file, domain);
WriteSections(file, domain);
const std::vector<hsize_t> nodal_dimensions{node_count, kDofsPerNode};
hdf5_internal::WriteUint8Dataset(file, "/model/nodal_constraint_mask",
nodal_dimensions, model_data.constraint_mask.data(),
model_data.constraint_mask.size());
Hdf5Handle mask{hdf5_internal::RequireHdf5Id(
H5Dopen2(file, "/model/nodal_constraint_mask", H5P_DEFAULT),
"Unable to reopen nonlinear constraint mask."),
H5Dclose};
hdf5_internal::WriteUint64Attribute(mask.Get(), "schema_version", 1U);
hdf5_internal::WriteStringAttribute(mask.Get(), "component_unit_dimensions",
"1,1,1,1,1,1");
hdf5_internal::WriteStringAttribute(mask.Get(), "coordinate_system",
"global-cartesian");
hdf5_internal::WriteStringAttribute(mask.Get(), "location",
"nodal-constraint");
auto start = WriteModelDoubleDataset(
file, "/model/prescribed_displacement_start", nodal_dimensions,
model_data.prescribed_start.data(), model_data.prescribed_start.size(),
"UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal-prescribed-start");
hdf5_internal::WriteDoubleAttribute(start.Get(), "load_factor", 0.0);
auto target = WriteModelDoubleDataset(
file, "/model/prescribed_displacement_target", nodal_dimensions,
model_data.prescribed_target.data(), model_data.prescribed_target.size(),
"UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal-prescribed-target");
hdf5_internal::WriteDoubleAttribute(target.Get(), "load_factor", 1.0);
const double gauss = 1.0 / std::sqrt(3.0);
const std::array<double, 8> locations{-gauss, -gauss, gauss, -gauss,
gauss, gauss, -gauss, gauss};
(void)WriteModelDoubleDataset(file, "/model/shell/midsurface_locations",
{4U, 2U}, locations.data(), locations.size(),
"XI,ETA", "1,1", "shell-natural",
"midsurface-location");
const std::array<double, 3> positions{-1.0, 0.0, 1.0};
auto section_positions = WriteModelDoubleDataset(
file, "/model/shell/section_positions", {3U}, positions.data(),
positions.size(), "ZETA", "1", "shell-natural", "section-position");
hdf5_internal::WriteStringAttribute(section_positions.Get(), "position_names",
"BOTTOM,MIDDLE,TOP");
}
void WriteAcceptedFrameTable(const hid_t file,
const std::vector<AcceptedFrameState>& frames) {
std::vector<AcceptedFrameWriteRow> rows;
rows.reserve(frames.size());
for (const auto& frame : frames) {
rows.push_back({frame.frame_index, frame.increment_index, frame.load_factor,
frame.iteration_count,
frame.is_factor_one ? static_cast<std::uint8_t>(1U)
: static_cast<std::uint8_t>(0U)});
}
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(AcceptedFrameWriteRow)),
"Unable to create accepted-frame file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(AcceptedFrameWriteRow)),
"Unable to create accepted-frame memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type,
const hid_t double_type, const hid_t flag_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "frame_index", HOFFSET(AcceptedFrameWriteRow, frame_index),
integer_type),
"Unable to define accepted-frame index.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "increment_index",
HOFFSET(AcceptedFrameWriteRow, increment_index), integer_type),
"Unable to define accepted increment index.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "load_factor", HOFFSET(AcceptedFrameWriteRow, load_factor),
double_type),
"Unable to define accepted-frame load factor.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "iteration_count",
HOFFSET(AcceptedFrameWriteRow, iteration_count), integer_type),
"Unable to define accepted-frame iteration count.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "is_factor_one",
HOFFSET(AcceptedFrameWriteRow, is_factor_one), flag_type),
"Unable to define accepted-frame factor-one flag.");
};
insert(file_type.Get(), H5T_STD_U64LE, H5T_IEEE_F64LE, H5T_STD_U8LE);
insert(memory_type.Get(), H5T_NATIVE_UINT64, H5T_NATIVE_DOUBLE,
H5T_NATIVE_UINT8);
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/steps/Step-1/accepted_frames", rows.size(), file_type.Get(),
memory_type.Get(), rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
}
std::vector<double> CopyVector(const Vector& source) {
std::vector<double> values(source.Size());
for (std::size_t index = 0U; index < source.Size(); ++index) {
values[index] = source[index];
}
return values;
}
std::vector<double> NodalDisplacement(const AcceptedFrameState& frame) {
std::vector<double> values(frame.displacement.Size());
for (std::size_t node = 0U; node < frame.nodal_internal_order.size(); ++node) {
const std::size_t offset = kDofsPerNode * node;
for (std::size_t component = 0U; component < 3U; ++component) {
values[offset + component] = frame.displacement[offset + component];
values[offset + 3U + component] =
frame.rotation_vector[offset + 3U + component];
}
}
return values;
}
std::vector<double> FlattenTriads(const AcceptedFrameState& frame) {
std::vector<double> values;
values.reserve(frame.nodal_triad.size() * 9U);
for (const auto& triad : frame.nodal_triad) {
for (const auto& axis : triad) {
values.insert(values.end(), axis.begin(), axis.end());
}
}
return values;
}
std::vector<double> FlattenDirectors(const AcceptedFrameState& frame) {
std::vector<double> values;
values.reserve(frame.nodal_director.size() * 3U);
for (const auto& director : frame.nodal_director) {
values.insert(values.end(), director.begin(), director.end());
}
return values;
}
std::vector<double> SpatialReaction(const AcceptedFrameState& frame) {
std::vector<double> values = CopyVector(frame.reaction);
for (std::size_t node = 0U; node < frame.nodal_internal_order.size(); ++node) {
const std::size_t offset = kDofsPerNode * node;
const Vector3 phi{frame.rotation_vector[offset + 3U],
frame.rotation_vector[offset + 4U],
frame.rotation_vector[offset + 5U]};
const Matrix transform = InverseTransposeLeftJacobianSO3(phi);
const std::array<double, 3> generalized{
frame.reaction[offset + 3U], frame.reaction[offset + 4U],
frame.reaction[offset + 5U]};
for (std::size_t row = 0U; row < 3U; ++row) {
values[offset + 3U + row] = 0.0;
for (std::size_t column = 0U; column < 3U; ++column) {
values[offset + 3U + row] += transform(row, column) * generalized[column];
}
}
}
return values;
}
void WriteShellIdentity(const hid_t dataset, const bool has_section_positions,
const std::string& local_frame_path) {
hdf5_internal::WriteStringAttribute(dataset, "source_element_type_dataset",
"/model/elements.source_element_type");
hdf5_internal::WriteStringAttribute(dataset, "internal_formulation",
"FESA-MITC4-total-lagrangian");
hdf5_internal::WriteStringAttribute(dataset, "midsurface_location_dataset",
"/model/shell/midsurface_locations");
hdf5_internal::WriteStringAttribute(dataset, "local_frame_dataset",
local_frame_path);
if (has_section_positions) {
hdf5_internal::WriteStringAttribute(dataset, "section_position_dataset",
"/model/shell/section_positions");
}
}
void WriteFrame(const hid_t file, const Domain& domain,
const AcceptedFrameState& frame) {
const std::string root = "/steps/Step-1/frames/" +
std::to_string(frame.frame_index);
(void)hdf5_internal::CreateGroup(file, (root + "/nodal").c_str());
(void)hdf5_internal::CreateGroup(file, (root + "/element/shell").c_str());
(void)hdf5_internal::CreateGroup(file, (root + "/global").c_str());
const hsize_t node_count = static_cast<hsize_t>(domain.Nodes().size());
const std::vector<hsize_t> nodal_dimensions{node_count, kDofsPerNode};
const auto displacement = NodalDisplacement(frame);
(void)WriteFrameDoubleDataset(
file, root + "/nodal/displacement", nodal_dimensions, displacement.data(),
displacement.size(), "UX,UY,UZ,URX,URY,URZ", "length,length,length,1,1,1",
"global-cartesian", "nodal", frame);
const auto triads = FlattenTriads(frame);
(void)WriteFrameDoubleDataset(file, root + "/nodal/triad",
{node_count, 3U, 3U}, triads.data(), triads.size(),
"X,Y,Z", "1,1,1", "global-cartesian",
"nodal-triad", frame);
const auto directors = FlattenDirectors(frame);
(void)WriteFrameDoubleDataset(file, root + "/nodal/director", {node_count, 3U},
directors.data(), directors.size(), "D1,D2,D3",
"1,1,1", "global-cartesian", "nodal-director",
frame);
const auto internal = CopyVector(frame.internal_force);
(void)WriteFrameDoubleDataset(
file, root + "/nodal/generalized_internal_force", nodal_dimensions,
internal.data(), internal.size(), "FI1,FI2,FI3,MI_PHI1,MI_PHI2,MI_PHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", frame);
const auto external = CopyVector(frame.external_force);
(void)WriteFrameDoubleDataset(
file, root + "/nodal/generalized_external_force", nodal_dimensions,
external.data(), external.size(), "FE1,FE2,FE3,ME_PHI1,ME_PHI2,ME_PHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", frame);
const auto residual = CopyVector(frame.residual);
(void)WriteFrameDoubleDataset(
file, root + "/nodal/generalized_residual", nodal_dimensions,
residual.data(), residual.size(), "RU1,RU2,RU3,RPHI1,RPHI2,RPHI3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", frame);
const auto reaction = SpatialReaction(frame);
(void)WriteFrameDoubleDataset(
file, root + "/nodal/reaction", nodal_dimensions, reaction.data(),
reaction.size(), "RF1,RF2,RF3,RM1,RM2,RM3",
"force,force,force,force*length,force*length,force*length",
"global-cartesian", "nodal", frame);
std::vector<double> local_frames;
std::vector<double> strains;
std::vector<double> resultants;
std::vector<double> stresses;
local_frames.reserve(frame.shell_results.size() * 9U);
strains.reserve(frame.shell_results.size() * 8U);
resultants.reserve(frame.shell_results.size() * 8U);
stresses.reserve(frame.shell_results.size() * kSectionPositions * 3U);
for (const auto& row : frame.shell_results) {
for (const auto& axis : row.local_frame) {
local_frames.insert(local_frames.end(), axis.begin(), axis.end());
}
strains.insert(strains.end(), row.generalized_strain.begin(),
row.generalized_strain.end());
resultants.insert(resultants.end(), row.section_resultant.begin(),
row.section_resultant.end());
for (const auto& stress : row.stress) {
stresses.insert(stresses.end(), stress.components.begin(),
stress.components.end());
}
}
const hsize_t element_count = static_cast<hsize_t>(domain.ShellElements().Size());
const std::string shell_root = root + "/element/shell";
auto local_frame = WriteFrameDoubleDataset(
file, shell_root + "/local_frame", {element_count, 4U, 3U, 3U},
local_frames.data(), local_frames.size(), "X,Y,Z", "1,1,1",
"global-cartesian", "shell-local-frame", frame);
hdf5_internal::WriteStringAttribute(local_frame.Get(), "axis_names", "E1,E2,E3");
hdf5_internal::WriteStringAttribute(local_frame.Get(), "source_element_type_dataset",
"/model/elements.source_element_type");
hdf5_internal::WriteStringAttribute(local_frame.Get(), "internal_formulation",
"FESA-MITC4-total-lagrangian");
hdf5_internal::WriteStringAttribute(local_frame.Get(), "midsurface_location_dataset",
"/model/shell/midsurface_locations");
auto strain = WriteFrameDoubleDataset(
file, shell_root + "/generalized_strain", {element_count, 4U, 8U},
strains.data(), strains.size(), "E11,E22,G12,K11,K22,K12,G13,G23",
"1,1,1,1/length,1/length,1/length,1,1", "shell-local", "midsurface",
frame);
WriteShellIdentity(strain.Get(), false, shell_root + "/local_frame");
auto resultant = WriteFrameDoubleDataset(
file, shell_root + "/section_resultant", {element_count, 4U, 8U},
resultants.data(), resultants.size(), "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);
WriteShellIdentity(resultant.Get(), false, shell_root + "/local_frame");
auto stress = WriteFrameDoubleDataset(
file, shell_root + "/stress", {element_count, 4U, 3U, 3U}, stresses.data(),
stresses.size(), "S11,S22,S12",
"force/length^2,force/length^2,force/length^2", "shell-local",
"section-position", frame);
WriteShellIdentity(stress.Get(), true, shell_root + "/local_frame");
(void)WriteFrameDoubleDataset(file, root + "/global/energy", {1U},
&frame.physical_strain_energy, 1U,
"PHYSICAL_STRAIN_ENERGY", "force*length",
"global", "global", frame);
(void)WriteFrameDoubleDataset(
file, root + "/global/equilibrium", {6U}, frame.global_equilibrium.data(),
frame.global_equilibrium.size(),
"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);
}
void WriteDiagnostics(const hid_t file,
const std::vector<Diagnostic>& input_diagnostics) {
std::vector<Diagnostic> diagnostics = input_diagnostics;
SortDiagnostics(diagnostics);
std::vector<std::string> files;
files.reserve(diagnostics.size());
for (const auto& diagnostic : diagnostics) {
files.push_back(hdf5_internal::NormalizedPathString(diagnostic.location.file));
}
std::vector<DiagnosticWriteRow> rows;
rows.reserve(diagnostics.size());
for (std::size_t index = 0U; index < diagnostics.size(); ++index) {
const auto& diagnostic = diagnostics[index];
rows.push_back({diagnostic.severity == Severity::kWarning ? "warning" : "error",
diagnostic.code.c_str(), files[index].c_str(),
static_cast<std::uint64_t>(diagnostic.location.line),
diagnostic.keyword.c_str(), diagnostic.entity_identity.c_str(),
diagnostic.message.c_str()});
}
auto string_type = hdf5_internal::MakeUtf8StringType();
Hdf5Handle file_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(DiagnosticWriteRow)),
"Unable to create nonlinear diagnostic file type."),
H5Tclose};
Hdf5Handle memory_type{hdf5_internal::RequireHdf5Id(
H5Tcreate(H5T_COMPOUND, sizeof(DiagnosticWriteRow)),
"Unable to create nonlinear diagnostic memory type."),
H5Tclose};
const auto insert = [&](const hid_t type, const hid_t integer_type) {
hdf5_internal::RequireHdf5(
H5Tinsert(type, "severity", HOFFSET(DiagnosticWriteRow, severity),
string_type.Get()),
"Unable to define nonlinear diagnostic severity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "code", HOFFSET(DiagnosticWriteRow, code),
string_type.Get()),
"Unable to define nonlinear diagnostic code.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "file", HOFFSET(DiagnosticWriteRow, file),
string_type.Get()),
"Unable to define nonlinear diagnostic file.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "line", HOFFSET(DiagnosticWriteRow, line), integer_type),
"Unable to define nonlinear diagnostic line.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "keyword", HOFFSET(DiagnosticWriteRow, keyword),
string_type.Get()),
"Unable to define nonlinear diagnostic keyword.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "entity_identity",
HOFFSET(DiagnosticWriteRow, entity_identity), string_type.Get()),
"Unable to define nonlinear diagnostic entity identity.");
hdf5_internal::RequireHdf5(
H5Tinsert(type, "message", HOFFSET(DiagnosticWriteRow, message),
string_type.Get()),
"Unable to define nonlinear diagnostic message.");
};
insert(file_type.Get(), H5T_STD_U64LE);
insert(memory_type.Get(), H5T_NATIVE_UINT64);
auto dataset = hdf5_internal::WriteCompoundDataset(
file, "/diagnostics", rows.size(), file_type.Get(), memory_type.Get(),
rows.data());
hdf5_internal::WriteUint64Attribute(dataset.Get(), "schema_version", 1U);
}
Status ValidateDiagnostics(const std::vector<Diagnostic>& diagnostics) {
for (const auto& diagnostic : diagnostics) {
if (!hdf5_internal::IsValidUtf8(diagnostic.code) ||
!hdf5_internal::IsValidUtf8(diagnostic.keyword) ||
!hdf5_internal::IsValidUtf8(diagnostic.entity_identity) ||
!hdf5_internal::IsValidUtf8(diagnostic.message)) {
return Failure("invalid-result-diagnostic",
"Nonlinear diagnostic text must be valid UTF-8.");
}
}
return Status::Ok();
}
} // namespace
Status NonlinearHdf5ResultsWriter::Write(
const std::filesystem::path& output_path, const Domain& domain,
const NonlinearAnalysisState& state,
const std::vector<Diagnostic>& diagnostics) {
if (output_path.empty() || output_path.filename().empty()) {
return Failure("invalid-output-path", "The HDF5 output path must name a file.");
}
const Status domain_status = ValidateDomain(domain);
if (!domain_status.IsOk()) {
return domain_status;
}
const Status state_status = ValidateState(domain, state);
if (!state_status.IsOk()) {
return state_status;
}
const Status diagnostic_status = ValidateDiagnostics(diagnostics);
if (!diagnostic_status.IsOk()) {
return diagnostic_status;
}
NonlinearModelData model_data;
const Status model_status = BuildModelData(domain, model_data);
if (!model_status.IsOk()) {
return model_status;
}
try {
hdf5_internal::Hdf5ErrorSilencer silence_backend_errors;
const std::filesystem::path candidate_path =
hdf5_internal::MakeCandidatePath(output_path);
hdf5_internal::CandidateFileGuard cleanup{candidate_path};
Hdf5Handle file{hdf5_internal::RequireHdf5Id(
H5Fcreate(candidate_path.string().c_str(), H5F_ACC_EXCL,
H5P_DEFAULT, H5P_DEFAULT),
"Unable to create nonlinear temporary HDF5 file."),
H5Fclose};
(void)hdf5_internal::CreateGroup(file.Get(), "/model");
(void)hdf5_internal::CreateGroup(file.Get(), "/model/shell");
(void)hdf5_internal::CreateGroup(file.Get(), "/steps/Step-1");
WriteMetadata(file.Get(), domain);
WriteNodes(file.Get(), domain);
WriteElements(file.Get(), domain);
WriteModelData(file.Get(), domain, model_data);
WriteAcceptedFrameTable(file.Get(), state.AcceptedFrames());
for (const auto& frame : state.AcceptedFrames()) {
WriteFrame(file.Get(), domain, frame);
}
WriteDiagnostics(file.Get(), diagnostics);
hdf5_internal::RequireHdf5(H5Fflush(file.Get(), H5F_SCOPE_GLOBAL),
"Unable to flush nonlinear temporary HDF5 file.");
hdf5_internal::RequireHdf5(
file.CloseChecked(), "Unable to close nonlinear temporary HDF5 file.");
const Status self_check = hdf5_internal::NonlinearSelfCheck(
candidate_path, domain, state, diagnostics.size());
const Status atomic = hdf5_internal::AtomicFinalizeValidatedCandidate(
candidate_path, output_path, self_check);
if (atomic.IsOk()) {
cleanup.Release();
}
return atomic;
} catch (const Hdf5Failure& failure) {
return Failure("hdf5-write-failure", failure.what());
} catch (const std::exception& failure) {
return Failure("hdf5-write-failure", failure.what());
}
}
} // namespace fesa