From 1829acd2016edffa65ba85abe74d30e0c3120e10 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 14 Nov 2013 12:52:12 +0100 Subject: [PATCH 01/67] Added very simple parser test to check a parser can be instantiated and a simple file parsed --- tests/testBlackoilState1.DATA | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/testBlackoilState1.DATA b/tests/testBlackoilState1.DATA index efcd29d25..c759447a0 100644 --- a/tests/testBlackoilState1.DATA +++ b/tests/testBlackoilState1.DATA @@ -9,3 +9,7 @@ DYV DZV 10*10 / + +ACTNUM + 1 998*2 3 / + From 1f1c0ebcf32d4a04825b5e89dbd4889ba26d8144 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 7 Jan 2014 16:10:45 +0100 Subject: [PATCH 02/67] 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 007c8b52c..b934c4477 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 35f7f471d..840e6f362 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 b3465fac6..347f1d2b5 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 731fe7d3f46089773b993d8b6fb4f434ae9ba014 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 8 Jan 2014 16:10:55 +0100 Subject: [PATCH 03/67] 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 b934c4477..86a7d78dd 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 840e6f362..f14be7946 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 347f1d2b5..96a193cc8 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 2ffcb219bb5cde1d523c33d07ebaad27021fd604 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 10 Jan 2014 13:43:02 +0100 Subject: [PATCH 04/67] 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 01f28ee14..e69d6ee2f 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 86a7d78dd..46a476465 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 3b0a9067b480a9dba1cd9a41d08cfd59efeeb3d4 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 22 Jan 2014 15:22:32 +0100 Subject: [PATCH 05/67] 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 46a476465..81a3f1c66 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 96a193cc8..3bf36eae3 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 7774a11eeebfc9474105f1fb1af41d177e624be1 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 24 Jan 2014 12:00:58 +0100 Subject: [PATCH 06/67] 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 81a3f1c66..a92b924e8 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 4049426ec9ca3e6a8ca2e04d31c9e9c0ae591bb2 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 28 Jan 2014 21:11:51 +0100 Subject: [PATCH 07/67] 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 e69d6ee2f..d022dcc41 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 74c3040c43c67d7cdba1282cb3df1bb63ee520a9 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 27 Jan 2014 13:45:59 +0100 Subject: [PATCH 08/67] Added an extra data file with an extra well --- tests/test_wellsmanager.cpp | 58 ++++++++++++++++----- tests/wells_manager_data_expanded.data | 72 ++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 13 deletions(-) create mode 100755 tests/wells_manager_data_expanded.data diff --git a/tests/test_wellsmanager.cpp b/tests/test_wellsmanager.cpp index 3bf36eae3..25c9678a1 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 000000000..dfcd0b48e --- /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 7ce9cb9055048338d5b29b8919511a65405d6c39 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 28 Jan 2014 21:02:47 +0100 Subject: [PATCH 09/67] 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 81a3f1c66..a03e3cc53 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 f14be7946..145fb2e25 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 dfcd0b48e..3d97003ae 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 0d492ce407ddb28981b980a71a9c26115ef77ed7 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 10:32:17 +0100 Subject: [PATCH 10/67] 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 3d97003ae..d025d6278 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 fbe2f316530a83fad28803c0e993dd43977a85b3 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 11:57:45 +0100 Subject: [PATCH 11/67] 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 a03e3cc53..63d868cf6 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 25c9678a1..dfbb84fed 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 d621fdfe79edf91bc7c14719393317b843e5eebc Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 29 Jan 2014 12:30:03 +0100 Subject: [PATCH 12/67] 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 63d868cf6..58e5b8639 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 0d8b883b8ea8ca1ff1016f291007eb403de1fcad Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 08:00:45 +0100 Subject: [PATCH 13/67] 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 e69d6ee2f..d022dcc41 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 70e2dd9f1239a52a77f5f51427400d7c15a6c639 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 08:02:13 +0100 Subject: [PATCH 14/67] 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 a92b924e8..f30d1cab9 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 35d0808fbe61489b88252efe35e644a57c57db26 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 15:55:17 +0100 Subject: [PATCH 15/67] 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 cdefe544d..14cf42658 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 30df3cab3749631dbca40ba01536bb36ba4ca0bd Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 30 Jan 2014 16:37:20 +0100 Subject: [PATCH 16/67] 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 14cf42658..e74a1a19c 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 e12ec1b25d68c3fe70f27b0ec127169c0fcced44 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 08:58:54 +0100 Subject: [PATCH 17/67] 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 e74a1a19c..ab069c8f6 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 145fb2e25..fdb493713 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 76dff2e3269c7cd4ddd5ab143246c3dcddff27e9 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 09:07:58 +0100 Subject: [PATCH 18/67] 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 ab069c8f6..693f2d486 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 fdb493713..d731f0fda 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 3d215691da68b5c0b7c89e7c288f528ba0a5d173 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 3 Feb 2014 09:22:28 +0100 Subject: [PATCH 19/67] 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 693f2d486..2241cacd6 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 d731f0fda..77bbb9038 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 aa329b8b3e099a005735ac8aa9f83a4534153d4b Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Thu, 6 Feb 2014 16:31:35 +0100 Subject: [PATCH 20/67] Removed WELOPEN from constructor, added throwing on non OPEN/SHUT statuses --- 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 ++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 30 deletions(-) create mode 100755 tests/wells_manager_data_wellSTOP.data diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 2241cacd6..49857d06f 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 fd3c71025..0caf67f13 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 dae5e7cb5..672077681 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 000000000..bdcbc3817 --- /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 a91a6c9484c97d2a5e7936b0c3912176a88282fa Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:01:54 +0100 Subject: [PATCH 21/67] 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 d022dcc41..2fc7f915a 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 f1f801aca513ae5b5a6ddb3ef795a428dc85f378 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 14:32:24 +0100 Subject: [PATCH 22/67] 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 2fc7f915a..6ee66075e 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 7fdda982136e125e271cab571838ec28938907d7 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:04:04 +0100 Subject: [PATCH 23/67] 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 7b0f69cb8..5b990af14 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 03567caed..bd375e1ca 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 730363434..1058fa997 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 e277ee2a5..b5e0fb4e0 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 e177b657f9ed62666f56af02bde9817892fb53c5 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 14:54:13 +0100 Subject: [PATCH 24/67] 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 5b990af14..0f2cefecb 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 5b77bcd4a0c10bd547a7ba49a8061cfc61fcc794 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 15:23:05 +0100 Subject: [PATCH 25/67] 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 0f2cefecb..1144d701e 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 1221ea25c10efe4ee1ec606d7301f4be0c11e557 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Wed, 5 Feb 2014 15:58:08 +0100 Subject: [PATCH 26/67] 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 1144d701e..d1729533c 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 4018bcfc7171a447cdd9832a5676ca092ca250ae Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 7 Feb 2014 16:14:43 +0100 Subject: [PATCH 27/67] make the saturation functions work with opm-parser --- .../props/satfunc/SaturationPropsFromDeck.hpp | 27 ++ .../satfunc/SaturationPropsFromDeck_impl.hpp | 457 ++++++++++++++++++ opm/core/simulator/initState_impl.hpp | 136 +++++- 3 files changed, 616 insertions(+), 4 deletions(-) diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp index c321a42af..2c570ffd3 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 c30069934..d9a3f32e7 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 12dbe7b52..18372573e 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 b6072e51122ad8215fc095a9ef9da615ff3c9ab2 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 11 Feb 2014 14:12:36 +0100 Subject: [PATCH 28/67] Added support for creation of WellsGroup objects from new parser Well and Group objects --- 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 ++++++- 5 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 tests/test_wellsgroup.cpp create mode 100755 tests/wells_group.data diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index 6c1059a29..62663ce85 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 143da9357..ebf9d986d 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 000000000..82c60163d --- /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 000000000..753d6031e --- /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 f22152c13..2df2dfd48 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 7ba64312093869e2920051dade87594a6bb4b190 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Tue, 11 Feb 2014 14:21:15 +0100 Subject: [PATCH 29/67] 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 82c60163d..9ba9e0ef3 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 42f5025c886ac6f4dd24a46b8c2a07a71602f99f Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 24 Jan 2014 12:00:03 +0100 Subject: [PATCH 30/67] 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 e476f015f..aedc85893 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 49072b29e..5c74a76a6 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 24cf0ab99f0f8a665cce46aaa6af33cc2e085dcb Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 6 Feb 2014 11:51:14 +0100 Subject: [PATCH 31/67] 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 aedc85893..2eee1934c 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 1181d1c1bce9e2430cdfa09b4cafeb227a9a1244 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 12 Feb 2014 15:39:57 +0100 Subject: [PATCH 32/67] Create WellsGroupInterface from opm-parser Well/Group objects --- 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 +++++++++++++++++++++++++++++++ 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 tests/test_wellcollection.cpp diff --git a/opm/core/wells/WellCollection.cpp b/opm/core/wells/WellCollection.cpp index 350d2ffb3..0cc7a9ce5 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 9cdb9e7f1..8933ff037 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 62663ce85..1087afcf3 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 ebf9d986d..03a61d5e1 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 000000000..0c12af446 --- /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 f94f63ff56f5479cb2431804e303a875d0e0eb3d Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Wed, 12 Feb 2014 15:45:06 +0100 Subject: [PATCH 33/67] 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 0cc7a9ce5..260c156fd 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 8933ff037..e017b50b3 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 d7568b4adf358d51572a5cf3ba8e3e788771a5c4 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Thu, 13 Feb 2014 16:00:39 +0100 Subject: [PATCH 34/67] 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 260c156fd..d40497c97 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 e017b50b3..a0e36562e 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 0c12af446..d22ba3b4b 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 753d6031e..5d2e58cb2 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 ae8944f6eae8215c0d50bc946c6a3ccd03076b9f Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 08:14:30 +0100 Subject: [PATCH 35/67] 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 1087afcf3..3316ccdbf 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 3c93c7a781894320f39b461a6731d6f2e7f85d07 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 13:43:25 +0100 Subject: [PATCH 36/67] 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 d40497c97..8be2dd09a 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 49857d06f..51f4cd6d8 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 77bbb9038..f54efd943 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 2df2dfd48..732b528be 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 d1494e7daaeb156e5f72ca6906969e77b15eea93 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 13:46:54 +0100 Subject: [PATCH 37/67] 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 8be2dd09a..9218db06e 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 a0e36562e..f084a04d8 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 ab426823999ef90aa9bac4fae6e65e51c044914f Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Fri, 14 Feb 2014 15:36:32 +0100 Subject: [PATCH 38/67] 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 51f4cd6d8..72abe9c18 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 f54efd943..165f5e127 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 0caf67f13..8cb097e2e 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 c93c1252e5236ab67a148cfd633e9037b3614a0f Mon Sep 17 00:00:00 2001 From: osae Date: Tue, 18 Feb 2014 13:49:35 +0100 Subject: [PATCH 39/67] 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 e476f015f..c28178a1c 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 c30069934..4b7ae04d6 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 ad2a9439f..04f6730e3 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 a055b529ecc5da7b93cfcab934273cd41a824bb6 Mon Sep 17 00:00:00 2001 From: Kristian Flikka Date: Mon, 24 Feb 2014 15:24:33 +0100 Subject: [PATCH 40/67] 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 72abe9c18..640911206 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 165f5e127..9e5c314cc 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 95a17f847a0efc7035948a8de4b3be2da1489626 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 41/67] 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 77aaf4ca7..65a820d32 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 ae0464afe3d5a771479fd4ef2d70746b6e6e18b6 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 42/67] Suppress unused argument warnings. --- opm/core/props/satfunc/SaturationPropsFromDeck.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opm/core/props/satfunc/SaturationPropsFromDeck.hpp b/opm/core/props/satfunc/SaturationPropsFromDeck.hpp index 2c570ffd3..757b1481a 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 8be1f0e8d39a9c3349cf6c630e1aa78742afad6e Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 4 Mar 2014 14:28:42 +0100 Subject: [PATCH 43/67] 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 +- 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 +++++++++++++++++----- 8 files changed, 58 insertions(+), 32 deletions(-) diff --git a/opm/core/pressure/tpfa/cfs_tpfa_residual.c b/opm/core/pressure/tpfa/cfs_tpfa_residual.c index 4c2ca10ed..b773408f5 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 5130ba0b3..a9e2ce73c 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/WellState.hpp b/opm/core/simulator/WellState.hpp index 51d2c3b98..39fbdbd3e 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 435c7a794..6998f4829 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 3316ccdbf..ee45b6ed5 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 640911206..7df448cf8 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 5e52d5410..e4b33a356 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 096b9c6a0..8424ab0b5 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 85a323e7da184c05d1c01c05622fdd9babd4e148 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 44/67] 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 65a820d32..77aaf4ca7 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 c0ddb9707ed54bd752dad658c3cb6f284bd1f85b 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 45/67] 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 39fbdbd3e..adf3fb244 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 db6a36d1fdbe9544b8d52d751805fedf7337c266 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 46/67] 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 adf3fb244..313914587 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 a592a23153addb1db664c7e2d129028093d2ed0e 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 47/67] 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 313914587..d46e12544 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 c0fd11710331e7faba9722eed6a341642939f974 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 48/67] 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 d46e12544..c12e68bd4 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 7413894a67652d3809bf40cbde34acd90a33ea14 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 12 Mar 2014 17:41:40 +0100 Subject: [PATCH 49/67] 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 7df448cf8..2deaea992 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 ca7de3c7c19db973f7e5d9348cf3d9d0067716a2 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 12 Mar 2014 18:18:40 +0100 Subject: [PATCH 50/67] 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 2deaea992..c438b29e9 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 d0ca878c2a72717c0b1cf4b0fe0e18cfe61ebb9b Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 13 Mar 2014 12:00:01 +0100 Subject: [PATCH 51/67] 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 8aaab2711..9bba77478 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 d2bbf9a728693e07e65c67809a10311d9fa6fa4d Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 13 Mar 2014 12:13:11 +0100 Subject: [PATCH 52/67] 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 9bba77478..62048ba06 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 0b3217fe6f68c90f0964c26af12ec5ad68b1500f Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Thu, 13 Mar 2014 16:33:32 +0100 Subject: [PATCH 53/67] 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 18372573e..89ff89234 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 a13f7b990f83e9c3545c8f34a3799e14730f4919 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 54/67] 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 c12e68bd4..090676ee5 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 5fc6cbc16fcd7d94c8bdb27dd0fe18ec891f611b 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 55/67] Made single-argument constructors explicit. Avoids unintended implicit conversions. --- opm/core/wells/WellsManager.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 9e5c314cc..c3bbe6338 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 0c1df1052471ab5e9d77644baef5262e17081695 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 18 Mar 2014 15:08:28 +0100 Subject: [PATCH 56/67] 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 c85cef78c..a6e699faf 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 dd99948869f699752f0c6d131f2923f072c4516b Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 11:09:13 +0100 Subject: [PATCH 57/67] 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 62048ba06..06b6ad65a 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 8c8bb67aa042583d0846f05d535be4e19a9eb448 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 11:19:25 +0100 Subject: [PATCH 58/67] 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. --- opm/core/linalg/LinearSolverIstl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opm/core/linalg/LinearSolverIstl.cpp b/opm/core/linalg/LinearSolverIstl.cpp index 77aaf4ca7..f9e2bb7bd 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 ddd662734f17bc7462ee5ce6b530270db6b09c69 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 18 Mar 2014 21:38:43 +0100 Subject: [PATCH 59/67] 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 62048ba06..08b54ba7a 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 d333f413d1005dd66bdd1d326de467892036e4e5 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 17:33:59 +0100 Subject: [PATCH 60/67] 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 f9e2bb7bd..34554891b 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 8f60175d0464ccbe29a652afb8ba606ba2c24276 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 19:49:48 +0100 Subject: [PATCH 61/67] 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 34554891b..c32ad02ee 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 2f3c17bd9d91c5a3fc41181ea9b6f6b4ef8b62bb Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Wed, 19 Mar 2014 21:32:36 +0100 Subject: [PATCH 62/67] 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. --- tests/test_linearsolver.cpp | 182 ++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 tests/test_linearsolver.cpp diff --git a/tests/test_linearsolver.cpp b/tests/test_linearsolver.cpp new file mode 100644 index 000000000..e4a92ddd6 --- /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 8d9cd23722f9f07fafd688ccda71ac662693334d Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 22:18:27 +0100 Subject: [PATCH 63/67] 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 e4a92ddd6..c122de7fc 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 eda0be1b8c33c0e86d24168e749b237c1190b9f0 Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 22:21:03 +0100 Subject: [PATCH 64/67] 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 c122de7fc..f1bb7f976 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 b8959c45dd41134323a1b5d7538bb5b9a88e410e Mon Sep 17 00:00:00 2001 From: Markus Blatt Date: Thu, 20 Mar 2014 21:49:48 +0100 Subject: [PATCH 65/67] 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 f1bb7f976..184b7765d 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 2eab8aeb38f3c2415d9b455597e0795fccbc66eb Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 21 Mar 2014 00:35:15 +0100 Subject: [PATCH 66/67] 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 ee45b6ed5..050e18712 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 c438b29e9..3421d3c2b 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 9ba9e0ef3..46fd8eb0c 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 f0126b2501de4e83c5c8256cf9eb1ae42c4638ba Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Tue, 25 Mar 2014 18:57:58 +0100 Subject: [PATCH 67/67] Removed WellsManager constructor which takes an ole Eclipsegridparser instance. --- opm/core/wells/WellsManager.cpp | 579 -------------------------------- opm/core/wells/WellsManager.hpp | 5 - tests/test_wellsmanager.cpp | 95 +----- 3 files changed, 8 insertions(+), 671 deletions(-) diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 3421d3c2b..02e762094 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 c3bbe6338..761a32315 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 8cb097e2e..2141e4bf8 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));