From 593af1c678d9d662c14eec41b0c5df3343a499c3 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 2 May 2018 07:37:23 +0200 Subject: [PATCH] Pass dimension information for extra fields in restart --- CMakeLists_files.cmake | 1 + opm/output/eclipse/EclipseIO.hpp | 2 +- opm/output/eclipse/RestartIO.hpp | 2 +- opm/output/eclipse/RestartValue.hpp | 40 ++++----- src/opm/output/eclipse/EclipseIO.cpp | 2 +- src/opm/output/eclipse/RestartIO.cpp | 54 +++++-------- src/opm/output/eclipse/RestartValue.cpp | 96 ++++++++++++++++++++++ tests/test_Restart.cpp | 103 ++++++++++-------------- 8 files changed, 179 insertions(+), 121 deletions(-) create mode 100644 src/opm/output/eclipse/RestartValue.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 6f3eb4b68..3e4be5d17 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -150,6 +150,7 @@ if(ENABLE_ECL_OUTPUT) src/opm/output/eclipse/Summary.cpp src/opm/output/eclipse/Tables.cpp src/opm/output/eclipse/RegionCache.cpp + src/opm/output/eclipse/RestartValue.cpp src/opm/output/data/Solution.cpp ) endif() diff --git a/opm/output/eclipse/EclipseIO.hpp b/opm/output/eclipse/EclipseIO.hpp index 0aca168d2..a1c5c38e6 100644 --- a/opm/output/eclipse/EclipseIO.hpp +++ b/opm/output/eclipse/EclipseIO.hpp @@ -218,7 +218,7 @@ public: missing, if the bool is false missing keywords will be ignored (there will *not* be an empty vector in the return value). */ - RestartValue loadRestart(const std::vector& solution_keys, const std::map& extra_keys = {}) const; + RestartValue loadRestart(const std::vector& solution_keys, const std::vector& extra_keys = {}) const; EclipseIO( const EclipseIO& ) = delete; diff --git a/opm/output/eclipse/RestartIO.hpp b/opm/output/eclipse/RestartIO.hpp index 5da9120d2..2f7c241b2 100644 --- a/opm/output/eclipse/RestartIO.hpp +++ b/opm/output/eclipse/RestartIO.hpp @@ -88,7 +88,7 @@ RestartValue load( const std::string& filename, const EclipseState& es, const EclipseGrid& grid, const Schedule& schedule, - const std::map& extra_keys = {}); + const std::vector& extra_keys = {}); } } diff --git a/opm/output/eclipse/RestartValue.hpp b/opm/output/eclipse/RestartValue.hpp index a6b6330ee..20e4b6aa1 100644 --- a/opm/output/eclipse/RestartValue.hpp +++ b/opm/output/eclipse/RestartValue.hpp @@ -22,6 +22,10 @@ #include #include +#include +#include +#include + namespace Opm { @@ -30,11 +34,12 @@ namespace Opm { std::string key; UnitSystem::measure dim; - bool required = true; + bool required; RestartKey( const std::string& _key, UnitSystem::measure _dim) : key(_key), - dim(_dim) + dim(_dim), + required(true) {} @@ -48,32 +53,27 @@ namespace Opm { /* - A simple struct - the only purpose is to facilitate return by value from the - RestartIO::load( ) function. + A simple class used to communicate values between the simulator and the + RestartIO function. */ - struct RestartValue { - + class RestartValue { + public: + using extra_vector = std::vector>>; data::Solution solution; data::Wells wells; - std::map> extra = {}; + extra_vector extra; + RestartValue(data::Solution sol, data::Wells wells_arg); - RestartValue(data::Solution sol, data::Wells wells_arg, std::map> extra_arg) : - solution(std::move(sol)), - wells(std::move(wells_arg)), - extra(std::move(extra_arg)) - { - } - - - RestartValue(data::Solution sol, data::Wells wells_arg) : - solution(std::move(sol)), - wells(std::move(wells_arg)) - { - } + bool has_extra(const std::string& key) const; + void add_extra(const std::string& key, UnitSystem::measure dimension, std::vector data); + void add_extra(const std::string& key, const std::vector& data); + const std::vector& get_extra(const std::string& key) const; + void convertFromSI(const UnitSystem& units); + void convertToSI(const UnitSystem& units); }; } diff --git a/src/opm/output/eclipse/EclipseIO.cpp b/src/opm/output/eclipse/EclipseIO.cpp index 87c903076..31c9db969 100644 --- a/src/opm/output/eclipse/EclipseIO.cpp +++ b/src/opm/output/eclipse/EclipseIO.cpp @@ -495,7 +495,7 @@ void EclipseIO::writeTimeStep(int report_step, -RestartValue EclipseIO::loadRestart(const std::vector& solution_keys, const std::map& extra_keys) const { +RestartValue EclipseIO::loadRestart(const std::vector& solution_keys, const std::vector& extra_keys) const { const auto& es = this->impl->es; const auto& grid = this->impl->grid; const auto& schedule = this->impl->schedule; diff --git a/src/opm/output/eclipse/RestartIO.cpp b/src/opm/output/eclipse/RestartIO.cpp index 0442e3ca3..f97823e9a 100644 --- a/src/opm/output/eclipse/RestartIO.cpp +++ b/src/opm/output/eclipse/RestartIO.cpp @@ -104,10 +104,9 @@ namespace { inline data::Solution restoreSOLUTION( ecl_file_view_type* file_view, const std::vector& solution_keys, - const UnitSystem& units, int numcells) { - data::Solution sol; + data::Solution sol( false ); for (const auto& value : solution_keys) { const std::string& key = value.key; UnitSystem::measure dim = value.dim; @@ -130,8 +129,6 @@ namespace { + ", mismatched number of cells" ); std::vector data = double_vector( ecl_kw ); - units.to_si( dim , data ); - sol.insert( key, dim, data , data::TargetType::RESTART_SOLUTION ); } @@ -224,7 +221,7 @@ RestartValue load( const std::string& filename, const EclipseState& es, const EclipseGrid& grid, const Schedule& schedule, - const std::map& extra_keys) { + const std::vector& extra_keys) { int sim_step = std::max(report_step - 1, 0); const bool unified = ( ERT::EclFiletype( filename ) == ECL_UNIFIED_RESTART_FILE ); @@ -248,23 +245,24 @@ RestartValue load( const std::string& filename, const ecl_kw_type * opm_iwel = ecl_file_view_iget_named_kw( file_view, "OPM_IWEL", 0 ); UnitSystem units( static_cast(ecl_kw_iget_int( intehead , INTEHEAD_UNIT_INDEX ))); - RestartValue rst_value( restoreSOLUTION( file_view, solution_keys, units , grid.getNumActive( )), + RestartValue rst_value( restoreSOLUTION( file_view, solution_keys, grid.getNumActive( )), restore_wells( opm_xwel, opm_iwel, sim_step , es, grid, schedule)); - for (const auto& pair : extra_keys) { - const std::string& key = pair.first; - bool required = pair.second; + for (const auto& extra : extra_keys) { + const std::string& key = extra.key; + bool required = extra.required; if (ecl_file_view_has_kw( file_view , key.c_str())) { const ecl_kw_type * ecl_kw = ecl_file_view_iget_named_kw( file_view , key.c_str() , 0 ); const double * data_ptr = ecl_kw_get_double_ptr( ecl_kw ); const double * end_ptr = data_ptr + ecl_kw_get_size( ecl_kw ); - rst_value.extra[ key ] = { data_ptr, end_ptr }; + rst_value.add_extra(key, extra.dim, {data_ptr, end_ptr}); } else if (required) throw std::runtime_error("No such key in file: " + key); } + rst_value.convertToSI(units); return rst_value; } @@ -505,10 +503,10 @@ void writeHeader(ecl_rst_file_type * rst_file, } -void writeExtraData(ecl_rst_file_type* rst_file, const std::map>& extra_data) { - for (const auto& pair : extra_data) { - const std::string& key = pair.first; - const std::vector& data = pair.second; + void writeExtraData(ecl_rst_file_type* rst_file, const RestartValue::extra_vector& extra_data) { + for (const auto& extra_value : extra_data) { + const std::string& key = extra_value.first.key; + const std::vector& data = extra_value.second; { ecl_kw_type * ecl_kw = ecl_kw_alloc_new_shared( key.c_str() , data.size() , ECL_DOUBLE , const_cast(data.data())); ecl_rst_file_add_kw( rst_file , ecl_kw); @@ -538,26 +536,10 @@ void writeWell(ecl_rst_file_type* rst_file, int sim_step, const EclipseState& es } void checkSaveArguments(const data::Solution& cells, - const EclipseGrid& grid, - const std::map>& extra_data) { - - const std::set reserved_keys = {"LOGIHEAD", "INTEHEAD" ,"DOUBHEAD", "IWEL", "XWEL","ICON", "XCON" , "OPM_IWEL" , "OPM_XWEL", "ZWEL"}; - - for (const auto& pair : extra_data) { - const std::string& key = pair.first; - if (key.size() > 8) - throw std::runtime_error("The keys in extra data must have maximum eight characaters"); - - if (cells.has( key )) - throw std::runtime_error("The keys used must unique across Solution and extra_data"); - - if (reserved_keys.find( key ) != reserved_keys.end()) - throw std::runtime_error("The extra_data uses a reserved key"); - } - - for (const auto& elm: cells) - if (elm.second.data.size() != grid.getNumActive()) - throw std::runtime_error("Wrong size on solution vector: " + elm.first); + const EclipseGrid& grid) { + for (const auto& elm: cells) + if (elm.second.data.size() != grid.getNumActive()) + throw std::runtime_error("Wrong size on solution vector: " + elm.first); } } @@ -571,7 +553,7 @@ void save(const std::string& filename, const Schedule& schedule, bool write_double) { - checkSaveArguments( value.solution, grid, value.extra); + checkSaveArguments( value.solution, grid); { int sim_step = std::max(report_step - 1, 0); int ert_phase_mask = es.runspec().eclPhaseMask( ); @@ -586,7 +568,7 @@ void save(const std::string& filename, rst_file.reset( ecl_rst_file_open_write( filename.c_str() ) ); - value.solution.convertFromSI( units ); + value.convertFromSI( units ); writeHeader( rst_file.get(), sim_step, report_step, posix_time , sim_time, ert_phase_mask, units, schedule , grid ); writeWell( rst_file.get(), sim_step, es , grid, schedule, value.wells); writeSolution( rst_file.get(), value.solution, write_double ); diff --git a/src/opm/output/eclipse/RestartValue.cpp b/src/opm/output/eclipse/RestartValue.cpp new file mode 100644 index 000000000..3fb6fc4c8 --- /dev/null +++ b/src/opm/output/eclipse/RestartValue.cpp @@ -0,0 +1,96 @@ +/* + Copyright (c) 2018 Statoil ASA + + This file is part of the Open Porous Media project (OPM). + + OPM 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. + + OPM 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 for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include +#include + +#include + +namespace Opm { + + namespace { + + const std::set reserved_keys = {"LOGIHEAD", "INTEHEAD" ,"DOUBHEAD", "IWEL", "XWEL","ICON", "XCON" , "OPM_IWEL" , "OPM_XWEL", "ZWEL"}; + + } + + + + RestartValue::RestartValue(data::Solution sol, data::Wells wells_arg) : + solution(std::move(sol)), + wells(std::move(wells_arg)) + { + } + + const std::vector& RestartValue::get_extra(const std::string& key) const { + const auto iter = std::find_if(this->extra.begin(), this->extra.end(), [&](std::pair> pair) {return (pair.first.key == key);}); + if (iter == this->extra.end()) + throw std::invalid_argument("No such extra key " + key); + + return iter->second; + } + + bool RestartValue::has_extra(const std::string& key) const { + const auto iter = std::find_if(this->extra.begin(), this->extra.end(), [&](std::pair> pair) {return (pair.first.key == key);}); + return (iter != this->extra.end()); + } + + void RestartValue::add_extra(const std::string& key, UnitSystem::measure dimension, std::vector data) { + if (key.size() > 8) + throw std::runtime_error("The keys used for Eclipse output must be maximum 8 characters long."); + + if (this->has_extra(key)) + throw std::runtime_error("The keys in the extra vector must be unique."); + + if (this->solution.has(key)) + throw std::runtime_error("The key " + key + " is already present in the solution section."); + + if (reserved_keys.find(key) != reserved_keys.end()) + throw std::runtime_error("Can not use reserved key:" + key); + + this->extra.push_back( std::make_pair(RestartKey(key, dimension), data)); + } + + void RestartValue::add_extra(const std::string& key, const std::vector& data) { + this->add_extra(key, UnitSystem::measure::identity, data); + } + + + void RestartValue::convertFromSI(const UnitSystem& units) { + this->solution.convertFromSI(units); + for (auto & extra_value : this->extra) { + const auto& restart_key = extra_value.first; + auto & data = extra_value.second; + + units.from_si(restart_key.dim, data); + } + } + + void RestartValue::convertToSI(const UnitSystem& units) { + this->solution.convertToSI(units); + for (auto & extra_value : this->extra) { + const auto& restart_key = extra_value.first; + auto & data = extra_value.second; + + units.to_si(restart_key.dim, data); + } + } + + +} diff --git a/tests/test_Restart.cpp b/tests/test_Restart.cpp index 29fb4bfcb..4818f9ad2 100644 --- a/tests/test_Restart.cpp +++ b/tests/test_Restart.cpp @@ -355,14 +355,15 @@ RestartValue first_sim(const EclipseState& es, EclipseIO& eclWriter, bool write_ auto sol = mkSolution( num_cells ); auto wells = mkWells(); + RestartValue restart_value(sol, wells); eclWriter.writeTimeStep( 1, false, first_step - start_time, - RestartValue(sol,wells), + restart_value, {}, {}, {}, write_double); - return { sol, wells , {}}; + return restart_value; } RestartValue second_sim(const EclipseIO& writer, const std::vector& solution_keys) { @@ -497,51 +498,22 @@ BOOST_AUTO_TEST_CASE(WriteWrongSOlutionSize) { BOOST_AUTO_TEST_CASE(ExtraData_KEYS) { Setup setup("FIRST_SIM.DATA"); - { - ERT::TestArea testArea("test_Restart"); - auto num_cells = setup.grid.getNumActive( ); - auto cells = mkSolution( num_cells ); - auto wells = mkWells(); + auto num_cells = setup.grid.getNumActive( ); + auto cells = mkSolution( num_cells ); + auto wells = mkWells(); + RestartValue restart_value(cells, wells); - /* To fit with the eclipse format limitations the keys must be max 8 characters long. */ - { - std::map> extra; - extra["TOO_LONG_KEY"] = {0,1,2,3}; - BOOST_CHECK_THROW( RestartIO::save("FILE.UNRST", 1 , - 100, - RestartValue(cells, wells, extra), - setup.es, - setup.grid, - setup.schedule), - std::runtime_error); - } + BOOST_CHECK_THROW( restart_value.add_extra("TOO-LONG-KEY", {0,1,2}), std::runtime_error); - /* The keys must be unique across solution and extra_data */ - { - std::map> extra; - extra["PRESSURE"] = {0,1,2,3}; - BOOST_CHECK_THROW( RestartIO::save("FILE.UNRST", 1 , - 100, - RestartValue(cells, wells, extra), - setup.es, - setup.grid, - setup.schedule), - std::runtime_error); - } + // Keys must be unique + restart_value.add_extra("KEY", {0,1,1}); + BOOST_CHECK_THROW( restart_value.add_extra("KEY", {0,1,1}), std::runtime_error); - /* Must avoid using reserved keys like 'LOGIHEAD' */ - { - std::map> extra; - extra["LOGIHEAD"] = {0,1,2,3}; - BOOST_CHECK_THROW( RestartIO::save("FILE.UNRST", 1 , - 100, - RestartValue(cells, wells, extra), - setup.es, - setup.grid, - setup.schedule), - std::runtime_error); - } - } + /* The keys must be unique across solution and extra_data */ + BOOST_CHECK_THROW( restart_value.add_extra("PRESSURE", {0,1}), std::runtime_error); + + /* Must avoid using reserved keys like 'LOGIHEAD' */ + BOOST_CHECK_THROW( restart_value.add_extra("LOGIHEAD", {0,1}), std::runtime_error); } BOOST_AUTO_TEST_CASE(ExtraData_content) { @@ -551,12 +523,14 @@ BOOST_AUTO_TEST_CASE(ExtraData_content) { auto num_cells = setup.grid.getNumActive( ); auto cells = mkSolution( num_cells ); auto wells = mkWells(); + const auto& units = setup.es.getUnits(); { - std::map> extra; - extra["EXTRA"] = {0,1,2,3}; + RestartValue restart_value(cells, wells); + + restart_value.add_extra("EXTRA", UnitSystem::measure::pressure, {10,1,2,3}); RestartIO::save("FILE.UNRST", 1 , 100, - RestartValue(cells, wells, extra), + restart_value, setup.es, setup.grid, setup.schedule); @@ -567,33 +541,38 @@ BOOST_AUTO_TEST_CASE(ExtraData_content) { { ecl_kw_type * ex = ecl_file_iget_named_kw( f , "EXTRA" , 0 ); BOOST_CHECK_EQUAL( ecl_kw_get_header( ex) , "EXTRA" ); - BOOST_CHECK_EQUAL( 4, ecl_kw_get_size( ex )); - BOOST_CHECK_EQUAL( 0 , ecl_kw_iget_double( ex, 0 )); - BOOST_CHECK_EQUAL( 3 , ecl_kw_iget_double( ex, 3 )); + BOOST_CHECK_EQUAL( 4 , ecl_kw_get_size( ex )); + + BOOST_CHECK_CLOSE( 10 , units.to_si( UnitSystem::measure::pressure, ecl_kw_iget_double( ex, 0 )), 0.00001); + BOOST_CHECK_CLOSE( units.from_si( UnitSystem::measure::pressure, 3) , ecl_kw_iget_double( ex, 3 ), 0.00001); } ecl_file_close( f ); } - BOOST_CHECK_THROW( RestartIO::load( "FILE.UNRST" , 1 , {}, setup.es, setup.grid , setup.schedule, {{"NOT-THIS", true}}) , std::runtime_error ); + BOOST_CHECK_THROW( RestartIO::load( "FILE.UNRST" , 1 , {}, setup.es, setup.grid , setup.schedule, + {{"NOT-THIS", UnitSystem::measure::identity, true}}) , std::runtime_error ); { const auto rst_value = RestartIO::load( "FILE.UNRST" , 1 , { RestartKey("SWAT", UnitSystem::measure::identity), RestartKey("NO", UnitSystem::measure::identity, false)}, - setup.es, setup.grid , setup.schedule, {{"EXTRA", true}, {"EXTRA2", false}}); - const auto pair = rst_value.extra.find( "EXTRA" ); - const std::vector extraval = pair->second; - const std::vector expected = {0,1,2,3}; + setup.es, setup.grid , setup.schedule, + {{"EXTRA", UnitSystem::measure::pressure, true}, + {"EXTRA2", UnitSystem::measure::identity, false}}); + + BOOST_CHECK(!rst_value.has_extra("EXTRA2")); + BOOST_CHECK( rst_value.has_extra("EXTRA")); + BOOST_CHECK_THROW(rst_value.get_extra("EXTRA2"), std::invalid_argument); + const auto& extraval = rst_value.get_extra("EXTRA"); + const std::vector expected = {10,1,2,3}; - BOOST_CHECK_EQUAL( rst_value.solution.has("SWAT") , true ); BOOST_CHECK_EQUAL( rst_value.solution.has("NO") , false ); - BOOST_CHECK_EQUAL( rst_value.extra.size() , 1 ); - BOOST_CHECK_EQUAL( extraval.size() , 4 ); - BOOST_CHECK_EQUAL_COLLECTIONS( extraval.begin(), extraval.end(), expected.begin() , expected.end()); - - const auto missing = rst_value.extra.find( "EXTRA2"); - BOOST_CHECK( missing == rst_value.extra.end() ); + for (size_t i=0; i < expected.size(); i++) + BOOST_CHECK_CLOSE(extraval[i], expected[i], 1e-5); } } } } + + + }