WellsManager_impl.hpp
Go to the documentation of this file.
3 
4 #include <opm/common/ErrorMacros.hpp>
6 
7 #include <algorithm>
8 #include <array>
9 #include <cstddef>
10 #include <exception>
11 #include <iterator>
12 #include <numeric>
13 
15 {
16 
17 
18  namespace ProductionControl
19  {
20  enum Mode { ORAT, WRAT, GRAT,
22  BHP , THP , GRUP };
23  /*
24  namespace Details {
25  std::map<std::string, Mode>
26  init_mode_map();
27  } // namespace Details
28  */
29  Mode mode(const std::string& control);
30 
31 
32  Mode mode(Opm::WellProducer::ControlModeEnum controlMode);
33  } // namespace ProductionControl
34 
35 
36  namespace InjectionControl
37  {
38  enum Mode { RATE, RESV, BHP,
39  THP, GRUP };
40  /*
41  namespace Details {
42  std::map<std::string, Mode>
43  init_mode_map();
44  } // namespace Details
45  */
46  Mode mode(const std::string& control);
47 
48  Mode mode(Opm::WellInjector::ControlModeEnum controlMode);
49 
50  } // namespace InjectionControl
51 
52 double computeWellIndex(const double radius,
53  const std::array<double, 3>& cubical,
54  const double* cell_permeability,
55  const double skin_factor,
56  const Opm::WellCompletion::DirectionEnum direction,
57  const double ntg);
58 
59 template <int dim, class C2F, class FC>
60 std::array<double, dim>
61 getCubeDim(const C2F& c2f,
62  FC begin_face_centroids,
63  int cell)
64 {
65  std::array< std::vector<double>, dim > X;
66  {
67  const std::vector<double>::size_type
68  nf = std::distance(c2f[cell].begin(),
69  c2f[cell].end ());
70 
71  for (int d = 0; d < dim; ++d) {
72  X[d].reserve(nf);
73  }
74  }
75 
76  typedef typename C2F::row_type::const_iterator FI;
77 
78  for (FI f = c2f[cell].begin(), e = c2f[cell].end(); f != e; ++f) {
81 
82  const FC& fc = increment(begin_face_centroids, *f, dim);
83 
84  for (int d = 0; d < dim; ++d) {
85  X[d].push_back(getCoordinate(fc, d));
86  }
87  }
88 
89  std::array<double, dim> cube;
90  for (int d = 0; d < dim; ++d) {
91  typedef std::vector<double>::iterator VI;
92  typedef std::pair<VI,VI> PVI;
93 
94  const PVI m = std::minmax_element(X[d].begin(), X[d].end());
95 
96  cube[d] = *m.second - *m.first;
97  }
98 
99  return cube;
100 }
101 } // end namespace WellsManagerDetail
102 
103 namespace Opm
104 {
105 template<class C2F, class FC, class NTG>
106 void WellsManager::createWellsFromSpecs(std::vector<WellConstPtr>& wells, size_t timeStep,
107  const C2F& c2f,
108  const int* cart_dims,
109  FC begin_face_centroids,
110  int dimensions,
111  std::vector<double>& dz,
112  std::vector<std::string>& well_names,
113  std::vector<WellData>& well_data,
114  std::map<std::string, int>& well_names_to_index,
115  const PhaseUsage& phaseUsage,
116  const std::map<int,int>& cartesian_to_compressed,
117  const double* permeability,
118  const NTG& ntg,
119  std::vector<int>& wells_on_proc)
120 {
121  if (dimensions != 3) {
122  OPM_THROW(std::domain_error,
123  "WellsManager::createWellsFromSpecs() only "
124  "supported in three space dimensions");
125  }
126 
127  std::vector<std::vector<PerfData> > wellperf_data;
128  wellperf_data.resize(wells.size());
129  wells_on_proc.resize(wells.size(), 1);
130 
131  // The well index on the current process.
132  // Note that some wells are deactivated as they live on the interior
133  // domain of another proccess. Therefore this might different from
134  // the index of the well according to the eclipse state
135  int well_index_on_proc = 0;
136  for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) {
137  WellConstPtr well = (*wellIter);
138 
139  if (well->getStatus(timeStep) == WellCommon::SHUT) {
140  continue;
141  }
142 
143  { // COMPDAT handling
144  CompletionSetConstPtr completionSet = well->getCompletions(timeStep);
145  // shut completions and open ones stored in this process will have 1 others 0.
146  std::vector<std::size_t> completion_on_proc(completionSet->size(), 1);
147  std::size_t shut_completions_number = 0;
148  for (size_t c=0; c<completionSet->size(); c++) {
149  CompletionConstPtr completion = completionSet->get(c);
150  if (completion->getState() == WellCompletion::OPEN) {
151  int i = completion->getI();
152  int j = completion->getJ();
153  int k = completion->getK();
154 
155  const int* cpgdim = cart_dims;
156  int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k);
157  std::map<int, int>::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx);
158  if (cgit == cartesian_to_compressed.end()) {
159  if ( is_parallel_run_ )
160  {
161  completion_on_proc[c]=0;
162  continue;
163  }
164  else
165  {
166  OPM_MESSAGE("****Warning: Cell with i,j,k indices " << i << ' ' << j << ' '
167  << k << " not found in grid. The completion will be igored (well = "
168  << well->name() << ')');
169  }
170  }
171  else
172  {
173  int cell = cgit->second;
174  PerfData pd;
175  pd.cell = cell;
176  {
177  const Value<double>& transmissibilityFactor = completion->getConnectionTransmissibilityFactorAsValueObject();
178  const double wellPi = completion ->getWellPi();
179  if (transmissibilityFactor.hasValue()) {
180  pd.well_index = transmissibilityFactor.getValue();
181  } else {
182  double radius = 0.5*completion->getDiameter();
183  if (radius <= 0.0) {
184  radius = 0.5*unit::feet;
185  OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius);
186  }
187 
188  std::array<double, 3> cubical =
189  WellsManagerDetail::getCubeDim<3>(c2f, begin_face_centroids, cell);
190 
191  // overwrite dz values calculated in getCubeDim.
192  if (dz.size() > 0) {
193  cubical[2] = dz[cell];
194  }
195 
196  const double* cell_perm = &permeability[dimensions*dimensions*cell];
197  pd.well_index =
198  WellsManagerDetail::computeWellIndex(radius, cubical, cell_perm,
199  completion->getSkinFactor(),
200  completion->getDirection(),
201  ntg[cell]);
202  }
203  pd.well_index *= wellPi;
204  }
205  wellperf_data[well_index_on_proc].push_back(pd);
206  }
207  } else {
208  ++shut_completions_number;
209  if (completion->getState() != WellCompletion::SHUT) {
210  OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion->getState() ) << " not handled");
211  }
212  }
213  }
214  if ( is_parallel_run_ )
215  {
216  // sum_completions_on_proc includes completions
217  // that are shut
218  std::size_t sum_completions_on_proc = std::accumulate(completion_on_proc.begin(),
219  completion_on_proc.end(),0);
220  // Set wells that are not on this processor to SHUT.
221  // A well is not here if only shut completions are found.
222  if ( sum_completions_on_proc == shut_completions_number )
223  {
224  // Mark well as not existent on this process
225  wells_on_proc[wellIter-wells.begin()] = 0;
226  continue;
227  }
228  else
229  {
230  // Check that the complete well is on this process
231  if ( sum_completions_on_proc < completionSet->size() )
232  {
233  std::cout<< "Well "<< well->name() << " does not seem to be"
234  << "completely in the disjoint partition of "
235  << "process. Therefore we deactivate it here." << std::endl;
236  // Mark well as not existent on this process
237  wells_on_proc[wellIter-wells.begin()] = 0;
238  wellperf_data[well_index_on_proc].clear();
239  continue;
240  }
241  }
242  }
243  }
244  { // WELSPECS handling
245  well_names_to_index[well->name()] = well_index_on_proc;
246  well_names.push_back(well->name());
247  {
248  WellData wd;
249  wd.reference_bhp_depth = well->getRefDepth();
250  wd.welspecsline = -1;
251  if (well->isInjector( timeStep ))
252  wd.type = INJECTOR;
253  else
254  wd.type = PRODUCER;
255 
256  wd.allowCrossFlow = well->getAllowCrossFlow();
257  well_data.push_back(wd);
258  }
259  }
260 
261  well_index_on_proc++;
262  }
263  // Set up reference depths that were defaulted. Count perfs.
264 
265  const int num_wells = well_data.size();
266 
267  int num_perfs = 0;
268  assert (dimensions == 3);
269  for (int w = 0; w < num_wells; ++w) {
270  num_perfs += wellperf_data[w].size();
271  }
272 
273  // Create the well data structures.
274  w_ = create_wells(phaseUsage.num_phases, num_wells, num_perfs);
275  if (!w_) {
276  OPM_THROW(std::runtime_error, "Failed creating Wells struct.");
277  }
278 
279 
280  // Add wells.
281  for (int w = 0; w < num_wells; ++w) {
282  const int w_num_perf = wellperf_data[w].size();
283  std::vector<int> perf_cells (w_num_perf);
284  std::vector<double> perf_prodind(w_num_perf);
285 
286  for (int perf = 0; perf < w_num_perf; ++perf) {
287  perf_cells [perf] = wellperf_data[w][perf].cell;
288  perf_prodind[perf] = wellperf_data[w][perf].well_index;
289  }
290 
291  const double* comp_frac = NULL;
292 
293  // We initialize all wells with a null component fraction,
294  // and must (for injection wells) overwrite it later.
295  const int ok =
296  add_well(well_data[w].type,
297  well_data[w].reference_bhp_depth,
298  w_num_perf,
299  comp_frac,
300  perf_cells.data(),
301  perf_prodind.data(),
302  well_names[w].c_str(),
303  well_data[w].allowCrossFlow,
304  w_);
305 
306  if (!ok) {
307  OPM_THROW(std::runtime_error,
308  "Failed adding well "
309  << well_names[w]
310  << " to Wells data structure.");
311  }
312  }
313 }
314 
315 template <class C2F, class FC>
317 WellsManager(const Opm::EclipseStateConstPtr eclipseState,
318  const size_t timeStep,
319  int number_of_cells,
320  const int* global_cell,
321  const int* cart_dims,
322  int dimensions,
323  const C2F& cell_to_faces,
324  FC begin_face_centroids,
325  const double* permeability,
326  bool is_parallel_run)
327  : w_(0), is_parallel_run_(is_parallel_run)
328 {
329  init(eclipseState, timeStep, number_of_cells, global_cell,
330  cart_dims, dimensions,
331  cell_to_faces, begin_face_centroids, permeability);
332 }
333 
335 template <class C2F, class FC>
336 void
337 WellsManager::init(const Opm::EclipseStateConstPtr eclipseState,
338  const size_t timeStep,
339  int number_of_cells,
340  const int* global_cell,
341  const int* cart_dims,
342  int dimensions,
343  const C2F& cell_to_faces,
344  FC begin_face_centroids,
345  const double* permeability)
346 {
347  if (dimensions != 3) {
348  OPM_THROW(std::runtime_error,
349  "We cannot initialize wells from a deck unless "
350  "the corresponding grid is 3-dimensional.");
351  }
352 
353  if (eclipseState->getSchedule()->numWells() == 0) {
354  OPM_MESSAGE("No wells specified in Schedule section, "
355  "initializing no wells");
356  return;
357  }
358 
359  std::map<int,int> cartesian_to_compressed;
360  setupCompressedToCartesian(global_cell, number_of_cells,
361  cartesian_to_compressed);
362 
363  // Obtain phase usage data.
364  PhaseUsage pu = phaseUsageFromDeck(eclipseState);
365 
366  // These data structures will be filled in this constructor,
367  // then used to initialize the Wells struct.
368  std::vector<std::string> well_names;
369  std::vector<WellData> well_data;
370 
371 
372  // For easy lookup:
373  std::map<std::string, int> well_names_to_index;
374 
375  ScheduleConstPtr schedule = eclipseState->getSchedule();
376  std::vector<WellConstPtr> wells = schedule->getWells(timeStep);
377  std::vector<int> wells_on_proc;
378 
379  well_names.reserve(wells.size());
380  well_data.reserve(wells.size());
381 
382  typedef GridPropertyAccess::ArrayPolicy::ExtractFromDeck<double> DoubleArray;
383  typedef GridPropertyAccess::Compressed<DoubleArray, GridPropertyAccess::Tag::NTG> NTGArray;
384 
385  DoubleArray ntg_glob(eclipseState, "NTG", 1.0);
386  NTGArray ntg(ntg_glob, global_cell);
387 
388  EclipseGridConstPtr eclGrid = eclipseState->getEclipseGrid();
389 
390  // use cell thickness (dz) from eclGrid
391  // dz overwrites values calculated by WellDetails::getCubeDim
392  std::vector<double> dz(number_of_cells);
393  {
394  std::vector<int> gc = compressedToCartesian(number_of_cells, global_cell);
395  for (int cell = 0; cell < number_of_cells; ++cell) {
396  dz[cell] = eclGrid->getCellThicknes(gc[cell]);
397  }
398  }
399 
400  createWellsFromSpecs(wells, timeStep, cell_to_faces,
401  cart_dims,
402  begin_face_centroids,
403  dimensions,
404  dz,
405  well_names, well_data, well_names_to_index,
406  pu, cartesian_to_compressed, permeability, ntg,
407  wells_on_proc);
408 
409  setupWellControls(wells, timeStep, well_names, pu, wells_on_proc);
410 
411  {
412  GroupTreeNodeConstPtr fieldNode =
413  schedule->getGroupTree(timeStep)->getNode("FIELD");
414 
415  GroupConstPtr fieldGroup =
416  schedule->getGroup(fieldNode->name());
417 
418  well_collection_.addField(fieldGroup, timeStep, pu);
419  addChildGroups(fieldNode, schedule, timeStep, pu);
420  }
421 
422  for (auto w = wells.begin(), e = wells.end(); w != e; ++w) {
423  well_collection_.addWell(*w, timeStep, pu);
424  }
425 
426  well_collection_.setWellsPointer(w_);
427  well_collection_.applyGroupControls();
428 
429  setupGuideRates(wells, timeStep, well_data, well_names_to_index);
430 
431  // Debug output.
432 #define EXTRA_OUTPUT
433 #ifdef EXTRA_OUTPUT
434  /*
435  std::cout << "\t WELL DATA" << std::endl;
436  for(int i = 0; i< num_wells; ++i) {
437  std::cout << i << ": " << well_data[i].type << " "
438  << well_data[i].control << " " << well_data[i].target
439  << std::endl;
440  }
441 
442  std::cout << "\n\t PERF DATA" << std::endl;
443  for(int i=0; i< int(wellperf_data.size()); ++i) {
444  for(int j=0; j< int(wellperf_data[i].size()); ++j) {
445  std::cout << i << ": " << wellperf_data[i][j].cell << " "
446  << wellperf_data[i][j].well_index << std::endl;
447  }
448  }
449  */
450 #endif
451 }
452 
453 } // end namespace Opm
void applyGroupControls()
Applies all group controls (injection and production)
Definition: WellsManager_impl.hpp:22
Definition: WellsManager_impl.hpp:20
void setWellsPointer(Wells *wells)
Adds the well pointer to each leaf node (does not take ownership).
Definition: WellsManager_impl.hpp:20
double getCoordinate(T *cc, int i)
Get the i-th corrdinate of a centroid.
Definition: GridHelpers.hpp:337
Definition: WellsManager_impl.hpp:38
Definition: AnisotropicEikonal.hpp:43
const double feet
Definition: Units.hpp:88
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:61
Definition: WellsManager_impl.hpp:38
Definition: WellsManager_impl.hpp:21
Definition: WellsManager_impl.hpp:20
Definition: WellsManager_impl.hpp:22
Mode mode(const std::string &control)
int add_well(enum WellType type, double depth_ref, int nperf, const double *comp_frac, const int *cells, const double *WI, const char *name, int allow_cf, struct Wells *W)
Definition: WellsManager_impl.hpp:14
WellsManager()
Default constructor – no wells.
Mode
Definition: WellsManager_impl.hpp:38
Definition: WellsManager_impl.hpp:38
void addField(GroupConstPtr fieldGroup, size_t timeStep, const PhaseUsage &phaseUsage)
int dimensions(const UnstructuredGrid &grid)
Get the dimensions of a grid.
struct Wells * create_wells(int nphases, int nwells, int nperf)
Definition: WellsManager_impl.hpp:39
std::vector< int > compressedToCartesian(const int num_cells, const int *global_cell)
T * increment(T *cc, int i, int dim)
Increment an iterator over an array that reresents a dense row-major matrix with dims columns...
Definition: GridHelpers.hpp:318
Mode
Definition: WellsManager_impl.hpp:20
void addWell(WellConstPtr wellChild, size_t timeStep, const PhaseUsage &phaseUsage)
Definition: WellsManager_impl.hpp:21
Definition: WellsManager_impl.hpp:22
Definition: WellsManager_impl.hpp:21
Definition: WellsManager_impl.hpp:39
Definition: legacy_well.h:37
PhaseUsage phaseUsageFromDeck(Opm::EclipseStateConstPtr eclipseState)
Definition: phaseUsageFromDeck.hpp:35
Definition: legacy_well.h:37
Mode mode(const std::string &control)