WellsManager_impl.hpp
Go to the documentation of this file.
1#include <opm/parser/eclipse/Units/Units.hpp>
2#include <opm/core/grid/GridHelpers.hpp>
3
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>
11
12#include <algorithm>
13#include <array>
14#include <cstddef>
15#include <exception>
16#include <iterator>
17#include <numeric>
18
20{
21
22
23 namespace ProductionControl
24 {
25 enum Mode { ORAT, WRAT, GRAT,
27 BHP , THP , GRUP };
28 /*
29 namespace Details {
30 std::map<std::string, Mode>
31 init_mode_map();
32 } // namespace Details
33 */
34 Mode mode(const std::string& control);
35
36
37 Mode mode(Opm::WellProducer::ControlModeEnum controlMode);
38 } // namespace ProductionControl
39
40
41 namespace InjectionControl
42 {
43 enum Mode { RATE, RESV, BHP,
45 /*
46 namespace Details {
47 std::map<std::string, Mode>
48 init_mode_map();
49 } // namespace Details
50 */
51 Mode mode(const std::string& control);
52
53 Mode mode(Opm::WellInjector::ControlModeEnum controlMode);
54
55 } // namespace InjectionControl
56
57double computeWellIndex(const double radius,
58 const std::array<double, 3>& cubical,
59 const double* cell_permeability,
60 const double skin_factor,
61 const Opm::WellCompletion::DirectionEnum direction,
62 const double ntg);
63
64template <int dim, class C2F, class FC>
65std::array<double, dim>
66getCubeDim(const C2F& c2f,
67 FC begin_face_centroids,
68 int cell)
69{
70 std::array< std::vector<double>, dim > X;
71 {
72 const std::vector<double>::size_type
73 nf = std::distance(c2f[cell].begin(),
74 c2f[cell].end ());
75
76 for (int d = 0; d < dim; ++d) {
77 X[d].reserve(nf);
78 }
79 }
80
81 typedef typename C2F::row_type::const_iterator FI;
82
83 for (FI f = c2f[cell].begin(), e = c2f[cell].end(); f != e; ++f) {
84 using Opm::UgGridHelpers::increment;
85 using Opm::UgGridHelpers::getCoordinate;
86
87 const FC& fc = increment(begin_face_centroids, *f, dim);
88
89 for (int d = 0; d < dim; ++d) {
90 X[d].push_back(getCoordinate(fc, d));
91 }
92 }
93
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;
98
99 const PVI m = std::minmax_element(X[d].begin(), X[d].end());
100
101 cube[d] = *m.second - *m.first;
102 }
103
104 return cube;
105}
106} // end namespace WellsManagerDetail
107
108namespace Opm
109{
110template<class C2F, class FC, class NTG>
111void WellsManager::createWellsFromSpecs(std::vector<const Well*>& wells, size_t timeStep,
112 const C2F& c2f,
113 const int* cart_dims,
114 FC begin_face_centroids,
115 int dimensions,
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,
123 const NTG& ntg,
124 std::vector<int>& wells_on_proc,
125 const std::unordered_set<std::string>& ignored_wells,
126 const DynamicListEconLimited& list_econ_limited)
127{
128 if (dimensions != 3) {
129 OPM_THROW(std::domain_error,
130 "WellsManager::createWellsFromSpecs() only "
131 "supported in three space dimensions");
132 }
133
134 std::vector<std::vector<PerfData> > wellperf_data;
135 wellperf_data.resize(wells.size());
136 wells_on_proc.resize(wells.size(), 1);
137
138 // The well index on the current process.
139 // Note that some wells are deactivated as they live on the interior
140 // domain of another proccess. Therefore this might different from
141 // the index of the well according to the eclipse state
142 int active_well_index = 0;
143 for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) {
144 const auto* well = (*wellIter);
145
146 if (well->getStatus(timeStep) == WellCommon::SHUT) {
147 continue;
148 }
149
150 if ( ignored_wells.find(well->name()) != ignored_wells.end() ) {
151 wells_on_proc[ wellIter - wells.begin() ] = 0;
152 continue;
153 }
154
155 if (list_econ_limited.wellShutEconLimited(well->name())) {
156 continue;
157 }
158
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());
162 }
163
164 { // COMPDAT handling
165 // shut completions and open ones stored in this process will have 1 others 0.
166
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();
172
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() << ')');
180 }
181 else
182 {
183 int cell = cgit->second;
184 // check if the connection is closed due to economic limits
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) {
190 continue;
191 }
192 }
193
194 PerfData pd;
195 pd.cell = cell;
196 {
197 const Value<double>& transmissibilityFactor = completion.getConnectionTransmissibilityFactorAsValueObject();
198 const double wellPi = completion.getWellPi();
199 if (transmissibilityFactor.hasValue()) {
200 pd.well_index = transmissibilityFactor.getValue();
201 } else {
202 double radius = 0.5*completion.getDiameter();
203 if (radius <= 0.0) {
204 radius = 0.5*unit::feet;
205 OPM_MESSAGE("**** Warning: Well bore internal radius set to " << radius);
206 }
207
208 std::array<double, 3> cubical =
209 WellsManagerDetail::getCubeDim<3>(c2f, begin_face_centroids, cell);
210
211 // overwrite dz values calculated in getCubeDim.
212 if (dz.size() > 0) {
213 cubical[2] = dz[cell];
214 }
215
216 const double* cell_perm = &permeability[dimensions*dimensions*cell];
217 pd.well_index =
218 WellsManagerDetail::computeWellIndex(radius, cubical, cell_perm,
219 completion.getSkinFactor(),
220 completion.getDirection(),
221 ntg[cell]);
222 }
223 pd.satnumid = completion.getSatTableId();
224 pd.well_index *= wellPi;
225 }
226 wellperf_data[active_well_index].push_back(pd);
227 }
228 } else {
229 if (completion.getState() != WellCompletion::SHUT) {
230 OPM_THROW(std::runtime_error, "Completion state: " << WellCompletion::StateEnum2String( completion.getState() ) << " not handled");
231 }
232 }
233 }
234 }
235 { // WELSPECS handling
236 well_names_to_index[well->name()] = active_well_index;
237 well_names.push_back(well->name());
238 {
239 WellData wd;
240 wd.reference_bhp_depth = well->getRefDepth( timeStep );
241 wd.welspecsline = -1;
242 if (well->isInjector( timeStep ))
243 wd.type = INJECTOR;
244 else
245 wd.type = PRODUCER;
246
247 wd.allowCrossFlow = well->getAllowCrossFlow();
248 well_data.push_back(wd);
249 }
250 }
251
252 active_well_index++;
253 }
254 // Set up reference depths that were defaulted. Count perfs.
255
256 const int num_wells = well_data.size();
257
258 int num_perfs = 0;
259 assert (dimensions == 3);
260 for (int w = 0; w < num_wells; ++w) {
261 num_perfs += wellperf_data[w].size();
262 }
263
264 // Create the well data structures.
265 w_ = create_wells(phaseUsage.num_phases, num_wells, num_perfs);
266 if (!w_) {
267 OPM_THROW(std::runtime_error, "Failed creating Wells struct.");
268 }
269
270
271 // Add wells.
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);
277
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;
282 }
283
284 const double* comp_frac = NULL;
285
286 // We initialize all wells with a null component fraction,
287 // and must (for injection wells) overwrite it later.
288 const int ok =
289 add_well(well_data[w].type,
290 well_data[w].reference_bhp_depth,
291 w_num_perf,
292 comp_frac,
293 perf_cells.data(),
294 perf_prodind.data(),
295 perf_satnumid.data(),
296 well_names[w].c_str(),
297 well_data[w].allowCrossFlow,
298 w_);
299
300 if (!ok) {
301 OPM_THROW(std::runtime_error,
302 "Failed adding well "
303 << well_names[w]
304 << " to Wells data structure.");
305 }
306 }
307}
308
309template <class C2F, class FC>
311WellsManager(const Opm::EclipseState& eclipseState,
312 const Opm::Schedule& schedule,
313 const size_t timeStep,
314 int number_of_cells,
315 const int* global_cell,
316 const int* cart_dims,
317 int dimensions,
318 const C2F& cell_to_faces,
319 FC begin_face_centroids,
320 const DynamicListEconLimited& list_econ_limited,
321 bool is_parallel_run,
322 const std::unordered_set<std::string>& deactivated_wells)
323 : w_(0), is_parallel_run_(is_parallel_run)
324{
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);
328}
329
331template <class C2F, class FC>
332void
333WellsManager::init(const Opm::EclipseState& eclipseState,
334 const Opm::Schedule& schedule,
335 const size_t timeStep,
336 int number_of_cells,
337 const int* global_cell,
338 const int* cart_dims,
339 int dimensions,
340 const C2F& cell_to_faces,
341 FC begin_face_centroids,
342 const DynamicListEconLimited& list_econ_limited,
343 const std::unordered_set<std::string>& deactivated_wells)
344{
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.");
349 }
350
351 if (schedule.numWells() == 0) {
352 OPM_MESSAGE("No wells specified in Schedule section, "
353 "initializing no wells");
354 return;
355 }
356
357 std::map<int,int> cartesian_to_compressed;
358 setupCompressedToCartesian(global_cell, number_of_cells,
359 cartesian_to_compressed);
360
361 // Obtain phase usage data.
362 PhaseUsage pu = phaseUsageFromDeck(eclipseState);
363
364 // These data structures will be filled in this constructor,
365 // then used to initialize the Wells struct.
366 std::vector<std::string> well_names;
367 std::vector<WellData> well_data;
368
369
370 // For easy lookup:
371 std::map<std::string, int> well_names_to_index;
372
373 auto wells = schedule.getWells(timeStep);
374 std::vector<int> wells_on_proc;
375
376 well_names.reserve(wells.size());
377 well_data.reserve(wells.size());
378
379 typedef GridPropertyAccess::ArrayPolicy::ExtractFromDeck<double> DoubleArray;
380 typedef GridPropertyAccess::Compressed<DoubleArray, GridPropertyAccess::Tag::NTG> NTGArray;
381
382 DoubleArray ntg_glob(eclipseState, "NTG", 1.0);
383 NTGArray ntg(ntg_glob, global_cell);
384
385 const auto& eclGrid = eclipseState.getInputGrid();
386
387 // use cell thickness (dz) from eclGrid
388 // dz overwrites values calculated by WellDetails::getCubeDim
389 std::vector<double> dz(number_of_cells);
390 {
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]);
394 }
395 }
396
397
398 std::vector<double> interleavedPerm;
400 number_of_cells,
401 global_cell,
402 cart_dims,
403 0.0,
404 interleavedPerm);
405
406 createWellsFromSpecs(wells, timeStep, cell_to_faces,
407 cart_dims,
408 begin_face_centroids,
409 dimensions,
410 dz,
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);
414
415 setupWellControls(wells, timeStep, well_names, pu, wells_on_proc, list_econ_limited);
416
417 {
418 const auto& fieldGroup = schedule.getGroup( "FIELD" );
419 well_collection_.addField(fieldGroup, timeStep, pu);
420
421 const auto& grouptree = schedule.getGroupTree( timeStep );
422 std::vector< std::string > group_stack = { "FIELD" };
423
424 do {
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() );
429
430 for( const auto& child : children ) {
431 well_collection_.addGroup( schedule.getGroup( child ), parent, timeStep, pu );
432 }
433
434 } while( !group_stack.empty() );
435 }
436
437 for (size_t i = 0; i < wells_on_proc.size(); ++i) {
438 // wells_on_proc is a vector of flag to indicate whether a well is on the process
439 if (wells_on_proc[i]) {
440 well_collection_.addWell(wells[i], timeStep, pu);
441 }
442 }
443
444 well_collection_.setWellsPointer(w_);
445
446 if (well_collection_.groupControlActive()) {
447 // here does not consider the well potentials related guide rate setting
448 setupGuideRates(wells, timeStep, well_data, well_names_to_index);
449 }
450
451 // Debug output.
452#define EXTRA_OUTPUT
453#ifdef EXTRA_OUTPUT
454 /*
455 std::cout << "\t WELL DATA" << std::endl;
456 for(int i = 0; i< num_wells; ++i) {
457 std::cout << i << ": " << well_data[i].type << " "
458 << well_data[i].control << " " << well_data[i].target
459 << std::endl;
460 }
461
462 std::cout << "\n\t PERF DATA" << std::endl;
463 for(int i=0; i< int(wellperf_data.size()); ++i) {
464 for(int j=0; j< int(wellperf_data[i].size()); ++j) {
465 std::cout << i << ": " << wellperf_data[i][j].cell << " "
466 << wellperf_data[i][j].well_index << std::endl;
467 }
468 }
469 */
470#endif
471}
472
473} // end namespace Opm
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)