From 86439d6a526574dd681857621c074c6bbde19106 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 14 Nov 2013 12:51:12 +0100 Subject: [PATCH 001/109] 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 002/109] 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 003/109] 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 004/109] 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 005/109] 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 006/109] 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 007/109] 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 008/109] 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 009/109] 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 010/109] 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 011/109] 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 012/109] 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 013/109] 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 014/109] 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 015/109] 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 016/109] 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 017/109] 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 018/109] 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 019/109] 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 020/109] 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 021/109] 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 022/109] 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 023/109] 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 024/109] 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 025/109] 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 026/109] 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 027/109] [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 028/109] 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 029/109] [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 030/109] 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 031/109] [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 032/109] 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 033/109] 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 034/109] 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 035/109] 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 036/109] 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 037/109] 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 038/109] 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 039/109] 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 040/109] 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 041/109] 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 042/109] 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 043/109] 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 044/109] 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 045/109] 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 046/109] 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 047/109] 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 048/109] 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 049/109] 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 050/109] 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 051/109] 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 052/109] 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 053/109] 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 054/109] 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 dbe6d2911de3f1892ecf610dfeb68a081d1104e9 Mon Sep 17 00:00:00 2001 From: osae Date: Tue, 18 Feb 2014 13:49:35 +0100 Subject: [PATCH 055/109] Endpoint scaling and hysteresis for gwseg. Activation of eps and hysteresis treatment for gwseg. Also some additional initialization. --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 4 ++-- opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp | 7 +++++++ opm/core/props/satfunc/SaturationPropsInterface.hpp | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index e476f015..c28178a1 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -58,8 +58,8 @@ namespace Opm // 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 (deck.hasField("ENDSCALE") && threephase_model != "simple") { - OPM_THROW(std::runtime_error, "Sorry, end point scaling currently available for the 'simple' model only."); + if (deck.hasField("ENDSCALE") && threephase_model != "gwseg") { + OPM_THROW(std::runtime_error, "Sorry, end point scaling currently available for the 'gwseg' model only."); } if (sat_samples > 1) { if (threephase_model == "stone2") { diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp index c3006993..4b7ae04d 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp @@ -646,6 +646,13 @@ namespace Opm { if (scr.empty() && su.empty() && (sxcr.empty() || !do_3pt_) && s0.empty()) { data.doNotScale = true; + data.smin = sl_tab; + if (oil) { + data.smax = (s0_tab < 0.0) ? 1.0 - su_tab : 1.0 - su_tab - s0_tab; + } else { + data.smax = su_tab; + } + data.scr = scr_tab; } else { data.doNotScale = false; data.do_3pt = do_3pt_; diff --git a/opm/core/props/satfunc/SaturationPropsInterface.hpp b/opm/core/props/satfunc/SaturationPropsInterface.hpp index ad2a9439..04f6730e 100644 --- a/opm/core/props/satfunc/SaturationPropsInterface.hpp +++ b/opm/core/props/satfunc/SaturationPropsInterface.hpp @@ -73,6 +73,14 @@ namespace Opm const int* cells, double* smin, double* smax) const = 0; + + /// Update saturation state for the hysteresis tracking + /// \param[in] n Number of data points. + /// \param[in] s Array of nP saturation values. + virtual void updateSatHyst(const int n, + const int* cells, + const double* s) = 0; + }; From 7e29a9ea45371247cdaa3205e4417e7d36db3403 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 24 Feb 2014 15:24:33 +0100 Subject: [PATCH 056/109] 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 057/109] 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_; From 54fbd6b1903c36e8d462001463bd5a306df26a54 Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Thu, 15 Aug 2013 23:25:09 +0200 Subject: [PATCH 058/109] Provide dependency information for opm-benchmarks --- cmake/Modules/opm-benchmarks-prereqs.cmake | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 cmake/Modules/opm-benchmarks-prereqs.cmake diff --git a/cmake/Modules/opm-benchmarks-prereqs.cmake b/cmake/Modules/opm-benchmarks-prereqs.cmake new file mode 100644 index 00000000..7b754d96 --- /dev/null +++ b/cmake/Modules/opm-benchmarks-prereqs.cmake @@ -0,0 +1,15 @@ +# -*- mode: cmake; tab-width: 2; indent-tabs-mode: t; truncate-lines: t; compile-command: "cmake -Wdev" -*- +# vim: set filetype=cmake autoindent tabstop=2 shiftwidth=2 noexpandtab softtabstop=2 nowrap: + +# defines that must be present in config.h for our headers +set (opm-benchmarks_CONFIG_VAR + ) + +# dependencies +set (opm-benchmarks_DEPS + # compile with C++0x/11 support if available + "CXX11Features REQUIRED" + # OPM dependency + "opm-core" + "opm-upscaling" + ) From ac61cde15fcc5e32a39ee1921cb85d8885920269 Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Sat, 7 Sep 2013 23:11:32 +0200 Subject: [PATCH 059/109] Depend on Boost::iostreams The benchmark library uses Boost::iostreams to do decompression. Since we only scan for the Boost dependency once, this submodule is added to all of the projects in order to have a coherent dependency on Boost. --- cmake/Modules/dune-cornerpoint-prereqs.cmake | 2 +- cmake/Modules/opm-autodiff-prereqs.cmake | 2 +- cmake/Modules/opm-benchmarks-prereqs.cmake | 3 ++ cmake/Modules/opm-core-prereqs.cmake | 52 ++++++++++---------- cmake/Modules/opm-polymer-prereqs.cmake | 2 +- cmake/Modules/opm-porsol-prereqs.cmake | 2 +- cmake/Modules/opm-upscaling-prereqs.cmake | 2 +- cmake/Modules/opm-verteq-prereqs.cmake | 2 +- 8 files changed, 34 insertions(+), 33 deletions(-) diff --git a/cmake/Modules/dune-cornerpoint-prereqs.cmake b/cmake/Modules/dune-cornerpoint-prereqs.cmake index c7a0a358..5b03c33b 100644 --- a/cmake/Modules/dune-cornerpoint-prereqs.cmake +++ b/cmake/Modules/dune-cornerpoint-prereqs.cmake @@ -16,7 +16,7 @@ set (dune-cornerpoint_DEPS "CXX11Features" # various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE dependency "dune-common REQUIRED; dune-grid REQUIRED; diff --git a/cmake/Modules/opm-autodiff-prereqs.cmake b/cmake/Modules/opm-autodiff-prereqs.cmake index 86fad9a3..f83413df 100644 --- a/cmake/Modules/opm-autodiff-prereqs.cmake +++ b/cmake/Modules/opm-autodiff-prereqs.cmake @@ -13,7 +13,7 @@ set (opm-autodiff_DEPS "CXX11Features" # Various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE prerequisites "dune-common REQUIRED; dune-istl REQUIRED; diff --git a/cmake/Modules/opm-benchmarks-prereqs.cmake b/cmake/Modules/opm-benchmarks-prereqs.cmake index 7b754d96..465d10e5 100644 --- a/cmake/Modules/opm-benchmarks-prereqs.cmake +++ b/cmake/Modules/opm-benchmarks-prereqs.cmake @@ -9,6 +9,9 @@ set (opm-benchmarks_CONFIG_VAR set (opm-benchmarks_DEPS # compile with C++0x/11 support if available "CXX11Features REQUIRED" + # various runtime library enhancements + "Boost 1.39.0 + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # OPM dependency "opm-core" "opm-upscaling" diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index 41419b15..fb7b37c6 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -3,33 +3,31 @@ # 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.44.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" - # Ensembles-based Reservoir Tools (ERT) - "ERT" - # DUNE dependency - "dune-common" - "dune-istl" - ) + # 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 iostreams 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" + # Ensembles-based Reservoir Tools (ERT) + "ERT" + # DUNE dependency + "dune-common" + "dune-istl" + ) diff --git a/cmake/Modules/opm-polymer-prereqs.cmake b/cmake/Modules/opm-polymer-prereqs.cmake index e34a37de..68f52ec0 100644 --- a/cmake/Modules/opm-polymer-prereqs.cmake +++ b/cmake/Modules/opm-polymer-prereqs.cmake @@ -13,7 +13,7 @@ set (opm-polymer_DEPS "CXX11Features" # various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # Ensembles-based Reservoir Tools "ERT" # OPM dependency diff --git a/cmake/Modules/opm-porsol-prereqs.cmake b/cmake/Modules/opm-porsol-prereqs.cmake index 01280c7c..cc0b192b 100644 --- a/cmake/Modules/opm-porsol-prereqs.cmake +++ b/cmake/Modules/opm-porsol-prereqs.cmake @@ -13,7 +13,7 @@ set (opm-porsol_DEPS "CXX11Features" # various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE dependency "dune-common REQUIRED; dune-istl REQUIRED; diff --git a/cmake/Modules/opm-upscaling-prereqs.cmake b/cmake/Modules/opm-upscaling-prereqs.cmake index c55bc130..b6f42eed 100644 --- a/cmake/Modules/opm-upscaling-prereqs.cmake +++ b/cmake/Modules/opm-upscaling-prereqs.cmake @@ -14,7 +14,7 @@ set (opm-upscaling_DEPS "CXX11Features" # various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # matrix library "BLAS REQUIRED" "LAPACK REQUIRED" diff --git a/cmake/Modules/opm-verteq-prereqs.cmake b/cmake/Modules/opm-verteq-prereqs.cmake index 6b3b7441..1b111a2e 100644 --- a/cmake/Modules/opm-verteq-prereqs.cmake +++ b/cmake/Modules/opm-verteq-prereqs.cmake @@ -13,7 +13,7 @@ set (opm-verteq_DEPS "CXX11Features" # various runtime library enhancements "Boost 1.39.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # OPM dependency "opm-core" ) From 514240d0eb40e4a6417c184eca4ab5a4a0f97370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 28 Feb 2014 18:29:19 +0100 Subject: [PATCH 060/109] Uniformly raise Boost version requirement to 1.44 Commit 70505ff raised opm-core's "Boost" requirement to version 1.44 in order to reflect the requirements of the opm-parser module. This commit propagates that requirement to all other known modules. --- cmake/Modules/dune-cornerpoint-prereqs.cmake | 2 +- cmake/Modules/opm-autodiff-prereqs.cmake | 2 +- cmake/Modules/opm-benchmarks-prereqs.cmake | 2 +- cmake/Modules/opm-core-prereqs.cmake | 2 +- cmake/Modules/opm-polymer-prereqs.cmake | 2 +- cmake/Modules/opm-porsol-prereqs.cmake | 2 +- cmake/Modules/opm-upscaling-prereqs.cmake | 2 +- cmake/Modules/opm-verteq-prereqs.cmake | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cmake/Modules/dune-cornerpoint-prereqs.cmake b/cmake/Modules/dune-cornerpoint-prereqs.cmake index 5b03c33b..f06134c0 100644 --- a/cmake/Modules/dune-cornerpoint-prereqs.cmake +++ b/cmake/Modules/dune-cornerpoint-prereqs.cmake @@ -15,7 +15,7 @@ set (dune-cornerpoint_DEPS # compile with C++0x/11 support if available "CXX11Features" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE dependency "dune-common REQUIRED; diff --git a/cmake/Modules/opm-autodiff-prereqs.cmake b/cmake/Modules/opm-autodiff-prereqs.cmake index f83413df..101e2f1f 100644 --- a/cmake/Modules/opm-autodiff-prereqs.cmake +++ b/cmake/Modules/opm-autodiff-prereqs.cmake @@ -12,7 +12,7 @@ set (opm-autodiff_DEPS # Compile with C++0x/11 support if available "CXX11Features" # Various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE prerequisites "dune-common REQUIRED; diff --git a/cmake/Modules/opm-benchmarks-prereqs.cmake b/cmake/Modules/opm-benchmarks-prereqs.cmake index 465d10e5..330774a8 100644 --- a/cmake/Modules/opm-benchmarks-prereqs.cmake +++ b/cmake/Modules/opm-benchmarks-prereqs.cmake @@ -10,7 +10,7 @@ set (opm-benchmarks_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 iostreams unit_test_framework REQUIRED" # OPM dependency "opm-core" diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index fb7b37c6..e8f2135a 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 iostreams unit_test_framework REQUIRED" # matrix library "BLAS REQUIRED" diff --git a/cmake/Modules/opm-polymer-prereqs.cmake b/cmake/Modules/opm-polymer-prereqs.cmake index 68f52ec0..8a17e078 100644 --- a/cmake/Modules/opm-polymer-prereqs.cmake +++ b/cmake/Modules/opm-polymer-prereqs.cmake @@ -12,7 +12,7 @@ set (opm-polymer_DEPS # compile with C++0x/11 support if available "CXX11Features" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # Ensembles-based Reservoir Tools "ERT" diff --git a/cmake/Modules/opm-porsol-prereqs.cmake b/cmake/Modules/opm-porsol-prereqs.cmake index cc0b192b..1f88e0bb 100644 --- a/cmake/Modules/opm-porsol-prereqs.cmake +++ b/cmake/Modules/opm-porsol-prereqs.cmake @@ -12,7 +12,7 @@ set (opm-porsol_DEPS # compile with C++0x/11 support if available "CXX11Features" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # DUNE dependency "dune-common REQUIRED; diff --git a/cmake/Modules/opm-upscaling-prereqs.cmake b/cmake/Modules/opm-upscaling-prereqs.cmake index b6f42eed..46374901 100644 --- a/cmake/Modules/opm-upscaling-prereqs.cmake +++ b/cmake/Modules/opm-upscaling-prereqs.cmake @@ -13,7 +13,7 @@ set (opm-upscaling_DEPS # compile with C++0x/11 support if available "CXX11Features" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # matrix library "BLAS REQUIRED" diff --git a/cmake/Modules/opm-verteq-prereqs.cmake b/cmake/Modules/opm-verteq-prereqs.cmake index 1b111a2e..d69ca480 100644 --- a/cmake/Modules/opm-verteq-prereqs.cmake +++ b/cmake/Modules/opm-verteq-prereqs.cmake @@ -12,7 +12,7 @@ set (opm-verteq_DEPS # compile with C++0x/11 support if available "CXX11Features" # various runtime library enhancements - "Boost 1.39.0 + "Boost 1.44.0 COMPONENTS date_time filesystem system iostreams unit_test_framework REQUIRED" # OPM dependency "opm-core" From 9fcd3d8181c33b082ba27fa6eda9122c9829e460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 28 Feb 2014 19:06:42 +0100 Subject: [PATCH 061/109] Reformat prerequisite file according to convention This commit makes a few adjustments to the white-space of file 'opm-parser-prereqs.cmake' to honour the conventions of the other *-prereqs.cmake files within the OPM project's module suites. No functional changes. --- cmake/Modules/opm-parser-prereqs.cmake | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cmake/Modules/opm-parser-prereqs.cmake b/cmake/Modules/opm-parser-prereqs.cmake index 521568b7..47c77ae9 100644 --- a/cmake/Modules/opm-parser-prereqs.cmake +++ b/cmake/Modules/opm-parser-prereqs.cmake @@ -3,16 +3,17 @@ # defines that must be present in config.h for our headers set (opm-parser_CONFIG_VAR - HAVE_ERT - ) + HAVE_ERT + ) # dependencies set (opm-parser_DEPS - # compile with C99 support if available - "C99" - # compile with C++0x/11 support if available - "CXX11Features REQUIRED" - # various runtime library enhancements - "Boost 1.44.0 COMPONENTS date_time filesystem system unit_test_framework REQUIRED" - "cJSON" - ) + # compile with C99 support if available + "C99" + # compile with C++0x/11 support if available + "CXX10Features REQUIRED" + # various runtime library enhancements + "Boost 1.44.0 + COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + "cJSON" + ) From 47d0956d6431d7b9580b850b2f28f20288abf402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 28 Feb 2014 19:13:46 +0100 Subject: [PATCH 062/109] Depend on Boost.Iostreams The benchmark library uses Boost::iostreams to do decompression. Since we only scan for the Boost dependency once, this submodule is added to all of the projects in order to have a coherent dependency on Boost. --- cmake/Modules/opm-parser-prereqs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/opm-parser-prereqs.cmake b/cmake/Modules/opm-parser-prereqs.cmake index 47c77ae9..fca8f248 100644 --- a/cmake/Modules/opm-parser-prereqs.cmake +++ b/cmake/Modules/opm-parser-prereqs.cmake @@ -14,6 +14,6 @@ set (opm-parser_DEPS "CXX10Features REQUIRED" # various runtime library enhancements "Boost 1.44.0 - COMPONENTS date_time filesystem system unit_test_framework REQUIRED" + COMPONENTS date_time filesystem system iostream unit_test_framework REQUIRED" "cJSON" ) From 90ebf7b56dffa4a3e7380e8b6b95160b3e1d2bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 28 Feb 2014 20:00:44 +0100 Subject: [PATCH 063/109] Fix mis-merge. In propagating the requirements for opm-benchmarks to the other modules I accidentally removed the 'opm-parser' prerequisite (see commit 86439d6 for details). This commit restores that prerequisite. --- cmake/Modules/opm-core-prereqs.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index e8f2135a..58b627e3 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -25,6 +25,8 @@ set (opm-core_DEPS "SuperLU" # xml processing (for config parsing) "TinyXML" + #Parser library + "opm-parser REQUIRED" # Ensembles-based Reservoir Tools (ERT) "ERT" # DUNE dependency From d9db5da62ec7af9f39fae9080c4e513f6721f0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 28 Feb 2014 22:07:27 +0100 Subject: [PATCH 064/109] Restore C++-11 checks While reformatting the parser-prereqs file I accidentally replaced the feature search 'CXX11Features' with 'CXX10Features'. This commit fixes that blunder. --- cmake/Modules/opm-parser-prereqs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/opm-parser-prereqs.cmake b/cmake/Modules/opm-parser-prereqs.cmake index fca8f248..6cf975b9 100644 --- a/cmake/Modules/opm-parser-prereqs.cmake +++ b/cmake/Modules/opm-parser-prereqs.cmake @@ -11,7 +11,7 @@ set (opm-parser_DEPS # compile with C99 support if available "C99" # compile with C++0x/11 support if available - "CXX10Features REQUIRED" + "CXX11Features REQUIRED" # various runtime library enhancements "Boost 1.44.0 COMPONENTS date_time filesystem system iostream unit_test_framework REQUIRED" From 0eb32276fbd64e4bdfa0f7c410f19267cc43b59d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 3 Mar 2014 09:17:11 +0100 Subject: [PATCH 065/109] Allow direct interpolation of props with new parser. After transitioning to use the new parser, the SinglePvtDead class was never used even when the 'samples' argument used to control usage was zero or negative. The resulting construction of SinglePvtDeadSpline objects was then failing. This change adds a new constructor to SinglePvtDead, and restores the ability to control spline usage with the samples argument. --- opm/core/props/pvt/BlackoilPvtProperties.cpp | 14 ++++++++---- opm/core/props/pvt/SinglePvtDead.cpp | 24 ++++++++++++++++++++ opm/core/props/pvt/SinglePvtDead.hpp | 4 +++- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/opm/core/props/pvt/BlackoilPvtProperties.cpp b/opm/core/props/pvt/BlackoilPvtProperties.cpp index 0fd7dda4..79f74089 100644 --- a/opm/core/props/pvt/BlackoilPvtProperties.cpp +++ b/opm/core/props/pvt/BlackoilPvtProperties.cpp @@ -157,8 +157,11 @@ namespace Opm 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)); + if (samples > 0) { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDeadSpline(pvdoTable, samples)); + } else { + props_[phase_usage_.phase_pos[Liquid]].reset(new SinglePvtDead(pvdoTable)); + } } else if (newParserDeck->hasKeyword("PVTO")) { Opm::PvtoTable pvtoTable(newParserDeck->getKeyword("PVTO"), /*tableIdx=*/0); @@ -175,8 +178,11 @@ namespace Opm 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)); + if (samples > 0) { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDeadSpline(pvdgTable, samples)); + } else { + props_[phase_usage_.phase_pos[Vapour]].reset(new SinglePvtDead(pvdgTable)); + } } else if (newParserDeck->hasKeyword("PVTG")) { Opm::PvtgTable pvtgTable(newParserDeck->getKeyword("PVTG"), /*tableIdx=*/0); diff --git a/opm/core/props/pvt/SinglePvtDead.cpp b/opm/core/props/pvt/SinglePvtDead.cpp index fb11d2de..9046f5ec 100644 --- a/opm/core/props/pvt/SinglePvtDead.cpp +++ b/opm/core/props/pvt/SinglePvtDead.cpp @@ -86,6 +86,30 @@ namespace Opm // << "\n\nvisc\n\n" << viscosity_ << std::endl; } + /// Constructor + SinglePvtDead::SinglePvtDead(const Opm::PvdgTable& pvdgTable) + { + // Copy data + const std::vector& press = pvdgTable.getPressureColumn(); + const std::vector& b = pvdgTable.getFormationFactorColumn(); + const std::vector& visc = pvdgTable.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 703da0b8..3c96e36c 100644 --- a/opm/core/props/pvt/SinglePvtDead.hpp +++ b/opm/core/props/pvt/SinglePvtDead.hpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -43,7 +44,8 @@ namespace Opm public: typedef std::vector > > table_t; SinglePvtDead(const table_t& pvd_table); - SinglePvtDead(const Opm::PvdoTable &pvdoTable); + SinglePvtDead(const Opm::PvdoTable& pvdoTable); + SinglePvtDead(const Opm::PvdgTable& pvdgTable); virtual ~SinglePvtDead(); /// Viscosity as a function of p and z. From c68667458efb892e6771ee5aed380305d0bf175b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 3 Mar 2014 15:05:53 +0100 Subject: [PATCH 066/109] Defer searching for opm-parser The "opm-parser" module is expected to become dependent upon the ERT library. Defer searching for the parser module until we've established whether or not ERT is availble. Suggested by: @andlaus --- cmake/Modules/opm-core-prereqs.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index 58b627e3..b648b913 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -25,11 +25,11 @@ set (opm-core_DEPS "SuperLU" # xml processing (for config parsing) "TinyXML" - #Parser library - "opm-parser REQUIRED" # Ensembles-based Reservoir Tools (ERT) "ERT" # DUNE dependency "dune-common" "dune-istl" + #Parser library + "opm-parser REQUIRED" ) From 121c3911cb1eb664615ae43c65426cf9f826133a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 3 Mar 2014 15:44:16 +0100 Subject: [PATCH 067/109] Elucidate description string for opm-parser. The 'opm-parser' module is (currently) designed to parse ECL-type simulation models. Reflect that fact in its description. --- 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 b648b913..8b3d45de 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -30,6 +30,6 @@ set (opm-core_DEPS # DUNE dependency "dune-common" "dune-istl" - #Parser library + # Parser library for ECL-type simulation models "opm-parser REQUIRED" ) From 97328b5a1772848d6210b7d80975541a5725c385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 4 Mar 2014 11:31:16 +0100 Subject: [PATCH 068/109] Suppress warnings from included istl code. This is done using #pragma GCC and works also in clang. --- opm/core/linalg/LinearSolverIstl.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 77aaf4ca..65a820d3 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -28,6 +28,13 @@ // the deprecated member anyway (in this compilation unit) #define DUNE_COMMON_FIELDVECTOR_SIZE_IS_METHOD 1 +// Silence warnings from Dune. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wignored-qualifiers" +#pragma GCC diagnostic ignored "-Wmismatched-tags" + // TODO: clean up includes. #include #include @@ -39,6 +46,9 @@ #include #include +// Reinstate warnings. +#pragma GCC diagnostic pop + #include #include From f35c193b38f67e9343f6d7cf74c3b9fd8a42fb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 4 Mar 2014 11:32:32 +0100 Subject: [PATCH 069/109] Suppress unused argument warnings. --- opm/core/props/satfunc/SatFuncSimple.cpp | 8 +++--- opm/core/props/satfunc/SatFuncSimple.hpp | 14 +++++------ opm/core/props/satfunc/SatFuncStone2.hpp | 25 +++++++++---------- .../props/satfunc/SaturationPropsFromDeck.hpp | 4 +-- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/opm/core/props/satfunc/SatFuncSimple.cpp b/opm/core/props/satfunc/SatFuncSimple.cpp index 940f12de..46bba18b 100644 --- a/opm/core/props/satfunc/SatFuncSimple.cpp +++ b/opm/core/props/satfunc/SatFuncSimple.cpp @@ -34,9 +34,9 @@ namespace Opm template<> void SatFuncBase >::initializeTableType(NonuniformTableLinear & table, - const std::vector& arg, - const std::vector& value, - const int samples) + const std::vector& arg, + const std::vector& value, + 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 b1781ded..e331819a 100644 --- a/opm/core/props/satfunc/SatFuncSimple.hpp +++ b/opm/core/props/satfunc/SatFuncSimple.hpp @@ -31,17 +31,17 @@ 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; + 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: diff --git a/opm/core/props/satfunc/SatFuncStone2.hpp b/opm/core/props/satfunc/SatFuncStone2.hpp index 4eb5e2fd..71f32696 100644 --- a/opm/core/props/satfunc/SatFuncStone2.hpp +++ b/opm/core/props/satfunc/SatFuncStone2.hpp @@ -31,20 +31,19 @@ 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 - {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 - {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - 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 - {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} - 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 - {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} + 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 + {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} + 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 + {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} + 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 + {OPM_THROW(std::runtime_error, "SatFuncStone2 -- need to be implemented ...");} private: diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp index 2c570ffd..757b1481 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp @@ -173,8 +173,8 @@ namespace Opm const std::vector& krmax); bool columnIsMasked_(Opm::DeckConstPtr newParserDeck, - const std::string &keywordName, - int columnIdx) + const std::string& keywordName, + int /* columnIdx */) { return newParserDeck->getKeyword(keywordName)->getRecord(0)->getItem(0)->getSIDouble(0) != -1.0; } }; From ae4738f5e7f78cf6ad92f92b8275b7831b94df1d Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 4 Mar 2014 14:28:42 +0100 Subject: [PATCH 070/109] Changed open / close behaviour of well_controls: 1) well_controls has an explicit open_close flag. 2) Will NOT flip current value to change open|close status --- opm/core/pressure/tpfa/cfs_tpfa_residual.c | 4 ++-- opm/core/pressure/tpfa/ifs_tpfa.c | 2 +- .../simulator/SimulatorIncompTwophase.cpp | 2 +- opm/core/simulator/WellState.hpp | 6 ++--- opm/core/well_controls.h | 14 +++++++++-- opm/core/wells/WellsGroup.cpp | 6 ++--- opm/core/wells/WellsManager.cpp | 12 ++-------- opm/core/wells/well_controls.c | 23 +++++++++++++++---- tests/test_wellcontrols.cpp | 23 +++++++++++++++---- 9 files changed, 59 insertions(+), 33 deletions(-) diff --git a/opm/core/pressure/tpfa/cfs_tpfa_residual.c b/opm/core/pressure/tpfa/cfs_tpfa_residual.c index 4c2ca10e..b773408f 100644 --- a/opm/core/pressure/tpfa/cfs_tpfa_residual.c +++ b/opm/core/pressure/tpfa/cfs_tpfa_residual.c @@ -869,7 +869,7 @@ assemble_completion_to_well(int i, int w, int c, int nc, int np, W = wells->W; ctrl = W->ctrls[ w ]; - if (well_controls_get_current(ctrl) < 0) { + if (well_controls_well_is_shut( ctrl )) { /* Interpreting a negative current control index to mean a shut well */ welleq_coeff_shut(np, h, &res, &w2c, &w2w); } @@ -933,7 +933,7 @@ assemble_well_contrib(struct cfs_tpfa_res_wells *wells , for (w = i = 0; w < W->number_of_wells; w++) { pw = wpress[ w ]; - is_open = (well_controls_get_current(W->ctrls[w]) >= 0); + is_open = well_controls_well_is_open(W->ctrls[w]); for (; i < W->well_connpos[w + 1]; i++, pmobp += np) { diff --git a/opm/core/pressure/tpfa/ifs_tpfa.c b/opm/core/pressure/tpfa/ifs_tpfa.c index 5130ba0b..a9e2ce73 100644 --- a/opm/core/pressure/tpfa/ifs_tpfa.c +++ b/opm/core/pressure/tpfa/ifs_tpfa.c @@ -363,7 +363,7 @@ assemble_well_contrib(int nc , for (w = 0; w < W->number_of_wells; w++) { ctrls = W->ctrls[ w ]; - if (well_controls_get_current(ctrls) < 0) { + if (well_controls_well_is_shut(ctrls) ) { /* Treat this well as a shut well, isolated from the domain. */ diff --git a/opm/core/simulator/SimulatorIncompTwophase.cpp b/opm/core/simulator/SimulatorIncompTwophase.cpp index 146b5cf8..12871c76 100644 --- a/opm/core/simulator/SimulatorIncompTwophase.cpp +++ b/opm/core/simulator/SimulatorIncompTwophase.cpp @@ -310,7 +310,7 @@ namespace Opm const int nw = wells->number_of_wells; for (int w = 0; w < nw; ++w) { const WellControls* wc = wells->ctrls[w]; - if (well_controls_get_current(wc) >= 0) { + if (well_controls_well_is_open( wc )) { if (well_controls_get_current_type(wc) == BHP) { return false; } diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index 51d2c3b9..39fbdbd3 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -51,8 +51,7 @@ namespace Opm // above or below (depending on if the well is an // injector or producer) pressure in first perforation // cell. - if ((well_controls_get_current(ctrl) < 0) || // SHUT - (well_controls_get_current_type(ctrl) != BHP)) { + if (well_controls_well_is_shut(ctrl) || (well_controls_get_current_type(ctrl) != BHP)) { const int first_cell = wells->well_cells[wells->well_connpos[w]]; const double safety_factor = (wells->type[w] == INJECTOR) ? 1.01 : 0.99; bhp_[w] = safety_factor*state.pressure()[first_cell]; @@ -61,8 +60,7 @@ namespace Opm } // Initialize well rates to match controls if type is SURFACE_RATE - if ((well_controls_get_current(ctrl) >= 0) && // open well - (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { + if (well_controls_well_is_open( ctrl ) || (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { const double rate_target = well_controls_get_current_target(ctrl); const double * distr = well_controls_get_current_distr( ctrl ); for (int p = 0; p < np; ++p) { diff --git a/opm/core/well_controls.h b/opm/core/well_controls.h index 435c7a79..6998f482 100644 --- a/opm/core/well_controls.h +++ b/opm/core/well_controls.h @@ -55,8 +55,18 @@ well_controls_get_current( const struct WellControls * ctrl); void well_controls_set_current( struct WellControls * ctrl, int current); -void -well_controls_invert_current( struct WellControls * ctrl ); + +bool +well_controls_well_is_shut(const struct WellControls * ctrl); + +bool +well_controls_well_is_open(const struct WellControls * ctrl); + +void +well_controls_open_well( struct WellControls * ctrl); + +void +well_controls_shut_well( struct WellControls * ctrl); int well_controls_add_new(enum WellControlType type , double target , const double * distr , struct WellControls * ctrl); diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index 3316ccdb..ee45b6ed 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -747,9 +747,7 @@ namespace Opm void WellNode::shutWell() { if (shut_well_) { - // We set the tilde of the current control - // set_current_control(self_index_, -1, wells_); - well_controls_invert_current(wells_->ctrls[self_index_]); + well_controls_shut_well( wells_->ctrls[self_index_]); } else { const double target = 0.0; @@ -767,7 +765,7 @@ namespace Opm well_controls_iset_target( wells_->ctrls[self_index_] , group_control_index_ , target); well_controls_iset_distr(wells_->ctrls[self_index_] , group_control_index_ , distr); } - well_controls_invert_current(wells_->ctrls[self_index_]); + well_controls_open_well( wells_->ctrls[self_index_]); } } diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 64091120..7df448cf 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -869,17 +869,9 @@ namespace Opm } 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); + well_controls_shut_well( w_->ctrls[index] ); } 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); + well_controls_open_well( w_->ctrls[index] ); } else { OPM_THROW(std::runtime_error, "Unknown Open/close keyword: \"" << line.openshutflag_<< "\". Allowed values: OPEN, SHUT."); } diff --git a/opm/core/wells/well_controls.c b/opm/core/wells/well_controls.c index 5e52d541..e4b33a35 100644 --- a/opm/core/wells/well_controls.c +++ b/opm/core/wells/well_controls.c @@ -98,6 +98,8 @@ struct WellControls */ int current; + bool well_is_open; + /* The capacity allocated. */ @@ -130,7 +132,7 @@ well_controls_create(void) ctrl = malloc(1 * sizeof *ctrl); if (ctrl != NULL) { - /* Initialise empty control set */ + /* Initialise empty control set; the well is created open. */ ctrl->num = 0; ctrl->number_of_phases = 0; ctrl->type = NULL; @@ -138,6 +140,7 @@ well_controls_create(void) ctrl->distr = NULL; ctrl->current = -1; ctrl->cpty = 0; + ctrl->well_is_open = true; } return ctrl; @@ -192,11 +195,23 @@ well_controls_set_current( struct WellControls * ctrl, int current) { ctrl->current = current; } -void -well_controls_invert_current( struct WellControls * ctrl ) { - ctrl->current = ~ctrl->current; +bool well_controls_well_is_shut(const struct WellControls * ctrl) { + return !ctrl->well_is_open; } +bool well_controls_well_is_open(const struct WellControls * ctrl) { + return ctrl->well_is_open; +} + +void well_controls_open_well( struct WellControls * ctrl) { + ctrl->well_is_open = true; +} + +void well_controls_shut_well( struct WellControls * ctrl) { + ctrl->well_is_open = false; +} + + enum WellControlType well_controls_iget_type(const struct WellControls * ctrl, int control_index) { diff --git a/tests/test_wellcontrols.cpp b/tests/test_wellcontrols.cpp index 096b9c6a..8424ab0b 100644 --- a/tests/test_wellcontrols.cpp +++ b/tests/test_wellcontrols.cpp @@ -45,11 +45,6 @@ BOOST_AUTO_TEST_CASE(Construction) well_controls_set_current( ctrls , 2 ); BOOST_CHECK_EQUAL( 2 , well_controls_get_current( ctrls )); - well_controls_invert_current( ctrls ); - BOOST_CHECK( well_controls_get_current( ctrls ) < 0 ); - well_controls_invert_current( ctrls ); - BOOST_CHECK_EQUAL( 2 , well_controls_get_current( ctrls )); - { enum WellControlType type1 = BHP; enum WellControlType type2 = SURFACE_RATE; @@ -103,3 +98,21 @@ BOOST_AUTO_TEST_CASE(Construction) } +BOOST_AUTO_TEST_CASE(OpenClose) +{ + struct WellControls * ctrls = well_controls_create(); + + BOOST_CHECK_EQUAL( true , well_controls_well_is_open(ctrls) ); + BOOST_CHECK_EQUAL( false , well_controls_well_is_shut(ctrls) ); + + well_controls_open_well( ctrls ); + BOOST_CHECK_EQUAL( true , well_controls_well_is_open(ctrls) ); + BOOST_CHECK_EQUAL( false , well_controls_well_is_shut(ctrls) ); + + well_controls_shut_well( ctrls ); + BOOST_CHECK_EQUAL( false , well_controls_well_is_open(ctrls) ); + BOOST_CHECK_EQUAL( true , well_controls_well_is_shut(ctrls) ); + + well_controls_destroy( ctrls ); +} + From ec22c9b49176bbecd4e75b0a573987b25ea64423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Wed, 5 Mar 2014 08:41:53 +0100 Subject: [PATCH 071/109] Remove warning pragmas. They are only available on gcc 4.6 and newer, and clang. --- opm/core/linalg/LinearSolverIstl.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 65a820d3..77aaf4ca 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -28,13 +28,6 @@ // the deprecated member anyway (in this compilation unit) #define DUNE_COMMON_FIELDVECTOR_SIZE_IS_METHOD 1 -// Silence warnings from Dune. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Wignored-qualifiers" -#pragma GCC diagnostic ignored "-Wmismatched-tags" - // TODO: clean up includes. #include #include @@ -46,9 +39,6 @@ #include #include -// Reinstate warnings. -#pragma GCC diagnostic pop - #include #include From 9edbe36b5840800b0048b4006a879bec0f5fbac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Thu, 6 Mar 2014 08:24:00 +0100 Subject: [PATCH 072/109] Remove extra semicolon. --- opm/core/props/satfunc/SatFuncSimple.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/props/satfunc/SatFuncSimple.cpp b/opm/core/props/satfunc/SatFuncSimple.cpp index 46bba18b..91200053 100644 --- a/opm/core/props/satfunc/SatFuncSimple.cpp +++ b/opm/core/props/satfunc/SatFuncSimple.cpp @@ -217,7 +217,7 @@ namespace Opm out << "sg_shift: " << sg_shift << std::endl; out << "sow_hyst: " << sow_hyst << std::endl; out << "sow_shift: " << sow_shift << std::endl; - }; + } } // namespace Opm From e510dde102541f5a8b5f6986534b510679de9f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 10 Mar 2014 13:30:43 +0100 Subject: [PATCH 073/109] Initialize well rates better. Instead of making well rates zero for wells that are not controlled by surface volume, we initialize them to a small value with the correct sign (positive for injectors, negative for producers). --- opm/core/simulator/WellState.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index 39fbdbd3..adf3fb24 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -59,13 +59,20 @@ namespace Opm bhp_[w] = well_controls_get_current_target( ctrl ); } - // Initialize well rates to match controls if type is SURFACE_RATE + // Initialize well rates to match controls if type is SURFACE_RATE, + // otherwise set to a small rate with the correct sign. if (well_controls_well_is_open( ctrl ) || (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { const double rate_target = well_controls_get_current_target(ctrl); const double * distr = well_controls_get_current_distr( ctrl ); for (int p = 0; p < np; ++p) { wellrates_[np*w + p] = rate_target * distr[p]; } + } else { + const double small_rate = 1e-14; + const double sign = (wells->type[w] == INJECTOR) ? 1.0 : -1.0; + for (int p = 0; p < np; ++p) { + wellrates_[np*w + p] = small_rate * sign; + } } } // The perforation rates and perforation pressures are From dbd0635fdadde1d6873d03102cbe1e1b30e637fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 11 Mar 2014 12:50:58 +0100 Subject: [PATCH 074/109] Commented choices in initialisation of well rates. --- opm/core/simulator/WellState.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index adf3fb24..31391458 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -59,8 +59,10 @@ namespace Opm bhp_[w] = well_controls_get_current_target( ctrl ); } - // Initialize well rates to match controls if type is SURFACE_RATE, - // otherwise set to a small rate with the correct sign. + // Initialize well rates to match controls if type is SURFACE_RATE. + // Otherwise, we cannot set the correct value here, so we assign + // a small rate with the correct sign so that any logic depending on + // that sign will work as expected. if (well_controls_well_is_open( ctrl ) || (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { const double rate_target = well_controls_get_current_target(ctrl); const double * distr = well_controls_get_current_distr( ctrl ); From c46ad990c16c5fe2c57a9aab6c6c57bd1c43d969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 11 Mar 2014 12:51:49 +0100 Subject: [PATCH 075/109] Assert to avoid future surprises. Well types must be either INJECTOR or PRODUCER for now, if we change this in the future, we should check this part of the code as well. --- opm/core/simulator/WellState.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index 31391458..d46e1254 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace Opm { @@ -45,6 +46,7 @@ namespace Opm bhp_.resize(nw); wellrates_.resize(nw * np, 0.0); for (int w = 0; w < nw; ++w) { + assert((wells->type[w] == INJECTOR) || (wells->type[w] == PRODUCER)); const WellControls* ctrl = wells->ctrls[w]; // Initialize bhp to be target pressure if // bhp-controlled well, otherwise set to a little From f1377d9729eecf7a8aecb0024a8e192fabb9e7a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Tue, 11 Mar 2014 12:54:04 +0100 Subject: [PATCH 076/109] Bugfix: || -> && --- opm/core/simulator/WellState.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index d46e1254..c12e68bd 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -65,7 +65,7 @@ namespace Opm // Otherwise, we cannot set the correct value here, so we assign // a small rate with the correct sign so that any logic depending on // that sign will work as expected. - if (well_controls_well_is_open( ctrl ) || (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { + if (well_controls_well_is_open( ctrl ) && (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { const double rate_target = well_controls_get_current_target(ctrl); const double * distr = well_controls_get_current_distr( ctrl ); for (int p = 0; p < np; ++p) { From c34d7a40fccfd46381d4c7af46910811af156089 Mon Sep 17 00:00:00 2001 From: "Kari B. Skjerve" Date: Tue, 11 Mar 2014 14:51:23 +0100 Subject: [PATCH 077/109] Added support of SWCR and SOWCR in CornerpointChopper --- opm/core/io/eclipse/CornerpointChopper.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/opm/core/io/eclipse/CornerpointChopper.hpp b/opm/core/io/eclipse/CornerpointChopper.hpp index 6bca2e61..0441b781 100644 --- a/opm/core/io/eclipse/CornerpointChopper.hpp +++ b/opm/core/io/eclipse/CornerpointChopper.hpp @@ -236,6 +236,8 @@ namespace Opm filterIntegerField("ACTNUM", new_ACTNUM_); filterDoubleField("PORO", new_PORO_); filterDoubleField("NTG", new_NTG_); + filterDoubleField("SWCR", new_SWCR_); + filterDoubleField("SOWCR", new_SOWCR_); filterDoubleField("PERMX", new_PERMX_); filterDoubleField("PERMY", new_PERMY_); filterDoubleField("PERMZ", new_PERMZ_); @@ -264,6 +266,8 @@ namespace Opm if (!new_ACTNUM_.empty()) sp.setIntegerField("ACTNUM", new_ACTNUM_); if (!new_PORO_.empty()) sp.setFloatingPointField("PORO", new_PORO_); if (!new_NTG_.empty()) sp.setFloatingPointField("NTG", new_NTG_); + if (!new_SWCR_.empty()) sp.setFloatingPointField("SWCR", new_SWCR_); + if (!new_SOWCR_.empty()) sp.setFloatingPointField("SOWCR", new_SOWCR_); if (!new_PERMX_.empty()) sp.setFloatingPointField("PERMX", new_PERMX_); if (!new_PERMY_.empty()) sp.setFloatingPointField("PERMY", new_PERMY_); if (!new_PERMZ_.empty()) sp.setFloatingPointField("PERMZ", new_PERMZ_); @@ -291,12 +295,16 @@ namespace Opm outputField(out, new_ACTNUM_, "ACTNUM"); outputField(out, new_PORO_, "PORO"); if (hasNTG()) {outputField(out, new_NTG_, "NTG");} + if (hasSWCR()) {outputField(out, new_SWCR_, "SWCR");} + if (hasSOWCR()) {outputField(out, new_SOWCR_, "SOWCR");} outputField(out, new_PERMX_, "PERMX"); outputField(out, new_PERMY_, "PERMY"); outputField(out, new_PERMZ_, "PERMZ"); outputField(out, new_SATNUM_, "SATNUM"); } bool hasNTG() const {return !new_NTG_.empty(); } + bool hasSWCR() const {return !new_SWCR_.empty(); } + bool hasSOWCR() const {return !new_SOWCR_.empty(); } private: EclipseGridParser parser_; @@ -309,6 +317,8 @@ namespace Opm std::vector new_ACTNUM_; std::vector new_PORO_; std::vector new_NTG_; + std::vector new_SWCR_; + std::vector new_SOWCR_; std::vector new_PERMX_; std::vector new_PERMY_; std::vector new_PERMZ_; From 53656841b5e6a705e3b5c4011b5778b8513ad20f Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 11 Mar 2014 18:21:52 +0100 Subject: [PATCH 078/109] =?UTF-8?q?Only=20look=20for=20BOOST=C2=B4s=20unit?= =?UTF-8?q?=20test=20framework=20module=20if=20not=20already=20found.=20Sy?= =?UTF-8?q?nc=20version=20with=20rest=20of=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmake/Modules/UseDynamicBoost.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/Modules/UseDynamicBoost.cmake b/cmake/Modules/UseDynamicBoost.cmake index 590d23a1..dbfb937c 100644 --- a/cmake/Modules/UseDynamicBoost.cmake +++ b/cmake/Modules/UseDynamicBoost.cmake @@ -1,5 +1,6 @@ -# be sure that this component is searched for -find_package (Boost COMPONENTS unit_test_framework QUIET) +if (NOT ${Boost_UNIT_TEST_FRAMEWORK_FOUND}) + find_package (Boost 1.44.0 COMPONENTS unit_test_framework QUIET) +endif () if (${Boost_UNIT_TEST_FRAMEWORK_FOUND}) # setup to do a test compile From 1f94abcf14770a0757cc3bd69a22d6f7420ce828 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 12 Mar 2014 17:41:40 +0100 Subject: [PATCH 079/109] Removed cpos = ~cpos constructions for closing a well; using well_controls_shut_well explicitly instead. --- 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 7df448cf..2deaea99 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -669,7 +669,7 @@ namespace Opm } // We need to check if the well is shut or not if (wci_line.open_shut_flag_ == "SHUT") { - cpos = ~cpos; + well_controls_shut_well( w_->ctrls[wix]); } set_current_control(wix, cpos, w_); @@ -797,7 +797,7 @@ namespace Opm } // 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 + well_controls_shut_well( w_->ctrls[wix] ); } set_current_control(wix, cpos, w_); } @@ -1204,7 +1204,7 @@ namespace Opm // We need to check if the well is shut or not if (well->getStatus( timeStep ) == WellCommon::SHUT) { - cpos = ~cpos; + well_controls_shut_well( w_->ctrls[well_index] ); } set_current_control(well_index, cpos, w_); } @@ -1334,7 +1334,7 @@ namespace Opm } // If it's shut, we complement the cpos if (well->getStatus(timeStep) == WellCommon::SHUT) { - cpos = ~cpos; // So we can easily retrieve the cpos later + well_controls_shut_well( w_->ctrls[well_index] ); } set_current_control(well_index, cpos, w_); } From 3aef6578f97944cdf7de74f45879ca175ce47e1a Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 11 Mar 2014 15:06:07 +0100 Subject: [PATCH 080/109] SimulatorTimer: rename currentTime() to simulationTimeElapsed() because the name "currentTime()" can be mistaken for the point in real-life time at which the simulation is run (e.g. March 11, 2014, 15:07:45.123), the _point_ in time which the simulator timer currently represents (e.g. Jun 5, 1985, 02:33:12.345) instead of the simulator time in seconds which elapsed since the START date (e.g. 52633.345 s). this rename may lead to some fallout in other modules. I'll fix them after this PR has been merged... --- opm/core/simulator/SimulatorCompressibleTwophase.cpp | 4 ++-- opm/core/simulator/SimulatorIncompTwophase.cpp | 4 ++-- opm/core/simulator/SimulatorOutput.cpp | 2 +- opm/core/simulator/SimulatorTimer.cpp | 12 +++++++++--- opm/core/simulator/SimulatorTimer.hpp | 9 +++++++-- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/opm/core/simulator/SimulatorCompressibleTwophase.cpp b/opm/core/simulator/SimulatorCompressibleTwophase.cpp index a5760716..7284d8d4 100644 --- a/opm/core/simulator/SimulatorCompressibleTwophase.cpp +++ b/opm/core/simulator/SimulatorCompressibleTwophase.cpp @@ -498,13 +498,13 @@ namespace Opm << std::endl; std::cout.precision(8); - watercut.push(timer.currentTime() + timer.currentStepLength(), + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), state.saturation(), - timer.currentTime() + timer.currentStepLength(), + timer.simulationTimeElapsed() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } sreport.total_time = step_timer.secsSinceStart(); diff --git a/opm/core/simulator/SimulatorIncompTwophase.cpp b/opm/core/simulator/SimulatorIncompTwophase.cpp index 12871c76..271cc8fe 100644 --- a/opm/core/simulator/SimulatorIncompTwophase.cpp +++ b/opm/core/simulator/SimulatorIncompTwophase.cpp @@ -578,12 +578,12 @@ namespace Opm dynamic_cast(*tsolver_) .solveGravity(&initial_porevol[0], stepsize, state); } - watercut.push(timer.currentTime() + timer.currentStepLength(), + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { wellreport.push(props_, *wells_, state.saturation(), - timer.currentTime() + timer.currentStepLength(), + timer.simulationTimeElapsed() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } } diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index fd7bf85b..ddb375af 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -72,7 +72,7 @@ SimulatorOutputBase::operator std::function () { void SimulatorOutputBase::writeOutput () { - const int this_time = timer_->currentTime (); + const int this_time = timer_->simulationTimeElapsed (); // if the simulator signals for timesteps that aren't reporting // times, then ignore them diff --git a/opm/core/simulator/SimulatorTimer.cpp b/opm/core/simulator/SimulatorTimer.cpp index fddc3aa8..b672b613 100644 --- a/opm/core/simulator/SimulatorTimer.cpp +++ b/opm/core/simulator/SimulatorTimer.cpp @@ -113,8 +113,8 @@ namespace Opm return timesteps_[current_step_ - 1]; } - /// Current time. - double SimulatorTimer::currentTime() const + /// time elapsed since the start of the simulation [s]. + double SimulatorTimer::simulationTimeElapsed() const { if (timeMap_) return timeMap_->getTimePassedUntil(current_step_); @@ -122,6 +122,12 @@ namespace Opm return current_time_; } + /// time elapsed since the start of the POSIX epoch (Jan 1st, 1970) [s]. + time_t SimulatorTimer::currentPosixTime() const + { + tm t = boost::posix_time::to_tm(currentDateTime()); + return std::mktime(&t); + } boost::posix_time::ptime SimulatorTimer::currentDateTime() const { @@ -161,7 +167,7 @@ namespace Opm void SimulatorTimer::report(std::ostream& os) const { os << "\n\n--------------- Simulation step number " << currentStepNum() << " ---------------" - << "\n Current time (days) " << Opm::unit::convert::to(currentTime(), Opm::unit::day) + << "\n Current time (days) " << Opm::unit::convert::to(simulationTimeElapsed(), Opm::unit::day) << "\n Current stepsize (days) " << Opm::unit::convert::to(currentStepLength(), Opm::unit::day) << "\n Total time (days) " << Opm::unit::convert::to(totalTime(), Opm::unit::day) << "\n" << std::endl; diff --git a/opm/core/simulator/SimulatorTimer.hpp b/opm/core/simulator/SimulatorTimer.hpp index 949859e5..ad49282f 100644 --- a/opm/core/simulator/SimulatorTimer.hpp +++ b/opm/core/simulator/SimulatorTimer.hpp @@ -79,8 +79,13 @@ namespace Opm /// it is an error to call stepLengthTaken(). double stepLengthTaken () const; - /// Current time. - double currentTime() const; + /// Time elapsed since the start of the POSIX epoch (Jan 1st, + /// 1970) until the current time step begins [s]. + time_t currentPosixTime() const; + + /// Time elapsed since the start of the simulation until the + /// beginning of the current time step [s]. + double simulationTimeElapsed() const; /// Return the current time as a posix time object. boost::posix_time::ptime currentDateTime() const; From 58f96a5808f40fc5204467e89a4220c8fc3cb182 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 12 Mar 2014 18:18:40 +0100 Subject: [PATCH 081/109] Will check if a well is open before throwing for an invalid control. --- opm/core/wells/WellsManager.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 2deaea99..c438b29e 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -664,12 +664,11 @@ namespace Opm } 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") { well_controls_shut_well( w_->ctrls[wix]); + } else if (cpos == -1 && mode != InjectionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control for " << wci_line.control_mode_ << " not specified in well " << well_names[wix]); } set_current_control(wix, cpos, w_); @@ -1329,12 +1328,10 @@ namespace Opm 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) { well_controls_shut_well( w_->ctrls[well_index] ); + } else if (cpos == -1 && mode != ProductionControl::GRUP) { + OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]); } set_current_control(well_index, cpos, w_); } From 31c09aed093d5bc46dc65c0125ccc4edb4149b10 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 13 Mar 2014 12:00:01 +0100 Subject: [PATCH 082/109] Apply changes from commit a953ba8 to new parser code too. The mentioned commit was applied before the merge of opm-parser-integrate and therefore the changes did not carry over to the code that uses the new parser. This code mimics the changed behaviour for the new parser. Closes issue #516 --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index 8aaab271..9bba7747 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -51,9 +51,9 @@ namespace Opm } pvt_.init(newParserDeck, /*numSamples=*/200); SaturationPropsFromDeck* ptr - = new SaturationPropsFromDeck(); + = new SaturationPropsFromDeck(); satprops_.reset(ptr); - ptr->init(newParserDeck, grid, /*numSamples=*/200); + ptr->init(newParserDeck, grid, /*numSamples=*/0); if (pvt_.numPhases() != satprops_->numPhases()) { OPM_THROW(std::runtime_error, "BlackoilPropertiesFromDeck::BlackoilPropertiesFromDeck() - Inconsistent number of phases in pvt data (" @@ -134,11 +134,11 @@ namespace Opm rock_.init(newParserDeck, grid); } - const int pvt_samples = param.getDefault("pvt_tab_size", 200); + const int pvt_samples = param.getDefault("pvt_tab_size", 0); pvt_.init(newParserDeck, pvt_samples); // Unfortunate lack of pointer smartness here... - const int sat_samples = param.getDefault("sat_tab_size", 200); + const int sat_samples = param.getDefault("sat_tab_size", 0); 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."); From 21bf14de0d6f1a83a46b4553a8764867213693c9 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 13 Mar 2014 12:13:11 +0100 Subject: [PATCH 083/109] Fixed missed on occurence of number of samples --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index 9bba7747..62048ba0 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -49,7 +49,7 @@ namespace Opm if (init_rock){ rock_.init(newParserDeck, grid); } - pvt_.init(newParserDeck, /*numSamples=*/200); + pvt_.init(newParserDeck, /*numSamples=*/0); SaturationPropsFromDeck* ptr = new SaturationPropsFromDeck(); satprops_.reset(ptr); From de792c72c7f84fa47f463c97b609e8612fa6305b Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 13 Mar 2014 16:33:32 +0100 Subject: [PATCH 084/109] initStateFromDeck(): do not accept potentially contradicting ways to set the initial condition i.e. PRESSURE and EQUIL are now considered mutually exclusive... --- opm/core/simulator/initState_impl.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/opm/core/simulator/initState_impl.hpp b/opm/core/simulator/initState_impl.hpp index 18372573..89ff8923 100644 --- a/opm/core/simulator/initState_impl.hpp +++ b/opm/core/simulator/initState_impl.hpp @@ -488,6 +488,11 @@ namespace Opm "found " << pu.num_phases << " phases in deck."); } state.init(grid, num_phases); + if (newParserDeck->hasKeyword("EQUIL") && newParserDeck->hasKeyword("PRESSURE")) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): The deck must either specify the initial " + "condition using the PRESSURE _or_ the EQUIL keyword (currently it has both)"); + } + if (newParserDeck->hasKeyword("EQUIL")) { if (num_phases != 2) { OPM_THROW(std::runtime_error, "initStateFromDeck(): EQUIL-based init currently handling only two-phase scenarios."); @@ -584,6 +589,10 @@ namespace Opm OPM_THROW(std::runtime_error, "initStateFromDeck(): user specified property object with " << num_phases << " phases, " "found " << pu.num_phases << " phases in deck."); } + if (deck.hasField("EQUIL") && deck.hasField("PRESSURE")) { + OPM_THROW(std::runtime_error, "initStateFromDeck(): The deck must either specify the initial " + "condition using the PRESSURE _or_ the EQUIL keyword (currently it has both)"); + } state.init(grid, num_phases); if (deck.hasField("EQUIL")) { if (num_phases != 2) { From 5fff1d25dc67dfb07540cbebc1e96ee574d52017 Mon Sep 17 00:00:00 2001 From: Liu Ming Date: Fri, 14 Mar 2014 15:34:38 +0800 Subject: [PATCH 085/109] Read keyword PLYSHEAR. --- opm/core/io/eclipse/EclipseGridParser.cpp | 1 + opm/core/io/eclipse/EclipseGridParser.hpp | 1 + opm/core/io/eclipse/SpecialEclipseFields.hpp | 36 ++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/opm/core/io/eclipse/EclipseGridParser.cpp b/opm/core/io/eclipse/EclipseGridParser.cpp index b050bdc4..e90ad00a 100644 --- a/opm/core/io/eclipse/EclipseGridParser.cpp +++ b/opm/core/io/eclipse/EclipseGridParser.cpp @@ -126,6 +126,7 @@ namespace EclipseKeywords string("EQUIL"), string("PVCDO"), string("TSTEP"), string("PLYVISC"), string("PLYROCK"), string("PLYADS"), string("PLYMAX"), string("TLMIXPAR"), string("WPOLYMER"), + string("PLYSHEAR"), string("GRUPTREE"), string("GCONINJE"), string("GCONPROD"), string("WGRUPCON"), string("ENDSCALE"), string("SCALECRS"), string("ENPTVD"), string("ENKRVD"), diff --git a/opm/core/io/eclipse/EclipseGridParser.hpp b/opm/core/io/eclipse/EclipseGridParser.hpp index edf5c3fe..bdc9749d 100644 --- a/opm/core/io/eclipse/EclipseGridParser.hpp +++ b/opm/core/io/eclipse/EclipseGridParser.hpp @@ -195,6 +195,7 @@ public: SPECIAL_FIELD(PLYROCK) SPECIAL_FIELD(PLYADS) SPECIAL_FIELD(PLYMAX) + SPECIAL_FIELD(PLYSHEAR) SPECIAL_FIELD(TLMIXPAR) SPECIAL_FIELD(WPOLYMER) SPECIAL_FIELD(GRUPTREE) diff --git a/opm/core/io/eclipse/SpecialEclipseFields.hpp b/opm/core/io/eclipse/SpecialEclipseFields.hpp index 98432f4f..6dc46ff9 100644 --- a/opm/core/io/eclipse/SpecialEclipseFields.hpp +++ b/opm/core/io/eclipse/SpecialEclipseFields.hpp @@ -2094,6 +2094,42 @@ struct PLYMAX : public SpecialBase }; +struct PLYSHEAR : public SpecialBase +{ + std::vector water_velocity_; + std::vector vrf_; + + virtual std::string name() const {return std::string("PLYSHEAR");} + + virtual void read(std::istream& is) + { + // Note. This function assumes that NTSFUN = 1, and reads only one table. + std::vector plyshear; + readVectorData(is, plyshear); + for (int i=0; i<(int)plyshear.size(); i+=2) { + water_velocity_.push_back(plyshear[i]); + vrf_.push_back(plyshear[i+1]); + } + } + + virtual void write(std::ostream& os) const + { + os << name() << '\n'; + for (int i=0; i<(int)water_velocity_.size(); ++i) { + os << water_velocity_[i] << " " << vrf_[i] << '\n'; + } + os << '\n'; + } + + virtual void convertToSI(const EclipseUnits& units) + { + for (int i=0; i<(int)water_velocity_.size(); ++i) { + water_velocity_[i] *= units.length / units.time; + } + } +}; + + struct TLMIXPAR : public SpecialBase { std::vector tlmixpar_; From d2e6ccec33d829210afec5764a834e83c16f1e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Fri, 14 Mar 2014 11:27:20 +0100 Subject: [PATCH 086/109] Fix logic related to open/shut wells initialisation. --- opm/core/simulator/WellState.hpp | 69 ++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/opm/core/simulator/WellState.hpp b/opm/core/simulator/WellState.hpp index c12e68bd..090676ee 100644 --- a/opm/core/simulator/WellState.hpp +++ b/opm/core/simulator/WellState.hpp @@ -48,34 +48,53 @@ namespace Opm for (int w = 0; w < nw; ++w) { assert((wells->type[w] == INJECTOR) || (wells->type[w] == PRODUCER)); const WellControls* ctrl = wells->ctrls[w]; - // Initialize bhp to be target pressure if - // bhp-controlled well, otherwise set to a little - // above or below (depending on if the well is an - // injector or producer) pressure in first perforation - // cell. - if (well_controls_well_is_shut(ctrl) || (well_controls_get_current_type(ctrl) != BHP)) { - const int first_cell = wells->well_cells[wells->well_connpos[w]]; - const double safety_factor = (wells->type[w] == INJECTOR) ? 1.01 : 0.99; - bhp_[w] = safety_factor*state.pressure()[first_cell]; - } else { - bhp_[w] = well_controls_get_current_target( ctrl ); - } - - // Initialize well rates to match controls if type is SURFACE_RATE. - // Otherwise, we cannot set the correct value here, so we assign - // a small rate with the correct sign so that any logic depending on - // that sign will work as expected. - if (well_controls_well_is_open( ctrl ) && (well_controls_get_current_type(ctrl) == SURFACE_RATE)) { - const double rate_target = well_controls_get_current_target(ctrl); - const double * distr = well_controls_get_current_distr( ctrl ); + if (well_controls_well_is_shut(ctrl)) { + // Shut well: + // 1. Assign zero well rates. for (int p = 0; p < np; ++p) { - wellrates_[np*w + p] = rate_target * distr[p]; + wellrates_[np*w + p] = 0.0; + } + // 2. Assign bhp equal to bhp control, if + // applicable, otherwise assign equal to + // first perforation cell pressure. + if (well_controls_get_current_type(ctrl) == BHP) { + bhp_[w] = well_controls_get_current_target( ctrl ); + } else { + const int first_cell = wells->well_cells[wells->well_connpos[w]]; + bhp_[w] = state.pressure()[first_cell]; } } else { - const double small_rate = 1e-14; - const double sign = (wells->type[w] == INJECTOR) ? 1.0 : -1.0; - for (int p = 0; p < np; ++p) { - wellrates_[np*w + p] = small_rate * sign; + // Open well: + // 1. Initialize well rates to match controls + // if type is SURFACE_RATE. Otherwise, we + // cannot set the correct value here, so we + // assign a small rate with the correct + // sign so that any logic depending on that + // sign will work as expected. + if (well_controls_get_current_type(ctrl) == SURFACE_RATE) { + const double rate_target = well_controls_get_current_target(ctrl); + const double * distr = well_controls_get_current_distr( ctrl ); + for (int p = 0; p < np; ++p) { + wellrates_[np*w + p] = rate_target * distr[p]; + } + } else { + const double small_rate = 1e-14; + const double sign = (wells->type[w] == INJECTOR) ? 1.0 : -1.0; + for (int p = 0; p < np; ++p) { + wellrates_[np*w + p] = small_rate * sign; + } + } + // 2. Initialize bhp to be target pressure if + // bhp-controlled well, otherwise set to a + // little above or below (depending on if + // the well is an injector or producer) + // pressure in first perforation cell. + if (well_controls_get_current_type(ctrl) == BHP) { + bhp_[w] = well_controls_get_current_target( ctrl ); + } else { + const int first_cell = wells->well_cells[wells->well_connpos[w]]; + const double safety_factor = (wells->type[w] == INJECTOR) ? 1.01 : 0.99; + bhp_[w] = safety_factor*state.pressure()[first_cell]; } } } From 314b3a32d87af4de808247b4cfaf533f2af5a221 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 14 Mar 2014 12:54:10 +0100 Subject: [PATCH 087/109] some cleanups for EclipseWriter - the output=true|false parameter gets respected now - if output is "true", it is checked that the value of the "output_dir" parameter is a directory (although I did not find an easy way to check whether it is writable short of writing a file to it.) - the "output_interval" parameter gets respected as well - the index of a written timestep is now independent of the time step index of the simulation: the initial solution is always 0, the first result written to disk is always 1 and so on... (i.e., it does not matter anymore how many steps the simulator needed between two writes) these changes have been proposed by @atgeirr. Thanks! --- opm/core/io/eclipse/EclipseWriter.cpp | 168 +++++++++++++++++--------- opm/core/io/eclipse/EclipseWriter.hpp | 8 +- 2 files changed, 116 insertions(+), 60 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 2ac553d5..c13aef46 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1,5 +1,5 @@ - /* - Copyright (c) 2013 Andreas Lauser +/* + Copyright (c) 2013-2014 Andreas Lauser Copyright (c) 2013 SINTEF ICT, Applied Mathematics. Copyright (c) 2013 Uni Research AS @@ -18,9 +18,7 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . */ -#if HAVE_CONFIG_H #include "config.h" -#endif // HAVE_CONFIG_H #include "EclipseWriter.hpp" @@ -259,14 +257,6 @@ EclipseKeyword ::EclipseKeyword ( copyData (data, transf, offset, stride); } -/** - * Extract the current time from a timer object into the C type used by ERT. - */ -static time_t current (const SimulatorTimer& timer) { - tm t = boost::posix_time::to_tm (timer.currentDateTime()); - return std::mktime(&t); -} - /** * Pointer to memory that holds the name to an Eclipse output file. */ @@ -274,7 +264,7 @@ struct EclipseFileName : public EclipseHandle { EclipseFileName (const std::string& outputDir, const std::string& baseName, ecl_file_enum type, - const SimulatorTimer& timer) + int outputStepIdx) // filename formatting function returns a pointer to allocated // memory that must be released with the free() function @@ -283,7 +273,7 @@ struct EclipseFileName : public EclipseHandle { baseName.c_str(), type, false, // formatted? - timer.currentStepNum ()), + outputStepIdx), freestr) { } private: /// Facade which allows us to free a const char* @@ -325,7 +315,8 @@ static int phaseMask (const PhaseUsage uses) { struct EclipseRestart : public EclipseHandle { EclipseRestart (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) + const SimulatorTimer& timer, + int outputStepIdx) // notice the poor man's polymorphism of the allocation function : EclipseHandle ( (timer.currentStepNum () > 0 ? ecl_rst_file_open_append @@ -333,18 +324,19 @@ struct EclipseRestart : public EclipseHandle { EclipseFileName (outputDir, baseName, ECL_UNIFIED_RESTART_FILE, - timer)), + outputStepIdx)), ecl_rst_file_close) { } void writeHeader (const SimulatorTimer& timer, + int outputStepIdx, const PhaseUsage uses, const EclipseGridParser parser, const int num_active_cells) { const std::vector dim = parserDim (parser); ecl_rst_file_fwrite_header (*this, - timer.currentStepNum (), - current (timer), - Opm::unit::convert::to (timer.currentTime (), + outputStepIdx, + timer.currentPosixTime(), + Opm::unit::convert::to (timer.simulationTimeElapsed (), Opm::unit::day), dim[0], dim[1], @@ -458,12 +450,12 @@ struct EclipseGrid : public EclipseHandle { */ void write (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) { + int outputStepIdx) { ecl_grid_fwrite_EGRID (*this, EclipseFileName (outputDir, baseName, ECL_EGRID_FILE, - timer)); + outputStepIdx)); } // GCC 4.4 doesn't generate these constructors for us; provide the @@ -500,7 +492,7 @@ private: EclipseGrid (const int dims[], const EclipseKeyword& zcorn, const EclipseKeyword& coord, - const EclipseKeyword& actnum, + const EclipseKeyword& actnum, const EclipseKeyword& mapaxes) : EclipseHandle ( ecl_grid_alloc_GRDECL_kw(dims[0], @@ -524,11 +516,11 @@ struct EclipseInit : public EclipseHandle { // constructor, so we'll have to do with a static wrapper) static EclipseInit make (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) { + int outputStepIdx) { EclipseFileName initFileName (outputDir, baseName, ECL_INIT_FILE, - timer); + outputStepIdx); bool fmt_file; if (!ecl_util_fmt_file(initFileName, &fmt_file)) { OPM_THROW(std::runtime_error, @@ -538,15 +530,15 @@ struct EclipseInit : public EclipseHandle { } void writeHeader (const EclipseGrid& grid, - const SimulatorTimer& timer, - const EclipseGridParser& parser, - const PhaseUsage uses) { + const SimulatorTimer& timer, + const EclipseGridParser& parser, + const PhaseUsage uses) { EclipseKeyword poro (PORO_KW, parser); ecl_init_file_fwrite_header (*this, grid, poro, phaseMask (uses), - current (timer)); + timer.currentPosixTime()); } void writeKeyword (const std::string& keyword, @@ -639,8 +631,7 @@ private: EclipseTimeStep* tstep = new EclipseTimeStep ( ecl_sum_add_tstep (*this, timer.currentStepNum (), - // currentTime is always relative to start - Opm::unit::convert::to (timer.currentTime (), + Opm::unit::convert::to (timer.simulationTimeElapsed (), Opm::unit::day))); return std::unique_ptr (tstep); } @@ -660,7 +651,7 @@ private: false, /* formatted */ true, /* unified */ ":", /* join string */ - current (timer), + timer.simulationTimeElapsed (), dim[0], dim[1], dim[2]); @@ -857,7 +848,8 @@ struct EclipseWellBhp : public EclipseWellReport { inline void EclipseSummary::writeTimeStep (const SimulatorTimer& timer, - const WellState& wellState) { + const WellState& wellState) +{ // internal view; do not move this code out of EclipseSummary! std::unique_ptr tstep = makeTimeStep (timer); // write all the variables @@ -873,7 +865,8 @@ static WellType WELL_TYPES[] = { INJECTOR, PRODUCER }; inline void EclipseSummary::addWells (const EclipseGridParser& parser, - const PhaseUsage& uses) { + const PhaseUsage& uses) +{ // TODO: Only create report variables that are requested with keywords // (e.g. "WOPR") in the input files, and only for those wells that are // mentioned in those keywords @@ -957,12 +950,19 @@ namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer &timer, const SimulatorState& reservoirState, - const WellState& wellState) { + const WellState& wellState) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + /* Grid files */ EclipseGrid ecl_grid = EclipseGrid::make (*parser_, *grid_); - ecl_grid.write (outputDir_, baseName_, timer); + ecl_grid.write (outputDir_, baseName_, /*stepIdx=*/0); - EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, timer); + EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); fortio.writeHeader (ecl_grid, timer, *parser_, @@ -973,7 +973,7 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, fortio.writeKeyword ("PERMZ", *parser_, &toMilliDarcy); /* Initial solution (pressure and saturation) */ - writeSolution (timer, reservoirState, wellState); + writeSolution_(timer, reservoirState); /* Create summary object (could not do it at construction time, since it requires knowledge of the start time). */ @@ -981,14 +981,16 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, summary_->addWells (*parser_, uses_); } -void EclipseWriter::writeSolution (const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& /*wellState*/) { +void EclipseWriter::writeSolution_(const SimulatorTimer& timer, + const SimulatorState& reservoirState) +{ // start writing to files EclipseRestart rst (outputDir_, baseName_, - timer); + timer, + outputTimeStepIdx_); rst.writeHeader (timer, + outputTimeStepIdx_, uses_, *parser_, reservoirState.pressure ().size ()); @@ -997,9 +999,7 @@ void EclipseWriter::writeSolution (const SimulatorTimer& timer, // write pressure and saturation fields (same as DataMap holds) // convert the pressures from Pascals to bar because Eclipse // seems to write bars - sol.add (EclipseKeyword (reservoirState.pressure (), - "PRESSURE", - &toBar)); + sol.add(EclipseKeyword(reservoirState.pressure(), "PRESSURE", &toBar)); for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { // Eclipse never writes the oil saturation, so all post-processors @@ -1015,13 +1015,27 @@ void EclipseWriter::writeSolution (const SimulatorTimer& timer, uses_.num_phases)); } } + + ++outputTimeStepIdx_; } void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, const SimulatorState& reservoirState, - const WellState& wellState) { + const WellState& wellState) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + + // respected the output_interval parameter + if (timer.currentStepNum() % outputInterval_ != 0) { + return; + } + /* Field variables (pressure, saturation) */ - writeSolution (timer, reservoirState, wellState); + writeSolution_(timer, reservoirState); /* Summary variables (well reporting) */ // TODO: instead of writing the header (smspec) every time, it should @@ -1037,15 +1051,23 @@ void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, // called. This has been changed so that the final summary file // will contain data from the whole simulation, instead of just // the last step. - summary_->writeTimeStep (timer, wellState); + summary_->writeTimeStep(timer, wellState); } +} // namespace Opm #else namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer&, const SimulatorState&, - const WellState&) { + const WellState&) + { + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + OPM_THROW(std::runtime_error, "The ERT libraries are required to write ECLIPSE output files."); } @@ -1053,20 +1075,35 @@ void EclipseWriter::writeInit(const SimulatorTimer&, void EclipseWriter::writeTimeStep( const SimulatorTimer&, const SimulatorState&, - const WellState&) { + const WellState&) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + OPM_THROW(std::runtime_error, "The ERT libraries are required to write ECLIPSE output files."); } +} // namespace Opm + #endif // HAVE_ERT +namespace Opm { + EclipseWriter::EclipseWriter ( const ParameterGroup& params, std::shared_ptr parser, std::shared_ptr grid) : parser_ (parser) , grid_ (grid) - , uses_ (phaseUsageFromDeck (*parser)) { + , uses_ (phaseUsageFromDeck (*parser)) +{ + // set the index of the first time step written to 0... + outputTimeStepIdx_ = 0; + // get the base name from the name of the deck using boost::filesystem::path; @@ -1082,14 +1119,31 @@ EclipseWriter::EclipseWriter ( // of some of the files (.SMSPEC, .UNSMRY) and not others baseName_ = boost::to_upper_copy (baseName_); + // retrieve the value of the "output" parameter + enableOutput_ = params.getDefault("output", /*defaultValue=*/true); + + // retrieve the interval at which something should get written to + // disk (once every N timesteps) + outputInterval_ = params.getDefault("output_interval", /*defaultValue=*/1); + // store in current directory if not explicitly set - if (params.has ("output_dir")) { - outputDir_ = params.get ("output_dir"); - } - else { - // this is needed to prevent file names like "/FOO.INIT" which - // lead to segfaults - outputDir_ = "."; + outputDir_ = params.getDefault("output_dir", "."); + + // set the index of the first time step written to 0... + outputTimeStepIdx_ = 0; + + if (enableOutput_) { + // make sure that the output directory exists, if not try to create it + if (!boost::filesystem::exists(outputDir_)) { + std::cout << "Trying to create directory \"" << outputDir_ << "\" for the simulation output\n"; + boost::filesystem::create_directories(outputDir_); + } + + if (!boost::filesystem::is_directory(outputDir_)) { + OPM_THROW(std::runtime_error, + "The path specified as output directory '" << outputDir_ + << "' is not a directory"); + } } } diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 5634a347..a3451ac3 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -88,15 +88,17 @@ public: private: std::shared_ptr parser_; std::shared_ptr grid_; + bool enableOutput_; + int outputInterval_; + int outputTimeStepIdx_; std::string outputDir_; std::string baseName_; PhaseUsage uses_; // active phases in the input deck std::shared_ptr summary_; /// Write solution field variables (pressure and saturation) - void writeSolution (const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& wellState); + void writeSolution_(const SimulatorTimer& timer, + const SimulatorState& reservoirState); }; } // namespace Opm From f8f7550782a017f90ea75f4e417b3b78aa0c7f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Atgeirr=20Fl=C3=B8=20Rasmussen?= Date: Mon, 17 Mar 2014 13:27:50 +0100 Subject: [PATCH 088/109] Made single-argument constructors explicit. Avoids unintended implicit conversions. --- opm/core/grid/GridManager.hpp | 6 +++--- opm/core/wells/WellsManager.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/opm/core/grid/GridManager.hpp b/opm/core/grid/GridManager.hpp index 030d5555..4ffe73d2 100644 --- a/opm/core/grid/GridManager.hpp +++ b/opm/core/grid/GridManager.hpp @@ -44,10 +44,10 @@ namespace Opm { public: /// Construct a 3d corner-point grid or tensor grid from a deck. - GridManager(const Opm::EclipseGridParser& deck); + explicit GridManager(const Opm::EclipseGridParser& deck); /// Construct a 3d corner-point grid or tensor grid from a deck. - GridManager(Opm::DeckConstPtr newParserDeck); + explicit GridManager(Opm::DeckConstPtr newParserDeck); /// Construct a 2d cartesian grid with cells of unit size. GridManager(int nx, int ny); @@ -65,7 +65,7 @@ namespace Opm /// Construct a grid from an input file. /// The file format used is currently undocumented, /// and is therefore only suited for internal use. - GridManager(const std::string& input_filename); + explicit GridManager(const std::string& input_filename); /// Destructor. ~GridManager(); diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 9e5c314c..c3bbe633 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -66,7 +66,7 @@ namespace Opm /// manage control switching does not exist. /// /// @param[in] W Existing wells object. - WellsManager(struct Wells* W); + explicit WellsManager(struct Wells* W); /// Construct from input deck and grid. /// The permeability argument may be zero if the input contain From 83dcf17d5b7c97b3bdef95e1051d5a4bdb6006b2 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 18 Mar 2014 15:08:28 +0100 Subject: [PATCH 089/109] endpoint scaling: invert condition yeah, true and false are difficult things sometimes... --- opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp index c85cef78..a6e699fa 100644 --- a/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp +++ b/opm/core/props/satfunc/SaturationPropsFromDeck_impl.hpp @@ -212,7 +212,7 @@ namespace Opm "SaturationPropsFromDeck::init() -- ENDSCALE: " "Currently only 'NODIR' accepted."); } - if (endscale.isReversible()) { + if (!endscale.isReversible()) { OPM_THROW(std::runtime_error, "SaturationPropsFromDeck::init() -- ENDSCALE: " "Currently only 'REVERS' accepted."); From 5c55fb73411feb218a4868837747a9b4ecdf29e8 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 18 Mar 2014 18:04:40 +0100 Subject: [PATCH 090/109] fix densities for new parser in BlackoilPvtProperties that is one of the more subtle differences between the old and the new parsers. now, valgrind does not seem to complain anymore, so everything should be All Right (TM) ;) --- opm/core/props/pvt/BlackoilPvtProperties.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/opm/core/props/pvt/BlackoilPvtProperties.cpp b/opm/core/props/pvt/BlackoilPvtProperties.cpp index 79f74089..da5c7a66 100644 --- a/opm/core/props/pvt/BlackoilPvtProperties.cpp +++ b/opm/core/props/pvt/BlackoilPvtProperties.cpp @@ -125,16 +125,17 @@ namespace Opm // 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[Liquid]) { + densities_[phase_usage_.phase_pos[Liquid]] + = densityKeyword->getRecord(region_number_)->getItem("OIL")->getSIDouble(0); + } if (phase_usage_.phase_used[Aqua]) { - densities_[phase_usage_.phase_pos[Aqua]] = d[ECL_water]; + densities_[phase_usage_.phase_pos[Aqua]] + = densityKeyword->getRecord(region_number_)->getItem("WATER")->getSIDouble(0); } 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]; + densities_[phase_usage_.phase_pos[Vapour]] + = densityKeyword->getRecord(region_number_)->getItem("GAS")->getSIDouble(0); } } else { OPM_THROW(std::runtime_error, "Input is missing DENSITY\n"); From fed0136a21f07a8338d4748be1c41c0668c82573 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 11:09:13 +0100 Subject: [PATCH 091/109] Fixes compilation of BlackoilPropertiesFromDeck.cpp. Patch 31c09aed was erroneous as it was trying to assing a SaturationPropsFromDeck to a SaturationPropsFromDeck in the constructor taking the new parser. This patch fixes this. --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index 62048ba0..06b6ad65 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -50,7 +50,7 @@ namespace Opm rock_.init(newParserDeck, grid); } pvt_.init(newParserDeck, /*numSamples=*/0); - SaturationPropsFromDeck* ptr + SaturationPropsFromDeck* ptr = new SaturationPropsFromDeck(); satprops_.reset(ptr); ptr->init(newParserDeck, grid, /*numSamples=*/0); From cf7f07b179b9757139a0f44abe9db2d4e6123ccb Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 11:19:25 +0100 Subject: [PATCH 092/109] Makes KAMG and FastAMG solver available with dune-istl 2.3 Previously we relied on the define DUNE_HAS_FAST_AMG to detect whether these preconditioners are available. This define is only available in the 2.2 release with cmake support. Therfore we now addtionally test whether we are using dune-isl 2.3 or newer. --- cmake/Modules/opm-core-prereqs.cmake | 3 +++ opm/core/linalg/LinearSolverIstl.cpp | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index 8b3d45de..f1c7a5dd 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -3,6 +3,9 @@ # defines that must be present in config.h for our headers set (opm-core_CONFIG_VAR + DUNE_ISTL_VERSION_MAJOR + DUNE_ISTL_VERSION_MINOR + DUNE_ISTL_VERSION_REVISION HAVE_ERT HAVE_SUITESPARSE_UMFPACK_H ) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 77aaf4ca..f9e2bb7b 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -30,6 +30,7 @@ // TODO: clean up includes. #include +#include #include #include #include @@ -61,7 +62,7 @@ namespace Opm solveCG_AMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, double prolongateFactor, int smoothsteps); -#ifdef HAS_DUNE_FAST_AMG +#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) LinearSolverInterface::LinearSolverReport solveKAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, double prolongateFactor, int smoothsteps); From 829f791261907f743a1ce43abfb1e9354b6692bd Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 18 Mar 2014 21:38:43 +0100 Subject: [PATCH 093/109] endpoint scaling: change default threephase_model to gwseg and throw an exception if "simple" is encountered... According to Ove, gwseg should be used, because "gwseg is the model relevant to the norne case - i.e the model eclipse uses. The fix for the simple model has to wait for a refac of the satfunc complex." --- opm/core/props/BlackoilPropertiesFromDeck.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/props/BlackoilPropertiesFromDeck.cpp b/opm/core/props/BlackoilPropertiesFromDeck.cpp index 62048ba0..08b54ba7 100644 --- a/opm/core/props/BlackoilPropertiesFromDeck.cpp +++ b/opm/core/props/BlackoilPropertiesFromDeck.cpp @@ -139,9 +139,9 @@ namespace Opm // Unfortunate lack of pointer smartness here... const int sat_samples = param.getDefault("sat_tab_size", 0); - 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."); + std::string threephase_model = param.getDefault("threephase_model", "gwseg"); + if (newParserDeck->hasKeyword("ENDSCALE") && threephase_model != "gwseg") { + OPM_THROW(std::runtime_error, "Sorry, end point scaling currently available for the 'gwseg' model only."); } if (sat_samples > 1) { if (threephase_model == "stone2") { From 95985f0694c961a05bfb062011fb30c6180f4480 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Mon, 17 Mar 2014 16:30:03 +0100 Subject: [PATCH 094/109] SimulatorTimer: allow it to be limited to a sub-range of all report steps --- opm/core/simulator/SimulatorTimer.cpp | 47 ++++++++++++++++++++++----- opm/core/simulator/SimulatorTimer.hpp | 11 ++++++- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/opm/core/simulator/SimulatorTimer.cpp b/opm/core/simulator/SimulatorTimer.cpp index b672b613..4c36c0d7 100644 --- a/opm/core/simulator/SimulatorTimer.cpp +++ b/opm/core/simulator/SimulatorTimer.cpp @@ -59,21 +59,46 @@ namespace Opm /// Use the SimulatorTimer as a shim around opm-parser's Opm::TimeMap void SimulatorTimer::init(Opm::TimeMapConstPtr timeMap, - int timeStepIdx) + size_t beginReportStepIdx, + size_t endReportStepIdx) { timeMap_ = timeMap; - current_step_ = timeStepIdx; + current_step_ = 0; + beginReportStepIdx_ = beginReportStepIdx; + endReportStepIdx_ = std::min(timeMap_->numTimesteps() + 1, endReportStepIdx); } /// Total number of steps. int SimulatorTimer::numSteps() const { if (timeMap_) - return timeMap_->numTimesteps(); + return endReportStepIdx_ - beginReportStepIdx_; else return timesteps_.size(); } + /// Index of the first considered simulation episode + size_t SimulatorTimer::beginReportStepIndex() const + { + if (!timeMap_) { + OPM_THROW(std::runtime_error, "indexFirstEpisode() is only implemented " + "for simulation timers which are based on Opm::TimeMap"); + } + + return beginReportStepIdx_; + } + + /// Index of the last considered simulation episode + size_t SimulatorTimer::endReportStepIndex() const + { + if (!timeMap_) { + OPM_THROW(std::runtime_error, "indexLastEpisode() is only implemented " + "for simulation timers which are based on Opm::TimeMap"); + } + + return endReportStepIdx_; + } + /// Current step number. int SimulatorTimer::currentStepNum() const { @@ -99,7 +124,7 @@ namespace Opm { assert(!done()); if (timeMap_) - return timeMap_->getTimeStepLength(current_step_); + return timeMap_->getTimeStepLength(beginReportStepIdx_ + current_step_); else return timesteps_[current_step_]; } @@ -108,7 +133,7 @@ namespace Opm { assert(current_step_ > 0); if (timeMap_) - return timeMap_->getTimeStepLength(current_step_ - 1); + return timeMap_->getTimeStepLength(beginReportStepIdx_ + current_step_ - 1); else return timesteps_[current_step_ - 1]; } @@ -117,7 +142,9 @@ namespace Opm double SimulatorTimer::simulationTimeElapsed() const { if (timeMap_) - return timeMap_->getTimePassedUntil(current_step_); + return + timeMap_->getTimePassedUntil(beginReportStepIdx_ + current_step_) + - timeMap_->getTimePassedUntil(beginReportStepIdx_); else return current_time_; } @@ -132,7 +159,7 @@ namespace Opm boost::posix_time::ptime SimulatorTimer::currentDateTime() const { if (timeMap_) - return timeMap_->getStartTime(current_step_); + return timeMap_->getStartTime(beginReportStepIdx_ + current_step_); else return boost::posix_time::ptime(start_date_) + boost::posix_time::seconds( (int) current_time_ ); } @@ -143,7 +170,9 @@ namespace Opm double SimulatorTimer::totalTime() const { if (timeMap_) - return timeMap_->getTotalTime(); + return + timeMap_->getTimePassedUntil(endReportStepIdx_) - + timeMap_->getTimePassedUntil(beginReportStepIdx_); else return total_time_; } @@ -187,7 +216,7 @@ namespace Opm bool SimulatorTimer::done() const { if (timeMap_) - return int(timeMap_->numTimesteps()) <= current_step_; + return current_step_ > int(endReportStepIdx_ - beginReportStepIdx_ - 1); else return int(timesteps_.size()) == current_step_; } diff --git a/opm/core/simulator/SimulatorTimer.hpp b/opm/core/simulator/SimulatorTimer.hpp index ad49282f..b743478d 100644 --- a/opm/core/simulator/SimulatorTimer.hpp +++ b/opm/core/simulator/SimulatorTimer.hpp @@ -51,11 +51,18 @@ namespace Opm /// Use the SimulatorTimer as a shim around opm-parser's Opm::TimeMap void init(TimeMapConstPtr timeMap, - int timeStepIdx = 0); + size_t beginReportStepIdx = 0, + size_t endReportStepIdx = std::numeric_limits::max()); /// Total number of steps. int numSteps() const; + /// Index of the first report step considered + size_t beginReportStepIndex() const; + + /// Index of the next-after-last report step to be considered + size_t endReportStepIndex() const; + /// Current step number. This is the number of timesteps that /// has been completed from the start of the run. The time /// after initialization but before the simulation has started @@ -112,6 +119,8 @@ namespace Opm private: Opm::TimeMapConstPtr timeMap_; std::vector timesteps_; + size_t beginReportStepIdx_; + size_t endReportStepIdx_; int current_step_; double current_time_; double total_time_; From 1ad8381b04941ef11117e9c75e8a9254d2d8b51e Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 15:15:55 +0100 Subject: [PATCH 095/109] [cmake] do not export dune-istl version but use module local detection. According to @rolk defines should only be exported in cmake/Modules/-prereqs.cmake if they are used in the headers. This is not the case for the dune-istl version macros and therefore we use opm_need_version_of in the config_hook in the top level CMakefile with this patch. --- CMakeLists.txt | 1 + cmake/Modules/opm-core-prereqs.cmake | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f0e895c0..b4fd8b2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ include (CMakeLists_files.cmake) macro (config_hook) # opm_need_version_of ("dune-common") + opm_need_version_of ("dune-istl") list (APPEND ${project}_CONFIG_IMPL_VARS HAVE_DUNE_ISTL ) diff --git a/cmake/Modules/opm-core-prereqs.cmake b/cmake/Modules/opm-core-prereqs.cmake index f1c7a5dd..8b3d45de 100644 --- a/cmake/Modules/opm-core-prereqs.cmake +++ b/cmake/Modules/opm-core-prereqs.cmake @@ -3,9 +3,6 @@ # defines that must be present in config.h for our headers set (opm-core_CONFIG_VAR - DUNE_ISTL_VERSION_MAJOR - DUNE_ISTL_VERSION_MINOR - DUNE_ISTL_VERSION_REVISION HAVE_ERT HAVE_SUITESPARSE_UMFPACK_H ) From 4aa4108367601b4f609bf82aa6c0c0fbdedf1a7b Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 11 Mar 2014 15:31:46 +0100 Subject: [PATCH 096/109] EclipseWriter: Don't mingle multiple operations using arguments This means that EclipseKeyword now never processes the data it gets. Instead the data must be explicitly preprocessed by the calling site using the new auxiliary functions "convertUnit()", "extractFromStriped()", etc. This approach needs a few additional temporary copies of the data, but given the facts that readability of this code is much better using this approach, and that EclipseWriter is neither a performance- nor a memory critical codepath, I don't care too much about those temporary arrays... --- opm/core/io/eclipse/EclipseWriter.cpp | 333 +++++++++++++++----------- opm/core/io/eclipse/EclipseWriter.hpp | 5 + 2 files changed, 201 insertions(+), 137 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index c13aef46..fdc17543 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -61,6 +61,30 @@ using namespace Opm::parameter; // namespace start here since we don't want the ERT headers in it namespace { +namespace { +/// Helper function when we don't really want any transformation +/// (The C++ committee removed std::identity because it was "troublesome" (!?!) +static double noConversion (const double& u) { return u; } + + +/// Helper method that can be used in keyword transformation (must carry +/// the barsa argument) +static double toBar (const double& pressure) { + return Opm::unit::convert::to (pressure, Opm::unit::barsa); +} + +/// Helper method that can be used in keyword transformation (must carry +/// the milliDarcy argument) +static double toMilliDarcy (const double& permeability) { + return Opm::unit::convert::to (permeability, Opm::prefix::milli * Opm::unit::darcy); +} + +/// Names of the saturation property for each phase. The order of these +/// names are critical; they must be the same as the BlackoilPhases enum +static const char* SAT_NAMES[] = { "SWAT", "SOIL", "SGAS" }; + +} // anonymous namespace + /// Smart pointer/handle class for ERT opaque types, such as ecl_kw_type*. /// /// \tparam T Type of handle being wrapper @@ -104,53 +128,131 @@ private: static void no_delete (T*) { } }; + +// throw away the data for all non-active cells in an array +void restrictToActiveCells_(std::vector &data, const std::vector &actnumData) +{ + assert(actnumData.size() == data.size()); + + size_t curActiveIdx = 0; + for (size_t curIdx = 0; curIdx < data.size(); ++curIdx) { + if (!actnumData[curIdx]) + continue; // ignore non-active cells + + assert(curActiveIdx <= curIdx); + data[curActiveIdx] = data[curIdx]; + ++ curActiveIdx; + } + + data.resize(curActiveIdx); +} + +// throw away the data for all non-active cells in an array. (this is +// the variant of the function which takes an UnstructuredGrid object.) +void restrictToActiveCells_(std::vector &data, const UnstructuredGrid &grid) +{ + if (!grid.global_cell) + // if there is no active -> global mapping, all cells + // are considered active + return; + + // activate those cells that are actually there + for (int i = 0; i < grid.number_of_cells; ++i) { + // make sure that global cell indices are always at least as + // large as the active one and that the global cell indices + // are in increasing order. the latter might become + // problematic if cells are extensively re-ordered, but that + // does not seem to be the case so far + assert(grid.global_cell[i] >= i); + assert(i == 0 || grid.global_cell[i - 1] < grid.global_cell[i]); + + data[i] = data[grid.global_cell[i]]; + } + data.resize(grid.number_of_cells); +} + +// convert the units of an array +template +void convertUnit_(std::vector &data, TransferFunction &transferFn) +{ + for (size_t curIdx = 0; curIdx < data.size(); ++curIdx) { + data[curIdx] = transferFn(data[curIdx]); + } +} + +// extract a sub-array of a larger one which represents multiple +// striped ones +void extractFromStripedData_(std::vector &data, + int offset, + int stride) +{ + size_t tmpIdx = 0; + for (size_t curIdx = offset; curIdx < data.size(); curIdx += stride) { + assert(tmpIdx <= curIdx); + data[tmpIdx] = data[curIdx]; + ++tmpIdx; + } + // shirk the result + data.resize(tmpIdx); +} + +// enclosure of the current grid in a Cartesian space +int getCartesianSize_(const UnstructuredGrid& grid) { + const int nx = grid.cartdims[0]; + const int ny = grid.cartdims[1]; + const int nz = grid.cartdims[2]; + return nx * ny * nz; +} + +void getActiveCells_(const UnstructuredGrid& grid, + std::vector & actnum) +{ + // we must fill the Cartesian grid with flags + const int size = getCartesianSize_(grid); + + // if we don't have a global_cells field, then assume that all + // grid cells is active + if (!grid.global_cell) { + if (grid.number_of_cells != size) { + OPM_THROW (std::runtime_error, + "No ACTNUM map but grid size != Cartesian size"); + } + actnum.assign (size, 1); + } + else { + // start out with entire map being inactive + actnum.assign (size, 0); + + // activate those cells that are actually there + for (int i = 0; i < grid.number_of_cells; ++i) { + actnum[grid.global_cell[i]] = 1; + } + } +} + /** * Eclipse "keyword" (i.e. named data) for a vector. (This class is * different from EclKW in the constructors it provide). */ template struct EclipseKeyword : public EclipseHandle { - EclipseKeyword (const std::string& name, /// identification - const std::vector& data, /// array holding values - const int offset = 0, /// distance to first - const int stride = 1) /// distance between each - - // allocate handle and put in smart pointer base class - : EclipseHandle ( - ecl_kw_alloc (name.c_str(), data.size (), type ()), - ecl_kw_free) { - copyData (data, &no_conversion, offset, stride); - } - - /// Special initialization from double-precision array which - /// automatically invokes a version of the copy function which - /// downcasts. This is really only applicable to the T = float - /// template instance. - /// The data and name parameters are switched in this version - /// so that it doesn't conflict with the one above in the case - /// of T = double. - EclipseKeyword (const std::vector& data, - const std::string& name, - double (* const transf)(const double&), - const int offset = 0, - const int stride = 1); - - /// Convenience constructor that gets the set of data - /// from the samely named item in the parser + /// Special initialization from double-precision array. EclipseKeyword (const std::string& name, - const EclipseGridParser& parser) - // allocate handle and put in smart pointer base class - // notice dataSize is called both here *and* in copyData, - // but GCC 4.4 doesn't support delegating constructors, so - // we cannot avoid this without otherwise using a member - : EclipseHandle ( - ecl_kw_alloc (name.c_str(), - dataSize (parser.getValue (name)), - type ()), - ecl_kw_free) { - const std::vector & data = parser.getValue (name); - copyData (data, &no_conversion, 0, 1); - } + const std::vector& data) + : EclipseHandle(ecl_kw_alloc(name.c_str(), + data.size(), + type()), + ecl_kw_free) + { copyData (data, &noConversion, /*offset=*/0, /*stride=*/1); } + + /// Initialization from integer array. + EclipseKeyword (const std::string& name, + const std::vector& data) + : EclipseHandle(ecl_kw_alloc(name.c_str(), + data.size(), + type()), + ecl_kw_free) + { copyData (data, &noConversion, /*offset=*/0, /*stride=*/1); } /// Constructor for optional fields EclipseKeyword (const std::string& name) @@ -158,22 +260,24 @@ struct EclipseKeyword : public EclipseHandle { static_cast (name); } + // constructor to keep compatibility with the old eclipse parser + EclipseKeyword (const std::string& name, + const EclipseGridParser& parser); + // GCC 4.4 doesn't generate these constructors for us; provide the // default implementation explicitly here instead EclipseKeyword (EclipseKeyword&& rhs) - : EclipseHandle (std::move (rhs)) { } - EclipseKeyword& operator= (EclipseKeyword&& rhs) { + : EclipseHandle (std::move (rhs)) + { } + + EclipseKeyword& operator= (EclipseKeyword&& rhs) + { EclipseHandle ::operator= (std::move(rhs)); return *this; } EclipseKeyword (const EclipseKeyword&) = delete; EclipseKeyword& operator= (const EclipseKeyword&) = delete; - /// Helper function when we don't really want any transformation - /// (The C++ committee removed std::identity because it was "troublesome" (!?!) - template - static U no_conversion (const U& u) { return u; } - private: /// Map the C++ data type (given by T) to an Eclipse type enum static ecl_type_enum type (); @@ -181,7 +285,7 @@ private: /// Helper function that is the meat of the constructor template void copyData (const std::vector & data, - U (* const transf)(const U&), + double (* const transf)(const double&), const int offset, const int stride) { // number of elements to take @@ -238,23 +342,7 @@ EclipseKeyword ::EclipseKeyword ( type ()), ecl_kw_free) { const std::vector & data = parser.getValue (name); - copyData (data, &no_conversion, 0, 1); -} - -/// Provide only the float version, since that is the one for which -/// we need this conversion (we don't want it for int, for instance) -template <> -EclipseKeyword ::EclipseKeyword ( - const std::vector& data, - const std::string& name, - double (* const transf)(const double&), - const int offset, - const int stride) - // allocate handle and put in smart pointer base class - : EclipseHandle ( - ecl_kw_alloc (name.c_str(), dataSize (data, offset, stride), type ()), - ecl_kw_free) { - copyData (data, transf, offset, stride); + copyData (data, &noConversion, 0, 1); } /** @@ -369,39 +457,6 @@ private: } }; -// enclosure of the current grid in a Cartesian space -int cart_size (const UnstructuredGrid& grid) { - const int nx = grid.cartdims[0]; - const int ny = grid.cartdims[1]; - const int nz = grid.cartdims[2]; - return nx * ny * nz; -} - -void active_cells (const UnstructuredGrid& grid, - std::vector & actnum) { - // we must fill the Cartesian grid with flags - const int size = cart_size (grid); - - // if we don't have a global_cells field, then assume that all - // grid cells is active - if (!grid.global_cell) { - if (grid.number_of_cells != size) { - OPM_THROW (std::runtime_error, - "No ACTNUM map but grid size != Cartesian size"); - } - actnum.assign (size, 1); - } - else { - // start out with entire map being inactive - actnum.assign (size, 0); - - // activate those cells that are actually there - for (int i = 0; i < grid.number_of_cells; ++i) { - actnum[grid.global_cell[i]] = 1; - } - } -} // active_cells - /** * Representation of an Eclipse grid. */ @@ -424,17 +479,21 @@ struct EclipseGrid : public EclipseHandle { else if (parser.hasField("ZCORN")) { struct grdecl g = parser.get_grdecl (); - EclipseKeyword coord_kw (COORD_KW, parser); - EclipseKeyword zcorn_kw (ZCORN_KW, parser); + auto coordData = parser.getFloatingPointValue(COORD_KW); + auto zcornData = parser.getFloatingPointValue(ZCORN_KW); + + EclipseKeyword coord_kw (COORD_KW, coordData); + EclipseKeyword zcorn_kw (ZCORN_KW, zcornData); // get the actually active cells, after processing std::vector actnum; - active_cells (grid, actnum); + getActiveCells_(grid, actnum); EclipseKeyword actnum_kw (ACTNUM_KW, actnum); EclipseKeyword mapaxes_kw (MAPAXES_KW); if (g.mapaxes) { - mapaxes_kw = std::move (EclipseKeyword (MAPAXES_KW, parser)); + auto mapaxesData = parser.getFloatingPointValue(MAPAXES_KW); + mapaxes_kw = std::move (EclipseKeyword (MAPAXES_KW, mapaxesData)); } return EclipseGrid (g.dims, zcorn_kw, coord_kw, actnum_kw, mapaxes_kw); @@ -541,15 +600,21 @@ struct EclipseInit : public EclipseHandle { timer.currentPosixTime()); } - void writeKeyword (const std::string& keyword, + void writeKeyword (const std::string& keywordName, const EclipseGridParser& parser, double (* const transf)(const double&)) { - EclipseKeyword kw (parser.getValue (keyword), - keyword, - transf); + auto data = parser.getValue (keywordName); + convertUnit_(data, transf); + EclipseKeyword kw (keywordName, data); ecl_kw_fwrite (kw, *this); } + void writeKeyword (const std::string& keywordName, const std::vector &data) + { + EclipseKeyword kw (keywordName, data); + ecl_kw_fwrite(kw, *this); + } + // GCC 4.4 doesn't generate these constructors for us; provide the // default implementation explicitly here instead EclipseInit (EclipseInit&& rhs) @@ -926,26 +991,6 @@ EclipseSummary::addWells (const EclipseGridParser& parser, } } -namespace { - -/// Helper method that can be used in keyword transformation (must curry -/// the barsa argument) -static double toBar (const double& pressure) { - return Opm::unit::convert::to (pressure, Opm::unit::barsa); -} - -/// Helper method that can be used in keyword transformation (must curry -/// the milliDarcy argument) -static double toMilliDarcy (const double& permeability) { - return Opm::unit::convert::to (permeability, Opm::prefix::milli * Opm::unit::darcy); -} - -/// Names of the saturation property for each phase. The order of these -/// names are critical; they must be the same as the BlackoilPhases enum -static const char* SAT_NAMES[] = { "SWAT", "SOIL", "SGAS" }; - -} // anonymous namespace - namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer &timer, @@ -981,6 +1026,22 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, summary_->addWells (*parser_, uses_); } +void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, + const std::vector &activeCellsBuf, + const std::vector &inactiveCellsBuf) const +{ + globalCellsBuf = inactiveCellsBuf; + + // overwrite the values of active cells + for (int activeCellIdx = 0; + activeCellIdx < grid_->number_of_cells; + ++activeCellIdx) + { + int globalCellIdx = grid_->global_cell[activeCellIdx]; + globalCellsBuf[globalCellIdx] = activeCellsBuf[activeCellIdx]; + } +} + void EclipseWriter::writeSolution_(const SimulatorTimer& timer, const SimulatorState& reservoirState) { @@ -999,7 +1060,9 @@ void EclipseWriter::writeSolution_(const SimulatorTimer& timer, // write pressure and saturation fields (same as DataMap holds) // convert the pressures from Pascals to bar because Eclipse // seems to write bars - sol.add(EclipseKeyword(reservoirState.pressure(), "PRESSURE", &toBar)); + auto data = reservoirState.pressure(); + convertUnit_(data, toBar); + sol.add(EclipseKeyword("PRESSURE", data)); for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { // Eclipse never writes the oil saturation, so all post-processors @@ -1008,14 +1071,15 @@ void EclipseWriter::writeSolution_(const SimulatorTimer& timer, continue; } if (uses_.phase_used [phase]) { - sol.add (EclipseKeyword (reservoirState.saturation(), - SAT_NAMES [phase], - &EclipseKeyword ::no_conversion, - uses_.phase_pos [phase], - uses_.num_phases)); + auto tmp = reservoirState.saturation(); + extractFromStripedData_(tmp, + /*offset=*/uses_.phase_pos[phase], + /*stride=*/uses_.num_phases); + sol.add (EclipseKeyword(SAT_NAMES[phase], tmp)); } } + ++outputTimeStepIdx_; } @@ -1053,7 +1117,6 @@ void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, // the last step. summary_->writeTimeStep(timer, wellState); } -} // namespace Opm #else namespace Opm { @@ -1087,12 +1150,8 @@ void EclipseWriter::writeTimeStep( "The ERT libraries are required to write ECLIPSE output files."); } -} // namespace Opm - #endif // HAVE_ERT -namespace Opm { - EclipseWriter::EclipseWriter ( const ParameterGroup& params, std::shared_ptr parser, diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index a3451ac3..5ec99543 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -25,6 +25,7 @@ #include #include +#include #include // std::unique_ptr struct UnstructuredGrid; @@ -96,6 +97,10 @@ private: PhaseUsage uses_; // active phases in the input deck std::shared_ptr summary_; + void activeToGlobalCellData_(std::vector &globalCellsBuf, + const std::vector &activeCellsBuf, + const std::vector &inactiveCellsBuf) const; + /// Write solution field variables (pressure and saturation) void writeSolution_(const SimulatorTimer& timer, const SimulatorState& reservoirState); From 75374c8745acca30d648165f77d6c2c0a544e8eb Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 13 Mar 2014 16:53:11 +0100 Subject: [PATCH 097/109] EclipseWriter: add method variants taking the new instead of the old parser --- opm/core/io/eclipse/EclipseWriter.cpp | 529 +++++++++++++++++++++++--- opm/core/io/eclipse/EclipseWriter.hpp | 11 + 2 files changed, 492 insertions(+), 48 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index fdc17543..65b7e400 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,10 @@ #include #include // WellType +#include +#include +#include + #include // to_upper_copy #include #include // path @@ -128,6 +133,37 @@ private: static void no_delete (T*) { } }; +// retrieve all data fields in SI units of a deck keyword +std::vector getAllSiDoubles_(Opm::DeckKeywordConstPtr keywordPtr) +{ + std::vector retBuff; + for (unsigned i = 0; i < keywordPtr->size(); ++i) { + Opm::DeckRecordConstPtr recordPtr(keywordPtr->getRecord(i)); + for (unsigned j = 0; j < recordPtr->size(); ++j) { + Opm::DeckItemConstPtr itemPtr(recordPtr->getItem(j)); + for (unsigned k = 0; k < itemPtr->size(); ++k) { + retBuff.push_back(itemPtr->getSIDouble(k)); + } + } + } + return retBuff; +} + +// retrieve all integer data fields of a deck keyword +std::vector getAllIntegers_(Opm::DeckKeywordConstPtr keywordPtr) +{ + std::vector retBuff; + for (unsigned i = 0; i < keywordPtr->size(); ++i) { + Opm::DeckRecordConstPtr recordPtr(keywordPtr->getRecord(i)); + for (unsigned j = 0; j < recordPtr->size(); ++j) { + Opm::DeckItemConstPtr itemPtr(recordPtr->getItem(j)); + for (unsigned k = 0; k < itemPtr->size(); ++k) { + retBuff.push_back(itemPtr->getInt(k)); + } + } + } + return retBuff; +} // throw away the data for all non-active cells in an array void restrictToActiveCells_(std::vector &data, const std::vector &actnumData) @@ -320,6 +356,32 @@ private: const int recs = (num - 1 - offset) / stride + 1; return recs; } + + int dataSize (Opm::DeckKeywordConstPtr keyword, + const int offset = 0, + const int stride = 1) + { + int numFlatItems = 0; + Opm::DeckRecordConstPtr record = keyword->getRecord(0); + for (unsigned itemIdx = 0; itemIdx < record->size(); ++itemIdx) { + numFlatItems += record->getItem(itemIdx)->size(); + } + + // range cannot start outside of data set + assert(offset >= 0 && offset < numFlatItems); + + // don't jump out of the set when trying to + assert(stride > 0 && stride < numFlatItems - offset); + + // number of (strided) entries it will provide. the last item + // in the array is num - 1. the last strided item we can pick + // (from recs number of records) is (recs - 1) * stride + offset, + // which must be <= num - 1. we are interested in the maximum + // case where it holds to equals. rearranging the above gives us: + const int recs = (numFlatItems - 1 - offset) / stride + 1; + return recs; + } + }; // specializations for known keyword types @@ -393,6 +455,30 @@ std::vector parserDim (const EclipseGridParser& parser) { return dim; } +/// Get dimensions of the grid from the parse of the input file +std::vector parserDim (Opm::DeckConstPtr newParserDeck) { + std::vector dim(/* n = */ 3); + // dimensions explicitly given + if (newParserDeck->hasKeyword("SPECGRID")) { + SpecgridWrapper specgrid(newParserDeck->getKeyword("SPECGRID")); + dim = specgrid.numBlocksVector(); + } + // dimensions implicitly given by number of deltas + else if (newParserDeck->hasKeyword("DXV")) { + assert(newParserDeck->hasKeyword("DYV")); + assert(newParserDeck->hasKeyword("DZV")); + dim[0] = newParserDeck->getKeyword("DXV")->getRawDoubleData().size(); + dim[1] = newParserDeck->getKeyword("DYV")->getRawDoubleData().size(); + dim[2] = newParserDeck->getKeyword("DZV")->getRawDoubleData().size(); + } + else { + OPM_THROW(std::runtime_error, + "Only decks featureing either the SPECGRID or the D[XYZ]V keywords " + "are currently supported"); + } + return dim; +} + /// Convert OPM phase usage to ERT bitmask static int phaseMask (const PhaseUsage uses) { return (uses.phase_used [BlackoilPhases::Liquid] ? ECL_OIL_PHASE : 0) @@ -432,6 +518,24 @@ struct EclipseRestart : public EclipseHandle { num_active_cells, phaseMask (uses)); } + + void writeHeader (const SimulatorTimer& timer, + int outputStepIdx, + const PhaseUsage uses, + Opm::DeckConstPtr newParserDeck, + const int num_active_cells) { + const std::vector dim = parserDim (newParserDeck); + ecl_rst_file_fwrite_header (*this, + outputStepIdx, + timer.currentPosixTime(), + Opm::unit::convert::to (timer.simulationTimeElapsed (), + Opm::unit::day), + dim[0], + dim[1], + dim[2], + num_active_cells, + phaseMask (uses)); + } }; /** @@ -504,6 +608,50 @@ struct EclipseGrid : public EclipseHandle { } } + /// Create a grid based on the keywords available in input file + static EclipseGrid make (Opm::DeckConstPtr newParserDeck, + const UnstructuredGrid& grid) + { + if (newParserDeck->hasKeyword("DXV")) { + // make sure that the DYV and DZV keywords are present if the + // DXV keyword is used in the deck... + assert(newParserDeck->hasKeyword("DYV")); + assert(newParserDeck->hasKeyword("DZV")); + + const auto& dxv = newParserDeck->getKeyword("DXV")->getSIDoubleData(); + const auto& dyv = newParserDeck->getKeyword("DYV")->getSIDoubleData(); + const auto& dzv = newParserDeck->getKeyword("DZV")->getSIDoubleData(); + + return EclipseGrid (dxv, dyv, dzv); + } + else if (newParserDeck->hasKeyword("ZCORN")) { + struct grdecl g; + GridManager::createGrdecl(newParserDeck, g); + + auto coordData = getAllSiDoubles_(newParserDeck->getKeyword(COORD_KW)); + auto zcornData = getAllSiDoubles_(newParserDeck->getKeyword(ZCORN_KW)); + EclipseKeyword coord_kw (COORD_KW, coordData); + EclipseKeyword zcorn_kw (ZCORN_KW, zcornData); + + // get the actually active cells, after processing + std::vector actnum; + getActiveCells_(grid, actnum); + EclipseKeyword actnum_kw (ACTNUM_KW, actnum); + + EclipseKeyword mapaxes_kw (MAPAXES_KW); + if (g.mapaxes) { + auto mapaxesData = getAllSiDoubles_(newParserDeck->getKeyword(MAPAXES_KW)); + mapaxes_kw = std::move (EclipseKeyword (MAPAXES_KW, mapaxesData)); + } + + return EclipseGrid (g.dims, zcorn_kw, coord_kw, actnum_kw, mapaxes_kw); + } + else { + OPM_THROW(std::runtime_error, + "Can't create an ERT grid (no supported keywords found in deck)"); + } + } + /** * Save the grid in an .EGRID file. */ @@ -600,6 +748,24 @@ struct EclipseInit : public EclipseHandle { timer.currentPosixTime()); } + void writeHeader (const UnstructuredGrid& grid, + const SimulatorTimer& timer, + Opm::DeckConstPtr newParserDeck, + const PhaseUsage uses) + { + auto dataField = getAllSiDoubles_(newParserDeck->getKeyword(PORO_KW)); + restrictToActiveCells_(dataField, grid); + + EclipseGrid eclGrid = EclipseGrid::make (newParserDeck, grid); + + EclipseKeyword poro (PORO_KW, dataField); + ecl_init_file_fwrite_header (*this, + eclGrid, + poro, + phaseMask (uses), + timer.currentPosixTime ()); + } + void writeKeyword (const std::string& keywordName, const EclipseGridParser& parser, double (* const transf)(const double&)) { @@ -656,6 +822,14 @@ struct EclipseSummary : public EclipseHandle { alloc_writer (outputDir, baseName, timer, parser), ecl_sum_free) { } + EclipseSummary (const std::string& outputDir, + const std::string& baseName, + const SimulatorTimer& timer, + Opm::DeckConstPtr newParserDeck) + : EclipseHandle ( + alloc_writer (outputDir, baseName, timer, newParserDeck), + ecl_sum_free) { } + typedef std::unique_ptr var_t; typedef std::vector vars_t; @@ -675,6 +849,10 @@ struct EclipseSummary : public EclipseHandle { void addWells (const EclipseGridParser& parser, const PhaseUsage& uses); + // add rate variables for each of the well in the input file + void addWells (Opm::DeckConstPtr newParserDeck, + const PhaseUsage& uses); + // no inline implementation of this since it depends on the // EclipseWellReport type being completed first void writeTimeStep (const SimulatorTimer& timer, @@ -721,6 +899,27 @@ private: dim[1], dim[2]); } + + /// Helper routine that lets us use local variables to hold + /// intermediate results while filling out the allocations function's + /// argument list. + static ecl_sum_type* alloc_writer (const std::string& outputDir, + const std::string& baseName, + const SimulatorTimer& timer, + Opm::DeckConstPtr newParserDeck) { + boost::filesystem::path casePath (outputDir); + casePath /= boost::to_upper_copy (baseName); + + const std::vector dim = parserDim (newParserDeck); + return ecl_sum_alloc_writer (casePath.string ().c_str (), + false, /* formatted */ + true, /* unified */ + ":", /* join string */ + timer.simulationTimeElapsed (), + dim[0], + dim[1], + dim[2]); + } }; @@ -752,6 +951,29 @@ protected: // producers can be seen as negative injectors , sign_ (type == INJECTOR ? +1. : -1.) { } + EclipseWellReport (const EclipseSummary& summary, /* section to add to */ + Opm::DeckConstPtr newParserDeck, /* well names */ + int whichWell, /* index of well line */ + PhaseUsage uses, /* phases present */ + BlackoilPhases::PhaseIndex phase, /* oil, water or gas */ + WellType type, /* prod. or inj. */ + char aggregation, /* rate or total */ + std::string unit) + : EclipseHandle ( + ecl_sum_add_var (summary, + varName (phase, + type, + aggregation).c_str (), + wellName (newParserDeck, whichWell).c_str (), + /* num = */ 0, + unit.c_str(), + /* defaultValue = */ 0.)) + // save these for when we update the value in a timestep + , index_ (whichWell * uses.num_phases + uses.phase_pos [phase]) + + // producers can be seen as negative injectors + , sign_ (type == INJECTOR ? +1. : -1.) { } + public: /// Allows us to pass this type to ecl_sum_tstep_iset operator int () { @@ -776,6 +998,14 @@ private: return parser.getWELSPECS().welspecs[whichWell].name_; } + /// Get the name associated with this well + std::string wellName (Opm::DeckConstPtr newParserDeck, + int whichWell) + { + Opm::WelspecsWrapper welspecs(newParserDeck->getKeyword("WELSPECS")); + return welspecs.wellName(whichWell); + } + /// Compose the name of the summary variable, e.g. "WOPR" for /// well oil production rate. std::string varName (BlackoilPhases::PhaseIndex phase, @@ -837,6 +1067,22 @@ struct EclipseWellRate : public EclipseWellReport { type, 'R', "SM3/DAY" /* surf. cub. m. per day */ ) { } + + EclipseWellRate (const EclipseSummary& summary, + Opm::DeckConstPtr newParserDeck, + int whichWell, + PhaseUsage uses, + BlackoilPhases::PhaseIndex phase, + WellType type) + : EclipseWellReport (summary, + newParserDeck, + whichWell, + uses, + phase, + type, + 'R', + "SM3/DAY" /* surf. cub. m. per day */ ) { } + virtual double update (const SimulatorTimer& /*timer*/, const WellState& wellState) { // TODO: Why only positive rates? @@ -864,6 +1110,24 @@ struct EclipseWellTotal : public EclipseWellReport { // nothing produced when the reporting starts , total_ (0.) { } + EclipseWellTotal (const EclipseSummary& summary, + Opm::DeckConstPtr newParserDeck, + int whichWell, + PhaseUsage uses, + BlackoilPhases::PhaseIndex phase, + WellType type) + : EclipseWellReport (summary, + newParserDeck, + whichWell, + uses, + phase, + type, + 'T', + "SM3" /* surface cubic meter */ ) + + // nothing produced when the reporting starts + , total_ (0.) { } + virtual double update (const SimulatorTimer& timer, const WellState& wellState) { if (timer.currentStepNum() == 0) { @@ -991,6 +1255,49 @@ EclipseSummary::addWells (const EclipseGridParser& parser, } } +inline void +EclipseSummary::addWells (Opm::DeckConstPtr newParserDeck, + const PhaseUsage& uses) { + // TODO: Only create report variables that are requested with keywords + // (e.g. "WOPR") in the input files, and only for those wells that are + // mentioned in those keywords + Opm::DeckKeywordConstPtr welspecsKeyword = newParserDeck->getKeyword("WELSPECS"); + const int numWells = welspecsKeyword->size(); + for (int phaseCounter = 0; + phaseCounter != BlackoilPhases::MaxNumPhases; + ++phaseCounter) { + const BlackoilPhases::PhaseIndex phase = + static_cast (phaseCounter); + // don't bother with reporting for phases that aren't there + if (!uses.phase_used [phaseCounter]) { + continue; + } + for (size_t typeIndex = 0; + typeIndex < sizeof (WELL_TYPES) / sizeof (WELL_TYPES[0]); + ++typeIndex) { + const WellType type = WELL_TYPES[typeIndex]; + for (int whichWell = 0; whichWell != numWells; ++whichWell) { + // W{O,G,W}{I,P}R + add (std::unique_ptr ( + new EclipseWellRate (*this, + newParserDeck, + whichWell, + uses, + phase, + type))); + // W{O,G,W}{I,P}T + add (std::unique_ptr ( + new EclipseWellTotal (*this, + newParserDeck, + whichWell, + uses, + phase, + type))); + } + } + } +} + namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer &timer, @@ -1002,33 +1309,75 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, if (!enableOutput_) { return; } + if (newParserDeck_) { + /* Grid files */ + EclipseGrid eclGrid = EclipseGrid::make (newParserDeck_, *grid_); + eclGrid.write (outputDir_, baseName_, /*stepIdx=*/0); - /* Grid files */ - EclipseGrid ecl_grid = EclipseGrid::make (*parser_, *grid_); - ecl_grid.write (outputDir_, baseName_, /*stepIdx=*/0); + EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); + fortio.writeHeader (*grid_, + timer, + newParserDeck_, + uses_); - EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); - fortio.writeHeader (ecl_grid, - timer, - *parser_, - uses_); + if (newParserDeck_->hasKeyword("PERM")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERM")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERM", data); + } - fortio.writeKeyword ("PERMX", *parser_, &toMilliDarcy); - fortio.writeKeyword ("PERMY", *parser_, &toMilliDarcy); - fortio.writeKeyword ("PERMZ", *parser_, &toMilliDarcy); + if (newParserDeck_->hasKeyword("PERMX")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMX")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMX", data); + } + if (newParserDeck_->hasKeyword("PERMY")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMY")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMY", data); + } + if (newParserDeck_->hasKeyword("PERMZ")) { + auto data = getAllSiDoubles_(newParserDeck_->getKeyword("PERMZ")); + convertUnit_(data, toMilliDarcy); + fortio.writeKeyword ("PERMZ", data); + } - /* Initial solution (pressure and saturation) */ - writeSolution_(timer, reservoirState); + /* Initial solution (pressure and saturation) */ + writeSolution_(timer, reservoirState); - /* Create summary object (could not do it at construction time, - since it requires knowledge of the start time). */ - summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, *parser_)); - summary_->addWells (*parser_, uses_); + /* Create summary object (could not do it at construction time, + since it requires knowledge of the start time). */ + summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, newParserDeck_)); + summary_->addWells (newParserDeck_, uses_); + } + else { + /* Grid files */ + EclipseGrid ecl_grid = EclipseGrid::make (*parser_, *grid_); + ecl_grid.write (outputDir_, baseName_, /*stepIdx=*/0); + + EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); + fortio.writeHeader (ecl_grid, + timer, + *parser_, + uses_); + + fortio.writeKeyword ("PERMX", *parser_, &toMilliDarcy); + fortio.writeKeyword ("PERMY", *parser_, &toMilliDarcy); + fortio.writeKeyword ("PERMZ", *parser_, &toMilliDarcy); + + /* Initial solution (pressure and saturation) */ + writeSolution_(timer, reservoirState); + + /* Create summary object (could not do it at construction time, + since it requires knowledge of the start time). */ + summary_.reset(new EclipseSummary(outputDir_, baseName_, timer, *parser_)); + summary_->addWells (*parser_, uses_); + } } void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, - const std::vector &activeCellsBuf, - const std::vector &inactiveCellsBuf) const + const std::vector &activeCellsBuf, + const std::vector &inactiveCellsBuf) const { globalCellsBuf = inactiveCellsBuf; @@ -1045,40 +1394,72 @@ void EclipseWriter::activeToGlobalCellData_(std::vector &globalCellsBuf, void EclipseWriter::writeSolution_(const SimulatorTimer& timer, const SimulatorState& reservoirState) { - // start writing to files - EclipseRestart rst (outputDir_, - baseName_, - timer, - outputTimeStepIdx_); - rst.writeHeader (timer, - outputTimeStepIdx_, - uses_, - *parser_, - reservoirState.pressure ().size ()); - EclipseSolution sol (rst); + if (newParserDeck_) { + // start writing to files + EclipseRestart rst(outputDir_, baseName_, timer, outputTimeStepIdx_); + rst.writeHeader (timer, outputTimeStepIdx_, uses_, newParserDeck_, reservoirState.pressure().size ()); + EclipseSolution sol (rst); - // write pressure and saturation fields (same as DataMap holds) - // convert the pressures from Pascals to bar because Eclipse - // seems to write bars - auto data = reservoirState.pressure(); - convertUnit_(data, toBar); - sol.add(EclipseKeyword("PRESSURE", data)); + // write out the pressure of the reference phase (whatever + // phase that is...). this is not the most performant solution + // thinkable, but this is also not in the most performance + // critical code path! + std::vector tmp = reservoirState.pressure(); + restrictToActiveCells_(tmp, *grid_); + convertUnit_(tmp, toBar); - for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { - // Eclipse never writes the oil saturation, so all post-processors - // must calculate this from the other saturations anyway - if (phase == BlackoilPhases::PhaseIndex::Liquid) { - continue; - } - if (uses_.phase_used [phase]) { - auto tmp = reservoirState.saturation(); - extractFromStripedData_(tmp, - /*offset=*/uses_.phase_pos[phase], - /*stride=*/uses_.num_phases); - sol.add (EclipseKeyword(SAT_NAMES[phase], tmp)); + sol.add(EclipseKeyword("PRESSURE", tmp)); + + for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { + // Eclipse never writes the oil saturation, so all post-processors + // must calculate this from the other saturations anyway + if (phase == BlackoilPhases::PhaseIndex::Liquid) { + continue; + } + if (uses_.phase_used [phase]) { + tmp = reservoirState.saturation(); + extractFromStripedData_(tmp, + /*offset=*/uses_.phase_pos[phase], + /*stride=*/uses_.num_phases); + sol.add(EclipseKeyword(SAT_NAMES[phase], tmp)); + } } } + else { + // start writing to files + EclipseRestart rst (outputDir_, + baseName_, + timer, + outputTimeStepIdx_); + rst.writeHeader (timer, + outputTimeStepIdx_, + uses_, + *parser_, + reservoirState.pressure ().size ()); + EclipseSolution sol (rst); + // write pressure and saturation fields (same as DataMap holds) + // convert the pressures from Pascals to bar because Eclipse + // seems to write bars + auto data = reservoirState.pressure(); + convertUnit_(data, toBar); + sol.add(EclipseKeyword("PRESSURE", data)); + + for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { + // Eclipse never writes the oil saturation, so all post-processors + // must calculate this from the other saturations anyway + if (phase == BlackoilPhases::PhaseIndex::Liquid) { + continue; + } + if (uses_.phase_used [phase]) { + auto tmp = reservoirState.saturation(); + extractFromStripedData_(tmp, + /*offset=*/uses_.phase_pos[phase], + /*stride=*/uses_.num_phases); + sol.add (EclipseKeyword(SAT_NAMES[phase], tmp)); + } + } + } ++outputTimeStepIdx_; } @@ -1157,6 +1538,7 @@ EclipseWriter::EclipseWriter ( std::shared_ptr parser, std::shared_ptr grid) : parser_ (parser) + , newParserDeck_(0) , grid_ (grid) , uses_ (phaseUsageFromDeck (*parser)) { @@ -1206,6 +1588,57 @@ EclipseWriter::EclipseWriter ( } } +EclipseWriter::EclipseWriter ( + const ParameterGroup& params, + Opm::DeckConstPtr newParserDeck, + std::shared_ptr grid) + : parser_ (0) + , newParserDeck_(newParserDeck) + , grid_ (grid) + , uses_ (phaseUsageFromDeck (newParserDeck)) +{ + // get the base name from the name of the deck + using boost::filesystem::path; + path deck (params.get ("deck_filename")); + if (boost::to_upper_copy (path (deck.extension ()).string ()) == ".DATA") { + baseName_ = path (deck.stem ()).string (); + } + else { + baseName_ = path (deck.filename ()).string (); + } + + // make uppercase of everything (or otherwise we'll get uppercase + // of some of the files (.SMSPEC, .UNSMRY) and not others + baseName_ = boost::to_upper_copy (baseName_); + + // retrieve the value of the "output" parameter + enableOutput_ = params.getDefault("output", /*defaultValue=*/true); + + // retrieve the interval at which something should get written to + // disk (once every N timesteps) + outputInterval_ = params.getDefault("output_interval", /*defaultValue=*/1); + + // store in current directory if not explicitly set + outputDir_ = params.getDefault("output_dir", "."); + + // set the index of the first time step written to 0... + outputTimeStepIdx_ = 0; + + if (enableOutput_) { + // make sure that the output directory exists, if not try to create it + if (!boost::filesystem::exists(outputDir_)) { + std::cout << "Trying to create directory \"" << outputDir_ << "\" for the simulation output\n"; + boost::filesystem::create_directories(outputDir_); + } + + if (!boost::filesystem::is_directory(outputDir_)) { + OPM_THROW(std::runtime_error, + "The path specified as output directory '" << outputDir_ + << "' is not a directory"); + } + } +} + // default destructor is OK, just need to be defined EclipseWriter::~EclipseWriter() { } diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 5ec99543..8b98d3be 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -24,6 +24,8 @@ #include #include +#include + #include #include #include // std::unique_ptr @@ -61,6 +63,14 @@ public: std::shared_ptr parser, std::shared_ptr grid); + /*! + * \brief Sets the common attributes required to write eclipse + * binary files using ERT. + */ + EclipseWriter(const parameter::ParameterGroup& params, + Opm::DeckConstPtr newParserDeck, + std::shared_ptr grid); + /** * We need a destructor in the compilation unit to avoid the * EclipseSummary being a complete type here. @@ -88,6 +98,7 @@ public: private: std::shared_ptr parser_; + Opm::DeckConstPtr newParserDeck_; std::shared_ptr grid_; bool enableOutput_; int outputInterval_; From 69a6a38856dc31a0dee93dcc182d0a886a342428 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 17:33:59 +0100 Subject: [PATCH 098/109] Complete cf7f07b PR #530 to support FastAMG with dune-istl 2.3 This fixes further occurences of DUNE_HAS_FASTAMG that were missed in pull request #530. Previously we relied on the define DUNE_HAS_FAST_AMG to detect whether these preconditioners are available. This define is only available in the 2.2 release with cmake support. Therfore we now addtionally test whether we are using dune-istl 2.3 or newer. --- opm/core/linalg/LinearSolverIstl.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index f9e2bb7b..34554891 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -174,7 +174,7 @@ namespace Opm linsolver_prolongate_factor_, linsolver_smooth_steps_); break; case KAMG: -#ifdef HAS_DUNE_FAST_AMG +#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) res = solveKAMG(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_, linsolver_smooth_steps_); #else @@ -182,7 +182,7 @@ namespace Opm #endif break; case FastAMG: -#ifdef HAS_DUNE_FAST_AMG +#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) res = solveFastAMG(A, x, b, linsolver_residual_tolerance_, maxit, linsolver_verbosity_, linsolver_prolongate_factor_); #else @@ -312,7 +312,7 @@ namespace Opm } -#ifdef HAS_DUNE_FAST_AMG +#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) LinearSolverInterface::LinearSolverReport solveKAMG(const Mat& A, Vector& x, Vector& b, double tolerance, int maxit, int verbosity, double linsolver_prolongate_factor, int linsolver_smooth_steps) From b773c3de8944b459b57c392212580375189fce71 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 19:49:48 +0100 Subject: [PATCH 099/109] Include fastamg.hh for dune-istl 2.3 For the inofficial 2.2 release this was included automatically with amg.hh. --- opm/core/linalg/LinearSolverIstl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 34554891..c32ad02e 100644 --- a/opm/core/linalg/LinearSolverIstl.cpp +++ b/opm/core/linalg/LinearSolverIstl.cpp @@ -40,6 +40,10 @@ #include #include +#if DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) +#include +#endif + #include #include From 7281a9567f5c420b0dc5debbc812cccf0b513268 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 21:32:36 +0100 Subject: [PATCH 100/109] Added a test for the linear solver interface. This test sets up a simple laplace problem and solves it with the available solvers. It assume that either dune-istl or UMFPack is present, which is assume to be safe. --- CMakeLists_files.cmake | 1 + tests/test_linearsolver.cpp | 182 ++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 tests/test_linearsolver.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 75671e11..c16ca66e 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -162,6 +162,7 @@ list (APPEND TEST_SOURCE_FILES tests/test_wachspresscoord.cpp tests/test_column_extract.cpp tests/test_geom2d.cpp + tests/test_linearsolver.cpp tests/test_param.cpp tests/test_blackoilfluid.cpp tests/test_shadow.cpp diff --git a/tests/test_linearsolver.cpp b/tests/test_linearsolver.cpp new file mode 100644 index 00000000..e4a92ddd --- /dev/null +++ b/tests/test_linearsolver.cpp @@ -0,0 +1,182 @@ +/* + Copyright 2014 Dr. Markus Blatt - HPC-Simulation-Software & Services + + 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 // to suppress our messages when throwing + +#define BOOST_TEST_MODULE OPM-IterativeSolverTest +#include + +#include +#include + +#include +#include +#include + +struct MyMatrix +{ + MyMatrix(int rows, int nnz) + : data(nnz, 0.0), rowStart(rows+1, -1), + colIndex(nnz, -1) + {} + MyMatrix() + : data(), rowStart(), colIndex() + {} + + std::vector data; + std::vector rowStart; + std::vector colIndex; +}; + +std::shared_ptr createLaplacian(int N) +{ + MyMatrix* mm=new MyMatrix(N*N, N*N*5); + int nnz=0; + mm->rowStart[0]=0; + for(int row=0; row0) + { + mm->colIndex[nnz]=row-N; + mm->data[nnz++]=-1; + dval+=1; + } + if(x>0) + { + mm->colIndex[nnz]=row-1; + mm->data[nnz++]=-1; + dval+=1; + } + mm->colIndex[nnz]=row; + mm->data[nnz++]=dval+(xcolIndex[nnz]=row+1; + mm->data[nnz++]=-1; + } + if(ycolIndex[nnz]=row+N; + mm->data[nnz++]=-1; + } + mm->rowStart[row+1]=nnz; + } + mm->data.resize(nnz); + mm->colIndex.resize(nnz); + return std::shared_ptr(mm); +} + +void createRandomVectors(int NN, std::vector& x, std::vector& b, + const MyMatrix& mat) +{ + x.resize(NN); + for(auto entry=x.begin(), end =x.end(); entry!=end; ++entry) + *entry=((double) (rand()%100))/10.0; + + b.resize(NN); + std::fill(b.begin(), b.end(), 0.0); + + // Construct the right hand side as b=A*x + for(std::size_t row=0; row x(N*N), b(N*N); + createRandomVectors(100*100, x, b, *mat); + std::vector exact(x); + std::fill(x.begin(), x.end(), 0.0); + Opm::LinearSolverFactory ls(param); + ls.solve(N*N, mat->data.size(), &(mat->rowStart[0]), + &(mat->colIndex[0]), &(mat->data[0]), &(b[0]), + &(x[0])); +} + + +BOOST_AUTO_TEST_CASE(DefaultTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +#ifdef HAVE_DUNE_ISTL +BOOST_AUTO_TEST_CASE(CGAMGTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("1")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(CGILUTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("0")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(BiCGILUTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("2")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(FastAMGTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("3")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +BOOST_AUTO_TEST_CASE(KAMGTest) +{ + Opm::parameter::ParameterGroup param; + param.insertParameter(std::string("linsolver"), std::string("istl")); + param.insertParameter(std::string("linsolver_type"), std::string("4")); + param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + run_test(param); +} + +#endif From ae6ef8249baf4adbace4b9cc40b26fa4dc3748b0 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 10:49:03 +0100 Subject: [PATCH 101/109] Use empty constructor to construct empty shared_ptr. At least for g++-4.4. shared_ptr does not have a constructor taking an integer and therefore compilation fails. Therefore we resort statements to construct empty pointers, like ```parser_(0)``` to using the empty constructor: ```parser_()``` This patch closes #533 --- opm/core/io/eclipse/EclipseWriter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 65b7e400..dce64e6a 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1538,7 +1538,7 @@ EclipseWriter::EclipseWriter ( std::shared_ptr parser, std::shared_ptr grid) : parser_ (parser) - , newParserDeck_(0) + , newParserDeck_() , grid_ (grid) , uses_ (phaseUsageFromDeck (*parser)) { @@ -1592,7 +1592,7 @@ EclipseWriter::EclipseWriter ( const ParameterGroup& params, Opm::DeckConstPtr newParserDeck, std::shared_ptr grid) - : parser_ (0) + : parser_ () , newParserDeck_(newParserDeck) , grid_ (grid) , uses_ (phaseUsageFromDeck (newParserDeck)) From 2443903928682029128920b5919f9cb3ecd39e73 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 20 Mar 2014 15:59:02 +0100 Subject: [PATCH 102/109] EclipseWriter: don't convert the pressure field to active cells because that array already is restricted to the active cells... found using ASAN. --- opm/core/io/eclipse/EclipseWriter.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index dce64e6a..f625032a 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1405,7 +1405,6 @@ void EclipseWriter::writeSolution_(const SimulatorTimer& timer, // thinkable, but this is also not in the most performance // critical code path! std::vector tmp = reservoirState.pressure(); - restrictToActiveCells_(tmp, *grid_); convertUnit_(tmp, toBar); sol.add(EclipseKeyword("PRESSURE", tmp)); From 2397eecf6fb8c21484b50487b59b109b02222cc8 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 20 Mar 2014 16:32:30 +0100 Subject: [PATCH 103/109] EclipseWriter: work around a GCC 4.4 smart_ptr bug maybe it is not a bug but a slightly spec. The problem is that GCC 4.4 does not implicitly convert std::shared_ptr<$FOO> to std::shared_ptr which caused the recent Jenkins build errors at Statoil. Note that this problem only occurs with the output writer in conjunction with the old Eclipse parser, so OPM/opm-autodiff#105 also makes the problem disappear. The present patch addresses the root cause, though... --- opm/core/io/OutputWriter.cpp | 6 +++--- opm/core/io/OutputWriter.hpp | 2 +- opm/core/io/eclipse/EclipseWriter.cpp | 2 +- opm/core/io/eclipse/EclipseWriter.hpp | 2 +- opm/core/simulator/SimulatorOutput.cpp | 2 +- opm/core/simulator/SimulatorOutput.hpp | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/opm/core/io/OutputWriter.cpp b/opm/core/io/OutputWriter.cpp index 434b0a0f..5645f420 100644 --- a/opm/core/io/OutputWriter.cpp +++ b/opm/core/io/OutputWriter.cpp @@ -48,7 +48,7 @@ private: /// Psuedo-constructor, can appear in template template unique_ptr create (const ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid) { return unique_ptr (new Format (params, parser, grid)); } @@ -61,7 +61,7 @@ create (const ParameterGroup& params, /// to the list below! typedef map (*)( const ParameterGroup&, - std::shared_ptr , + std::shared_ptr , std::shared_ptr )> map_t; map_t FORMATS = { { "output_ecl", &create }, @@ -71,7 +71,7 @@ map_t FORMATS = { unique_ptr OutputWriter::create (const ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid) { // allocate a list which will be filled with writers. this list // is initially empty (no output). diff --git a/opm/core/io/OutputWriter.hpp b/opm/core/io/OutputWriter.hpp index 909ba893..ea19ea01 100644 --- a/opm/core/io/OutputWriter.hpp +++ b/opm/core/io/OutputWriter.hpp @@ -108,7 +108,7 @@ public: */ static std::unique_ptr create (const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid); }; diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index dce64e6a..9ea66ff6 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1535,7 +1535,7 @@ void EclipseWriter::writeTimeStep( EclipseWriter::EclipseWriter ( const ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid) : parser_ (parser) , newParserDeck_() diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 8b98d3be..4adfbba6 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -60,7 +60,7 @@ public: * binary files using ERT. */ EclipseWriter(const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid); /*! diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index ddb375af..2fade1fd 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -30,7 +30,7 @@ using namespace Opm; SimulatorOutputBase::SimulatorOutputBase ( const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, diff --git a/opm/core/simulator/SimulatorOutput.hpp b/opm/core/simulator/SimulatorOutput.hpp index da9e71e3..22dffeeb 100644 --- a/opm/core/simulator/SimulatorOutput.hpp +++ b/opm/core/simulator/SimulatorOutput.hpp @@ -53,7 +53,7 @@ protected: * need to pick them up from the object members. */ SimulatorOutputBase (const parameter::ParameterGroup& p, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -139,7 +139,7 @@ private: template struct SimulatorOutput : public SimulatorOutputBase { SimulatorOutput (const parameter::ParameterGroup& params, - std::shared_ptr parser, + std::shared_ptr parser, std::shared_ptr grid, std::shared_ptr timer, std::shared_ptr state, @@ -161,7 +161,7 @@ struct SimulatorOutput : public SimulatorOutputBase { * the arguments passed exceeds the lifetime of this object. */ SimulatorOutput (const parameter::ParameterGroup& params, - const EclipseGridParser& parser, + EclipseGridParser& parser, const UnstructuredGrid& grid, const SimulatorTimer& timer, const SimulatorState& state, From 34d246600e52b710fe3a2c9e14d2967f446ccf6e Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 22:18:27 +0100 Subject: [PATCH 104/109] Fixed test_linearsolver for the official 2.2 release. This release does neither support KAMG nor FastAMG. Therfore this patch deactivates test these preconditioners. --- tests/test_linearsolver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_linearsolver.cpp b/tests/test_linearsolver.cpp index e4a92ddd..c122de7f 100644 --- a/tests/test_linearsolver.cpp +++ b/tests/test_linearsolver.cpp @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -161,6 +162,7 @@ BOOST_AUTO_TEST_CASE(BiCGILUTest) run_test(param); } +#if defined(HAS_DUNE_FAST_AMG) || DUNE_VERSION_NEWER(DUNE_ISTL, 2, 3) BOOST_AUTO_TEST_CASE(FastAMGTest) { Opm::parameter::ParameterGroup param; @@ -178,5 +180,5 @@ BOOST_AUTO_TEST_CASE(KAMGTest) param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); run_test(param); } - +#endif #endif From 621afe327329554813b5abc6a7032bc10303b129 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 22:21:03 +0100 Subject: [PATCH 105/109] Fixes the laplacian. As we do not represent dofs on the boundary, we need to make sure that all diagonal values are 4. --- tests/test_linearsolver.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_linearsolver.cpp b/tests/test_linearsolver.cpp index c122de7f..f1bb7f97 100644 --- a/tests/test_linearsolver.cpp +++ b/tests/test_linearsolver.cpp @@ -60,21 +60,18 @@ std::shared_ptr createLaplacian(int N) int x=row%N; int y=row/N; - double dval=0; if(y>0) { mm->colIndex[nnz]=row-N; mm->data[nnz++]=-1; - dval+=1; } if(x>0) { mm->colIndex[nnz]=row-1; mm->data[nnz++]=-1; - dval+=1; } mm->colIndex[nnz]=row; - mm->data[nnz++]=dval+(xdata[nnz++]=4; if(xcolIndex[nnz]=row+1; From 23332dcf8fcccd617529a740a4c58d6f28f82d08 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 21:49:48 +0100 Subject: [PATCH 106/109] Increase verbosity such that we see the actual solve --- tests/test_linearsolver.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_linearsolver.cpp b/tests/test_linearsolver.cpp index f1bb7f97..184b7765 100644 --- a/tests/test_linearsolver.cpp +++ b/tests/test_linearsolver.cpp @@ -128,6 +128,7 @@ BOOST_AUTO_TEST_CASE(DefaultTest) { Opm::parameter::ParameterGroup param; param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + param.insertParameter(std::string("linsolver_verbosity"), std::string("2")); run_test(param); } @@ -138,6 +139,7 @@ BOOST_AUTO_TEST_CASE(CGAMGTest) param.insertParameter(std::string("linsolver"), std::string("istl")); param.insertParameter(std::string("linsolver_type"), std::string("1")); param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + param.insertParameter(std::string("linsolver_verbosity"), std::string("2")); run_test(param); } @@ -147,6 +149,7 @@ BOOST_AUTO_TEST_CASE(CGILUTest) param.insertParameter(std::string("linsolver"), std::string("istl")); param.insertParameter(std::string("linsolver_type"), std::string("0")); param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + param.insertParameter(std::string("linsolver_verbosity"), std::string("2")); run_test(param); } @@ -156,6 +159,7 @@ BOOST_AUTO_TEST_CASE(BiCGILUTest) param.insertParameter(std::string("linsolver"), std::string("istl")); param.insertParameter(std::string("linsolver_type"), std::string("2")); param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + param.insertParameter(std::string("linsolver_verbosity"), std::string("2")); run_test(param); } @@ -166,6 +170,7 @@ BOOST_AUTO_TEST_CASE(FastAMGTest) param.insertParameter(std::string("linsolver"), std::string("istl")); param.insertParameter(std::string("linsolver_type"), std::string("3")); param.insertParameter(std::string("linsolver_max_iterations"), std::string("200")); + param.insertParameter(std::string("linsolver_verbosity"), std::string("2")); run_test(param); } From 5eaed335b9ab69b30957c26e95accc941cf1893a Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 21 Mar 2014 00:35:15 +0100 Subject: [PATCH 107/109] Changed the access to Parser/EclipseState/Schedule to use new well injection and production properties. To be aligned with opm-parser:e75970a28b96374409a55e3e6cea2198b6a0ea23 --- opm/core/wells/WellsGroup.cpp | 22 ++++---- opm/core/wells/WellsManager.cpp | 94 ++++++++++++++++++--------------- tests/test_wellsgroup.cpp | 16 +++--- 3 files changed, 71 insertions(+), 61 deletions(-) diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index ee45b6ed..050e1871 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -1171,19 +1171,21 @@ namespace Opm 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); + const WellInjectionProperties& properties = well->getInjectionProperties(timeStep); + injection_specification.BHP_limit_ = properties.BHPLimit; + injection_specification.injector_type_ = toInjectorType(WellInjector::Type2String(properties.injectorType)); + injection_specification.control_mode_ = toInjectionControlMode(WellInjector::ControlMode2String(properties.controlMode)); + injection_specification.surface_flow_max_rate_ = properties.surfaceInjectionRate; + injection_specification.reservoir_flow_max_rate_ = properties.reservoirInjectionRate; production_specification.guide_rate_ = 0.0; // We know we're not a producer } 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); - production_specification.control_mode_ = toProductionControlMode(WellProducer::ControlMode2String(well->getProducerControlMode(timeStep))); - production_specification.water_max_rate_ = well->getWaterRate(timeStep); + const WellProductionProperties& properties = well->getProductionProperties(timeStep); + production_specification.BHP_limit_ = properties.BHPLimit; + production_specification.reservoir_flow_max_rate_ = properties.ResVRate; + production_specification.oil_max_rate_ = properties.OilRate; + production_specification.control_mode_ = toProductionControlMode(WellProducer::ControlMode2String(properties.controlMode)); + production_specification.water_max_rate_ = properties.WaterRate; 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)); diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index c438b29e..3421d3c2 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -1131,14 +1131,15 @@ namespace Opm } if (well->isInjector(timeStep)) { - clear_well_controls(well_index, w_); + const WellInjectionProperties& injectionProperties = well->getInjectionProperties(timeStep); int ok = 1; int control_pos[5] = { -1, -1, -1, -1, -1 }; - - if (well->hasInjectionControl(timeStep , WellInjector::RATE)) { + + clear_well_controls(well_index, w_); + if (injectionProperties.hasInjectionControl(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); + WellInjector::TypeEnum injectorType = injectionProperties.injectorType; if (injectorType == WellInjector::TypeEnum::WATER) { distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; @@ -1149,16 +1150,16 @@ namespace Opm } ok = append_well_controls(SURFACE_RATE, - well->getSurfaceInjectionRate( timeStep ) , + injectionProperties.surfaceInjectionRate, distr, well_index, w_); } - if (ok && well->hasInjectionControl(timeStep , WellInjector::RESV)) { + if (ok && injectionProperties.hasInjectionControl(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); + WellInjector::TypeEnum injectorType = injectionProperties.injectorType; if (injectorType == WellInjector::TypeEnum::WATER) { distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0; @@ -1169,22 +1170,22 @@ namespace Opm } ok = append_well_controls(RESERVOIR_RATE, - well->getReservoirInjectionRate( timeStep ), + injectionProperties.reservoirInjectionRate, distr, well_index, w_); } - if (ok && well->hasInjectionControl(timeStep , WellInjector::BHP)) { + if (ok && injectionProperties.hasInjectionControl(WellInjector::BHP)) { control_pos[InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); ok = append_well_controls(BHP, - well->getBHPLimit(timeStep), + injectionProperties.BHPLimit, NULL, well_index, w_); } - if (ok && well->hasInjectionControl(timeStep , WellInjector::THP)) { + if (ok && injectionProperties.hasInjectionControl(WellInjector::THP)) { OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); } @@ -1195,7 +1196,7 @@ namespace Opm { - InjectionControl::Mode mode = InjectionControl::mode( well->getInjectorControlMode(timeStep) ); + InjectionControl::Mode mode = InjectionControl::mode( injectionProperties.controlMode ); 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]); @@ -1211,34 +1212,39 @@ namespace Opm // 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); + { + WellInjector::TypeEnum injectorType = injectionProperties.injectorType; + if (injectorType == 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 (injectorType == 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 (injectorType == 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_); + const WellProductionProperties& productionProperties = well->getProductionProperties(timeStep); int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 }; int ok = 1; - if (ok && well->hasProductionControl(timeStep , WellProducer::ORAT)) { + + clear_well_controls(well_index, w_); + if (ok && productionProperties.hasProductionControl(WellProducer::ORAT)) { if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) { OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified."); } @@ -1247,13 +1253,13 @@ namespace Opm 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 ), + -productionProperties.OilRate, distr, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::WRAT)) { + if (ok && productionProperties.hasProductionControl(WellProducer::WRAT)) { if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified."); } @@ -1261,13 +1267,13 @@ namespace Opm 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), + -productionProperties.WaterRate, distr, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::GRAT)) { + if (ok && productionProperties.hasProductionControl(WellProducer::GRAT)) { if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) { OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified."); } @@ -1275,13 +1281,13 @@ namespace Opm 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 ), + -productionProperties.GasRate, distr, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::LRAT)) { + if (ok && productionProperties.hasProductionControl(WellProducer::LRAT)) { if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) { OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified."); } @@ -1293,32 +1299,32 @@ namespace Opm 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), + -productionProperties.LiquidRate , distr, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::RESV)) { + if (ok && productionProperties.hasProductionControl(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), + -productionProperties.ResVRate , distr, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::BHP)) { + if (ok && productionProperties.hasProductionControl(WellProducer::BHP)) { control_pos[ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]); ok = append_well_controls(BHP, - well->getBHPLimit( timeStep ) , + productionProperties.BHPLimit , NULL, well_index, w_); } - if (ok && well->hasProductionControl(timeStep , WellProducer::THP)) { + if (ok && productionProperties.hasProductionControl(WellProducer::THP)) { OPM_THROW(std::runtime_error, "We cannot handle THP limit for well " << well_names[well_index]); } @@ -1326,7 +1332,7 @@ namespace Opm OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]); } - ProductionControl::Mode mode = ProductionControl::mode(well->getProducerControlMode(timeStep)); + ProductionControl::Mode mode = ProductionControl::mode(productionProperties.controlMode); int cpos = control_pos[mode]; if (well->getStatus(timeStep) == WellCommon::SHUT) { well_controls_shut_well( w_->ctrls[well_index] ); diff --git a/tests/test_wellsgroup.cpp b/tests/test_wellsgroup.cpp index 9ba9e0ef..46fd8eb0 100644 --- a/tests/test_wellsgroup.cpp +++ b/tests/test_wellsgroup.cpp @@ -59,16 +59,18 @@ BOOST_AUTO_TEST_CASE(ConstructGroupFromWell) { std::shared_ptr 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_); + const WellInjectionProperties& properties = well->getInjectionProperties(2); + BOOST_CHECK_EQUAL(properties.surfaceInjectionRate, wellsGroup->injSpec().surface_flow_max_rate_); + BOOST_CHECK_EQUAL(properties.BHPLimit, wellsGroup->injSpec().BHP_limit_); + BOOST_CHECK_EQUAL(properties.reservoirInjectionRate, 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_); + const WellProductionProperties& properties = well->getProductionProperties(2); + BOOST_CHECK_EQUAL(properties.ResVRate, wellsGroup->prodSpec().reservoir_flow_max_rate_); + BOOST_CHECK_EQUAL(properties.BHPLimit, wellsGroup->prodSpec().BHP_limit_); + BOOST_CHECK_EQUAL(properties.OilRate, wellsGroup->prodSpec().oil_max_rate_); + BOOST_CHECK_EQUAL(properties.WaterRate, wellsGroup->prodSpec().water_max_rate_); BOOST_CHECK_EQUAL(0.0, wellsGroup->injSpec().guide_rate_); } } From dbd351dd4d6c94e977e364efb7c0896c3a83075c Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Mon, 24 Mar 2014 16:41:02 +0100 Subject: [PATCH 108/109] Output correct information for temporary use. Change the SimulatorTimer class a little bit to output the totaltime and current time correctly. --- opm/core/simulator/SimulatorTimer.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/opm/core/simulator/SimulatorTimer.cpp b/opm/core/simulator/SimulatorTimer.cpp index 4c36c0d7..5295afe4 100644 --- a/opm/core/simulator/SimulatorTimer.cpp +++ b/opm/core/simulator/SimulatorTimer.cpp @@ -102,7 +102,7 @@ namespace Opm /// Current step number. int SimulatorTimer::currentStepNum() const { - return current_step_; + return current_step_ + beginReportStepIdx_; } /// Set current step number. @@ -143,8 +143,7 @@ namespace Opm { if (timeMap_) return - timeMap_->getTimePassedUntil(beginReportStepIdx_ + current_step_) - - timeMap_->getTimePassedUntil(beginReportStepIdx_); + timeMap_->getTimePassedUntil(beginReportStepIdx_ + current_step_); else return current_time_; } @@ -171,8 +170,7 @@ namespace Opm { if (timeMap_) return - timeMap_->getTimePassedUntil(endReportStepIdx_) - - timeMap_->getTimePassedUntil(beginReportStepIdx_); + timeMap_->getTotalTime(); else return total_time_; } From 20468d19876092fe1aaf9762121ad2527a2b3228 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 25 Mar 2014 18:57:58 +0100 Subject: [PATCH 109/109] Removed WellsManager constructor which takes an ole Eclipsegridparser instance. --- examples/compute_tof.cpp | 8 +- examples/sim_2p_comp_reorder.cpp | 10 +- examples/sim_2p_incomp.cpp | 10 +- examples/wells_example.cpp | 7 +- opm/core/wells/WellsManager.cpp | 579 ------------------------------- opm/core/wells/WellsManager.hpp | 5 - tests/test_wellsmanager.cpp | 95 +---- 7 files changed, 39 insertions(+), 675 deletions(-) diff --git a/examples/compute_tof.cpp b/examples/compute_tof.cpp index 876977ec..c325e070 100644 --- a/examples/compute_tof.cpp +++ b/examples/compute_tof.cpp @@ -47,6 +47,9 @@ #include #include +#include +#include + #include #include @@ -111,13 +114,16 @@ try double gravity[3] = { 0.0 }; if (use_deck) { std::string deck_filename = param.get("deck_filename"); + Opm::ParserPtr parser(new Opm::Parser()); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile(deck_filename))); + deck.reset(new EclipseGridParser(deck_filename)); // Grid init grid.reset(new GridManager(*deck)); // Rock and fluid init props.reset(new IncompPropertiesFromDeck(*deck, *grid->c_grid())); // Wells init. - wells.reset(new Opm::WellsManager(*deck, *grid->c_grid(), props->permeability())); + wells.reset(new Opm::WellsManager(eclipseState , 0 , *grid->c_grid(), props->permeability())); // Gravity. gravity[2] = deck->hasField("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). diff --git a/examples/sim_2p_comp_reorder.cpp b/examples/sim_2p_comp_reorder.cpp index 18077859..aaddefe2 100644 --- a/examples/sim_2p_comp_reorder.cpp +++ b/examples/sim_2p_comp_reorder.cpp @@ -44,6 +44,10 @@ #include #include +#include +#include + + #include #include @@ -80,6 +84,7 @@ try // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); + EclipseStateConstPtr eclipseState; std::unique_ptr deck; std::unique_ptr grid; std::unique_ptr props; @@ -89,7 +94,10 @@ try // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { + ParserPtr parser(new Opm::Parser()); std::string deck_filename = param.get("deck_filename"); + + eclipseState.reset( new EclipseState(parser->parseFile(deck_filename))); deck.reset(new EclipseGridParser(deck_filename)); // Grid init grid.reset(new GridManager(*deck)); @@ -242,7 +250,7 @@ try << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state - WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + WellsManager wells(eclipseState , epoch , *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. diff --git a/examples/sim_2p_incomp.cpp b/examples/sim_2p_incomp.cpp index 68791092..e28f90f4 100644 --- a/examples/sim_2p_incomp.cpp +++ b/examples/sim_2p_incomp.cpp @@ -45,6 +45,10 @@ #include #include +#include +#include + + #include #include @@ -94,6 +98,8 @@ try // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); + EclipseStateConstPtr eclipseState; + std::unique_ptr deck; std::unique_ptr grid; std::unique_ptr props; @@ -103,8 +109,10 @@ try // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { + ParserPtr parser(new Opm::Parser()); std::string deck_filename = param.get("deck_filename"); deck.reset(new EclipseGridParser(deck_filename)); + eclipseState.reset( new EclipseState(parser->parseFile(deck_filename))); // Grid init grid.reset(new GridManager(*deck)); // Rock and fluid init @@ -260,7 +268,7 @@ try << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, well_state - WellsManager wells(*deck, *grid->c_grid(), props->permeability()); + WellsManager wells(eclipseState , epoch , *grid->c_grid(), props->permeability()); // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every epoch, // since number of wells may change etc. diff --git a/examples/wells_example.cpp b/examples/wells_example.cpp index 6af03a78..d0b91531 100644 --- a/examples/wells_example.cpp +++ b/examples/wells_example.cpp @@ -19,6 +19,9 @@ #include #include +#include +#include + int main(int argc, char** argv) try { @@ -39,9 +42,11 @@ try // Define rock and fluid properties IncompPropertiesFromDeck incomp_properties(parser, *grid.c_grid()); RockCompressibility rock_comp(parser); + ParserPtr newParser(new Opm::Parser()); + EclipseStateConstPtr eclipseState(new Opm::EclipseState(newParser->parseFile(file_name))); // Finally handle the wells - WellsManager wells(parser, *grid.c_grid(), incomp_properties.permeability()); + WellsManager wells(eclipseState , 0 , *grid.c_grid(), incomp_properties.permeability()); double gravity[3] = {0.0, 0.0, parameters.getDefault("gravity", 0.0)}; Opm::LinearSolverFactory linsolver(parameters); diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 3421d3c2..02e76209 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -349,585 +349,6 @@ namespace Opm - /// Construct wells from deck. - WellsManager::WellsManager(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]; - // We need to check if the well is shut or not - if (wci_line.open_shut_flag_ == "SHUT") { - well_controls_shut_well( w_->ctrls[wix]); - } else if (cpos == -1 && mode != InjectionControl::GRUP) { - OPM_THROW(std::runtime_error, "Control for " << wci_line.control_mode_ << " not specified in well " << well_names[wix]); - } - 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") { - well_controls_shut_well( w_->ctrls[wix] ); - } - 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") { - well_controls_shut_well( w_->ctrls[index] ); - } else if (line.openshutflag_ == "OPEN") { - well_controls_open_well( w_->ctrls[index] ); - } 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(); - } diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index c3bbe633..761a3231 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -72,11 +72,6 @@ namespace Opm /// 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 UnstructuredGrid& grid, diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 8cb097e2..2141e4bf 100644 --- a/tests/test_wellsmanager.cpp +++ b/tests/test_wellsmanager.cpp @@ -173,30 +173,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); - - Deck.setCurrentEpoch(0); - { - Opm::WellsManager wellsManager(Deck, *gridManager.c_grid(), NULL); - const Wells* wells = wellsManager.c_wells(); - wells_static_check( wells ); - check_controls_epoch0( wells->ctrls ); - } - - - Deck.setCurrentEpoch(1); - { - Opm::WellsManager wellsManager(Deck, *gridManager.c_grid(), NULL); - const Wells* wells = wellsManager.c_wells(); - - wells_static_check( wells ); - check_controls_epoch1( wells->ctrls ); - } -} - BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::ParserPtr parser(new Opm::Parser()); @@ -205,70 +181,16 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works) { Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); - Deck.setCurrentEpoch(0); { 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; wells_static_check( wellsManager.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() , false)); } - Deck.setCurrentEpoch(1); { 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; wells_static_check( wellsManager.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(),false)); - } -} - - -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, *gridManager.c_grid(), NULL); - Opm::WellsManager oldWellsManager(Deck, *gridManager.c_grid(), NULL); - - BOOST_CHECK(wells_equal(wellsManager.c_wells(), oldWellsManager.c_wells(),false)); - } - - Deck.setCurrentEpoch(1); - { - 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)); - } - - Deck.setCurrentEpoch(2); - { - 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)); } } @@ -277,12 +199,11 @@ BOOST_AUTO_TEST_CASE(New_Constructor_Works_ExpandedData) { BOOST_AUTO_TEST_CASE(WellsEqual) { Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); + Opm::ParserPtr parser(new Opm::Parser()); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile("wells_manager_data.data"))); - Deck.setCurrentEpoch(0); - Opm::WellsManager wellsManager0(Deck, *gridManager.c_grid(), NULL); - - Deck.setCurrentEpoch(1); - Opm::WellsManager wellsManager1(Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager0(eclipseState , 0 , *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager1(eclipseState , 1 , *gridManager.c_grid(), NULL); BOOST_CHECK( wells_equal( wellsManager0.c_wells() , wellsManager0.c_wells(),false) ); BOOST_CHECK( !wells_equal( wellsManager0.c_wells() , wellsManager1.c_wells(),false) ); @@ -293,11 +214,11 @@ BOOST_AUTO_TEST_CASE(ControlsEqual) { Opm::EclipseGridParser Deck("wells_manager_data.data"); Opm::GridManager gridManager(Deck); - Deck.setCurrentEpoch(0); - Opm::WellsManager wellsManager0(Deck, *gridManager.c_grid(), NULL); + Opm::ParserPtr parser(new Opm::Parser()); + Opm::EclipseStateConstPtr eclipseState(new Opm::EclipseState(parser->parseFile("wells_manager_data.data"))); - Deck.setCurrentEpoch(1); - Opm::WellsManager wellsManager1(Deck, *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager0(eclipseState , 0 , *gridManager.c_grid(), NULL); + Opm::WellsManager wellsManager1(eclipseState , 1 , *gridManager.c_grid(), NULL); BOOST_CHECK( well_controls_equal( wellsManager0.c_wells()->ctrls[0] , wellsManager0.c_wells()->ctrls[0] , false)); BOOST_CHECK( well_controls_equal( wellsManager0.c_wells()->ctrls[1] , wellsManager0.c_wells()->ctrls[1] , false));