From 86439d6a526574dd681857621c074c6bbde19106 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 14 Nov 2013 12:51:12 +0100 Subject: [PATCH 01/56] Added required dependencies on opm-parser and CJSON --- cmake/Modules/opm-core-prereqs.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index 0c68ec60..b46cf989 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -25,6 +25,9 @@ set (opm-core_DEPS "SuperLU" # xml processing (for config parsing) "TinyXML" + #Parser library + "opm-parser REQUIRED" + "cJSON REQUIRED" # Ensembles-based Reservoir Tools (ERT) "ERT" # DUNE dependency From ce8da887642c6dc0740203d7f1c490933cbd4e61 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 14 Nov 2013 12:52:12 +0100 Subject: [PATCH 02/56] Added very simple parser test to check a parser can be instantiated and a simple file parsed --- CMakeLists_files.cmake | 1 + tests/testBlackoilState1.DATA | 4 +++ tests/test_parser.cpp | 54 +++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 tests/test_parser.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 89c5da73..ef699bd5 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -162,6 +162,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_shadow.cpp tests/test_units.cpp tests/test_blackoilstate.cpp + tests/test_parser.cpp ) # originally generated with the command: diff --git a/tests/testBlackoilState1.DATA b/tests/testBlackoilState1.DATA index efcd29d2..c759447a 100644 --- a/tests/testBlackoilState1.DATA +++ b/tests/testBlackoilState1.DATA @@ -9,3 +9,7 @@ DYV DZV 10*10 / + +ACTNUM + 1 998*2 3 / + diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp new file mode 100644 index 00000000..dd2b6a1f --- /dev/null +++ b/tests/test_parser.cpp @@ -0,0 +1,54 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE OPM-ParserTest +#include + +#include +#include +#include + +#include +#include +#include +#include + +BOOST_AUTO_TEST_CASE(CreateParser) +{ + const std::string filename1 = "testBlackoilState1.DATA"; + Opm::ParserPtr parser(new Opm::Parser() ); + Opm::DeckConstPtr deck = parser->parse( filename1 ); + + BOOST_CHECK_EQUAL( 5U , deck->size() ); + Opm::DeckItemConstPtr actnum = deck->getKeyword("ACTNUM")->getRecord(0)->getItem(0); + const std::vector& actnum_data = actnum->getIntData(); + + BOOST_CHECK_EQUAL( 1000U , actnum->size() ); + BOOST_CHECK_EQUAL( 1, actnum_data[0] ); + BOOST_CHECK_EQUAL( 2, actnum_data[400] ); + BOOST_CHECK_EQUAL( 3, actnum_data[999] ); +} From 04d7d82e7c1b9e5bb698d381f6d834ec0eb60b3f Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 15 Nov 2013 15:38:03 +0100 Subject: [PATCH 03/56] Set the boost version requirement to 1.44 - and added cJSON dependency --- cmake/Modules/opm-parser-prereqs.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/opm-parser-prereqs.cmake b/cmake/Modules/opm-parser-prereqs.cmake index 80c95136..521568b7 100644 --- a/cmake/Modules/opm-parser-prereqs.cmake +++ b/cmake/Modules/opm-parser-prereqs.cmake @@ -13,6 +13,6 @@ set (opm-parser_DEPS # compile with C++0x/11 support if available "CXX11Features REQUIRED" # various runtime library enhancements - "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + "Boost 1.44.0 COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + "cJSON" ) From 1d39d4bcda2df4f177a77f42ad04ad9b49db0708 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 15 Nov 2013 15:39:55 +0100 Subject: [PATCH 04/56] Removed explicit cJSON dependency from opm-core --- cmake/Modules/opm-core-prereqs.cmake | 51 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index b46cf989..bb8cb5fd 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -3,34 +3,33 @@ # defines that must be present in config.h for our headers set (opm-core_CONFIG_VAR - HAVE_ERT - HAVE_SUITESPARSE_UMFPACK_H - ) + HAVE_ERT + HAVE_SUITESPARSE_UMFPACK_H + ) # dependencies set (opm-core_DEPS - # compile with C99 support if available - "C99" - # compile with C++0x/11 support if available - "CXX11Features REQUIRED" - # various runtime library enhancements - "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" - # matrix library - "BLAS REQUIRED" - "LAPACK REQUIRED" - # Tim Davis' SuiteSparse archive - "SuiteSparse COMPONENTS umfpack" - # solver - "SuperLU" - # xml processing (for config parsing) - "TinyXML" + # compile with C99 support if available + "C99" + # compile with C++0x/11 support if available + "CXX11Features REQUIRED" + # various runtime library enhancements + "Boost 1.39.0 + COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + # matrix library + "BLAS REQUIRED" + "LAPACK REQUIRED" + # Tim Davis' SuiteSparse archive + "SuiteSparse COMPONENTS umfpack" + # solver + "SuperLU" + # xml processing (for config parsing) + "TinyXML" #Parser library "opm-parser REQUIRED" - "cJSON REQUIRED" - # Ensembles-based Reservoir Tools (ERT) - "ERT" - # DUNE dependency - "dune-common" - "dune-istl" - ) + # Ensembles-based Reservoir Tools (ERT) + "ERT" + # DUNE dependency + "dune-common" + "dune-istl" + ) From 954203fb833a34d2a6250e85d232c12daf966b3e Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 15 Nov 2013 15:40:42 +0100 Subject: [PATCH 05/56] Added CJSON_INCLUDE_DIRS to OPM_PARSER_INCLUDE_DIRS --- cmake/Modules/Findopm-parser.cmake | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/cmake/Modules/Findopm-parser.cmake b/cmake/Modules/Findopm-parser.cmake index 3ae6cc0b..5f2701ed 100644 --- a/cmake/Modules/Findopm-parser.cmake +++ b/cmake/Modules/Findopm-parser.cmake @@ -5,9 +5,9 @@ # # If found, it sets these variables: # -# HAVE_OPM_PARSER Defined if a test program compiled -# OPM_PARSER_INCLUDE_DIRS Header file directories -# OPM_PARSER_LIBRARIES Archives and shared objects +# HAVE_OPM_PARSER Defined if a test program compiled +# OPM_PARSER_INCLUDE_DIRS Header file directories +# OPM_PARSER_LIBRARIES Archives and shared objects include (FindPackageHandleStandardArgs) @@ -61,18 +61,18 @@ find_path (OPM_PARSER_INCLUDE_DIR # then it is probably a build directory; read the CMake cache of # opm-parser to figure out where the source directory is if ((NOT OPM_PARSER_INCLUDE_DIR) AND - (OPM_PARSER_ROOT AND (EXISTS "${OPM_PARSER_ROOT}/CMakeCache.txt"))) + (OPM_PARSER_ROOT AND (EXISTS "${OPM_PARSER_ROOT}/CMakeCache.txt"))) set (_regex "^OPMParser_SOURCE_DIR:STATIC=\(.*\)$") file (STRINGS - "${OPM_PARSER_ROOT}/CMakeCache.txt" - _cache_entry - REGEX "${_regex}") + "${OPM_PARSER_ROOT}/CMakeCache.txt" + _cache_entry + REGEX "${_regex}") string(REGEX REPLACE "${_regex}" "\\1" - OPM_PARSER_INCLUDE_DIR - "${_cache_entry}") + OPM_PARSER_INCLUDE_DIR + "${_cache_entry}") if (OPM_PARSER_INCLUDE_DIR) - set (OPM_PARSER_INCLUDE_DIR "${OPM_PARSER_INCLUDE_DIR}" - CACHE PATH "Path to OPM parser header files" FORCE) + set (OPM_PARSER_INCLUDE_DIR "${OPM_PARSER_INCLUDE_DIR}" + CACHE PATH "Path to OPM parser header files" FORCE) endif () endif () @@ -109,14 +109,16 @@ endif () # get the prerequisite Boost libraries if (NOT Boost_FOUND) find_package(Boost 1.44.0 - COMPONENTS filesystem date_time system unit_test_framework REQUIRED ${OPM_PARSER_QUIET}) + COMPONENTS filesystem date_time system unit_test_framework REQUIRED ${OPM_PARSER_QUIET}) endif () # setup list of all required libraries to link with opm-parser. notice that # we use the plural form to get *all* the libraries needed by cjson set (OPM_PARSER_INCLUDE_DIRS ${OPM_PARSER_INCLUDE_DIR} + ${CJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + set (OPM_PARSER_LIBRARIES ${OPM_PARSER_LIBRARY} ${OPM_JSON_LIBRARY} @@ -126,7 +128,7 @@ set (OPM_PARSER_LIBRARIES # see if we can compile a minimum example # CMake logical test doesn't handle lists (sic) if (NOT (OPM_PARSER_INCLUDE_DIR MATCHES "-NOTFOUND" - OR OPM_PARSER_LIBRARIES MATCHES "-NOTFOUND")) + OR OPM_PARSER_LIBRARIES MATCHES "-NOTFOUND")) include (CMakePushCheckState) include (CheckCSourceCompiles) cmake_push_check_state () From 70505ffcb378307c3bdc14c9ee833bc0cadfea94 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Mon, 18 Nov 2013 13:24:53 +0100 Subject: [PATCH 06/56] Raised the opm-core boost dependency to 1.44 to reflect parser requirement --- cmake/Modules/opm-core-prereqs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index bb8cb5fd..41419b15 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -14,7 +14,7 @@ set (opm-core_DEPS # compile with C++0x/11 support if available "CXX11Features REQUIRED" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system unit_test_framework REQUIRED" # matrix library "BLAS REQUIRED" From b11a667de0c77887199b1e7913b93fd6143a17f5 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 6 Dec 2013 17:11:52 +0100 Subject: [PATCH 07/56] dune.module: specify dependency on opm-parser this is tested in cmake, but dunecontrol was unware of it up to now. this depends on OPM/opm-parser#67 --- dune.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dune.module b/dune.module index 02dc99c3..b3b7ad9c 100644 --- a/dune.module +++ b/dune.module @@ -4,4 +4,4 @@ Description: Open Porous Media Initiative Core Library Version: 1.1 Label: 2013.10 Maintainer: atgeirr@sintef.no -Depends: dune-common (>= 2.2) dune-istl (>= 2.2) +Depends: dune-common (>= 2.2) dune-istl (>= 2.2) opm-parser From 43fd5e23ab752cf28570f8ea57212f144c36f9d8 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 7 Jan 2014 16:10:45 +0100 Subject: [PATCH 08/56] New constructor for WellsManager that also takes the new parser as argument. New test comparing old and new --- opm/core/wells/WellsManager.cpp | 594 ++++++++++++++++++++++++++++++++ opm/core/wells/WellsManager.hpp | 11 +- tests/test_wellsmanager.cpp | 37 ++ 3 files changed, 641 insertions(+), 1 deletion(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 007c8b52..b934c447 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -18,6 +18,8 @@ */ #include "config.h" + + #include #include #include @@ -234,6 +236,598 @@ namespace Opm } + /// Construct wells from deck. + WellsManager::WellsManager(const Opm::SchedulePtr schedule, + const size_t timeStep, + const Opm::EclipseGridParser& deck, + const UnstructuredGrid& grid, + const double* permeability) + : w_(0) + { + if (grid.dimensions != 3) { + OPM_THROW(std::runtime_error, "We cannot initialize wells from a deck unless the corresponding grid is 3-dimensional."); + } + // NOTE: Implementation copied and modified from dune-porsol's class BlackoilWells. + std::vector keywords; + keywords.push_back("WELSPECS"); + keywords.push_back("COMPDAT"); +// keywords.push_back("WELTARG"); + if (!deck.hasFields(keywords)) { + OPM_MESSAGE("Missing well keywords in deck, initializing no wells."); + return; + } + if (!(deck.hasField("WCONINJE") || deck.hasField("WCONPROD")) ) { + OPM_THROW(std::runtime_error, "Needed field is missing in file"); + } + + // Obtain phase usage data. + PhaseUsage pu = phaseUsageFromDeck(deck); + + // These data structures will be filled in this constructor, + // then used to initialize the Wells struct. + std::vector well_names; + std::vector well_data; + std::vector > wellperf_data; + + // For easy lookup: + std::map well_names_to_index; + typedef std::map::const_iterator WNameIt; + + // Get WELSPECS data. + // It is allowed to have multiple lines corresponding to + // the same well, in which case the last one encountered + // is the one used. + const WELSPECS& welspecs = deck.getWELSPECS(); + const int num_welspecs = welspecs.welspecs.size(); + well_names.reserve(num_welspecs); + well_data.reserve(num_welspecs); + for (int w = 0; w < num_welspecs; ++w) { + // First check if this well has already been encountered. + // If so, we modify it's data instead of appending a new well + // to the well_data and well_names vectors. + const std::string& name = welspecs.welspecs[w].name_; + const double refdepth = welspecs.welspecs[w].datum_depth_BHP_; + WNameIt wit = well_names_to_index.find(name); + if (wit == well_names_to_index.end()) { + // New well, append data. + well_names_to_index[welspecs.welspecs[w].name_] = well_data.size(); + well_names.push_back(name); + WellData wd; + // If negative (defaulted), set refdepth to a marker + // value, will be changed after getting perforation + // data to the centroid of the cell of the top well + // perforation. + wd.reference_bhp_depth = (refdepth < 0.0) ? -1e100 : refdepth; + wd.welspecsline = w; + well_data.push_back(wd); + } else { + // Existing well, change data. + const int wix = wit->second; + well_data[wix].reference_bhp_depth = (refdepth < 0.0) ? -1e100 : refdepth; + well_data[wix].welspecsline = w; + } + } + const int num_wells = well_data.size(); + wellperf_data.resize(num_wells); + + + // global_cell is a map from compressed cells to Cartesian grid cells. + // We must make the inverse lookup. + const int* global_cell = grid.global_cell; + const int* cpgdim = grid.cartdims; + std::map cartesian_to_compressed; + + if (global_cell) { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(global_cell[i], i)); + } + } + else { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(i, i)); + } + } + + // Get COMPDAT data + // It is *not* allowed to have multiple lines corresponding to + // the same perforation! + const COMPDAT& compdat = deck.getCOMPDAT(); + const int num_compdat = compdat.compdat.size(); + for (int kw = 0; kw < num_compdat; ++kw) { + // Extract well name, or the part of the well name that + // comes before the '*'. + std::string name = compdat.compdat[kw].well_; + std::string::size_type len = name.find('*'); + if (len != std::string::npos) { + name = name.substr(0, len); + } + // Look for well with matching name. + bool found = false; + for (int wix = 0; wix < num_wells; ++wix) { + if (well_names[wix].compare(0,len, name) == 0) { // equal + // Extract corresponding WELSPECS defintion for + // purpose of default location specification. + const WelspecsLine& wspec = welspecs.welspecs[well_data[wix].welspecsline]; + + // We have a matching name. + int ix = compdat.compdat[kw].grid_ind_[0] - 1; + int jy = compdat.compdat[kw].grid_ind_[1] - 1; + int kz1 = compdat.compdat[kw].grid_ind_[2] - 1; + int kz2 = compdat.compdat[kw].grid_ind_[3] - 1; + + if (ix < 0) { + // Defaulted I location. Extract from WELSPECS. + ix = wspec.I_ - 1; + } + if (jy < 0) { + // Defaulted J location. Extract from WELSPECS. + jy = wspec.J_ - 1; + } + if (kz1 < 0) { + // Defaulted KZ1. Use top layer. + kz1 = 0; + } + if (kz2 < 0) { + // Defaulted KZ2. Use bottom layer. + kz2 = cpgdim[2] - 1; + } + + for (int kz = kz1; kz <= kz2; ++kz) { + int cart_grid_indx = ix + cpgdim[0]*(jy + cpgdim[1]*kz); + std::map::const_iterator cgit = + cartesian_to_compressed.find(cart_grid_indx); + if (cgit == cartesian_to_compressed.end()) { + OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << ix << ' ' << jy << ' ' + << kz << " not found in grid (well = " << name << ')'); + } + int cell = cgit->second; + PerfData pd; + pd.cell = cell; + if (compdat.compdat[kw].connect_trans_fac_ > 0.0) { + pd.well_index = compdat.compdat[kw].connect_trans_fac_; + } else { + double radius = 0.5*compdat.compdat[kw].diameter_; + if (radius <= 0.0) { + radius = 0.5*unit::feet; + OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); + } + std::array cubical = getCubeDim(grid, cell); + const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; + pd.well_index = computeWellIndex(radius, cubical, cell_perm, + compdat.compdat[kw].skin_factor_); + } + wellperf_data[wix].push_back(pd); + } + found = true; + break; + } + } + if (!found) { + OPM_THROW(std::runtime_error, "Undefined well name: " << compdat.compdat[kw].well_ + << " in COMPDAT"); + } + } + + // Set up reference depths that were defaulted. Count perfs. + int num_perfs = 0; + assert(grid.dimensions == 3); + for (int w = 0; w < num_wells; ++w) { + num_perfs += wellperf_data[w].size(); + if (well_data[w].reference_bhp_depth < 0.0) { + // It was defaulted. Set reference depth to minimum perforation depth. + double min_depth = 1e100; + int num_wperfs = wellperf_data[w].size(); + for (int perf = 0; perf < num_wperfs; ++perf) { + double depth = grid.cell_centroids[3*wellperf_data[w][perf].cell + 2]; + min_depth = std::min(min_depth, depth); + } + well_data[w].reference_bhp_depth = min_depth; + } + } + + // Create the well data structures. + w_ = create_wells(pu.num_phases, num_wells, num_perfs); + if (!w_) { + OPM_THROW(std::runtime_error, "Failed creating Wells struct."); + } + + // Classify wells + if (deck.hasField("WCONINJE")) { + const std::vector& lines = deck.getWCONINJE().wconinje; + for (size_t i = 0 ; i < lines.size(); ++i) { + const std::map::const_iterator it = well_names_to_index.find(lines[i].well_); + if (it != well_names_to_index.end()) { + const int well_index = it->second; + well_data[well_index].type = INJECTOR; + } else { + OPM_THROW(std::runtime_error, "Unseen well name: " << lines[i].well_ << " first seen in WCONINJE"); + } + } + } + if (deck.hasField("WCONPROD")) { + const std::vector& lines = deck.getWCONPROD().wconprod; + for (size_t i = 0; i < lines.size(); ++i) { + const std::map::const_iterator it = well_names_to_index.find(lines[i].well_); + if (it != well_names_to_index.end()) { + const int well_index = it->second; + well_data[well_index].type = PRODUCER; + } else { + OPM_THROW(std::runtime_error, "Unseen well name: " << lines[i].well_ << " first seen in WCONPROD"); + } + + } + } + + // Add wells. + for (int w = 0; w < num_wells; ++w) { + const int w_num_perf = wellperf_data[w].size(); + std::vector perf_cells(w_num_perf); + std::vector perf_prodind(w_num_perf); + for (int perf = 0; perf < w_num_perf; ++perf) { + perf_cells[perf] = wellperf_data[w][perf].cell; + perf_prodind[perf] = wellperf_data[w][perf].well_index; + } + const double* comp_frac = NULL; + // We initialize all wells with a null component fraction, + // and must (for injection wells) overwrite it later. + int ok = add_well(well_data[w].type, well_data[w].reference_bhp_depth, w_num_perf, + comp_frac, &perf_cells[0], &perf_prodind[0], well_names[w].c_str(), w_); + if (!ok) { + OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); + } + } + + // Get WCONINJE data, add injection controls to wells. + // It is allowed to have multiple lines corresponding to + // the same well, in which case the last one encountered + // is the one used. + if (deck.hasField("WCONINJE")) { + const WCONINJE& wconinjes = deck.getWCONINJE(); + const int num_wconinjes = wconinjes.wconinje.size(); + for (int kw = 0; kw < num_wconinjes; ++kw) { + const WconinjeLine& wci_line = wconinjes.wconinje[kw]; + // Extract well name, or the part of the well name that + // comes before the '*'. + std::string name = wci_line.well_; + std::string::size_type len = name.find('*'); + if (len != std::string::npos) { + name = name.substr(0, len); + } + bool well_found = false; + for (int wix = 0; wix < num_wells; ++wix) { + if (well_names[wix].compare(0,len, name) == 0) { //equal + well_found = true; + assert(well_data[wix].type == w_->type[wix]); + if (well_data[wix].type != INJECTOR) { + OPM_THROW(std::runtime_error, "Found WCONINJE entry for a non-injector well: " << well_names[wix]); + } + + // Add all controls that are present in well. + // First we must clear existing controls, in case the + // current WCONINJE line is modifying earlier controls. + clear_well_controls(wix, w_); + int ok = 1; + int control_pos[5] = { -1, -1, -1, -1, -1 }; + if (ok && wci_line.surface_flow_max_rate_ >= 0.0) { + control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + if (wci_line.injector_type_ == "WATER") { + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (wci_line.injector_type_ == "OIL") { + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (wci_line.injector_type_ == "GAS") { + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } else { + OPM_THROW(std::runtime_error, "Injector type " << wci_line.injector_type_ << " not supported." + "WellsManager only supports WATER, OIL and GAS injector types."); + } + ok = append_well_controls(SURFACE_RATE, wci_line.surface_flow_max_rate_, + distr, wix, w_); + } + if (ok && wci_line.reservoir_flow_max_rate_ >= 0.0) { + control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + if (wci_line.injector_type_ == "WATER") { + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (wci_line.injector_type_ == "OIL") { + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (wci_line.injector_type_ == "GAS") { + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } else { + OPM_THROW(std::runtime_error, "Injector type " << wci_line.injector_type_ << " not supported." + "WellsManager only supports WATER, OIL and GAS injector types."); + } + ok = append_well_controls(RESERVOIR_RATE, wci_line.reservoir_flow_max_rate_, + distr, wix, w_); + } + if (ok && wci_line.BHP_limit_ > 0.0) { + control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[wix]); + ok = append_well_controls(BHP, wci_line.BHP_limit_, + NULL, wix, w_); + } + if (ok && wci_line.THP_limit_ > 0.0) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[wix]); + } + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[wix]); + } + InjectionControl::Mode mode = InjectionControl::mode(wci_line.control_mode_); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != InjectionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control for " << wci_line.control_mode_ << " not specified in well " << well_names[wix]); + } + // We need to check if the well is shut or not + if (wci_line.open_shut_flag_ == "SHUT") { + cpos = ~cpos; + } + set_current_control(wix, cpos, w_); + + // Set well component fraction. + double cf[3] = { 0.0, 0.0, 0.0 }; + if (wci_line.injector_type_[0] == 'W') { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (wci_line.injector_type_[0] == 'O') { + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (wci_line.injector_type_[0] == 'G') { + if (!pu.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + std::copy(cf, cf + pu.num_phases, w_->comp_frac + wix*pu.num_phases); + } + } + if (!well_found) { + OPM_THROW(std::runtime_error, "Undefined well name: " << wci_line.well_ + << " in WCONINJE"); + } + } + } + + // Get WCONPROD data + // It is allowed to have multiple lines corresponding to + // the same well, in which case the last one encountered + // is the one used. + if (deck.hasField("WCONPROD")) { + const WCONPROD& wconprods = deck.getWCONPROD(); + const int num_wconprods = wconprods.wconprod.size(); + for (int kw = 0; kw < num_wconprods; ++kw) { + const WconprodLine& wcp_line = wconprods.wconprod[kw]; + std::string name = wcp_line.well_; + std::string::size_type len = name.find('*'); + if (len != std::string::npos) { + name = name.substr(0, len); + } + bool well_found = false; + for (int wix = 0; wix < num_wells; ++wix) { + if (well_names[wix].compare(0,len, name) == 0) { //equal + well_found = true; + assert(well_data[wix].type == w_->type[wix]); + if (well_data[wix].type != PRODUCER) { + OPM_THROW(std::runtime_error, "Found WCONPROD entry for a non-producer well: " << well_names[wix]); + } + // Add all controls that are present in well. + // First we must clear existing controls, in case the + // current WCONPROD line is modifying earlier controls. + clear_well_controls(wix, w_); + int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + int ok = 1; + if (ok && wcp_line.oil_max_rate_ >= 0.0) { + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); + } + control_pos[ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, -wcp_line.oil_max_rate_, + distr, wix, w_); + } + if (ok && wcp_line.water_max_rate_ >= 0.0) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); + } + control_pos[ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + ok = append_well_controls(SURFACE_RATE, -wcp_line.water_max_rate_, + distr, wix, w_); + } + if (ok && wcp_line.gas_max_rate_ >= 0.0) { + if (!pu.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); + } + control_pos[ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + ok = append_well_controls(SURFACE_RATE, -wcp_line.gas_max_rate_, + distr, wix, w_); + } + if (ok && wcp_line.liquid_max_rate_ >= 0.0) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); + } + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified."); + } + control_pos[ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, -wcp_line.liquid_max_rate_, + distr, wix, w_); + } + if (ok && wcp_line.reservoir_flow_max_rate_ >= 0.0) { + control_pos[ProductionControl::RESV] = well_controls_get_num(w_->ctrls[wix]); + double distr[3] = { 1.0, 1.0, 1.0 }; + ok = append_well_controls(RESERVOIR_RATE, -wcp_line.reservoir_flow_max_rate_, + distr, wix, w_); + } + if (ok && wcp_line.BHP_limit_ > 0.0) { + control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[wix]); + ok = append_well_controls(BHP, wcp_line.BHP_limit_, + NULL, wix, w_); + } + if (ok && wcp_line.THP_limit_ > 0.0) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[wix]); + } + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[wix]); + } + ProductionControl::Mode mode = ProductionControl::mode(wcp_line.control_mode_); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != ProductionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[wix]); + } + // If it's shut, we complement the cpos + if (wcp_line.open_shut_flag_ == "SHUT") { + cpos = ~cpos; // So we can easily retrieve the cpos later + } + set_current_control(wix, cpos, w_); + } + } + if (!well_found) { + OPM_THROW(std::runtime_error, "Undefined well name: " << wcp_line.well_ + << " in WCONPROD"); + } + } + } + + // Get WELTARG data + if (deck.hasField("WELTARG")) { + OPM_THROW(std::runtime_error, "We currently do not handle WELTARG."); + /* + const WELTARG& weltargs = deck.getWELTARG(); + const int num_weltargs = weltargs.weltarg.size(); + for (int kw = 0; kw < num_weltargs; ++kw) { + std::string name = weltargs.weltarg[kw].well_; + std::string::size_type len = name.find('*'); + if (len != std::string::npos) { + name = name.substr(0, len); + } + bool well_found = false; + for (int wix = 0; wix < num_wells; ++wix) { + if (well_names[wix].compare(0,len, name) == 0) { //equal + well_found = true; + well_data[wix].target = weltargs.weltarg[kw].new_value_; + break; + } + } + if (!well_found) { + OPM_THROW(std::runtime_error, "Undefined well name: " << weltargs.weltarg[kw].well_ + << " in WELTARG"); + } + } + */ + } + + // Debug output. +#define EXTRA_OUTPUT +#ifdef EXTRA_OUTPUT + /* + std::cout << "\t WELL DATA" << std::endl; + for(int i = 0; i< num_wells; ++i) { + std::cout << i << ": " << well_data[i].type << " " + << well_data[i].control << " " << well_data[i].target + << std::endl; + } + + std::cout << "\n\t PERF DATA" << std::endl; + for(int i=0; i< int(wellperf_data.size()); ++i) { + for(int j=0; j< int(wellperf_data[i].size()); ++j) { + std::cout << i << ": " << wellperf_data[i][j].cell << " " + << wellperf_data[i][j].well_index << std::endl; + } + } + */ +#endif + + if (deck.hasField("WELOPEN")) { + const WELOPEN& welopen = deck.getWELOPEN(); + for (size_t i = 0; i < welopen.welopen.size(); ++i) { + WelopenLine line = welopen.welopen[i]; + std::string wellname = line.well_; + std::map::const_iterator it = well_names_to_index.find(wellname); + if (it == well_names_to_index.end()) { + OPM_THROW(std::runtime_error, "Trying to open/shut well with name: \"" << wellname<<"\" but it's not registered under WELSPECS."); + } + const int index = it->second; + if (line.openshutflag_ == "SHUT") { + int cur_ctrl = well_controls_get_current(w_->ctrls[index]); + if (cur_ctrl >= 0) { + well_controls_invert_current(w_->ctrls[index]); + } + assert(well_controls_get_current(w_->ctrls[index]) < 0); + } else if (line.openshutflag_ == "OPEN") { + int cur_ctrl = well_controls_get_current(w_->ctrls[index]); + if (cur_ctrl < 0) { + well_controls_invert_current(w_->ctrls[index]); + } + assert(well_controls_get_current(w_->ctrls[index]) >= 0); + } else { + OPM_THROW(std::runtime_error, "Unknown Open/close keyword: \"" << line.openshutflag_<< "\". Allowed values: OPEN, SHUT."); + } + } + } + + // Build the well_collection_ well group hierarchy. + if (deck.hasField("GRUPTREE")) { + std::cout << "Found gruptree" << std::endl; + const GRUPTREE& gruptree = deck.getGRUPTREE(); + std::map::const_iterator it = gruptree.tree.begin(); + for( ; it != gruptree.tree.end(); ++it) { + well_collection_.addChild(it->first, it->second, deck); + } + } + for (size_t i = 0; i < welspecs.welspecs.size(); ++i) { + WelspecsLine line = welspecs.welspecs[i]; + well_collection_.addChild(line.name_, line.group_, deck); + } + + + + // Set the guide rates: + if (deck.hasField("WGRUPCON")) { + std::cout << "Found Wgrupcon" << std::endl; + WGRUPCON wgrupcon = deck.getWGRUPCON(); + const std::vector& lines = wgrupcon.wgrupcon; + std::cout << well_collection_.getLeafNodes().size() << std::endl; + for (size_t i = 0; i < lines.size(); i++) { + std::string name = lines[i].well_; + const int wix = well_names_to_index[name]; + WellNode& wellnode = *well_collection_.getLeafNodes()[wix]; + assert(wellnode.name() == name); + if (well_data[wix].type == PRODUCER) { + wellnode.prodSpec().guide_rate_ = lines[i].guide_rate_; + if (lines[i].phase_ == "OIL") { + wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL; + } else { + OPM_THROW(std::runtime_error, "Guide rate type " << lines[i].phase_ << " specified for producer " + << name << " in WGRUPCON, cannot handle."); + } + } else if (well_data[wix].type == INJECTOR) { + wellnode.injSpec().guide_rate_ = lines[i].guide_rate_; + if (lines[i].phase_ == "RAT") { + wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT; + } else { + OPM_THROW(std::runtime_error, "Guide rate type " << lines[i].phase_ << " specified for injector " + << name << " in WGRUPCON, cannot handle."); + } + } else { + OPM_THROW(std::runtime_error, "Unknown well type " << well_data[wix].type << " for well " << name); + } + } + } + well_collection_.setWellsPointer(w_); + well_collection_.applyGroupControls(); + } + + /// Construct wells from deck. WellsManager::WellsManager(const Opm::EclipseGridParser& deck, diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 35f7f471..840e6f36 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -21,6 +21,8 @@ #define OPM_WELLSMANAGER_HEADER_INCLUDED +#include + #include #include @@ -56,7 +58,14 @@ namespace Opm /// order to approximate these by the Peaceman formula. WellsManager(const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, - const double* permeability); + const double* permeability); + + + WellsManager(const Opm::SchedulePtr schedule, + const size_t timeStep, + const Opm::EclipseGridParser& deck, + const UnstructuredGrid& grid, + const double* permeability); /// Destructor. ~WellsManager(); diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index b3465fac..347f1d2b 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -27,6 +27,10 @@ #define BOOST_TEST_MODULE WellsManagerTests #include + +#include +#include + #include #include #include @@ -195,6 +199,39 @@ BOOST_AUTO_TEST_CASE(Constructor_Works) { } } +BOOST_AUTO_TEST_CASE(New_Constructor_Works) { + + Opm::ParserPtr parser(new Opm::Parser()); + Opm::SchedulePtr schedule(new Opm::Schedule(parser->parse("wells_manager_data.data"))); + + Opm::EclipseGridParser Deck("wells_manager_data.data"); + Opm::GridManager gridManager(Deck); + + Deck.setCurrentEpoch(0); + { + Opm::WellsManager wellsManager(schedule, 0, Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); + + const Wells* wells = wellsManager.c_wells(); + wells_static_check( wells ); + check_controls_epoch0( wells->ctrls ); + + BOOST_CHECK(wells_equal(wells, oldWellsManager.c_wells())); + } + + Deck.setCurrentEpoch(1); + { + Opm::WellsManager wellsManager(schedule, 1,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); + + const Wells* wells = wellsManager.c_wells(); + wells_static_check( wells ); + check_controls_epoch1( wells->ctrls ); + + BOOST_CHECK(wells_equal(wells, oldWellsManager.c_wells())); + } +} + BOOST_AUTO_TEST_CASE(WellsEqual) { From bc8ad56db1666a912cd814fe06bc324e8e3e34e9 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 8 Jan 2014 16:10:55 +0100 Subject: [PATCH 09/56] Changed new WellsManager constructor to take in EclipseStateConstPtr instead of SchedulePtr --- opm/core/wells/WellsManager.cpp | 2 +- opm/core/wells/WellsManager.hpp | 4 ++-- tests/test_wellsmanager.cpp | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index b934c447..86a7d78d 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -237,7 +237,7 @@ namespace Opm /// Construct wells from deck. - WellsManager::WellsManager(const Opm::SchedulePtr schedule, + WellsManager::WellsManager(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 840e6f36..f14be794 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -21,7 +21,7 @@ #define OPM_WELLSMANAGER_HEADER_INCLUDED -#include +#include #include #include @@ -61,7 +61,7 @@ namespace Opm const double* permeability); - WellsManager(const Opm::SchedulePtr schedule, + WellsManager(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 347f1d2b..96a193cc 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -202,14 +202,14 @@ BOOST_AUTO_TEST_CASE(Constructor_Works) { BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::ParserPtr parser(new Opm::Parser()); - Opm::SchedulePtr schedule(new Opm::Schedule(parser->parse("wells_manager_data.data"))); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parse("wells_manager_data.data"))); Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); Deck.setCurrentEpoch(0); { - Opm::WellsManager wellsManager(schedule, 0, Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); const Wells* wells = wellsManager.c_wells(); @@ -221,7 +221,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Deck.setCurrentEpoch(1); { - Opm::WellsManager wellsManager(schedule, 1,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 1,Deck, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); const Wells* wells = wellsManager.c_wells(); From 2a35d3057cc4ddbbf5ddbe01342586f9fc320e30 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 10 Jan 2014 13:43:02 +0100 Subject: [PATCH 10/56] Changed phaseUsageFromDeck to use eclipseState --- opm/core/props/phaseUsageFromDeck.hpp | 39 +++++++++++++++++++++++++++ opm/core/wells/WellsManager.cpp | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/opm/core/props/phaseUsageFromDeck.hpp b/opm/core/props/phaseUsageFromDeck.hpp index 01f28ee1..e69d6ee2 100644 --- a/opm/core/props/phaseUsageFromDeck.hpp +++ b/opm/core/props/phaseUsageFromDeck.hpp @@ -24,11 +24,50 @@ #include #include +#include + namespace Opm { + /// Looks at presence of WATER, OIL and GAS keywords in state object + /// to determine active phases. + inline PhaseUsage phaseUsageFromDeck(Opm::EclipseStateConstPtr eclipseState) + { + PhaseUsage pu; + std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); + + // Discover phase usage. + if (eclipseState->hasPhase(PhaseEnum::WATER)) { + pu.phase_used[BlackoilPhases::Aqua] = 1; + } + if (eclipseState->hasPhase(PhaseEnum::OIL)) { + pu.phase_used[BlackoilPhases::Liquid] = 1; + } + if (eclipseState->hasPhase(PhaseEnum::GAS)) { + pu.phase_used[BlackoilPhases::Vapour] = 1; + } + pu.num_phases = 0; + for (int i = 0; i < BlackoilPhases::MaxNumPhases; ++i) { + pu.phase_pos[i] = pu.num_phases; + pu.num_phases += pu.phase_used[i]; + } + + // Only 2 or 3 phase systems handled. + if (pu.num_phases < 2 || pu.num_phases > 3) { + OPM_THROW(std::runtime_error, "Cannot handle cases with " << pu.num_phases << " phases."); + } + + // We need oil systems, since we do not support the keywords needed for + // water-gas systems. + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Cannot handle cases with no OIL, i.e. water-gas systems."); + } + + return pu; + } + /// Looks at presence of WATER, OIL and GAS keywords in deck /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(const EclipseGridParser& deck) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 86a7d78d..46a47646 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -261,7 +261,7 @@ namespace Opm } // Obtain phase usage data. - PhaseUsage pu = phaseUsageFromDeck(deck); + PhaseUsage pu = phaseUsageFromDeck(eclipseState); // These data structures will be filled in this constructor, // then used to initialize the Wells struct. From 2e64991de8193d3270bf0883b99d8332a6953d6e Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Sat, 11 Jan 2014 08:03:42 +0100 Subject: [PATCH 11/56] Renamed Parser->parse() -> Parser->parseFile() to follow opm-parser change. --- tests/test_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_parser.cpp b/tests/test_parser.cpp index dd2b6a1f..002e751b 100644 --- a/tests/test_parser.cpp +++ b/tests/test_parser.cpp @@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(CreateParser) { const std::string filename1 = "testBlackoilState1.DATA"; Opm::ParserPtr parser(new Opm::Parser() ); - Opm::DeckConstPtr deck = parser->parse( filename1 ); + Opm::DeckConstPtr deck = parser->parseFile( filename1 ); BOOST_CHECK_EQUAL( 5U , deck->size() ); Opm::DeckItemConstPtr actnum = deck->getKeyword("ACTNUM")->getRecord(0)->getItem(0); From 6d970ef189146e053f9df74a34c108a670c9d962 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 22 Jan 2014 15:22:32 +0100 Subject: [PATCH 12/56] Removes the WELSPECS usage in WellsManager constructor. Step 1 in rewrite of constructor --- opm/core/wells/WellsManager.cpp | 72 ++++++++++++--------------------- tests/test_wellsmanager.cpp | 2 +- 2 files changed, 27 insertions(+), 47 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 46a47646..81a3f1c6 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -247,18 +247,11 @@ namespace Opm if (grid.dimensions != 3) { OPM_THROW(std::runtime_error, "We cannot initialize wells from a deck unless the corresponding grid is 3-dimensional."); } - // NOTE: Implementation copied and modified from dune-porsol's class BlackoilWells. - std::vector keywords; - keywords.push_back("WELSPECS"); - keywords.push_back("COMPDAT"); -// keywords.push_back("WELTARG"); - if (!deck.hasFields(keywords)) { - OPM_MESSAGE("Missing well keywords in deck, initializing no wells."); + + if (eclipseState->getSchedule()->numWells() == 0) { + OPM_MESSAGE("No wells specified in Schedule section, initializing no wells"); return; } - if (!(deck.hasField("WCONINJE") || deck.hasField("WCONPROD")) ) { - OPM_THROW(std::runtime_error, "Needed field is missing in file"); - } // Obtain phase usage data. PhaseUsage pu = phaseUsageFromDeck(eclipseState); @@ -273,44 +266,35 @@ namespace Opm std::map well_names_to_index; typedef std::map::const_iterator WNameIt; - // Get WELSPECS data. - // It is allowed to have multiple lines corresponding to - // the same well, in which case the last one encountered - // is the one used. - const WELSPECS& welspecs = deck.getWELSPECS(); - const int num_welspecs = welspecs.welspecs.size(); - well_names.reserve(num_welspecs); - well_data.reserve(num_welspecs); - for (int w = 0; w < num_welspecs; ++w) { - // First check if this well has already been encountered. - // If so, we modify it's data instead of appending a new well - // to the well_data and well_names vectors. - const std::string& name = welspecs.welspecs[w].name_; - const double refdepth = welspecs.welspecs[w].datum_depth_BHP_; - WNameIt wit = well_names_to_index.find(name); - if (wit == well_names_to_index.end()) { - // New well, append data. - well_names_to_index[welspecs.welspecs[w].name_] = well_data.size(); - well_names.push_back(name); + + + // Main "well-loop" to populate the data structures (well_names, well_data, ...) + ScheduleConstPtr schedule = eclipseState->getSchedule(); + std::vector wells = schedule->getWells(timeStep); + well_names.reserve(wells.size()); + well_data.reserve(wells.size()); + + int well_index = 0; + for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { + WellConstPtr well = (*wellIter); + well_names_to_index[well->name()] = well_index; + well_names.push_back(well->name()); + { WellData wd; // If negative (defaulted), set refdepth to a marker // value, will be changed after getting perforation // data to the centroid of the cell of the top well // perforation. - wd.reference_bhp_depth = (refdepth < 0.0) ? -1e100 : refdepth; - wd.welspecsline = w; + wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); + wd.welspecsline = -1; well_data.push_back(wd); - } else { - // Existing well, change data. - const int wix = wit->second; - well_data[wix].reference_bhp_depth = (refdepth < 0.0) ? -1e100 : refdepth; - well_data[wix].welspecsline = w; } + well_index++; } + const int num_wells = well_data.size(); wellperf_data.resize(num_wells); - // global_cell is a map from compressed cells to Cartesian grid cells. // We must make the inverse lookup. const int* global_cell = grid.global_cell; @@ -345,23 +329,20 @@ namespace Opm bool found = false; for (int wix = 0; wix < num_wells; ++wix) { if (well_names[wix].compare(0,len, name) == 0) { // equal - // Extract corresponding WELSPECS defintion for - // purpose of default location specification. - const WelspecsLine& wspec = welspecs.welspecs[well_data[wix].welspecsline]; - // We have a matching name. int ix = compdat.compdat[kw].grid_ind_[0] - 1; int jy = compdat.compdat[kw].grid_ind_[1] - 1; int kz1 = compdat.compdat[kw].grid_ind_[2] - 1; int kz2 = compdat.compdat[kw].grid_ind_[3] - 1; + WellConstPtr well = schedule->getWell(well_names[wix]); if (ix < 0) { // Defaulted I location. Extract from WELSPECS. - ix = wspec.I_ - 1; + ix = well->getHeadI() - 1; } if (jy < 0) { // Defaulted J location. Extract from WELSPECS. - jy = wspec.J_ - 1; + jy = well->getHeadJ() - 1; } if (kz1 < 0) { // Defaulted KZ1. Use top layer. @@ -784,9 +765,8 @@ namespace Opm well_collection_.addChild(it->first, it->second, deck); } } - for (size_t i = 0; i < welspecs.welspecs.size(); ++i) { - WelspecsLine line = welspecs.welspecs[i]; - well_collection_.addChild(line.name_, line.group_, deck); + for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) { + well_collection_.addChild((*wellIter)->name(), (*wellIter)->getGroupName(timeStep), deck); } diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 96a193cc..3bf36eae 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -202,7 +202,7 @@ BOOST_AUTO_TEST_CASE(Constructor_Works) { BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::ParserPtr parser(new Opm::Parser()); - Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parse("wells_manager_data.data"))); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile("wells_manager_data.data"))); Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); From 1148e6b86e597965c781d45be5be7fde0955ba12 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 24 Jan 2014 12:00:58 +0100 Subject: [PATCH 13/56] WellType is determined from well->isProducer() instead of looking for WCONINJE / WCONPROD keywords. --- opm/core/wells/WellsManager.cpp | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 81a3f1c6..a92b924e 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -287,6 +287,12 @@ namespace Opm // perforation. wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); wd.welspecsline = -1; + + if (well->isProducer(timeStep)) + wd.type = PRODUCER; + else + wd.type = INJECTOR; + well_data.push_back(wd); } well_index++; @@ -412,32 +418,6 @@ namespace Opm OPM_THROW(std::runtime_error, "Failed creating Wells struct."); } - // Classify wells - if (deck.hasField("WCONINJE")) { - const std::vector& lines = deck.getWCONINJE().wconinje; - for (size_t i = 0 ; i < lines.size(); ++i) { - const std::map::const_iterator it = well_names_to_index.find(lines[i].well_); - if (it != well_names_to_index.end()) { - const int well_index = it->second; - well_data[well_index].type = INJECTOR; - } else { - OPM_THROW(std::runtime_error, "Unseen well name: " << lines[i].well_ << " first seen in WCONINJE"); - } - } - } - if (deck.hasField("WCONPROD")) { - const std::vector& lines = deck.getWCONPROD().wconprod; - for (size_t i = 0; i < lines.size(); ++i) { - const std::map::const_iterator it = well_names_to_index.find(lines[i].well_); - if (it != well_names_to_index.end()) { - const int well_index = it->second; - well_data[well_index].type = PRODUCER; - } else { - OPM_THROW(std::runtime_error, "Unseen well name: " << lines[i].well_ << " first seen in WCONPROD"); - } - - } - } // Add wells. for (int w = 0; w < num_wells; ++w) { From 07feb6509d06a19594e8c731a423b9bbaf4006c6 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 28 Jan 2014 21:11:51 +0100 Subject: [PATCH 14/56] Updated Namespace of PhaseEnum from opm-parser --- opm/core/props/phaseUsageFromDeck.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/props/phaseUsageFromDeck.hpp b/opm/core/props/phaseUsageFromDeck.hpp index e69d6ee2..d022dcc4 100644 --- a/opm/core/props/phaseUsageFromDeck.hpp +++ b/opm/core/props/phaseUsageFromDeck.hpp @@ -39,13 +39,13 @@ namespace Opm std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); // Discover phase usage. - if (eclipseState->hasPhase(PhaseEnum::WATER)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::WATER)) { pu.phase_used[BlackoilPhases::Aqua] = 1; } - if (eclipseState->hasPhase(PhaseEnum::OIL)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::OIL)) { pu.phase_used[BlackoilPhases::Liquid] = 1; } - if (eclipseState->hasPhase(PhaseEnum::GAS)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::GAS)) { pu.phase_used[BlackoilPhases::Vapour] = 1; } pu.num_phases = 0; From 9f127099cfbc53b0769c7f649febeb4f3463b291 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 27 Jan 2014 13:45:59 +0100 Subject: [PATCH 15/56] Added an extra data file with an extra well --- CMakeLists_files.cmake | 1 + tests/test_wellsmanager.cpp | 58 ++++++++++++++++----- tests/wells_manager_data_expanded.data | 72 ++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 13 deletions(-) create mode 100755 tests/wells_manager_data_expanded.data diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 3bf606de..25abeca9 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -182,6 +182,7 @@ list (APPEND TEST_DATA_FILES tests/testBlackoilState1.DATA tests/testBlackoilState2.DATA tests/wells_manager_data.data + tests/wells_manager_data_expanded.data ) # originally generated with the command: diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 3bf36eae..25c9678a 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -174,8 +174,6 @@ void check_controls_epoch1( struct WellControls ** ctrls) { } - - BOOST_AUTO_TEST_CASE(Constructor_Works) { Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); @@ -194,7 +192,7 @@ BOOST_AUTO_TEST_CASE(Constructor_Works) { Opm::WellsManager wellsManager(Deck, *gridManager.c_grid(), NULL); const Wells* wells = wellsManager.c_wells(); - wells_static_check( wells ); + wells_static_check( wells ); check_controls_epoch1( wells->ctrls ); } } @@ -212,11 +210,15 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::WellsManager wellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); - const Wells* wells = wellsManager.c_wells(); - wells_static_check( wells ); - check_controls_epoch0( wells->ctrls ); + std::cout << "Checking new well structure, epoch 0" << std::endl; + wells_static_check( wellsManager.c_wells() ); - BOOST_CHECK(wells_equal(wells, oldWellsManager.c_wells())); + std::cout << "Checking old well structure, epoch 0" << std::endl; + wells_static_check( oldWellsManager.c_wells() ); + + check_controls_epoch0( wellsManager.c_wells()->ctrls ); + + BOOST_CHECK(wells_equal(wellsManager.c_wells(), oldWellsManager.c_wells())); } Deck.setCurrentEpoch(1); @@ -224,11 +226,41 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::WellsManager wellsManager(eclipseState, 1,Deck, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); - const Wells* wells = wellsManager.c_wells(); - wells_static_check( wells ); - check_controls_epoch1( wells->ctrls ); + std::cout << "Checking new well structure, epoch 1" << std::endl; + wells_static_check( wellsManager.c_wells() ); - BOOST_CHECK(wells_equal(wells, oldWellsManager.c_wells())); + std::cout << "Checking old well structure, epoch 1" << std::endl; + wells_static_check( oldWellsManager.c_wells() ); + + check_controls_epoch1( wellsManager.c_wells()->ctrls ); + + BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells())); + } +} + + +BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { + + Opm::ParserPtr parser(new Opm::Parser()); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile("wells_manager_data_expanded.data"))); + + Opm::EclipseGridParser Deck("wells_manager_data_expanded.data"); + Opm::GridManager gridManager(Deck); + + Deck.setCurrentEpoch(0); + { + Opm::WellsManager wellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); + + BOOST_CHECK(wells_equal(wellsManager.c_wells(), oldWellsManager.c_wells())); + } + + Deck.setCurrentEpoch(1); + { + Opm::WellsManager wellsManager(eclipseState, 1,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); + + BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells())); } } @@ -244,8 +276,8 @@ BOOST_AUTO_TEST_CASE(WellsEqual) { Deck.setCurrentEpoch(1); Opm::WellsManager wellsManager1(Deck, *gridManager.c_grid(), NULL); - BOOST_CHECK( wells_equal( wellsManager0.c_wells() , wellsManager0.c_wells()) ); - BOOST_CHECK( !wells_equal( wellsManager0.c_wells() , wellsManager1.c_wells()) ); + BOOST_CHECK( wells_equal( wellsManager0.c_wells() , wellsManager0.c_wells()) ); + BOOST_CHECK( !wells_equal( wellsManager0.c_wells() , wellsManager1.c_wells()) ); } diff --git a/tests/wells_manager_data_expanded.data b/tests/wells_manager_data_expanded.data new file mode 100755 index 00000000..dfcd0b48 --- /dev/null +++ b/tests/wells_manager_data_expanded.data @@ -0,0 +1,72 @@ +OIL +GAS +WATER + +DIMENS + 10 10 5 / + +GRID + +DXV +10*1000.0 / + +DYV +10*1000.0 / + +DZV +10.0 20.0 30.0 10.0 5.0 / + +SCHEDULE + +WELSPECS + 'INJ1' 'G' 1 1 8335 'GAS' / + 'PROD1' 'G' 10 10 8400 'OIL' / +/ + +COMPDAT + 'INJ1' 1 1 1 2 'OPEN' 1 10.6092 0.5 / + 'INJ1' 1 1 3 5 'OPEN' 1 12.6092 0.5 / + 'INJ1' 1 1 2* 'OPEN' 1 14.6092 0.5 / + 'PROD1' 10 3 3 3 'OPEN' 0 10.6092 0.5 / +/ + +WCONPROD + 'PROD1' 'OPEN' 'ORAT' 20000 4* 1000 / +/ + +WCONINJE + 'INJ1' 'GAS' 'OPEN' 'RATE' 100 200 400 / +/ + + +DATES + 1 'FEB' 2000 / +/ + +WELSPECS + 'INJ1' 'G3' 1 1 8335 'GAS' / + 'QNJ2' 'G3' 1 1 8335 'GAS' / +/ + + +COMPDAT + 'QNJ2' 3 4 1 2 'OPEN' 1 10.6092 0.5 / + 'QNJ2' 4 4 3 5 'OPEN' 1 12.6092 0.5 / +/ + +WCONPROD + 'PROD1' 'OPEN' 'RESV' 999 3* 123 100 / +/ + +WCONINJE + 'INJ1' 'WATER' 'OPEN' 'RESV' 10 20 40 / + 'QNJ2' 'WATER' 'OPEN' 'RESV' 7 33 39 / +/ + + + +TSTEP +14.0 + / + +END From b6afe6735778fec41469f32593ffeb2abbcba30b Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 28 Jan 2014 21:02:47 +0100 Subject: [PATCH 16/56] Compdat extraction from new CompletionXXX classes --- opm/core/wells/WellsManager.cpp | 245 +++++++++++++++---------- opm/core/wells/WellsManager.hpp | 2 +- tests/wells_manager_data_expanded.data | 2 +- 3 files changed, 145 insertions(+), 104 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 81a3f1c6..a03e3cc5 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -253,6 +253,24 @@ namespace Opm return; } + // global_cell is a map from compressed cells to Cartesian grid cells. + // We must make the inverse lookup. + const int* global_cell = grid.global_cell; + const int* cpgdim = grid.cartdims; + std::map cartesian_to_compressed; + + if (global_cell) { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(global_cell[i], i)); + } + } + else { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(i, i)); + } + } + + // Obtain phase usage data. PhaseUsage pu = phaseUsageFromDeck(eclipseState); @@ -273,123 +291,146 @@ namespace Opm std::vector wells = schedule->getWells(timeStep); well_names.reserve(wells.size()); well_data.reserve(wells.size()); + wellperf_data.resize(wells.size()); int well_index = 0; for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); - well_names_to_index[well->name()] = well_index; - well_names.push_back(well->name()); - { - WellData wd; - // If negative (defaulted), set refdepth to a marker - // value, will be changed after getting perforation - // data to the centroid of the cell of the top well - // perforation. - wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); - wd.welspecsline = -1; - well_data.push_back(wd); + { // WELSPECS handling + well_names_to_index[well->name()] = well_index; + well_names.push_back(well->name()); + { + WellData wd; + // If negative (defaulted), set refdepth to a marker + // value, will be changed after getting perforation + // data to the centroid of the cell of the top well + // perforation. + wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); + wd.welspecsline = -1; + well_data.push_back(wd); + } + } + + { // COMPDAT handling + CompletionSetConstPtr completionSet = well->getCompletions(timeStep); + for (size_t c=0; csize(); c++) { + CompletionConstPtr completion = completionSet->get(c); + int i = completion->getI() - 1; + int j = completion->getJ() - 1; + int k = completion->getK() - 1; + int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k); + std::map::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); + if (cgit == cartesian_to_compressed.end()) { + OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' + << k << " not found in grid (well = " << well->name() << ')'); + } + int cell = cgit->second; + PerfData pd; + pd.cell = cell; + if (completion->getCF() > 0.0) { + pd.well_index = completion->getCF(); + } else { + double radius = 0.5*completion->getDiameter(); + if (radius <= 0.0) { + radius = 0.5*unit::feet; + OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); + } + std::array cubical = getCubeDim(grid, cell); + const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; + pd.well_index = computeWellIndex(radius, cubical, cell_perm, + completion->getDiameter()); + } + wellperf_data[well_index].push_back(pd); + } } well_index++; } - const int num_wells = well_data.size(); - wellperf_data.resize(num_wells); - // global_cell is a map from compressed cells to Cartesian grid cells. - // We must make the inverse lookup. - const int* global_cell = grid.global_cell; - const int* cpgdim = grid.cartdims; - std::map cartesian_to_compressed; - if (global_cell) { - for (int i = 0; i < grid.number_of_cells; ++i) { - cartesian_to_compressed.insert(std::make_pair(global_cell[i], i)); - } - } - else { - for (int i = 0; i < grid.number_of_cells; ++i) { - cartesian_to_compressed.insert(std::make_pair(i, i)); - } - } - // Get COMPDAT data - // It is *not* allowed to have multiple lines corresponding to - // the same perforation! - const COMPDAT& compdat = deck.getCOMPDAT(); - const int num_compdat = compdat.compdat.size(); - for (int kw = 0; kw < num_compdat; ++kw) { - // Extract well name, or the part of the well name that - // comes before the '*'. - std::string name = compdat.compdat[kw].well_; - std::string::size_type len = name.find('*'); - if (len != std::string::npos) { - name = name.substr(0, len); - } - // Look for well with matching name. - bool found = false; - for (int wix = 0; wix < num_wells; ++wix) { - if (well_names[wix].compare(0,len, name) == 0) { // equal - // We have a matching name. - int ix = compdat.compdat[kw].grid_ind_[0] - 1; - int jy = compdat.compdat[kw].grid_ind_[1] - 1; - int kz1 = compdat.compdat[kw].grid_ind_[2] - 1; - int kz2 = compdat.compdat[kw].grid_ind_[3] - 1; - WellConstPtr well = schedule->getWell(well_names[wix]); - if (ix < 0) { - // Defaulted I location. Extract from WELSPECS. - ix = well->getHeadI() - 1; - } - if (jy < 0) { - // Defaulted J location. Extract from WELSPECS. - jy = well->getHeadJ() - 1; - } - if (kz1 < 0) { - // Defaulted KZ1. Use top layer. - kz1 = 0; - } - if (kz2 < 0) { - // Defaulted KZ2. Use bottom layer. - kz2 = cpgdim[2] - 1; - } +// // Get COMPDAT data +// // It is *not* allowed to have multiple lines corresponding to +// // the same perforation! +// const COMPDAT& compdat = deck.getCOMPDAT(); +// const int num_compdat = compdat.compdat.size(); +// for (int kw = 0; kw < num_compdat; ++kw) { +// // Extract well name, or the part of the well name that +// // comes before the '*'. +// std::string name = compdat.compdat[kw].well_; +// std::string::size_type len = name.find('*'); +// if (len != std::string::npos) { +// name = name.substr(0, len); +// } +// // Look for well with matching name. +// bool found = false; +// for (int wix = 0; wix < num_wells; ++wix) { +// if (well_names[wix].compare(0,len, name) == 0) { // equal +// // We have a matching name. +// int ix = compdat.compdat[kw].grid_ind_[0] - 1; +// int jy = compdat.compdat[kw].grid_ind_[1] - 1; +// int kz1 = compdat.compdat[kw].grid_ind_[2] - 1; +// int kz2 = compdat.compdat[kw].grid_ind_[3] - 1; - for (int kz = kz1; kz <= kz2; ++kz) { - int cart_grid_indx = ix + cpgdim[0]*(jy + cpgdim[1]*kz); - std::map::const_iterator cgit = - cartesian_to_compressed.find(cart_grid_indx); - if (cgit == cartesian_to_compressed.end()) { - OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << ix << ' ' << jy << ' ' - << kz << " not found in grid (well = " << name << ')'); - } - int cell = cgit->second; - PerfData pd; - pd.cell = cell; - if (compdat.compdat[kw].connect_trans_fac_ > 0.0) { - pd.well_index = compdat.compdat[kw].connect_trans_fac_; - } else { - double radius = 0.5*compdat.compdat[kw].diameter_; - if (radius <= 0.0) { - radius = 0.5*unit::feet; - OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); - } - std::array cubical = getCubeDim(grid, cell); - const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; - pd.well_index = computeWellIndex(radius, cubical, cell_perm, - compdat.compdat[kw].skin_factor_); - } - wellperf_data[wix].push_back(pd); - } - found = true; - break; - } - } - if (!found) { - OPM_THROW(std::runtime_error, "Undefined well name: " << compdat.compdat[kw].well_ - << " in COMPDAT"); - } - } +// WellConstPtr well = schedule->getWell(well_names[wix]); +// if (ix < 0) { +// // Defaulted I location. Extract from WELSPECS. +// ix = well->getHeadI() - 1; +// } +// if (jy < 0) { +// // Defaulted J location. Extract from WELSPECS. +// jy = well->getHeadJ() - 1; +// } +// if (kz1 < 0) { +// // Defaulted KZ1. Use top layer. +// kz1 = 0; +// } +// if (kz2 < 0) { +// // Defaulted KZ2. Use bottom layer. +// kz2 = cpgdim[2] - 1; +// } + +// for (int kz = kz1; kz <= kz2; ++kz) { +// int cart_grid_indx = ix + cpgdim[0]*(jy + cpgdim[1]*kz); +// std::map::const_iterator cgit = +// cartesian_to_compressed.find(cart_grid_indx); +// if (cgit == cartesian_to_compressed.end()) { +// OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << ix << ' ' << jy << ' ' +// << kz << " not found in grid (well = " << name << ')'); +// } +// int cell = cgit->second; +// PerfData pd; +// pd.cell = cell; +// if (compdat.compdat[kw].connect_trans_fac_ > 0.0) { +// pd.well_index = compdat.compdat[kw].connect_trans_fac_; +// } else { +// double radius = 0.5*compdat.compdat[kw].diameter_; +// if (radius <= 0.0) { +// radius = 0.5*unit::feet; +// OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); +// } +// std::array cubical = getCubeDim(grid, cell); +// const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; +// pd.well_index = computeWellIndex(radius, cubical, cell_perm, +// compdat.compdat[kw].skin_factor_); +// } +// wellperf_data[wix].push_back(pd); +// } +// found = true; +// break; +// } +// } +// if (!found) { +// OPM_THROW(std::runtime_error, "Undefined well name: " << compdat.compdat[kw].well_ +// << " in COMPDAT"); +// } +// } // Set up reference depths that were defaulted. Count perfs. + + const int num_wells = well_data.size(); + int num_perfs = 0; assert(grid.dimensions == 3); for (int w = 0; w < num_wells; ++w) { diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index f14be794..145fb2e2 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -124,7 +124,7 @@ namespace Opm // Data Wells* w_; - WellCollection well_collection_; + WellCollection well_collection_; diff --git a/tests/wells_manager_data_expanded.data b/tests/wells_manager_data_expanded.data index dfcd0b48..3d97003a 100755 --- a/tests/wells_manager_data_expanded.data +++ b/tests/wells_manager_data_expanded.data @@ -26,7 +26,7 @@ WELSPECS COMPDAT 'INJ1' 1 1 1 2 'OPEN' 1 10.6092 0.5 / 'INJ1' 1 1 3 5 'OPEN' 1 12.6092 0.5 / - 'INJ1' 1 1 2* 'OPEN' 1 14.6092 0.5 / + 'INJ1' 1 1 1 1 'OPEN' 1 14.6092 0.5 / 'PROD1' 10 3 3 3 'OPEN' 0 10.6092 0.5 / / From 3cdd27c3a92630867c695892663050cc77ee5157 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 10:32:17 +0100 Subject: [PATCH 17/56] Changed data file not not have same perforation multiple times, as the old system doesn't support this --- tests/wells_manager_data_expanded.data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/wells_manager_data_expanded.data b/tests/wells_manager_data_expanded.data index 3d97003a..d025d627 100755 --- a/tests/wells_manager_data_expanded.data +++ b/tests/wells_manager_data_expanded.data @@ -26,7 +26,7 @@ WELSPECS COMPDAT 'INJ1' 1 1 1 2 'OPEN' 1 10.6092 0.5 / 'INJ1' 1 1 3 5 'OPEN' 1 12.6092 0.5 / - 'INJ1' 1 1 1 1 'OPEN' 1 14.6092 0.5 / + 'INJ1' 2 2 1 1 'OPEN' 1 14.6092 0.5 / 'PROD1' 10 3 3 3 'OPEN' 0 10.6092 0.5 / / From bca4242da93a95d48306a10f4a3f50bc438f319a Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 11:57:45 +0100 Subject: [PATCH 18/56] Updated COMPDAT i,j,k indices, and removed the old commented part --- opm/core/wells/WellsManager.cpp | 92 +++------------------------------ tests/test_wellsmanager.cpp | 14 ++--- 2 files changed, 13 insertions(+), 93 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index a03e3cc5..63d868cf 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -315,9 +315,9 @@ namespace Opm CompletionSetConstPtr completionSet = well->getCompletions(timeStep); for (size_t c=0; csize(); c++) { CompletionConstPtr completion = completionSet->get(c); - int i = completion->getI() - 1; - int j = completion->getJ() - 1; - int k = completion->getK() - 1; + int i = completion->getI(); + int j = completion->getJ(); + int k = completion->getK(); int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k); std::map::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); if (cgit == cartesian_to_compressed.end()) { @@ -337,8 +337,7 @@ namespace Opm } std::array cubical = getCubeDim(grid, cell); const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; - pd.well_index = computeWellIndex(radius, cubical, cell_perm, - completion->getDiameter()); + pd.well_index = computeWellIndex(radius, cubical, cell_perm, completion->getDiameter()); } wellperf_data[well_index].push_back(pd); } @@ -346,87 +345,6 @@ namespace Opm well_index++; } - - - - -// // Get COMPDAT data -// // It is *not* allowed to have multiple lines corresponding to -// // the same perforation! -// const COMPDAT& compdat = deck.getCOMPDAT(); -// const int num_compdat = compdat.compdat.size(); -// for (int kw = 0; kw < num_compdat; ++kw) { -// // Extract well name, or the part of the well name that -// // comes before the '*'. -// std::string name = compdat.compdat[kw].well_; -// std::string::size_type len = name.find('*'); -// if (len != std::string::npos) { -// name = name.substr(0, len); -// } -// // Look for well with matching name. -// bool found = false; -// for (int wix = 0; wix < num_wells; ++wix) { -// if (well_names[wix].compare(0,len, name) == 0) { // equal -// // We have a matching name. -// int ix = compdat.compdat[kw].grid_ind_[0] - 1; -// int jy = compdat.compdat[kw].grid_ind_[1] - 1; -// int kz1 = compdat.compdat[kw].grid_ind_[2] - 1; -// int kz2 = compdat.compdat[kw].grid_ind_[3] - 1; - -// WellConstPtr well = schedule->getWell(well_names[wix]); -// if (ix < 0) { -// // Defaulted I location. Extract from WELSPECS. -// ix = well->getHeadI() - 1; -// } -// if (jy < 0) { -// // Defaulted J location. Extract from WELSPECS. -// jy = well->getHeadJ() - 1; -// } -// if (kz1 < 0) { -// // Defaulted KZ1. Use top layer. -// kz1 = 0; -// } -// if (kz2 < 0) { -// // Defaulted KZ2. Use bottom layer. -// kz2 = cpgdim[2] - 1; -// } - -// for (int kz = kz1; kz <= kz2; ++kz) { -// int cart_grid_indx = ix + cpgdim[0]*(jy + cpgdim[1]*kz); -// std::map::const_iterator cgit = -// cartesian_to_compressed.find(cart_grid_indx); -// if (cgit == cartesian_to_compressed.end()) { -// OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << ix << ' ' << jy << ' ' -// << kz << " not found in grid (well = " << name << ')'); -// } -// int cell = cgit->second; -// PerfData pd; -// pd.cell = cell; -// if (compdat.compdat[kw].connect_trans_fac_ > 0.0) { -// pd.well_index = compdat.compdat[kw].connect_trans_fac_; -// } else { -// double radius = 0.5*compdat.compdat[kw].diameter_; -// if (radius <= 0.0) { -// radius = 0.5*unit::feet; -// OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); -// } -// std::array cubical = getCubeDim(grid, cell); -// const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; -// pd.well_index = computeWellIndex(radius, cubical, cell_perm, -// compdat.compdat[kw].skin_factor_); -// } -// wellperf_data[wix].push_back(pd); -// } -// found = true; -// break; -// } -// } -// if (!found) { -// OPM_THROW(std::runtime_error, "Undefined well name: " << compdat.compdat[kw].well_ -// << " in COMPDAT"); -// } -// } - // Set up reference depths that were defaulted. Count perfs. const int num_wells = well_data.size(); @@ -435,6 +353,7 @@ namespace Opm assert(grid.dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); + printf("New version; num_perfs %d \n", num_perfs); if (well_data[w].reference_bhp_depth < 0.0) { // It was defaulted. Set reference depth to minimum perforation depth. double min_depth = 1e100; @@ -1025,6 +944,7 @@ namespace Opm assert(grid.dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); + printf("Old version; num_perfs %d \n", num_perfs); if (well_data[w].reference_bhp_depth < 0.0) { // It was defaulted. Set reference depth to minimum perforation depth. double min_depth = 1e100; diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 25c9678a..dfbb84fe 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -61,7 +61,7 @@ void wells_static_check(const Wells* wells) { } -/* +/* The number of controls is determined by looking at which elements have been given explicit - non-default - values in the WCONxxxx keyword. Is that at all interesting? @@ -71,7 +71,7 @@ void wells_static_check(const Wells* wells) { void check_controls_epoch0( struct WellControls ** ctrls) { // The injector { - const struct WellControls * ctrls0 = ctrls[0]; + const struct WellControls * ctrls0 = ctrls[0]; BOOST_CHECK_EQUAL( 3 , well_controls_get_num(ctrls0)); // The number of controls for the injector == 3?? BOOST_CHECK_EQUAL( SURFACE_RATE , well_controls_iget_type(ctrls0 , 0) ); @@ -87,7 +87,7 @@ void check_controls_epoch0( struct WellControls ** ctrls) { BOOST_CHECK_EQUAL( 0 , well_controls_get_current(ctrls0) ); // The phase distribution in the active target - { + { const double * distr = well_controls_iget_distr( ctrls0 , 0 ); BOOST_CHECK_EQUAL( 0 , distr[0] ); // Water BOOST_CHECK_EQUAL( 0 , distr[1] ); // Oil @@ -110,7 +110,7 @@ void check_controls_epoch0( struct WellControls ** ctrls) { BOOST_CHECK_EQUAL( 0 , well_controls_get_current(ctrls1)); // The phase distribution in the active target - { + { const double * distr = well_controls_iget_distr( ctrls1 , 0 ); BOOST_CHECK_EQUAL( 0 , distr[0] ); // Water BOOST_CHECK_EQUAL( 1 , distr[1] ); // Oil @@ -125,7 +125,7 @@ void check_controls_epoch0( struct WellControls ** ctrls) { void check_controls_epoch1( struct WellControls ** ctrls) { // The injector { - const struct WellControls * ctrls0 = ctrls[0]; + const struct WellControls * ctrls0 = ctrls[0]; BOOST_CHECK_EQUAL( 3 , well_controls_get_num(ctrls0)); // The number of controls for the injector == 3?? BOOST_CHECK_EQUAL( SURFACE_RATE , well_controls_iget_type(ctrls0 , 0 )); @@ -140,7 +140,7 @@ void check_controls_epoch1( struct WellControls ** ctrls) { // Which control is active BOOST_CHECK_EQUAL( 1 , well_controls_get_current(ctrls0)); - { + { const double * distr = well_controls_iget_distr( ctrls0 , 1 ); BOOST_CHECK_EQUAL( 1 , distr[0] ); // Water BOOST_CHECK_EQUAL( 0 , distr[1] ); // Oil @@ -164,7 +164,7 @@ void check_controls_epoch1( struct WellControls ** ctrls) { // Which control is active BOOST_CHECK_EQUAL( 1 , well_controls_get_current(ctrls1) ); - { + { const double * distr = well_controls_iget_distr( ctrls1 , 1 ); BOOST_CHECK_EQUAL( 1 , distr[0] ); // Water BOOST_CHECK_EQUAL( 1 , distr[1] ); // Oil From fabcb45d4c914d2e111c03936330dbd36de11a73 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 12:30:03 +0100 Subject: [PATCH 19/56] Removed print statement --- opm/core/wells/WellsManager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 63d868cf..58e5b863 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -353,7 +353,6 @@ namespace Opm assert(grid.dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); - printf("New version; num_perfs %d \n", num_perfs); if (well_data[w].reference_bhp_depth < 0.0) { // It was defaulted. Set reference depth to minimum perforation depth. double min_depth = 1e100; @@ -944,7 +943,6 @@ namespace Opm assert(grid.dimensions == 3); for (int w = 0; w < num_wells; ++w) { num_perfs += wellperf_data[w].size(); - printf("Old version; num_perfs %d \n", num_perfs); if (well_data[w].reference_bhp_depth < 0.0) { // It was defaulted. Set reference depth to minimum perforation depth. double min_depth = 1e100; From 827b7099bcc0e41169c26abc2640e5ce57013ef4 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 08:00:45 +0100 Subject: [PATCH 20/56] Added namespace prefix to enum. --- opm/core/props/phaseUsageFromDeck.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/props/phaseUsageFromDeck.hpp b/opm/core/props/phaseUsageFromDeck.hpp index e69d6ee2..d022dcc4 100644 --- a/opm/core/props/phaseUsageFromDeck.hpp +++ b/opm/core/props/phaseUsageFromDeck.hpp @@ -39,13 +39,13 @@ namespace Opm std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); // Discover phase usage. - if (eclipseState->hasPhase(PhaseEnum::WATER)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::WATER)) { pu.phase_used[BlackoilPhases::Aqua] = 1; } - if (eclipseState->hasPhase(PhaseEnum::OIL)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::OIL)) { pu.phase_used[BlackoilPhases::Liquid] = 1; } - if (eclipseState->hasPhase(PhaseEnum::GAS)) { + if (eclipseState->hasPhase(Phase::PhaseEnum::GAS)) { pu.phase_used[BlackoilPhases::Vapour] = 1; } pu.num_phases = 0; From 59bd79af58bb016d2adf828833c6fa8b19d06bc3 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 08:02:13 +0100 Subject: [PATCH 21/56] Implemented well injector properties from opm-parser::Well. --- opm/core/wells/WellsManager.cpp | 224 +++++++++++++++++--------------- 1 file changed, 120 insertions(+), 104 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index a92b924e..f30d1cab 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -30,6 +30,8 @@ #include #include +#include + #include #include #include @@ -142,6 +144,25 @@ namespace << control << " in input file"); } } + + Mode mode(Opm::WellInjector::ControlModeEnum controlMode) + { + switch ( controlMode ) { + case Opm::WellInjector::GRUP: + return GRUP; + case Opm::WellInjector::RESV: + return RESV; + case Opm::WellInjector::RATE: + return RATE; + case Opm::WellInjector::THP: + return THP; + case Opm::WellInjector::BHP: + return BHP; + default: + throw std::invalid_argument("unhandled enum value"); + } + } + } // namespace InjectionControl @@ -437,117 +458,112 @@ namespace Opm OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); } } + + + well_index = 0; + for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { + WellConstPtr well = (*wellIter); + + if (well->isInjector(timeStep)) { + clear_well_controls(well_index, w_); + int ok = 1; + int control_pos[5] = { -1, -1, -1, -1, -1 }; - // Get WCONINJE data, add injection controls to wells. - // It is allowed to have multiple lines corresponding to - // the same well, in which case the last one encountered - // is the one used. - if (deck.hasField("WCONINJE")) { - const WCONINJE& wconinjes = deck.getWCONINJE(); - const int num_wconinjes = wconinjes.wconinje.size(); - for (int kw = 0; kw < num_wconinjes; ++kw) { - const WconinjeLine& wci_line = wconinjes.wconinje[kw]; - // Extract well name, or the part of the well name that - // comes before the '*'. - std::string name = wci_line.well_; - std::string::size_type len = name.find('*'); - if (len != std::string::npos) { - name = name.substr(0, len); + if (well->getSurfaceInjectionRate( timeStep ) >= 0.0) { + control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); + + if (injectorType == WellInjector::TypeEnum::WATER) { + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::OIL) { + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::GAS) { + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + + ok = append_well_controls(SURFACE_RATE, + well->getSurfaceInjectionRate( timeStep ) , + distr, + well_index, + w_); } - bool well_found = false; - for (int wix = 0; wix < num_wells; ++wix) { - if (well_names[wix].compare(0,len, name) == 0) { //equal - well_found = true; - assert(well_data[wix].type == w_->type[wix]); - if (well_data[wix].type != INJECTOR) { - OPM_THROW(std::runtime_error, "Found WCONINJE entry for a non-injector well: " << well_names[wix]); - } - // Add all controls that are present in well. - // First we must clear existing controls, in case the - // current WCONINJE line is modifying earlier controls. - clear_well_controls(wix, w_); - int ok = 1; - int control_pos[5] = { -1, -1, -1, -1, -1 }; - if (ok && wci_line.surface_flow_max_rate_ >= 0.0) { - control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - if (wci_line.injector_type_ == "WATER") { - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (wci_line.injector_type_ == "OIL") { - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (wci_line.injector_type_ == "GAS") { - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } else { - OPM_THROW(std::runtime_error, "Injector type " << wci_line.injector_type_ << " not supported." - "WellsManager only supports WATER, OIL and GAS injector types."); - } - ok = append_well_controls(SURFACE_RATE, wci_line.surface_flow_max_rate_, - distr, wix, w_); - } - if (ok && wci_line.reservoir_flow_max_rate_ >= 0.0) { - control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - if (wci_line.injector_type_ == "WATER") { - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (wci_line.injector_type_ == "OIL") { - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (wci_line.injector_type_ == "GAS") { - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } else { - OPM_THROW(std::runtime_error, "Injector type " << wci_line.injector_type_ << " not supported." - "WellsManager only supports WATER, OIL and GAS injector types."); - } - ok = append_well_controls(RESERVOIR_RATE, wci_line.reservoir_flow_max_rate_, - distr, wix, w_); - } - if (ok && wci_line.BHP_limit_ > 0.0) { - control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[wix]); - ok = append_well_controls(BHP, wci_line.BHP_limit_, - NULL, wix, w_); - } - if (ok && wci_line.THP_limit_ > 0.0) { - OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[wix]); - } - if (!ok) { - OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[wix]); - } - InjectionControl::Mode mode = InjectionControl::mode(wci_line.control_mode_); - int cpos = control_pos[mode]; - if (cpos == -1 && mode != InjectionControl::GRUP) { - OPM_THROW(std::runtime_error, "Control for " << wci_line.control_mode_ << " not specified in well " << well_names[wix]); - } - // We need to check if the well is shut or not - if (wci_line.open_shut_flag_ == "SHUT") { - cpos = ~cpos; - } - set_current_control(wix, cpos, w_); + if (ok && well->getReservoirInjectionRate(timeStep) >= 0.0) { + control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); + + if (injectorType == WellInjector::TypeEnum::WATER) { + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::OIL) { + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::GAS) { + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + + ok = append_well_controls(RESERVOIR_RATE, + well->getReservoirInjectionRate( timeStep ), + distr, + well_index, + w_); + } - // Set well component fraction. - double cf[3] = { 0.0, 0.0, 0.0 }; - if (wci_line.injector_type_[0] == 'W') { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (wci_line.injector_type_[0] == 'O') { - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (wci_line.injector_type_[0] == 'G') { - if (!pu.phase_used[BlackoilPhases::Vapour]) { - OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } - std::copy(cf, cf + pu.num_phases, w_->comp_frac + wix*pu.num_phases); + if (ok && well->getBHPLimit(timeStep) > 0.0) { + control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); + ok = append_well_controls(BHP, + well->getBHPLimit(timeStep), + NULL, + well_index, + w_); + } + + if (ok && well->getTHPLimit(timeStep) > 0.0) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); + } + + + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); + } + + + { + InjectionControl::Mode mode = InjectionControl::mode( well->getInjectorControlMode(timeStep) ); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != InjectionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control not specified in well " << well_names[well_index]); } + + // We need to check if the well is shut or not + if (well->getStatus( timeStep ) == WellCommon::SHUT) { + cpos = ~cpos; + } + set_current_control(well_index, cpos, w_); } - if (!well_found) { - OPM_THROW(std::runtime_error, "Undefined well name: " << wci_line.well_ - << " in WCONINJE"); + + + // Set well component fraction. + double cf[3] = { 0.0, 0.0, 0.0 }; + if (well->getInjectorType(timeStep) == WellInjector::WATER) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (well->getInjectorType(timeStep) == WellInjector::OIL) { + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (well->getInjectorType(timeStep) == WellInjector::GAS) { + if (!pu.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well."); + } + cf[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; } + std::copy(cf, cf + pu.num_phases, w_->comp_frac + well_index*pu.num_phases); + + well_index++; } } From af79c27b9ef2daf782de57e5b9fc702b2d6be89f Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 15:55:17 +0100 Subject: [PATCH 22/56] Implemented WCONPROD support from Parser::Schedule object. --- opm/core/wells/WellsManager.cpp | 244 ++++++++++++++++++-------------- 1 file changed, 135 insertions(+), 109 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index cdefe544..14cf4265 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -105,6 +105,33 @@ namespace << control << " in input file"); } } + + + Mode mode(Opm::WellProducer::ControlModeEnum controlMode) + { + switch( controlMode ) { + case Opm::WellProducer::ORAT: + return ORAT; + case Opm::WellProducer::WRAT: + return WRAT; + case Opm::WellProducer::GRAT: + return GRAT; + case Opm::WellProducer::LRAT: + return LRAT; + case Opm::WellProducer::CRAT: + return CRAT; + case Opm::WellProducer::RESV: + return RESV; + case Opm::WellProducer::BHP: + return BHP; + case Opm::WellProducer::THP: + return THP; + case Opm::WellProducer::GRUP: + return GRUP; + default: + throw std::invalid_argument("unhandled enum value"); + } + } } // namespace ProductionControl @@ -521,117 +548,116 @@ namespace Opm std::copy(cf, cf + pu.num_phases, w_->comp_frac + well_index*pu.num_phases); } + + if (well->isProducer(timeStep)) { + // Add all controls that are present in well. + // First we must clear existing controls, in case the + // current WCONPROD line is modifying earlier controls. + clear_well_controls(well_index, w_); + int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + int ok = 1; + if (ok && well->hasProductionControl(timeStep , WellProducer::ORAT)) { + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); + } + + control_pos[ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getOilRate( timeStep ), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::WRAT)) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); + } + control_pos[ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getWaterRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::GRAT)) { + if (!pu.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); + } + control_pos[ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getGasRate( timeStep ), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::LRAT)) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); + } + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified."); + } + control_pos[ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; + distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getLiquidRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::RESV)) { + control_pos[ProductionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 1.0, 1.0, 1.0 }; + ok = append_well_controls(RESERVOIR_RATE, + -well->getResVRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::BHP)) { + control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); + ok = append_well_controls(BHP, + well->getBHPLimit( timeStep ) , + NULL, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::THP)) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); + } + + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); + } + + ProductionControl::Mode mode = ProductionControl::mode(well->getProducerControlMode(timeStep)); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != ProductionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]); + } + // If it's shut, we complement the cpos + if (well->getStatus(timeStep) == WellCommon::SHUT) { + cpos = ~cpos; // So we can easily retrieve the cpos later + } + set_current_control(well_index, cpos, w_); + } well_index++; } - - // Get WCONPROD data - // It is allowed to have multiple lines corresponding to - // the same well, in which case the last one encountered - // is the one used. - if (deck.hasField("WCONPROD")) { - const WCONPROD& wconprods = deck.getWCONPROD(); - const int num_wconprods = wconprods.wconprod.size(); - for (int kw = 0; kw < num_wconprods; ++kw) { - const WconprodLine& wcp_line = wconprods.wconprod[kw]; - std::string name = wcp_line.well_; - std::string::size_type len = name.find('*'); - if (len != std::string::npos) { - name = name.substr(0, len); - } - bool well_found = false; - for (int wix = 0; wix < num_wells; ++wix) { - if (well_names[wix].compare(0,len, name) == 0) { //equal - well_found = true; - assert(well_data[wix].type == w_->type[wix]); - if (well_data[wix].type != PRODUCER) { - std::cout << "Looking at well: " << well_names[wix] << std::endl; - OPM_THROW(std::runtime_error, "Found WCONPROD entry for a non-producer well: " << well_names[wix]); - } - // Add all controls that are present in well. - // First we must clear existing controls, in case the - // current WCONPROD line is modifying earlier controls. - clear_well_controls(wix, w_); - int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - int ok = 1; - if (ok && wcp_line.oil_max_rate_ >= 0.0) { - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); - } - control_pos[ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - ok = append_well_controls(SURFACE_RATE, -wcp_line.oil_max_rate_, - distr, wix, w_); - } - if (ok && wcp_line.water_max_rate_ >= 0.0) { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); - } - control_pos[ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - ok = append_well_controls(SURFACE_RATE, -wcp_line.water_max_rate_, - distr, wix, w_); - } - if (ok && wcp_line.gas_max_rate_ >= 0.0) { - if (!pu.phase_used[BlackoilPhases::Vapour]) { - OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); - } - control_pos[ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - ok = append_well_controls(SURFACE_RATE, -wcp_line.gas_max_rate_, - distr, wix, w_); - } - if (ok && wcp_line.liquid_max_rate_ >= 0.0) { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); - } - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified."); - } - control_pos[ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - ok = append_well_controls(SURFACE_RATE, -wcp_line.liquid_max_rate_, - distr, wix, w_); - } - if (ok && wcp_line.reservoir_flow_max_rate_ >= 0.0) { - control_pos[ProductionControl::RESV] = well_controls_get_num(w_->ctrls[wix]); - double distr[3] = { 1.0, 1.0, 1.0 }; - ok = append_well_controls(RESERVOIR_RATE, -wcp_line.reservoir_flow_max_rate_, - distr, wix, w_); - } - if (ok && wcp_line.BHP_limit_ > 0.0) { - control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[wix]); - ok = append_well_controls(BHP, wcp_line.BHP_limit_, - NULL, wix, w_); - } - if (ok && wcp_line.THP_limit_ > 0.0) { - OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[wix]); - } - if (!ok) { - OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[wix]); - } - ProductionControl::Mode mode = ProductionControl::mode(wcp_line.control_mode_); - int cpos = control_pos[mode]; - if (cpos == -1 && mode != ProductionControl::GRUP) { - OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[wix]); - } - // If it's shut, we complement the cpos - if (wcp_line.open_shut_flag_ == "SHUT") { - cpos = ~cpos; // So we can easily retrieve the cpos later - } - set_current_control(wix, cpos, w_); - } - } - if (!well_found) { - OPM_THROW(std::runtime_error, "Undefined well name: " << wcp_line.well_ - << " in WCONPROD"); - } - } - } + // Get WELTARG data if (deck.hasField("WELTARG")) { From 4a36a72cdbe6dee04e606890107f47f5ac4c60b9 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 16:37:20 +0100 Subject: [PATCH 23/56] Using well->hasInjectionControl() to check whether a particular control is available. --- opm/core/wells/WellsManager.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 14cf4265..e74a1a19 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -453,7 +453,7 @@ namespace Opm int ok = 1; int control_pos[5] = { -1, -1, -1, -1, -1 }; - if (well->getSurfaceInjectionRate( timeStep ) >= 0.0) { + if (well->hasInjectionControl(timeStep , WellInjector::RATE)) { control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]); double distr[3] = { 0.0, 0.0, 0.0 }; WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); @@ -473,7 +473,7 @@ namespace Opm w_); } - if (ok && well->getReservoirInjectionRate(timeStep) >= 0.0) { + if (ok && well->hasInjectionControl(timeStep , WellInjector::RESV)) { control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); double distr[3] = { 0.0, 0.0, 0.0 }; WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); @@ -493,7 +493,7 @@ namespace Opm w_); } - if (ok && well->getBHPLimit(timeStep) > 0.0) { + if (ok && well->hasInjectionControl(timeStep , WellInjector::BHP)) { control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); ok = append_well_controls(BHP, well->getBHPLimit(timeStep), @@ -502,7 +502,7 @@ namespace Opm w_); } - if (ok && well->getTHPLimit(timeStep) > 0.0) { + if (ok && well->hasInjectionControl(timeStep , WellInjector::THP)) { OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); } From d390dc2725130eb250f51937da32fa308292323f Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 08:58:54 +0100 Subject: [PATCH 24/56] Extracted well specification and completion data setup to a function --- opm/core/wells/WellsManager.cpp | 276 ++++++++++++++++---------------- opm/core/wells/WellsManager.hpp | 92 +++++++---- 2 files changed, 196 insertions(+), 172 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index e74a1a19..ab069c8f 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -47,23 +47,8 @@ namespace { - struct WellData - { - WellType type; - // WellControlType control; - // double target; - double reference_bhp_depth; - // Opm::InjectionSpecification::InjectorType injected_phase; - int welspecsline; - }; - struct PerfData - { - int cell; - double well_index; - }; - namespace ProductionControl { enum Mode { ORAT, WRAT, GRAT, @@ -301,23 +286,8 @@ namespace Opm return; } - // global_cell is a map from compressed cells to Cartesian grid cells. - // We must make the inverse lookup. - const int* global_cell = grid.global_cell; - const int* cpgdim = grid.cartdims; std::map cartesian_to_compressed; - - if (global_cell) { - for (int i = 0; i < grid.number_of_cells; ++i) { - cartesian_to_compressed.insert(std::make_pair(global_cell[i], i)); - } - } - else { - for (int i = 0; i < grid.number_of_cells; ++i) { - cartesian_to_compressed.insert(std::make_pair(i, i)); - } - } - + setupCompressedToCartesian(grid, cartesian_to_compressed); // Obtain phase usage data. PhaseUsage pu = phaseUsageFromDeck(eclipseState); @@ -330,121 +300,17 @@ namespace Opm // For easy lookup: std::map well_names_to_index; - typedef std::map::const_iterator WNameIt; - - - // Main "well-loop" to populate the data structures (well_names, well_data, ...) ScheduleConstPtr schedule = eclipseState->getSchedule(); std::vector wells = schedule->getWells(timeStep); + well_names.reserve(wells.size()); well_data.reserve(wells.size()); wellperf_data.resize(wells.size()); + createWellsFromSpecs(schedule, timeStep, grid, well_names, well_data, wellperf_data, well_names_to_index, pu, cartesian_to_compressed, permeability); + int well_index = 0; - for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { - WellConstPtr well = (*wellIter); - { // WELSPECS handling - well_names_to_index[well->name()] = well_index; - well_names.push_back(well->name()); - { - WellData wd; - // If negative (defaulted), set refdepth to a marker - // value, will be changed after getting perforation - // data to the centroid of the cell of the top well - // perforation. - wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); - wd.welspecsline = -1; - if (well->isInjector( timeStep )) - wd.type = INJECTOR; - else - wd.type = PRODUCER; - well_data.push_back(wd); - } - } - - { // COMPDAT handling - CompletionSetConstPtr completionSet = well->getCompletions(timeStep); - for (size_t c=0; csize(); c++) { - CompletionConstPtr completion = completionSet->get(c); - int i = completion->getI(); - int j = completion->getJ(); - int k = completion->getK(); - int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k); - std::map::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); - if (cgit == cartesian_to_compressed.end()) { - OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' - << k << " not found in grid (well = " << well->name() << ')'); - } - int cell = cgit->second; - PerfData pd; - pd.cell = cell; - if (completion->getCF() > 0.0) { - pd.well_index = completion->getCF(); - } else { - double radius = 0.5*completion->getDiameter(); - if (radius <= 0.0) { - radius = 0.5*unit::feet; - OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); - } - std::array cubical = getCubeDim(grid, cell); - const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; - pd.well_index = computeWellIndex(radius, cubical, cell_perm, completion->getDiameter()); - } - wellperf_data[well_index].push_back(pd); - } - } - well_index++; - } - - // Set up reference depths that were defaulted. Count perfs. - - const int num_wells = well_data.size(); - - int num_perfs = 0; - assert(grid.dimensions == 3); - for (int w = 0; w < num_wells; ++w) { - num_perfs += wellperf_data[w].size(); - if (well_data[w].reference_bhp_depth < 0.0) { - // It was defaulted. Set reference depth to minimum perforation depth. - double min_depth = 1e100; - int num_wperfs = wellperf_data[w].size(); - for (int perf = 0; perf < num_wperfs; ++perf) { - double depth = grid.cell_centroids[3*wellperf_data[w][perf].cell + 2]; - min_depth = std::min(min_depth, depth); - } - well_data[w].reference_bhp_depth = min_depth; - } - } - - // Create the well data structures. - w_ = create_wells(pu.num_phases, num_wells, num_perfs); - if (!w_) { - OPM_THROW(std::runtime_error, "Failed creating Wells struct."); - } - - - // Add wells. - for (int w = 0; w < num_wells; ++w) { - const int w_num_perf = wellperf_data[w].size(); - std::vector perf_cells(w_num_perf); - std::vector perf_prodind(w_num_perf); - for (int perf = 0; perf < w_num_perf; ++perf) { - perf_cells[perf] = wellperf_data[w][perf].cell; - perf_prodind[perf] = wellperf_data[w][perf].well_index; - } - const double* comp_frac = NULL; - // We initialize all wells with a null component fraction, - // and must (for injection wells) overwrite it later. - int ok = add_well(well_data[w].type, well_data[w].reference_bhp_depth, w_num_perf, - comp_frac, &perf_cells[0], &perf_prodind[0], well_names[w].c_str(), w_); - if (!ok) { - OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); - } - } - - - well_index = 0; for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); @@ -1433,4 +1299,138 @@ namespace Opm well_collection_.applyExplicitReinjectionControls(well_reservoirrates_phase, well_surfacerates_phase); } + void WellsManager::setupCompressedToCartesian(const UnstructuredGrid& grid, std::map& cartesian_to_compressed ) { + // global_cell is a map from compressed cells to Cartesian grid cells. + // We must make the inverse lookup. + const int* global_cell = grid.global_cell; + + if (global_cell) { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(global_cell[i], i)); + } + } + else { + for (int i = 0; i < grid.number_of_cells; ++i) { + cartesian_to_compressed.insert(std::make_pair(i, i)); + } + } + + } + + void WellsManager::createWellsFromSpecs(ScheduleConstPtr schedule, size_t timeStep, + const UnstructuredGrid& grid, + std::vector& well_names, + std::vector& well_data, + std::vector >& wellperf_data, + std::map& well_names_to_index, + const PhaseUsage& phaseUsage, + std::map cartesian_to_compressed, + const double* permeability) + { + std::vector wells = schedule->getWells(timeStep); + int well_index = 0; + for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { + WellConstPtr well = (*wellIter); + { // WELSPECS handling + well_names_to_index[well->name()] = well_index; + well_names.push_back(well->name()); + { + WellData wd; + // If negative (defaulted), set refdepth to a marker + // value, will be changed after getting perforation + // data to the centroid of the cell of the top well + // perforation. + wd.reference_bhp_depth = (well->getRefDepth() < 0.0) ? -1e100 : well->getRefDepth(); + wd.welspecsline = -1; + if (well->isInjector( timeStep )) + wd.type = INJECTOR; + else + wd.type = PRODUCER; + well_data.push_back(wd); + } + } + + { // COMPDAT handling + CompletionSetConstPtr completionSet = well->getCompletions(timeStep); + for (size_t c=0; csize(); c++) { + CompletionConstPtr completion = completionSet->get(c); + int i = completion->getI(); + int j = completion->getJ(); + int k = completion->getK(); + + const int* cpgdim = grid.cartdims; + int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k); + std::map::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx); + if (cgit == cartesian_to_compressed.end()) { + OPM_THROW(std::runtime_error, "Cell with i,j,k indices " << i << ' ' << j << ' ' + << k << " not found in grid (well = " << well->name() << ')'); + } + int cell = cgit->second; + PerfData pd; + pd.cell = cell; + if (completion->getCF() > 0.0) { + pd.well_index = completion->getCF(); + } else { + double radius = 0.5*completion->getDiameter(); + if (radius <= 0.0) { + radius = 0.5*unit::feet; + OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius); + } + std::array cubical = getCubeDim(grid, cell); + const double* cell_perm = &permeability[grid.dimensions*grid.dimensions*cell]; + pd.well_index = computeWellIndex(radius, cubical, cell_perm, completion->getDiameter()); + } + wellperf_data[well_index].push_back(pd); + } + } + well_index++; + } + + // Set up reference depths that were defaulted. Count perfs. + + const int num_wells = well_data.size(); + + int num_perfs = 0; + assert(grid.dimensions == 3); + for (int w = 0; w < num_wells; ++w) { + num_perfs += wellperf_data[w].size(); + if (well_data[w].reference_bhp_depth < 0.0) { + // It was defaulted. Set reference depth to minimum perforation depth. + double min_depth = 1e100; + int num_wperfs = wellperf_data[w].size(); + for (int perf = 0; perf < num_wperfs; ++perf) { + double depth = grid.cell_centroids[3*wellperf_data[w][perf].cell + 2]; + min_depth = std::min(min_depth, depth); + } + well_data[w].reference_bhp_depth = min_depth; + } + } + + // Create the well data structures. + w_ = create_wells(phaseUsage.num_phases, num_wells, num_perfs); + if (!w_) { + OPM_THROW(std::runtime_error, "Failed creating Wells struct."); + } + + + // Add wells. + for (int w = 0; w < num_wells; ++w) { + const int w_num_perf = wellperf_data[w].size(); + std::vector perf_cells(w_num_perf); + std::vector perf_prodind(w_num_perf); + for (int perf = 0; perf < w_num_perf; ++perf) { + perf_cells[perf] = wellperf_data[w][perf].cell; + perf_prodind[perf] = wellperf_data[w][perf].well_index; + } + const double* comp_frac = NULL; + // We initialize all wells with a null component fraction, + // and must (for injection wells) overwrite it later. + int ok = add_well(well_data[w].type, well_data[w].reference_bhp_depth, w_num_perf, + comp_frac, &perf_cells[0], &perf_prodind[0], well_names[w].c_str(), w_); + if (!ok) { + OPM_THROW(std::runtime_error, "Failed adding well " << well_names[w] << " to Wells data structure."); + } + } + + } } // namespace Opm diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 145fb2e2..fdb49371 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -34,7 +34,22 @@ namespace Opm { class EclipseGridParser; + struct WellData + { + WellType type; + // WellControlType control; + // double target; + double reference_bhp_depth; + // Opm::InjectionSpecification::InjectorType injected_phase; + int welspecsline; + }; + + struct PerfData + { + int cell; + double well_index; + }; /// This class manages a Wells struct in the sense that it /// encapsulates creation and destruction of the wells /// data structure. @@ -42,41 +57,41 @@ namespace Opm class WellsManager { public: - /// Default constructor -- no wells. - WellsManager(); + /// Default constructor -- no wells. + WellsManager(); - /// Construct from existing wells object. - /// WellsManager is not properly initialised in the sense that the logic to - /// manage control switching does not exist. - /// - /// @param[in] W Existing wells object. - WellsManager(struct Wells* W); + /// Construct from existing wells object. + /// WellsManager is not properly initialised in the sense that the logic to + /// manage control switching does not exist. + /// + /// @param[in] W Existing wells object. + WellsManager(struct Wells* W); - /// Construct from input deck and grid. - /// The permeability argument may be zero if the input contain - /// well productivity indices, otherwise it must be given in - /// order to approximate these by the Peaceman formula. - WellsManager(const Opm::EclipseGridParser& deck, - const UnstructuredGrid& grid, - const double* permeability); + /// Construct from input deck and grid. + /// The permeability argument may be zero if the input contain + /// well productivity indices, otherwise it must be given in + /// order to approximate these by the Peaceman formula. + WellsManager(const Opm::EclipseGridParser& deck, + const UnstructuredGrid& grid, + const double* permeability); - WellsManager(const Opm::EclipseStateConstPtr eclipseState, - const size_t timeStep, - const Opm::EclipseGridParser& deck, - const UnstructuredGrid& grid, - const double* permeability); + WellsManager(const Opm::EclipseStateConstPtr eclipseState, + const size_t timeStep, + const Opm::EclipseGridParser& deck, + const UnstructuredGrid& grid, + const double* permeability); - /// Destructor. - ~WellsManager(); + /// Destructor. + ~WellsManager(); /// Does the "deck" define any wells? bool empty() const; - /// Access the managed Wells. - /// The method is named similarly to c_str() in std::string, - /// to make it clear that we are returning a C-compatible struct. - const Wells* c_wells() const; + /// Access the managed Wells. + /// The method is named similarly to c_str() in std::string, + /// to make it clear that we are returning a C-compatible struct. + const Wells* c_wells() const; /// Access the well group hierarchy. const WellCollection& wellCollection() const; @@ -117,18 +132,27 @@ namespace Opm void applyExplicitReinjectionControls(const std::vector& well_reservoirrates_phase, const std::vector& well_surfacerates_phase); + private: - // Disable copying and assignment. - WellsManager(const WellsManager& other); - WellsManager& operator=(const WellsManager& other); - - // Data - Wells* w_; - WellCollection well_collection_; - + // Disable copying and assignment. + WellsManager(const WellsManager& other); + WellsManager& operator=(const WellsManager& other); + static void setupCompressedToCartesian(const UnstructuredGrid& grid, std::map& cartesian_to_compressed ); + void createWellsFromSpecs( ScheduleConstPtr schedule, size_t timeStep, + const UnstructuredGrid& grid, + std::vector& well_names, + std::vector& well_data, + std::vector >& wellperf_data, + std::map & well_names_to_index, + const PhaseUsage& phaseUsage, + const std::map cartesian_to_compressed, + const double* permeability); + // Data + Wells* w_; + WellCollection well_collection_; }; } // namespace Opm From 57a779167b09d90db2369e0c615a2b6efea2ae82 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 09:07:58 +0100 Subject: [PATCH 25/56] Extracted well controls setup to separate function --- opm/core/wells/WellsManager.cpp | 433 ++++++++++++++++---------------- opm/core/wells/WellsManager.hpp | 3 + 2 files changed, 222 insertions(+), 214 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index ab069c8f..693f2d48 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -310,220 +310,7 @@ namespace Opm createWellsFromSpecs(schedule, timeStep, grid, well_names, well_data, wellperf_data, well_names_to_index, pu, cartesian_to_compressed, permeability); - int well_index = 0; - for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { - WellConstPtr well = (*wellIter); - - if (well->isInjector(timeStep)) { - clear_well_controls(well_index, w_); - int ok = 1; - int control_pos[5] = { -1, -1, -1, -1, -1 }; - - if (well->hasInjectionControl(timeStep , WellInjector::RATE)) { - control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); - - if (injectorType == WellInjector::TypeEnum::WATER) { - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (injectorType == WellInjector::TypeEnum::OIL) { - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (injectorType == WellInjector::TypeEnum::GAS) { - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } - - ok = append_well_controls(SURFACE_RATE, - well->getSurfaceInjectionRate( timeStep ) , - distr, - well_index, - w_); - } - - if (ok && well->hasInjectionControl(timeStep , WellInjector::RESV)) { - control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); - - if (injectorType == WellInjector::TypeEnum::WATER) { - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (injectorType == WellInjector::TypeEnum::OIL) { - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (injectorType == WellInjector::TypeEnum::GAS) { - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } - - ok = append_well_controls(RESERVOIR_RATE, - well->getReservoirInjectionRate( timeStep ), - distr, - well_index, - w_); - } - - if (ok && well->hasInjectionControl(timeStep , WellInjector::BHP)) { - control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); - ok = append_well_controls(BHP, - well->getBHPLimit(timeStep), - NULL, - well_index, - w_); - } - - if (ok && well->hasInjectionControl(timeStep , WellInjector::THP)) { - OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); - } - - - if (!ok) { - OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); - } - - - { - InjectionControl::Mode mode = InjectionControl::mode( well->getInjectorControlMode(timeStep) ); - int cpos = control_pos[mode]; - if (cpos == -1 && mode != InjectionControl::GRUP) { - OPM_THROW(std::runtime_error, "Control not specified in well " << well_names[well_index]); - } - - // We need to check if the well is shut or not - if (well->getStatus( timeStep ) == WellCommon::SHUT) { - cpos = ~cpos; - } - set_current_control(well_index, cpos, w_); - } - - - // Set well component fraction. - double cf[3] = { 0.0, 0.0, 0.0 }; - if (well->getInjectorType(timeStep) == WellInjector::WATER) { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - } else if (well->getInjectorType(timeStep) == WellInjector::OIL) { - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - } else if (well->getInjectorType(timeStep) == WellInjector::GAS) { - if (!pu.phase_used[BlackoilPhases::Vapour]) { - OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well."); - } - cf[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - } - std::copy(cf, cf + pu.num_phases, w_->comp_frac + well_index*pu.num_phases); - - } - - if (well->isProducer(timeStep)) { - // Add all controls that are present in well. - // First we must clear existing controls, in case the - // current WCONPROD line is modifying earlier controls. - clear_well_controls(well_index, w_); - int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - int ok = 1; - if (ok && well->hasProductionControl(timeStep , WellProducer::ORAT)) { - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); - } - - control_pos[ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - ok = append_well_controls(SURFACE_RATE, - -well->getOilRate( timeStep ), - distr, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::WRAT)) { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); - } - control_pos[ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - ok = append_well_controls(SURFACE_RATE, - -well->getWaterRate(timeStep), - distr, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::GRAT)) { - if (!pu.phase_used[BlackoilPhases::Vapour]) { - OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); - } - control_pos[ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Vapour]] = 1.0; - ok = append_well_controls(SURFACE_RATE, - -well->getGasRate( timeStep ), - distr, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::LRAT)) { - if (!pu.phase_used[BlackoilPhases::Aqua]) { - OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); - } - if (!pu.phase_used[BlackoilPhases::Liquid]) { - OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified."); - } - control_pos[ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 0.0, 0.0, 0.0 }; - distr[pu.phase_pos[BlackoilPhases::Aqua]] = 1.0; - distr[pu.phase_pos[BlackoilPhases::Liquid]] = 1.0; - ok = append_well_controls(SURFACE_RATE, - -well->getLiquidRate(timeStep), - distr, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::RESV)) { - control_pos[ProductionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); - double distr[3] = { 1.0, 1.0, 1.0 }; - ok = append_well_controls(RESERVOIR_RATE, - -well->getResVRate(timeStep), - distr, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::BHP)) { - control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); - ok = append_well_controls(BHP, - well->getBHPLimit( timeStep ) , - NULL, - well_index, - w_); - } - - if (ok && well->hasProductionControl(timeStep , WellProducer::THP)) { - OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); - } - - if (!ok) { - OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); - } - - ProductionControl::Mode mode = ProductionControl::mode(well->getProducerControlMode(timeStep)); - int cpos = control_pos[mode]; - if (cpos == -1 && mode != ProductionControl::GRUP) { - OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]); - } - // If it's shut, we complement the cpos - if (well->getStatus(timeStep) == WellCommon::SHUT) { - cpos = ~cpos; // So we can easily retrieve the cpos later - } - set_current_control(well_index, cpos, w_); - } - well_index++; - } - + setupWellControls(wells, timeStep, well_names, pu); // Get WELTARG data if (deck.hasField("WELTARG")) { @@ -1433,4 +1220,222 @@ namespace Opm } } + + void WellsManager::setupWellControls(std::vector& wells, size_t timeStep, + std::vector& well_names, const PhaseUsage& phaseUsage) { + int well_index = 0; + for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { + WellConstPtr well = (*wellIter); + + if (well->isInjector(timeStep)) { + clear_well_controls(well_index, w_); + int ok = 1; + int control_pos[5] = { -1, -1, -1, -1, -1 }; + + if (well->hasInjectionControl(timeStep , WellInjector::RATE)) { + control_pos[InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); + + if (injectorType == WellInjector::TypeEnum::WATER) { + distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::OIL) { + distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::GAS) { + distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + + ok = append_well_controls(SURFACE_RATE, + well->getSurfaceInjectionRate( timeStep ) , + distr, + well_index, + w_); + } + + if (ok && well->hasInjectionControl(timeStep , WellInjector::RESV)) { + control_pos[InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + WellInjector::TypeEnum injectorType = well->getInjectorType(timeStep); + + if (injectorType == WellInjector::TypeEnum::WATER) { + distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::OIL) { + distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (injectorType == WellInjector::TypeEnum::GAS) { + distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + + ok = append_well_controls(RESERVOIR_RATE, + well->getReservoirInjectionRate( timeStep ), + distr, + well_index, + w_); + } + + if (ok && well->hasInjectionControl(timeStep , WellInjector::BHP)) { + control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); + ok = append_well_controls(BHP, + well->getBHPLimit(timeStep), + NULL, + well_index, + w_); + } + + if (ok && well->hasInjectionControl(timeStep , WellInjector::THP)) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); + } + + + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); + } + + + { + InjectionControl::Mode mode = InjectionControl::mode( well->getInjectorControlMode(timeStep) ); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != InjectionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control not specified in well " << well_names[well_index]); + } + + // We need to check if the well is shut or not + if (well->getStatus( timeStep ) == WellCommon::SHUT) { + cpos = ~cpos; + } + set_current_control(well_index, cpos, w_); + } + + + // Set well component fraction. + double cf[3] = { 0.0, 0.0, 0.0 }; + if (well->getInjectorType(timeStep) == WellInjector::WATER) { + if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + } else if (well->getInjectorType(timeStep) == WellInjector::OIL) { + if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + } else if (well->getInjectorType(timeStep) == WellInjector::GAS) { + if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well."); + } + cf[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0; + } + std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases); + + } + + if (well->isProducer(timeStep)) { + // Add all controls that are present in well. + // First we must clear existing controls, in case the + // current WCONPROD line is modifying earlier controls. + clear_well_controls(well_index, w_); + int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + int ok = 1; + if (ok && well->hasProductionControl(timeStep , WellProducer::ORAT)) { + if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); + } + + control_pos[ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getOilRate( timeStep ), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::WRAT)) { + if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); + } + control_pos[ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getWaterRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::GRAT)) { + if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); + } + control_pos[ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getGasRate( timeStep ), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::LRAT)) { + if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { + OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); + } + if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified."); + } + control_pos[ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 0.0, 0.0, 0.0 }; + distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; + distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0; + ok = append_well_controls(SURFACE_RATE, + -well->getLiquidRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::RESV)) { + control_pos[ProductionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]); + double distr[3] = { 1.0, 1.0, 1.0 }; + ok = append_well_controls(RESERVOIR_RATE, + -well->getResVRate(timeStep), + distr, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::BHP)) { + control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); + ok = append_well_controls(BHP, + well->getBHPLimit( timeStep ) , + NULL, + well_index, + w_); + } + + if (ok && well->hasProductionControl(timeStep , WellProducer::THP)) { + OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); + } + + if (!ok) { + OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); + } + + ProductionControl::Mode mode = ProductionControl::mode(well->getProducerControlMode(timeStep)); + int cpos = control_pos[mode]; + if (cpos == -1 && mode != ProductionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]); + } + // If it's shut, we complement the cpos + if (well->getStatus(timeStep) == WellCommon::SHUT) { + cpos = ~cpos; // So we can easily retrieve the cpos later + } + set_current_control(well_index, cpos, w_); + } + well_index++; + } + + } } // namespace Opm diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index fdb49371..d731f0fd 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -138,6 +138,9 @@ namespace Opm WellsManager(const WellsManager& other); WellsManager& operator=(const WellsManager& other); static void setupCompressedToCartesian(const UnstructuredGrid& grid, std::map& cartesian_to_compressed ); + void setupWellControls(std::vector& wells, size_t timeStep, + std::vector& well_names, const PhaseUsage& phaseUsage); + void createWellsFromSpecs( ScheduleConstPtr schedule, size_t timeStep, const UnstructuredGrid& grid, std::vector& well_names, From 74cb703238f1ea1c58c13b24803778d6d65b1d80 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 09:22:28 +0100 Subject: [PATCH 26/56] Removed WELTARG commented code, moved wellperf_data into createWellsFromSpecs function --- opm/core/wells/WellsManager.cpp | 83 +++++++++++---------------------- opm/core/wells/WellsManager.hpp | 3 +- 2 files changed, 29 insertions(+), 57 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 693f2d48..2241cacd 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -296,7 +296,7 @@ namespace Opm // then used to initialize the Wells struct. std::vector well_names; std::vector well_data; - std::vector > wellperf_data; + // For easy lookup: std::map well_names_to_index; @@ -306,61 +306,12 @@ namespace Opm well_names.reserve(wells.size()); well_data.reserve(wells.size()); - wellperf_data.resize(wells.size()); - createWellsFromSpecs(schedule, timeStep, grid, well_names, well_data, wellperf_data, well_names_to_index, pu, cartesian_to_compressed, permeability); + + createWellsFromSpecs(wells, timeStep, grid, well_names, well_data, well_names_to_index, pu, cartesian_to_compressed, permeability); setupWellControls(wells, timeStep, well_names, pu); - // Get WELTARG data - if (deck.hasField("WELTARG")) { - OPM_THROW(std::runtime_error, "We currently do not handle WELTARG."); - /* - const WELTARG& weltargs = deck.getWELTARG(); - const int num_weltargs = weltargs.weltarg.size(); - for (int kw = 0; kw < num_weltargs; ++kw) { - std::string name = weltargs.weltarg[kw].well_; - std::string::size_type len = name.find('*'); - if (len != std::string::npos) { - name = name.substr(0, len); - } - bool well_found = false; - for (int wix = 0; wix < num_wells; ++wix) { - if (well_names[wix].compare(0,len, name) == 0) { //equal - well_found = true; - well_data[wix].target = weltargs.weltarg[kw].new_value_; - break; - } - } - if (!well_found) { - OPM_THROW(std::runtime_error, "Undefined well name: " << weltargs.weltarg[kw].well_ - << " in WELTARG"); - } - } - */ - } - - // Debug output. -#define EXTRA_OUTPUT -#ifdef EXTRA_OUTPUT - /* - std::cout << "\t WELL DATA" << std::endl; - for(int i = 0; i< num_wells; ++i) { - std::cout << i << ": " << well_data[i].type << " " - << well_data[i].control << " " << well_data[i].target - << std::endl; - } - - std::cout << "\n\t PERF DATA" << std::endl; - for(int i=0; i< int(wellperf_data.size()); ++i) { - for(int j=0; j< int(wellperf_data[i].size()); ++j) { - std::cout << i << ": " << wellperf_data[i][j].cell << " " - << wellperf_data[i][j].well_index << std::endl; - } - } - */ -#endif - if (deck.hasField("WELOPEN")) { const WELOPEN& welopen = deck.getWELOPEN(); for (size_t i = 0; i < welopen.welopen.size(); ++i) { @@ -438,6 +389,27 @@ namespace Opm } well_collection_.setWellsPointer(w_); well_collection_.applyGroupControls(); + + // Debug output. +#define EXTRA_OUTPUT +#ifdef EXTRA_OUTPUT + /* + std::cout << "\t WELL DATA" << std::endl; + for(int i = 0; i< num_wells; ++i) { + std::cout << i << ": " << well_data[i].type << " " + << well_data[i].control << " " << well_data[i].target + << std::endl; + } + + std::cout << "\n\t PERF DATA" << std::endl; + for(int i=0; i< int(wellperf_data.size()); ++i) { + for(int j=0; j< int(wellperf_data[i].size()); ++j) { + std::cout << i << ": " << wellperf_data[i][j].cell << " " + << wellperf_data[i][j].well_index << std::endl; + } + } + */ +#endif } @@ -1104,17 +1076,18 @@ namespace Opm } - void WellsManager::createWellsFromSpecs(ScheduleConstPtr schedule, size_t timeStep, + void WellsManager::createWellsFromSpecs(std::vector& wells, size_t timeStep, const UnstructuredGrid& grid, std::vector& well_names, std::vector& well_data, - std::vector >& wellperf_data, std::map& well_names_to_index, const PhaseUsage& phaseUsage, std::map cartesian_to_compressed, const double* permeability) { - std::vector wells = schedule->getWells(timeStep); + std::vector > wellperf_data; + wellperf_data.resize(wells.size()); + int well_index = 0; for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index d731f0fd..77bbb903 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -141,11 +141,10 @@ namespace Opm void setupWellControls(std::vector& wells, size_t timeStep, std::vector& well_names, const PhaseUsage& phaseUsage); - void createWellsFromSpecs( ScheduleConstPtr schedule, size_t timeStep, + void createWellsFromSpecs( std::vector& wells, size_t timeStep, const UnstructuredGrid& grid, std::vector& well_names, std::vector& well_data, - std::vector >& wellperf_data, std::map & well_names_to_index, const PhaseUsage& phaseUsage, const std::map cartesian_to_compressed, From 322da86bb16d6a15ca7b0614b99e10df545f7ee3 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 6 Feb 2014 11:03:59 +0100 Subject: [PATCH 27/56] [cmake] Makes arbitrary build subdirectories possible. Instead of guessing the suffix or subdir of the build directory, we now simply compare PROJECT_{BINARY,SOURCE]_DIR to detect it. By this e.g. opm-core/opm-parallel is a possible build directory, too. --- cmake/Modules/Findcjson.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Findcjson.cmake b/cmake/Modules/Findcjson.cmake index fde5a782..305f05b1 100644 --- a/cmake/Modules/Findcjson.cmake +++ b/cmake/Modules/Findcjson.cmake @@ -31,13 +31,14 @@ if (CMAKE_SIZEOF_VOID_P) math (EXPR _BITS "8 * ${CMAKE_SIZEOF_VOID_P}") endif (CMAKE_SIZEOF_VOID_P) +string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/?(.*)" "\\1" BUILD_DIR_SUFFIX "${PROJECT_BINARY_DIR}") + find_library (CJSON_LIBRARY NAMES "cjson" HINTS "${CJSON_ROOT}" PATHS "${PROJECT_BINARY_DIR}/../opm-parser" - "${PROJECT_BINARY_DIR}/../opm-parser-build" - "${PROJECT_BINARY_DIR}/../../opm-parser/build" - "${PROJECT_BINARY_DIR}/../../opm-parser/cmake-build" + "${PROJECT_BINARY_DIR}/../opm-parser${BUILD_DIR_SUFFIX}" + "${PROJECT_BINARY_DIR}/../../opm-parser/${BUILD_DIR_SUFFIX}" PATH_SUFFIXES "lib" "lib${_BITS}" "lib/${CMAKE_LIBRARY_ARCHITECTURE}" "opm/json" DOC "Path to cjson library archive/shared object files" From 6c0438ab815d309310732f9b4f25de5274951b70 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 6 Feb 2014 12:41:10 +0100 Subject: [PATCH 28/56] Whitespace change that replaces introduced tab with spaces. --- cmake/Modules/Findcjson.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/Findcjson.cmake b/cmake/Modules/Findcjson.cmake index 305f05b1..57055739 100644 --- a/cmake/Modules/Findcjson.cmake +++ b/cmake/Modules/Findcjson.cmake @@ -38,7 +38,7 @@ find_library (CJSON_LIBRARY HINTS "${CJSON_ROOT}" PATHS "${PROJECT_BINARY_DIR}/../opm-parser" "${PROJECT_BINARY_DIR}/../opm-parser${BUILD_DIR_SUFFIX}" - "${PROJECT_BINARY_DIR}/../../opm-parser/${BUILD_DIR_SUFFIX}" + "${PROJECT_BINARY_DIR}/../../opm-parser/${BUILD_DIR_SUFFIX}" PATH_SUFFIXES "lib" "lib${_BITS}" "lib/${CMAKE_LIBRARY_ARCHITECTURE}" "opm/json" DOC "Path to cjson library archive/shared object files" From 00b802dd4176a0e145de2edbec7ce08d8b8c62f3 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 6 Feb 2014 13:17:43 +0100 Subject: [PATCH 29/56] [cmake] Support more arbitrary sibling/subdir build directories for opm-parser. --- cmake/Modules/Findopm-parser.cmake | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmake/Modules/Findopm-parser.cmake b/cmake/Modules/Findopm-parser.cmake index 5f2701ed..67680015 100644 --- a/cmake/Modules/Findopm-parser.cmake +++ b/cmake/Modules/Findopm-parser.cmake @@ -31,6 +31,10 @@ if ((NOT OPM_PARSER_ROOT) AND OPM_ROOT) set (OPM_PARSER_ROOT "${OPM_ROOT}/opm-parser") endif () +# Detect the build dir suffix or subdirectory +string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/?(.*)" "\\1" BUILD_DIR_SUFFIX "${PROJECT_BINARY_DIR}") + +message("PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} BUILD_DIR_SUFFIX=${BUILD_DIR_SUFFIX}") # if a root is specified, then don't search in system directories # or in relative directories to this one if (OPM_PARSER_ROOT) @@ -43,9 +47,8 @@ else () "${PROJECT_SOURCE_DIR}/../opm-parser") set (_opm_parser_build "${PROJECT_BINARY_DIR}/../opm-parser" - "${PROJECT_BINARY_DIR}/../opm-parser-build" - "${PROJECT_BINARY_DIR}/../../opm-parser/build" - "${PROJECT_BINARY_DIR}/../../opm-parser/cmake-build") + "${PROJECT_BINARY_DIR}/../opm-parser${BUILD_DIR_SUFFIX}" + "${PROJECT_BINARY_DIR}/../../opm-parser/${BUILD_DIR_SUFFIX}") endif () # use this header as signature @@ -82,6 +85,7 @@ if (CMAKE_SIZEOF_VOID_P) math (EXPR _BITS "8 * ${CMAKE_SIZEOF_VOID_P}") endif (CMAKE_SIZEOF_VOID_P) +message("_opm_parser_build=${_opm_parser_build}") # these libraries constitute the parser core find_library (OPM_PARSER_LIBRARY NAMES "Parser" From 786aa125f215bea284a477fdebb8e67602501743 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 6 Feb 2014 13:21:23 +0100 Subject: [PATCH 30/56] Removed printf debugging messages --- cmake/Modules/Findopm-parser.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/Modules/Findopm-parser.cmake b/cmake/Modules/Findopm-parser.cmake index 67680015..2698209f 100644 --- a/cmake/Modules/Findopm-parser.cmake +++ b/cmake/Modules/Findopm-parser.cmake @@ -34,7 +34,6 @@ endif () # Detect the build dir suffix or subdirectory string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/?(.*)" "\\1" BUILD_DIR_SUFFIX "${PROJECT_BINARY_DIR}") -message("PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} PROJECT_BINARY_DIR=${PROJECT_BINARY_DIR} BUILD_DIR_SUFFIX=${BUILD_DIR_SUFFIX}") # if a root is specified, then don't search in system directories # or in relative directories to this one if (OPM_PARSER_ROOT) @@ -85,7 +84,6 @@ if (CMAKE_SIZEOF_VOID_P) math (EXPR _BITS "8 * ${CMAKE_SIZEOF_VOID_P}") endif (CMAKE_SIZEOF_VOID_P) -message("_opm_parser_build=${_opm_parser_build}") # these libraries constitute the parser core find_library (OPM_PARSER_LIBRARY NAMES "Parser" From 61b3a4618c4e8a778b714dd55d0a76da16373dac Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 6 Feb 2014 13:29:01 +0100 Subject: [PATCH 31/56] [cmake] Do not use find_package in config mode for opm-parser. Currently opm-parser does not create opm-parser-config.cmake files. Therefore building with duncontrol fails as it sets opm-parser_DIR. With this patch we force CMake to always use module mode for finding which fixes this. --- cmake/Modules/OpmFind.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/Modules/OpmFind.cmake b/cmake/Modules/OpmFind.cmake index 58501ba9..592a9830 100644 --- a/cmake/Modules/OpmFind.cmake +++ b/cmake/Modules/OpmFind.cmake @@ -59,6 +59,7 @@ set (_opm_proj_exemptions dune-istl dune-grid dune-geometry + opm-parser ) # although a DUNE module, it is delivered in the OPM suite From 8cdaf7d05d7d124bf039af49bb04d04c3cb9e262 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Thu, 6 Feb 2014 16:31:35 +0100 Subject: [PATCH 32/56] Removed WELOPEN from constructor, added throwing on non OPEN/SHUT statuses --- CMakeLists_files.cmake | 1 + opm/core/wells/WellsManager.cpp | 32 +++----------------- tests/test_wellsmanager.cpp | 23 ++++++++++++++ tests/wells_manager_data_expanded.data | 12 ++++++-- tests/wells_manager_data_wellSTOP.data | 42 ++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 30 deletions(-) create mode 100755 tests/wells_manager_data_wellSTOP.data diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 6b194b47..891b094c 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -183,6 +183,7 @@ list (APPEND TEST_DATA_FILES tests/testBlackoilState2.DATA tests/wells_manager_data.data tests/wells_manager_data_expanded.data + tests/wells_manager_data_wellSTOP.data ) # originally generated with the command: diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 2241cacd..49857d06 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -312,34 +312,6 @@ namespace Opm setupWellControls(wells, timeStep, well_names, pu); - if (deck.hasField("WELOPEN")) { - const WELOPEN& welopen = deck.getWELOPEN(); - for (size_t i = 0; i < welopen.welopen.size(); ++i) { - WelopenLine line = welopen.welopen[i]; - std::string wellname = line.well_; - std::map::const_iterator it = well_names_to_index.find(wellname); - if (it == well_names_to_index.end()) { - OPM_THROW(std::runtime_error, "Trying to open/shut well with name: \"" << wellname<<"\" but it's not registered under WELSPECS."); - } - const int index = it->second; - if (line.openshutflag_ == "SHUT") { - int cur_ctrl = well_controls_get_current(w_->ctrls[index]); - if (cur_ctrl >= 0) { - well_controls_invert_current(w_->ctrls[index]); - } - assert(well_controls_get_current(w_->ctrls[index]) < 0); - } else if (line.openshutflag_ == "OPEN") { - int cur_ctrl = well_controls_get_current(w_->ctrls[index]); - if (cur_ctrl < 0) { - well_controls_invert_current(w_->ctrls[index]); - } - assert(well_controls_get_current(w_->ctrls[index]) >= 0); - } else { - OPM_THROW(std::runtime_error, "Unknown Open/close keyword: \"" << line.openshutflag_<< "\". Allowed values: OPEN, SHUT."); - } - } - } - // Build the well_collection_ well group hierarchy. if (deck.hasField("GRUPTREE")) { std::cout << "Found gruptree" << std::endl; @@ -1200,6 +1172,10 @@ namespace Opm for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) { WellConstPtr well = (*wellIter); + if ( !( well->getStatus( timeStep ) == WellCommon::SHUT || well->getStatus( timeStep ) == WellCommon::OPEN) ) { + OPM_THROW(std::runtime_error, "Currently we do not support well status " << WellCommon::Status2String(well->getStatus( timeStep ))); + } + if (well->isInjector(timeStep)) { clear_well_controls(well_index, w_); int ok = 1; diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index fd3c7102..0caf67f1 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -262,6 +262,14 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells(), true)); } + + Deck.setCurrentEpoch(2); + { + Opm::WellsManager wellsManager(eclipseState, 2,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); + + BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells(), true)); + } } @@ -303,3 +311,18 @@ BOOST_AUTO_TEST_CASE(ControlsEqual) { } + +BOOST_AUTO_TEST_CASE(WellHasSTOP_ExceptionIsThrown) { + Opm::EclipseGridParser Deck("wells_manager_data_wellSTOP.data"); + Opm::GridManager gridManager(Deck); + + Opm::ParserPtr parser(new Opm::Parser()); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile("wells_manager_data_wellSTOP.data"))); + + Deck.setCurrentEpoch(0); + + BOOST_CHECK_THROW( new Opm::WellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL), std::runtime_error ); +} + + + diff --git a/tests/wells_manager_data_expanded.data b/tests/wells_manager_data_expanded.data index dae5e7cb..67207768 100755 --- a/tests/wells_manager_data_expanded.data +++ b/tests/wells_manager_data_expanded.data @@ -72,7 +72,15 @@ WCONINJE TSTEP -14.0 - / +14.0 / +/ + +WELOPEN + 'INJ1' 'SHUT' 5* / +/ + +TSTEP +14.0 / +/ END diff --git a/tests/wells_manager_data_wellSTOP.data b/tests/wells_manager_data_wellSTOP.data new file mode 100755 index 00000000..bdcbc381 --- /dev/null +++ b/tests/wells_manager_data_wellSTOP.data @@ -0,0 +1,42 @@ +OIL +GAS +WATER + +DIMENS + 10 10 5 / + +GRID + +DXV +10*1000.0 / + +DYV +10*1000.0 / + +DZV +10.0 20.0 30.0 10.0 5.0 / + +DEPTHZ +121*2000 +/ + +SCHEDULE + +WELSPECS + 'INJ1' 'G' 1 1 8335 'GAS' / + 'PROD1' 'G' 10 10 8400 'OIL' / +/ + +COMPDAT + 'INJ1' 1 1 1 1 'OPEN' 1 10.6092 0.5 / + 'PROD1' 10 3 3 3 'OPEN' 0 10.6092 0.5 / +/ + +WELOPEN + 'INJ1' 'STOP' 5* / +/ + +TSTEP + 10 / + +END From 2537ed0a0d9d619b0b078f151bab0a02af639ed6 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:01:54 +0100 Subject: [PATCH 33/56] git add variant of phaseUsageFromDeck() which takes a deck of the new parser --- opm/core/props/phaseUsageFromDeck.hpp | 40 +++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/opm/core/props/phaseUsageFromDeck.hpp b/opm/core/props/phaseUsageFromDeck.hpp index d022dcc4..2fc7f915 100644 --- a/opm/core/props/phaseUsageFromDeck.hpp +++ b/opm/core/props/phaseUsageFromDeck.hpp @@ -24,13 +24,13 @@ #include #include +#include #include namespace Opm { - /// Looks at presence of WATER, OIL and GAS keywords in state object /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(Opm::EclipseStateConstPtr eclipseState) @@ -67,7 +67,6 @@ namespace Opm return pu; } - /// Looks at presence of WATER, OIL and GAS keywords in deck /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(const EclipseGridParser& deck) @@ -105,6 +104,43 @@ namespace Opm return pu; } + /// Looks at presence of WATER, OIL and GAS keywords in deck + /// to determine active phases. + inline PhaseUsage phaseUsageFromDeck(Opm::DeckConstPtr newParserDeck) + { + PhaseUsage pu; + std::fill(pu.phase_used, pu.phase_used + BlackoilPhases::MaxNumPhases, 0); + + // Discover phase usage. + if (newParserDeck->hasKeyword("WATER")) { + pu.phase_used[BlackoilPhases::Aqua] = 1; + } + if (newParserDeck->hasKeyword("OIL")) { + pu.phase_used[BlackoilPhases::Liquid] = 1; + } + if (newParserDeck->hasKeyword("GAS")) { + pu.phase_used[BlackoilPhases::Vapour] = 1; + } + pu.num_phases = 0; + for (int i = 0; i < BlackoilPhases::MaxNumPhases; ++i) { + pu.phase_pos[i] = pu.num_phases; + pu.num_phases += pu.phase_used[i]; + } + + // Only 2 or 3 phase systems handled. + if (pu.num_phases < 2 || pu.num_phases > 3) { + OPM_THROW(std::runtime_error, "Cannot handle cases with " << pu.num_phases << " phases."); + } + + // We need oil systems, since we do not support the keywords needed for + // water-gas systems. + if (!pu.phase_used[BlackoilPhases::Liquid]) { + OPM_THROW(std::runtime_error, "Cannot handle cases with no OIL, i.e. water-gas systems."); + } + + return pu; + } + } #endif // OPM_PHASEUSAGEFROMDECK_HEADER_INCLUDED From 96a7730d8c2125e42ebc1734381218de568bd81a Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 11:58:20 +0100 Subject: [PATCH 34/56] add variants of all methods which take the new parser to the grid manager --- opm/core/grid/GridManager.cpp | 161 ++++++++++++++++++++++++++++++++-- opm/core/grid/GridManager.hpp | 11 ++- 2 files changed, 166 insertions(+), 6 deletions(-) diff --git a/opm/core/grid/GridManager.cpp b/opm/core/grid/GridManager.cpp index f557f0ca..2deaab44 100644 --- a/opm/core/grid/GridManager.cpp +++ b/opm/core/grid/GridManager.cpp @@ -23,16 +23,13 @@ #include #include #include + +#include #include #include - - namespace Opm { - - - /// Construct a 3d corner-point grid from a deck. GridManager::GridManager(const Opm::EclipseGridParser& deck) { @@ -58,6 +55,30 @@ namespace Opm } } + /// Construct a 3d corner-point grid from a deck. + GridManager::GridManager(Opm::DeckConstPtr newParserDeck) + { + // We accept two different ways to specify the grid. + // 1. Corner point format. + // Requires ZCORN, COORDS, DIMENS or SPECGRID, optionally + // ACTNUM, optionally MAPAXES. + // For this format, we will verify that DXV, DYV, DZV, + // DEPTHZ and TOPS are not present. + // 2. Tensor grid format. + // Requires DXV, DYV, DZV, optionally DEPTHZ or TOPS. + // For this format, we will verify that ZCORN, COORDS + // and ACTNUM are not present. + // Note that for TOPS, we only allow a uniform vector of values. + + if (newParserDeck->hasKeyword("ZCORN") && newParserDeck->hasKeyword("COORD")) { + initFromDeckCornerpoint(newParserDeck); + } else if (newParserDeck->hasKeyword("DXV") && newParserDeck->hasKeyword("DYV") && newParserDeck->hasKeyword("DZV")) { + initFromDeckTensorgrid(newParserDeck); + } else { + OPM_THROW(std::runtime_error, "Could not initialize grid from deck. " + "Need either ZCORN + COORD or DXV + DYV + DZV keywords."); + } + } @@ -152,6 +173,71 @@ namespace Opm } } + // Construct corner-point grid from deck. + void GridManager::initFromDeckCornerpoint(Opm::DeckConstPtr newParserDeck) + { + // Extract data from deck. + // Collect in input struct for preprocessing. + struct grdecl grdecl; + createGrDecl(newParserDeck, grdecl); + + // Process grid. + ug_ = create_grid_cornerpoint(&grdecl, 0.0); + if (!ug_) { + OPM_THROW(std::runtime_error, "Failed to construct grid."); + } + } + + void GridManager::createGrDecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl) + { + // Extract data from deck. + const std::vector& zcorn = newParserDeck->getKeyword("ZCORN")->getSIDoubleData(); + const std::vector& coord = newParserDeck->getKeyword("COORD")->getSIDoubleData(); + const int* actnum = NULL; + if (newParserDeck->hasKeyword("ACTNUM")) { + actnum = &(newParserDeck->getKeyword("ACTNUM")->getIntData()[0]); + } + + std::array dims; + if (newParserDeck->hasKeyword("DIMENS")) { + Opm::DeckKeywordConstPtr dimensKeyword = newParserDeck->getKeyword("DIMENS"); + dims[0] = dimensKeyword->getRecord(0)->getItem(0)->getInt(0); + dims[1] = dimensKeyword->getRecord(0)->getItem(1)->getInt(0); + dims[2] = dimensKeyword->getRecord(0)->getItem(2)->getInt(0); + } else if (newParserDeck->hasKeyword("SPECGRID")) { + Opm::DeckKeywordConstPtr specgridKeyword = newParserDeck->getKeyword("SPECGRID"); + dims[0] = specgridKeyword->getRecord(0)->getItem(0)->getInt(0); + dims[1] = specgridKeyword->getRecord(0)->getItem(1)->getInt(0); + dims[2] = specgridKeyword->getRecord(0)->getItem(2)->getInt(0); + } else { + OPM_THROW(std::runtime_error, "Deck must have either DIMENS or SPECGRID."); + } + + // Collect in input struct for preprocessing. + + grdecl.zcorn = &zcorn[0]; + grdecl.coord = &coord[0]; + grdecl.actnum = actnum; + grdecl.dims[0] = dims[0]; + grdecl.dims[1] = dims[1]; + grdecl.dims[2] = dims[2]; + + if (newParserDeck->hasKeyword("MAPAXES")) { + Opm::DeckKeywordConstPtr mapaxesKeyword = newParserDeck->getKeyword("MAPAXES"); + Opm::DeckRecordConstPtr mapaxesRecord = mapaxesKeyword->getRecord(0); + + // memleak alert: here we need to make sure that C code + // can properly take ownership of the grdecl.mapaxes + // object. if it is not freed, it will result in a + // memleak... + double *cWtfMapaxes = static_cast(malloc(sizeof(double)*mapaxesRecord->size())); + for (unsigned i = 0; i < mapaxesRecord->size(); ++i) + cWtfMapaxes[i] = mapaxesRecord->getItem(i)->getSIDouble(0); + grdecl.mapaxes = cWtfMapaxes; + } else + grdecl.mapaxes = NULL; + + } namespace { @@ -230,6 +316,71 @@ namespace Opm } } + void GridManager::initFromDeckTensorgrid(Opm::DeckConstPtr newParserDeck) + { + // Extract logical cartesian size. + std::array dims; + if (newParserDeck->hasKeyword("DIMENS")) { + Opm::DeckKeywordConstPtr dimensKeyword = newParserDeck->getKeyword("DIMENS"); + dims[0] = dimensKeyword->getRecord(0)->getItem(0)->getInt(0); + dims[1] = dimensKeyword->getRecord(0)->getItem(1)->getInt(0); + dims[2] = dimensKeyword->getRecord(0)->getItem(2)->getInt(0); + } else if (newParserDeck->hasKeyword("SPECGRID")) { + Opm::DeckKeywordConstPtr specgridKeyword = newParserDeck->getKeyword("SPECGRID"); + dims[0] = specgridKeyword->getRecord(0)->getItem(0)->getInt(0); + dims[1] = specgridKeyword->getRecord(0)->getItem(1)->getInt(0); + dims[2] = specgridKeyword->getRecord(0)->getItem(2)->getInt(0); + } else { + OPM_THROW(std::runtime_error, "Deck must have either DIMENS or SPECGRID."); + } + + // Extract coordinates (or offsets from top, in case of z). + const std::vector& dxv = newParserDeck->getKeyword("DXV")->getSIDoubleData(); + const std::vector& dyv = newParserDeck->getKeyword("DYV")->getSIDoubleData(); + const std::vector& dzv = newParserDeck->getKeyword("DZV")->getSIDoubleData(); + std::vector x = coordsFromDeltas(dxv); + std::vector y = coordsFromDeltas(dyv); + std::vector z = coordsFromDeltas(dzv); + + // Check that number of cells given are consistent with DIMENS/SPECGRID. + if (dims[0] != int(dxv.size())) { + OPM_THROW(std::runtime_error, "Number of DXV data points do not match DIMENS or SPECGRID."); + } + if (dims[1] != int(dyv.size())) { + OPM_THROW(std::runtime_error, "Number of DYV data points do not match DIMENS or SPECGRID."); + } + if (dims[2] != int(dzv.size())) { + OPM_THROW(std::runtime_error, "Number of DZV data points do not match DIMENS or SPECGRID."); + } + + // Extract top corner depths, if available. + const double* top_depths = 0; + std::vector top_depths_vec; + if (newParserDeck->hasKeyword("DEPTHZ")) { + const std::vector& depthz = newParserDeck->getKeyword("DEPTHZ")->getSIDoubleData(); + if (depthz.size() != x.size()*y.size()) { + OPM_THROW(std::runtime_error, "Incorrect size of DEPTHZ: " << depthz.size()); + } + top_depths = &depthz[0]; + } else if (newParserDeck->hasKeyword("TOPS")) { + // We only support constant values for TOPS. + // It is not 100% clear how we best can deal with + // varying TOPS (stair-stepping grid, or not). + const std::vector& tops = newParserDeck->getKeyword("TOPS")->getSIDoubleData(); + if (std::count(tops.begin(), tops.end(), tops[0]) != int(tops.size())) { + OPM_THROW(std::runtime_error, "We do not support nonuniform TOPS, please use ZCORN/COORDS instead."); + } + top_depths_vec.resize(x.size()*y.size(), tops[0]); + top_depths = &top_depths_vec[0]; + } + + // Construct grid. + ug_ = create_grid_tensor3d(dxv.size(), dyv.size(), dzv.size(), + &x[0], &y[0], &z[0], top_depths); + if (!ug_) { + OPM_THROW(std::runtime_error, "Failed to construct grid."); + } + } } // namespace Opm diff --git a/opm/core/grid/GridManager.hpp b/opm/core/grid/GridManager.hpp index 03666377..d900e585 100644 --- a/opm/core/grid/GridManager.hpp +++ b/opm/core/grid/GridManager.hpp @@ -20,10 +20,12 @@ #ifndef OPM_GRIDMANAGER_HEADER_INCLUDED #define OPM_GRIDMANAGER_HEADER_INCLUDED +#include + #include struct UnstructuredGrid; - +struct grdecl; namespace Opm { @@ -44,6 +46,9 @@ namespace Opm /// Construct a 3d corner-point grid or tensor grid from a deck. GridManager(const Opm::EclipseGridParser& deck); + /// Construct a 3d corner-point grid or tensor grid from a deck. + GridManager(Opm::DeckConstPtr newParserDeck); + /// Construct a 2d cartesian grid with cells of unit size. GridManager(int nx, int ny); @@ -72,6 +77,8 @@ namespace Opm /// to make it clear that we are returning a C-compatible struct. const UnstructuredGrid* c_grid() const; + static void createGrDecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl); + private: // Disable copying and assignment. GridManager(const GridManager& other); @@ -79,8 +86,10 @@ namespace Opm // Construct corner-point grid from deck. void initFromDeckCornerpoint(const Opm::EclipseGridParser& deck); + void initFromDeckCornerpoint(Opm::DeckConstPtr newParserDeck); // Construct tensor grid from deck. void initFromDeckTensorgrid(const Opm::EclipseGridParser& deck); + void initFromDeckTensorgrid(Opm::DeckConstPtr newParserDeck); // The managed UnstructuredGrid. UnstructuredGrid* ug_; From 71ab7bd33564b0d3f907133a1185f76fcf924c75 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 14:32:24 +0100 Subject: [PATCH 35/56] add back blank line as requested by @bska --- opm/core/props/phaseUsageFromDeck.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/opm/core/props/phaseUsageFromDeck.hpp b/opm/core/props/phaseUsageFromDeck.hpp index 2fc7f915..6ee66075 100644 --- a/opm/core/props/phaseUsageFromDeck.hpp +++ b/opm/core/props/phaseUsageFromDeck.hpp @@ -67,6 +67,7 @@ namespace Opm return pu; } + /// Looks at presence of WATER, OIL and GAS keywords in deck /// to determine active phases. inline PhaseUsage phaseUsageFromDeck(const EclipseGridParser& deck) From 099d6ee79758af1a872222b3973d4abc6522e0af Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 18:15:23 +0100 Subject: [PATCH 36/56] rename "createGrDecl" to "createGrdecl" --- opm/core/grid/GridManager.cpp | 4 ++-- opm/core/grid/GridManager.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/grid/GridManager.cpp b/opm/core/grid/GridManager.cpp index 2deaab44..392de07c 100644 --- a/opm/core/grid/GridManager.cpp +++ b/opm/core/grid/GridManager.cpp @@ -179,7 +179,7 @@ namespace Opm // Extract data from deck. // Collect in input struct for preprocessing. struct grdecl grdecl; - createGrDecl(newParserDeck, grdecl); + createGrdecl(newParserDeck, grdecl); // Process grid. ug_ = create_grid_cornerpoint(&grdecl, 0.0); @@ -188,7 +188,7 @@ namespace Opm } } - void GridManager::createGrDecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl) + void GridManager::createGrdecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl) { // Extract data from deck. const std::vector& zcorn = newParserDeck->getKeyword("ZCORN")->getSIDoubleData(); diff --git a/opm/core/grid/GridManager.hpp b/opm/core/grid/GridManager.hpp index d900e585..030d5555 100644 --- a/opm/core/grid/GridManager.hpp +++ b/opm/core/grid/GridManager.hpp @@ -77,7 +77,7 @@ namespace Opm /// to make it clear that we are returning a C-compatible struct. const UnstructuredGrid* c_grid() const; - static void createGrDecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl); + static void createGrdecl(Opm::DeckConstPtr newParserDeck, struct grdecl &grdecl); private: // Disable copying and assignment. From 4542a43dbf526f0ee7c8e437e5370a7a9e453494 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:03:21 +0100 Subject: [PATCH 37/56] add variants of all methods which take a deck of the new parser to the PVT properties --- opm/core/props/pvt/BlackoilPvtProperties.cpp | 81 +++++++++++++++++++- opm/core/props/pvt/BlackoilPvtProperties.hpp | 10 ++- opm/core/props/pvt/SinglePvtConstCompr.hpp | 31 +++++++- opm/core/props/pvt/SinglePvtDead.cpp | 24 ++++++ opm/core/props/pvt/SinglePvtDead.hpp | 4 + opm/core/props/pvt/SinglePvtDeadSpline.cpp | 45 +++++++++-- opm/core/props/pvt/SinglePvtDeadSpline.hpp | 7 +- opm/core/props/pvt/SinglePvtLiveGas.cpp | 25 +++++- opm/core/props/pvt/SinglePvtLiveGas.hpp | 4 + opm/core/props/pvt/SinglePvtLiveOil.cpp | 69 ++++++++++++++++- opm/core/props/pvt/SinglePvtLiveOil.hpp | 5 +- 11 files changed, 289 insertions(+), 16 deletions(-) diff --git a/opm/core/props/pvt/BlackoilPvtProperties.cpp b/opm/core/props/pvt/BlackoilPvtProperties.cpp index 217107cb..0fd7dda4 100644 --- a/opm/core/props/pvt/BlackoilPvtProperties.cpp +++ b/opm/core/props/pvt/BlackoilPvtProperties.cpp @@ -32,6 +32,9 @@ #include #include +#include +#include +#include namespace Opm { @@ -40,7 +43,6 @@ namespace Opm { } - void BlackoilPvtProperties::init(const EclipseGridParser& deck, const int samples) { // If we need multiple regions, this class and the SinglePvt* classes must change. @@ -113,6 +115,83 @@ namespace Opm } } + void BlackoilPvtProperties::init(Opm::DeckConstPtr newParserDeck, int samples) + { + // If we need multiple regions, this class and the SinglePvt* classes must change. + region_number_ = 0; + + phase_usage_ = phaseUsageFromDeck(newParserDeck); + + // Surface densities. Accounting for different orders in eclipse and our code. + if (newParserDeck->hasKeyword("DENSITY")) { + Opm::DeckKeywordConstPtr densityKeyword = newParserDeck->getKeyword("DENSITY"); + const std::vector& d = densityKeyword->getRecord(region_number_)->getItem(0)->getSIDoubleData(); + enum { ECL_oil = 0, ECL_water = 1, ECL_gas = 2 }; + if (phase_usage_.phase_used[Aqua]) { + densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; + } + if (phase_usage_.phase_used[Vapour]) { + densities_[phase_usage_.phase_pos[Vapour]] = d[ECL_gas]; + } + if (phase_usage_.phase_used[Liquid]) { + densities_[phase_usage_.phase_pos[Liquid]] = d[ECL_oil]; + } + } else { + OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); + } + + // Set the properties. + props_.resize(phase_usage_.num_phases); + // Water PVT + if (phase_usage_.phase_used[Aqua]) { + if (newParserDeck->hasKeyword("PVTW")) { + Opm::PvtwTable pvtwTable(newParserDeck->getKeyword("PVTW")); + + props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(pvtwTable)); + } else { + // Eclipse 100 default. + props_[phase_usage_.phase_pos[Aqua]].reset(new SinglePvtConstCompr(0.5*Opm::prefix::centi*Opm::unit::Poise)); + } + } + // Oil PVT + if (phase_usage_.phase_used[Liquid]) { + if (newParserDeck->hasKeyword("PVDO")) { + Opm::PvdoTable pvdoTable(newParserDeck->getKeyword("PVDO"), region_number_); + + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(pvdoTable, samples)); + } else if (newParserDeck->hasKeyword("PVTO")) { + Opm::PvtoTable pvtoTable(newParserDeck->getKeyword("PVTO"), /*tableIdx=*/0); + + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtLiveOil(pvtoTable)); + } else if (newParserDeck->hasKeyword("PVCDO")) { + Opm::PvdcoTable pvcdoTable(newParserDeck->getKeyword("PVCDO")); + + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtConstCompr(pvcdoTable)); + } else { + OPM_THROW(std::runtime_error, "Input is missing PVDO or PVTO\n"); + } + } + // Gas PVT + if (phase_usage_.phase_used[Vapour]) { + if (newParserDeck->hasKeyword("PVDG")) { + Opm::PvdgTable pvdgTable(newParserDeck->getKeyword("PVDG"), region_number_); + + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(pvdgTable, samples)); + } else if (newParserDeck->hasKeyword("PVTG")) { + Opm::PvtgTable pvtgTable(newParserDeck->getKeyword("PVTG"), /*tableIdx=*/0); + + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtLiveGas(pvtgTable)); + } else { + OPM_THROW(std::runtime_error, "Input is missing PVDG or PVTG\n"); + } + } + + // Must inform pvt property objects of phase structure. + for (int i = 0; i < phase_usage_.num_phases; ++i) { + props_[i]->setPhaseConfiguration(phase_usage_.num_phases, phase_usage_.phase_pos); + } + } + const double* BlackoilPvtProperties::surfaceDensities() const { return densities_; diff --git a/opm/core/props/pvt/BlackoilPvtProperties.hpp b/opm/core/props/pvt/BlackoilPvtProperties.hpp index 4ab60921..9b466b2d 100644 --- a/opm/core/props/pvt/BlackoilPvtProperties.hpp +++ b/opm/core/props/pvt/BlackoilPvtProperties.hpp @@ -20,11 +20,12 @@ #ifndef OPM_BLACKOILPVTPROPERTIES_HEADER_INCLUDED #define OPM_BLACKOILPVTPROPERTIES_HEADER_INCLUDED - - #include #include #include + +#include + #include #include @@ -55,6 +56,11 @@ namespace Opm /// data without fitting a spline. void init(const EclipseGridParser& deck, const int samples); + + /// Initialize from deck. + /// \param deck An input deck from the opm-parser module. + void init(Opm::DeckConstPtr deck, int samples); + /// \return Object describing the active phases. PhaseUsage phaseUsage() const; diff --git a/opm/core/props/pvt/SinglePvtConstCompr.hpp b/opm/core/props/pvt/SinglePvtConstCompr.hpp index e97a7242..d6c4d088 100644 --- a/opm/core/props/pvt/SinglePvtConstCompr.hpp +++ b/opm/core/props/pvt/SinglePvtConstCompr.hpp @@ -20,9 +20,12 @@ #ifndef OPM_SINGLEPVTCONSTCOMPR_HEADER_INCLUDED #define OPM_SINGLEPVTCONSTCOMPR_HEADER_INCLUDED - #include #include + +#include +#include + #include #include @@ -55,6 +58,32 @@ namespace Opm visc_comp_ = pvtw[region_number][4]; } + SinglePvtConstCompr(const Opm::PvtwTable &pvtwTable) + { + if (pvtwTable.numRows() != 1) + OPM_THROW(std::runtime_error, + "The table specified by the PVTW keyword is required" + "to exhibit exactly one row."); + ref_press_ = pvtwTable.getPressureColumn()[0]; + ref_B_ = pvtwTable.getFormationFactorColumn()[0]; + comp_ = pvtwTable.getCompressibilityColumn()[0]; + viscosity_ = pvtwTable.getViscosityColumn()[0]; + visc_comp_ = pvtwTable.getViscosibilityColumn()[0]; + } + + SinglePvtConstCompr(const Opm::PvdcoTable &pvdcoTable) + { + if (pvdcoTable.numRows() != 1) + OPM_THROW(std::runtime_error, + "The table specified by the PVDCO keyword is required" + "to exhibit exactly one row."); + ref_press_ = pvdcoTable.getPressureColumn()[0]; + ref_B_ = pvdcoTable.getFormationFactorColumn()[0]; + comp_ = pvdcoTable.getCompressibilityColumn()[0]; + viscosity_ = pvdcoTable.getViscosityColumn()[0]; + visc_comp_ = pvdcoTable.getViscosibilityColumn()[0]; + } + SinglePvtConstCompr(double visc) : ref_press_(0.0), ref_B_(1.0), diff --git a/opm/core/props/pvt/SinglePvtDead.cpp b/opm/core/props/pvt/SinglePvtDead.cpp index 2db23dbe..fb11d2de 100644 --- a/opm/core/props/pvt/SinglePvtDead.cpp +++ b/opm/core/props/pvt/SinglePvtDead.cpp @@ -62,6 +62,30 @@ namespace Opm // << "\n\nvisc\n\n" << viscosity_ << std::endl; } + /// Constructor + SinglePvtDead::SinglePvtDead(const Opm::PvdoTable& pvdoTable) + { + // Copy data + const std::vector& press = pvdoTable.getPressureColumn(); + const std::vector& b = pvdoTable.getFormationFactorColumn(); + const std::vector& visc = pvdoTable.getViscosityColumn(); + + const int sz = b.size(); + std::vector bInv(sz); + for (int i = 0; i < sz; ++i) { + bInv[i] = 1.0 / b[i]; + } + b_ = NonuniformTableLinear(press, bInv); + viscosity_ = NonuniformTableLinear(press, visc); + + // Dumping the created tables. +// static int count = 0; +// std::ofstream os((std::string("dump-") + boost::lexical_cast(count++)).c_str()); +// os.precision(15); +// os << "1/B\n\n" << one_over_B_ +// << "\n\nvisc\n\n" << viscosity_ << std::endl; + } + // Destructor SinglePvtDead::~SinglePvtDead() { diff --git a/opm/core/props/pvt/SinglePvtDead.hpp b/opm/core/props/pvt/SinglePvtDead.hpp index 2c40a951..703da0b8 100644 --- a/opm/core/props/pvt/SinglePvtDead.hpp +++ b/opm/core/props/pvt/SinglePvtDead.hpp @@ -23,6 +23,9 @@ #include #include + +#include + #include namespace Opm @@ -40,6 +43,7 @@ namespace Opm public: typedef std::vector > > table_t; SinglePvtDead(const table_t& pvd_table); + SinglePvtDead(const Opm::PvdoTable &pvdoTable); virtual ~SinglePvtDead(); /// Viscosity as a function of p and z. diff --git a/opm/core/props/pvt/SinglePvtDeadSpline.cpp b/opm/core/props/pvt/SinglePvtDeadSpline.cpp index a0873e0b..0dc3b560 100644 --- a/opm/core/props/pvt/SinglePvtDeadSpline.cpp +++ b/opm/core/props/pvt/SinglePvtDeadSpline.cpp @@ -20,6 +20,9 @@ #include "config.h" #include #include + +#include + #include // Extra includes for debug dumping of tables. @@ -29,7 +32,6 @@ namespace Opm { - //------------------------------------------------------------------------ // Member functions //------------------------------------------------------------------------- @@ -54,13 +56,42 @@ namespace Opm } buildUniformMonotoneTable(press, B_inv, samples, b_); buildUniformMonotoneTable(press, visc, samples, viscosity_); + } - // Dumping the created tables. -// static int count = 0; -// std::ofstream os((std::string("dump-") + boost::lexical_cast(count++)).c_str()); -// os.precision(15); -// os << "1/B\n\n" << one_over_B_ -// << "\n\nvisc\n\n" << viscosity_ << std::endl; + SinglePvtDeadSpline::SinglePvtDeadSpline(const Opm::PvdoTable &pvdoTable, int samples) + { + int numRows = pvdoTable.numRows(); + + // Copy data + const std::vector &press = pvdoTable.getPressureColumn(); + const std::vector &B = pvdoTable.getFormationFactorColumn(); + const std::vector &visc = pvdoTable.getViscosityColumn(); + + std::vector B_inv(numRows); + for (int i = 0; i < numRows; ++i) { + B_inv[i] = 1.0 / B[i]; + } + + buildUniformMonotoneTable(press, B_inv, samples, b_); + buildUniformMonotoneTable(press, visc, samples, viscosity_); + } + + SinglePvtDeadSpline::SinglePvtDeadSpline(const Opm::PvdgTable &pvdgTable, int samples) + { + int numRows = pvdgTable.numRows(); + + // Copy data + const std::vector &press = pvdgTable.getPressureColumn(); + const std::vector &B = pvdgTable.getFormationFactorColumn(); + const std::vector &visc = pvdgTable.getViscosityColumn(); + + std::vector B_inv(numRows); + for (int i = 0; i < numRows; ++i) { + B_inv[i] = 1.0 / B[i]; + } + + buildUniformMonotoneTable(press, B_inv, samples, b_); + buildUniformMonotoneTable(press, visc, samples, viscosity_); } // Destructor diff --git a/opm/core/props/pvt/SinglePvtDeadSpline.hpp b/opm/core/props/pvt/SinglePvtDeadSpline.hpp index 337c8a80..65bd2561 100644 --- a/opm/core/props/pvt/SinglePvtDeadSpline.hpp +++ b/opm/core/props/pvt/SinglePvtDeadSpline.hpp @@ -20,9 +20,12 @@ #ifndef OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED #define OPM_SINGLEPVTDEADSPLINE_HEADER_INCLUDED - #include #include + +#include +#include + #include namespace Opm @@ -41,6 +44,8 @@ namespace Opm typedef std::vector > > table_t; SinglePvtDeadSpline(const table_t& pvd_table, const int samples); + SinglePvtDeadSpline(const Opm::PvdoTable &pvdoTable, int samples); + SinglePvtDeadSpline(const Opm::PvdgTable &pvdgTable, int samples); virtual ~SinglePvtDeadSpline(); /// Viscosity as a function of p and z. diff --git a/opm/core/props/pvt/SinglePvtLiveGas.cpp b/opm/core/props/pvt/SinglePvtLiveGas.cpp index 6a43f475..098016aa 100644 --- a/opm/core/props/pvt/SinglePvtLiveGas.cpp +++ b/opm/core/props/pvt/SinglePvtLiveGas.cpp @@ -32,8 +32,9 @@ #include #include #include -#include +#include +#include namespace Opm { @@ -41,6 +42,7 @@ namespace Opm using Opm::linearInterpolation; using Opm::linearInterpolationDerivative; + //------------------------------------------------------------------------ // Member functions //------------------------------------------------------------------------- @@ -81,6 +83,27 @@ namespace Opm } } + SinglePvtLiveGas::SinglePvtLiveGas(const Opm::PvtgTable& pvtgTable) + { + // GAS, PVTG + saturated_gas_table_.resize(4); + saturated_gas_table_[0] = pvtgTable.getOuterTable()->getPressureColumn(); + saturated_gas_table_[1] = pvtgTable.getOuterTable()->getGasFormationFactorColumn(); + saturated_gas_table_[2] = pvtgTable.getOuterTable()->getGasViscosityColumn(); + saturated_gas_table_[3] = pvtgTable.getOuterTable()->getOilSolubilityColumn(); + + int sz = pvtgTable.getOuterTable()->numRows(); + undersat_gas_tables_.resize(sz); + for (int i=0; igetGasViscosityColumn(); + } + } + // Destructor SinglePvtLiveGas::~SinglePvtLiveGas() { diff --git a/opm/core/props/pvt/SinglePvtLiveGas.hpp b/opm/core/props/pvt/SinglePvtLiveGas.hpp index de595393..c09f8ffc 100644 --- a/opm/core/props/pvt/SinglePvtLiveGas.hpp +++ b/opm/core/props/pvt/SinglePvtLiveGas.hpp @@ -21,6 +21,9 @@ #define OPM_SINGLEPVTLIVEGAS_HEADER_INCLUDED #include + +#include + #include namespace Opm @@ -38,6 +41,7 @@ namespace Opm typedef std::vector > > table_t; SinglePvtLiveGas(const table_t& pvto); + SinglePvtLiveGas(const Opm::PvtgTable& pvtgTable); virtual ~SinglePvtLiveGas(); /// Viscosity as a function of p and z. diff --git a/opm/core/props/pvt/SinglePvtLiveOil.cpp b/opm/core/props/pvt/SinglePvtLiveOil.cpp index d18252ee..66f1036e 100644 --- a/opm/core/props/pvt/SinglePvtLiveOil.cpp +++ b/opm/core/props/pvt/SinglePvtLiveOil.cpp @@ -21,8 +21,8 @@ #include #include #include -#include +#include namespace Opm { @@ -31,7 +31,6 @@ namespace Opm using Opm::linearInterpolationDerivative; using Opm::tableIndex; - //------------------------------------------------------------------------ // Member functions //------------------------------------------------------------------------- @@ -102,7 +101,73 @@ namespace Opm undersat_oil_tables_[i][2].push_back(mu); } } + } + SinglePvtLiveOil::SinglePvtLiveOil(const Opm::PvtoTable &pvtoTable) + { + const auto saturatedPvto = pvtoTable.getOuterTable(); + + // OIL, PVTO + saturated_oil_table_.resize(4); + const int sz = saturatedPvto->numRows(); + for (int k=0; k<4; ++k) { + saturated_oil_table_[k].resize(sz); + } + for (int i=0; igetPressureColumn()[i]; // p + saturated_oil_table_[1][i] = 1.0/saturatedPvto->getOilFormationFactorColumn()[i]; // 1/Bo + saturated_oil_table_[2][i] = saturatedPvto->getOilViscosityColumn()[i]; // mu_o + saturated_oil_table_[3][i] = saturatedPvto->getGasSolubilityColumn()[i]; // Rs + } + + undersat_oil_tables_.resize(sz); + for (int i=0; inumRows(); + undersat_oil_tables_[i][0].resize(tsize); + undersat_oil_tables_[i][1].resize(tsize); + undersat_oil_tables_[i][2].resize(tsize); + for (int j=0; jgetPressureColumn()[j]; // p + undersat_oil_tables_[i][1][j] = 1.0/undersaturatedPvto->getOilFormationFactorColumn()[j]; // 1/Bo + undersat_oil_tables_[i][2][j] = undersaturatedPvto->getOilViscosityColumn()[j]; // mu_o + } + } + + // Complete undersaturated tables by extrapolating from existing data + // as is done in Eclipse and Mrst + int iNext = -1; + for (int i=0; i 1) { + continue; + } + // Look ahead for next record containing undersaturated data + if (iNext < i) { + iNext = i+1; + while (iNext > >::size_type sz_t; + for (sz_t j=1; j + +#include + #include namespace Opm @@ -39,6 +41,7 @@ namespace Opm typedef std::vector > > table_t; SinglePvtLiveOil(const table_t& pvto); + SinglePvtLiveOil(const Opm::PvtoTable &pvtoTable); virtual ~SinglePvtLiveOil(); /// Viscosity as a function of p and z. From ec4423cab709e8912b42969eda22ac333df44053 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 14:37:33 +0100 Subject: [PATCH 38/56] remove needless inclusion of thanks to @bska for catching this one... --- opm/core/props/pvt/SinglePvtLiveGas.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/opm/core/props/pvt/SinglePvtLiveGas.cpp b/opm/core/props/pvt/SinglePvtLiveGas.cpp index 098016aa..43debe7b 100644 --- a/opm/core/props/pvt/SinglePvtLiveGas.cpp +++ b/opm/core/props/pvt/SinglePvtLiveGas.cpp @@ -34,7 +34,6 @@ #include #include -#include namespace Opm { From dada2366cdd0e62aea0753077635ff8093f65a78 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:04:04 +0100 Subject: [PATCH 39/56] add variants of all methods which take a deck of the new parser to the rock properties --- opm/core/props/rock/RockCompressibility.cpp | 49 ++++ opm/core/props/rock/RockCompressibility.hpp | 6 + opm/core/props/rock/RockFromDeck.cpp | 250 ++++++++++++++++++++ opm/core/props/rock/RockFromDeck.hpp | 16 ++ 4 files changed, 321 insertions(+) diff --git a/opm/core/props/rock/RockCompressibility.cpp b/opm/core/props/rock/RockCompressibility.cpp index 7b0f69cb..5b990af1 100644 --- a/opm/core/props/rock/RockCompressibility.cpp +++ b/opm/core/props/rock/RockCompressibility.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include namespace Opm @@ -65,6 +67,53 @@ namespace Opm } } + RockCompressibility::RockCompressibility(Opm::DeckConstPtr newParserDeck) + : pref_(0.0), + rock_comp_(0.0) + { + if (newParserDeck->hasKeyword("ROCKTAB")) { + Opm::DeckKeywordConstPtr rtKeyword = newParserDeck->getKeyword("ROCKTAB"); + if (rtKeyword->size() != 1) + OPM_THROW(std::runtime_error, "Can only handle a single region in ROCKTAB."); + + std::vector rtColumnNames + { + "PRESSURE", + "POREVOL MULT", + "TRANSMISC MULT" + }; + if (newParserDeck->hasKeyword("RKTRMDIR")) + { + // the number of colums of the "ROCKTAB" keyword + // depends on the presence of the "RKTRMDIR" + // keyword. Messy stuff... + rtColumnNames.push_back("TRANSMISC MULT Y"); + rtColumnNames.push_back("TRANSMISC MULT Z"); + + // well, okay. we don't support non-isotropic + // transmiscibility multipliers yet + OPM_THROW(std::runtime_error, "Support for non-isotropic " + "transmiscibility multipliers is not implemented yet."); + }; + + Opm::SimpleTable rtTable(rtKeyword, rtColumnNames); + + p_ = rtTable.getColumn(0); + poromult_ = rtTable.getColumn(1); + transmult_ = rtTable.getColumn(2); + } else if (newParserDeck->hasKeyword("ROCK")) { + Opm::DeckKeywordConstPtr rockKeyword = newParserDeck->getKeyword("ROCK"); + if (rockKeyword->size() != 1) + OPM_THROW(std::runtime_error, "Can only handle a single region in ROCK."); + + Opm::DeckRecordConstPtr rockRecord = rockKeyword->getRecord(0); + pref_ = rockRecord->getItem(0)->getSIDouble(0); + rock_comp_ = rockRecord->getItem(1)->getSIDouble(0); + } else { + std::cout << "**** warning: no rock compressibility data found in deck (ROCK or ROCKTAB)." << std::endl; + } + } + bool RockCompressibility::isActive() const { return !p_.empty() || (rock_comp_ != 0.0); diff --git a/opm/core/props/rock/RockCompressibility.hpp b/opm/core/props/rock/RockCompressibility.hpp index 03567cae..bd375e1c 100644 --- a/opm/core/props/rock/RockCompressibility.hpp +++ b/opm/core/props/rock/RockCompressibility.hpp @@ -20,6 +20,8 @@ #ifndef OPM_ROCKCOMPRESSIBILITY_HEADER_INCLUDED #define OPM_ROCKCOMPRESSIBILITY_HEADER_INCLUDED +#include + #include namespace Opm @@ -35,6 +37,10 @@ namespace Opm /// Looks for the keywords ROCK and ROCKTAB. RockCompressibility(const EclipseGridParser& deck); + /// Construct from input deck. + /// Looks for the keywords ROCK and ROCKTAB. + RockCompressibility(Opm::DeckConstPtr newParserDeck); + /// Construct from parameters. /// Accepts the following parameters (with defaults). /// rock_compressibility_pref (100.0) [given in bar] diff --git a/opm/core/props/rock/RockFromDeck.cpp b/opm/core/props/rock/RockFromDeck.cpp index 73036343..1058fa99 100644 --- a/opm/core/props/rock/RockFromDeck.cpp +++ b/opm/core/props/rock/RockFromDeck.cpp @@ -21,6 +21,9 @@ #include "config.h" #include #include + +#include + #include namespace Opm @@ -37,6 +40,10 @@ namespace Opm PermeabilityKind fillTensor(const EclipseGridParser& parser, std::vector*>& tensor, std::array& kmap); + PermeabilityKind fillTensor(Opm::DeckConstPtr newParserDeck, + std::vector*>& tensor, + std::array& kmap); + } // anonymous namespace @@ -64,6 +71,15 @@ namespace Opm assignPermeability(deck, grid, perm_threshold); } + void RockFromDeck::init(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid) + { + assignPorosity(newParserDeck, grid); + permfield_valid_.assign(grid.number_of_cells, false); + const double perm_threshold = 0.0; // Maybe turn into parameter? + assignPermeability(newParserDeck, grid, perm_threshold); + } + void RockFromDeck::assignPorosity(const EclipseGridParser& parser, const UnstructuredGrid& grid) @@ -79,6 +95,21 @@ namespace Opm } } + void RockFromDeck::assignPorosity(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid) + { + porosity_.assign(grid.number_of_cells, 1.0); + const int* gc = grid.global_cell; + if (newParserDeck->hasKeyword("PORO")) { + const std::vector& poro = newParserDeck->getKeyword("PORO")->getSIDoubleData(); + for (int c = 0; c < int(porosity_.size()); ++c) { + const int deck_pos = (gc == NULL) ? c : gc[c]; + assert(0 <= c && c < (int) porosity_.size()); + assert(0 <= deck_pos && deck_pos < (int) poro.size()); + porosity_[c] = poro[deck_pos]; + } + } + } void RockFromDeck::assignPermeability(const EclipseGridParser& parser, @@ -135,6 +166,59 @@ namespace Opm } } + void RockFromDeck::assignPermeability(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + double perm_threshold) + { + const int dim = 3; + const int num_global_cells = grid.cartdims[0]*grid.cartdims[1]*grid.cartdims[2]; + const int nc = grid.number_of_cells; + + assert(num_global_cells > 0); + + permeability_.assign(dim * dim * nc, 0.0); + + std::vector*> tensor; + tensor.reserve(10); + + const std::vector zero(num_global_cells, 0.0); + tensor.push_back(&zero); + + std::array kmap; + PermeabilityKind pkind = fillTensor(newParserDeck, tensor, kmap); + if (pkind == Invalid) { + OPM_THROW(std::runtime_error, "Invalid permeability field."); + } + + // Assign permeability values only if such values are + // given in the input deck represented by 'newParserDeck'. In + // other words: Don't set any (arbitrary) default values. + // It is infinitely better to experience a reproducible + // crash than subtle errors resulting from a (poorly + // chosen) default value... + // + if (tensor.size() > 1) { + const int* gc = grid.global_cell; + int off = 0; + + for (int c = 0; c < nc; ++c, off += dim*dim) { + // SharedPermTensor K(dim, dim, &permeability_[off]); + int kix = 0; + const int glob = (gc == NULL) ? c : gc[c]; + + for (int i = 0; i < dim; ++i) { + for (int j = 0; j < dim; ++j, ++kix) { + // K(i,j) = (*tensor[kmap[kix]])[glob]; + permeability_[off + kix] = (*tensor[kmap[kix]])[glob]; + } + // K(i,i) = std::max(K(i,i), perm_threshold); + permeability_[off + 3*i + i] = std::max(permeability_[off + 3*i + i], perm_threshold); + } + + permfield_valid_[c] = std::vector::value_type(1); + } + } + } namespace { @@ -204,6 +288,72 @@ namespace Opm return retval; } + /// @brief + /// Classify and verify a given permeability specification + /// from a structural point of view. In particular, we + /// verify that there are no off-diagonal permeability + /// components such as @f$k_{xy}@f$ unless the + /// corresponding diagonal components are known as well. + /// + /// @param newParserDeck [in] + /// An Eclipse data parser capable of answering which + /// permeability components are present in a given input + /// deck. + /// + /// @return + /// An enum value with the following possible values: + /// ScalarPerm only one component was given. + /// DiagonalPerm more than one component given. + /// TensorPerm at least one cross-component given. + /// None no components given. + /// Invalid invalid set of components given. + PermeabilityKind classifyPermeability(Opm::DeckConstPtr newParserDeck) + { + const bool xx = newParserDeck->hasKeyword("PERMX" ); + const bool xy = newParserDeck->hasKeyword("PERMXY"); + const bool xz = newParserDeck->hasKeyword("PERMXZ"); + + const bool yx = newParserDeck->hasKeyword("PERMYX"); + const bool yy = newParserDeck->hasKeyword("PERMY" ); + const bool yz = newParserDeck->hasKeyword("PERMYZ"); + + const bool zx = newParserDeck->hasKeyword("PERMZX"); + const bool zy = newParserDeck->hasKeyword("PERMZY"); + const bool zz = newParserDeck->hasKeyword("PERMZ" ); + + int num_cross_comp = xy + xz + yx + yz + zx + zy; + int num_comp = xx + yy + zz + num_cross_comp; + PermeabilityKind retval = None; + if (num_cross_comp > 0) { + retval = TensorPerm; + } else { + if (num_comp == 1) { + retval = ScalarPerm; + } else if (num_comp >= 2) { + retval = DiagonalPerm; + } + } + + bool ok = true; + if (num_comp > 0) { + // At least one tensor component specified on input. + // Verify that any remaining components are OK from a + // structural point of view. In particular, there + // must not be any cross-components (e.g., k_{xy}) + // unless the corresponding diagonal component (e.g., + // k_{xx}) is present as well... + // + ok = xx || !(xy || xz || yx || zx) ; + ok = ok && (yy || !(yx || yz || xy || zy)); + ok = ok && (zz || !(zx || zy || xz || yz)); + } + if (!ok) { + retval = Invalid; + } + + return retval; + } + /// @brief /// Copy isotropic (scalar) permeability to other diagonal @@ -333,6 +483,106 @@ namespace Opm return kind; } + /// @brief + /// Extract pointers to appropriate tensor components from + /// input deck. The permeability tensor is, generally, + /// @code + /// [ kxx kxy kxz ] + /// K = [ kyx kyy kyz ] + /// [ kzx kzy kzz ] + /// @endcode + /// We store these values in a linear array using natural + /// ordering with the column index cycling the most rapidly. + /// In particular we use the representation + /// @code + /// [ 0 1 2 3 4 5 6 7 8 ] + /// K = [ kxx, kxy, kxz, kyx, kyy, kyz, kzx, kzy, kzz ] + /// @endcode + /// Moreover, we explicitly enforce symmetric tensors by + /// assigning + /// @code + /// 3 1 6 2 7 5 + /// kyx = kxy, kzx = kxz, kzy = kyz + /// @endcode + /// However, we make no attempt at enforcing positive + /// definite tensors. + /// + /// @param [in] parser + /// An Eclipse data parser capable of answering which + /// permeability components are present in a given input + /// deck as well as retrieving the numerical value of each + /// permeability component in each grid cell. + /// + /// @param [out] tensor + /// @param [out] kmap + PermeabilityKind fillTensor(Opm::DeckConstPtr newParserDeck, + std::vector*>& tensor, + std::array& kmap) + { + PermeabilityKind kind = classifyPermeability(newParserDeck); + if (kind == Invalid) { + OPM_THROW(std::runtime_error, "Invalid set of permeability fields given."); + } + assert(tensor.size() == 1); + for (int i = 0; i < 9; ++i) { kmap[i] = 0; } + + enum { xx, xy, xz, // 0, 1, 2 + yx, yy, yz, // 3, 4, 5 + zx, zy, zz }; // 6, 7, 8 + + // ----------------------------------------------------------- + // 1st row: [kxx, kxy, kxz] + if (newParserDeck->hasKeyword("PERMX" )) { + kmap[xx] = tensor.size(); + tensor.push_back(&newParserDeck->getKeyword("PERMX")->getSIDoubleData()); + + setScalarPermIfNeeded(kmap, xx, yy, zz); + } + if (newParserDeck->hasKeyword("PERMXY")) { + kmap[xy] = kmap[yx] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMXY")->getSIDoubleData()); + } + if (newParserDeck->hasKeyword("PERMXZ")) { + kmap[xz] = kmap[zx] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMXZ")->getSIDoubleData()); + } + + // ----------------------------------------------------------- + // 2nd row: [kyx, kyy, kyz] + if (newParserDeck->hasKeyword("PERMYX")) { + kmap[yx] = kmap[xy] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMYX")->getSIDoubleData()); + } + if (newParserDeck->hasKeyword("PERMY" )) { + kmap[yy] = tensor.size(); + tensor.push_back(&newParserDeck->getKeyword("PERMY")->getSIDoubleData()); + + setScalarPermIfNeeded(kmap, yy, zz, xx); + } + if (newParserDeck->hasKeyword("PERMYZ")) { + kmap[yz] = kmap[zy] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMYZ")->getSIDoubleData()); + } + + // ----------------------------------------------------------- + // 3rd row: [kzx, kzy, kzz] + if (newParserDeck->hasKeyword("PERMZX")) { + kmap[zx] = kmap[xz] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMZX")->getSIDoubleData()); + } + if (newParserDeck->hasKeyword("PERMZY")) { + kmap[zy] = kmap[yz] = tensor.size(); // Enforce symmetry. + tensor.push_back(&newParserDeck->getKeyword("PERMZY")->getSIDoubleData()); + } + if (newParserDeck->hasKeyword("PERMZ" )) { + kmap[zz] = tensor.size(); + tensor.push_back(&newParserDeck->getKeyword("PERMZ")->getSIDoubleData()); + + setScalarPermIfNeeded(kmap, zz, xx, yy); + } + return kind; + } + } // anonymous namespace } // namespace Opm diff --git a/opm/core/props/rock/RockFromDeck.hpp b/opm/core/props/rock/RockFromDeck.hpp index e277ee2a..b5e0fb4e 100644 --- a/opm/core/props/rock/RockFromDeck.hpp +++ b/opm/core/props/rock/RockFromDeck.hpp @@ -22,6 +22,9 @@ #include + +#include + #include struct UnstructuredGrid; @@ -43,6 +46,14 @@ namespace Opm void init(const EclipseGridParser& deck, const UnstructuredGrid& grid); + /// Initialize from deck and grid. + /// \param newParserDeck Deck produced by the opm-parser code + /// \param grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + void init(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid); + /// \return D, the number of spatial dimensions. Always 3 for deck input. int numDimensions() const { @@ -72,9 +83,14 @@ namespace Opm private: void assignPorosity(const EclipseGridParser& parser, const UnstructuredGrid& grid); + void assignPorosity(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid); void assignPermeability(const EclipseGridParser& parser, const UnstructuredGrid& grid, const double perm_threshold); + void assignPermeability(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + double perm_threshold); std::vector porosity_; std::vector permeability_; From a1076136875a0cb27c521e368415df97942e43ca Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 14:54:13 +0100 Subject: [PATCH 40/56] use the (new) RocktabTable class for rock compressibility thanks to @bska for pointing this out --- opm/core/props/rock/RockCompressibility.cpp | 32 ++++++++------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/opm/core/props/rock/RockCompressibility.cpp b/opm/core/props/rock/RockCompressibility.cpp index 5b990af1..0f2cefec 100644 --- a/opm/core/props/rock/RockCompressibility.cpp +++ b/opm/core/props/rock/RockCompressibility.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include #include @@ -76,31 +76,23 @@ namespace Opm if (rtKeyword->size() != 1) OPM_THROW(std::runtime_error, "Can only handle a single region in ROCKTAB."); - std::vector rtColumnNames + // the number of colums of the "ROCKTAB" keyword + // depends on the presence of the "RKTRMDIR" + // keyword. Messy stuff... + bool isDirectional = newParserDeck->hasKeyword("RKTRMDIR"); + if (isDirectional) { - "PRESSURE", - "POREVOL MULT", - "TRANSMISC MULT" - }; - if (newParserDeck->hasKeyword("RKTRMDIR")) - { - // the number of colums of the "ROCKTAB" keyword - // depends on the presence of the "RKTRMDIR" - // keyword. Messy stuff... - rtColumnNames.push_back("TRANSMISC MULT Y"); - rtColumnNames.push_back("TRANSMISC MULT Z"); - // well, okay. we don't support non-isotropic - // transmiscibility multipliers yet + // transmisibility multipliers yet OPM_THROW(std::runtime_error, "Support for non-isotropic " - "transmiscibility multipliers is not implemented yet."); + "transmisibility multipliers is not implemented yet."); }; - Opm::SimpleTable rtTable(rtKeyword, rtColumnNames); + Opm::RocktabTable rocktabTable(rtKeyword, isDirectional); - p_ = rtTable.getColumn(0); - poromult_ = rtTable.getColumn(1); - transmult_ = rtTable.getColumn(2); + p_ = rocktabTable.getPressureColumn(); + poromult_ = rocktabTable.getPoreVolumeMultiplierColumn(); + transmult_ = rocktabTable.getTransmisibilityMultiplierColumn(); } else if (newParserDeck->hasKeyword("ROCK")) { Opm::DeckKeywordConstPtr rockKeyword = newParserDeck->getKeyword("ROCK"); if (rockKeyword->size() != 1) From 927530a26ab24caadd4122b572b50bdfa8f8bed4 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 15:23:05 +0100 Subject: [PATCH 41/56] handle the ROCK keywords using the new opm-parser utility class --- opm/core/props/rock/RockCompressibility.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/opm/core/props/rock/RockCompressibility.cpp b/opm/core/props/rock/RockCompressibility.cpp index 0f2cefec..1144d701 100644 --- a/opm/core/props/rock/RockCompressibility.cpp +++ b/opm/core/props/rock/RockCompressibility.cpp @@ -26,6 +26,7 @@ #include #include +#include #include @@ -94,13 +95,12 @@ namespace Opm poromult_ = rocktabTable.getPoreVolumeMultiplierColumn(); transmult_ = rocktabTable.getTransmisibilityMultiplierColumn(); } else if (newParserDeck->hasKeyword("ROCK")) { - Opm::DeckKeywordConstPtr rockKeyword = newParserDeck->getKeyword("ROCK"); - if (rockKeyword->size() != 1) + Opm::RockTable rockTable(newParserDeck->getKeyword("ROCK")); + if (rockTable.numRows() != 1) OPM_THROW(std::runtime_error, "Can only handle a single region in ROCK."); - Opm::DeckRecordConstPtr rockRecord = rockKeyword->getRecord(0); - pref_ = rockRecord->getItem(0)->getSIDouble(0); - rock_comp_ = rockRecord->getItem(1)->getSIDouble(0); + pref_ = rockTable.getPressureColumn()[0]; + rock_comp_ = rockTable.getCompressibilityColumn()[0]; } else { std::cout << "**** warning: no rock compressibility data found in deck (ROCK or ROCKTAB)." << std::endl; } From 7f9e6895474aaf7d60647e6409f3a5176a1c9ea3 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 15:58:08 +0100 Subject: [PATCH 42/56] more typos thanks to @bska --- opm/core/props/rock/RockCompressibility.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/props/rock/RockCompressibility.cpp b/opm/core/props/rock/RockCompressibility.cpp index 1144d701..d1729533 100644 --- a/opm/core/props/rock/RockCompressibility.cpp +++ b/opm/core/props/rock/RockCompressibility.cpp @@ -84,16 +84,16 @@ namespace Opm if (isDirectional) { // well, okay. we don't support non-isotropic - // transmisibility multipliers yet + // transmissibility multipliers yet OPM_THROW(std::runtime_error, "Support for non-isotropic " - "transmisibility multipliers is not implemented yet."); + "transmissibility multipliers is not implemented yet."); }; Opm::RocktabTable rocktabTable(rtKeyword, isDirectional); p_ = rocktabTable.getPressureColumn(); poromult_ = rocktabTable.getPoreVolumeMultiplierColumn(); - transmult_ = rocktabTable.getTransmisibilityMultiplierColumn(); + transmult_ = rocktabTable.getTransmissibilityMultiplierColumn(); } else if (newParserDeck->hasKeyword("ROCK")) { Opm::RockTable rockTable(newParserDeck->getKeyword("ROCK")); if (rockTable.numRows() != 1) From 88541d87620032bfbe20679eb2257a5cfb49585c Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 7 Feb 2014 16:14:43 +0100 Subject: [PATCH 43/56] make the saturation functions work with opm-parser --- opm/core/props/satfunc/SatFuncBase.hpp | 141 ++++++ opm/core/props/satfunc/SatFuncSimple.cpp | 4 +- opm/core/props/satfunc/SatFuncSimple.hpp | 16 +- opm/core/props/satfunc/SatFuncStone2.hpp | 16 +- .../props/satfunc/SaturationPropsFromDeck.hpp | 27 ++ .../satfunc/SaturationPropsFromDeck_impl.hpp | 457 ++++++++++++++++++ opm/core/simulator/initState_impl.hpp | 136 +++++- 7 files changed, 775 insertions(+), 22 deletions(-) diff --git a/opm/core/props/satfunc/SatFuncBase.hpp b/opm/core/props/satfunc/SatFuncBase.hpp index 47bd31d1..268ecd9d 100644 --- a/opm/core/props/satfunc/SatFuncBase.hpp +++ b/opm/core/props/satfunc/SatFuncBase.hpp @@ -22,6 +22,11 @@ #include #include #include + +#include +#include +#include + #include namespace Opm @@ -78,6 +83,10 @@ namespace Opm const int table_num, const PhaseUsage phase_usg, const int samples); + void init(Opm::DeckConstPtr newParserDeck, + const int table_num, + const PhaseUsage phase_usg, + const int samples); void updateSatHyst(const double* s, const EPSTransforms* epst, const EPSTransforms* epst_hyst, @@ -248,6 +257,138 @@ namespace Opm } } + template + void SatFuncBase::init(Opm::DeckConstPtr newParserDeck, + const int table_num, + const PhaseUsage phase_usg, + const int samples) + { + phase_usage = phase_usg; + double swco = 0.0; + double swmax = 1.0; + if (phase_usage.phase_used[Aqua]) { + Opm::SwofTable swof(newParserDeck->getKeyword("SWOF"), table_num); + const std::vector& sw = swof.getSwColumn(); + const std::vector& krw = swof.getKrwColumn(); + const std::vector& krow = swof.getKrowColumn(); + const std::vector& pcow = swof.getPcowColumn(); + if (krw.front() != 0.0 || krow.back() != 0.0) { + OPM_THROW(std::runtime_error, "Error SWOF data - non-zero krw(swco) and/or krow(1-sor)"); + } + + // Extend the tables with constant values such that the + // derivatives at the endpoints are zero + int n = sw.size(); + std::vector sw_ex(n+2); + std::vector krw_ex(n+2); + std::vector krow_ex(n+2); + std::vector pcow_ex(n+2); + + extendTable(sw,sw_ex,1); + extendTable(krw,krw_ex,0); + extendTable(krow,krow_ex,0); + extendTable(pcow,pcow_ex,0); + + initializeTableType(krw_,sw_ex, krw_ex, samples); + initializeTableType(krow_,sw_ex, krow_ex, samples); + initializeTableType(pcow_,sw_ex, pcow_ex, samples); + + krocw_ = krow[0]; // At connate water -> ecl. SWOF + swco = sw[0]; + smin_[phase_usage.phase_pos[Aqua]] = sw[0]; + swmax = sw.back(); + smax_[phase_usage.phase_pos[Aqua]] = sw.back(); + + krwmax_ = krw.back(); + kromax_ = krow.front(); + swcr_ = swmax; + sowcr_ = 1.0 - swco; + krwr_ = krw.back(); + krorw_ = krow.front(); + for (std::vector::size_type i=1; i 0.0) { + swcr_ = sw[i-1]; + krorw_ = krow[i-1]; + break; + } + } + for (std::vector::size_type i=sw.size()-1; i>=1; --i) { + if (krow[i-1]> 0.0) { + sowcr_ = 1.0 - sw[i]; + krwr_ = krw[i]; + break; + } + } + } + if (phase_usage.phase_used[Vapour]) { + Opm::SgofTable sgof(newParserDeck->getKeyword("SGOF"), table_num); + const std::vector& sg = sgof.getSgColumn(); + const std::vector& krg = sgof.getKrgColumn(); + const std::vector& krog = sgof.getKrogColumn(); + const std::vector& pcog = sgof.getPcogColumn(); + + // Extend the tables with constant values such that the + // derivatives at the endpoints are zero + int n = sg.size(); + std::vector sg_ex(n+2); + std::vector krg_ex(n+2); + std::vector krog_ex(n+2); + std::vector pcog_ex(n+2); + + extendTable(sg,sg_ex,1); + extendTable(krg,krg_ex,0); + extendTable(krog,krog_ex,0); + extendTable(pcog,pcog_ex,0); + + initializeTableType(krg_,sg_ex, krg_ex, samples); + initializeTableType(krog_,sg_ex, krog_ex, samples); + initializeTableType(pcog_,sg_ex, pcog_ex, samples); + + smin_[phase_usage.phase_pos[Vapour]] = sg[0]; + if (std::fabs(sg.back() + swco - 1.0) > 1e-3) { + OPM_THROW(std::runtime_error, "Gas maximum saturation in SGOF table = " << sg.back() << + ", should equal (1.0 - connate water sat) = " << (1.0 - swco)); + } + smax_[phase_usage.phase_pos[Vapour]] = sg.back(); + smin_[phase_usage.phase_pos[Vapour]] = sg.front(); + krgmax_ = krg.back(); + + sgcr_ = sg.front(); + sogcr_ = 1.0 - sg.back(); + krgr_ = krg.back(); + krorg_ = krg.front(); + for (std::vector::size_type i=1; i 0.0) { + sgcr_ = sg[i-1]; + krorg_ = krog[i-1]; + break; + } + } + for (std::vector::size_type i=sg.size()-1; i>=1; --i) { + if (krog[i-1]> 0.0) { + sogcr_ = 1.0 - sg[i]; + krgr_ = krg[i]; + break; + } + } + + } + + if (phase_usage.phase_used[Vapour] && phase_usage.phase_used[Aqua]) { + sowcr_ -= smin_[phase_usage.phase_pos[Vapour]]; + sogcr_ -= smin_[phase_usage.phase_pos[Aqua]]; + smin_[phase_usage.phase_pos[Liquid]] = 0.0; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Aqua]] + - smin_[phase_usage.phase_pos[Vapour]]; // First entry in SGOF-table supposed to be zero anyway ... + } else if (phase_usage.phase_used[Aqua]) { + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - smax_[phase_usage.phase_pos[Aqua]]; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Aqua]]; + } else if (phase_usage.phase_used[Vapour]) { + smin_[phase_usage.phase_pos[Liquid]] = 1.0 - smax_[phase_usage.phase_pos[Vapour]]; + smax_[phase_usage.phase_pos[Liquid]] = 1.0 - smin_[phase_usage.phase_pos[Vapour]]; + } + } + template void SatFuncBase::updateSatHyst(const double* s, const EPSTransforms* epst, diff --git a/opm/core/props/satfunc/SatFuncSimple.cpp b/opm/core/props/satfunc/SatFuncSimple.cpp index 83a377ff..940f12de 100644 --- a/opm/core/props/satfunc/SatFuncSimple.cpp +++ b/opm/core/props/satfunc/SatFuncSimple.cpp @@ -36,7 +36,7 @@ namespace Opm void SatFuncBase >::initializeTableType(NonuniformTableLinear & table, const std::vector& arg, const std::vector& value, - const int /*samples*/) + const int samples) { table = NonuniformTableLinear(arg, value); } @@ -84,7 +84,7 @@ namespace Opm } } - double EPSTransforms::Transform::scaleSatDeriv(double s, double /*s_r*/, double /*s_cr*/, double /*s_max*/) const + double EPSTransforms::Transform::scaleSatDeriv(double s, double s_r, double s_cr, double s_max) const { if (doNotScale) { return 1.0; diff --git a/opm/core/props/satfunc/SatFuncSimple.hpp b/opm/core/props/satfunc/SatFuncSimple.hpp index f2d58b5a..b1781ded 100644 --- a/opm/core/props/satfunc/SatFuncSimple.hpp +++ b/opm/core/props/satfunc/SatFuncSimple.hpp @@ -31,23 +31,23 @@ namespace Opm void evalKrDeriv(const double* s, double* kr, double* dkrds) const; void evalPc(const double* s, double* pc) const; void evalPcDeriv(const double* s, double* pc, double* dpcds) const; - - void evalKr(const double* /*s*/, double* /*kr*/, const EPSTransforms* /*epst*/) const + + void evalKr(const double* s, double* kr, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncSimple -- need to be implemented ...");} - void evalKr(const double* /*s*/, double* /*kr*/, const EPSTransforms* /*epst*/, const EPSTransforms* /*epst_hyst*/, const SatHyst* /*sat_hyst*/) const + void evalKr(const double* s, double* kr, const EPSTransforms* epst, const EPSTransforms* epst_hyst, const SatHyst* sat_hyst) const {OPM_THROW(std::runtime_error, "SatFuncSimple -- need to be implemented ...");} void evalKrDeriv(const double* s, double* kr, double* dkrds, const EPSTransforms* epst) const; - void evalKrDeriv(const double* /*s*/, double* /*kr*/, double* /*dkrds*/, const EPSTransforms* /*epst*/, const EPSTransforms* /*epst_hyst*/, const SatHyst* /*sat_hyst*/) const + void evalKrDeriv(const double* s, double* kr, double* dkrds, const EPSTransforms* epst, const EPSTransforms* epst_hyst, const SatHyst* sat_hyst) const {OPM_THROW(std::runtime_error, "SatFuncSimple -- need to be implemented ...");} - void evalPc(const double* /*s*/, double* /*pc*/, const EPSTransforms* /*epst*/) const + void evalPc(const double* s, double* pc, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncSimple -- need to be implemented ...");} - void evalPcDeriv(const double* /*s*/, double* /*pc*/, double* /*dpcds*/, const EPSTransforms* /*epst*/) const + void evalPcDeriv(const double* s, double* pc, double* dpcds, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncSimple -- need to be implemented ...");} private: }; - + typedef SatFuncSimple > SatFuncSimpleUniform; typedef SatFuncSimple > SatFuncSimpleNonuniform; @@ -185,7 +185,7 @@ namespace Opm double dkrww = _dsdsw*epst->wat.scaleKrDeriv(s[wpos], this->krw_.derivative(_sw)); double krg = epst->gas.scaleKr(s[gpos], this->krg_(_sg), this->krgr_); double dkrgg = _dsdsg*epst->gas.scaleKrDeriv(s[gpos], this->krg_.derivative(_sg)); - // TODO Check the arguments to the krow- and krog-tables below... + // TODO Check the arguments to the krow- and krog-tables below... double krow = epst->watoil.scaleKr(1.0-s[wpos]-s[gpos], this->krow_(1.0-_sow-this->smin_[gpos]), this->krorw_); // ???? double dkrow = _dsdsow*epst->watoil.scaleKrDeriv(1.0-s[wpos]-s[gpos], this->krow_.derivative(1.0-_sow-this->smin_[gpos])); // ???? //double krog = epst->gasoil.scaleKr(this->krog_(1.0-_sog-this->smin_[wpos]), 1.0-s[wpos]-s[gpos], this->krorg_); // ???? diff --git a/opm/core/props/satfunc/SatFuncStone2.hpp b/opm/core/props/satfunc/SatFuncStone2.hpp index e22f1e9a..4eb5e2fd 100644 --- a/opm/core/props/satfunc/SatFuncStone2.hpp +++ b/opm/core/props/satfunc/SatFuncStone2.hpp @@ -31,25 +31,25 @@ namespace Opm void evalKrDeriv(const double* s, double* kr, double* dkrds) const; void evalPc(const double* s, double* pc) const; void evalPcDeriv(const double* s, double* pc, double* dpcds) const; - - void evalKr(const double* /*s*/, double* /*kr*/, const EPSTransforms* /*epst*/) const + + void evalKr(const double* s, double* kr, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - void evalKr(const double* /*s*/, double* /*kr*/, const EPSTransforms* /*epst*/, const EPSTransforms* /*epst_hyst*/, const SatHyst* /*sat_hyst*/) const + void evalKr(const double* s, double* kr, const EPSTransforms* epst, const EPSTransforms* epst_hyst, const SatHyst* sat_hyst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - void evalKrDeriv(const double* /*s*/, double* /*kr*/, double* /*dkrds*/, const EPSTransforms* /*epst*/) const + void evalKrDeriv(const double* s, double* kr, double* dkrds, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - void evalKrDeriv(const double* /*s*/, double* /*kr*/, double* /*dkrds*/, const EPSTransforms* /*epst*/, const EPSTransforms* /*epst_hyst*/, const SatHyst* /*sat_hyst*/) const + void evalKrDeriv(const double* s, double* kr, double* dkrds, const EPSTransforms* epst, const EPSTransforms* epst_hyst, const SatHyst* sat_hyst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - void evalPc(const double* /*s*/, double* /*pc*/, const EPSTransforms* /*epst*/) const + void evalPc(const double* s, double* pc, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - void evalPcDeriv(const double* /*s*/, double* /*pc*/, double* /*dpcds*/, const EPSTransforms* /*epst*/) const + void evalPcDeriv(const double* s, double* pc, double* dpcds, const EPSTransforms* epst) const {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} private: }; - + typedef SatFuncStone2 > SatFuncStone2Uniform; typedef SatFuncStone2 > SatFuncStone2Nonuniform; diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp index c321a42a..2c570ffd 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp @@ -27,6 +27,9 @@ #include #include #include + +#include + #include struct UnstructuredGrid; @@ -60,6 +63,17 @@ namespace Opm const UnstructuredGrid& grid, const int samples); + /// Initialize from deck and grid. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + /// \param[in] samples Number of uniform sample points for saturation tables. + /// NOTE: samples will only be used with the SatFuncSetUniform template argument. + void init(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const int samples); + /// \return P, the number of phases. int numPhases() const; @@ -132,6 +146,14 @@ namespace Opm const UnstructuredGrid& grid, const std::string& keyword, std::vector& scaleparam); + void initEPS(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid); + void initEPSHyst(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid); + void initEPSKey(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const std::string& keyword, + std::vector& scaleparam); void initEPSParam(const int cell, EPSTransforms::Transform& data, const bool oil, @@ -149,6 +171,11 @@ namespace Opm const std::vector& s0, const std::vector& krsr, const std::vector& krmax); + + bool columnIsMasked_(Opm::DeckConstPtr newParserDeck, + const std::string &keywordName, + int columnIdx) + { return newParserDeck->getKeyword(keywordName)->getRecord(0)->getItem(0)->getSIDouble(0) != -1.0; } }; diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp index c3006993..d9a3f32e 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp @@ -26,6 +26,11 @@ #include #include +#include +#include +#include +#include + #include namespace Opm @@ -140,6 +145,123 @@ namespace Opm } + /// Initialize from deck. + template + void SaturationPropsFromDeck::init(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const int samples) + { + phase_usage_ = phaseUsageFromDeck(newParserDeck); + + // Extract input data. + // Oil phase should be active. + if (!phase_usage_.phase_used[Liquid]) { + OPM_THROW(std::runtime_error, "SaturationPropsFromDeck::init() -- oil phase must be active."); + } + + // Obtain SATNUM, if it exists, and create cell_to_func_. + // Otherwise, let the cell_to_func_ mapping be just empty. + int satfuncs_expected = 1; + if (newParserDeck->hasKeyword("SATNUM")) { + const std::vector& satnum = newParserDeck->getKeyword("SATNUM")->getIntData(); + satfuncs_expected = *std::max_element(satnum.begin(), satnum.end()); + const int num_cells = grid.number_of_cells; + cell_to_func_.resize(num_cells); + const int* gc = grid.global_cell; + for (int cell = 0; cell < num_cells; ++cell) { + const int newParserDeck_pos = (gc == NULL) ? cell : gc[cell]; + cell_to_func_[cell] = satnum[newParserDeck_pos] - 1; + } + } + + // Find number of tables, check for consistency. + enum { Uninitialized = -1 }; + int num_tables = Uninitialized; + if (phase_usage_.phase_used[Aqua]) { + num_tables = newParserDeck->getKeyword("SWOF")->size(); + if (num_tables < satfuncs_expected) { + OPM_THROW(std::runtime_error, "Found " << num_tables << " SWOF tables, SATNUM specifies at least " << satfuncs_expected); + } + } + if (phase_usage_.phase_used[Vapour]) { + int num_sgof_tables = newParserDeck->getKeyword("SGOF")->size(); + if (num_sgof_tables < satfuncs_expected) { + OPM_THROW(std::runtime_error, "Found " << num_tables << " SGOF tables, SATNUM specifies at least " << satfuncs_expected); + } + if (num_tables == Uninitialized) { + num_tables = num_sgof_tables; + } else if (num_tables != num_sgof_tables) { + OPM_THROW(std::runtime_error, "Inconsistent number of tables in SWOF and SGOF."); + } + } + + // Initialize tables. + satfuncset_.resize(num_tables); + for (int table = 0; table < num_tables; ++table) { + satfuncset_[table].init(newParserDeck, table, phase_usage_, samples); + } + + // Saturation table scaling + do_hyst_ = false; + do_eps_ = false; + do_3pt_ = false; + if (newParserDeck->hasKeyword("ENDSCALE")) { + Opm::EndscaleWrapper endscale(newParserDeck->getKeyword("ENDSCALE")); + if (endscale.directionSwitch() != std::string("NODIR")) { + OPM_THROW(std::runtime_error, + "SaturationPropsFromDeck::init() -- ENDSCALE: " + "Currently only 'NODIR' accepted."); + } + if (endscale.isReversible()) { + OPM_THROW(std::runtime_error, + "SaturationPropsFromDeck::init() -- ENDSCALE: " + "Currently only 'REVERS' accepted."); + } + if (newParserDeck->hasKeyword("SCALECRS")) { + Opm::ScalecrsWrapper scalecrs(newParserDeck->getKeyword("SCALECRS")); + if (scalecrs.isEnabled()) { + do_3pt_ = true; + } + } + do_eps_ = true; + + initEPS(newParserDeck, grid); + + // For now, a primitive detection of hysteresis. TODO: SATOPTS HYSTER/ and EHYSTR + do_hyst_ = + newParserDeck->hasKeyword("ISWL") + || newParserDeck->hasKeyword("ISWU") + || newParserDeck->hasKeyword("ISWCR") + || newParserDeck->hasKeyword("ISGL") + || newParserDeck->hasKeyword("ISGU") + || newParserDeck->hasKeyword("ISGCR") + || newParserDeck->hasKeyword("ISOWCR") + || newParserDeck->hasKeyword("ISOGCR"); + if (do_hyst_) { + if (newParserDeck->hasKeyword("KRW") + || newParserDeck->hasKeyword("KRG") + || newParserDeck->hasKeyword("KRO") + || newParserDeck->hasKeyword("KRWR") + || newParserDeck->hasKeyword("KRGR") + || newParserDeck->hasKeyword("KRORW") + || newParserDeck->hasKeyword("KRORG") + || newParserDeck->hasKeyword("IKRW") + || newParserDeck->hasKeyword("IKRG") + || newParserDeck->hasKeyword("IKRO") + || newParserDeck->hasKeyword("IKRWR") + || newParserDeck->hasKeyword("IKRGR") + || newParserDeck->hasKeyword("IKRORW") + || newParserDeck->hasKeyword("IKRORG") ) { + OPM_THROW(std::runtime_error, + "SaturationPropsFromDeck::init() -- ENDSCALE: " + "Currently hysteresis and relperm value scaling " + "cannot be combined."); + } + initEPSHyst(newParserDeck, grid); + } + } + } + /// \return P, the number of phases. @@ -383,6 +505,69 @@ namespace Opm } } + // Initialize saturation scaling parameters + template + void SaturationPropsFromDeck::initEPS(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid) + { + std::vector swl, swcr, swu, sgl, sgcr, sgu, sowcr, sogcr; + std::vector krw, krg, kro, krwr, krgr, krorw, krorg; + // Initialize saturation scaling parameter + initEPSKey(newParserDeck, grid, std::string("SWL"), swl); + initEPSKey(newParserDeck, grid, std::string("SWU"), swu); + initEPSKey(newParserDeck, grid, std::string("SWCR"), swcr); + initEPSKey(newParserDeck, grid, std::string("SGL"), sgl); + initEPSKey(newParserDeck, grid, std::string("SGU"), sgu); + initEPSKey(newParserDeck, grid, std::string("SGCR"), sgcr); + initEPSKey(newParserDeck, grid, std::string("SOWCR"), sowcr); + initEPSKey(newParserDeck, grid, std::string("SOGCR"), sogcr); + initEPSKey(newParserDeck, grid, std::string("KRW"), krw); + initEPSKey(newParserDeck, grid, std::string("KRG"), krg); + initEPSKey(newParserDeck, grid, std::string("KRO"), kro); + initEPSKey(newParserDeck, grid, std::string("KRWR"), krwr); + initEPSKey(newParserDeck, grid, std::string("KRGR"), krgr); + initEPSKey(newParserDeck, grid, std::string("KRORW"), krorw); + initEPSKey(newParserDeck, grid, std::string("KRORG"), krorg); + + eps_transf_.resize(grid.number_of_cells); + + const int wpos = phase_usage_.phase_pos[BlackoilPhases::Aqua]; + const int gpos = phase_usage_.phase_pos[BlackoilPhases::Vapour]; + const bool oilWater = phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && !phase_usage_.phase_used[Vapour]; + const bool oilGas = !phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && phase_usage_.phase_used[Vapour]; + const bool threephase = phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && phase_usage_.phase_used[Vapour]; + + for (int cell = 0; cell < grid.number_of_cells; ++cell) { + if (oilWater) { + // ### krw + initEPSParam(cell, eps_transf_[cell].wat, false, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, funcForCell(cell).smax_[wpos], + funcForCell(cell).sowcr_, -1.0, funcForCell(cell).krwr_, funcForCell(cell).krwmax_, swl, swcr, swu, sowcr, sgl, krwr, krw); + // ### krow + initEPSParam(cell, eps_transf_[cell].watoil, true, 0.0, funcForCell(cell).sowcr_, funcForCell(cell).smin_[wpos], + funcForCell(cell).swcr_, -1.0, funcForCell(cell).krorw_, funcForCell(cell).kromax_, swl, sowcr, swl, swcr, sgl, krorw, kro); + } else if (oilGas) { + // ### krg + initEPSParam(cell, eps_transf_[cell].gas, false, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, funcForCell(cell).smax_[gpos], + funcForCell(cell).sogcr_, -1.0, funcForCell(cell).krgr_, funcForCell(cell).krgmax_, sgl, sgcr, sgu, sogcr, swl, krgr, krg); + // ### krog + initEPSParam(cell, eps_transf_[cell].gasoil, true, 0.0, funcForCell(cell).sogcr_, funcForCell(cell).smin_[gpos], + funcForCell(cell).sgcr_, -1.0, funcForCell(cell).krorg_, funcForCell(cell).kromax_, sgl, sogcr, sgl, sgcr, swl, krorg, kro); + } else if (threephase) { + // ### krw + initEPSParam(cell, eps_transf_[cell].wat, false, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, funcForCell(cell).smax_[wpos], funcForCell(cell).sowcr_, + funcForCell(cell).smin_[gpos], funcForCell(cell).krwr_, funcForCell(cell).krwmax_, swl, swcr, swu, sowcr, sgl, krwr, krw); + // ### krow + initEPSParam(cell, eps_transf_[cell].watoil, true, 0.0, funcForCell(cell).sowcr_, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, + funcForCell(cell).smin_[gpos], funcForCell(cell).krorw_, funcForCell(cell).kromax_, swl, sowcr, swl, swcr, sgl, krorw, kro); + // ### krg + initEPSParam(cell, eps_transf_[cell].gas, false, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, funcForCell(cell).smax_[gpos], funcForCell(cell).sogcr_, + funcForCell(cell).smin_[wpos], funcForCell(cell).krgr_, funcForCell(cell).krgmax_, sgl, sgcr, sgu, sogcr, swl, krgr, krg); + // ### krog + initEPSParam(cell, eps_transf_[cell].gasoil, true, 0.0, funcForCell(cell).sogcr_, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, + funcForCell(cell).smin_[wpos], funcForCell(cell).krorg_, funcForCell(cell).kromax_, sgl, sogcr, sgl, sgcr, swl, krorg, kro); + } + } + } // Initialize hysteresis saturation scaling parameters template @@ -450,6 +635,71 @@ namespace Opm } } + // Initialize hysteresis saturation scaling parameters + template + void SaturationPropsFromDeck::initEPSHyst(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid) + { + std::vector iswl, iswcr, iswu, isgl, isgcr, isgu, isowcr, isogcr; + std::vector ikrw, ikrg, ikro, ikrwr, ikrgr, ikrorw, ikrorg; + // Initialize hysteresis saturation scaling parameters + initEPSKey(newParserDeck, grid, std::string("ISWL"), iswl); + initEPSKey(newParserDeck, grid, std::string("ISWU"), iswu); + initEPSKey(newParserDeck, grid, std::string("ISWCR"), iswcr); + initEPSKey(newParserDeck, grid, std::string("ISGL"), isgl); + initEPSKey(newParserDeck, grid, std::string("ISGU"), isgu); + initEPSKey(newParserDeck, grid, std::string("ISGCR"), isgcr); + initEPSKey(newParserDeck, grid, std::string("ISOWCR"), isowcr); + initEPSKey(newParserDeck, grid, std::string("ISOGCR"), isogcr); + initEPSKey(newParserDeck, grid, std::string("IKRW"), ikrw); + initEPSKey(newParserDeck, grid, std::string("IKRG"), ikrg); + initEPSKey(newParserDeck, grid, std::string("IKRO"), ikro); + initEPSKey(newParserDeck, grid, std::string("IKRWR"), ikrwr); + initEPSKey(newParserDeck, grid, std::string("IKRGR"), ikrgr); + initEPSKey(newParserDeck, grid, std::string("IKRORW"), ikrorw); + initEPSKey(newParserDeck, grid, std::string("IKRORG"), ikrorg); + + eps_transf_hyst_.resize(grid.number_of_cells); + sat_hyst_.resize(grid.number_of_cells); + + const int wpos = phase_usage_.phase_pos[BlackoilPhases::Aqua]; + const int gpos = phase_usage_.phase_pos[BlackoilPhases::Vapour]; + const bool oilWater = phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && !phase_usage_.phase_used[Vapour]; + const bool oilGas = !phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && phase_usage_.phase_used[Vapour]; + const bool threephase = phase_usage_.phase_used[Aqua] && phase_usage_.phase_used[Liquid] && phase_usage_.phase_used[Vapour]; + + for (int cell = 0; cell < grid.number_of_cells; ++cell) { + if (oilWater) { + // ### krw + initEPSParam(cell, eps_transf_hyst_[cell].wat, false, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, funcForCell(cell).smax_[wpos], + funcForCell(cell).sowcr_, -1.0, funcForCell(cell).krwr_, funcForCell(cell).krwmax_, iswl, iswcr, iswu, isowcr, isgl, ikrwr, ikrw); + // ### krow + initEPSParam(cell, eps_transf_hyst_[cell].watoil, true, 0.0, funcForCell(cell).sowcr_, funcForCell(cell).smin_[wpos], + funcForCell(cell).swcr_, -1.0, funcForCell(cell).krorw_, funcForCell(cell).kromax_, iswl, isowcr, iswl, iswcr, isgl, ikrorw, ikro); + } else if (oilGas) { + // ### krg + initEPSParam(cell, eps_transf_hyst_[cell].gas, false, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, funcForCell(cell).smax_[gpos], + funcForCell(cell).sogcr_, -1.0, funcForCell(cell).krgr_, funcForCell(cell).krgmax_, isgl, isgcr, isgu, isogcr, iswl, ikrgr, ikrg); + // ### krog + initEPSParam(cell, eps_transf_hyst_[cell].gasoil, true, 0.0, funcForCell(cell).sogcr_, funcForCell(cell).smin_[gpos], + funcForCell(cell).sgcr_, -1.0, funcForCell(cell).krorg_, funcForCell(cell).kromax_, isgl, isogcr, isgl, isgcr, iswl, ikrorg, ikro); + } else if (threephase) { + // ### krw + initEPSParam(cell, eps_transf_hyst_[cell].wat, false, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, funcForCell(cell).smax_[wpos], funcForCell(cell).sowcr_, + funcForCell(cell).smin_[gpos], funcForCell(cell).krwr_, funcForCell(cell).krwmax_, iswl, iswcr, iswu, isowcr, isgl, ikrwr, ikrw); + // ### krow + initEPSParam(cell, eps_transf_hyst_[cell].watoil, true, 0.0, funcForCell(cell).sowcr_, funcForCell(cell).smin_[wpos], funcForCell(cell).swcr_, + funcForCell(cell).smin_[gpos], funcForCell(cell).krorw_, funcForCell(cell).kromax_, iswl, isowcr, iswl, iswcr, isgl, ikrorw, ikro); + // ### krg + initEPSParam(cell, eps_transf_hyst_[cell].gas, false, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, funcForCell(cell).smax_[gpos], funcForCell(cell).sogcr_, + funcForCell(cell).smin_[wpos], funcForCell(cell).krgr_, funcForCell(cell).krgmax_, isgl, isgcr, isgu, isogcr, iswl, ikrgr, ikrg); + // ### krog + initEPSParam(cell, eps_transf_hyst_[cell].gasoil, true, 0.0, funcForCell(cell).sogcr_, funcForCell(cell).smin_[gpos], funcForCell(cell).sgcr_, + funcForCell(cell).smin_[wpos], funcForCell(cell).krorg_, funcForCell(cell).kromax_, isgl, isogcr, isgl, isgcr, iswl, ikrorg, ikro); + } + } + } + // Initialize saturation scaling parameter template void SaturationPropsFromDeck::initEPSKey(const EclipseGridParser& deck, @@ -624,6 +874,213 @@ namespace Opm } } + // Initialize saturation scaling parameter + template + void SaturationPropsFromDeck::initEPSKey(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const std::string& keyword, + std::vector& scaleparam) + { + const bool useAqua = phase_usage_.phase_used[Aqua]; + const bool useLiquid = phase_usage_.phase_used[Liquid]; + const bool useVapour = phase_usage_.phase_used[Vapour]; + bool useKeyword = newParserDeck->hasKeyword(keyword); + bool hasENPTVD = newParserDeck->hasKeyword("ENPTVD"); + bool hasENKRVD = newParserDeck->hasKeyword("ENKRVD"); + int itab = 0; + std::vector > > table_dummy; + std::vector > >& table = table_dummy; + + // Active keyword assigned default values for each cell (in case of possible box-wise assignment) + int phase_pos_aqua = phase_usage_.phase_pos[BlackoilPhases::Aqua]; + int phase_pos_vapour = phase_usage_.phase_pos[BlackoilPhases::Vapour]; + if ((keyword[0] == 'S' && (useKeyword || hasENPTVD)) || (keyword[1] == 'S' && useKeyword) ) { + if (keyword == std::string("SWL") || keyword == std::string("ISWL") ) { + if (useAqua && (useKeyword || columnIsMasked_(newParserDeck, "ENPTVD", 0))) { + itab = 1; + scaleparam.resize(grid.number_of_cells); + for (int i=0; i 0) { + Opm::EnptvdTable enptvd(newParserDeck->getKeyword("ENPTVD")); + table.resize(1); // only one region supported so far + for (unsigned i = 0; i < table.size(); ++i) { + table[i].resize(9); + + for (int k = 0; k < enptvd.numRows(); ++k) { + table[i][0] = enptvd.getDepthColumn(); + table[i][1] = enptvd.getSwcoColumn(); + table[i][2] = enptvd.getSwcritColumn(); + table[i][3] = enptvd.getSwmaxColumn(); + table[i][4] = enptvd.getSgcoColumn(); + table[i][5] = enptvd.getSgcritColumn(); + table[i][6] = enptvd.getSgmaxColumn(); + table[i][7] = enptvd.getSowcritColumn(); + table[i][8] = enptvd.getSogcritColumn(); + } + } + } + } else if ((keyword[0] == 'K' && (useKeyword || hasENKRVD)) || (keyword[1] == 'K' && useKeyword) ) { + if (keyword == std::string("KRW") || keyword == std::string("IKRW") ) { + if (useAqua && (useKeyword || columnIsMasked_(newParserDeck, "ENKRVD", 0))) { + itab = 1; + scaleparam.resize(grid.number_of_cells); + for (int i=0; i 0) { + Opm::EnkrvdTable enkrvd(newParserDeck->getKeyword("ENKRVD")); + table.resize(1); // only one region supported so far + for (unsigned i = 0; i < table.size(); ++i) { + table[i].resize(8); + + for (int k = 0; k < enkrvd.numRows(); ++k) { + table[i][0] = enkrvd.getDepthColumn(); + table[i][1] = enkrvd.getKrwmaxColumn(); + table[i][2] = enkrvd.getKrgmaxColumn(); + table[i][3] = enkrvd.getKromaxColumn(); + table[i][4] = enkrvd.getKrwcritColumn(); + table[i][5] = enkrvd.getKrgcritColumn(); + table[i][6] = enkrvd.getKrocritgColumn(); + table[i][7] = enkrvd.getKrocritwColumn(); + } + } + } + } + + if (scaleparam.empty()) { + return; + } else if (useKeyword) { + // Keyword values from deck + std::cout << "--- Scaling parameter '" << keyword << "' assigned." << std::endl; + const int* gc = grid.global_cell; + const std::vector& val = newParserDeck->getKeyword(keyword)->getSIDoubleData(); + for (int c = 0; c < int(scaleparam.size()); ++c) { + const int deck_pos = (gc == NULL) ? c : gc[c]; + scaleparam[c] = val[deck_pos]; + } + } else { + // TODO for new parser + /* + std::cout << "--- Scaling parameter '" << keyword << "' assigned via "; + if (keyword[0] == 'S') + newParserDeck.getENPTVD().write(std::cout); + else + newParserDeck.getENKRVD().write(std::cout); + */ + const double* cc = grid.cell_centroids; + const int dim = grid.dimensions; + for (int cell = 0; cell < grid.number_of_cells; ++cell) { + int jtab = cell_to_func_.empty() ? 0 : cell_to_func_[cell]; + if (table[itab][jtab][0] != -1.0) { + std::vector& depth = table[0][jtab]; + std::vector& val = table[itab][jtab]; + double zc = cc[dim*cell+dim-1]; + if (zc >= depth.front() && zc <= depth.back()) { //don't want extrap outside depth interval + scaleparam[cell] = linearInterpolation(depth, val, zc); + } + } + } + } + } + // Saturation scaling template void SaturationPropsFromDeck::initEPSParam(const int cell, diff --git a/opm/core/simulator/initState_impl.hpp b/opm/core/simulator/initState_impl.hpp index 12dbe7b5..18372573 100644 --- a/opm/core/simulator/initState_impl.hpp +++ b/opm/core/simulator/initState_impl.hpp @@ -32,6 +32,8 @@ #include #include +#include + #include #include @@ -471,6 +473,103 @@ namespace Opm } + /// Initialize a state from input deck. + template + void initStateFromDeck(const UnstructuredGrid& grid, + const Props& props, + Opm::DeckConstPtr newParserDeck, + const double gravity, + State& state) + { + const int num_phases = props.numPhases(); + const PhaseUsage pu = phaseUsageFromDeck(newParserDeck); + if (num_phases != pu.num_phases) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): user specified property object with " << num_phases << " phases, " + "found " << pu.num_phases << " phases in deck."); + } + state.init(grid, num_phases); + if (newParserDeck->hasKeyword("EQUIL")) { + if (num_phases != 2) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios."); + } + if (pu.phase_used[BlackoilPhases::Vapour]) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): EQUIL-based init currently handling only oil-water scenario (no gas)."); + } + // Set saturations depending on oil-water contact. + EquilWrapper equil(newParserDeck->getKeyword("EQUIL")); + if (equil.numRegions() != 1) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): No region support yet."); + } + const double woc = equil.waterOilContactDepth(0); + initWaterOilContact(grid, props, woc, WaterBelow, state); + // Set pressure depending on densities and depths. + const double datum_z = equil.datumDepth(0); + const double datum_p = equil.datumDepthPressure(0); + initHydrostaticPressure(grid, props, woc, gravity, datum_z, datum_p, state); + } else if (newParserDeck->hasKeyword("PRESSURE")) { + // Set saturations from SWAT/SGAS, pressure from PRESSURE. + std::vector& s = state.saturation(); + std::vector& p = state.pressure(); + const std::vector& p_deck = newParserDeck->getKeyword("PRESSURE")->getSIDoubleData(); + const int num_cells = grid.number_of_cells; + if (num_phases == 2) { + if (!pu.phase_used[BlackoilPhases::Aqua]) { + // oil-gas: we require SGAS + if (!newParserDeck->hasKeyword("SGAS")) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): missing SGAS keyword in 2-phase init"); + } + const std::vector& sg_deck = newParserDeck->getKeyword("SGAS")->getSIDoubleData(); + const int gpos = pu.phase_pos[BlackoilPhases::Vapour]; + const int opos = pu.phase_pos[BlackoilPhases::Liquid]; + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + s[2*c + gpos] = sg_deck[c_deck]; + s[2*c + opos] = 1.0 - sg_deck[c_deck]; + p[c] = p_deck[c_deck]; + } + } else { + // water-oil or water-gas: we require SWAT + if (!newParserDeck->hasKeyword("SWAT")) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): missing SWAT keyword in 2-phase init"); + } + const std::vector& sw_deck = newParserDeck->getKeyword("SWAT")->getSIDoubleData(); + const int wpos = pu.phase_pos[BlackoilPhases::Aqua]; + const int nwpos = (wpos + 1) % 2; + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + s[2*c + wpos] = sw_deck[c_deck]; + s[2*c + nwpos] = 1.0 - sw_deck[c_deck]; + p[c] = p_deck[c_deck]; + } + } + } else if (num_phases == 3) { + const bool has_swat_sgas = newParserDeck->hasKeyword("SWAT") && newParserDeck->hasKeyword("SGAS"); + if (!has_swat_sgas) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): missing SGAS or SWAT keyword in 3-phase init."); + } + const int wpos = pu.phase_pos[BlackoilPhases::Aqua]; + const int gpos = pu.phase_pos[BlackoilPhases::Vapour]; + const int opos = pu.phase_pos[BlackoilPhases::Liquid]; + const std::vector& sw_deck = newParserDeck->getKeyword("SWAT")->getSIDoubleData(); + const std::vector& sg_deck = newParserDeck->getKeyword("SGAS")->getSIDoubleData(); + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + s[3*c + wpos] = sw_deck[c_deck]; + s[3*c + opos] = 1.0 - (sw_deck[c_deck] + sg_deck[c_deck]); + s[3*c + gpos] = sg_deck[c_deck]; + p[c] = p_deck[c_deck]; + } + } else { + OPM_THROW(std::runtime_error, "initStateFromDeck(): init with SWAT etc. only available with 2 or 3 phases."); + } + } else { + OPM_THROW(std::runtime_error, "initStateFromDeck(): we must either have EQUIL, or PRESSURE and SWAT/SOIL/SGAS."); + } + + // Finally, init face pressures. + initFacePressure(grid, state); + } + /// Initialize a state from input deck. template void initStateFromDeck(const UnstructuredGrid& grid, @@ -568,10 +667,6 @@ namespace Opm initFacePressure(grid, state); } - - - - /// Initialize surface volume from pressure and saturation by z = As. /// Here saturation is used as an intial guess for z in the /// computation of A. @@ -770,6 +865,39 @@ namespace Opm } } + /// Initialize a blackoil state from input deck. + template + void initBlackoilStateFromDeck(const UnstructuredGrid& grid, + const Props& props, + Opm::DeckConstPtr newParserDeck, + const double gravity, + State& state) + { + initStateFromDeck(grid, props, newParserDeck, gravity, state); + if (newParserDeck->hasKeyword("RS")) { + const std::vector& rs_deck = newParserDeck->getKeyword("RS")->getSIDoubleData(); + const int num_cells = grid.number_of_cells; + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + state.gasoilratio()[c] = rs_deck[c_deck]; + } + initBlackoilSurfvolUsingRSorRV(grid, props, state); + computeSaturation(props,state); + } else if (newParserDeck->hasKeyword("RV")){ + const std::vector& rv_deck = newParserDeck->getKeyword("RV")->getSIDoubleData(); + const int num_cells = grid.number_of_cells; + for (int c = 0; c < num_cells; ++c) { + int c_deck = (grid.global_cell == NULL) ? c : grid.global_cell[c]; + state.rv()[c] = rv_deck[c_deck]; + } + initBlackoilSurfvolUsingRSorRV(grid, props, state); + computeSaturation(props,state); + } + else { + OPM_THROW(std::runtime_error, "Temporarily, we require the RS or the RV field."); + } + } + } // namespace Opm From 6a1183dc33de98c63a144fa1bd3bb7238ca6719b Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 11 Feb 2014 14:12:36 +0100 Subject: [PATCH 44/56] Added support for creation of WellsGroup objects from new parser Well and Group objects --- CMakeLists_files.cmake | 24 ++++---- opm/core/wells/WellsGroup.cpp | 51 ++++++++++++++++ opm/core/wells/WellsGroup.hpp | 18 +++++- tests/test_wellsgroup.cpp | 108 ++++++++++++++++++++++++++++++++++ tests/wells_group.data | 61 +++++++++++++++++++ tests/wells_manager_data.data | 20 ++++++- 6 files changed, 268 insertions(+), 14 deletions(-) create mode 100644 tests/test_wellsgroup.cpp create mode 100755 tests/wells_group.data diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 891b094c..3bc43b3a 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -165,11 +165,12 @@ list (APPEND TEST_SOURCE_FILES tests/test_param.cpp tests/test_blackoilfluid.cpp tests/test_shadow.cpp - tests/test_units.cpp - tests/test_blackoilstate.cpp - tests/test_parser.cpp - tests/test_wellsmanager.cpp - tests/test_wellcontrols.cpp + tests/test_units.cpp + tests/test_blackoilstate.cpp + tests/test_parser.cpp + tests/test_wellsmanager.cpp + tests/test_wellcontrols.cpp + tests/test_wellsgroup.cpp ) # originally generated with the command: @@ -178,12 +179,13 @@ list (APPEND TEST_DATA_FILES tests/extratestdata.xml tests/testdata.xml tests/liveoil.DATA - tests/wetgas.DATA - tests/testBlackoilState1.DATA - tests/testBlackoilState2.DATA - tests/wells_manager_data.data - tests/wells_manager_data_expanded.data - tests/wells_manager_data_wellSTOP.data + tests/wetgas.DATA + tests/testBlackoilState1.DATA + tests/testBlackoilState2.DATA + tests/wells_manager_data.data + tests/wells_manager_data_expanded.data + tests/wells_manager_data_wellSTOP.data + tests/wells_group.data ) # originally generated with the command: diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index 6c1059a2..62663ce8 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -1141,4 +1141,55 @@ namespace Opm return return_value; } + + std::shared_ptr createGroupWellsGroup(GroupConstPtr group, size_t timeStep, const PhaseUsage& phase_usage ) + { + InjectionSpecification injection_specification; + if (group->isInjectionGroup(timeStep)) { + injection_specification.injector_type_ = toInjectorType(Phase::PhaseEnum2String(group->getInjectionPhase(timeStep))); + injection_specification.control_mode_ = toInjectionControlMode(GroupInjection::ControlEnum2String(group->getInjectionControlMode(timeStep))); + injection_specification.surface_flow_max_rate_ = group->getSurfaceMaxRate(timeStep); + injection_specification.reservoir_flow_max_rate_ = group->getReservoirMaxRate(timeStep); + injection_specification.reinjection_fraction_target_ = group->getTargetReinjectFraction(timeStep); + injection_specification.voidage_replacment_fraction_ = group->getTargetVoidReplacementFraction(timeStep); + } + ProductionSpecification production_specification; + if (group->isProductionGroup(timeStep)) { + production_specification.oil_max_rate_ = group->getOilTargetRate(timeStep); + production_specification.control_mode_ = toProductionControlMode(GroupProduction::ControlEnum2String(group->getProductionControlMode(timeStep))); + production_specification.water_max_rate_ = group->getWaterTargetRate(timeStep); + production_specification.gas_max_rate_ = group->getGasTargetRate(timeStep); + production_specification.liquid_max_rate_ = group->getLiquidTargetRate(timeStep); + production_specification.procedure_ = toProductionProcedure(GroupProductionExceedLimit::ActionEnum2String(group->getProductionExceedLimitAction(timeStep))); + production_specification.reservoir_flow_max_rate_ = group->getReservoirMaxRate(timeStep); + } + + std::shared_ptr wells_group(new WellsGroup(group->name(), production_specification, injection_specification, phase_usage)); + return wells_group; + } + + std::shared_ptr createWellWellsGroup(WellConstPtr well, size_t timeStep, const PhaseUsage& phase_usage ) + { + InjectionSpecification injection_specification; + ProductionSpecification production_specification; + if (well->isInjector(timeStep)) { + injection_specification.BHP_limit_ = well->getBHPLimit(timeStep); + injection_specification.injector_type_ = toInjectorType(WellInjector::Type2String(well->getInjectorType(timeStep))); + injection_specification.control_mode_ = toInjectionControlMode(WellInjector::ControlMode2String(well->getInjectorControlMode(timeStep))); + injection_specification.surface_flow_max_rate_ = well->getSurfaceInjectionRate(timeStep); + injection_specification.reservoir_flow_max_rate_ = well->getReservoirInjectionRate(timeStep); + production_specification.guide_rate_ = 0.0; // We know we're not a producer + } + + if (well->isProducer(timeStep)) { + production_specification.BHP_limit_ = well->getBHPLimit(timeStep); + production_specification.reservoir_flow_max_rate_ = well->getResVRate(timeStep); + production_specification.oil_max_rate_ = well->getOilRate(timeStep); + production_specification.control_mode_ = toProductionControlMode(WellProducer::ControlMode2String(well->getProducerControlMode(timeStep))); + production_specification.water_max_rate_ = well->getWaterRate(timeStep); + injection_specification.guide_rate_ = 0.0; // we know we're not an injector + } + std::shared_ptr wells_group(new WellNode(well->name(), production_specification, injection_specification, phase_usage)); + return wells_group; + } } diff --git a/opm/core/wells/WellsGroup.hpp b/opm/core/wells/WellsGroup.hpp index 143da935..ebf9d986 100644 --- a/opm/core/wells/WellsGroup.hpp +++ b/opm/core/wells/WellsGroup.hpp @@ -25,6 +25,10 @@ #include #include #include + +#include +#include + #include #include @@ -403,9 +407,21 @@ namespace Opm /// \param[in] name the name of the wells group. /// \param[in] deck the deck from which to fetch information. std::shared_ptr createWellsGroup(const std::string& name, - const EclipseGridParser& deck); + const EclipseGridParser& deck); + /// Creates the WellsGroupInterface for the given well + /// \param[in] well the Well to construct object for + /// \param[in] timeStep the time step in question + /// \param[in] the phase usage + std::shared_ptr createWellWellsGroup(WellConstPtr well, size_t timeStep, + const PhaseUsage& phase_usage ); + /// Creates the WellsGroupInterface for the given Group + /// \param[in] group the Group to construct object for + /// \param[in] timeStep the time step in question + /// \param[in] the phase usage + std::shared_ptr createGroupWellsGroup(GroupConstPtr group, size_t timeStep, + const PhaseUsage& phase_usage ); } #endif /* OPM_WELLSGROUP_HPP */ diff --git a/tests/test_wellsgroup.cpp b/tests/test_wellsgroup.cpp new file mode 100644 index 00000000..82c60163 --- /dev/null +++ b/tests/test_wellsgroup.cpp @@ -0,0 +1,108 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE WellsGroupTest + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +using namespace Opm; + +BOOST_AUTO_TEST_CASE(ConstructGroupFromWell) { + ParserPtr parser(new Parser()); + boost::filesystem::path scheduleFile("wells_group.data"); + DeckConstPtr deck = parser->parseFile(scheduleFile.string()); + EclipseStateConstPtr eclipseState(new EclipseState(deck)); + PhaseUsage pu = phaseUsageFromDeck(eclipseState); + + std::vector wells = eclipseState->getSchedule()->getWells(); + + for (size_t i=0; i wellsGroup = createWellWellsGroup(well, 2, pu); + BOOST_CHECK_EQUAL(well->name(), wellsGroup->name()); + if (well->isInjector(2)) { + BOOST_CHECK_EQUAL(well->getSurfaceInjectionRate(2), wellsGroup->injSpec().surface_flow_max_rate_); + BOOST_CHECK_EQUAL(well->getBHPLimit(2), wellsGroup->injSpec().BHP_limit_); + BOOST_CHECK_EQUAL(well->getReservoirInjectionRate(2), wellsGroup->injSpec().reservoir_flow_max_rate_); + BOOST_CHECK_EQUAL(0.0, wellsGroup->prodSpec().guide_rate_); + } + if (well->isProducer(2)) { + BOOST_CHECK_EQUAL(well->getResVRate(2), wellsGroup->prodSpec().reservoir_flow_max_rate_); + BOOST_CHECK_EQUAL(well->getBHPLimit(2), wellsGroup->prodSpec().BHP_limit_); + BOOST_CHECK_EQUAL(well->getOilRate(2), wellsGroup->prodSpec().oil_max_rate_); + BOOST_CHECK_EQUAL(well->getWaterRate(2), wellsGroup->prodSpec().water_max_rate_); + BOOST_CHECK_EQUAL(0.0, wellsGroup->injSpec().guide_rate_); + } + } +} + + +BOOST_AUTO_TEST_CASE(ConstructGroupFromGroup) { + ParserPtr parser(new Parser()); + boost::filesystem::path scheduleFile("wells_group.data"); + DeckConstPtr deck = parser->parseFile(scheduleFile.string()); + EclipseStateConstPtr eclipseState(new EclipseState(deck)); + PhaseUsage pu = phaseUsageFromDeck(eclipseState); + + std::vector nodes = eclipseState->getSchedule()->getGroupTree(2)->getNodes(); + + for (size_t i=0; igetSchedule()->getGroup(nodes[i]->name()); + std::shared_ptr wellsGroup = createGroupWellsGroup(group, 2, pu); + BOOST_CHECK_EQUAL(group->name(), wellsGroup->name()); + if (group->isInjectionGroup(2)) { + BOOST_CHECK_EQUAL(group->getSurfaceMaxRate(2), wellsGroup->injSpec().surface_flow_max_rate_); + BOOST_CHECK_EQUAL(group->getReservoirMaxRate(2), wellsGroup->injSpec().reservoir_flow_max_rate_); + BOOST_CHECK_EQUAL(group->getTargetReinjectFraction(2), wellsGroup->injSpec().reinjection_fraction_target_); + BOOST_CHECK_EQUAL(group->getTargetVoidReplacementFraction(2), wellsGroup->injSpec().voidage_replacment_fraction_); + } + if (group->isProductionGroup(2)) { + BOOST_CHECK_EQUAL(group->getReservoirMaxRate(2), wellsGroup->prodSpec().reservoir_flow_max_rate_); + BOOST_CHECK_EQUAL(group->getGasTargetRate(2), wellsGroup->prodSpec().gas_max_rate_); + BOOST_CHECK_EQUAL(group->getOilTargetRate(2), wellsGroup->prodSpec().oil_max_rate_); + BOOST_CHECK_EQUAL(group->getWaterTargetRate(2), wellsGroup->prodSpec().water_max_rate_); + BOOST_CHECK_EQUAL(group->getLiquidTargetRate(2), wellsGroup->prodSpec().liquid_max_rate_); + } + } +} + + + diff --git a/tests/wells_group.data b/tests/wells_group.data new file mode 100755 index 00000000..753d6031 --- /dev/null +++ b/tests/wells_group.data @@ -0,0 +1,61 @@ +OIL +GAS +WATER + +DIMENS + 10 10 5 / + +GRID + +DXV +10*1000.0 / + +DYV +10*1000.0 / + +DZV +10.0 20.0 30.0 10.0 5.0 / + +DEPTHZ +121*2000 +/ + +SCHEDULE + +GRUPTREE + 'G1' 'FIELD' / + 'G2' 'FIELD' / +/ + + +WELSPECS + 'INJ1' 'G1' 1 1 8335 'GAS' / + 'PROD1' 'G2' 10 10 8400 'OIL' / +/ + +TSTEP + 14.0 / +/ + +WELSPECS + 'INJ2' 'G1' 1 1 8335 'GAS' / + 'PROD2' 'G2' 10 10 8400 'OIL' / +/ + + +WCONINJE + 'INJ1' 'WATER' 'OPEN' 'RESV' 10 20 40 / + 'INJ2' 'WATER' 'OPEN' 'RESV' 10 20 40 / +/ + +WCONPROD + 'PROD1' 'OPEN' 'RESV' 999 3* 123 100 / + 'PROD2' 'OPEN' 'RESV' 999 3* 123 100 / +/ + +TSTEP + 3 / +/ + + +END diff --git a/tests/wells_manager_data.data b/tests/wells_manager_data.data index f22152c1..2df2dfd4 100755 --- a/tests/wells_manager_data.data +++ b/tests/wells_manager_data.data @@ -56,7 +56,23 @@ WCONINJE TSTEP -14.0 - / + 14.0 / +/ + + +WELSPECS + 'TEST1' 'G1' 1 1 8335 'GAS' / + 'TEST2' 'G2' 10 10 8400 'OIL' / +/ + +GRUPTREE + 'G1' 'SHIP' / + 'G2' 'SHIP' / +/ + +TSTEP + 3 / +/ + END From a8136610e0d9e465895ba397929bb3f781623b85 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 11 Feb 2014 14:21:15 +0100 Subject: [PATCH 45/56] Fixed copyright section --- tests/test_wellsgroup.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wellsgroup.cpp b/tests/test_wellsgroup.cpp index 82c60163..9ba9e0ef 100644 --- a/tests/test_wellsgroup.cpp +++ b/tests/test_wellsgroup.cpp @@ -1,5 +1,5 @@ /* - Copyright 2012 SINTEF ICT, Applied Mathematics. + Copyright 2014 Statoil. This file is part of the Open Porous Media project (OPM). From 0addefc3112065c3873e155da1fedbb36e42f8cb Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:00:03 +0100 Subject: [PATCH 46/56] add variants of all methods which take a deck of the new parser to the BlackOilPropertiesFromDeck --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 87 ++++++++++++++++++- opm/core/props/BlackoilPropertiesFromDeck.hpp | 29 ++++++- 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index e476f015..aedc8589 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -23,9 +23,7 @@ namespace Opm { - - BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, - const UnstructuredGrid& grid, + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, const UnstructuredGrid& grid, bool init_rock) { if (init_rock){ @@ -43,6 +41,25 @@ namespace Opm } } + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + bool init_rock) + { + if (init_rock){ + rock_.init(newParserDeck, grid); + } + pvt_.init(newParserDeck, /*numSamples=*/200); + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, /*numSamples=*/200); + + if (pvt_.numPhases() != satprops_->numPhases()) { + OPM_THROW(std::runtime_error, "BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); + } + } + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, const UnstructuredGrid& grid, const parameter::ParameterGroup& param, @@ -107,6 +124,70 @@ namespace Opm } } + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const parameter::ParameterGroup& param, + bool init_rock) + { + if(init_rock){ + rock_.init(newParserDeck, grid); + } + + const int pvt_samples = param.getDefault("pvt_tab_size", 200); + pvt_.init(newParserDeck, pvt_samples); + + // Unfortunate lack of pointer smartness here... + const int sat_samples = param.getDefault("sat_tab_size", 200); + std::string threephase_model = param.getDefault("threephase_model", "simple"); + if (newParserDeck->hasKeyword("ENDSCALE") && threephase_model != "simple") { + OPM_THROW(std::runtime_error, "Sorry, end point scaling currently available for the 'simple' model only."); + } + if (sat_samples > 1) { + if (threephase_model == "stone2") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else if (threephase_model == "simple") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else if (threephase_model == "gwseg") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else { + OPM_THROW(std::runtime_error, "Unknown threephase_model: " << threephase_model); + } + } else { + if (threephase_model == "stone2") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else if (threephase_model == "simple") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else if (threephase_model == "gwseg") { + SaturationPropsFromDeck* ptr + = new SaturationPropsFromDeck(); + satprops_.reset(ptr); + ptr->init(newParserDeck, grid, sat_samples); + } else { + OPM_THROW(std::runtime_error, "Unknown threephase_model: " << threephase_model); + } + } + + if (pvt_.numPhases() != satprops_->numPhases()) { + OPM_THROW(std::runtime_error, "BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" + << pvt_.numPhases() << ") and saturation-dependent function data (" << satprops_->numPhases() << ")."); + } + } + BlackoilPropertiesFromDeck::~BlackoilPropertiesFromDeck() { } diff --git a/opm/core/props/BlackoilPropertiesFromDeck.hpp b/opm/core/props/BlackoilPropertiesFromDeck.hpp index 49072b29..5c74a76a 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.hpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.hpp @@ -27,6 +27,9 @@ #include #include #include + +#include + #include struct UnstructuredGrid; @@ -44,7 +47,15 @@ namespace Opm /// \param[in] grid Grid to which property object applies, needed for the /// mapping from cell indices (typically from a processed grid) /// to logical cartesian indices consistent with the deck. - BlackoilPropertiesFromDeck(const EclipseGridParser& deck, + BlackoilPropertiesFromDeck(const EclipseGridParser &deck, + const UnstructuredGrid& grid, bool init_rock=true ); + + /// Initialize from deck and grid. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + BlackoilPropertiesFromDeck(Opm::DeckConstPtr newParserDeck, const UnstructuredGrid& grid, bool init_rock=true ); /// Initialize from deck, grid and parameters. @@ -63,6 +74,22 @@ namespace Opm const parameter::ParameterGroup& param, bool init_rock=true); + /// Initialize from deck, grid and parameters. + /// \param[in] deck Deck input parser + /// \param[in] grid Grid to which property object applies, needed for the + /// mapping from cell indices (typically from a processed grid) + /// to logical cartesian indices consistent with the deck. + /// \param[in] param Parameters. Accepted parameters include: + /// pvt_tab_size (200) number of uniform sample points for dead-oil pvt tables. + /// sat_tab_size (200) number of uniform sample points for saturation tables. + /// threephase_model("simple") three-phase relperm model (accepts "simple" and "stone2"). + /// For both size parameters, a 0 or negative value indicates that no spline fitting is to + /// be done, and the input fluid data used directly for linear interpolation. + BlackoilPropertiesFromDeck(Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid, + const parameter::ParameterGroup& param, + bool init_rock=true); + /// Destructor. virtual ~BlackoilPropertiesFromDeck(); From 3163865b2ecede7316c262b1c13408890dd7476d Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 6 Feb 2014 11:51:14 +0100 Subject: [PATCH 47/56] add back a newline which went MIA this confused the heck out of us during review. thanks to @bska for stumbling over it... --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index aedc8589..2eee1934 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -23,7 +23,8 @@ namespace Opm { - BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, const UnstructuredGrid& grid, + BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck(const EclipseGridParser& deck, + const UnstructuredGrid& grid, bool init_rock) { if (init_rock){ From 36348a8f8aaa39d0376c64a9e806b01866c61dae Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 12 Feb 2014 15:39:57 +0100 Subject: [PATCH 48/56] Create WellsGroupInterface from opm-parser Well/Group objects --- CMakeLists_files.cmake | 1 + opm/core/wells/WellCollection.cpp | 82 ++++++++++++++++++++++++++++ opm/core/wells/WellCollection.hpp | 30 ++++++++++- opm/core/wells/WellsGroup.cpp | 2 +- opm/core/wells/WellsGroup.hpp | 2 +- tests/test_wellcollection.cpp | 90 +++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 tests/test_wellcollection.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 3bc43b3a..75671e11 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -171,6 +171,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_wellsmanager.cpp tests/test_wellcontrols.cpp tests/test_wellsgroup.cpp + tests/test_wellcollection.cpp ) # originally generated with the command: diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index 350d2ffb..0cc7a9ce 100644 --- a/opm/core/wells/WellCollection.cpp +++ b/opm/core/wells/WellCollection.cpp @@ -19,10 +19,92 @@ #include "config.h" #include + +#include +#include + #include namespace Opm { + void WellCollection::addChild(GroupConstPtr groupChild, GroupConstPtr groupParent, + size_t timeStep, const PhaseUsage& phaseUsage) { + WellsGroupInterface* parent = findNode(groupParent->name()); + if (!parent) { + roots_.push_back(createGroupWellsGroup(groupParent, timeStep, phaseUsage)); + parent = roots_[roots_.size() - 1].get(); + } + + std::shared_ptr child; + + for (size_t i = 0; i < roots_.size(); ++i) { + if (roots_[i]->name() == groupChild->name()) { + child = roots_[i]; + // We've found a new parent to the previously thought root, need to remove it + for(size_t j = i; j < roots_.size() - 1; ++j) { + roots_[j] = roots_[j+1]; + } + + roots_.resize(roots_.size()-1); + break; + } + } + if (!child.get()) { + child = createGroupWellsGroup(groupChild, timeStep, phaseUsage); + } + + WellsGroup* parent_as_group = static_cast (parent); + if (!parent_as_group) { + OPM_THROW(std::runtime_error, "Trying to add child to group named " << groupParent->name() << ", but it's not a group."); + } + parent_as_group->addChild(child); + + if(child->isLeafNode()) { + leaf_nodes_.push_back(static_cast(child.get())); + } + + child->setParent(parent); + } + + void WellCollection::addChild(WellConstPtr wellChild, GroupConstPtr groupParent, + size_t timeStep, const PhaseUsage& phaseUsage) { + WellsGroupInterface* parent = findNode(groupParent->name()); + if (!parent) { + roots_.push_back(createGroupWellsGroup(groupParent, timeStep, phaseUsage)); + parent = roots_[roots_.size() - 1].get(); + } + + std::shared_ptr child; + + for (size_t i = 0; i < roots_.size(); ++i) { + if (roots_[i]->name() == wellChild->name()) { + child = roots_[i]; + // We've found a new parent to the previously thought root, need to remove it + for(size_t j = i; j < roots_.size() - 1; ++j) { + roots_[j] = roots_[j+1]; + } + + roots_.resize(roots_.size()-1); + break; + } + } + if (!child.get()) { + child = createWellWellsGroup(wellChild, timeStep, phaseUsage); + } + + WellsGroup* parent_as_group = static_cast (parent); + if (!parent_as_group) { + OPM_THROW(std::runtime_error, "Trying to add child to group named " << groupParent->name() << ", but it's not a group."); + } + parent_as_group->addChild(child); + + if(child->isLeafNode()) { + leaf_nodes_.push_back(static_cast(child.get())); + } + + child->setParent(parent); + } + void WellCollection::addChild(const std::string& child_name, const std::string& parent_name, diff --git a/opm/core/wells/WellCollection.hpp b/opm/core/wells/WellCollection.hpp index 9cdb9e7f..8933ff03 100644 --- a/opm/core/wells/WellCollection.hpp +++ b/opm/core/wells/WellCollection.hpp @@ -23,10 +23,15 @@ #define OPM_WELLCOLLECTION_HPP #include +#include + #include #include #include -#include +#include + +#include +#include namespace Opm { @@ -34,6 +39,29 @@ namespace Opm class WellCollection { public: + + /// Adds and creates if necessary the child to the collection + /// and appends it to parent's children. Also adds and creates the parent + /// if necessary. + /// \param[in] wellChild the child well + /// \param[in] groupParent the parent group + /// \param[in] phaseUsage the phase usage + /// \param[in] timeStep the current time step + void addChild(WellConstPtr wellChild, GroupConstPtr groupParent, + size_t timeStep, const PhaseUsage& phaseUsage); + + + /// Adds and creates if necessary the child to the collection + /// and appends it to parent's children. Also adds and creates the parent + /// if necessary. + /// \param[in] groupChild the child group + /// \param[in] groupParent the parent group + /// \param[in] phaseUsage the phase usage + /// \param[in] timeStep the current time step + void addChild(GroupConstPtr groupChild, GroupConstPtr groupParent, + size_t timeStep, const PhaseUsage& phaseUsage); + + /// Adds and creates if necessary the child to the collection /// and appends it to parent's children. Also adds and creates the parent /// if necessary. diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index 62663ce8..1087afcf 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -74,7 +74,7 @@ namespace Opm { return parent_; } - const std::string& WellsGroupInterface::name() + const std::string& WellsGroupInterface::name() const { return name_; } diff --git a/opm/core/wells/WellsGroup.hpp b/opm/core/wells/WellsGroup.hpp index ebf9d986..03a61d5e 100644 --- a/opm/core/wells/WellsGroup.hpp +++ b/opm/core/wells/WellsGroup.hpp @@ -62,7 +62,7 @@ namespace Opm virtual ~WellsGroupInterface(); /// The unique identifier for the well or well group. - const std::string& name(); + const std::string& name() const; /// Production specifications for the well or well group. const ProductionSpecification& prodSpec() const; diff --git a/tests/test_wellcollection.cpp b/tests/test_wellcollection.cpp new file mode 100644 index 00000000..0c12af44 --- /dev/null +++ b/tests/test_wellcollection.cpp @@ -0,0 +1,90 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + 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 NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE WellCollectionTest +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Opm; + +BOOST_AUTO_TEST_CASE(AddWellsAndGroupToCollection) { + ParserPtr parser(new Parser()); + boost::filesystem::path scheduleFile("wells_group.data"); + DeckConstPtr deck = parser->parseFile(scheduleFile.string()); + EclipseStateConstPtr eclipseState(new EclipseState(deck)); + PhaseUsage pu = phaseUsageFromDeck(eclipseState); + + GroupTreeNodePtr field=eclipseState->getSchedule()->getGroupTree(2)->getNode("FIELD"); + GroupTreeNodePtr g1=eclipseState->getSchedule()->getGroupTree(2)->getNode("G1"); + GroupTreeNodePtr g2=eclipseState->getSchedule()->getGroupTree(2)->getNode("G2"); + + WellCollection collection; + + // Add groups to WellCollection + GroupConstPtr fieldGroup = eclipseState->getSchedule()->getGroup(field->name()); + for (auto iter = field->begin(); iter != field->end(); ++iter) { + GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); + collection.addChild(childGroupNode, fieldGroup, 2, pu); + } + + GroupConstPtr g1Group = eclipseState->getSchedule()->getGroup(g1->name()); + for (auto iter = g1->begin(); iter != g1->end(); ++iter) { + GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); + collection.addChild(childGroupNode, g1Group, 2, pu); + } + + + GroupConstPtr g2Group = eclipseState->getSchedule()->getGroup(g2->name()); + for (auto iter = g2->begin(); iter != g2->end(); ++iter) { + GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); + collection.addChild(childGroupNode, g2Group, 2, pu); + } + + BOOST_CHECK_EQUAL("FIELD", collection.findNode("FIELD")->name()); + BOOST_CHECK_EQUAL("FIELD", collection.findNode("G1")->getParent()->name()); + BOOST_CHECK_EQUAL("FIELD", collection.findNode("G2")->getParent()->name()); + + // Add wells to WellCollection + WellCollection wellCollection; + std::vector wells = eclipseState->getSchedule()->getWells(); + for (size_t i=0; igetSchedule()->getGroup(wells[i]->getGroupName(2)); + collection.addChild(wells[i], parentGroup, 2, pu); + } + + BOOST_CHECK_EQUAL("G1", collection.findNode("INJ1")->getParent()->name()); + BOOST_CHECK_EQUAL("G1", collection.findNode("INJ2")->getParent()->name()); + BOOST_CHECK_EQUAL("G2", collection.findNode("PROD1")->getParent()->name()); + BOOST_CHECK_EQUAL("G2", collection.findNode("PROD2")->getParent()->name()); +} + From 663eb5a56d3000e38fabfdad7269f6de1d23715c Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 12 Feb 2014 15:45:06 +0100 Subject: [PATCH 49/56] Extracted common functionality from the three create functions --- opm/core/wells/WellCollection.cpp | 58 +++++++++++-------------------- opm/core/wells/WellCollection.hpp | 1 + 2 files changed, 21 insertions(+), 38 deletions(-) diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index 0cc7a9ce..260c156f 100644 --- a/opm/core/wells/WellCollection.cpp +++ b/opm/core/wells/WellCollection.cpp @@ -35,20 +35,8 @@ namespace Opm parent = roots_[roots_.size() - 1].get(); } - std::shared_ptr child; + std::shared_ptr child = getAndUnRootChild(groupChild->name()); - for (size_t i = 0; i < roots_.size(); ++i) { - if (roots_[i]->name() == groupChild->name()) { - child = roots_[i]; - // We've found a new parent to the previously thought root, need to remove it - for(size_t j = i; j < roots_.size() - 1; ++j) { - roots_[j] = roots_[j+1]; - } - - roots_.resize(roots_.size()-1); - break; - } - } if (!child.get()) { child = createGroupWellsGroup(groupChild, timeStep, phaseUsage); } @@ -74,20 +62,8 @@ namespace Opm parent = roots_[roots_.size() - 1].get(); } - std::shared_ptr child; + std::shared_ptr child = getAndUnRootChild(wellChild->name()); - for (size_t i = 0; i < roots_.size(); ++i) { - if (roots_[i]->name() == wellChild->name()) { - child = roots_[i]; - // We've found a new parent to the previously thought root, need to remove it - for(size_t j = i; j < roots_.size() - 1; ++j) { - roots_[j] = roots_[j+1]; - } - - roots_.resize(roots_.size()-1); - break; - } - } if (!child.get()) { child = createWellWellsGroup(wellChild, timeStep, phaseUsage); } @@ -115,20 +91,9 @@ namespace Opm roots_.push_back(createWellsGroup(parent_name, deck)); parent = roots_[roots_.size() - 1].get(); } - std::shared_ptr child; - for (size_t i = 0; i < roots_.size(); ++i) { - if (roots_[i]->name() == child_name) { - child = roots_[i]; - // We've found a new parent to the previously thought root, need to remove it - for(size_t j = i; j < roots_.size() - 1; ++j) { - roots_[j] = roots_[j+1]; - } + std::shared_ptr child = getAndUnRootChild(child_name); - roots_.resize(roots_.size()-1); - break; - } - } if (!child.get()) { child = createWellsGroup(child_name, deck); } @@ -147,6 +112,23 @@ namespace Opm } + std::shared_ptr WellCollection::getAndUnRootChild(std::string child_name) { + std::shared_ptr child; + + for (size_t i = 0; i < roots_.size(); ++i) { + if (roots_[i]->name() == child_name) { + child = roots_[i]; + // We've found a new parent to the previously thought root, need to remove it + for(size_t j = i; j < roots_.size() - 1; ++j) { + roots_[j] = roots_[j+1]; + } + + roots_.resize(roots_.size()-1); + break; + } + } + return child; + } const std::vector& WellCollection::getLeafNodes() const { return leaf_nodes_; diff --git a/opm/core/wells/WellCollection.hpp b/opm/core/wells/WellCollection.hpp index 8933ff03..e017b50b 100644 --- a/opm/core/wells/WellCollection.hpp +++ b/opm/core/wells/WellCollection.hpp @@ -140,6 +140,7 @@ namespace Opm const std::vector& well_surfacerates_phase); private: + std::shared_ptr getAndUnRootChild(std::string child_name); // To account for the possibility of a forest std::vector > roots_; From 15a7255f30af32284cd8288fc20668d1f870ed54 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Thu, 13 Feb 2014 16:00:39 +0100 Subject: [PATCH 50/56] Changed the WellCollection addChild functions to be more specific, and strict --- opm/core/wells/WellCollection.cpp | 50 ++++++++++++++----------------- opm/core/wells/WellCollection.hpp | 25 ++++------------ tests/test_wellcollection.cpp | 11 +++---- tests/wells_group.data | 7 +++++ 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index 260c156f..d40497c9 100644 --- a/opm/core/wells/WellCollection.cpp +++ b/opm/core/wells/WellCollection.cpp @@ -27,56 +27,52 @@ namespace Opm { - void WellCollection::addChild(GroupConstPtr groupChild, GroupConstPtr groupParent, + void WellCollection::addField(GroupConstPtr fieldGroup, size_t timeStep, const PhaseUsage& phaseUsage) { + WellsGroupInterface* fieldNode = findNode(fieldGroup->name()); + if (fieldNode) { + OPM_THROW(std::runtime_error, "Trying to add FIELD node, but this already exists. Can only have one FIELD node."); + } + + roots_.push_back(createGroupWellsGroup(fieldGroup, timeStep, phaseUsage)); + } + + void WellCollection::addGroup(GroupConstPtr groupChild, std::string parent_name, size_t timeStep, const PhaseUsage& phaseUsage) { - WellsGroupInterface* parent = findNode(groupParent->name()); + WellsGroupInterface* parent = findNode(parent_name); if (!parent) { - roots_.push_back(createGroupWellsGroup(groupParent, timeStep, phaseUsage)); - parent = roots_[roots_.size() - 1].get(); + OPM_THROW(std::runtime_error, "Trying to add child to group named " << parent_name << ", but this does not exist in the WellCollection."); } - std::shared_ptr child = getAndUnRootChild(groupChild->name()); + if (findNode(groupChild->name())) { + OPM_THROW(std::runtime_error, "Trying to add child named " << groupChild->name() << ", but this group is already in the WellCollection."); - if (!child.get()) { - child = createGroupWellsGroup(groupChild, timeStep, phaseUsage); } + std::shared_ptr child = createGroupWellsGroup(groupChild, timeStep, phaseUsage); + WellsGroup* parent_as_group = static_cast (parent); if (!parent_as_group) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << groupParent->name() << ", but it's not a group."); + OPM_THROW(std::runtime_error, "Trying to add child to group named " << parent->name() << ", but it's not a group."); } parent_as_group->addChild(child); - - if(child->isLeafNode()) { - leaf_nodes_.push_back(static_cast(child.get())); - } - child->setParent(parent); } - void WellCollection::addChild(WellConstPtr wellChild, GroupConstPtr groupParent, - size_t timeStep, const PhaseUsage& phaseUsage) { - WellsGroupInterface* parent = findNode(groupParent->name()); + void WellCollection::addWell(WellConstPtr wellChild, size_t timeStep, const PhaseUsage& phaseUsage) { + WellsGroupInterface* parent = findNode(wellChild->getGroupName(timeStep)); if (!parent) { - roots_.push_back(createGroupWellsGroup(groupParent, timeStep, phaseUsage)); - parent = roots_[roots_.size() - 1].get(); + OPM_THROW(std::runtime_error, "Trying to add child to group named " << wellChild->getGroupName(timeStep) << ", but this group does not exist in the WellCollection."); } - std::shared_ptr child = getAndUnRootChild(wellChild->name()); - - if (!child.get()) { - child = createWellWellsGroup(wellChild, timeStep, phaseUsage); - } + std::shared_ptr child = createWellWellsGroup(wellChild, timeStep, phaseUsage); WellsGroup* parent_as_group = static_cast (parent); if (!parent_as_group) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << groupParent->name() << ", but it's not a group."); + OPM_THROW(std::runtime_error, "Trying to add child to group named " << wellChild->getGroupName(timeStep) << ", but it's not a group."); } parent_as_group->addChild(child); - if(child->isLeafNode()) { - leaf_nodes_.push_back(static_cast(child.get())); - } + leaf_nodes_.push_back(static_cast(child.get())); child->setParent(parent); } diff --git a/opm/core/wells/WellCollection.hpp b/opm/core/wells/WellCollection.hpp index e017b50b..a0e36562 100644 --- a/opm/core/wells/WellCollection.hpp +++ b/opm/core/wells/WellCollection.hpp @@ -40,28 +40,13 @@ namespace Opm { public: - /// Adds and creates if necessary the child to the collection - /// and appends it to parent's children. Also adds and creates the parent - /// if necessary. - /// \param[in] wellChild the child well - /// \param[in] groupParent the parent group - /// \param[in] phaseUsage the phase usage - /// \param[in] timeStep the current time step - void addChild(WellConstPtr wellChild, GroupConstPtr groupParent, + void addField(GroupConstPtr fieldGroup, size_t timeStep, const PhaseUsage& phaseUsage); + + void addWell(WellConstPtr wellChild, size_t timeStep, const PhaseUsage& phaseUsage); + + void addGroup(GroupConstPtr groupChild, std::string parent_name, size_t timeStep, const PhaseUsage& phaseUsage); - - /// Adds and creates if necessary the child to the collection - /// and appends it to parent's children. Also adds and creates the parent - /// if necessary. - /// \param[in] groupChild the child group - /// \param[in] groupParent the parent group - /// \param[in] phaseUsage the phase usage - /// \param[in] timeStep the current time step - void addChild(GroupConstPtr groupChild, GroupConstPtr groupParent, - size_t timeStep, const PhaseUsage& phaseUsage); - - /// Adds and creates if necessary the child to the collection /// and appends it to parent's children. Also adds and creates the parent /// if necessary. diff --git a/tests/test_wellcollection.cpp b/tests/test_wellcollection.cpp index 0c12af44..d22ba3b4 100644 --- a/tests/test_wellcollection.cpp +++ b/tests/test_wellcollection.cpp @@ -52,22 +52,24 @@ BOOST_AUTO_TEST_CASE(AddWellsAndGroupToCollection) { // Add groups to WellCollection GroupConstPtr fieldGroup = eclipseState->getSchedule()->getGroup(field->name()); + collection.addField(fieldGroup, 2, pu); + for (auto iter = field->begin(); iter != field->end(); ++iter) { GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); - collection.addChild(childGroupNode, fieldGroup, 2, pu); + collection.addGroup(childGroupNode, fieldGroup->name(), 2, pu); } GroupConstPtr g1Group = eclipseState->getSchedule()->getGroup(g1->name()); for (auto iter = g1->begin(); iter != g1->end(); ++iter) { GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); - collection.addChild(childGroupNode, g1Group, 2, pu); + collection.addGroup(childGroupNode, g1Group->name(), 2, pu); } GroupConstPtr g2Group = eclipseState->getSchedule()->getGroup(g2->name()); for (auto iter = g2->begin(); iter != g2->end(); ++iter) { GroupConstPtr childGroupNode = eclipseState->getSchedule()->getGroup((*iter).second->name()); - collection.addChild(childGroupNode, g2Group, 2, pu); + collection.addGroup(childGroupNode, g2Group->name(), 2, pu); } BOOST_CHECK_EQUAL("FIELD", collection.findNode("FIELD")->name()); @@ -78,8 +80,7 @@ BOOST_AUTO_TEST_CASE(AddWellsAndGroupToCollection) { WellCollection wellCollection; std::vector wells = eclipseState->getSchedule()->getWells(); for (size_t i=0; igetSchedule()->getGroup(wells[i]->getGroupName(2)); - collection.addChild(wells[i], parentGroup, 2, pu); + collection.addWell(wells[i], 2, pu); } BOOST_CHECK_EQUAL("G1", collection.findNode("INJ1")->getParent()->name()); diff --git a/tests/wells_group.data b/tests/wells_group.data index 753d6031..5d2e58cb 100755 --- a/tests/wells_group.data +++ b/tests/wells_group.data @@ -42,6 +42,13 @@ WELSPECS 'PROD2' 'G2' 10 10 8400 'OIL' / / +GCONINJE +'G1' GAS RATE 30000 / +/ + +GCONPROD +'G2' ORAT 10000 / +/ WCONINJE 'INJ1' 'WATER' 'OPEN' 'RESV' 10 20 40 / From 501467a1e30461e642450ee14c79b99c855d6c7f Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 08:14:30 +0100 Subject: [PATCH 51/56] Added an else to avoid unnecessary checking. --- opm/core/wells/WellsGroup.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index 1087afcf..3316ccdb 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -1145,6 +1145,7 @@ namespace Opm std::shared_ptr createGroupWellsGroup(GroupConstPtr group, size_t timeStep, const PhaseUsage& phase_usage ) { InjectionSpecification injection_specification; + ProductionSpecification production_specification; if (group->isInjectionGroup(timeStep)) { injection_specification.injector_type_ = toInjectorType(Phase::PhaseEnum2String(group->getInjectionPhase(timeStep))); injection_specification.control_mode_ = toInjectionControlMode(GroupInjection::ControlEnum2String(group->getInjectionControlMode(timeStep))); @@ -1153,8 +1154,7 @@ namespace Opm injection_specification.reinjection_fraction_target_ = group->getTargetReinjectFraction(timeStep); injection_specification.voidage_replacment_fraction_ = group->getTargetVoidReplacementFraction(timeStep); } - ProductionSpecification production_specification; - if (group->isProductionGroup(timeStep)) { + else if (group->isProductionGroup(timeStep)) { production_specification.oil_max_rate_ = group->getOilTargetRate(timeStep); production_specification.control_mode_ = toProductionControlMode(GroupProduction::ControlEnum2String(group->getProductionControlMode(timeStep))); production_specification.water_max_rate_ = group->getWaterTargetRate(timeStep); @@ -1180,8 +1180,7 @@ namespace Opm injection_specification.reservoir_flow_max_rate_ = well->getReservoirInjectionRate(timeStep); production_specification.guide_rate_ = 0.0; // We know we're not a producer } - - if (well->isProducer(timeStep)) { + else if (well->isProducer(timeStep)) { production_specification.BHP_limit_ = well->getBHPLimit(timeStep); production_specification.reservoir_flow_max_rate_ = well->getResVRate(timeStep); production_specification.oil_max_rate_ = well->getOilRate(timeStep); From 9b6430f907c1e62e0b14cd24318b8ff4c98ea030 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 13:43:25 +0100 Subject: [PATCH 52/56] Added building of WellsCollection group structure from new opm-parser objects --- opm/core/wells/WellCollection.cpp | 12 +++++++----- opm/core/wells/WellsManager.cpp | 30 +++++++++++++++++------------- opm/core/wells/WellsManager.hpp | 3 +++ tests/wells_manager_data.data | 10 +++++----- 4 files changed, 32 insertions(+), 23 deletions(-) diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index d40497c9..8be2dd09 100644 --- a/opm/core/wells/WellCollection.cpp +++ b/opm/core/wells/WellCollection.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include namespace Opm @@ -40,11 +42,11 @@ namespace Opm size_t timeStep, const PhaseUsage& phaseUsage) { WellsGroupInterface* parent = findNode(parent_name); if (!parent) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << parent_name << ", but this does not exist in the WellCollection."); + OPM_THROW(std::runtime_error, "Trying to add child group to group named " << parent_name << ", but this does not exist in the WellCollection."); } if (findNode(groupChild->name())) { - OPM_THROW(std::runtime_error, "Trying to add child named " << groupChild->name() << ", but this group is already in the WellCollection."); + OPM_THROW(std::runtime_error, "Trying to add child group named " << groupChild->name() << ", but this group is already in the WellCollection."); } @@ -52,7 +54,7 @@ namespace Opm WellsGroup* parent_as_group = static_cast (parent); if (!parent_as_group) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << parent->name() << ", but it's not a group."); + OPM_THROW(std::runtime_error, "Trying to add child group to group named " << parent->name() << ", but it's not a group."); } parent_as_group->addChild(child); child->setParent(parent); @@ -61,14 +63,14 @@ namespace Opm void WellCollection::addWell(WellConstPtr wellChild, size_t timeStep, const PhaseUsage& phaseUsage) { WellsGroupInterface* parent = findNode(wellChild->getGroupName(timeStep)); if (!parent) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << wellChild->getGroupName(timeStep) << ", but this group does not exist in the WellCollection."); + OPM_THROW(std::runtime_error, "Trying to add well " << wellChild->name() << " Step: " << boost::lexical_cast(timeStep) << " to group named " << wellChild->getGroupName(timeStep) << ", but this group does not exist in the WellCollection."); } std::shared_ptr child = createWellWellsGroup(wellChild, timeStep, phaseUsage); WellsGroup* parent_as_group = static_cast (parent); if (!parent_as_group) { - OPM_THROW(std::runtime_error, "Trying to add child to group named " << wellChild->getGroupName(timeStep) << ", but it's not a group."); + OPM_THROW(std::runtime_error, "Trying to add well to group named " << wellChild->getGroupName(timeStep) << ", but it's not a group."); } parent_as_group->addChild(child); diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 49857d06..51f4cd6d 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -307,24 +307,19 @@ namespace Opm well_names.reserve(wells.size()); well_data.reserve(wells.size()); - createWellsFromSpecs(wells, timeStep, grid, well_names, well_data, well_names_to_index, pu, cartesian_to_compressed, permeability); - setupWellControls(wells, timeStep, well_names, pu); - // Build the well_collection_ well group hierarchy. - if (deck.hasField("GRUPTREE")) { - std::cout << "Found gruptree" << std::endl; - const GRUPTREE& gruptree = deck.getGRUPTREE(); - std::map::const_iterator it = gruptree.tree.begin(); - for( ; it != gruptree.tree.end(); ++it) { - well_collection_.addChild(it->first, it->second, deck); - } - } - for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) { - well_collection_.addChild((*wellIter)->name(), (*wellIter)->getGroupName(timeStep), deck); + { + GroupTreeNodeConstPtr fieldNode = eclipseState->getSchedule()->getGroupTree(timeStep)->getNode("FIELD"); + GroupConstPtr fieldGroup = eclipseState->getSchedule()->getGroup(fieldNode->name()); + well_collection_.addField(fieldGroup, timeStep, pu); + addChildGroups(fieldNode, eclipseState->getSchedule(), timeStep, pu); } + for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) { + well_collection_.addWell((*wellIter), timeStep, pu); + } // Set the guide rates: @@ -1387,4 +1382,13 @@ namespace Opm } } + + void WellsManager::addChildGroups(GroupTreeNodeConstPtr parentNode, ScheduleConstPtr schedule, size_t timeStep, const PhaseUsage& phaseUsage) { + for (auto childIter = parentNode->begin(); childIter != parentNode->end(); ++childIter) { + GroupTreeNodeConstPtr childNode = (*childIter).second; + well_collection_.addGroup(schedule->getGroup(childNode->name()), parentNode->name(), timeStep, phaseUsage); + addChildGroups(childNode, schedule, timeStep, phaseUsage); + } + } + } // namespace Opm diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 77bbb903..f54efd94 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -25,6 +25,7 @@ #include #include +#include struct Wells; struct UnstructuredGrid; @@ -150,6 +151,8 @@ namespace Opm const std::map cartesian_to_compressed, const double* permeability); + void addChildGroups(GroupTreeNodeConstPtr parentNode, ScheduleConstPtr schedule, size_t timeStep, const PhaseUsage& phaseUsage); + // Data diff --git a/tests/wells_manager_data.data b/tests/wells_manager_data.data index 2df2dfd4..732b528b 100755 --- a/tests/wells_manager_data.data +++ b/tests/wells_manager_data.data @@ -17,8 +17,8 @@ DZV 10.0 20.0 30.0 10.0 5.0 / DEPTHZ -121*2000 -/ +121*2000 / + SCHEDULE @@ -34,11 +34,11 @@ COMPDAT WCONPROD 'PROD1' 'OPEN' 'ORAT' 20000 4* 1000 / - / +/ WCONINJE 'INJ1' 'GAS' 'OPEN' 'RATE' 100 200 400 / - / +/ DATES @@ -53,7 +53,7 @@ WCONINJE 'INJ1' 'WATER' 'OPEN' 'RESV' 10 20 40 / / - +END TSTEP 14.0 / From a5144288a310c5d1ef078bf82a495de5bd49f809 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 13:46:54 +0100 Subject: [PATCH 53/56] Removed the previously introduced getAndUnRootChild method --- opm/core/wells/WellCollection.cpp | 33 +++++++++++++------------------ opm/core/wells/WellCollection.hpp | 1 - 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index 8be2dd09..9218db06 100644 --- a/opm/core/wells/WellCollection.cpp +++ b/opm/core/wells/WellCollection.cpp @@ -90,7 +90,20 @@ namespace Opm parent = roots_[roots_.size() - 1].get(); } - std::shared_ptr child = getAndUnRootChild(child_name); + std::shared_ptr child; + + for (size_t i = 0; i < roots_.size(); ++i) { + if (roots_[i]->name() == child_name) { + child = roots_[i]; + // We've found a new parent to the previously thought root, need to remove it + for(size_t j = i; j < roots_.size() - 1; ++j) { + roots_[j] = roots_[j+1]; + } + + roots_.resize(roots_.size()-1); + break; + } + } if (!child.get()) { child = createWellsGroup(child_name, deck); @@ -110,24 +123,6 @@ namespace Opm } - std::shared_ptr WellCollection::getAndUnRootChild(std::string child_name) { - std::shared_ptr child; - - for (size_t i = 0; i < roots_.size(); ++i) { - if (roots_[i]->name() == child_name) { - child = roots_[i]; - // We've found a new parent to the previously thought root, need to remove it - for(size_t j = i; j < roots_.size() - 1; ++j) { - roots_[j] = roots_[j+1]; - } - - roots_.resize(roots_.size()-1); - break; - } - } - return child; - } - const std::vector& WellCollection::getLeafNodes() const { return leaf_nodes_; } diff --git a/opm/core/wells/WellCollection.hpp b/opm/core/wells/WellCollection.hpp index a0e36562..f084a04d 100644 --- a/opm/core/wells/WellCollection.hpp +++ b/opm/core/wells/WellCollection.hpp @@ -125,7 +125,6 @@ namespace Opm const std::vector& well_surfacerates_phase); private: - std::shared_ptr getAndUnRootChild(std::string child_name); // To account for the possibility of a forest std::vector > roots_; From 8eaea50e177a3f6a21c65997d52b91b29accd285 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 15:36:32 +0100 Subject: [PATCH 54/56] Removed old EclipseGridParser from WellsManager constructor, WGROUPCON still missing --- opm/core/wells/WellsManager.cpp | 34 --------------------------------- opm/core/wells/WellsManager.hpp | 1 - tests/test_wellsmanager.cpp | 12 ++++++------ 3 files changed, 6 insertions(+), 41 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 51f4cd6d..72abe9c1 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -272,7 +272,6 @@ namespace Opm /// Construct wells from deck. WellsManager::WellsManager(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, - const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, const double* permeability) : w_(0) @@ -321,39 +320,6 @@ namespace Opm well_collection_.addWell((*wellIter), timeStep, pu); } - - // Set the guide rates: - if (deck.hasField("WGRUPCON")) { - std::cout << "Found Wgrupcon" << std::endl; - WGRUPCON wgrupcon = deck.getWGRUPCON(); - const std::vector& lines = wgrupcon.wgrupcon; - std::cout << well_collection_.getLeafNodes().size() << std::endl; - for (size_t i = 0; i < lines.size(); i++) { - std::string name = lines[i].well_; - const int wix = well_names_to_index[name]; - WellNode& wellnode = *well_collection_.getLeafNodes()[wix]; - assert(wellnode.name() == name); - if (well_data[wix].type == PRODUCER) { - wellnode.prodSpec().guide_rate_ = lines[i].guide_rate_; - if (lines[i].phase_ == "OIL") { - wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL; - } else { - OPM_THROW(std::runtime_error, "Guide rate type " << lines[i].phase_ << " specified for producer " - << name << " in WGRUPCON, cannot handle."); - } - } else if (well_data[wix].type == INJECTOR) { - wellnode.injSpec().guide_rate_ = lines[i].guide_rate_; - if (lines[i].phase_ == "RAT") { - wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT; - } else { - OPM_THROW(std::runtime_error, "Guide rate type " << lines[i].phase_ << " specified for injector " - << name << " in WGRUPCON, cannot handle."); - } - } else { - OPM_THROW(std::runtime_error, "Unknown well type " << well_data[wix].type << " for well " << name); - } - } - } well_collection_.setWellsPointer(w_); well_collection_.applyGroupControls(); diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index f54efd94..165f5e12 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -79,7 +79,6 @@ namespace Opm WellsManager(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, - const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, const double* permeability); diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 0caf67f1..8cb097e2 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Deck.setCurrentEpoch(0); { - Opm::WellsManager wellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 0, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); std::cout << "Checking new well structure, epoch 0" << std::endl; @@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Deck.setCurrentEpoch(1); { - Opm::WellsManager wellsManager(eclipseState, 1,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 1, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); std::cout << "Checking new well structure, epoch 1" << std::endl; @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { Deck.setCurrentEpoch(0); { - Opm::WellsManager wellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 0, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); BOOST_CHECK(wells_equal(wellsManager.c_wells(), oldWellsManager.c_wells(),false)); @@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { Deck.setCurrentEpoch(1); { - Opm::WellsManager wellsManager(eclipseState, 1,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 1, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells(), true)); @@ -265,7 +265,7 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { Deck.setCurrentEpoch(2); { - Opm::WellsManager wellsManager(eclipseState, 2,Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager(eclipseState, 2, *gridManager.c_grid(), NULL); Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); BOOST_CHECK(wells_equal( wellsManager.c_wells(), oldWellsManager.c_wells(), true)); @@ -321,7 +321,7 @@ BOOST_AUTO_TEST_CASE(WellHasSTOP_ExceptionIsThrown) { Deck.setCurrentEpoch(0); - BOOST_CHECK_THROW( new Opm::WellsManager(eclipseState, 0, Deck, *gridManager.c_grid(), NULL), std::runtime_error ); + BOOST_CHECK_THROW( new Opm::WellsManager(eclipseState, 0, *gridManager.c_grid(), NULL), std::runtime_error ); } From 7e29a9ea45371247cdaa3205e4417e7d36db3403 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 24 Feb 2014 15:24:33 +0100 Subject: [PATCH 55/56] Add handling of WGRUPCON to new-parser friendly WellsManager constructor --- opm/core/wells/WellsManager.cpp | 38 ++++++++++++++++++++++++++++++++- opm/core/wells/WellsManager.hpp | 2 +- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 72abe9c1..64091120 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -268,7 +268,6 @@ namespace Opm { } - /// Construct wells from deck. WellsManager::WellsManager(const Opm::EclipseStateConstPtr eclipseState, const size_t timeStep, @@ -323,6 +322,9 @@ namespace Opm well_collection_.setWellsPointer(w_); well_collection_.applyGroupControls(); + + setupGuideRates(wells, timeStep, well_data, well_names_to_index); + // Debug output. #define EXTRA_OUTPUT #ifdef EXTRA_OUTPUT @@ -1357,4 +1359,38 @@ namespace Opm } } + + + + void WellsManager::setupGuideRates(std::vector& wells, const size_t timeStep, std::vector& well_data, std::map& well_names_to_index) + { + for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) { + WellConstPtr well = *wellIter; + const int wix = well_names_to_index[well->name()]; + WellNode& wellnode = *well_collection_.getLeafNodes()[wix]; + + if (well->getGuideRatePhase(timeStep) != GuideRate::UNDEFINED) { + if (well_data[wix].type == PRODUCER) { + wellnode.prodSpec().guide_rate_ = well->getGuideRate(timeStep); + if (well->getGuideRatePhase(timeStep) == GuideRate::OIL) { + wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL; + } else { + OPM_THROW(std::runtime_error, "Guide rate type " << GuideRate::GuideRatePhaseEnum2String(well->getGuideRatePhase(timeStep)) << " specified for producer " + << well->name() << " in WGRUPCON, cannot handle."); + } + } else if (well_data[wix].type == INJECTOR) { + wellnode.injSpec().guide_rate_ = well->getGuideRate(timeStep); + if (well->getGuideRatePhase(timeStep) == GuideRate::RAT) { + wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT; + } else { + OPM_THROW(std::runtime_error, "Guide rate type " << GuideRate::GuideRatePhaseEnum2String(well->getGuideRatePhase(timeStep)) << " specified for injector " + << well->name() << " in WGRUPCON, cannot handle."); + } + } else { + OPM_THROW(std::runtime_error, "Unknown well type " << well_data[wix].type << " for well " << well->name()); + } + } + } + } + } // namespace Opm diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 165f5e12..9e5c314c 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -151,7 +151,7 @@ namespace Opm const double* permeability); void addChildGroups(GroupTreeNodeConstPtr parentNode, ScheduleConstPtr schedule, size_t timeStep, const PhaseUsage& phaseUsage); - + void setupGuideRates(std::vector& wells, const size_t timeStep, std::vector& well_data, std::map& well_names_to_index); // Data From 20e21b7892b1d2f3257cac917d4b5b11ef959141 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 20 Feb 2014 17:59:41 +0100 Subject: [PATCH 56/56] SimulatorTimer: make it possible to base it on opm-parser's TimeMap Since SimulatorTimer is a fairly shallow shim if using the TimeMap, it can also be removed relatively easily. Having said this, that would trigger _many_ changes in _a lot_ of places and I'm not motivated at all to fight that battle as long as the old parser needs to be supported. I thus decided that the best way is to add a "wrapper mode" to SimulationTimer... --- opm/core/simulator/SimulatorTimer.cpp | 59 ++++++++++++++++++++++----- opm/core/simulator/SimulatorTimer.hpp | 7 ++++ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/opm/core/simulator/SimulatorTimer.cpp b/opm/core/simulator/SimulatorTimer.cpp index f3bef455..fddc3aa8 100644 --- a/opm/core/simulator/SimulatorTimer.cpp +++ b/opm/core/simulator/SimulatorTimer.cpp @@ -57,10 +57,21 @@ namespace Opm start_date_ = deck.getStartDate(); } + /// Use the SimulatorTimer as a shim around opm-parser's Opm::TimeMap + void SimulatorTimer::init(Opm::TimeMapConstPtr timeMap, + int timeStepIdx) + { + timeMap_ = timeMap; + current_step_ = timeStepIdx; + } + /// Total number of steps. int SimulatorTimer::numSteps() const { - return timesteps_.size(); + if (timeMap_) + return timeMap_->numTimesteps(); + else + return timesteps_.size(); } /// Current step number. @@ -72,13 +83,14 @@ namespace Opm /// Set current step number. void SimulatorTimer::setCurrentStepNum(int step) { - if (current_step_ < 0 || current_step_ > int(timesteps_.size())) { + if (current_step_ < 0 || current_step_ > int(numSteps())) { // Note that we do allow current_step_ == timesteps_.size(), // that is the done() state. OPM_THROW(std::runtime_error, "Trying to set invalid step number: " << step); } current_step_ = step; - current_time_ = std::accumulate(timesteps_.begin(), timesteps_.begin() + step, 0.0); + if (timeMap_) + current_time_ = std::accumulate(timesteps_.begin(), timesteps_.begin() + step, 0.0); } @@ -86,25 +98,37 @@ namespace Opm double SimulatorTimer::currentStepLength() const { assert(!done()); - return timesteps_[current_step_]; + if (timeMap_) + return timeMap_->getTimeStepLength(current_step_); + else + return timesteps_[current_step_]; } double SimulatorTimer::stepLengthTaken() const { assert(current_step_ > 0); - return timesteps_[current_step_ - 1]; + if (timeMap_) + return timeMap_->getTimeStepLength(current_step_ - 1); + else + return timesteps_[current_step_ - 1]; } /// Current time. double SimulatorTimer::currentTime() const { - return current_time_; + if (timeMap_) + return timeMap_->getTimePassedUntil(current_step_); + else + return current_time_; } boost::posix_time::ptime SimulatorTimer::currentDateTime() const { - return boost::posix_time::ptime(start_date_) + boost::posix_time::seconds( (int) current_time_ ); + if (timeMap_) + return timeMap_->getStartTime(current_step_); + else + return boost::posix_time::ptime(start_date_) + boost::posix_time::seconds( (int) current_time_ ); } @@ -112,7 +136,10 @@ namespace Opm /// Total time. double SimulatorTimer::totalTime() const { - return total_time_; + if (timeMap_) + return timeMap_->getTotalTime(); + else + return total_time_; } /// Set total time. @@ -121,7 +148,13 @@ namespace Opm /// access to later timesteps. void SimulatorTimer::setTotalTime(double time) { - total_time_ = time; + if (timeMap_) { + // well, what can we do if we use opm-parser's TimeMap? + OPM_THROW(std::logic_error, + "Not implemented: SimulatorTimer::setTotalTime() if using a TimeMap."); + } + else + total_time_ = time; } /// Print a report with current and total time etc. @@ -138,7 +171,8 @@ namespace Opm SimulatorTimer& SimulatorTimer::operator++() { assert(!done()); - current_time_ += timesteps_[current_step_]; + if (!timeMap_) + current_time_ += timesteps_[current_step_]; ++current_step_; return *this; } @@ -146,7 +180,10 @@ namespace Opm /// Return true if op++() has been called numSteps() times. bool SimulatorTimer::done() const { - return int(timesteps_.size()) == current_step_; + if (timeMap_) + return int(timeMap_->numTimesteps()) <= current_step_; + else + return int(timesteps_.size()) == current_step_; } diff --git a/opm/core/simulator/SimulatorTimer.hpp b/opm/core/simulator/SimulatorTimer.hpp index a1d8a270..949859e5 100644 --- a/opm/core/simulator/SimulatorTimer.hpp +++ b/opm/core/simulator/SimulatorTimer.hpp @@ -20,6 +20,8 @@ #ifndef OPM_SIMULATORTIMER_HEADER_INCLUDED #define OPM_SIMULATORTIMER_HEADER_INCLUDED +#include + #include #include #include @@ -47,6 +49,10 @@ namespace Opm /// Note that DATES are folded into TSTEP by the parser. void init(const EclipseGridParser& deck); + /// Use the SimulatorTimer as a shim around opm-parser's Opm::TimeMap + void init(TimeMapConstPtr timeMap, + int timeStepIdx = 0); + /// Total number of steps. int numSteps() const; @@ -99,6 +105,7 @@ namespace Opm bool done() const; private: + Opm::TimeMapConstPtr timeMap_; std::vector timesteps_; int current_step_; double current_time_;