/* Copyright 2014 Andreas Lauser 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 #if HAVE_DYNAMIC_BOOST_TEST #define BOOST_TEST_DYN_LINK #endif #define BOOST_TEST_MODULE TransmissibilityMultipliers #include #include #include #include #include #include #include #include #if HAVE_OPM_GRID #include #include #include #endif #include #include #include #include // as surprising as it seems, this is a minimal deck required to get to the point where // the transmissibilities are calculated. The problem here is that the OPM property // objects mix fluid and rock properties, so that all properties need to be defined even // if they are not of interest :/ std::string deckPreMult = "RUNSPEC\n" "TABDIMS\n" "/\n" "OIL\n" "GAS\n" "WATER\n" "METRIC\n" "DIMENS\n" "2 2 2/\n" "GRID\n" "DXV\n" "1.0 2.0 /\n" "DYV\n" "3.0 4.0 /\n" "DZV\n" "5.0 6.0/\n" "TOPS\n" "4*100 /\n"; std::string deckPostMult = "PROPS\n" "DENSITY\n" "100 200 300 /\n" "PVTW\n" " 100 1 1e-6 1.0 0 /\n" "PVDG\n" "1 1 1e-2\n" "100 0.25 2e-2 /\n" "PVTO\n" "1e-3 1.0 1.0 1.0\n" " 100.0 1.0 1.0\n" "/\n" "1.0 10.0 1.1 0.9\n" " 100.0 1.1 0.9\n" "/\n" "/\n" "SWOF\n" "0.0 0.0 1.0 0.0\n" "1.0 1.0 0.0 1.0/\n" "SGOF\n" "0.0 0.0 1.0 0.0\n" "1.0 1.0 0.0 1.0/\n" "PORO\n" "8*0.3 /\n" "PERMX\n" "8*1 /\n" "SCHEDULE\n" "TSTEP\n" "1.0 2.0 3.0 4.0 /\n"; std::string origDeckString = deckPreMult + deckPostMult; std::string multDeckString = deckPreMult + "MULTX\n" + "1 2 3 4 5 6 7 8 /\n" + "MULTY\n" + "1 2 3 4 5 6 7 8 /\n" + "MULTZ\n" + "1 2 3 4 5 6 7 8 /\n" + deckPostMult; std::string multMinusDeckString = deckPreMult + "MULTX-\n" + "1 2 3 4 5 6 7 8 /\n" + "MULTY-\n" + "1 2 3 4 5 6 7 8 /\n" + "MULTZ-\n" + "1 2 3 4 5 6 7 8 /\n" + deckPostMult; // the NTG values get harmonically averaged for the transmissibilites. If the value // is the same on both sides, the averaging buils down to a no-op, though... std::string ntgDeckString = deckPreMult + "NTG\n" + "8*0.5 /\n" + deckPostMult; /// \brief The check of the transmissibility values /// /// Written along the UgGridHelpers interface to make it usable for both /// UnstructuredGrid and CpGrid. /// \tparam Grid The type of the grid. /// \param grid The grid where the transmissibility values live /// \param origGeology The originaly geology without multipliers. /// \param multGeology The geology using posititve multipliers. /// \param multMinusGeology The geology uing negative multipliers /// \param ntgGeology template void checkTransmissibilityValues(const G& grid, const Opm::DerivedGeology& origGeology, const Opm::DerivedGeology& multGeology, const Opm::DerivedGeology& multMinusGeology, const Opm::DerivedGeology& ntgGeology); BOOST_AUTO_TEST_CASE(TransmissibilityMultipliersLegacyGridInterface) { Opm::ParameterGroup param; Opm::Parser parser; Opm::ParseContext parseContext; ///// // create a DerivedGeology object without any multipliers involved auto origDeck = parser.parseString(origDeckString, parseContext); Opm::EclipseState origEclipseState(origDeck , parseContext); auto origGridManager = std::make_shared(origEclipseState.getInputGrid()); auto origProps = std::make_shared(origDeck, origEclipseState, *(origGridManager->c_grid())); Opm::DerivedGeology origGeology(*(origGridManager->c_grid()), *origProps, origEclipseState, false); ///// ///// // create a DerivedGeology object _with_ transmissibility multipliers involved auto multDeck = parser.parseString(multDeckString, parseContext); Opm::EclipseState multEclipseState(multDeck, parseContext); auto multGridManager = std::make_shared(multEclipseState.getInputGrid()); auto multProps = std::make_shared(multDeck, multEclipseState, *(multGridManager->c_grid())); Opm::DerivedGeology multGeology(*(multGridManager->c_grid()), *multProps, multEclipseState, false); ///// ///// // create a DerivedGeology object _with_ transmissibility multipliers involved for // the negative faces auto multMinusDeck = parser.parseString(multMinusDeckString, parseContext); Opm::EclipseState multMinusEclipseState(multMinusDeck , parseContext); auto multMinusGridManager = std::make_shared(multMinusEclipseState.getInputGrid()); auto multMinusProps = std::make_shared(multMinusDeck, multMinusEclipseState, *(multMinusGridManager->c_grid())); Opm::DerivedGeology multMinusGeology(*(multMinusGridManager->c_grid()), *multMinusProps, multMinusEclipseState, false); ///// ///// // create a DerivedGeology object with the NTG keyword involved auto ntgDeck = parser.parseString(ntgDeckString, parseContext); Opm::EclipseState ntgEclipseState(ntgDeck, parseContext); auto ntgGridManager = std::make_shared(ntgEclipseState.getInputGrid()); auto ntgProps = std::make_shared(ntgDeck, ntgEclipseState, *(ntgGridManager->c_grid())); Opm::DerivedGeology ntgGeology(*(ntgGridManager->c_grid()), *ntgProps, ntgEclipseState, false); ///// checkTransmissibilityValues(*(origGridManager->c_grid()), origGeology, multGeology, multMinusGeology, ntgGeology); } template void checkTransmissibilityValues(const G& grid, const Opm::DerivedGeology& origGeology, const Opm::DerivedGeology& multGeology, const Opm::DerivedGeology& multMinusGeology, const Opm::DerivedGeology& ntgGeology) { // compare the transmissibilities (note that for this we assume that the multipliers // do not change the grid topology) int numFaces=Opm::UgGridHelpers::numFaces(grid); typename Opm::UgGridHelpers::FaceCellTraits::Type face_cells= Opm::UgGridHelpers::faceCells(grid); for (int faceIdx = 0; faceIdx < numFaces; ++ faceIdx) { // in DUNE-speak, a face here is more like an intersection which is not specific // to a codim-0 entity (i.e., cell) // get the cell indices of the compressed grid for the face's interior and // exterior cell int insideCellIdx = face_cells(faceIdx, 0); int outsideCellIdx = face_cells(faceIdx, 1); if (insideCellIdx < 0 || outsideCellIdx < 0) { // do not consider cells at the domain boundary: Their would only be used for // Dirichlet-like boundary conditions which have not been implemented so // far... continue; } // translate these to canonical indices (i.e., the logically Cartesian ones used by the deck) const int* global_cell=Opm::UgGridHelpers::globalCell(grid); if (global_cell) { insideCellIdx = global_cell[insideCellIdx]; outsideCellIdx = global_cell[outsideCellIdx]; } double origTrans = origGeology.transmissibility()[faceIdx]; double multTrans = multGeology.transmissibility()[faceIdx]; double multMinusTrans = multMinusGeology.transmissibility()[faceIdx]; double ntgTrans = ntgGeology.transmissibility()[faceIdx]; BOOST_CHECK_CLOSE(origTrans*(insideCellIdx + 1), multTrans, 1e-6); BOOST_CHECK_CLOSE(origTrans*(outsideCellIdx + 1), multMinusTrans, 1e-6); const int* cartdims = Opm::UgGridHelpers::cartDims(grid); int insideCellKIdx = insideCellIdx/(cartdims[0]*cartdims[1]); int outsideCellKIdx = outsideCellIdx/(cartdims[0]*cartdims[1]); if (insideCellKIdx == outsideCellKIdx) // NTG only reduces the permebility of the X-Y plane BOOST_CHECK_CLOSE(origTrans*0.5, ntgTrans, 1e-6); } } #if HAVE_OPM_GRID BOOST_AUTO_TEST_CASE(TransmissibilityMultipliersCpGrid) { int argc = 1; char **argv; argv = new (char*); argv[0] = strdup("footest"); Dune::MPIHelper::instance(argc, argv); Opm::ParameterGroup param; Opm::Parser parser; Opm::ParseContext parseContext; ///// // create a DerivedGeology object without any multipliers involved auto origDeck = parser.parseString(origDeckString , parseContext); Opm::EclipseState origEclipseState(origDeck , parseContext); auto origGrid = std::make_shared(); origGrid->processEclipseFormat(origEclipseState.getInputGrid(), 0.0, false); auto origProps = std::make_shared(origDeck, origEclipseState, *origGrid); Opm::DerivedGeology origGeology(*origGrid, *origProps, origEclipseState, false); ///// ///// // create a DerivedGeology object _with_ transmissibility multipliers involved auto multDeck = parser.parseString(multDeckString,parseContext); Opm::EclipseState multEclipseState(multDeck, parseContext); auto multGrid = std::make_shared(); multGrid->processEclipseFormat(multEclipseState.getInputGrid(), 0.0, false); auto multProps = std::make_shared(multDeck, multEclipseState, *multGrid); Opm::DerivedGeology multGeology(*multGrid, *multProps, multEclipseState, false); ///// ///// // create a DerivedGeology object _with_ transmissibility multipliers involved for // the negative faces const auto& multMinusDeck = parser.parseString(multMinusDeckString , parseContext); Opm::EclipseState multMinusEclipseState(multMinusDeck, parseContext); auto multMinusGrid = std::make_shared(); multMinusGrid->processEclipseFormat(multMinusEclipseState.getInputGrid(), 0.0, false); auto multMinusProps = std::make_shared(multMinusDeck, multMinusEclipseState, *multMinusGrid); Opm::DerivedGeology multMinusGeology(*multMinusGrid, *multMinusProps, multMinusEclipseState, false); ///// ///// // create a DerivedGeology object with the NTG keyword involved auto ntgDeck = parser.parseString(ntgDeckString, parseContext); Opm::EclipseState ntgEclipseState(ntgDeck, parseContext); auto ntgGrid = std::make_shared(); ntgGrid->processEclipseFormat(ntgEclipseState.getInputGrid(), 0.0, false); auto ntgProps = std::make_shared(ntgDeck, ntgEclipseState, *ntgGrid); Opm::DerivedGeology ntgGeology(*ntgGrid, *ntgProps, ntgEclipseState, false); ///// return checkTransmissibilityValues(*origGrid, origGeology, multGeology, multMinusGeology, ntgGeology); } #endif