diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 6a530de73..5685673af 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -695,6 +695,7 @@ if(ENABLE_ECL_INPUT) opm/input/eclipse/EclipseState/Grid/SatfuncPropertyInitializers.hpp opm/input/eclipse/EclipseState/Grid/Fault.hpp opm/input/eclipse/EclipseState/Grid/Box.hpp + opm/input/eclipse/EclipseState/Grid/FieldProps.hpp opm/input/eclipse/EclipseState/Grid/FieldPropsManager.hpp opm/input/eclipse/EclipseState/Grid/FaultFace.hpp opm/input/eclipse/EclipseState/Grid/NNC.hpp diff --git a/opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp b/opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp index 67c38045b..33c2d8de2 100644 --- a/opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp +++ b/opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp @@ -66,6 +66,7 @@ namespace Opm { EclipseGrid(size_t nx, size_t ny, size_t nz, double dx = 1.0, double dy = 1.0, double dz = 1.0); + explicit EclipseGrid(const GridDims& gd); EclipseGrid(const std::array& dims , const std::vector& coord , diff --git a/src/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp b/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp similarity index 99% rename from src/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp rename to opm/input/eclipse/EclipseState/Grid/FieldProps.hpp index 28a825b3e..387e8d297 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp +++ b/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp @@ -377,8 +377,11 @@ public: - + /// Normal constructor for FieldProps. FieldProps(const Deck& deck, const Phases& phases, const EclipseGrid& grid, const TableManager& table_arg); + /// Special case constructor used to process ACTNUM only. + FieldProps(const Deck& deck, const EclipseGrid& grid); + void reset_actnum(const std::vector& actnum); void apply_numerical_aquifers(const NumericalAquifers& numerical_aquifers); @@ -386,6 +389,7 @@ public: const std::string& default_region() const; std::vector actnum(); + const std::vector& actnumRaw() const; template static bool supported(const std::string& keyword); @@ -504,6 +508,7 @@ public: static bool rst_cmp(const FieldProps& full_arg, const FieldProps& rst_arg); private: void scanGRIDSection(const GRIDSection& grid_section); + void scanGRIDSectionOnlyACTNUM(const GRIDSection& grid_section); void scanEDITSection(const EDITSection& edit_section); void scanPROPSSection(const PROPSSection& props_section); void scanREGIONSSection(const REGIONSSection& regions_section); diff --git a/src/opm/input/eclipse/EclipseState/Grid/EclipseGrid.cpp b/src/opm/input/eclipse/EclipseState/Grid/EclipseGrid.cpp index 11c55820e..fe4bc0b1c 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/EclipseGrid.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/EclipseGrid.cpp @@ -21,6 +21,7 @@ #define _USE_MATH_DEFINES #include +#include #include #include @@ -124,6 +125,25 @@ EclipseGrid::EclipseGrid(const std::string& fileName ) } +EclipseGrid::EclipseGrid(const GridDims& gd) + : GridDims(gd), + m_minpvMode(MinpvMode::ModeEnum::Inactive), + m_pinchoutMode(PinchMode::ModeEnum::TOPBOT), + m_multzMode(PinchMode::ModeEnum::TOP), + m_pinchGapMode(PinchMode::ModeEnum::GAP) +{ + this->m_nactive = this->getCartesianSize(); + this->active_volume = std::nullopt; + // Nothing else initialized. Leaving in particular as empty: + // m_actnum, + // m_global_to_active, + // m_active_to_global. + // + // EclipseGrid will only be usable for constructing Box objects with + // all cells active. +} + + EclipseGrid::EclipseGrid(size_t nx, size_t ny , size_t nz, double dx, double dy, double dz) : GridDims(nx, ny, nz), @@ -478,6 +498,9 @@ EclipseGrid::EclipseGrid(const Deck& deck, const int * actnum) } size_t EclipseGrid::activeIndex(size_t globalIndex) const { + if (m_global_to_active.empty()) { + return globalIndex; + } if (m_global_to_active[ globalIndex] == -1) { throw std::invalid_argument("Input argument does not correspond to an active cell"); @@ -1145,23 +1168,15 @@ EclipseGrid::EclipseGrid(const Deck& deck, const int * actnum) const std::vector& zcorn = ZCORNKeyWord.getSIDoubleData(); const std::vector& coord = COORDKeyWord.getSIDoubleData(); - int * actnum = nullptr; - std::vector actnumVector; + EclipseGrid topologyOnlyGrid(static_cast(*this)); + FieldProps fp(deck, topologyOnlyGrid); + const auto& actnumVector = fp.actnumRaw(); + if (actnumVector.size() != this->getCartesianSize()) + throw std::invalid_argument("ACTNUM vector has wrong size"); - if (deck.hasKeyword()) { - const auto& actnumKeyword = deck.get().back(); - actnumVector = actnumKeyword.getIntData(); + OpmLog::info(fmt::format("\nCreating cornerpoint grid from keywords ZCORN, COORD and ACTNUM")); - if (actnumVector.size() != this->getCartesianSize()) - throw std::invalid_argument("ACTNUM vector has wrong size"); - - actnum = actnumVector.data(); - OpmLog::info(fmt::format("\nCreating cornerpoint grid from keywords ZCORN, COORD and ACTNUM")); - } else - OpmLog::info(fmt::format("\nCreating cornerpoint grid from keywords ZCORN and COORD")); - - - initCornerPointGrid( coord , zcorn, actnum); + initCornerPointGrid( coord , zcorn, actnumVector.data()); } } @@ -1410,8 +1425,11 @@ std::vector EclipseGrid::createDVector(const std::array& dims, st bool EclipseGrid::cellActive( size_t globalIndex ) const { assertGlobalIndex( globalIndex ); - - return m_actnum[globalIndex]>0; + if (m_actnum.empty()) { + return true; + } else { + return m_actnum[globalIndex]>0; + } } bool EclipseGrid::cellActive( size_t i , size_t j , size_t k ) const { diff --git a/src/opm/input/eclipse/EclipseState/Grid/FieldProps.cpp b/src/opm/input/eclipse/EclipseState/Grid/FieldProps.cpp index 595382dfa..449634824 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/FieldProps.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/FieldProps.cpp @@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . */ + +#include + #include #include #include @@ -46,7 +49,6 @@ #include #include -#include "FieldProps.hpp" #include "Operate.hpp" @@ -490,6 +492,32 @@ FieldProps::FieldProps(const Deck& deck, const Phases& phases, const EclipseGrid } +// Special constructor ONLY used to get the correct ACTNUM. +// The grid argument should have all active cells. +FieldProps::FieldProps(const Deck& deck, const EclipseGrid& grid) : + active_size(grid.getNumActive()), + global_size(grid.getCartesianSize()), + unit_system(deck.getActiveUnitSystem()), + nx(grid.getNX()), + ny(grid.getNY()), + nz(grid.getNZ()), + m_phases(), + m_satfuncctrl(deck), + m_actnum(global_size, 1), // NB! activates all at start! + cell_volume(), // NB! empty for this purpose. + cell_depth(), // NB! empty for this purpose. + m_default_region(default_region_keyword(deck)), + grid_ptr(&grid), + tables() // NB! empty for this purpose. +{ + if (this->active_size != this->global_size) { + throw std::logic_error("Programmer error: FieldProps special case processing for ACTNUM called with grid object that already had deactivated cells."); + } + if (DeckSection::hasGRID(deck)) + this->scanGRIDSectionOnlyACTNUM(GRIDSection(deck)); +} + + void FieldProps::reset_actnum(const std::vector& new_actnum) { if (this->global_size != new_actnum.size()) @@ -1148,6 +1176,11 @@ std::vector FieldProps::actnum() { } +const std::vector& FieldProps::actnumRaw() const { + return m_actnum; +} + + void FieldProps::scanGRIDSection(const GRIDSection& grid_section) { Box box(*this->grid_ptr); @@ -1168,6 +1201,25 @@ void FieldProps::scanGRIDSection(const GRIDSection& grid_section) { } } +void FieldProps::scanGRIDSectionOnlyACTNUM(const GRIDSection& grid_section) { + Box box(*this->grid_ptr); + + for (const auto& keyword : grid_section) { + const std::string& name = keyword.name(); + if (name == "ACTNUM") { + this->handle_int_keyword(Fieldprops::keywords::GRID::int_keywords.at(name), keyword, box); + } else if (name == "EQUALS" || (Fieldprops::keywords::box_keywords.count(name) == 1)) { + this->handle_keyword(keyword, box); + } + } + const auto iter = this->int_data.find("ACTNUM"); + if (iter == this->int_data.end()) { + m_actnum.assign(this->grid_ptr->getCartesianSize(), 1); + } else { + m_actnum = iter->second.data; + } +} + void FieldProps::scanEDITSection(const EDITSection& edit_section) { Box box(*this->grid_ptr); for (const auto& keyword : edit_section) { diff --git a/src/opm/input/eclipse/EclipseState/Grid/FieldPropsManager.cpp b/src/opm/input/eclipse/EclipseState/Grid/FieldPropsManager.cpp index d6d6fbe11..8ad22783c 100644 --- a/src/opm/input/eclipse/EclipseState/Grid/FieldPropsManager.cpp +++ b/src/opm/input/eclipse/EclipseState/Grid/FieldPropsManager.cpp @@ -20,14 +20,12 @@ #include #include #include +#include #include #include #include #include -#include "FieldProps.hpp" - - namespace Opm { diff --git a/tests/parser/EclipseGridTests.cpp b/tests/parser/EclipseGridTests.cpp index 73daa2ee7..04596dfb5 100644 --- a/tests/parser/EclipseGridTests.cpp +++ b/tests/parser/EclipseGridTests.cpp @@ -978,6 +978,111 @@ BOOST_AUTO_TEST_CASE(GridBoxActnum) { } } + +/// Similar to createActnumBoxDeck(), but uses a corner point grid, and +/// also used EQUALS for re-activating a cell. +/// Creates a deck where the top-layer has ACTNUM = 0 and two partially +/// overlapping 2*2*2 boxes in the center, one [5,7]^3 and one [6,8]^3 +/// have ACTNUM = 0, then the cell (7,7,7) is made active using EQUALS. +static Opm::Deck createActnumBoxDeck2() { + const char* deckData = "RUNSPEC\n" + "\n" + "DIMENS \n" + " 10 10 10 / \n" + "GRID\n" + "COORD\n" + " 726*1 / \n" + "ZCORN \n" + " 8000*1 / \n" + "PORO \n" + " 1000*0.15 /\n" + "EQUALS\n" + " ACTNUM 0 1 10 1 10 1 1 /\n" // disable top layer + "/ \n" + // start box + "BOX\n" + " 5 7 5 7 5 7 /\n" + "ACTNUM \n" + " 0 0 0 0 0 0 0 0 0\n" + " 0 0 0 0 0 0 0 0 0\n" + " 0 0 0 0 0 0 0 0 0\n" + "/\n" + "BOX\n" // don't need ENDBOX + " 6 8 6 8 6 8 /\n" + "ACTNUM \n" + " 27*0\n" + "/\n" + "ENDBOX\n" + "EQUALS\n" + " ACTNUM 1 7 7 7 7 7 7 /\n" // re-enable cell (7,7,7) + "/ \n" + "FLUXNUM\n" + "1000*0 /\n" + "EDIT\n" + "PORV\n" + "1000*1 /\n"; + + Opm::Parser parser; + return parser.parseString( deckData); +} + +BOOST_AUTO_TEST_CASE(GridBoxActnum2) { + auto deck = createActnumBoxDeck2(); + Opm::EclipseState es( deck); + const auto& fp = es.fieldProps(); + const auto& grid = es.getInputGrid(); + + BOOST_CHECK_NO_THROW(fp.get_int("ACTNUM")); + + size_t active = 10 * 10 * 10 // 1000 + - (10 * 10 * 1) // - top layer + - ( 3 * 3 * 3) // - [5,7]^3 box + - ( 3 * 3 * 3) // - [6,8]^3 box + + ( 2 * 2 * 2) // + inclusion/exclusion + + 1; // cell (7,7,7) + BOOST_CHECK_NO_THROW(grid.getNumActive()); + BOOST_CHECK_EQUAL(grid.getNumActive(), active); + + BOOST_CHECK_EQUAL(es.getInputGrid().getNumActive(), active); + + { + size_t active_index = 0; + // NB: The implementation of this test actually assumes that + // the loops are running with z as the outer and x as the + // inner direction. + for (size_t z = 0; z < grid.getNZ(); z++) { + for (size_t y = 0; y < grid.getNY(); y++) { + for (size_t x = 0; x < grid.getNX(); x++) { + if (z == 0) + BOOST_CHECK(!grid.cellActive(x, y, z)); + else if (x == 6 && y == 6 && z == 6) { + BOOST_CHECK(grid.cellActive(x, y, z)); + BOOST_CHECK_EQUAL( grid.activeIndex(x,y,z) , active_index ); + size_t g = grid.getGlobalIndex( x,y,z ); + BOOST_CHECK_EQUAL( grid.activeIndex(g) , active_index ); + ++active_index; + } else if (x >= 4 && x <= 6 && y >= 4 && y <= 6 && z >= 4 && z <= 6) + BOOST_CHECK(!grid.cellActive(x, y, z)); + else if (x >= 5 && x <= 7 && y >= 5 && y <= 7 && z >= 5 && z <= 7) + BOOST_CHECK(!grid.cellActive(x, y, z)); + else { + size_t g = grid.getGlobalIndex( x,y,z ); + + BOOST_CHECK(grid.cellActive(x, y, z)); + BOOST_CHECK_EQUAL( grid.activeIndex(x,y,z) , active_index ); + BOOST_CHECK_EQUAL( grid.activeIndex(g) , active_index ); + + active_index++; + } + } + } + } + + BOOST_CHECK_THROW( grid.activeIndex(0,0,0) , std::invalid_argument ); + } +} + + BOOST_AUTO_TEST_CASE(GridActnumVia3D) { auto deck = createActnumDeck(); diff --git a/tests/parser/FieldPropsTests.cpp b/tests/parser/FieldPropsTests.cpp index 074397fbe..940070a48 100644 --- a/tests/parser/FieldPropsTests.cpp +++ b/tests/parser/FieldPropsTests.cpp @@ -48,7 +48,7 @@ #include #include -#include "src/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp" +#include using namespace Opm; @@ -134,6 +134,58 @@ PERMX } + +BOOST_AUTO_TEST_CASE(CreateFieldPropsForActnum) { + std::string deck_string = R"( +GRID + +BOX + 1 3 1 3 1 1 / + +ACTNUM + 9*0 / + +BOX + 1 3 1 2 1 1 / + +ACTNUM + 6*1 / + +ENDBOX + +-- Re-enable (3,3,1) +EQUALS + ACTNUM 1 3 3 3 3 1 1 / +/ +)"; + + Deck deck = Parser{}.parseString(deck_string); + EclipseGrid grid{GridDims{3, 3, 1}}; + FieldProps fp(deck, grid); + std::vector expected_actnum = { 1, 1, 1, + 1, 1, 1, + 0, 0, 1 }; + auto actnum = fp.actnumRaw(); + BOOST_CHECK_EQUAL_COLLECTIONS(actnum.begin(), actnum.end(), expected_actnum.begin(), expected_actnum.end()); +} + + +BOOST_AUTO_TEST_CASE(CreateFieldPropsForActnumButNoActnumInDeck) { + std::string deck_string = R"( +GRID +)"; + + Deck deck = Parser{}.parseString(deck_string); + EclipseGrid grid{GridDims{3, 3, 1}}; + FieldProps fp(deck, grid); + std::vector expected_actnum = { 1, 1, 1, + 1, 1, 1, + 1, 1, 1 }; + auto actnum = fp.actnumRaw(); + BOOST_CHECK_EQUAL_COLLECTIONS(actnum.begin(), actnum.end(), expected_actnum.begin(), expected_actnum.end()); +} + + BOOST_AUTO_TEST_CASE(INVALID_COPY) { std::string deck_string = R"( GRID