From c637463327203a73d776dc9d754c2dc59a7bf76a Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 9 Oct 2014 15:42:04 +0200 Subject: [PATCH] Added functionality to calculate PORV property. --- .../eclipse/EclipseState/EclipseState.cpp | 86 ++++- .../EclipseState/Grid/tests/CMakeLists.txt | 5 + .../EclipseState/Grid/tests/PORVTests.cpp | 339 ++++++++++++++++++ opm/parser/share/keywords/P/PORV | 1 + 4 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 opm/parser/eclipse/EclipseState/Grid/tests/PORVTests.cpp create mode 100644 opm/parser/share/keywords/P/PORV diff --git a/opm/parser/eclipse/EclipseState/EclipseState.cpp b/opm/parser/eclipse/EclipseState/EclipseState.cpp index bb4ab127a..b3636619e 100644 --- a/opm/parser/eclipse/EclipseState/EclipseState.cpp +++ b/opm/parser/eclipse/EclipseState/EclipseState.cpp @@ -59,6 +59,53 @@ namespace Opm { private: const EclipseState& m_eclipseState; }; + + /*-----------------------------------------------------------------*/ + + class InitPORV : public GridPropertyBasePostProcessor + { + public: + InitPORV(const EclipseState& eclipseState) : + m_eclipseState( eclipseState ) + { } + + + void apply(std::vector& ) const { + EclipseGridConstPtr grid = m_eclipseState.getEclipseGrid(); + /* + Observe that this apply method does not alter the + values input vector, instead it fetches the PORV + property one more time, and then manipulates that. + */ + auto porv = m_eclipseState.getDoubleGridProperty("PORV"); + if (porv->containsNaN()) { + auto poro = m_eclipseState.getDoubleGridProperty("PORO"); + auto ntg = m_eclipseState.getDoubleGridProperty("NTG"); + if (poro->containsNaN()) + throw std::logic_error("Do not have information for the PORV keyword - some defaulted values in PORO"); + { + for (size_t globalIndex = 0; globalIndex < porv->getCartesianSize(); globalIndex++) { + if (std::isnan(porv->iget(globalIndex))) { + double cell_poro = poro->iget(globalIndex); + double cell_ntg = ntg->iget(globalIndex); + double cell_volume = grid->getCellVolume(globalIndex); + porv->iset( globalIndex , cell_poro * cell_volume * cell_ntg); + } + } + } + } + + if (m_eclipseState.hasDoubleGridProperty("MULTPV")) { + auto multpv = m_eclipseState.getDoubleGridProperty("MULTPV"); + porv->multiplyWith( *multpv ); + } + } + + + private: + const EclipseState& m_eclipseState; + }; + } @@ -212,19 +259,19 @@ namespace Opm { m_transMult = std::make_shared( grid->getNX() , grid->getNY() , grid->getNZ()); if (hasDoubleGridProperty("MULTX")) - m_transMult->applyMULT(getDoubleGridProperty("MULTX"), FaceDir::XPlus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTX"), FaceDir::XPlus); if (hasDoubleGridProperty("MULTX-")) - m_transMult->applyMULT(getDoubleGridProperty("MULTX-"), FaceDir::XMinus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTX-"), FaceDir::XMinus); if (hasDoubleGridProperty("MULTY")) - m_transMult->applyMULT(getDoubleGridProperty("MULTY"), FaceDir::YPlus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTY"), FaceDir::YPlus); if (hasDoubleGridProperty("MULTY-")) - m_transMult->applyMULT(getDoubleGridProperty("MULTY-"), FaceDir::YMinus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTY-"), FaceDir::YMinus); if (hasDoubleGridProperty("MULTZ")) - m_transMult->applyMULT(getDoubleGridProperty("MULTZ"), FaceDir::ZPlus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTZ"), FaceDir::ZPlus); if (hasDoubleGridProperty("MULTZ-")) - m_transMult->applyMULT(getDoubleGridProperty("MULTZ-"), FaceDir::ZMinus); + m_transMult->applyMULT(m_doubleGridProperties->getKeyword("MULTZ-"), FaceDir::ZMinus); } void EclipseState::initFaults(DeckConstPtr deck, ParserLogPtr parserLog) { @@ -402,9 +449,22 @@ namespace Opm { return m_doubleGridProperties->hasKeyword( keyword ); } + /* - Observe that this will autocreate a property if it has not been explicitly added. + 1. The public methods getIntGridProperty & getDoubleGridProperty + will invoke and run the property post processor (if any is + registered); the post processor will only run one time. + + It is important that post processor is not run prematurely, + internal functions in EclipseState should therefor ask for + properties by invoking the getKeyword() method of the + m_intGridProperties / m_doubleGridProperties() directly and + not through these methods. + + 2. Observe that this will autocreate a property if it has not + been explicitly added. */ + std::shared_ptr > EclipseState::getIntGridProperty( const std::string& keyword ) const { return m_intGridProperties->getKeyword( keyword ); } @@ -417,6 +477,15 @@ namespace Opm { return gridProperty; } + + /* + Due to the post processor which might be applied to the + GridProperty objects it is essential that this method use the + m_intGridProperties / m_doubleGridProperties fields directly + and *NOT* use the public methods getIntGridProperty / + getDoubleGridProperty. + */ + void EclipseState::loadGridPropertyFromDeckKeyword(std::shared_ptr inputBox, DeckKeywordConstPtr deckKeyword, ParserLogPtr parserLog, @@ -456,6 +525,7 @@ namespace Opm { double nan = std::numeric_limits::quiet_NaN(); const auto eptLookup = std::make_shared>(*deck, *this); const auto distributeTopLayer = std::make_shared(*this); + const auto initPORV = std::make_shared(*this); // Note that the variants of grid keywords for radial grids @@ -599,7 +669,7 @@ namespace Opm { SupportedDoubleKeywordInfo( "PORO" , nan, distributeTopLayer , "1" ), // pore volume - SupportedDoubleKeywordInfo( "PORV" , nan, "Volume" ), + SupportedDoubleKeywordInfo( "PORV" , nan, initPORV , "Volume" ), // pore volume multipliers SupportedDoubleKeywordInfo( "MULTPV", 1.0, "1" ), diff --git a/opm/parser/eclipse/EclipseState/Grid/tests/CMakeLists.txt b/opm/parser/eclipse/EclipseState/Grid/tests/CMakeLists.txt index 0ba44e30f..020e9066b 100644 --- a/opm/parser/eclipse/EclipseState/Grid/tests/CMakeLists.txt +++ b/opm/parser/eclipse/EclipseState/Grid/tests/CMakeLists.txt @@ -27,6 +27,11 @@ target_link_libraries(runBoxTests Parser ${Boost_LIBRARIES}) add_test(NAME runBoxTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runBoxTests ) +add_executable(runPORVTests PORVTests.cpp) +target_link_libraries(runPORVTests Parser ${Boost_LIBRARIES}) +add_test(NAME runPORVTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runPORVTests ) + + add_executable(runBoxManagerTests BoxManagerTests.cpp) target_link_libraries(runBoxManagerTests Parser ${Boost_LIBRARIES}) add_test(NAME runBoxManagerTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${TEST_MEMCHECK_TOOL} ${EXECUTABLE_OUTPUT_PATH}/runBoxManagerTests ) diff --git a/opm/parser/eclipse/EclipseState/Grid/tests/PORVTests.cpp b/opm/parser/eclipse/EclipseState/Grid/tests/PORVTests.cpp new file mode 100644 index 000000000..a3fcb1784 --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Grid/tests/PORVTests.cpp @@ -0,0 +1,339 @@ +/* + Copyright 2014 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 + +#define BOOST_TEST_MODULE PORVTESTS +#include +#include + + +#include + +#include +#include +#include +#include +#include + + + +static Opm::DeckPtr createCARTDeck() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "EDIT\n" + "\n"; + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + + + + +static Opm::DeckPtr createDeckWithPORO() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "PORO\n" + "100*0.10 100*0.20 100*0.30 100*0.40 100*0.50 100*0.60 100*0.70 100*0.80 100*0.90 100*1.0 /\n" + "EDIT\n" + "\n"; + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + + +static Opm::DeckPtr createDeckWithPORVPORO() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "BOX \n" + "1 10 1 10 1 1 /\n" + "PORV\n" + "100*77 /\n" + "ENDBOX \n" + "PORO\n" + "100*0.10 100*0.20 100*0.30 100*0.40 100*0.50 100*0.60 100*0.70 100*0.80 100*0.90 100*1.0 /\n" + "EDIT\n" + "\n"; + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + +static Opm::DeckPtr createDeckWithMULTPV() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "BOX \n" + "1 10 1 10 1 1 /\n" + "PORV\n" + "100*77 /\n" + "ENDBOX \n" + "PORO\n" + "100*0.10 100*0.20 100*0.30 100*0.40 100*0.50 100*0.60 100*0.70 100*0.80 100*0.90 100*1.0 /\n" + "EDIT\n" + "BOX \n" + "1 5 1 5 1 1 /\n" + "MULTPV\n" + "25*10 / \n" + "ENDBOX \n" + "BOX \n" + "1 10 1 10 10 10 /\n" + "MULTPV\n" + "100*10 / \n" + "ENDBOX \n" + "\n"; + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + +static Opm::DeckPtr createDeckWithBOXPORV() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "PORV\n" + "1000*123.456 /\n" + "BOX \n" + "2 3 2 3 2 3 /\n" + "PORV\n" + "8*789.012 /\n" + "ENDBOX\n" + "MULTPV\n" + "1000*10 /\n" + "EDIT\n" + "\n"; + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + +static Opm::DeckPtr createDeckWithNTG() { + const char *deckData = + "RUNSPEC\n" + "\n" + "DIMENS\n" + " 10 10 10 /\n" + "GRID\n" + "DX\n" + "1000*0.25 /\n" + "DYV\n" + "10*0.25 /\n" + "DZ\n" + "1000*0.25 /\n" + "TOPS\n" + "100*0.25 /\n" + "PORO\n" + "1000*0.20 /\n" + "BOX \n" + "1 1 1 1 1 1 /\n" + "PORV \n" + "1*10 /\n" + "ENDBOX\n" + "MULTPV\n" + "1000*10 /\n" + "NTG\n" + "1000*2 /\n" + "EDIT\n" + "\n"; + + + Opm::ParserPtr parser(new Opm::Parser()); + return parser->parseString(deckData) ; +} + + + + +/* + There is something fishy with the tests involving grid property post + processors. It seems that halfways through the test suddenly the + adress of a EclipseState object from a previous test is used; this + leads to segmentation fault. Here the problem is 'solved' by having + all the tests in one BOOST_AUTO_TEST_CASE() { } clause. + + An issue has been posted on Stackoverflow: questions/26275555 +*/ + + +BOOST_AUTO_TEST_CASE(MANY_TEST) { + /* Check that an excpetion is raised if we try to create a PORV field without PORO. */ + { + Opm::DeckPtr deck = createCARTDeck(); + Opm::EclipseState state(deck); + auto poro = state.getDoubleGridProperty("PORO"); + BOOST_CHECK( poro->containsNaN() ); + BOOST_CHECK_THROW( state.getDoubleGridProperty("PORV") , std::logic_error ); + } + + /* Check that the PORV field is correctly calculated from PORO. */ + { + Opm::DeckPtr deck = createDeckWithPORO(); + Opm::EclipseState state(deck); + auto poro = state.getDoubleGridProperty("PORO"); + BOOST_CHECK( !poro->containsNaN() ); + { + auto porv = state.getDoubleGridProperty("PORV"); + double cell_volume = 0.25 * 0.25 * 0.25; + + + BOOST_CHECK_CLOSE( cell_volume * 0.10 , porv->iget(0,0,0) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 0.10 , porv->iget(9,9,0) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(0,0,4) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(9,9,4) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 1.00 , porv->iget(0,0,9) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 1.00 , porv->iget(9,9,9) , 0.001); + } + } + + /* Check that explicit PORV and CellVOlume * PORO can be combined. */ + { + Opm::DeckPtr deck = createDeckWithPORVPORO(); + Opm::EclipseState state(deck); + auto porv = state.getDoubleGridProperty("PORV"); + double cell_volume = 0.25 * 0.25 * 0.25; + + BOOST_CHECK_CLOSE( 77.0 , porv->iget(0,0,0) , 0.001); + BOOST_CHECK_CLOSE( 77.0 , porv->iget(9,9,0) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(0,0,4) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(9,9,4) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 1.00 , porv->iget(0,0,9) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 1.00 , porv->iget(9,9,9) , 0.001); + } + + /* Check that MULTPV is correctly accounted for. */ + { + Opm::DeckPtr deck = createDeckWithMULTPV(); + Opm::EclipseState state(deck); + auto porv = state.getDoubleGridProperty("PORV"); + double cell_volume = 0.25 * 0.25 * 0.25; + + BOOST_CHECK_CLOSE( 770.0 , porv->iget(0,0,0) , 0.001); + BOOST_CHECK_CLOSE( 770.0 , porv->iget(4,4,0) , 0.001); + BOOST_CHECK_CLOSE( 77.0 , porv->iget(9,9,0) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(0,0,4) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 0.50 , porv->iget(9,9,4) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 0.90 , porv->iget(0,0,8) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 0.90 , porv->iget(9,9,8) , 0.001); + + BOOST_CHECK_CLOSE( cell_volume * 10.00 , porv->iget(0,0,9) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * 10.00 , porv->iget(9,9,9) , 0.001); + } + + /* Check that MULTIPLE Boxed PORV and MULTPV statements work */ + { + Opm::DeckPtr deck = createDeckWithBOXPORV(); + Opm::EclipseState state(deck); + auto porv = state.getDoubleGridProperty("PORV"); + + BOOST_CHECK_CLOSE( 1234.56 , porv->iget(0,0,0) , 0.001); + BOOST_CHECK_CLOSE( 1234.56 , porv->iget(9,9,9) , 0.001); + + BOOST_CHECK_CLOSE( 7890.12 , porv->iget(1,1,1) , 0.001); + BOOST_CHECK_CLOSE( 7890.12 , porv->iget(2,2,2) , 0.001); + + } + + /* Check that MULTIPLE Boxed PORV and MULTPV statements work and NTG */ + { + Opm::DeckPtr deck = createDeckWithNTG(); + Opm::EclipseState state(deck); + auto porv = state.getDoubleGridProperty("PORV"); + double cell_volume = 0.25 * 0.25 * 0.25; + double poro = 0.20; + double multpv = 10; + double NTG = 2; + double PORV = 10; + + BOOST_CHECK_CLOSE( PORV * multpv , porv->iget(0,0,0) , 0.001); + BOOST_CHECK_CLOSE( cell_volume * poro*multpv*NTG , porv->iget(9,9,9) , 0.001); + } +} diff --git a/opm/parser/share/keywords/P/PORV b/opm/parser/share/keywords/P/PORV new file mode 100644 index 000000000..b75a27af6 --- /dev/null +++ b/opm/parser/share/keywords/P/PORV @@ -0,0 +1 @@ +{"name" : "PORV" , "data" : {"value_type" : "DOUBLE" , "dimension":"Length*Length*Length"}}