diff --git a/ApplicationLibCode/CMakeLists.txt b/ApplicationLibCode/CMakeLists.txt index b40cadf649..4c99a32ae8 100644 --- a/ApplicationLibCode/CMakeLists.txt +++ b/ApplicationLibCode/CMakeLists.txt @@ -231,15 +231,24 @@ list(APPEND RI_LIBRARIES RigGeoMechDataModel) # HDF5 # if(RESINSIGHT_FOUND_HDF5) - list( - APPEND - CPP_SOURCES + + set(HDF5_FILES FileInterface/RifHdf5Reader.h FileInterface/RifHdf5Reader.cpp FileInterface/RifHdf5SummaryReader.h FileInterface/RifHdf5SummaryReader.cpp FileInterface/RifOpmHdf5Summary.h - FileInterface/RifOpmHdf5Summary.cpp) + FileInterface/RifOpmHdf5Summary.cpp + FileInterface/RifHdf5Exporter.h + FileInterface/RifHdf5Exporter.cpp + FileInterface/RifHdf5SummaryExporter.h + FileInterface/RifHdf5SummaryExporter.cpp + ) + + list( + APPEND + CPP_SOURCES + ${HDF5_FILES}) add_definitions(-DUSE_HDF5) @@ -255,13 +264,8 @@ if(RESINSIGHT_FOUND_HDF5) endif() # MSVC source_group( - "FileInterface" - FILES FileInterface/RifHdf5Reader.h - FileInterface/RifHdf5Reader.cpp - FileInterface/RifHdf5SummaryReader.h - FileInterface/RifHdf5SummaryReader.cpp - FileInterface/RifOpmHdf5Summary.h - FileInterface/RifOpmHdf5Summary.cpp) + "FileInterface\\HDF5" + FILES ${HDF5_FILES}) endif() diff --git a/ApplicationLibCode/FileInterface/RifHdf5Exporter.cpp b/ApplicationLibCode/FileInterface/RifHdf5Exporter.cpp new file mode 100644 index 0000000000..23d3d64dc6 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifHdf5Exporter.cpp @@ -0,0 +1,159 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifHdf5Exporter.h" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifHdf5Exporter::RifHdf5Exporter( const std::string& fileName ) + : m_fileName( fileName ) +{ + try + { + // Create new or overwrite existing + m_hdfFile = new H5::H5File( m_fileName, H5F_ACC_TRUNC ); + } + catch ( ... ) + { + delete m_hdfFile; + m_hdfFile = nullptr; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +RifHdf5Exporter::~RifHdf5Exporter() +{ + if ( m_hdfFile ) + { + delete m_hdfFile; + m_hdfFile = nullptr; + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5Exporter::exportSummaryVector( H5::Group& summaryRootGroup, + const std::string& vectorName, + const std::string& vectorSubNodeName, + const std::string& datasetName, + const std::vector& values ) +{ + auto summaryGroup = findOrCreateGroup( &summaryRootGroup, vectorName ); + + auto subNodeGroup = summaryGroup.createGroup( vectorSubNodeName ); + + return writeDataset( subNodeGroup, datasetName, values ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5Exporter::writeDataset( const H5::Group& group, const std::string& datasetName, const std::vector& values ) +{ + try + { + hsize_t dimsf[1]; + dimsf[0] = values.size(); + H5::DataSpace dataspace( 1, dimsf ); + + H5::DataType datatype( H5::PredType::NATIVE_FLOAT ); + H5::DataSet dataset = group.createDataSet( datasetName, datatype, dataspace ); + dataset.write( values.data(), H5::PredType::NATIVE_FLOAT ); + + return true; + } + catch ( ... ) + { + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5Exporter::writeDataset( const H5::Group& group, const std::string& datasetName, const std::vector& values ) +{ + try + { + hsize_t dimsf[1]; + dimsf[0] = values.size(); + H5::DataSpace dataspace( 1, dimsf ); + + H5::DataType datatype( H5::PredType::NATIVE_INT ); + H5::DataSet dataset = group.createDataSet( datasetName, datatype, dataspace ); + dataset.write( values.data(), H5::PredType::NATIVE_INT ); + + return true; + } + catch ( ... ) + { + } + + return false; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5Exporter::writeDataset( const std::string& groupName, const std::string& datasetName, const std::vector& values ) +{ + if ( !m_hdfFile ) return false; + + { + auto generalGroup = findOrCreateGroup( nullptr, groupName ); + + return writeDataset( generalGroup, datasetName, values ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +H5::Group RifHdf5Exporter::findOrCreateGroup( H5::Group* parentGroup, const std::string& groupName ) +{ + H5::Group group; + if ( parentGroup ) + { + try + { + group = parentGroup->openGroup( groupName ); + } + catch ( ... ) + { + group = parentGroup->createGroup( groupName ); + } + } + else + { + try + { + group = m_hdfFile->openGroup( groupName ); + } + catch ( ... ) + { + group = m_hdfFile->createGroup( groupName ); + } + } + + return group; +} diff --git a/ApplicationLibCode/FileInterface/RifHdf5Exporter.h b/ApplicationLibCode/FileInterface/RifHdf5Exporter.h new file mode 100644 index 0000000000..c4e5510f5d --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifHdf5Exporter.h @@ -0,0 +1,53 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "H5Cpp.h" + +#include +#include + +//================================================================================================== +// +// +//================================================================================================== +class RifHdf5Exporter +{ +public: + explicit RifHdf5Exporter( const std::string& fileName ); + ~RifHdf5Exporter(); + + H5::Group findOrCreateGroup( H5::Group* parentGroup, const std::string& groupName ); + + bool exportSummaryVector( H5::Group& summaryRootGroup, + const std::string& vectorName, + const std::string& vectorSubNodeName, + const std::string& datasetName, + const std::vector& values ); + + bool writeDataset( const std::string& groupName, const std::string& datasetName, const std::vector& values ); + +private: + bool writeDataset( const H5::Group& group, const std::string& datasetName, const std::vector& values ); + bool writeDataset( const H5::Group& group, const std::string& datasetName, const std::vector& values ); + +private: + std::string m_fileName; + H5::H5File* m_hdfFile; +}; diff --git a/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.cpp b/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.cpp new file mode 100644 index 0000000000..a70d926a93 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.cpp @@ -0,0 +1,119 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RifHdf5SummaryExporter.h" + +#include + +#include "RifHdf5Exporter.h" +#include "RifSummaryReaderInterface.h" + +#include "opm/io/eclipse/ESmry.hpp" + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5SummaryExporter::writeSummaryDataToHdf( const std::string& hdfFileName, Opm::EclIO::ESmry& sourceSummaryData ) +{ + auto timesteps = sourceSummaryData.numberOfTimeSteps(); + if ( timesteps == 0 ) return false; + + RifHdf5Exporter exporter( hdfFileName ); + + writeGeneralSection( exporter, sourceSummaryData ); + writeSummaryVectors( exporter, sourceSummaryData ); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5SummaryExporter::writeGeneralSection( RifHdf5Exporter& exporter, Opm::EclIO::ESmry& sourceSummaryData ) +{ + auto timesteps = sourceSummaryData.dates(); + + { + std::vector values( 1 ); + values[0] = -1; + + exporter.writeDataset( "general", "checksum", values ); + } + + { + auto startDate = sourceSummaryData.startdate(); + + time_t firstTimeStep = std::chrono::system_clock::to_time_t( startDate ); + + QDateTime dt = QDateTime::fromTime_t( firstTimeStep ); + + int day = dt.date().day(); + int month = dt.date().month(); + int year = dt.date().year(); + + int hour = dt.time().hour(); + int minute = dt.time().minute(); + int second = dt.time().second(); + + std::vector timeValues( 7 ); + timeValues[0] = day; + timeValues[1] = month; + timeValues[2] = year; + timeValues[3] = hour; + timeValues[4] = minute; + timeValues[5] = second; + timeValues[6] = 0; // Unknown value, could be millisec + + exporter.writeDataset( "general", "start_date", timeValues ); + } + + { + std::vector values( 2 ); + values[0] = 1; + values[1] = 7; + + exporter.writeDataset( "general", "version", values ); + } +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +bool RifHdf5SummaryExporter::writeSummaryVectors( RifHdf5Exporter& exporter, Opm::EclIO::ESmry& sourceSummaryData ) +{ + size_t valueCount = sourceSummaryData.numberOfTimeSteps(); + if ( valueCount == 0 ) return false; + + const std::string datasetName( "values" ); + + const std::vector& summaryNodeList = sourceSummaryData.summaryNodeList(); + + auto summaryVectorsGroup = exporter.findOrCreateGroup( nullptr, "summary_vectors" ); + + for ( const auto& summaryNode : summaryNodeList ) + { + auto smspecKeywordIndex = summaryNode.smspecKeywordIndex; + QString smspecKeywordText = QString( "%1" ).arg( smspecKeywordIndex ); + const auto& quantity = summaryNode.keyword; + const std::vector& values = sourceSummaryData.get( summaryNode ); + + exporter.exportSummaryVector( summaryVectorsGroup, quantity, smspecKeywordText.toStdString(), datasetName, values ); + } + + return true; +} diff --git a/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.h b/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.h new file mode 100644 index 0000000000..1dfde64c83 --- /dev/null +++ b/ApplicationLibCode/FileInterface/RifHdf5SummaryExporter.h @@ -0,0 +1,48 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2021 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include + +class RifSummaryReaderInterface; +class RifHdf5Exporter; + +namespace Opm +{ +namespace EclIO +{ + class ESmry; + struct SummaryNode; +} // namespace EclIO +} // namespace Opm + +//================================================================================================== +// +// +//================================================================================================== +class RifHdf5SummaryExporter +{ +public: + static bool writeSummaryDataToHdf( const std::string& hdfFileName, Opm::EclIO::ESmry& sourceSummaryData ); + +private: + static bool writeGeneralSection( RifHdf5Exporter& exporter, Opm::EclIO::ESmry& sourceSummaryData ); + static bool writeSummaryVectors( RifHdf5Exporter& exporter, Opm::EclIO::ESmry& sourceSummaryData ); +}; diff --git a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake index 8d8f0aeaa3..b9ba75dac0 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists_files.cmake +++ b/ApplicationLibCode/UnitTests/CMakeLists_files.cmake @@ -25,6 +25,7 @@ ${CMAKE_CURRENT_LIST_DIR}/WellPathAsciiFileReader-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/opm-flowdiagnostics-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigTofAccumulatedPhaseFractionsCalculator-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/HDF5FileReader-Test.cpp +${CMAKE_CURRENT_LIST_DIR}/HDF5FileWriter-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigCellGeometryTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RigHexIntersectionTools-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/ObservedDataParser-Test.cpp diff --git a/ApplicationLibCode/UnitTests/HDF5FileWriter-Test.cpp b/ApplicationLibCode/UnitTests/HDF5FileWriter-Test.cpp new file mode 100644 index 0000000000..4076146337 --- /dev/null +++ b/ApplicationLibCode/UnitTests/HDF5FileWriter-Test.cpp @@ -0,0 +1,132 @@ +#ifdef USE_HDF5 + +#include "gtest/gtest.h" + +#include "RiaTestDataDirectory.h" +#include "RifHdf5Exporter.h" +#include "RifHdf5SummaryReader.h" +#include "RifOpmHdf5Summary.h" +#include "RifReaderEclipseSummary.h" + +#include "H5Cpp.h" + +#include + +#include "RifHdf5SummaryExporter.h" +#include "opm/io/eclipse/ESmry.hpp" +#include + +static const QString H5_TEST_DATA_DIRECTORY = QString( "%1/h5-file/" ).arg( TEST_DATA_DIR ); + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( DISABLED_HDFTests, WriteToHdf5SummaryExporter ) +{ + QString file_path = H5_TEST_DATA_DIRECTORY + "NORNE_ATW2013_RFTPLT_V2.SMSPEC"; + + // RifReaderEclipseSummary eclReader; + // eclReader.open( file_path, true, nullptr ); + + Opm::EclIO::ESmry esmry( file_path.toStdString() ); + + RifHdf5SummaryExporter exporter; + std::string exportFileName = "e:/project/scratch_export/hdf_complete.h5"; + + exporter.writeSummaryDataToHdf( exportFileName, esmry ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( DISABLED_HDFTests, WriteDataToH5 ) +{ + QString file_path = H5_TEST_DATA_DIRECTORY + "NORNE_ATW2013_RFTPLT_V2.SMPEC"; + + try + { + RifReaderEclipseSummary summaryReader; + summaryReader.open( file_path, true, nullptr ); + + { + std::string exportFileName = "e:/project/scratch_export/hdf-test.h5"; + + H5::H5File file( exportFileName, H5F_ACC_TRUNC ); // Overwrite existing + + // General group + { + std::vector values( 7 ); + + int day = 1; + int month = 2; + int year = 3; + int hour = 4; + int minute = 5; + int second = 6; + int unknown = 7; + + values[0] = day; + values[1] = month; + values[2] = year; + values[3] = hour; + values[4] = minute; + values[5] = second; + values[6] = 0; + + auto generalGroup = file.createGroup( "/general" ); + + // start_date + { + hsize_t dimsf[1]; + dimsf[0] = values.size(); + H5::DataSpace dataspace( 1, dimsf ); + + H5::DataType datatype( H5::PredType::NATIVE_INT ); + H5::DataSet dataset = generalGroup.createDataSet( "start_date", datatype, dataspace ); + dataset.write( values.data(), H5::PredType::NATIVE_INT ); + } + } + + { + auto summaryGroup = file.createGroup( "/summary_vectors" ); + auto myVectorGroup = summaryGroup.createGroup( "BPR" ); + auto dataGroup = myVectorGroup.createGroup( "66" ); + + std::vector a( 10 ); + std::iota( a.begin(), a.end(), 10 ); + + // dataset dimensions + hsize_t dimsf[1]; + dimsf[0] = a.size(); + H5::DataSpace dataspace( 1, dimsf ); + + H5::DataType datatype( H5::PredType::NATIVE_FLOAT ); + H5::DataSet dataset = dataGroup.createDataSet( "values", datatype, dataspace ); + + dataset.write( a.data(), H5::PredType::NATIVE_FLOAT ); + } + } + } + + catch ( H5::FileIException& error ) // catch failure caused by the H5File operations + { + std::cout << error.getCDetailMsg(); + } + + catch ( H5::DataSetIException& error ) // catch failure caused by the DataSet operations + { + std::cout << error.getCDetailMsg(); + } + + catch ( H5::DataSpaceIException& error ) // catch failure caused by the DataSpace operations + { + std::cout << error.getCDetailMsg(); + } + + catch ( H5::DataTypeIException& error ) // catch failure caused by the DataSpace operations + { + std::cout << error.getCDetailMsg(); + } +} + +#endif // USE_HDF5