SteadyStateUpscaler_impl.hpp
Go to the documentation of this file.
1 //===========================================================================
2 //
3 // File: SteadyStateUpscaler_impl.hpp
4 //
5 // Created: Fri Aug 28 14:07:51 2009
6 //
7 // Author(s): Atgeirr F Rasmussen <atgeirr@sintef.no>
8 // Brd Skaflestad <bard.skaflestad@sintef.no>
9 //
10 // $Date$
11 //
12 // $Revision$
13 //
14 //===========================================================================
15 
16 /*
17  Copyright 2009, 2010 SINTEF ICT, Applied Mathematics.
18  Copyright 2009, 2010 Statoil ASA.
19 
20  This file is part of The Open Reservoir Simulator Project (OpenRS).
21 
22  OpenRS is free software: you can redistribute it and/or modify
23  it under the terms of the GNU General Public License as published by
24  the Free Software Foundation, either version 3 of the License, or
25  (at your option) any later version.
26 
27  OpenRS is distributed in the hope that it will be useful,
28  but WITHOUT ANY WARRANTY; without even the implied warranty of
29  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30  GNU General Public License for more details.
31 
32  You should have received a copy of the GNU General Public License
33  along with OpenRS. If not, see <http://www.gnu.org/licenses/>.
34 */
35 
36 #ifndef OPM_STEADYSTATEUPSCALER_IMPL_HEADER
37 #define OPM_STEADYSTATEUPSCALER_IMPL_HEADER
38 
39 
40 #include <boost/lexical_cast.hpp>
41 #include <opm/porsol/common/MatrixInverse.hpp>
42 #include <opm/porsol/common/SimulatorUtilities.hpp>
43 #include <opm/porsol/common/ReservoirPropertyFixedMobility.hpp>
44 #include <opm/core/utility/Units.hpp>
45 #include <algorithm>
46 #include <iostream>
47 
48 namespace Opm
49 {
50 
51 
52 
53  template <class Traits>
55  : Super(),
56  use_gravity_(false),
57  output_vtk_(false),
58  print_inoutflows_(false),
59  simulation_steps_(10),
60  stepsize_(0.1),
61  relperm_threshold_(1.0e-8),
62  maximum_mobility_contrast_(1.0e9),
63  sat_change_threshold_(0.0)
64  {
65  }
66 
67 
68 
69 
70  template <class Traits>
71  inline void SteadyStateUpscaler<Traits>::initImpl(const Opm::parameter::ParameterGroup& param)
72  {
73  Super::initImpl(param);
74  use_gravity_ = param.getDefault("use_gravity", use_gravity_);
75  output_vtk_ = param.getDefault("output_vtk", output_vtk_);
76  print_inoutflows_ = param.getDefault("print_inoutflows", print_inoutflows_);
77  simulation_steps_ = param.getDefault("simulation_steps", simulation_steps_);
78  stepsize_ = Opm::unit::convert::from(param.getDefault("stepsize", stepsize_),
79  Opm::unit::day);
80  relperm_threshold_ = param.getDefault("relperm_threshold", relperm_threshold_);
81  maximum_mobility_contrast_ = param.getDefault("maximum_mobility_contrast", maximum_mobility_contrast_);
82  sat_change_threshold_ = param.getDefault("sat_change_threshold", sat_change_threshold_);
83 
84  transport_solver_.init(param);
85  // Set viscosities and densities if given.
86  double v1_default = this->res_prop_.viscosityFirstPhase();
87  double v2_default = this->res_prop_.viscositySecondPhase();
88  this->res_prop_.setViscosities(param.getDefault("viscosity1", v1_default), param.getDefault("viscosity2", v2_default));
89  double d1_default = this->res_prop_.densityFirstPhase();
90  double d2_default = this->res_prop_.densitySecondPhase();
91  this->res_prop_.setDensities(param.getDefault("density1", d1_default), param.getDefault("density2", d2_default));
92  }
93 
94 
95 
96 
97  namespace {
98  double maxMobility(double m1, double m2)
99  {
100  return std::max(m1, m2);
101  }
102  // The matrix variant expects diagonal mobilities.
103  template <class SomeMatrixType>
104  double maxMobility(double m1, SomeMatrixType& m2)
105  {
106  double m = m1;
107  for (int i = 0; i < std::min(m2.numRows(), m2.numCols()); ++i) {
108  m = std::max(m, m2(i,i));
109  }
110  return m;
111  }
112  void thresholdMobility(double& m, double threshold)
113  {
114  m = std::max(m, threshold);
115  }
116  // The matrix variant expects diagonal mobilities.
117  template <class SomeMatrixType>
118  void thresholdMobility(SomeMatrixType& m, double threshold)
119  {
120  for (int i = 0; i < std::min(m.numRows(), m.numCols()); ++i) {
121  m(i, i) = std::max(m(i, i), threshold);
122  }
123  }
124  } // anon namespace
125 
126 
127 
128 
129  template <class Traits>
130  inline std::pair<typename SteadyStateUpscaler<Traits>::permtensor_t,
131  typename SteadyStateUpscaler<Traits>::permtensor_t>
133  upscaleSteadyState(const int flow_direction,
134  const std::vector<double>& initial_saturation,
135  const double boundary_saturation,
136  const double pressure_drop,
137  const permtensor_t& upscaled_perm)
138  {
139  static int count = 0;
140  ++count;
141  int num_cells = this->ginterf_.numberOfCells();
142  // No source or sink.
143  std::vector<double> src(num_cells, 0.0);
144  Opm::SparseVector<double> injection(num_cells);
145  // Gravity.
146  Dune::FieldVector<double, 3> gravity(0.0);
147  if (use_gravity_) {
148  gravity[2] = Opm::unit::gravity;
149  }
150  if (gravity.two_norm() > 0.0) {
151  OPM_MESSAGE("Warning: Gravity is experimental for flow solver.");
152  }
153 
154  // Set up initial saturation profile.
155  std::vector<double> saturation = initial_saturation;
156 
157  // Set up boundary conditions.
158  setupUpscalingConditions(this->ginterf_, this->bctype_, flow_direction,
159  pressure_drop, boundary_saturation, this->twodim_hack_, this->bcond_);
160 
161  // Set up solvers.
162  if (flow_direction == 0) {
163  this->flow_solver_.init(this->ginterf_, this->res_prop_, gravity, this->bcond_);
164  }
165  transport_solver_.initObj(this->ginterf_, this->res_prop_, this->bcond_);
166 
167  // Run pressure solver.
168  this->flow_solver_.solve(this->res_prop_, saturation, this->bcond_, src,
169  this->residual_tolerance_, this->linsolver_verbosity_,
170  this->linsolver_type_, false,
171  this->linsolver_maxit_, this->linsolver_prolongate_factor_,
172  this->linsolver_smooth_steps_);
173  double max_mod = this->flow_solver_.postProcessFluxes();
174  std::cout << "Max mod = " << max_mod << std::endl;
175 
176  // Do a run till steady state. For now, we just do some pressure and transport steps...
177  std::vector<double> saturation_old = saturation;
178  for (int iter = 0; iter < simulation_steps_; ++iter) {
179  // Run transport solver.
180  transport_solver_.transportSolve(saturation, stepsize_, gravity, this->flow_solver_.getSolution(), injection);
181 
182  // Run pressure solver.
183  this->flow_solver_.solve(this->res_prop_, saturation, this->bcond_, src,
184  this->residual_tolerance_, this->linsolver_verbosity_,
185  this->linsolver_type_, false,
186  this->linsolver_maxit_, this->linsolver_prolongate_factor_,
187  this->linsolver_smooth_steps_);
188  max_mod = this->flow_solver_.postProcessFluxes();
189  std::cout << "Max mod = " << max_mod << std::endl;
190 
191  // Print in-out flows if requested.
192  if (print_inoutflows_) {
193  std::pair<double, double> w_io, o_io;
194  computeInOutFlows(w_io, o_io, this->flow_solver_.getSolution(), saturation);
195  std::cout << "Pressure step " << iter
196  << "\nWater flow [in] " << w_io.first
197  << " [out] " << w_io.second
198  << "\nOil flow [in] " << o_io.first
199  << " [out] " << o_io.second
200  << std::endl;
201  }
202 
203  // Output.
204  if (output_vtk_) {
205  writeVtkOutput(this->ginterf_,
206  this->res_prop_,
207  this->flow_solver_.getSolution(),
208  saturation,
209  std::string("output-steadystate")
210  + '-' + boost::lexical_cast<std::string>(count)
211  + '-' + boost::lexical_cast<std::string>(flow_direction)
212  + '-' + boost::lexical_cast<std::string>(iter));
213  }
214 
215  // Comparing old to new.
216  int num_cells = saturation.size();
217  double maxdiff = 0.0;
218  for (int i = 0; i < num_cells; ++i) {
219  maxdiff = std::max(maxdiff, std::fabs(saturation[i] - saturation_old[i]));
220  }
221 #ifdef VERBOSE
222  std::cout << "Maximum saturation change: " << maxdiff << std::endl;
223 #endif
224  if (maxdiff < sat_change_threshold_) {
225 #ifdef VERBOSE
226  std::cout << "Maximum saturation change is under steady state threshold." << std::endl;
227 #endif
228  break;
229  }
230 
231  // Copy to old.
232  saturation_old = saturation;
233  }
234 
235  // Compute phase mobilities.
236  // First: compute maximal mobilities.
237  typedef typename Super::ResProp::Mobility Mob;
238  Mob m;
239  double m1max = 0;
240  double m2max = 0;
241  for (int c = 0; c < num_cells; ++c) {
242  this->res_prop_.phaseMobility(0, c, saturation[c], m.mob);
243  m1max = maxMobility(m1max, m.mob);
244  this->res_prop_.phaseMobility(1, c, saturation[c], m.mob);
245  m2max = maxMobility(m2max, m.mob);
246  }
247  // Second: set thresholds.
248  const double mob1_abs_thres = relperm_threshold_ / this->res_prop_.viscosityFirstPhase();
249  const double mob1_rel_thres = m1max / maximum_mobility_contrast_;
250  const double mob1_threshold = std::max(mob1_abs_thres, mob1_rel_thres);
251  const double mob2_abs_thres = relperm_threshold_ / this->res_prop_.viscositySecondPhase();
252  const double mob2_rel_thres = m2max / maximum_mobility_contrast_;
253  const double mob2_threshold = std::max(mob2_abs_thres, mob2_rel_thres);
254  // Third: extract and threshold.
255  std::vector<Mob> mob1(num_cells);
256  std::vector<Mob> mob2(num_cells);
257  for (int c = 0; c < num_cells; ++c) {
258  this->res_prop_.phaseMobility(0, c, saturation[c], mob1[c].mob);
259  thresholdMobility(mob1[c].mob, mob1_threshold);
260  this->res_prop_.phaseMobility(1, c, saturation[c], mob2[c].mob);
261  thresholdMobility(mob2[c].mob, mob2_threshold);
262  }
263 
264  // Compute upscaled relperm for each phase.
265  ReservoirPropertyFixedMobility<Mob> fluid_first(mob1);
266  permtensor_t eff_Kw = Super::upscaleEffectivePerm(fluid_first);
267  ReservoirPropertyFixedMobility<Mob> fluid_second(mob2);
268  permtensor_t eff_Ko = Super::upscaleEffectivePerm(fluid_second);
269 
270  // Set the steady state saturation fields for eventual outside access.
271  last_saturation_state_.swap(saturation);
272 
273  // Compute the (anisotropic) upscaled mobilities.
274  // eff_Kw := lambda_w*K
275  // => lambda_w = eff_Kw*inv(K);
276  permtensor_t lambda_w(matprod(eff_Kw, inverse3x3(upscaled_perm)));
277  permtensor_t lambda_o(matprod(eff_Ko, inverse3x3(upscaled_perm)));
278 
279  // Compute (anisotropic) upscaled relative permeabilities.
280  // lambda = k_r/mu
281  permtensor_t k_rw(lambda_w);
282  k_rw *= this->res_prop_.viscosityFirstPhase();
283  permtensor_t k_ro(lambda_o);
284  k_ro *= this->res_prop_.viscositySecondPhase();
285  return std::make_pair(k_rw, k_ro);
286  }
287 
288 
289 
290 
291  template <class Traits>
292  inline const std::vector<double>&
294  {
295  return last_saturation_state_;
296  }
297 
298 
299 
300 
301  template <class Traits>
303  {
304  typedef typename GridInterface::CellIterator CellIter;
305  double pore_vol = 0.0;
306  double sat_vol = 0.0;
307  for (CellIter c = this->ginterf_.cellbegin(); c != this->ginterf_.cellend(); ++c) {
308  double cell_pore_vol = c->volume()*this->res_prop_.porosity(c->index());
309  pore_vol += cell_pore_vol;
310  sat_vol += cell_pore_vol*last_saturation_state_[c->index()];
311  }
312  // Dividing by pore volume gives average saturations.
313  return sat_vol/pore_vol;
314  }
315 
316 
317  static inline double calc_frac_flow (
318  std::map< int, double >& frac_flow_by_bid,
319  const BCs& bcond,
320  const ResProp& res_prop,
321  GridInterface::CellIterator c,
322  CellIterator::FaceIterator f ) {
323 
324  const SatBC& sc = bcond.satCond( f );
325 
326  if( sc.isPeriodic() ) {
327  assert( sc.isDirichlet() );
328  return res_prop.fractionalFlow( c->index(), sc.saturation() );
329  }
330 
331  assert( sc.saturationDifference() == 0.0 );
332  const int partner_bid = bcond.getPeriodicPartner( f->boundaryId() );
333  const auto it = frac_flow_by_bid.find( partner_bid );
334 
335  // This is for the periodic case, so that we are sure all fractional flows have
336  // been set in frac_flow_by_bid.
337  if( it == frac_flow_by_bid.end() ) {
338  OPM_THROW(std::runtime_error, "Could not find periodic partner fractional flow. Face bid = " << f->boundaryId()
339  << " and partner bid = " << partner_bid);
340  }
341 
342  return it->second;
343  }
344 
345  template <class Traits>
346  template <class FlowSol>
347  void SteadyStateUpscaler<Traits>::computeInOutFlows(std::pair<double, double>& water_inout,
348  std::pair<double, double>& oil_inout,
349  const FlowSol& flow_solution,
350  const std::vector<double>& saturations) const
351  {
352 
353  struct sideflux {
354  double water;
355  double oil;
356 
357  sideflux& operator+=( const sideflux& other ) {
358  this->water += other.water;
359  this->oil += other.oil;
360  return *this;
361  }
362  };
363 
364  std::map< int, double > frac_flow_by_bid;
365  // Two passes: First pass, deal with outflow
366  sideflux outflow;
367  for( auto c = this->ginterf_.cellbegin(); c != this->ginterf_.cellend(); ++c ) {
368  const double frac_flow = this->res_prop_.fractional_flow( c->index(), saturations[ c->index() ] );
369 
370  for( auto f = c->facebegin(); f != c->faceend(); ++f ) {
371  if( !f->boundary() ) continue;
372 
373  const double flux = flow_solution.outflux( f );
374  if( flux < 0.0 ) continue;
375 
376  const SatBC& sc = this->bcond_.satCond( f );
377  if( sc.isPeriodic() ) frac_flow_by_bid[ f->boundaryId() ] = frac_flow;
378 
379  outflow += { flux * frac_flow, flux * ( 1.0 - frac_flow ) };
380  }
381  }
382 
383  // second pass, deal with inflow.
384  sideflux inflow;
385  for( auto c = this->ginterf_.cellbegin(); c != this->ginterf_.cellend(); ++c ) {
386  for( auto f = c->facebegin(); f != c->faceend(); ++f ) {
387  if( !f->boundary() ) continue;
388 
389  const double flux = flow_solution.outflux( f );
390  if( flux >= 0.0 ) continue;
391 
392  const double frac_flow = calc_frac_flow( frac_flow_by_bid, this->bcond_, this->res_prop_, c, f );
393 
394  inflow += { flux * frac_flow, flux * ( 1.0 - frac_flow ) };
395  }
396  }
397 
398  water_inout = std::make_pair( in.water, out.water );
399  oil_inout = std::make_pair( in.oil, out.oil );
400 
401  }
402 } // namespace Opm
403 
404 
405 #endif // OPM_STEADYSTATEUPSCALER_IMPL_HEADER
SteadyStateUpscaler()
Default constructor.
Definition: SteadyStateUpscaler_impl.hpp:54
Definition: applier.hpp:18
const std::vector< double > & lastSaturationState() const
Definition: SteadyStateUpscaler_impl.hpp:293
std::pair< permtensor_t, permtensor_t > upscaleSteadyState(const int flow_direction, const std::vector< double > &initial_saturation, const double boundary_saturation, const double pressure_drop, const permtensor_t &upscaled_perm)
Definition: SteadyStateUpscaler_impl.hpp:133
A base class for upscaling.
Definition: UpscalerBase.hpp:52
virtual void initImpl(const Opm::parameter::ParameterGroup &param)
Override from superclass.
Definition: SteadyStateUpscaler_impl.hpp:71
min[0]
Definition: elasticity_upscale_impl.hpp:144
void computeInOutFlows(std::pair< double, double > &water_inout, std::pair< double, double > &oil_inout, const FlowSol &flow_solution, const std::vector< double > &saturations) const
Definition: SteadyStateUpscaler_impl.hpp:347
double lastSaturationUpscaled() const
Definition: SteadyStateUpscaler_impl.hpp:302
static double calc_frac_flow(std::map< int, double > &frac_flow_by_bid, const BCs &bcond, const ResProp &res_prop, GridInterface::CellIterator c, CellIterator::FaceIterator f)
Definition: SteadyStateUpscaler_impl.hpp:317
GridInterface::CellIterator CellIter
Definition: UpscalerBase.hpp:127
Super::permtensor_t permtensor_t
Definition: SteadyStateUpscaler.hpp:57