SimulatorBase_impl.hpp
Go to the documentation of this file.
1 /*
2  Copyright 2013 SINTEF ICT, Applied Mathematics.
3  Copyright 2014 IRIS AS
4  Copyright 2015 Andreas Lauser
5 
6  This file is part of the Open Porous Media project (OPM).
7 
8  OPM is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  OPM is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with OPM. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include <algorithm>
23 
24 namespace Opm
25 {
26 
27  template <class Implementation>
28  SimulatorBase<Implementation>::SimulatorBase(const parameter::ParameterGroup& param,
29  const Grid& grid,
30  const DerivedGeology& geo,
32  const RockCompressibility* rock_comp_props,
34  const double* gravity,
35  const bool has_disgas,
36  const bool has_vapoil,
37  std::shared_ptr<EclipseState> eclipse_state,
38  OutputWriter& output_writer,
39  const std::vector<double>& threshold_pressures_by_face)
40  : param_(param),
41  model_param_(param),
42  solver_param_(param),
43  grid_(grid),
44  props_(props),
45  rock_comp_props_(rock_comp_props),
46  gravity_(gravity),
47  geo_(geo),
48  solver_(linsolver),
49  has_disgas_(has_disgas),
50  has_vapoil_(has_vapoil),
51  terminal_output_(param.getDefault("output_terminal", true)),
52  eclipse_state_(eclipse_state),
53  output_writer_(output_writer),
54  rateConverter_(props_, std::vector<int>(AutoDiffGrid::numCells(grid_), 0)),
55  threshold_pressures_by_face_(threshold_pressures_by_face),
56  is_parallel_run_( false )
57  {
58  // Misc init.
59  const int num_cells = AutoDiffGrid::numCells(grid);
60  allcells_.resize(num_cells);
61  for (int cell = 0; cell < num_cells; ++cell) {
62  allcells_[cell] = cell;
63  }
64 #if HAVE_MPI
65  if ( solver_.parallelInformation().type() == typeid(ParallelISTLInformation) )
66  {
67  const ParallelISTLInformation& info =
68  boost::any_cast<const ParallelISTLInformation&>(solver_.parallelInformation());
69  // Only rank 0 does print to std::cout
70  terminal_output_ = terminal_output_ && ( info.communicator().rank() == 0 );
71  is_parallel_run_ = ( info.communicator().size() > 1 );
72  }
73 #endif
74  }
75 
76  template <class Implementation>
77  SimulatorReport SimulatorBase<Implementation>::run(SimulatorTimer& timer,
78  ReservoirState& state)
79  {
80  WellState prev_well_state;
81 
82  // Create timers and file for writing timing info.
83  Opm::time::StopWatch solver_timer;
84  double stime = 0.0;
85  Opm::time::StopWatch step_timer;
86  Opm::time::StopWatch total_timer;
87  total_timer.start();
88  std::string tstep_filename = output_writer_.outputDirectory() + "/step_timing.txt";
89  std::ofstream tstep_os(tstep_filename.c_str());
90 
91  // adaptive time stepping
92  std::unique_ptr< AdaptiveTimeStepping > adaptiveTimeStepping;
93  if( param_.getDefault("timestep.adaptive", true ) )
94  {
95  adaptiveTimeStepping.reset( new AdaptiveTimeStepping( param_, solver_.parallelInformation(), terminal_output_ ) );
96  }
97 
98  // init output writer
99  output_writer_.writeInit( timer );
100 
101  std::string restorefilename = param_.getDefault("restorefile", std::string("") );
102  if( ! restorefilename.empty() )
103  {
104  // -1 means that we'll take the last report step that was written
105  const int desiredRestoreStep = param_.getDefault("restorestep", int(-1) );
106  output_writer_.restore( timer, state, prev_well_state, restorefilename, desiredRestoreStep );
107  }
108 
109  unsigned int totalNewtonIterations = 0;
110  unsigned int totalLinearIterations = 0;
111 
112  // Main simulation loop.
113  while (!timer.done()) {
114  // Report timestep.
115  step_timer.start();
116  if ( terminal_output_ )
117  {
118  timer.report(std::cout);
119  }
120 
121  // Create wells and well state.
122  WellsManager wells_manager(eclipse_state_,
123  timer.currentStepNum(),
124  Opm::UgGridHelpers::numCells(grid_),
125  Opm::UgGridHelpers::globalCell(grid_),
126  Opm::UgGridHelpers::cartDims(grid_),
127  Opm::UgGridHelpers::dimensions(grid_),
128  Opm::UgGridHelpers::cell2Faces(grid_),
129  Opm::UgGridHelpers::beginFaceCentroids(grid_),
130  props_.permeability(),
131  is_parallel_run_);
132  const Wells* wells = wells_manager.c_wells();
133  WellState well_state;
134  well_state.init(wells, state, prev_well_state);
135 
136  // give the polymer and surfactant simulators the chance to do their stuff
137  asImpl().handleAdditionalWellInflow(timer, wells_manager, well_state, wells);
138 
139  // write simulation state at the report stage
140  output_writer_.writeTimeStep( timer, state, well_state );
141 
142  // Max oil saturation (for VPPARS), hysteresis update.
143  props_.updateSatOilMax(state.saturation());
144  props_.updateSatHyst(state.saturation(), allcells_);
145 
146  // Compute reservoir volumes for RESV controls.
147  asImpl().computeRESV(timer.currentStepNum(), wells, state, well_state);
148 
149  // Run a multiple steps of the solver depending on the time step control.
150  solver_timer.start();
151 
152  auto solver = asImpl().createSolver(wells);
153 
154  // If sub stepping is enabled allow the solver to sub cycle
155  // in case the report steps are too large for the solver to converge
156  //
157  // \Note: The report steps are met in any case
158  // \Note: The sub stepping will require a copy of the state variables
159  if( adaptiveTimeStepping ) {
160  adaptiveTimeStepping->step( timer, *solver, state, well_state, output_writer_ );
161  }
162  else {
163  // solve for complete report step
164  solver->step(timer.currentStepLength(), state, well_state);
165  }
166 
167  // take time that was used to solve system for this reportStep
168  solver_timer.stop();
169 
170  // accumulate the number of Newton and Linear Iterations
171  totalNewtonIterations += solver->newtonIterations();
172  totalLinearIterations += solver->linearIterations();
173 
174  // Report timing.
175  const double st = solver_timer.secsSinceStart();
176 
177  if ( terminal_output_ )
178  {
179  std::cout << "Fully implicit solver took: " << st << " seconds." << std::endl;
180  }
181 
182  stime += st;
183  if ( output_writer_.output() ) {
184  SimulatorReport step_report;
185  step_report.pressure_time = st;
186  step_report.total_time = step_timer.secsSinceStart();
187  step_report.reportParam(tstep_os);
188  }
189 
190  // Increment timer, remember well state.
191  ++timer;
192  prev_well_state = well_state;
193  }
194 
195  // Write final simulation state.
196  output_writer_.writeTimeStep( timer, state, prev_well_state );
197 
198  // Stop timer and create timing report
199  total_timer.stop();
200  SimulatorReport report;
201  report.pressure_time = stime;
202  report.transport_time = 0.0;
203  report.total_time = total_timer.secsSinceStart();
204  report.total_newton_iterations = totalNewtonIterations;
205  report.total_linear_iterations = totalLinearIterations;
206  return report;
207  }
208 
209  namespace SimFIBODetails {
210  typedef std::unordered_map<std::string, WellConstPtr> WellMap;
211 
212  inline WellMap
213  mapWells(const std::vector<WellConstPtr>& wells)
214  {
215  WellMap wmap;
216 
217  for (std::vector<WellConstPtr>::const_iterator
218  w = wells.begin(), e = wells.end();
219  w != e; ++w)
220  {
221  wmap.insert(std::make_pair((*w)->name(), *w));
222  }
223 
224  return wmap;
225  }
226 
227  inline int
228  resv_control(const WellControls* ctrl)
229  {
230  int i, n = well_controls_get_num(ctrl);
231 
232  bool match = false;
233  for (i = 0; (! match) && (i < n); ++i) {
234  match = well_controls_iget_type(ctrl, i) == RESERVOIR_RATE;
235  }
236 
237  if (! match) { i = 0; }
238 
239  return i - 1; // -1 if no match, undo final "++" otherwise
240  }
241 
242  inline bool
243  is_resv(const Wells& wells,
244  const int w)
245  {
246  return (0 <= resv_control(wells.ctrls[w]));
247  }
248 
249  inline bool
250  is_resv(const WellMap& wmap,
251  const std::string& name,
252  const std::size_t step)
253  {
254  bool match = false;
255 
256  WellMap::const_iterator i = wmap.find(name);
257 
258  if (i != wmap.end()) {
259  WellConstPtr wp = i->second;
260 
261  match = (wp->isProducer(step) &&
262  wp->getProductionProperties(step)
263  .hasProductionControl(WellProducer::RESV))
264  || (wp->isInjector(step) &&
265  wp->getInjectionProperties(step)
266  .hasInjectionControl(WellInjector::RESV));
267  }
268 
269  return match;
270  }
271 
272  inline std::vector<int>
273  resvWells(const Wells* wells,
274  const std::size_t step,
275  const WellMap& wmap)
276  {
277  std::vector<int> resv_wells;
278  if( wells )
279  {
280  for (int w = 0, nw = wells->number_of_wells; w < nw; ++w) {
281  if (is_resv(*wells, w) ||
282  ((wells->name[w] != 0) &&
283  is_resv(wmap, wells->name[w], step)))
284  {
285  resv_wells.push_back(w);
286  }
287  }
288  }
289 
290  return resv_wells;
291  }
292 
293  inline void
294  historyRates(const PhaseUsage& pu,
295  const WellProductionProperties& p,
296  std::vector<double>& rates)
297  {
298  assert (! p.predictionMode);
299  assert (rates.size() ==
300  std::vector<double>::size_type(pu.num_phases));
301 
302  if (pu.phase_used[ BlackoilPhases::Aqua ]) {
303  const std::vector<double>::size_type
304  i = pu.phase_pos[ BlackoilPhases::Aqua ];
305 
306  rates[i] = p.WaterRate;
307  }
308 
309  if (pu.phase_used[ BlackoilPhases::Liquid ]) {
310  const std::vector<double>::size_type
311  i = pu.phase_pos[ BlackoilPhases::Liquid ];
312 
313  rates[i] = p.OilRate;
314  }
315 
316  if (pu.phase_used[ BlackoilPhases::Vapour ]) {
317  const std::vector<double>::size_type
318  i = pu.phase_pos[ BlackoilPhases::Vapour ];
319 
320  rates[i] = p.GasRate;
321  }
322  }
323  } // namespace SimFIBODetails
324 
325  template <class Implementation>
327  WellsManager& /* wells_manager */,
328  WellState& /* well_state */,
329  const Wells* /* wells */)
330  { }
331 
332  template <class Implementation>
334  -> std::unique_ptr<Solver>
335  {
336  auto model = std::unique_ptr<Model>(new Model(model_param_,
337  grid_,
338  props_,
339  geo_,
340  rock_comp_props_,
341  wells,
342  solver_,
343  eclipse_state_,
344  has_disgas_,
345  has_vapoil_,
346  terminal_output_));
347 
348  if (!threshold_pressures_by_face_.empty()) {
349  model->setThresholdPressures(threshold_pressures_by_face_);
350  }
351 
352  return std::unique_ptr<Solver>(new Solver(solver_param_, std::move(model)));
353  }
354 
355  template <class Implementation>
356  void SimulatorBase<Implementation>::computeRESV(const std::size_t step,
357  const Wells* wells,
358  const BlackoilState& x,
359  WellState& xw)
360  {
362 
363  const std::vector<WellConstPtr>& w_ecl = eclipse_state_->getSchedule()->getWells(step);
364  const WellMap& wmap = SimFIBODetails::mapWells(w_ecl);
365 
366  const std::vector<int>& resv_wells = SimFIBODetails::resvWells(wells, step, wmap);
367 
368  if (! resv_wells.empty()) {
369  const PhaseUsage& pu = props_.phaseUsage();
370  const std::vector<double>::size_type np = props_.numPhases();
371 
372  rateConverter_.defineState(x);
373 
374  std::vector<double> distr (np);
375  std::vector<double> hrates(np);
376  std::vector<double> prates(np);
377 
378  for (std::vector<int>::const_iterator
379  rp = resv_wells.begin(), e = resv_wells.end();
380  rp != e; ++rp)
381  {
382  WellControls* ctrl = wells->ctrls[*rp];
383  const bool is_producer = wells->type[*rp] == PRODUCER;
384 
385  // RESV control mode, all wells
386  {
387  const int rctrl = SimFIBODetails::resv_control(ctrl);
388 
389  if (0 <= rctrl) {
390  const std::vector<double>::size_type off = (*rp) * np;
391 
392  if (is_producer) {
393  // Convert to positive rates to avoid issues
394  // in coefficient calculations.
395  std::transform(xw.wellRates().begin() + (off + 0*np),
396  xw.wellRates().begin() + (off + 1*np),
397  prates.begin(), std::negate<double>());
398  } else {
399  std::copy(xw.wellRates().begin() + (off + 0*np),
400  xw.wellRates().begin() + (off + 1*np),
401  prates.begin());
402  }
403 
404  const int fipreg = 0; // Hack. Ignore FIP regions.
405  rateConverter_.calcCoeff(prates, fipreg, distr);
406 
407  well_controls_iset_distr(ctrl, rctrl, & distr[0]);
408  }
409  }
410 
411  // RESV control, WCONHIST wells. A bit of duplicate
412  // work, regrettably.
413  if (is_producer && wells->name[*rp] != 0) {
414  WellMap::const_iterator i = wmap.find(wells->name[*rp]);
415 
416  if (i != wmap.end()) {
417  WellConstPtr wp = i->second;
418 
419  const WellProductionProperties& p =
420  wp->getProductionProperties(step);
421 
422  if (! p.predictionMode) {
423  // History matching (WCONHIST/RESV)
424  SimFIBODetails::historyRates(pu, p, hrates);
425 
426  const int fipreg = 0; // Hack. Ignore FIP regions.
427  rateConverter_.calcCoeff(hrates, fipreg, distr);
428 
429  // WCONHIST/RESV target is sum of all
430  // observed phase rates translated to
431  // reservoir conditions. Recall sign
432  // convention: Negative for producers.
433  const double target =
434  - std::inner_product(distr.begin(), distr.end(),
435  hrates.begin(), 0.0);
436 
437  well_controls_clear(ctrl);
438  well_controls_assert_number_of_phases(ctrl, int(np));
439 
440  static const double invalid_alq = -std::numeric_limits<double>::max();
441  static const int invalid_vfp = -std::numeric_limits<int>::max();
442 
443  const int ok_resv =
444  well_controls_add_new(RESERVOIR_RATE, target,
445  invalid_alq, invalid_vfp,
446  & distr[0], ctrl);
447 
448  // For WCONHIST the BHP limit is set to 1 atm.
449  // or a value specified using WELTARG
450  double bhp_limit = (p.BHPLimit > 0) ? p.BHPLimit : unit::convert::from(1.0, unit::atm);
451  const int ok_bhp =
452  well_controls_add_new(BHP, bhp_limit,
453  invalid_alq, invalid_vfp,
454  NULL, ctrl);
455 
456  if (ok_resv != 0 && ok_bhp != 0) {
457  xw.currentControls()[*rp] = 0;
458  well_controls_set_current(ctrl, 0);
459  }
460  }
461  }
462  }
463  }
464  }
465 
466  if( wells )
467  {
468  for (int w = 0, nw = wells->number_of_wells; w < nw; ++w) {
469  WellControls* ctrl = wells->ctrls[w];
470  const bool is_producer = wells->type[w] == PRODUCER;
471  if (!is_producer && wells->name[w] != 0) {
472  WellMap::const_iterator i = wmap.find(wells->name[w]);
473  if (i != wmap.end()) {
474  WellConstPtr wp = i->second;
475  const WellInjectionProperties& injector = wp->getInjectionProperties(step);
476  if (!injector.predictionMode) {
477  //History matching WCONINJEH
478  static const double invalid_alq = -std::numeric_limits<double>::max();
479  static const int invalid_vfp = -std::numeric_limits<int>::max();
480  // For WCONINJEH the BHP limit is set to a large number
481  // or a value specified using WELTARG
482  double bhp_limit = (injector.BHPLimit > 0) ? injector.BHPLimit : std::numeric_limits<double>::max();
483  const int ok_bhp =
484  well_controls_add_new(BHP, bhp_limit,
485  invalid_alq, invalid_vfp,
486  NULL, ctrl);
487  if (!ok_bhp) {
488  OPM_THROW(std::runtime_error, "Failed to add well control.");
489  }
490  }
491  }
492  }
493  }
494  }
495  }
496 } // namespace Opm
Definition: GeoProps.hpp:53
Definition: BlackoilPropsAdInterface.hpp:38
std::vector< int > resvWells(const Wells *wells, const std::size_t step, const WellMap &wmap)
Definition: SimulatorBase_impl.hpp:273
Definition: AdditionalObjectDeleter.hpp:22
Traits::Grid Grid
Definition: SimulatorBase.hpp:92
bool terminal_output_
Definition: SimulatorBase.hpp:186
SimulatorBase(const parameter::ParameterGroup &param, const Grid &grid, const DerivedGeology &geo, BlackoilPropsAdInterface &props, const RockCompressibility *rock_comp_props, NewtonIterationBlackoilInterface &linsolver, const double *gravity, const bool disgas, const bool vapoil, std::shared_ptr< EclipseState > eclipse_state, OutputWriter &output_writer, const std::vector< double > &threshold_pressures_by_face)
Definition: SimulatorBase_impl.hpp:28
bool is_resv(const Wells &wells, const int w)
Definition: SimulatorBase_impl.hpp:243
void historyRates(const PhaseUsage &pu, const WellProductionProperties &p, std::vector< double > &rates)
Definition: SimulatorBase_impl.hpp:294
std::unique_ptr< Solver > createSolver(const Wells *wells)
Definition: SimulatorBase_impl.hpp:333
Traits::Solver Solver
Definition: SimulatorBase.hpp:93
bool is_parallel_run_
Definition: SimulatorBase.hpp:195
void handleAdditionalWellInflow(SimulatorTimer &timer, WellsManager &wells_manager, WellState &well_state, const Wells *wells)
Definition: SimulatorBase_impl.hpp:326
STL namespace.
virtual const boost::any & parallelInformation() const =0
Get the information about the parallelization of the grid.
Traits::OutputWriter OutputWriter
Definition: SimulatorBase.hpp:91
NewtonIterationBlackoilInterface & solver_
Definition: SimulatorBase.hpp:181
Traits::ReservoirState ReservoirState
Definition: SimulatorBase.hpp:89
Interface class for (linear) solvers for the fully implicit black-oil system.
Definition: NewtonIterationBlackoilInterface.hpp:31
std::vector< int > allcells_
Definition: SimulatorBase.hpp:183
void computeRESV(const std::size_t step, const Wells *wells, const BlackoilState &x, WellState &xw)
Definition: SimulatorBase_impl.hpp:356
Traits::Model Model
Definition: SimulatorBase.hpp:166
std::unordered_map< std::string, WellConstPtr > WellMap
Definition: SimulatorBase_impl.hpp:210
int resv_control(const WellControls *ctrl)
Definition: SimulatorBase_impl.hpp:228
SimulatorReport run(SimulatorTimer &timer, ReservoirState &state)
Definition: SimulatorBase_impl.hpp:77
WellMap mapWells(const std::vector< WellConstPtr > &wells)
Definition: SimulatorBase_impl.hpp:213
Traits::WellState WellState
Definition: SimulatorBase.hpp:90