From edc7a79ce4dcbe6cc0d8c73294108abc6884f93c Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Thu, 29 Jan 2026 11:36:56 +0100 Subject: [PATCH 1/8] set-up HDF5 support in cmake --- CMakeLists.txt | 24 +++++++++++++++++++++++ src/CMakeLists.txt | 9 +++++++++ src/MFrontGenericInterfaceConfig.cmake.in | 17 ++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 671d9f889..870ae9ee7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,26 @@ if(enable-julia-bindings) set(CMAKE_CXX_STANDARD "${_CMAKE_CXX_STANDARD}") endif(enable-julia-bindings) +# HDF5 support (optional) + +option(enable-hdf5-support "enable HDF5 support" OFF) +option(enable-hdf5-automatic-support "if set, try to support HDF5, even if not explicitly requested by enable-hdf5-support" ON) +if(enable-hdf5-support) + find_package(HDF5 REQUIRED COMPONENTS CXX) +else(enable-hdf5-support) + if(enable-hdf5-automatic-support) + find_package(HDF5 COMPONENTS CXX) + endif(enable-hdf5-automatic-support) +endif(enable-hdf5-support) +if(HDF5_FOUND) + set(MGIS_HDF5_SUPPORT ON) + MESSAGE(STATUS "hdf5 headers : " ${HDF5_INCLUDE_DIRS}) + MESSAGE(STATUS "hdf5 librairies : " ${HDF5_LIBRARIES}) + MESSAGE(STATUS "hdf5 definitions : " ${HDF5_DEFINITIONS}) +else(HDF5_FOUND) + set(MGIS_HDF5_SUPPORT OFF) +endif(HDF5_FOUND) + # summary if(enable-c-bindings) @@ -129,6 +149,10 @@ if(enable-fenics-bindings) message(STATUS "FEniCS bindings support enabled") endif(enable-fenics-bindings) +if(MGIS_HDF5_SUPPORT) + message(STATUS "HDF5 support enabled") +endif(MGIS_HDF5_SUPPORT) + find_package(Threads) # if(MINGW) # file(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/thread-mingw.cxx" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 211597a0e..8c59ce9dd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -141,6 +141,15 @@ else(UNIX) endif(Threads_FOUND) endif(UNIX) +if(MGIS_HDF5_SUPPORT) + target_compile_definitions(MFrontGenericInterface PUBLIC MGIS_HAVE_HDF5) + target_link_libraries(MFrontGenericInterface PUBLIC hdf5::hdf5_cpp) + if(enable-static) + target_compile_definitions(MFrontGenericInterface-static PUBLIC MGIS_HAVE_HDF5) + target_link_libraries(MFrontGenericInterface-static PUBLIC hdf5::hdf5_cpp) + endif(enable-static) +endif(MGIS_HDF5_SUPPORT) + if(MGIS_ADDITIONAL_LINK_FLAGS) target_link_options(MFrontGenericInterface PUBLIC "${MGIS_ADDITIONAL_LINK_FLAGS}") endif(MGIS_ADDITIONAL_LINK_FLAGS) diff --git a/src/MFrontGenericInterfaceConfig.cmake.in b/src/MFrontGenericInterfaceConfig.cmake.in index c16d647d2..e3993304e 100644 --- a/src/MFrontGenericInterfaceConfig.cmake.in +++ b/src/MFrontGenericInterfaceConfig.cmake.in @@ -37,6 +37,23 @@ if(MGIS_Threads_FOUND) find_package(Threads REQUIRED) endif(MGIS_Threads_FOUND) +set(MGIS_HDF5_SUPPORT @MGIS_HDF5_SUPPORT@) +if(MGIS_HDF5_SUPPORT) + set(MGIS_HDF5_DIR "@HDF5_DIR@") + if(MGIS_HDF5_DIR) + set(_HDF5_MODULE_PATH "@HDF5_DIR@") + if(HDF5_DIR) + set(_HDF5_MODULE_PATH "${HDF5_DIR}") + elseif(DEFINED ENV{${HDF5_DIR}}) + set(_HDF5_MODULE_PATH "$ENV{HDF5_DIR}") + endif(HDF5_DIR) + find_package(HDF5 REQUIRED COMPONENTS CXX + HINTS "${CMAKE_CURRENT_LIST_DIR}" "${_HDF5_MODULE_PATH}") + else() + find_package(HDF5 REQUIRED COMPONENTS CXX) + endif() +endif(MGIS_HDF5_SUPPORT) + set(MGIS_REQUIRED_ADDITIONAL_PACKAGES "@MGIS_REQUIRED_ADDITIONAL_PACKAGES@") foreach(_package ${MGIS_REQUIRED_ADDITIONAL_PACKAGES}) find_package(${_package} REQUIRED) From 374120bd07f5b44c7bebac334e73c9c4820cfd90 Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Fri, 30 Jan 2026 10:03:26 +0100 Subject: [PATCH 2/8] first implementation of the save function --- include/CMakeLists.txt | 6 +- .../MGIS/Behaviour/MaterialDataManager.hxx | 27 ++ .../MGIS/Behaviour/MaterialStateManager.hxx | 35 ++ include/MGIS/Utilities/HDF5Support.hxx | 182 +++++++++ include/MGIS/Utilities/Markdown.hxx | 18 +- src/CMakeLists.txt | 15 +- src/HDF5Support.cxx | 377 ++++++++++++++++++ src/MaterialDataManager.cxx | 31 ++ src/MaterialStateManager.cxx | 82 +++- tests/CMakeLists.txt | 18 + tests/HDF5Test.cxx | 53 +++ 11 files changed, 829 insertions(+), 15 deletions(-) create mode 100644 include/MGIS/Utilities/HDF5Support.hxx create mode 100644 src/HDF5Support.cxx create mode 100644 tests/HDF5Test.cxx diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index a946b6b78..112ed2f8a 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -46,9 +46,13 @@ mgis_header(MGIS/Behaviour FiniteStrainSupport.hxx) mgis_header(MGIS/Model Model.hxx) if(MGIS_HAVE_TFEL) -mgis_header(MGIS Database.hxx) + mgis_header(MGIS Database.hxx) endif(MGIS_HAVE_TFEL) +if(MGIS_HDF5_SUPPORT) + mgis_header(MGIS/Utilities HDF5Support.hxx) +endif(MGIS_HDF5_SUPPORT) + if(enable-mgis-function) mgis_header(MGIS/Function CompileTimeSize.hxx) mgis_header(MGIS/Function SpaceConcept.hxx) diff --git a/include/MGIS/Behaviour/MaterialDataManager.hxx b/include/MGIS/Behaviour/MaterialDataManager.hxx index 06245ca15..7bdb69c3d 100644 --- a/include/MGIS/Behaviour/MaterialDataManager.hxx +++ b/include/MGIS/Behaviour/MaterialDataManager.hxx @@ -19,6 +19,10 @@ #include #include #include +#ifdef MGIS_HAVE_HDF5 +#include "MGIS/Utilities/HDF5Support.hxx" +#endif /* MGIS_HAVE_HDF5 */ + #include "MGIS/Config.hxx" #include "MGIS/Behaviour/MaterialStateManager.hxx" @@ -280,6 +284,29 @@ namespace mgis::behaviour { MGIS_EXPORT std::vector allocatePostProcessingVariables( const MaterialDataManager&, const std::string_view); +#ifdef MGIS_HAVE_HDF5 + + /*! + * \brief structure used to customize the saving of a `MaterialDataManager` + */ + struct MaterialDataManagerSavingOptions + : public MaterialStateManagerSavingOptions {}; + + /*! + * \brief save a `MaterialDataManager` to a file + * \param[in] ctx: execution context + * \param[in] g: group + * \param[in] m: material data manager + * \param[in] opts: options + */ + MGIS_EXPORT [[nodiscard]] bool save( + Context&, + H5::Group&, + const MaterialDataManager&, + const MaterialDataManagerSavingOptions& = {}) noexcept; + +#endif /* MGIS_HAVE_HDF5 */ + } // end of namespace mgis::behaviour #endif /* LIB_MGIS_BEHAVIOUR_MATERIALDATAMANAGER_HXX */ diff --git a/include/MGIS/Behaviour/MaterialStateManager.hxx b/include/MGIS/Behaviour/MaterialStateManager.hxx index b37cf5010..52400b283 100644 --- a/include/MGIS/Behaviour/MaterialStateManager.hxx +++ b/include/MGIS/Behaviour/MaterialStateManager.hxx @@ -22,6 +22,10 @@ #include #include #include +#ifdef MGIS_HAVE_HDF5 +#include "MGIS/Utilities/HDF5Support.hxx" +#endif /* MGIS_HAVE_HDF5 */ + #include "MGIS/Config.hxx" #include "MGIS/StorageMode.hxx" @@ -354,6 +358,37 @@ namespace mgis::behaviour { const mgis::behaviour::MaterialStateManager&, const std::string_view); +#ifdef MGIS_HAVE_HDF5 + + /*! + * \brief structure used to customize the saving of a `MaterialStateManager` + */ + struct MaterialStateManagerSavingOptions { + const bool allow_overwrite = true; + const bool save_gradients = true; + const bool save_thermodynamic_forces = true; + const bool save_stored_energies = true; + const bool save_dissipated_energies = true; + const bool save_mass_densities = true; + const bool save_material_properties = true; + const bool save_external_state_variables = true; + }; + + /*! + * \brief save a `MaterialStateManager` to a file + * \param[in] ctx: execution context + * \param[in] g: group + * \param[in] s: material state manager + * \param[in] opts: options + */ + MGIS_EXPORT [[nodiscard]] bool save( + Context&, + H5::Group&, + const MaterialStateManager&, + const MaterialStateManagerSavingOptions& = {}) noexcept; + +#endif /* MGIS_HAVE_HDF5 */ + } // end of namespace mgis::behaviour #endif /* LIB_MGIS_BEHAVIOUR_MATERIALSTATEMANAGER_HXX */ diff --git a/include/MGIS/Utilities/HDF5Support.hxx b/include/MGIS/Utilities/HDF5Support.hxx new file mode 100644 index 000000000..72b22ca53 --- /dev/null +++ b/include/MGIS/Utilities/HDF5Support.hxx @@ -0,0 +1,182 @@ +/*! + * \file MGIS/Utilities/HDF5Support.hxx + * \brief + * \author Thomas Helfer + * \date 29/01/2026 + * \copyright (C) Copyright Thomas Helfer 2018. + * Use, modification and distribution are subject + * to one of the following licences: + * - GNU Lesser General Public License (LGPL), Version 3.0. (See accompanying + * file LGPL-3.0.txt) + * - CECILL-C, Version 1.0 (See accompanying files + * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). + */ + +#ifndef LIB_MGIS_UTILITIES_HDF5SUPPORT_HXX +#define LIB_MGIS_UTILITIES_HDF5SUPPORT_HXX + +#include +#include +#include +#include +#include "MGIS/Config.hxx" +#include "MGIS/Context.hxx" + +namespace mgis::utilities::hdf5 { + + template + H5::PredType getNativeType() noexcept; + // + template <> + MGIS_EXPORT [[nodiscard]] H5::PredType getNativeType() noexcept; + template <> + MGIS_EXPORT [[nodiscard]] H5::PredType getNativeType() noexcept; + template <> + MGIS_EXPORT [[nodiscard]] H5::PredType getNativeType() noexcept; + /*! + * \brief check if an object with the given path exists in the given group + * \param[in] g: group + * \param[in] p: path + */ + MGIS_EXPORT [[nodiscard]] bool exists(const H5::Group&, + const std::string&) noexcept; + /*! + * \brief check if a group with the given path exists in the given given + * \param[in] g: group + * \param[in] p: path + */ + MGIS_EXPORT [[nodiscard]] bool subGroupExists(const H5::Group&, + const std::string&) noexcept; + /*! + * \brief create a new group + * \param[out, in] ctx: execution context + * \param[in] g: parent group + * \param[in] n: group name + */ + MGIS_EXPORT [[nodiscard]] std::optional createGroup( + Context&, const H5::Group&, const std::string&) noexcept; + /*! + * \brief open a new group + * \param[out, in] ctx: execution context + * \param[in] g: parent group + * \param[in] n: group name + */ + MGIS_EXPORT [[nodiscard]] std::optional openGroup( + Context&, const H5::Group&, const std::string&) noexcept; + /*! + * \brief open a data set + * \param[out, in] ctx: execution context + * \param[in] g: parent group + * \param[in] n: data set name + */ + MGIS_EXPORT [[nodiscard]] std::optional openDataSet( + Context& ctx, const H5::Group&, const std::string&) noexcept; + /*! + * \brief remove a data set + * \param[out, in] ctx: execution context + * \param[in] g: parent group + * \param[in] n: data set name + */ + MGIS_EXPORT [[nodiscard]] bool removeDataSet(Context&, + const H5::Group&, + const std::string&) noexcept; + /*! + * \param[in] g: group + * \param[in] b: boolean allowing other objects than groups to be + * inside the given group + */ + MGIS_EXPORT [[nodiscard]] std::optional> + getSubGroupNames(Context&, const H5::Group&, const bool) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param[out] n: names + * \param[in] g: group + * \param[in] b: boolean allowing other objects than groups to be + * inside the given group + */ + MGIS_EXPORT [[nodiscard]] bool getSubGroupNames(Context&, + std::vector&, + const H5::Group&, + const bool) noexcept; + /*! + * \return all the dataset names in a give group + * \param[out, in] ctx: execution context + * \param[in] g: group + */ + MGIS_EXPORT [[nodiscard]] std::optional> + getDataSetNames(Context&, const H5::Group&) noexcept; + /*! + * \return all the dataset names in a give group + * \param[out, in] ctx: execution context + * \param[out] n: names + * \param[in] g: group + */ + MGIS_EXPORT [[nodiscard]] bool getDataSetNames(Context&, + std::vector&, + const H5::Group&) noexcept; + /*! + * \return true if the given group contains an object with the + * given name + * \param[out, in] ctx: execution context + * \param[in] g: group + * \param[in] n: name + */ + MGIS_EXPORT [[nodiscard]] std::optional contains( + Context&, const H5::Group&, const std::string&) noexcept; + /*! + * \brief delete an existing group or dataset if it exists + * \param[out, in] ctx: execution context + * \param[in] g: parent group + * \param[in] n: group or dataset name + */ + MGIS_EXPORT [[nodiscard]] bool unlinkIfExists(Context&, + const H5::Group&, + const std::string&) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param o : object to be written + * \param n : name of the dataset + * \param g : HDF5 group + * \param[in] b: allow overwrite + */ + MGIS_EXPORT [[nodiscard]] bool write(Context&, + H5::Group&, + const std::string&, + const real&, + const bool) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param[out] o : object to be written + * \param n : name of the dataset + * \param g : HDF5 group + */ + MGIS_EXPORT [[nodiscard]] bool read(Context&, + real&, + const H5::Group&, + const std::string&) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param o : object to be written + * \param n : name of the dataset + * \param g : HDF5 group + * \param[in] b: allow overwrite + */ + MGIS_EXPORT [[nodiscard]] bool write(Context&, + H5::Group&, + const std::string&, + std::span, + const bool) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param[out] o : object to be written + * \param n : name of the dataset + * \param g : HDF5 group + */ + MGIS_EXPORT [[nodiscard]] bool read(Context&, + std::vector&, + const H5::Group&, + const std::string&) noexcept; + +} // end of namespace mgis::utilities::hdf5 + +#endif /* LIB_MGIS_UTILITIES_HDF5SUPPORT_HXX */ diff --git a/include/MGIS/Utilities/Markdown.hxx b/include/MGIS/Utilities/Markdown.hxx index 623f238bc..91c64771d 100644 --- a/include/MGIS/Utilities/Markdown.hxx +++ b/include/MGIS/Utilities/Markdown.hxx @@ -18,18 +18,14 @@ #include #include "MGIS/Config.hxx" -namespace mgis { +namespace mgis::utilities { - namespace utilities { + /*! + * \return the heading signs associated with the given heading level + * \param[in] l: heading level + */ + MGIS_EXPORT std::string get_heading_signs(const mgis::size_type); - /*! - * \return the heading signs associated with the given heading level - * \param[in] l: heading level - */ - MGIS_EXPORT std::string get_heading_signs(const mgis::size_type); - - } // end of namespace utilities - -} // end of namespace mgis +} // end of namespace mgis::utilities #endif /* LIB_MGIS_UTILITIES_MARKDOWN_HXX */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c59ce9dd..76e4b4371 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,11 @@ if(enable-mgis-function) endif(MGIS_HAVE_TFEL) endif(enable-mgis-function) +if(MGIS_HDF5_SUPPORT) + list(APPEND MFrontGenericInterface_SOURCES HDF5Support.cxx) +endif(MGIS_HDF5_SUPPORT) + + mgis_library(MFrontGenericInterface ${MFrontGenericInterface_SOURCES}) @@ -143,10 +148,16 @@ endif(UNIX) if(MGIS_HDF5_SUPPORT) target_compile_definitions(MFrontGenericInterface PUBLIC MGIS_HAVE_HDF5) - target_link_libraries(MFrontGenericInterface PUBLIC hdf5::hdf5_cpp) + target_compile_definitions(MFrontGenericInterface PUBLIC ${HDF5_DEFINITIONS}) + target_include_directories(MFrontGenericInterface SYSTEM PUBLIC ${HDF5_INCLUDE_DIRS}) + target_link_libraries(MFrontGenericInterface PUBLIC ${HDF5_LIBRARIES}) + # target_link_libraries(MFrontGenericInterface PUBLIC hdf5::hdf5_cpp) if(enable-static) target_compile_definitions(MFrontGenericInterface-static PUBLIC MGIS_HAVE_HDF5) - target_link_libraries(MFrontGenericInterface-static PUBLIC hdf5::hdf5_cpp) + target_compile_definitions(MFrontGenericInterface-static PUBLIC ${HDF5_DEFINITIONS}) + target_include_directories(MFrontGenericInterface-static SYSTEM PUBLIC ${HDF5_INCLUDE_DIRS}) + target_link_libraries(MFrontGenericInterface-static PUBLIC ${HDF5_LIBRARIES}) + # target_link_libraries(MFrontGenericInterface-static PUBLIC hdf5::hdf5_cpp hdf5::hdf5) endif(enable-static) endif(MGIS_HDF5_SUPPORT) diff --git a/src/HDF5Support.cxx b/src/HDF5Support.cxx new file mode 100644 index 000000000..4118052d6 --- /dev/null +++ b/src/HDF5Support.cxx @@ -0,0 +1,377 @@ +/*! + * \file src/HDF5Support.cxx + * \brief + * \author Thomas Helfer + * \date 29/01/2026 + * \copyright (C) Copyright Thomas Helfer 2018. + * Use, modification and distribution are subject + * to one of the following licences: + * - GNU Lesser General Public License (LGPL), Version 3.0. (See accompanying + * file LGPL-3.0.txt) + * - CECILL-C, Version 1.0 (See accompanying files + * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). + */ + +#include "MGIS/Utilities/HDF5Support.hxx" + +namespace mgis::utilities::hdf5 { + + static InvalidResult registerH5ExceptionInErrorBacktraceWithoutSourceLocation( + ErrorBacktrace& e) noexcept { + try { + throw; + } catch (H5::GroupIException& exception) { + e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); + } catch (H5::Exception& exception) { + e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); + } catch (std::exception& exception) { + e.registerErrorMessageWithoutSourceLocation( + std::string{exception.what()}); + } catch (...) { + e.registerErrorMessageWithoutSourceLocation("unknown exception thrown"); + } + return {}; + } + +#ifdef MGIS_USE_SOURCE_LOCATION_INFORMATION + static InvalidResult registerH5ExceptionInErrorBacktrace( + ErrorBacktrace& e, const std::source_location& l) noexcept { + try { + throw; + } catch (H5::GroupIException& exception) { + e.registerErrorMessage(exception.getDetailMsg()); + } catch (H5::Exception& exception) { + e.registerErrorMessage(exception.getDetailMsg()); + } catch (std::exception& exception) { + e.registerErrorMessage(std::string{exception.what()}, l); + } catch (...) { + e.registerErrorMessage("unknown exception thrown"); + } + return {}; + } +#else + static InvalidResult registerH5ExceptionInErrorBacktrace( + ErrorBacktrace& e) noexcept { + return registerH5ExceptionInErrorBacktraceWithoutSourceLocation(e); + } +#endif + + static std::vector tokenize(std::string_view s, + const char c, + const bool keep_empty_strings) { + std::vector res; + auto b = std::string::size_type{}; + auto e = s.find_first_of(c, b); + while (std::string::npos != e || std::string::npos != b) { + // Found a token, add it to the vector. + res.push_back(std::string{s.substr(b, e - b)}); + if (keep_empty_strings) { + b = e == std::string::npos ? e : e + 1; + } else { + b = s.find_first_not_of(c, e); + } + e = s.find_first_of(c, b); + } + return res; + } // end of tokenize + + bool exists(const H5::Group& g, const std::string& p) noexcept { + // break paths + const auto leafs = tokenize(p, '/', false); + if (leafs.empty()) { + return false; + } + auto cpath = std::string{}; + for (const auto& l : leafs) { + if (!cpath.empty()) { + cpath += '/'; + } + cpath += l; + const auto b = H5Lexists(g.getId(), cpath.c_str(), H5P_DEFAULT) > 0; + if (!b) { + return false; + } + } + return true; + } + + bool subGroupExists(const H5::Group& g, const std::string& p) noexcept { + if (!exists(g, p)) { + return false; + } + H5O_info_t infobuf; +#ifdef H5Oget_info_by_name_vers +#if H5Oget_info_by_name_vers >= 3 + auto status = H5Oget_info_by_name(g.getId(), p.c_str(), &infobuf, + H5O_INFO_ALL, H5P_DEFAULT); +#else + auto status = + H5Oget_info_by_name(g.getId(), p.c_str(), &infobuf, H5P_DEFAULT); +#endif +#else + auto status = + H5Oget_info_by_name(g.getId(), p.c_str(), &infobuf, H5P_DEFAULT); +#endif + return (status >= 0) && (infobuf.type == H5O_TYPE_GROUP); + } + + std::optional createGroup(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + if (subGroupExists(g, n)) { + return openGroup(ctx, g, n); + } + try { + H5::Group gr = g.createGroup(n); + return gr; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return {}; + } // end of createGroup + + std::optional openGroup(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + try { + H5::Group gr = g.openGroup(n); + return gr; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return {}; + } // end of openGroup + + bool removeDataSet(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + try { + g.unlink(n); + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of removeDataSet + + std::optional openDataSet(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + try { + H5::DataSet d = g.openDataSet(n); + return d; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return {}; + } // end of openDataSet + + std::optional> getSubGroupNames( + Context& ctx, const H5::Group& g, const bool b) noexcept { + std::vector names; + if (!getSubGroupNames(ctx, names, g, b)) { + return {}; + } + return names; + } // end of getSubGroupNames + + bool getSubGroupNames(Context& ctx, + std::vector& n, + const H5::Group& g, + const bool b) noexcept { + n.clear(); + try { + const hsize_t s = g.getNumObjs(); + for (hsize_t i = 0; i != s; ++i) { + if (g.getObjTypeByIdx(i) == H5G_GROUP) { + n.push_back(g.getObjnameByIdx(i)); + } else if (!b) { + return ctx.registerErrorMessage("getSubGroupNames: object '" + + g.getObjnameByIdx(i) + + "' is not a group"); + } + } + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of getSubGroupNames + + std::optional> getDataSetNames( + Context& ctx, const H5::Group& g) noexcept { + std::vector names; + if (!getDataSetNames(ctx, names, g)) { + return {}; + } + return names; + } + + bool getDataSetNames(Context& ctx, + std::vector& n, + const H5::Group& g) noexcept { + n.clear(); + try { + const hsize_t s = g.getNumObjs(); + for (hsize_t i = 0; i != s; ++i) { + if (g.getObjTypeByIdx(i) == H5G_DATASET) { + n.push_back(g.getObjnameByIdx(i)); + } + } + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of getDataSetNames + + std::optional contains(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + try { + bool found = false; + const hsize_t s = g.getNumObjs(); + for (hsize_t i = 0; (i != s) && (!found); ++i) { + if (g.getObjnameByIdx(i) == n) { + found = true; + } + } + return found; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return {}; + } // end of contains + + bool unlinkIfExists(Context& ctx, + const H5::Group& g, + const std::string& n) noexcept { + try { + if (exists(g, n)) { + g.unlink(n); + } + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } + + template <> + [[nodiscard]] H5::PredType getNativeType() noexcept { + return H5::PredType::NATIVE_FLOAT; + } // end of getNativeType + + template <> + [[nodiscard]] H5::PredType getNativeType() noexcept { + return H5::PredType::NATIVE_DOUBLE; + } // end of getNativeType + + template <> + [[nodiscard]] H5::PredType getNativeType() noexcept { + return H5::PredType::NATIVE_LDOUBLE; + } // end of getNativeType + + MGIS_EXPORT [[nodiscard]] bool write(Context& ctx, + H5::Group& g, + const std::string& d, + const real& o, + const bool b) noexcept { + if (b) { + if (!unlinkIfExists(ctx, g, d)) { + return false; + } + } + try { + hsize_t dimsf[1] = {1}; + H5::DataSpace dataspace(1, dimsf); + auto dataset = g.createDataSet(d, getNativeType(), dataspace); + dataset.write(&o, getNativeType()); + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of write + + MGIS_EXPORT [[nodiscard]] bool read(Context& ctx, + real& o, + const H5::Group& g, + const std::string& d) noexcept { + auto odataset = openDataSet(ctx, g, d); + if (isInvalid(odataset)) { + return false; + } + try { + const auto s = odataset->getSpace(); + if (s.getSimpleExtentNdims() != 1) { + return ctx.registerErrorMessage("madnex::read: invalid type size"); + } + hsize_t dims[1]; + s.getSimpleExtentDims(dims); + if (dims[0] != 1) { + return ctx.registerErrorMessage("madnex::read: invalid type"); + } + odataset->read(&o, getNativeType()); + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of read + + bool write(Context& ctx, + H5::Group& g, + const std::string& d, + std::span o, + const bool b) noexcept { + if (b) { + if (!unlinkIfExists(ctx, g, d)) { + return false; + } + } + try { + if (o.empty()) { + auto c = real{}; + hsize_t dimsf[1]; + dimsf[0] = 1u; + H5::DataSpace dataspace(1, dimsf); + H5::StrType datatype(getNativeType()); + auto dataset = g.createDataSet(d, datatype, dataspace); + dataset.write(&c, getNativeType()); + } else { + hsize_t dimsf[1]; + dimsf[0] = o.size(); + H5::DataSpace dataspace(1, dimsf); + auto dataset = g.createDataSet(d, getNativeType(), dataspace); + dataset.write(&o[0], getNativeType()); + } + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } + + bool read(Context& ctx, + std::vector& o, + const H5::Group& g, + const std::string& d) noexcept { + try { + auto odataset = openDataSet(ctx, g, d); + if (isInvalid(odataset)) { + return false; + } + H5::DataSpace filespace = odataset->getSpace(); + hsize_t dims[1]; + filespace.getSimpleExtentDims(dims); + o.resize(dims[0]); + odataset->read(&o[0], getNativeType()); + return true; + } catch (...) { + registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } + +} // end of namespace mgis::utilities::hdf5 \ No newline at end of file diff --git a/src/MaterialDataManager.cxx b/src/MaterialDataManager.cxx index 21bcf949d..a2c3240b2 100644 --- a/src/MaterialDataManager.cxx +++ b/src/MaterialDataManager.cxx @@ -182,4 +182,35 @@ namespace mgis::behaviour { return outputs; } // end of allocatePostProcessingVariables + bool save(Context& ctx, + H5::Group& g, + const MaterialDataManager& m, + const MaterialDataManagerSavingOptions& opts) noexcept { + using namespace mgis::utilities::hdf5; + // group for the state at the beginning of the time step + if (!unlinkIfExists(ctx, g, "s0")) { + return false; + } + auto og_s0 = openGroup(ctx, g, "s0"); + if (isInvalid(og_s0)) { + return false; + } + // group for the state at the end of the time step + if (!unlinkIfExists(ctx, g, "s1")) { + return false; + } + auto og_s1 = openGroup(ctx, g, "s1"); + if (isInvalid(og_s1)) { + return false; + } + // + if (!save(ctx, *og_s0, m.s0, opts)) { + return false; + } + if (!save(ctx, *og_s1, m.s1, opts)) { + return false; + } + return true; + } // end of save + } // end of namespace mgis::behaviour diff --git a/src/MaterialStateManager.cxx b/src/MaterialStateManager.cxx index 3dea5d93b..730c90e30 100644 --- a/src/MaterialStateManager.cxx +++ b/src/MaterialStateManager.cxx @@ -11,7 +11,6 @@ * - CECILL-C, Version 1.0 (See accompanying files * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). */ -#include #include #include "MGIS/Raise.hxx" @@ -506,4 +505,85 @@ namespace mgis::behaviour { } } // end of extractInternalStateVariable + bool save(Context& ctx, + H5::Group& g, + const std::string& n, + const MaterialStateManager::FieldHolder& f, + const MaterialStateManagerSavingOptions& opts) noexcept { + using namespace mgis::utilities::hdf5; + if (std::holds_alternative(f.value)) { + return write(ctx, g, n, std::get(f.value), opts.allow_overwrite); + } else if (std::holds_alternative>(f.value)) { + return write(ctx, g, n, std::get>(f.value), + opts.allow_overwrite); + } + return write(ctx, g, n, std::get>(f.value), + opts.allow_overwrite); + } // end of save + + bool save(Context& ctx, + H5::Group& g, + const MaterialStateManager& s, + const MaterialStateManagerSavingOptions& opts) noexcept { + using namespace mgis::utilities::hdf5; + if (opts.save_gradients) { + if (!write(ctx, g, "gradients", s.gradients, opts.allow_overwrite)) { + return false; + } + } + if (opts.save_thermodynamic_forces) { + if (!write(ctx, g, "thermodynamic_forces", s.thermodynamic_forces, + opts.allow_overwrite)) { + return false; + } + } + if (s.mass_density.has_value()) { + if (!save(ctx, g, "mass_density", *(s.mass_density), opts)) { + return false; + } + } + // + if (opts.save_material_properties) { + auto og_mp = openGroup(ctx, g, "material_properties"); + if (isInvalid(og_mp)) { + return false; + } + for (const auto& [n, mp] : s.material_properties) { + if (!save(ctx, *og_mp, n, mp, opts)) { + return false; + } + } + } + // + if (!write(ctx, g, "internal_state_variables", s.internal_state_variables, + opts.allow_overwrite)) { + return false; + } + if ((opts.save_stored_energies) && (!s.stored_energies.empty())) { + if (!write(ctx, g, "stored_energies", s.stored_energies, + opts.allow_overwrite)) { + return false; + } + } + if ((opts.save_dissipated_energies) && (!s.dissipated_energies.empty())) { + if (!write(ctx, g, "dissipated_energies", s.dissipated_energies, + opts.allow_overwrite)) { + return false; + } + } + // + if (opts.save_external_state_variables) { + auto og_esv = createGroup(ctx, g, "external_state_variables"); + if (isInvalid(og_esv)) { + return false; + } + for (const auto& [n, esv] : s.external_state_variables) { + if (!save(ctx, *og_esv, n, esv, opts)) { + return false; + } + } + } + return true; + } // end of save + } // end of namespace mgis::behaviour diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 236f07c1b..e74ce6210 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -665,4 +665,22 @@ endif(MGIS_HAVE_MFRONT_SUPPORT) endif(MGIS_HAVE_TFEL) +if(MGIS_HDF5_SUPPORT) + + add_executable(HDF5Test EXCLUDE_FROM_ALL HDF5Test.cxx) + target_link_libraries(HDF5Test PRIVATE MFrontGenericInterface) + add_test(NAME HDF5Test + COMMAND HDF5Test "$") + add_dependencies(check HDF5Test) + if((CMAKE_HOST_WIN32) AND (NOT MSYS)) + set_property(TEST HDF5Test + PROPERTY DEPENDS BehaviourTest + PROPERTY ENVIRONMENT "PATH=$\;${MGIS_PATH_STRING}") + else((CMAKE_HOST_WIN32) AND (NOT MSYS)) + set_property(TEST HDF5Test + PROPERTY DEPENDS BehaviourTest) + endif((CMAKE_HOST_WIN32) AND (NOT MSYS)) + +endif(MGIS_HDF5_SUPPORT) + endif(enable-mgis-function) diff --git a/tests/HDF5Test.cxx b/tests/HDF5Test.cxx new file mode 100644 index 000000000..77c9a5ec0 --- /dev/null +++ b/tests/HDF5Test.cxx @@ -0,0 +1,53 @@ +/*! + * \file HDF5Test.cxx + * \brief + * \author Thomas Helfer + * \date 29/01/2026 + */ + +#include +#include +#include +#include +#include "MGIS/Behaviour/State.hxx" +#include "MGIS/Behaviour/Behaviour.hxx" +#include "MGIS/Behaviour/MaterialDataManager.hxx" +#include "MGIS/Behaviour/Integrate.hxx" + +int main(const int argc, const char* const* argv) { + using namespace mgis; + using namespace mgis::behaviour; + using namespace mgis::utilities::hdf5; + if (argc != 2) { + std::cerr << "IntegrateTest: invalid number of arguments\n"; + std::exit(-1); + } + auto ctx = Context{}; + auto f = H5::H5File("HDF5Test.h5", H5F_ACC_TRUNC); + auto r = f.openGroup("/"); + const auto b = load(argv[1], "Norton", Hypothesis::TRIDIMENSIONAL); + MaterialDataManager m{b, 100}; + const auto de = 5.e-5; + // initialize the external state variable + m.s1.external_state_variables["Temperature"] = 293.15; + // copy d.s1 in d.s0 + update(m); + for (size_type idx = 0; idx != m.n; ++idx) { + m.s1.gradients[idx * m.s1.gradients_stride] = de; + } + const auto dt = real(180); + for (size_type i = 0; i != 20; ++i) { + auto og = createGroup(ctx, r, "TimeStep_" + std::string{i}); + if (isInvalid(og)) { + std::cerr << ctx.getErrorMessage() << '\n'; + return EXIT_FAILURE; + } + integrate(m, IntegrationType::INTEGRATION_NO_TANGENT_OPERATOR, dt, 0, m.n); + if (!save(ctx, *og, m.s1)) { + std::cerr << ctx.getErrorMessage() << '\n'; + return EXIT_FAILURE; + } + update(m); + } + return EXIT_SUCCESS; +} // end of main From e89e05429e0e023ab9e374511a72b95738a47c9f Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 14:24:24 +0100 Subject: [PATCH 3/8] implementation of the function for MaterialStateManager. format --- bindings/python/src/Variable.cxx | 5 +- .../MGIS/Behaviour/MaterialDataManager.hxx | 15 +- .../MGIS/Behaviour/MaterialStateManager.hxx | 76 +++- include/MGIS/Behaviour/Variable.hxx | 46 ++- include/MGIS/ErrorBacktrace.hxx | 46 ++- include/MGIS/Utilities/HDF5Support.hxx | 56 ++- src/Behaviour.cxx | 11 +- src/ErrorBacktrace.cxx | 6 +- src/HDF5Support.cxx | 85 +++-- src/MaterialStateManager.cxx | 340 ++++++++++++++++-- src/Model.cxx | 5 +- src/Variable.cxx | 29 +- tests/HDF5Test.cxx | 70 +++- ...dCoalescedMemoryAccessFunctionViewTest.cxx | 4 +- 14 files changed, 682 insertions(+), 112 deletions(-) diff --git a/bindings/python/src/Variable.cxx b/bindings/python/src/Variable.cxx index 1517ed655..4c45915eb 100644 --- a/bindings/python/src/Variable.cxx +++ b/bindings/python/src/Variable.cxx @@ -104,7 +104,10 @@ void declareVariable(pybind11::module_& m) { // free functions m.def("getVariable", getVariableByString, pybind11::return_value_policy::reference); - m.def("getVariableSize", &mgis::behaviour::getVariableSize); + m.def("getVariableSize", + pybind11::overload_cast( + &mgis::behaviour::getVariableSize)); m.def("getVariableSize", getVariableSizeByString); m.def("getArraySize", &mgis::behaviour::getArraySize); m.def("getVariableOffset", getVariableOffsetByString); diff --git a/include/MGIS/Behaviour/MaterialDataManager.hxx b/include/MGIS/Behaviour/MaterialDataManager.hxx index 7bdb69c3d..132015b77 100644 --- a/include/MGIS/Behaviour/MaterialDataManager.hxx +++ b/include/MGIS/Behaviour/MaterialDataManager.hxx @@ -293,7 +293,7 @@ namespace mgis::behaviour { : public MaterialStateManagerSavingOptions {}; /*! - * \brief save a `MaterialDataManager` to a file + * \brief save a `MaterialDataManager` to an HDF5 group * \param[in] ctx: execution context * \param[in] g: group * \param[in] m: material data manager @@ -305,6 +305,19 @@ namespace mgis::behaviour { const MaterialDataManager&, const MaterialDataManagerSavingOptions& = {}) noexcept; + /*! + * \brief restore a `MaterialDataManager` from an HDF5 group + * \param[in] ctx: execution context + * \param[in] g: group + * \param[in] m: material data manager + * \param[in] opts: options + */ + MGIS_EXPORT [[nodiscard]] bool restore( + Context&, + H5::Group&, + const MaterialDataManager&, + const MaterialDataManagerSavingOptions& = {}) noexcept; + #endif /* MGIS_HAVE_HDF5 */ } // end of namespace mgis::behaviour diff --git a/include/MGIS/Behaviour/MaterialStateManager.hxx b/include/MGIS/Behaviour/MaterialStateManager.hxx index 52400b283..c1a89c368 100644 --- a/include/MGIS/Behaviour/MaterialStateManager.hxx +++ b/include/MGIS/Behaviour/MaterialStateManager.hxx @@ -244,6 +244,37 @@ namespace mgis::behaviour { const MaterialStateManager::StorageMode = MaterialStateManager::LOCAL_STORAGE, const MaterialStateManager::UpdatePolicy = MaterialStateManager::UPDATE); + /*! + * \brief set the given material property + * \param[out] m: material data manager + * \param[in] n: name + * \param[in] v: value + * \param[in] p: update policy + */ + MGIS_EXPORT [[nodiscard]] bool setMaterialProperty( + Context&, + MaterialStateManager&, + const std::string_view&, + const real, + const MaterialStateManager::UpdatePolicy = + MaterialStateManager::UPDATE) noexcept; + /*! + * \brief set the given material property + * \param[out] m: material data manager + * \param[in] n: name + * \param[in] v: values + * \param[in] s: storage mode + * \param[in] p: update policy + */ + MGIS_EXPORT [[nodiscard]] bool setMaterialProperty( + Context&, + MaterialStateManager&, + const std::string_view&, + const std::span&, + const MaterialStateManager::StorageMode = + MaterialStateManager::LOCAL_STORAGE, + const MaterialStateManager::UpdatePolicy = + MaterialStateManager::UPDATE) noexcept; /*! * \return true if the given external state variable is defined. * \param[out] m: material data manager @@ -375,7 +406,7 @@ namespace mgis::behaviour { }; /*! - * \brief save a `MaterialStateManager` to a file + * \brief save a `MaterialStateManager` to an HDF5 group * \param[in] ctx: execution context * \param[in] g: group * \param[in] s: material state manager @@ -387,6 +418,49 @@ namespace mgis::behaviour { const MaterialStateManager&, const MaterialStateManagerSavingOptions& = {}) noexcept; + /*! + * \brief structure used to customize how to restore a `MaterialStateManager` + */ + struct MaterialStateManagerRestoreOptions { + const bool restore_gradients = true; + const bool restore_thermodynamic_forces = true; + /*! + * \brief flag stating if the stored energies shall be read + * + * \note this flag is ignored if the behaviour does not compute the + * stored energy + */ + const bool restore_stored_energies = true; + /*! + * \brief flag stating if the dissipated energies shall be read + * + * \note this flag is ignored if the behaviour does not compute the + * dissipated energy + */ + const bool restore_dissipated_energies = true; + const bool restore_internal_state_variables = true; + const bool restore_mass_densities = true; + const bool restore_material_properties = true; + //! \brief list of material properties that shall not be restored + const std::vector ignored_material_properties = {}; + const bool restore_external_state_variables = true; + //! \brief list of external state variables that shall not be restored + const std::vector ignored_external_state_variables = {}; + }; // end of MaterialStateManagerRestoreOptions + + /*! + * \brief restore a `MaterialStateManager` from a HDF5 group + * \param[in] ctx: execution context + * \param[in] g: group + * \param[in] s: material state manager + * \param[in] opts: options + */ + MGIS_EXPORT [[nodiscard]] bool restore( + Context&, + MaterialStateManager&, + const H5::Group&, + const MaterialStateManagerRestoreOptions&) noexcept; + #endif /* MGIS_HAVE_HDF5 */ } // end of namespace mgis::behaviour diff --git a/include/MGIS/Behaviour/Variable.hxx b/include/MGIS/Behaviour/Variable.hxx index fe167d8d5..485aaf7d7 100644 --- a/include/MGIS/Behaviour/Variable.hxx +++ b/include/MGIS/Behaviour/Variable.hxx @@ -19,6 +19,7 @@ #include #include #include "MGIS/Config.hxx" +#include "MGIS/Context.hxx" #include "MGIS/Behaviour/Hypothesis.hxx" namespace mgis::behaviour { @@ -57,53 +58,72 @@ namespace mgis::behaviour { * \param[in] vs: variables * \param[in] n: name */ - MGIS_EXPORT bool contains(const std::vector &, - const std::string_view); + MGIS_EXPORT [[nodiscard]] bool contains(const std::vector &, + const std::string_view) noexcept; /*! * \return the variable with the given name * \param[in] vs: variables * \param[in] n: name */ - MGIS_EXPORT const Variable &getVariable(const std::vector &, - const std::string_view); + MGIS_EXPORT [[nodiscard]] const Variable &getVariable( + const std::vector &, const std::string_view); + /*! + * \return the variable with the given name + * \param[in] vs: variables + * \param[in] n: name + */ + MGIS_EXPORT [[nodiscard]] std::optional getVariable( + Context &, + const std::vector &, + const std::string_view) noexcept; /*! * \return the type of a variable from an identifier * \param[in] id: type identifier */ - MGIS_EXPORT Variable::Type getVariableType(const int); + MGIS_EXPORT [[nodiscard]] Variable::Type getVariableType(const int); /*! * \return a symbolic representation from a type identifier * \param[in] id: type identifier */ - MGIS_EXPORT std::string getVariableTypeSymbolicRepresentation(const int); + MGIS_EXPORT [[nodiscard]] std::string getVariableTypeSymbolicRepresentation( + const int); + /*! + * \return the size of a variable + * \param[in] v: variable + * \param[in] h: modelling hypothesis + */ + MGIS_EXPORT [[nodiscard]] size_type getVariableSize(const Variable &, + const Hypothesis); /*! * \return the size of a variable + * \param[in, out] ctx: execution context * \param[in] v: variable * \param[in] h: modelling hypothesis */ - MGIS_EXPORT size_type getVariableSize(const Variable &, const Hypothesis); + MGIS_EXPORT [[nodiscard]] std::optional getVariableSize( + Context &, const Variable &, const Hypothesis) noexcept; /*! * \return the size of an array that may contain the values described by the * given array of variables * \param[in] vs: variables * \param[in] h: modelling hypothesis */ - MGIS_EXPORT size_type getArraySize(const std::vector &, - const Hypothesis); + MGIS_EXPORT [[nodiscard]] size_type getArraySize( + const std::vector &, const Hypothesis); /*! * \return the offset of the given variable for the given hypothesis * \param[in] vs: variables * \param[in] n: variable name * \param[in] h: modelling hypothesis */ - MGIS_EXPORT size_type getVariableOffset(const std::vector &, - const std::string_view, - const Hypothesis); + MGIS_EXPORT [[nodiscard]] size_type getVariableOffset( + const std::vector &, const std::string_view, const Hypothesis); /*! * \return the type of the given variable as a string * \param[in] v: variable */ - MGIS_EXPORT std::string getVariableTypeAsString(const Variable &); + MGIS_EXPORT [[nodiscard]] std::string getVariableTypeAsString( + const Variable &); } // end of namespace mgis::behaviour diff --git a/include/MGIS/ErrorBacktrace.hxx b/include/MGIS/ErrorBacktrace.hxx index ad47d03c8..fd39fe13e 100644 --- a/include/MGIS/ErrorBacktrace.hxx +++ b/include/MGIS/ErrorBacktrace.hxx @@ -48,7 +48,13 @@ namespace mgis { //! \brief specify if error reporting shall be fatal static void unsetErrorReportingAsFatal() noexcept; #ifdef MGIS_USE_SOURCE_LOCATION_INFORMATION - InvalidResult registerErrorMessage( + /*! + * \brief register a new error message + * \param[in] e: error message + * \param[in] l: description of the call site + * \note for convenience, this method always return an invalid object + */ + [[nodiscard]] InvalidResult registerErrorMessage( const char *const, const std::source_location & = std::source_location::current()) override final; @@ -58,25 +64,27 @@ namespace mgis { * \param[in] l: description of the call site * \note for convenience, this method always return an invalid object */ - InvalidResult registerErrorMessage( + [[nodiscard]] InvalidResult registerErrorMessage( const ErrorReport, const std::source_location & = std::source_location::current()) noexcept; #else - InvalidResult registerErrorMessage(const char *const) override final; + [[nodiscard]] InvalidResult registerErrorMessage( + const char *const) override final; /*! * \brief register a new error message * \param[in] e: error code * \note for convenience, this method always return an invalid results */ - InvalidResult registerErrorMessage(const ErrorReport) noexcept; + [[nodiscard]] InvalidResult registerErrorMessage( + const ErrorReport) noexcept; #endif /*! * \brief register a new error message * \param[in] e: error code * \note for convenience, this method always return `InvalidResult` */ - InvalidResult registerErrorMessageWithoutSourceLocation( + [[nodiscard]] InvalidResult registerErrorMessageWithoutSourceLocation( const ErrorReport) noexcept; //! \brief remove the error messages void clearErrorMessages() noexcept; @@ -84,15 +92,15 @@ namespace mgis { * \return the registered error messages * \param[in] b: boolean. If true, the error message are cleared */ - std::string getErrorMessage(const bool = true) noexcept; + [[nodiscard]] std::string getErrorMessage(const bool = true) noexcept; /*! * \return the error message without the decoration (file name, function * name, line number) \param[in] b: boolean. If true, the error message are * cleared */ - std::string getRawErrorMessage(const bool = true) noexcept; + [[nodiscard]] std::string getRawErrorMessage(const bool = true) noexcept; //! \return if the list registered error messages is empty - bool empty() const noexcept; + [[nodiscard]] bool empty() const noexcept; //! \brief destructor ~ErrorBacktrace() noexcept override; @@ -129,25 +137,31 @@ namespace mgis { #ifdef MGIS_USE_SOURCE_LOCATION_INFORMATION /*! * \brief a custom Lippincott-like function that extract error messages from - * an exception an \param[out] e: error back trace handler \param[in] l: - * description of the call site + * an exception + * + * \param[out] e: error back trace handler + * \param[in] l: description of the call site */ - MGIS_EXPORT InvalidResult registerExceptionInErrorBacktrace( + MGIS_EXPORT [[nodiscard]] InvalidResult registerExceptionInErrorBacktrace( ErrorBacktrace &, const std::source_location & = std::source_location::current()) noexcept; #else /*! * \brief a custom Lippincott-like function that extract error messages from - * an exception an \param[out] e: error back trace handler + * an exception + * + * \param[out] e: error back trace handler */ - MGIS_EXPORT InvalidResult - registerExceptionInErrorBacktrace(ErrorBacktrace &) noexcept; + MGIS_EXPORT [[nodiscard]] InvalidResult registerExceptionInErrorBacktrace( + ErrorBacktrace &) noexcept; #endif /*! * \brief a custom Lippincott-like function that extract error messages from - * an exception an \param[out] e: error back trace handler + * an exception + * + *\param[out] e: error back trace handler */ - MGIS_EXPORT InvalidResult + MGIS_EXPORT [[nodiscard]] InvalidResult registerExceptionInErrorBacktraceWithoutSourceLocation( ErrorBacktrace &) noexcept; diff --git a/include/MGIS/Utilities/HDF5Support.hxx b/include/MGIS/Utilities/HDF5Support.hxx index 72b22ca53..f5c7fc4f5 100644 --- a/include/MGIS/Utilities/HDF5Support.hxx +++ b/include/MGIS/Utilities/HDF5Support.hxx @@ -134,9 +134,9 @@ namespace mgis::utilities::hdf5 { const std::string&) noexcept; /*! * \param[out, in] ctx: execution context - * \param o : object to be written - * \param n : name of the dataset * \param g : HDF5 group + * \param n : name of the dataset + * \param o : object to be written * \param[in] b: allow overwrite */ MGIS_EXPORT [[nodiscard]] bool write(Context&, @@ -147,8 +147,8 @@ namespace mgis::utilities::hdf5 { /*! * \param[out, in] ctx: execution context * \param[out] o : object to be written - * \param n : name of the dataset * \param g : HDF5 group + * \param n : name of the dataset */ MGIS_EXPORT [[nodiscard]] bool read(Context&, real&, @@ -156,9 +156,9 @@ namespace mgis::utilities::hdf5 { const std::string&) noexcept; /*! * \param[out, in] ctx: execution context - * \param o : object to be written - * \param n : name of the dataset * \param g : HDF5 group + * \param n : name of the dataset + * \param o : object to be written * \param[in] b: allow overwrite */ MGIS_EXPORT [[nodiscard]] bool write(Context&, @@ -168,14 +168,56 @@ namespace mgis::utilities::hdf5 { const bool) noexcept; /*! * \param[out, in] ctx: execution context - * \param[out] o : object to be written - * \param n : name of the dataset + * \param[out] o : object to be read * \param g : HDF5 group + * \param n : name of the dataset */ MGIS_EXPORT [[nodiscard]] bool read(Context&, std::vector&, const H5::Group&, const std::string&) noexcept; + /*! + * \param[out, in] ctx: execution context + * \param[out] o : object to be read + * \param g : HDF5 group + * \param n : name of the dataset + */ + MGIS_EXPORT [[nodiscard]] bool read(Context&, + std::span, + const H5::Group&, + const std::string&) noexcept; + +#ifdef MGIS_USE_SOURCE_LOCATION_INFORMATION + /*! + * \brief a custom Lippincott-like function that extract error messages from + * a standard exception or an exception generated by the HD5 library + * + * \param[out] e: error back trace handler + * \param[in] l: description of the call site + */ + MGIS_EXPORT [[nodiscard]] InvalidResult registerH5ExceptionInErrorBacktrace( + ErrorBacktrace&, + const std::source_location& = std::source_location::current()) noexcept; +#else + /*! + * \brief a custom Lippincott-like function that extract error messages from + * a standard exception or an exception generated by the HD5 library + * + * \param[out] e: error back trace handler + * \param[in] l: description of the call site + */ + MGIS_EXPORT [[nodiscard]] InvalidResult registerH5ExceptionInErrorBacktrace( + ErrorBacktrace&) noexcept; +#endif + /*! + * \brief a custom Lippincott-like function that extract error messages from + *a standard exception or an exception generated by the HD5 library + * + *\param[out] e: error back trace handler + */ + MGIS_EXPORT [[nodiscard]] InvalidResult + registerH5ExceptionInErrorBacktraceWithoutSourceLocation( + ErrorBacktrace&) noexcept; } // end of namespace mgis::utilities::hdf5 diff --git a/src/Behaviour.cxx b/src/Behaviour.cxx index ae86d0045..25e096ae2 100644 --- a/src/Behaviour.cxx +++ b/src/Behaviour.cxx @@ -12,8 +12,7 @@ * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). */ -#include - +#include #include #include #include "MGIS/Raise.hxx" @@ -197,7 +196,7 @@ namespace mgis::behaviour { try { return load(l, f, h); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } // end of load @@ -210,7 +209,7 @@ namespace mgis::behaviour { try { return load(o, l, f, h); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } @@ -286,7 +285,7 @@ namespace mgis::behaviour { try { return loadFromDatabase(opts); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } // end of loadFromDatabase @@ -298,7 +297,7 @@ namespace mgis::behaviour { try { return loadFromDatabase(o, opts); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } // end of loadFromDatabase diff --git a/src/ErrorBacktrace.cxx b/src/ErrorBacktrace.cxx index 269f61570..a0fe86153 100644 --- a/src/ErrorBacktrace.cxx +++ b/src/ErrorBacktrace.cxx @@ -4,6 +4,7 @@ * \date 04/11/2022 */ +#include #include #include #include "MGIS/Raise.hxx" @@ -223,10 +224,11 @@ namespace mgis { try { throw; } catch (std::exception &exception) { - e.registerErrorMessageWithoutSourceLocation( + std::ignore = e.registerErrorMessageWithoutSourceLocation( std::string{exception.what()}); } catch (...) { - e.registerErrorMessageWithoutSourceLocation("unknown exception thrown"); + std::ignore = e.registerErrorMessageWithoutSourceLocation( + "unknown exception thrown"); } return {}; } diff --git a/src/HDF5Support.cxx b/src/HDF5Support.cxx index 4118052d6..55b243b4f 100644 --- a/src/HDF5Support.cxx +++ b/src/HDF5Support.cxx @@ -12,45 +12,49 @@ * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). */ +#include #include "MGIS/Utilities/HDF5Support.hxx" namespace mgis::utilities::hdf5 { - static InvalidResult registerH5ExceptionInErrorBacktraceWithoutSourceLocation( + InvalidResult registerH5ExceptionInErrorBacktraceWithoutSourceLocation( ErrorBacktrace& e) noexcept { try { throw; } catch (H5::GroupIException& exception) { - e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); + std::ignore = + e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); } catch (H5::Exception& exception) { - e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); + std::ignore = + e.registerErrorMessageWithoutSourceLocation(exception.getDetailMsg()); } catch (std::exception& exception) { - e.registerErrorMessageWithoutSourceLocation( + std::ignore = e.registerErrorMessageWithoutSourceLocation( std::string{exception.what()}); } catch (...) { - e.registerErrorMessageWithoutSourceLocation("unknown exception thrown"); + std::ignore = e.registerErrorMessageWithoutSourceLocation( + "unknown exception thrown"); } return {}; } #ifdef MGIS_USE_SOURCE_LOCATION_INFORMATION - static InvalidResult registerH5ExceptionInErrorBacktrace( + InvalidResult registerH5ExceptionInErrorBacktrace( ErrorBacktrace& e, const std::source_location& l) noexcept { try { throw; } catch (H5::GroupIException& exception) { - e.registerErrorMessage(exception.getDetailMsg()); + std::ignore = e.registerErrorMessage(exception.getDetailMsg()); } catch (H5::Exception& exception) { - e.registerErrorMessage(exception.getDetailMsg()); + std::ignore = e.registerErrorMessage(exception.getDetailMsg()); } catch (std::exception& exception) { - e.registerErrorMessage(std::string{exception.what()}, l); + std::ignore = e.registerErrorMessage(std::string{exception.what()}, l); } catch (...) { - e.registerErrorMessage("unknown exception thrown"); + std::ignore = e.registerErrorMessage("unknown exception thrown"); } return {}; } #else - static InvalidResult registerH5ExceptionInErrorBacktrace( + InvalidResult registerH5ExceptionInErrorBacktrace( ErrorBacktrace& e) noexcept { return registerH5ExceptionInErrorBacktraceWithoutSourceLocation(e); } @@ -125,7 +129,7 @@ namespace mgis::utilities::hdf5 { H5::Group gr = g.createGroup(n); return gr; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return {}; } // end of createGroup @@ -137,7 +141,7 @@ namespace mgis::utilities::hdf5 { H5::Group gr = g.openGroup(n); return gr; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return {}; } // end of openGroup @@ -149,7 +153,7 @@ namespace mgis::utilities::hdf5 { g.unlink(n); return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } // end of removeDataSet @@ -161,7 +165,7 @@ namespace mgis::utilities::hdf5 { H5::DataSet d = g.openDataSet(n); return d; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return {}; } // end of openDataSet @@ -193,7 +197,7 @@ namespace mgis::utilities::hdf5 { } return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } // end of getSubGroupNames @@ -220,7 +224,7 @@ namespace mgis::utilities::hdf5 { } return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } // end of getDataSetNames @@ -238,7 +242,7 @@ namespace mgis::utilities::hdf5 { } return found; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return {}; } // end of contains @@ -252,7 +256,7 @@ namespace mgis::utilities::hdf5 { } return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } @@ -289,7 +293,7 @@ namespace mgis::utilities::hdf5 { dataset.write(&o, getNativeType()); return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } // end of write @@ -315,7 +319,7 @@ namespace mgis::utilities::hdf5 { odataset->read(&o, getNativeType()); return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } // end of read @@ -348,7 +352,7 @@ namespace mgis::utilities::hdf5 { } return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } @@ -357,21 +361,46 @@ namespace mgis::utilities::hdf5 { std::vector& o, const H5::Group& g, const std::string& d) noexcept { + auto odataset = openDataSet(ctx, g, d); + if (isInvalid(odataset)) { + return false; + } try { - auto odataset = openDataSet(ctx, g, d); - if (isInvalid(odataset)) { - return false; - } H5::DataSpace filespace = odataset->getSpace(); hsize_t dims[1]; filespace.getSimpleExtentDims(dims); o.resize(dims[0]); - odataset->read(&o[0], getNativeType()); + odataset->read(o.data(), getNativeType()); return true; } catch (...) { - registerH5ExceptionInErrorBacktrace(ctx); + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } return false; } + bool read(Context& ctx, + std::span values, + const H5::Group& g, + const std::string& n) noexcept { + using namespace mgis::utilities::hdf5; + const auto odataset = openDataSet(ctx, g, n); + if (isInvalid(odataset)) { + return false; + } + try { + H5::DataSpace filespace = odataset->getSpace(); + hsize_t dims[1]; + filespace.getSimpleExtentDims(dims); + if (values.size() != dims[0]) { + return ctx.registerErrorMessage("invalid size while reading dataset '" + + n + "'"); + } + odataset->read(values.data(), getNativeType()); + return true; + } catch (...) { + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of restore + } // end of namespace mgis::utilities::hdf5 \ No newline at end of file diff --git a/src/MaterialStateManager.cxx b/src/MaterialStateManager.cxx index 730c90e30..c61e3ba64 100644 --- a/src/MaterialStateManager.cxx +++ b/src/MaterialStateManager.cxx @@ -12,6 +12,7 @@ * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). */ +#include #include #include "MGIS/Raise.hxx" #include "MGIS/Behaviour/Behaviour.hxx" @@ -135,9 +136,9 @@ namespace mgis::behaviour { MaterialStateManager::~MaterialStateManager() = default; - static MaterialStateManager::FieldHolder& getFieldHolder( + [[nodiscard]] static MaterialStateManager::FieldHolder& getFieldHolder( std::map& m, - const std::string_view& n) { + const std::string_view& n) noexcept { // #if __cplusplus > 201103L // return m[n]; // #else /* __cplusplus > 201103L */ @@ -145,17 +146,6 @@ namespace mgis::behaviour { // #endif /* __cplusplus > 201103L */ } // end of getFieldHolder - // static std::map::iterator - // getFieldHolderIterator( - // std::map& m, - // const std::string_view& n) { - // // #if __cplusplus > 201103L - // // return m.find(n); - // // #else /* __cplusplus > 201103L */ - // return m.find(n.to_string()); - // // #endif /* __cplusplus > 201103L */ - // } // end of getFieldHolder - static std::map::const_iterator getFieldHolderIterator( @@ -182,12 +172,34 @@ namespace mgis::behaviour { .value = v, .shall_be_updated = (p == MaterialStateManager::UPDATE)}; } // end of setMaterialProperty - MGIS_EXPORT void setMaterialProperty( + bool setMaterialProperty( + Context& ctx, MaterialStateManager& m, const std::string_view& n, - const std::span& v, - const MaterialStateManager::StorageMode s, - const MaterialStateManager::UpdatePolicy p) { + const real v, + const MaterialStateManager::UpdatePolicy p) noexcept { + const auto omp = getVariable(ctx, m.b.mps, n); + if (isInvalid(omp)) { + return false; + } + const auto& mp = *(*omp); + if (mp.type != Variable::SCALAR) { + return ctx.registerErrorMessage( + "setMaterialProperty: " + "invalid material property " + "(only scalar material property is supported)"); + } + getFieldHolder(m.material_properties, + n) = MaterialStateManager::FieldHolder{ + .value = v, .shall_be_updated = (p == MaterialStateManager::UPDATE)}; + return true; + } // end of setMaterialProperty + + void setMaterialProperty(MaterialStateManager& m, + const std::string_view& n, + const std::span& v, + const MaterialStateManager::StorageMode s, + const MaterialStateManager::UpdatePolicy p) { const auto mp = getVariable(m.b.mps, n); mgis::raise_if(mp.type != Variable::SCALAR, "setMaterialProperty: " @@ -208,6 +220,42 @@ namespace mgis::behaviour { } } // end of setMaterialProperty + bool setMaterialProperty( + Context& ctx, + MaterialStateManager& m, + const std::string_view& n, + const std::span& v, + const MaterialStateManager::StorageMode s, + const MaterialStateManager::UpdatePolicy p) noexcept { + const auto omp = getVariable(ctx, m.b.mps, n); + if (isInvalid(omp)) { + return false; + } + const auto& mp = *(*omp); + if (mp.type != Variable::SCALAR) { + return ctx.registerErrorMessage( + "setMaterialProperty: " + "invalid material property " + "(only scalar material property is supported)"); + } + if (static_cast(v.size()) != m.n) { + return ctx.registerErrorMessage( + "setMaterialProperty: invalid number of values " + "(does not match the number of integration points)"); + } + if (s == MaterialStateManager::LOCAL_STORAGE) { + getFieldHolder(m.material_properties, n) = + MaterialStateManager ::FieldHolder{ + .value = std::vector{v.begin(), v.end()}, + .shall_be_updated = (p == MaterialStateManager::UPDATE)}; + } else { + getFieldHolder(m.material_properties, + n) = MaterialStateManager ::FieldHolder{ + .value = v, .shall_be_updated = (p == MaterialStateManager::UPDATE)}; + } + return true; + } // end of setMaterialProperty + bool isMaterialPropertyDefined(const MaterialStateManager& m, const std::string_view& n) { const auto p = getFieldHolderIterator(m.material_properties, n); @@ -505,11 +553,12 @@ namespace mgis::behaviour { } } // end of extractInternalStateVariable - bool save(Context& ctx, - H5::Group& g, - const std::string& n, - const MaterialStateManager::FieldHolder& f, - const MaterialStateManagerSavingOptions& opts) noexcept { + [[nodiscard]] static bool save( + Context& ctx, + H5::Group& g, + const std::string& n, + const MaterialStateManager::FieldHolder& f, + const MaterialStateManagerSavingOptions& opts) noexcept { using namespace mgis::utilities::hdf5; if (std::holds_alternative(f.value)) { return write(ctx, g, n, std::get(f.value), opts.allow_overwrite); @@ -544,7 +593,7 @@ namespace mgis::behaviour { } // if (opts.save_material_properties) { - auto og_mp = openGroup(ctx, g, "material_properties"); + auto og_mp = createGroup(ctx, g, "material_properties"); if (isInvalid(og_mp)) { return false; } @@ -555,9 +604,11 @@ namespace mgis::behaviour { } } // - if (!write(ctx, g, "internal_state_variables", s.internal_state_variables, - opts.allow_overwrite)) { - return false; + if (!s.b.isvs.empty()) { + if (!write(ctx, g, "internal_state_variables", s.internal_state_variables, + opts.allow_overwrite)) { + return false; + } } if ((opts.save_stored_energies) && (!s.stored_energies.empty())) { if (!write(ctx, g, "stored_energies", s.stored_energies, @@ -586,4 +637,241 @@ namespace mgis::behaviour { return true; } // end of save + [[nodiscard]] static bool restoreScalarFieldHolder( + Context& ctx, + MaterialStateManager::FieldHolder& f, + const H5::Group& g, + const std::string& n, + const size_type ng) noexcept { + using namespace mgis::utilities::hdf5; + auto odataset = openDataSet(ctx, g, n); + if (isInvalid(odataset)) { + return false; + } + try { + const auto s = odataset->getSpace(); + if (s.getSimpleExtentNdims() != 1) { + return ctx.registerErrorMessage("unexpected multidimensional array"); + } + hsize_t dims[1]; + s.getSimpleExtentDims(dims); + if (dims[0] == 1) { + // uniform value + auto value = real{}; + odataset->read(&value, getNativeType()); + f = value; + return true; + } + if (dims[0] != ng) { + return ctx.registerErrorMessage( + "unexpected data size for '" + n + "' (expected '" + + std::to_string(ng) + "', got '" + std::to_string(dims[0]) + "')"); + } + if (std::holds_alternative>(f.value)) { + auto& values = std::get>(f.value); + const auto fsize = values.size(); + if (fsize != ng) { + return ctx.registerErrorMessage( + "invalid storage for '" + n + "' (expected '" + + std::to_string(ng) + "', got '" + std::to_string(fsize) + "')"); + } + odataset->read(values.data(), getNativeType()); + } else if (std::holds_alternative>(f.value)) { + auto& values = std::get>(f.value); + if (values.empty()) { + values.resize(ng); + } + const auto fsize = values.size(); + if (fsize != ng) { + return ctx.registerErrorMessage( + "invalid storage for '" + n + "' (expected '" + + std::to_string(ng) + "', got '" + std::to_string(fsize) + "')"); + } + odataset->read(values.data(), getNativeType()); + } + return true; + } catch (...) { + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of restoreScalarFieldHolder + + [[nodiscard]] static bool restoreFieldHolder( + Context& ctx, + MaterialStateManager::FieldHolder& f, + const H5::Group& g, + const std::string& n, + const size_type ng, + const size_type vsize) noexcept { + using namespace mgis::utilities::hdf5; + if (vsize == 1) { + return restoreScalarFieldHolder(ctx, f, g, n, ng); + } + if (std::holds_alternative(f.value)) { + // by default, f can be initialized to a real value + f.value = std::vector{}; + } + auto odataset = openDataSet(ctx, g, n); + if (isInvalid(odataset)) { + return false; + } + try { + const auto s = odataset->getSpace(); + if (s.getSimpleExtentNdims() != 1) { + return ctx.registerErrorMessage("unexpected multidimensional array"); + } + hsize_t dims[1]; + s.getSimpleExtentDims(dims); + if (dims[0] == vsize) { + // uniform value + if (std::holds_alternative>(f.value)) { + auto& values = std::get>(f.value); + if (values.size() == vsize) { + odataset->read(values.data(), getNativeType()); + return true; + } + if (values.size() != ng * vsize) { + return ctx.registerErrorMessage( + "unexpected data size for '" + n + "' (expected '" + + std::to_string(ng * vsize) + "', got '" + + std::to_string(dims[0]) + "')"); + } + // duplicate the uniform values + auto tmp = std::vector(vsize); + odataset->read(tmp.data(), getNativeType()); + for (size_type idx = 0; idx != ng; ++idx) { + std::copy(tmp.begin(), tmp.end(), values.begin() + idx * vsize); + } + return true; + } + auto& values = std::get>(f.value); + if (values.empty()) { + values.resize(vsize); + } + odataset->read(values.data(), getNativeType()); + return true; + } + if (dims[0] != vsize * ng) { + return ctx.registerErrorMessage( + "unexpected data size for '" + n + "' (expected '" + + std::to_string(ng * vsize) + "', got '" + std::to_string(dims[0]) + + "')"); + } + if (std::holds_alternative>(f.value)) { + auto& values = std::get>(f.value); + const auto fsize = values.size(); + if (fsize != ng) { + return ctx.registerErrorMessage( + "invalid storage for '" + n + "' (expected '" + + std::to_string(ng) + "', got '" + std::to_string(fsize) + "')"); + } + odataset->read(values.data(), getNativeType()); + } else if (std::holds_alternative>(f.value)) { + auto& values = std::get>(f.value); + if (values.empty()) { + values.resize(ng); + } + const auto fsize = values.size(); + if (fsize != ng) { + return ctx.registerErrorMessage( + "invalid storage for '" + n + "' (expected '" + + std::to_string(ng) + "', got '" + std::to_string(fsize) + "')"); + } + odataset->read(values.data(), getNativeType()); + } + return true; + } catch (...) { + std::ignore = registerH5ExceptionInErrorBacktrace(ctx); + } + return false; + } // end of restoreFieldHolder + + bool restore(Context& ctx, + MaterialStateManager& s, + const H5::Group& g, + const MaterialStateManagerRestoreOptions& opts) noexcept { + using namespace mgis::utilities::hdf5; + if (opts.restore_gradients) { + if (!read(ctx, s.gradients, g, "gradients")) { + return ctx.registerErrorMessage("restoring gradients failed"); + } + } + if (opts.restore_thermodynamic_forces) { + if (!read(ctx, s.thermodynamic_forces, g, "thermodynamic_forces")) { + return ctx.registerErrorMessage( + "restoring thermodynamic forces failed"); + } + } + if (opts.restore_mass_densities) { + if (!s.mass_density.has_value()) { + s.mass_density = real{}; + } + if (!restoreScalarFieldHolder(ctx, *(s.mass_density), g, "mass_density", + s.n)) { + return ctx.registerErrorMessage("restoring mass density failed"); + } + } + if (opts.restore_internal_state_variables) { + if (!read(ctx, s.internal_state_variables, g, + "internal_state_variables")) { + return ctx.registerErrorMessage( + "restoring internal state variables failed"); + } + } + if ((opts.restore_stored_energies) && (s.b.computesStoredEnergy)) { + if (!read(ctx, s.stored_energies, g, "stored_energies")) { + return ctx.registerErrorMessage("restoring stored energies failed"); + return false; + } + } + if ((opts.restore_dissipated_energies) && (s.b.computesDissipatedEnergy)) { + if (!read(ctx, s.dissipated_energies, g, "dissipated_energies")) { + return ctx.registerErrorMessage("restoring dissipated energies failed"); + } + } + if ((opts.restore_material_properties) && (!s.b.mps.empty())) { + if (!subGroupExists(g, "material_properties")) { + return ctx.registerErrorMessage("no material properties saved"); + } + auto ogmp = openGroup(ctx, g, "material_properties"); + if (isInvalid(ogmp)) { + return false; + } + for (const auto& mp : s.b.mps) { + const auto ovsize = getVariableSize(ctx, mp, s.b.hypothesis); + if (isInvalid(ovsize)) { + return ctx.registerErrorMessage("restoring material property '" + + mp.name + "' failed"); + } + auto& f = s.material_properties[mp.name]; + if (!restoreFieldHolder(ctx, f, *ogmp, mp.name, s.n, *ovsize)) { + return ctx.registerErrorMessage("restoring material property '" + + mp.name + "' failed"); + } + } + } + if ((opts.restore_external_state_variables) && (!s.b.esvs.empty())) { + if (!subGroupExists(g, "external_state_variables")) { + return ctx.registerErrorMessage("no external state variables saved"); + } + auto ogesv = openGroup(ctx, g, "external_state_variables"); + if (isInvalid(ogesv)) { + return false; + } + for (const auto& esv : s.b.esvs) { + const auto ovsize = getVariableSize(ctx, esv, s.b.hypothesis); + if (isInvalid(ovsize)) { + return ctx.registerErrorMessage( + "restoring external state variable '" + esv.name + "' failed"); + } + auto& f = s.external_state_variables[esv.name]; + if (!restoreFieldHolder(ctx, f, *ogesv, esv.name, s.n, *ovsize)) { + return ctx.registerErrorMessage( + "restoring external state variable '" + esv.name + "' failed"); + } + } + } + return true; + } // end of restore + } // end of namespace mgis::behaviour diff --git a/src/Model.cxx b/src/Model.cxx index a29f93231..2461d454e 100644 --- a/src/Model.cxx +++ b/src/Model.cxx @@ -12,6 +12,7 @@ * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). */ +#include #include "MGIS/Raise.hxx" #include "MGIS/Context.hxx" #include "MGIS/Model/Model.hxx" @@ -50,7 +51,7 @@ namespace mgis::model { try { return ::mgis::model::load(l, m, h); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } // end of load @@ -67,7 +68,7 @@ namespace mgis::model { try { return ::mgis::model::loadFromDatabase(opts); } catch (...) { - registerExceptionInErrorBacktrace(ctx); + std::ignore = registerExceptionInErrorBacktrace(ctx); } return {}; } // end of load diff --git a/src/Variable.cxx b/src/Variable.cxx index f9afaa4c9..91d21ae38 100644 --- a/src/Variable.cxx +++ b/src/Variable.cxx @@ -112,7 +112,7 @@ namespace mgis::behaviour::internals { mgis::raise("invalid space dimension"); } return static_cast(N); - } // end of getVariableSize + } // end of getTinyVectorVariableSize static size_t getSymmetricTensorVariableSize( int &v, const mgis::behaviour::Hypothesis h) { @@ -302,7 +302,19 @@ namespace mgis::behaviour { return s; } // end of getVariableSize - bool contains(const std::vector &vs, const std::string_view n) { + std::optional getVariableSize(Context &ctx, + const Variable &v, + const Hypothesis h) noexcept { + try { + return getVariableSize(v, h); + } catch (...) { + std::ignore = registerExceptionInErrorBacktrace(ctx); + } + return {}; + } // end of getVariableSize + + bool contains(const std::vector &vs, + const std::string_view n) noexcept { return std::find_if(vs.begin(), vs.end(), [&n](const Variable &v) { return v.name == n; }) != vs.end(); @@ -318,6 +330,19 @@ namespace mgis::behaviour { return *p; } // end of getVariable + std::optional getVariable( + Context &ctx, + const std::vector &vs, + const std::string_view n) noexcept { + const auto p = std::find_if( + vs.begin(), vs.end(), [&n](const Variable &v) { return v.name == n; }); + if (p == vs.end()) { + return ctx.registerErrorMessage("getVariable: no variable named '" + + std::string(n) + "'"); + } + return &(*p); + } // end of getVariable + size_type getArraySize(const std::vector &vs, const Hypothesis h) { auto s = size_type{}; for (const auto &v : vs) { diff --git a/tests/HDF5Test.cxx b/tests/HDF5Test.cxx index 77c9a5ec0..c98080c7e 100644 --- a/tests/HDF5Test.cxx +++ b/tests/HDF5Test.cxx @@ -19,12 +19,12 @@ int main(const int argc, const char* const* argv) { using namespace mgis::behaviour; using namespace mgis::utilities::hdf5; if (argc != 2) { - std::cerr << "IntegrateTest: invalid number of arguments\n"; + std::cerr << "HDF5Test: invalid number of arguments\n"; std::exit(-1); } auto ctx = Context{}; - auto f = H5::H5File("HDF5Test.h5", H5F_ACC_TRUNC); - auto r = f.openGroup("/"); + auto file = H5::H5File("HDF5Test.h5", H5F_ACC_TRUNC); + auto root = file.openGroup("/"); const auto b = load(argv[1], "Norton", Hypothesis::TRIDIMENSIONAL); MaterialDataManager m{b, 100}; const auto de = 5.e-5; @@ -32,12 +32,12 @@ int main(const int argc, const char* const* argv) { m.s1.external_state_variables["Temperature"] = 293.15; // copy d.s1 in d.s0 update(m); + const auto dt = real(180); for (size_type idx = 0; idx != m.n; ++idx) { m.s1.gradients[idx * m.s1.gradients_stride] = de; } - const auto dt = real(180); for (size_type i = 0; i != 20; ++i) { - auto og = createGroup(ctx, r, "TimeStep_" + std::string{i}); + auto og = createGroup(ctx, root, "TimeStep_" + std::to_string(i)); if (isInvalid(og)) { std::cerr << ctx.getErrorMessage() << '\n'; return EXIT_FAILURE; @@ -48,6 +48,66 @@ int main(const int argc, const char* const* argv) { return EXIT_FAILURE; } update(m); + for (size_type idx = 0; idx != m.n; ++idx) { + m.s1.gradients[idx * m.s1.gradients_stride] += de; + } + } + // restore and checks + const auto o = + getVariableOffset(b.isvs, "EquivalentViscoplasticStrain", b.hypothesis); + auto pi = std::array{}; // values of the equivalent plastic strain + auto pe = std::array{}; // values of the equivalent plastic strain + const auto ni = size_type{o}; + const auto ne = + size_type{(m.n - 1) * m.s0.internal_state_variables_stride + o}; + for (size_type i = 0; i != 20; ++i) { + auto og = openGroup(ctx, root, "TimeStep_" + std::to_string(i)); + if (isInvalid(og)) { + std::cerr << ctx.getErrorMessage() << '\n'; + return EXIT_FAILURE; + } + setExternalStateVariable(m.s1, "Temperature", 500); + if (!restore(ctx, m.s1, *og, {.restore_mass_densities = false})) { + std::cerr << ctx.getErrorMessage() << '\n'; + return EXIT_FAILURE; + } + const auto& T = m.s1.external_state_variables.at("Temperature"); + if (!std::holds_alternative(T.value)) { + std::cerr << "invalid type for the temperature\n"; + return EXIT_FAILURE; + } + if (std::abs(std::get(T.value) - 293.15) > 1e-10) { + std::cerr << "invalid value for the temperature\n"; + return EXIT_FAILURE; + } + pi[i] = m.s1.internal_state_variables[ni]; + pe[i] = m.s1.internal_state_variables[ne]; + } + // + const auto p_ref = std::array{ + 1.3523277308229e-11, 1.0955374667213e-07, 5.5890770166084e-06, + 3.2392193670428e-05, 6.645865307584e-05, 9.9676622883138e-05, + 0.00013302758358953, 0.00016635821069889, 0.00019969195920296, + 0.00023302522883648, 0.00026635857194317, 0.000299691903777, + 0.0003330252373404, 0.00036635857063843, 0.00039969190397718, + 0.00043302523730968, 0.00046635857064314, 0.00049969190397646, + 0.00053302523730979, 0.00056635857064313}; + std::cerr.precision(14); + for (size_type i = 0; i != 20; ++i) { + if (std::abs(pi[i] - p_ref[i]) > 1.e-12) { + std::cerr << "invalid value for the equivalent " + "viscoplastic strain at the first integration point" + << "(expected '" << p_ref[i] << "', computed '" << pi[i] + << "')\n"; + return EXIT_FAILURE; + } + if (std::abs(pe[i] - p_ref[i]) > 1.e-12) { + std::cerr << "invalid value for the equivalent " + "viscoplastic strain at the last integration point" + << "(expected '" << p_ref[i] << "', computed '" << pe[i] + << "')\n"; + return EXIT_FAILURE; + } } return EXIT_SUCCESS; } // end of main diff --git a/tests/StridedCoalescedMemoryAccessFunctionViewTest.cxx b/tests/StridedCoalescedMemoryAccessFunctionViewTest.cxx index 8c5a3a197..a3eb9e367 100644 --- a/tests/StridedCoalescedMemoryAccessFunctionViewTest.cxx +++ b/tests/StridedCoalescedMemoryAccessFunctionViewTest.cxx @@ -44,8 +44,8 @@ struct StridedCoalescedMemoryAccessFunctionViewBaseTest final auto space = BasicLinearSpace{2}; auto values = std::vector{5, 12, -2, 3}; auto coalesced_view = - StridedCoalescedMemoryAccessFunctionViewBase( - space, values); + StridedCoalescedMemoryAccessFunctionViewBase(space, values); auto ptr0 = coalesced_view.getValues(0); auto ptr1 = coalesced_view.getValues(1); return {std::array{ptr0[0], ptr0[1]}, // From 44bc3db5e6a12226543260dfbc424cb8b7214a70 Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 14:59:56 +0100 Subject: [PATCH 4/8] Implement restore for MaterialDataManager --- .../MGIS/Behaviour/MaterialDataManager.hxx | 12 ++++++---- src/MaterialDataManager.cxx | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/include/MGIS/Behaviour/MaterialDataManager.hxx b/include/MGIS/Behaviour/MaterialDataManager.hxx index 132015b77..7c5374051 100644 --- a/include/MGIS/Behaviour/MaterialDataManager.hxx +++ b/include/MGIS/Behaviour/MaterialDataManager.hxx @@ -304,7 +304,11 @@ namespace mgis::behaviour { H5::Group&, const MaterialDataManager&, const MaterialDataManagerSavingOptions& = {}) noexcept; - + /*! + * \brief structure used to customize the saving of a `MaterialDataManager` + */ + struct MaterialDataManagerRestoreOptions + : public MaterialStateManagerRestoreOptions {}; /*! * \brief restore a `MaterialDataManager` from an HDF5 group * \param[in] ctx: execution context @@ -314,9 +318,9 @@ namespace mgis::behaviour { */ MGIS_EXPORT [[nodiscard]] bool restore( Context&, - H5::Group&, - const MaterialDataManager&, - const MaterialDataManagerSavingOptions& = {}) noexcept; + MaterialDataManager&, + const H5::Group&, + const MaterialDataManagerRestoreOptions& = {}) noexcept; #endif /* MGIS_HAVE_HDF5 */ diff --git a/src/MaterialDataManager.cxx b/src/MaterialDataManager.cxx index a2c3240b2..c806ae248 100644 --- a/src/MaterialDataManager.cxx +++ b/src/MaterialDataManager.cxx @@ -213,4 +213,28 @@ namespace mgis::behaviour { return true; } // end of save + bool restore(Context& ctx, + MaterialDataManager& m, + const H5::Group& g, + const MaterialDataManagerRestoreOptions& opts) noexcept { + using namespace mgis::utilities::hdf5; + // group for the state at the beginning of the time step + auto og_s0 = openGroup(ctx, g, "s0"); + if (isInvalid(og_s0)) { + return false; + } + auto og_s1 = openGroup(ctx, g, "s1"); + if (isInvalid(og_s1)) { + return false; + } + // + if (!restore(ctx, m.s0, *og_s0, opts)) { + return false; + } + if (!restore(ctx, m.s1, *og_s1, opts)) { + return false; + } + return true; + } // end of restore + } // end of namespace mgis::behaviour From aab0db9f0463eee28760d0a9db0e60a6b892e859 Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 15:15:48 +0100 Subject: [PATCH 5/8] add missing #ifdef --- INSTALL-cmake.md | 7 +++++++ docs/web/release-notes-3.2.md | 26 +++++++++++++++++++++++++- src/MaterialDataManager.cxx | 8 ++++++-- src/MaterialStateManager.cxx | 4 ++++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/INSTALL-cmake.md b/INSTALL-cmake.md index 5f70a5583..189b1e04d 100644 --- a/INSTALL-cmake.md +++ b/INSTALL-cmake.md @@ -63,6 +63,13 @@ Options - `enable-exceptions`: use exceptions to report contract violation and error reporting. By default, contract violation leads to abort the program. - `enable-parallel-stl-algorithms`: by default, STL algorithms are used if available +- `enable-hdf5-support`: enable HDF5 support for save/restore + operations. Note that if this support is not explicitely requested, + `MGIS` still tries by default to support `HDF5` but configuration will + not fail if the `HDF5` library is not found. See + `enable-hdf5-automatic-support` for details. +- `enable-hdf5-automatic-support`: if set, `MGIS` tries by default to + support `HDF5` even if enable-hdf5-support` is not set. `cmake` usefull variables ======================= diff --git a/docs/web/release-notes-3.2.md b/docs/web/release-notes-3.2.md index ff3b38cdc..67ff9d923 100644 --- a/docs/web/release-notes-3.2.md +++ b/docs/web/release-notes-3.2.md @@ -22,6 +22,20 @@ This version is meant to be used with `TFEL` Version 5.2. # Documentation +## New `cmake` options + +### HDF5 support + +The following options control the support of the `HDF5` library: + +- `enable-hdf5-support`: enable HDF5 support for save/restore + operations. Note that if this support is not explicitely requested, + `MGIS` still tries by default to support `HDF5` but configuration will + not fail if the `HDF5` library is not found. See + `enable-hdf5-automatic-support` for details. +- `enable-hdf5-automatic-support`: if set, `MGIS` tries by default to + support `HDF5` even if enable-hdf5-support` is not set. + ## `Doxygen` documentation The doxygen documentation is now online: @@ -29,7 +43,7 @@ The doxygen documentation is now online: # New features -## Scripts to define environment variables for `mGIS` to work properly +## Scripts to define environment variables for `MGIS` to work properly Depending on the system and compilation options, some of following variables shall be set for `MGIS` to work properly: `MGISHOME`, `PATH`, @@ -72,6 +86,12 @@ directory (refered to `` in the following): # New features of the `MGIS/Behaviour` library +## Save/restore operations in `MaterialStateManager` and `MaterialDataManager` + +If `HDF5` support is enabled, the functions `save` and `restore` allows +saving and restoring the values stored in a `MaterialStateManager` or in +a `MaterialDataManager` to an `HDF5` file. + ## Control on variables updated/reverted in `MaterialStateManager` By default, material properties, mass density and external state @@ -152,6 +172,10 @@ const auto e2 = f.get<0, tfel::math::stensor<2, real>>(1); For more details, see +## Issue 209: Add basic support for `save`/`restore` operations in `MaterialDataManager` + +For more details, see + ## Issue 201: Add the ability to update only the state variables of a `MaterialStateManager` For more details, see diff --git a/src/MaterialDataManager.cxx b/src/MaterialDataManager.cxx index c806ae248..a383ba246 100644 --- a/src/MaterialDataManager.cxx +++ b/src/MaterialDataManager.cxx @@ -182,6 +182,8 @@ namespace mgis::behaviour { return outputs; } // end of allocatePostProcessingVariables +#ifdef MGIS_HAVE_HDF5 + bool save(Context& ctx, H5::Group& g, const MaterialDataManager& m, @@ -228,13 +230,15 @@ namespace mgis::behaviour { return false; } // - if (!restore(ctx, m.s0, *og_s0, opts)) { + if (!restore(ctx, *og_s0, m.s0, opts)) { return false; } - if (!restore(ctx, m.s1, *og_s1, opts)) { + if (!restore(ctx, *og_s1, m.s1, opts)) { return false; } return true; } // end of restore +#endif /* MGIS_HAVE_HDF5 */ + } // end of namespace mgis::behaviour diff --git a/src/MaterialStateManager.cxx b/src/MaterialStateManager.cxx index c61e3ba64..ac9e7e7fd 100644 --- a/src/MaterialStateManager.cxx +++ b/src/MaterialStateManager.cxx @@ -553,6 +553,8 @@ namespace mgis::behaviour { } } // end of extractInternalStateVariable +#ifdef MGIS_HAVE_HDF5 + [[nodiscard]] static bool save( Context& ctx, H5::Group& g, @@ -874,4 +876,6 @@ namespace mgis::behaviour { return true; } // end of restore +#endif /* MGIS_HAVE_HDF5 */ + } // end of namespace mgis::behaviour From 5f999ac1160cc2b3126f44da1a71996aa35f4adb Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 15:18:14 +0100 Subject: [PATCH 6/8] add missing #ifdef --- src/MaterialDataManager.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MaterialDataManager.cxx b/src/MaterialDataManager.cxx index a383ba246..21d71a992 100644 --- a/src/MaterialDataManager.cxx +++ b/src/MaterialDataManager.cxx @@ -230,10 +230,10 @@ namespace mgis::behaviour { return false; } // - if (!restore(ctx, *og_s0, m.s0, opts)) { + if (!restore(ctx, m.s0, *og_s0, opts)) { return false; } - if (!restore(ctx, *og_s1, m.s1, opts)) { + if (!restore(ctx, m.s1, *og_s1, opts)) { return false; } return true; From f921f3efe2c100519921f2a188ba74b5a95db199 Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 16:53:58 +0100 Subject: [PATCH 7/8] Improvements. Implement getGreedyMaterialStateManagerRestoreOptions --- docs/web/release-notes-3.2.md | 19 +++++- include/CMakeLists.txt | 1 + .../MGIS/Behaviour/MaterialDataManager.hxx | 2 +- .../MGIS/Behaviour/MaterialStateManager.hxx | 17 ++++- include/MGIS/Utilities/HDF5Support.hxx | 10 +-- src/HDF5Support.cxx | 18 ++---- src/MaterialDataManager.cxx | 3 + src/MaterialStateManager.cxx | 64 +++++++++++++++++++ tests/HDF5Test.cxx | 1 + 9 files changed, 106 insertions(+), 29 deletions(-) diff --git a/docs/web/release-notes-3.2.md b/docs/web/release-notes-3.2.md index 67ff9d923..d8bd953a7 100644 --- a/docs/web/release-notes-3.2.md +++ b/docs/web/release-notes-3.2.md @@ -88,9 +88,22 @@ directory (refered to `` in the following): ## Save/restore operations in `MaterialStateManager` and `MaterialDataManager` -If `HDF5` support is enabled, the functions `save` and `restore` allows -saving and restoring the values stored in a `MaterialStateManager` or in -a `MaterialDataManager` to an `HDF5` file. +If `HDF5` support is enabled, two functions `save` and `restore` are +available: + +- The `save` function allows saving the values stored in a + `MaterialStateManager` or in a `MaterialDataManager` to an `HDF5` + file. +- The `restore` function allows retrieving the values stored in a + `MaterialStateManager` or in a `MaterialDataManager` from an `HDF5` + file. + +Those functions have options allowing to precisely select what is saved +or restored. + +By default, one expects to restore the maximum amount of information. +The `getGreedyMaterialStateManagerRestoreOptions` function creates +option that can restore everything that has been saved. ## Control on variables updated/reverted in `MaterialStateManager` diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 112ed2f8a..539424fa0 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -50,6 +50,7 @@ if(MGIS_HAVE_TFEL) endif(MGIS_HAVE_TFEL) if(MGIS_HDF5_SUPPORT) + mgis_header(MGIS/Utilities HDF5Forward.hxx) mgis_header(MGIS/Utilities HDF5Support.hxx) endif(MGIS_HDF5_SUPPORT) diff --git a/include/MGIS/Behaviour/MaterialDataManager.hxx b/include/MGIS/Behaviour/MaterialDataManager.hxx index 7c5374051..3f798a553 100644 --- a/include/MGIS/Behaviour/MaterialDataManager.hxx +++ b/include/MGIS/Behaviour/MaterialDataManager.hxx @@ -20,7 +20,7 @@ #include #include #ifdef MGIS_HAVE_HDF5 -#include "MGIS/Utilities/HDF5Support.hxx" +#include "MGIS/Utilities/HDF5Forward.hxx" #endif /* MGIS_HAVE_HDF5 */ #include "MGIS/Config.hxx" diff --git a/include/MGIS/Behaviour/MaterialStateManager.hxx b/include/MGIS/Behaviour/MaterialStateManager.hxx index c1a89c368..ef21984e5 100644 --- a/include/MGIS/Behaviour/MaterialStateManager.hxx +++ b/include/MGIS/Behaviour/MaterialStateManager.hxx @@ -23,7 +23,7 @@ #include #include #ifdef MGIS_HAVE_HDF5 -#include "MGIS/Utilities/HDF5Support.hxx" +#include "MGIS/Utilities/HDF5Forward.hxx" #endif /* MGIS_HAVE_HDF5 */ #include "MGIS/Config.hxx" @@ -447,13 +447,26 @@ namespace mgis::behaviour { //! \brief list of external state variables that shall not be restored const std::vector ignored_external_state_variables = {}; }; // end of MaterialStateManagerRestoreOptions - + /*! + * \brief return restore options selecting all that can be read in the given + * group. + * + * \param[in] ctx: execution context + * \param[in] g: group + */ + MGIS_EXPORT [[nodiscard]] std::optional + getGreedyMaterialStateManagerRestoreOptions(Context&, + const H5::Group&) noexcept; /*! * \brief restore a `MaterialStateManager` from a HDF5 group + * * \param[in] ctx: execution context * \param[in] g: group * \param[in] s: material state manager * \param[in] opts: options + * + * \note update policies are set to their values for material properties and + * external state variables created during the restoration */ MGIS_EXPORT [[nodiscard]] bool restore( Context&, diff --git a/include/MGIS/Utilities/HDF5Support.hxx b/include/MGIS/Utilities/HDF5Support.hxx index f5c7fc4f5..2025b931e 100644 --- a/include/MGIS/Utilities/HDF5Support.hxx +++ b/include/MGIS/Utilities/HDF5Support.hxx @@ -21,6 +21,7 @@ #include #include "MGIS/Config.hxx" #include "MGIS/Context.hxx" +#include "MGIS/Utilities/HDF5Forward.hxx" namespace mgis::utilities::hdf5 { @@ -105,15 +106,6 @@ namespace mgis::utilities::hdf5 { */ MGIS_EXPORT [[nodiscard]] std::optional> getDataSetNames(Context&, const H5::Group&) noexcept; - /*! - * \return all the dataset names in a give group - * \param[out, in] ctx: execution context - * \param[out] n: names - * \param[in] g: group - */ - MGIS_EXPORT [[nodiscard]] bool getDataSetNames(Context&, - std::vector&, - const H5::Group&) noexcept; /*! * \return true if the given group contains an object with the * given name diff --git a/src/HDF5Support.cxx b/src/HDF5Support.cxx index 55b243b4f..70167dea2 100644 --- a/src/HDF5Support.cxx +++ b/src/HDF5Support.cxx @@ -204,29 +204,19 @@ namespace mgis::utilities::hdf5 { std::optional> getDataSetNames( Context& ctx, const H5::Group& g) noexcept { - std::vector names; - if (!getDataSetNames(ctx, names, g)) { - return {}; - } - return names; - } - - bool getDataSetNames(Context& ctx, - std::vector& n, - const H5::Group& g) noexcept { - n.clear(); try { + std::vector names; const hsize_t s = g.getNumObjs(); for (hsize_t i = 0; i != s; ++i) { if (g.getObjTypeByIdx(i) == H5G_DATASET) { - n.push_back(g.getObjnameByIdx(i)); + names.push_back(g.getObjnameByIdx(i)); } } - return true; + return names; } catch (...) { std::ignore = registerH5ExceptionInErrorBacktrace(ctx); } - return false; + return {}; } // end of getDataSetNames std::optional contains(Context& ctx, diff --git a/src/MaterialDataManager.cxx b/src/MaterialDataManager.cxx index 21d71a992..60841221c 100644 --- a/src/MaterialDataManager.cxx +++ b/src/MaterialDataManager.cxx @@ -15,6 +15,9 @@ #include #include #include "MGIS/Raise.hxx" +#ifdef MGIS_HAVE_HDF5 +#include "MGIS/Utilities/HDF5Support.hxx" +#endif /* MGIS_HAVE_HDF5 */ #include "MGIS/Behaviour/Behaviour.hxx" #include "MGIS/Behaviour/MaterialDataManager.hxx" diff --git a/src/MaterialStateManager.cxx b/src/MaterialStateManager.cxx index ac9e7e7fd..621b4f47a 100644 --- a/src/MaterialStateManager.cxx +++ b/src/MaterialStateManager.cxx @@ -15,6 +15,9 @@ #include #include #include "MGIS/Raise.hxx" +#ifdef MGIS_HAVE_HDF5 +#include "MGIS/Utilities/HDF5Support.hxx" +#endif /* MGIS_HAVE_HDF5 */ #include "MGIS/Behaviour/Behaviour.hxx" #include "MGIS/Behaviour/MaterialStateManager.hxx" @@ -639,6 +642,57 @@ namespace mgis::behaviour { return true; } // end of save + std::optional + getGreedyMaterialStateManagerRestoreOptions(Context& ctx, + const Behaviour& b, + const H5::Group& g) noexcept { + using namespace mgis::utilities::hdf5; + const auto odatasets = getDataSetNames(ctx, g); + if (isInvalid(odatasets)) { + return {}; + } + auto contains = [odatasets](std::string_view n) noexcept { + return std::find(odatasets->begin(), odatasets->end(), n) != + odatasets->end(); + }; + auto select_ignored_variables = [&g, &ctx]( + const std::vector& variables, + const std::string& gn) { + auto og = openGroup(ctx, g, gn); + auto ignored_variables = std::vector{}; + if (isInvalid(og)) { + return ignored_variables; + } + const auto oldatasets = getDataSetNames(ctx, *og); + if (isInvalid(oldatasets)) { + return ignored_variables; + } + for (const auto& v : variables) { + const auto found = std::find(oldatasets->begin(), oldatasets->end(), + v.name) != oldatasets->end(); + if (!found) { + ignored_variables.push_back(v.name); + } + } + return ignored_variables; + }; + return MaterialStateManagerRestoreOptions{ + .restore_gradients = contains("gradients"), + .restore_thermodynamic_forces = contains("thermodynamic_forces"), + .restore_stored_energies = contains("stored_energies"), + .restore_dissipated_energies = contains("dissipated_energies"), + .restore_internal_state_variables = + contains("internal_state_variables"), + .restore_mass_densities = contains("mass_density"), + .restore_material_properties = subGroupExists(g, "material_properties"), + .ignored_material_properties = + select_ignored_variables(b.mps, "material_properties"), + .restore_external_state_variables = + subGroupExists(g, "external_state_variables"), + .ignored_external_state_variables = + select_ignored_variables(b.esvs, "external_state_variables")}; + } // end of getGreedyMaterialStateManagerRestoreOptions + [[nodiscard]] static bool restoreScalarFieldHolder( Context& ctx, MaterialStateManager::FieldHolder& f, @@ -840,6 +894,11 @@ namespace mgis::behaviour { return false; } for (const auto& mp : s.b.mps) { + if (std::find(opts.ignored_material_properties.begin(), + opts.ignored_material_properties.end(), mp.name) != + opts.ignored_material_properties.end()) { + continue; + } const auto ovsize = getVariableSize(ctx, mp, s.b.hypothesis); if (isInvalid(ovsize)) { return ctx.registerErrorMessage("restoring material property '" + @@ -861,6 +920,11 @@ namespace mgis::behaviour { return false; } for (const auto& esv : s.b.esvs) { + if (std::find(opts.ignored_external_state_variables.begin(), + opts.ignored_external_state_variables.end(), esv.name) != + opts.ignored_external_state_variables.end()) { + continue; + } const auto ovsize = getVariableSize(ctx, esv, s.b.hypothesis); if (isInvalid(ovsize)) { return ctx.registerErrorMessage( diff --git a/tests/HDF5Test.cxx b/tests/HDF5Test.cxx index c98080c7e..0a71101f6 100644 --- a/tests/HDF5Test.cxx +++ b/tests/HDF5Test.cxx @@ -13,6 +13,7 @@ #include "MGIS/Behaviour/Behaviour.hxx" #include "MGIS/Behaviour/MaterialDataManager.hxx" #include "MGIS/Behaviour/Integrate.hxx" +#include "MGIS/Utilities/HDF5Support.hxx" int main(const int argc, const char* const* argv) { using namespace mgis; From c34c73658dbabb720ca928cd22686494ebc4b38f Mon Sep 17 00:00:00 2001 From: Thomas Helfer Date: Mon, 2 Feb 2026 16:56:10 +0100 Subject: [PATCH 8/8] add missing file. format --- include/MGIS/Utilities/HDF5Forward.hxx | 25 +++++++++++++++++++++++++ src/MaterialStateManager.cxx | 4 ++-- 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 include/MGIS/Utilities/HDF5Forward.hxx diff --git a/include/MGIS/Utilities/HDF5Forward.hxx b/include/MGIS/Utilities/HDF5Forward.hxx new file mode 100644 index 000000000..b7cdc21e0 --- /dev/null +++ b/include/MGIS/Utilities/HDF5Forward.hxx @@ -0,0 +1,25 @@ +/*! + * \file MGIS/Utilities/HDF5Forward.hxx + * \brief + * \author Thomas Helfer + * \date 02/02/2026 + * \copyright (C) Copyright Thomas Helfer 2018. + * Use, modification and distribution are subject + * to one of the following licences: + * - GNU Lesser General Public License (LGPL), Version 3.0. (See accompanying + * file LGPL-3.0.txt) + * - CECILL-C, Version 1.0 (See accompanying files + * CeCILL-C_V1-en.txt and CeCILL-C_V1-fr.txt). + */ + +#ifndef LIB_MGIS_UTILITIES_HDF5FORWARD_HXX +#define LIB_MGIS_UTILITIES_HDF5FORWARD_HXX + +namespace H5 { + + class Group; + class DataSet; + +} // end of namespace H5 + +#endif /* LIB_MGIS_UTILITIES_HDF5FORWARD_HXX */ diff --git a/src/MaterialStateManager.cxx b/src/MaterialStateManager.cxx index 621b4f47a..c4738d82d 100644 --- a/src/MaterialStateManager.cxx +++ b/src/MaterialStateManager.cxx @@ -895,8 +895,8 @@ namespace mgis::behaviour { } for (const auto& mp : s.b.mps) { if (std::find(opts.ignored_material_properties.begin(), - opts.ignored_material_properties.end(), mp.name) != - opts.ignored_material_properties.end()) { + opts.ignored_material_properties.end(), + mpxs.name) != opts.ignored_material_properties.end()) { continue; } const auto ovsize = getVariableSize(ctx, mp, s.b.hypothesis);