1#include <opm/parser/eclipse/Units/Units.hpp>
2#include <opm/core/grid/GridHelpers.hpp>
4#include <opm/common/ErrorMacros.hpp>
5#include <opm/common/OpmLog/OpmLog.hpp>
6#include <opm/core/utility/compressedToCartesian.hpp>
8#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
9#include <opm/parser/eclipse/EclipseState/Schedule/CompletionSet.hpp>
10#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
23 namespace ProductionControl
37 Mode mode(Opm::WellProducer::ControlModeEnum controlMode);
41 namespace InjectionControl
53 Mode mode(Opm::WellInjector::ControlModeEnum controlMode);
58 const std::array<double, 3>& cubical,
59 const double* cell_permeability,
60 const double skin_factor,
61 const Opm::WellCompletion::DirectionEnum direction,
64template <
int dim,
class C2F,
class FC>
65std::array<double, dim>
67 FC begin_face_centroids,
70 std::array< std::vector<double>, dim > X;
72 const std::vector<double>::size_type
73 nf = std::distance(c2f[cell].begin(),
76 for (
int d = 0; d < dim; ++d) {
81 typedef typename C2F::row_type::const_iterator FI;
83 for (FI f = c2f[cell].begin(), e = c2f[cell].end(); f != e; ++f) {
84 using Opm::UgGridHelpers::increment;
85 using Opm::UgGridHelpers::getCoordinate;
87 const FC& fc = increment(begin_face_centroids, *f, dim);
89 for (
int d = 0; d < dim; ++d) {
90 X[d].push_back(getCoordinate(fc, d));
94 std::array<double, dim> cube;
95 for (
int d = 0; d < dim; ++d) {
96 typedef std::vector<double>::iterator VI;
97 typedef std::pair<VI,VI> PVI;
99 const PVI m = std::minmax_element(X[d].begin(), X[d].end());
101 cube[d] = *m.second - *m.first;
110template<
class C2F,
class FC,
class NTG>
111void WellsManager::createWellsFromSpecs(std::vector<const Well*>& wells,
size_t timeStep,
113 const int* cart_dims,
114 FC begin_face_centroids,
116 std::vector<double>& dz,
117 std::vector<std::string>& well_names,
118 std::vector<WellData>& well_data,
119 std::map<std::string, int>& well_names_to_index,
120 const PhaseUsage& phaseUsage,
121 const std::map<int,int>& cartesian_to_compressed,
122 const double* permeability,
124 std::vector<int>& wells_on_proc,
125 const std::unordered_set<std::string>& ignored_wells,
126 const DynamicListEconLimited& list_econ_limited)
128 if (dimensions != 3) {
129 OPM_THROW(std::domain_error,
130 "WellsManager::createWellsFromSpecs() only "
131 "supported in three space dimensions");
134 std::vector<std::vector<PerfData> > wellperf_data;
135 wellperf_data.resize(wells.size());
136 wells_on_proc.resize(wells.size(), 1);
142 int active_well_index = 0;
143 for (
auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) {
144 const auto* well = (*wellIter);
146 if (well->getStatus(timeStep) == WellCommon::SHUT) {
150 if ( ignored_wells.find(well->name()) != ignored_wells.end() ) {
151 wells_on_proc[ wellIter - wells.begin() ] = 0;
155 if (list_econ_limited.wellShutEconLimited(well->name())) {
159 std::vector<int> cells_connection_closed;
160 if (list_econ_limited.anyConnectionClosedForWell(well->name())) {
161 cells_connection_closed = list_econ_limited.getClosedConnectionsForWell(well->name());
167 for(
const auto& completion : well->getCompletions(timeStep)) {
168 if (completion.getState() == WellCompletion::OPEN) {
169 int i = completion.getI();
170 int j = completion.getJ();
171 int k = completion.getK();
173 const int* cpgdim = cart_dims;
174 int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k);
175 std::map<int, int>::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx);
176 if (cgit == cartesian_to_compressed.end()) {
177 OPM_MESSAGE(
"****Warning: Cell with i,j,k indices " << i <<
' ' << j <<
' '
178 << k <<
" not found in grid. The completion will be igored (well = "
179 << well->name() <<
')');
183 int cell = cgit->second;
185 if (!cells_connection_closed.empty()) {
186 const bool connection_found = std::find(cells_connection_closed.begin(),
187 cells_connection_closed.end(), cell)
188 != cells_connection_closed.end();
189 if (connection_found) {
197 const Value<double>& transmissibilityFactor = completion.getConnectionTransmissibilityFactorAsValueObject();
198 const double wellPi = completion.getWellPi();
199 if (transmissibilityFactor.hasValue()) {
200 pd.well_index = transmissibilityFactor.getValue();
202 double radius = 0.5*completion.getDiameter();
204 radius = 0.5*unit::feet;
205 OPM_MESSAGE(
"**** Warning: Well bore internal radius set to " << radius);
208 std::array<double, 3> cubical =
209 WellsManagerDetail::getCubeDim<3>(c2f, begin_face_centroids, cell);
213 cubical[2] = dz[cell];
216 const double* cell_perm = &permeability[dimensions*dimensions*cell];
219 completion.getSkinFactor(),
220 completion.getDirection(),
223 pd.satnumid = completion.getSatTableId();
224 pd.well_index *= wellPi;
226 wellperf_data[active_well_index].push_back(pd);
229 if (completion.getState() != WellCompletion::SHUT) {
230 OPM_THROW(std::runtime_error,
"Completion state: " << WellCompletion::StateEnum2String( completion.getState() ) <<
" not handled");
236 well_names_to_index[well->name()] = active_well_index;
237 well_names.push_back(well->name());
240 wd.reference_bhp_depth = well->getRefDepth( timeStep );
241 wd.welspecsline = -1;
242 if (well->isInjector( timeStep ))
247 wd.allowCrossFlow = well->getAllowCrossFlow();
248 well_data.push_back(wd);
256 const int num_wells = well_data.size();
259 assert (dimensions == 3);
260 for (
int w = 0; w < num_wells; ++w) {
261 num_perfs += wellperf_data[w].size();
265 w_ =
create_wells(phaseUsage.num_phases, num_wells, num_perfs);
267 OPM_THROW(std::runtime_error,
"Failed creating Wells struct.");
272 for (
int w = 0; w < num_wells; ++w) {
273 const int w_num_perf = wellperf_data[w].size();
274 std::vector<int> perf_cells (w_num_perf);
275 std::vector<double> perf_prodind(w_num_perf);
276 std::vector<int> perf_satnumid(w_num_perf);
278 for (
int perf = 0; perf < w_num_perf; ++perf) {
279 perf_cells [perf] = wellperf_data[w][perf].cell;
280 perf_prodind[perf] = wellperf_data[w][perf].well_index;
281 perf_satnumid[perf] = wellperf_data[w][perf].satnumid;
284 const double* comp_frac = NULL;
290 well_data[w].reference_bhp_depth,
295 perf_satnumid.data(),
296 well_names[w].c_str(),
297 well_data[w].allowCrossFlow,
301 OPM_THROW(std::runtime_error,
302 "Failed adding well "
304 <<
" to Wells data structure.");
309template <
class C2F,
class FC>
312 const Opm::Schedule& schedule,
313 const size_t timeStep,
315 const int* global_cell,
316 const int* cart_dims,
318 const C2F& cell_to_faces,
319 FC begin_face_centroids,
321 bool is_parallel_run,
322 const std::unordered_set<std::string>& deactivated_wells)
323 : w_(0), is_parallel_run_(is_parallel_run)
325 init(eclipseState, schedule, timeStep, number_of_cells, global_cell,
326 cart_dims, dimensions,
327 cell_to_faces, begin_face_centroids, list_econ_limited, deactivated_wells);
331template <
class C2F,
class FC>
333WellsManager::init(
const Opm::EclipseState& eclipseState,
334 const Opm::Schedule& schedule,
335 const size_t timeStep,
337 const int* global_cell,
338 const int* cart_dims,
340 const C2F& cell_to_faces,
341 FC begin_face_centroids,
343 const std::unordered_set<std::string>& deactivated_wells)
345 if (dimensions != 3) {
346 OPM_THROW(std::runtime_error,
347 "We cannot initialize wells from a deck unless "
348 "the corresponding grid is 3-dimensional.");
351 if (schedule.numWells() == 0) {
352 OPM_MESSAGE(
"No wells specified in Schedule section, "
353 "initializing no wells");
357 std::map<int,int> cartesian_to_compressed;
358 setupCompressedToCartesian(global_cell, number_of_cells,
359 cartesian_to_compressed);
366 std::vector<std::string> well_names;
367 std::vector<WellData> well_data;
371 std::map<std::string, int> well_names_to_index;
373 auto wells = schedule.getWells(timeStep);
374 std::vector<int> wells_on_proc;
376 well_names.reserve(wells.size());
377 well_data.reserve(wells.size());
379 typedef GridPropertyAccess::ArrayPolicy::ExtractFromDeck<double> DoubleArray;
380 typedef GridPropertyAccess::Compressed<DoubleArray, GridPropertyAccess::Tag::NTG> NTGArray;
382 DoubleArray ntg_glob(eclipseState,
"NTG", 1.0);
383 NTGArray ntg(ntg_glob, global_cell);
385 const auto& eclGrid = eclipseState.getInputGrid();
389 std::vector<double> dz(number_of_cells);
391 std::vector<int> gc = compressedToCartesian(number_of_cells, global_cell);
392 for (
int cell = 0; cell < number_of_cells; ++cell) {
393 dz[cell] = eclGrid.getCellThicknes(gc[cell]);
398 std::vector<double> interleavedPerm;
406 createWellsFromSpecs(wells, timeStep, cell_to_faces,
408 begin_face_centroids,
411 well_names, well_data, well_names_to_index,
412 pu, cartesian_to_compressed, interleavedPerm.data(), ntg,
413 wells_on_proc, deactivated_wells, list_econ_limited);
415 setupWellControls(wells, timeStep, well_names, pu, wells_on_proc, list_econ_limited);
418 const auto& fieldGroup = schedule.getGroup(
"FIELD" );
419 well_collection_.
addField(fieldGroup, timeStep, pu);
421 const auto& grouptree = schedule.getGroupTree( timeStep );
422 std::vector< std::string > group_stack = {
"FIELD" };
425 auto parent = group_stack.back();
426 group_stack.pop_back();
427 const auto& children = grouptree.children( parent );
428 group_stack.insert( group_stack.end(), children.begin(), children.end() );
430 for(
const auto& child : children ) {
431 well_collection_.
addGroup( schedule.getGroup( child ), parent, timeStep, pu );
434 }
while( !group_stack.empty() );
437 for (
size_t i = 0; i < wells_on_proc.size(); ++i) {
439 if (wells_on_proc[i]) {
440 well_collection_.
addWell(wells[i], timeStep, pu);
448 setupGuideRates(wells, timeStep, well_data, well_names_to_index);
to handle the wells and connections violating economic limits.
Definition: DynamicListEconLimited.hpp:33
static void extractInterleavedPermeability(const Opm::EclipseState &eclState, const int number_of_cells, const int *global_cell, const int *cart_dims, const double perm_threshold, std::vector< double > &permeability)
void addGroup(const Group &groupChild, std::string parent_name, size_t timeStep, const PhaseUsage &phaseUsage)
void setWellsPointer(Wells *wells)
Adds the well pointer to each leaf node (does not take ownership).
void addField(const Group &fieldGroup, size_t timeStep, const PhaseUsage &phaseUsage)
void addWell(const Well *wellChild, size_t timeStep, const PhaseUsage &phaseUsage)
bool groupControlActive() const
Whether we have active group control.
WellsManager()
Default constructor – no wells.
@ INJECTOR
Definition: legacy_well.h:37
@ PRODUCER
Definition: legacy_well.h:37
Definition: AnisotropicEikonal.hpp:44
PhaseUsage phaseUsageFromDeck(const Opm::EclipseState &eclipseState)
Definition: phaseUsageFromDeck.hpp:37
Mode mode(const std::string &control)
Mode
Definition: WellsManager_impl.hpp:43
@ GRUP
Definition: WellsManager_impl.hpp:44
@ RESV
Definition: WellsManager_impl.hpp:43
@ BHP
Definition: WellsManager_impl.hpp:43
@ RATE
Definition: WellsManager_impl.hpp:43
@ THP
Definition: WellsManager_impl.hpp:44
Mode mode(const std::string &control)
Mode
Definition: WellsManager_impl.hpp:25
@ CRAT
Definition: WellsManager_impl.hpp:26
@ WRAT
Definition: WellsManager_impl.hpp:25
@ BHP
Definition: WellsManager_impl.hpp:27
@ THP
Definition: WellsManager_impl.hpp:27
@ GRAT
Definition: WellsManager_impl.hpp:25
@ GRUP
Definition: WellsManager_impl.hpp:27
@ RESV
Definition: WellsManager_impl.hpp:26
@ ORAT
Definition: WellsManager_impl.hpp:25
@ LRAT
Definition: WellsManager_impl.hpp:26
Definition: WellsManager_impl.hpp:20
double computeWellIndex(const double radius, const std::array< double, 3 > &cubical, const double *cell_permeability, const double skin_factor, const Opm::WellCompletion::DirectionEnum direction, const double ntg)
std::array< double, dim > getCubeDim(const C2F &c2f, FC begin_face_centroids, int cell)
Definition: WellsManager_impl.hpp:66
int add_well(enum WellType type, double depth_ref, int nperf, const double *comp_frac, const int *cells, const double *WI, const int *sat_table_id, const char *name, int allow_cf, struct Wells *W)
struct Wells * create_wells(int nphases, int nwells, int nperf)