TpfaCompressible.hpp
Go to the documentation of this file.
1 /*
2  Copyright 2010 SINTEF ICT, Applied Mathematics.
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_TPFACOMPRESSIBLE_HEADER_INCLUDED
21 #define OPM_TPFACOMPRESSIBLE_HEADER_INCLUDED
22 
23 
26 #include <opm/core/linalg/LinearSolverIstl.hpp>
28 
29 #include <opm/common/ErrorMacros.hpp>
30 #include <opm/core/utility/SparseTable.hpp>
31 
32 #include <array>
33 #include <iostream>
34 #include <memory>
35 
36 namespace Opm
37 {
38 
39 
40  template <class GridInterface,
41  class RockInterface,
42  class FluidInterface,
43  class WellsInterface,
44  class BCInterface>
46  {
47  public:
49 
53  : pgrid_(0), prock_(0), pfluid_(0)
54  {
55  }
56 
57 
60  void init(const Opm::parameter::ParameterGroup& param)
61  {
62  // Initialize inflow mixture to a fixed, user-provided mix.
63  typename FluidInterface::CompVec mix(0.0);
64  const int nc = FluidInterface::numComponents;
65  double inflow_mixture_gas = param.getDefault("inflow_mixture_gas", 1.0);
66  double inflow_mixture_oil = param.getDefault("inflow_mixture_oil", 0.0);
67  switch (nc) {
68  case 2:
69  mix[FluidInterface::Gas] = inflow_mixture_gas;
70  mix[FluidInterface::Oil] = inflow_mixture_oil;
71  break;
72  case 3: {
73  double inflow_mixture_water = param.getDefault("inflow_mixture_water", 0.0);
74  mix[FluidInterface::Water] = inflow_mixture_water;
75  mix[FluidInterface::Gas] = inflow_mixture_gas;
76  mix[FluidInterface::Oil] = inflow_mixture_oil;
77  break;
78  }
79  default:
80  OPM_THROW(std::runtime_error, "Unhandled number of components: " << nc);
81  }
82  inflow_mixture_ = mix;
83  linsolver_.reset(new LinearSolverIstl(param));
84  flux_rel_tol_ = param.getDefault("flux_rel_tol", 1e-5);
85  press_rel_tol_ = param.getDefault("press_rel_tol", 1e-5);
86  max_num_iter_ = param.getDefault("max_num_iter", 15);
87  max_relative_voldiscr_ = param.getDefault("max_relative_voldiscr", 0.15);
88  relax_time_voldiscr_ = param.getDefault("relax_time_voldiscr", 0.0);
89  relax_weight_pressure_iteration_ = param.getDefault("relax_weight_pressure_iteration", 1.0);
90  experimental_jacobian_ = param.getDefault("experimental_jacobian", false);
91  nonlinear_residual_tolerance_ = param.getDefault("nonlinear_residual_tolerance", 0.0);
92  output_residual_ = param.getDefault("output_residual", false);
93  voldisc_factor_ = param.getDefault("voldisc_factor", 1.0);
94  }
95 
96 
97 
98 
101  typename FluidInterface::CompVec inflowMixture() const
102  {
103  return inflow_mixture_;
104  }
105 
106 
107 
108 
137  void setup(const GridInterface& grid,
138  const RockInterface& rock,
139  const FluidInterface& fluid,
140  const WellsInterface& wells,
141  const typename GridInterface::Vector& grav,
142  const BCInterface& bc,
143  const std::vector<typename FluidInterface::PhaseVec>* face_pressure = 0)
144  {
145  pgrid_ = &grid;
146  prock_ = &rock;
147  pfluid_ = &fluid;
148  pwells_ = &wells;
149  gravity_ = grav;
150 
151  // Extract perm tensors.
152  const double* perm = &(rock.permeability(0)(0,0));
153  poro_.clear();
154  poro_.resize(grid.numCells(), 1.0);
155  for (int i = 0; i < grid.numCells(); ++i) {
156  poro_[i] = rock.porosity(i);
157  }
158  // Initialize
159  psolver_.init(grid, wells, perm, &poro_[0], grav);
160 
161  // Build bctypes_ and bcvalues_.
162  int num_faces = grid.numFaces();
163  bctypes_.clear();
164  bctypes_.resize(num_faces, PressureAssembler::FBC_UNSET);
165  bcvalues_.clear();
166  bcvalues_.resize(num_faces, 0.0);
167  for (int face = 0; face < num_faces; ++face) {
168  int bid = pgrid_->boundaryId(face);
169  if (bid == 0) {
170  bctypes_[face] = PressureAssembler::FBC_UNSET;
171  continue;
172  }
173  FlowBC face_bc = bc.flowCond(bid);
174  if (face_bc.isDirichlet()) {
175  bctypes_[face] = PressureAssembler::FBC_PRESSURE;
176  bcvalues_[face] = face_bc.pressure();
177  if (face_bc.pressure() < 0.0) {
178  bcvalues_[face] = (*face_pressure)[face][FluidInterface::Liquid];
179  }
180  } else if (face_bc.isNeumann()) {
181  bctypes_[face] = PressureAssembler::FBC_FLUX;
182  bcvalues_[face] = face_bc.outflux(); // TODO: may have to switch sign here depending on orientation.
183  if (bcvalues_[face] != 0.0) {
184  OPM_THROW(std::runtime_error, "Nonzero Neumann conditions not yet properly implemented "
185  "(signs must be fixed, also face pressures are not correctly computed for this case)");
186  }
187  } else {
188  OPM_THROW(std::runtime_error, "Unhandled boundary condition type.");
189  }
190  }
191 
192  // Setup unchanging well data structures.
193  perf_wells_.clear();
194  perf_cells_.clear();
195  perf_A_.clear();
196  perf_mob_.clear();
197  perf_sat_.clear();
198  int num_wells = pwells_->numWells();
199  for (int well = 0; well < num_wells; ++well) {
200  int num_perf = pwells_->numPerforations(well);
201  for (int perf = 0; perf < num_perf; ++perf) {
202  int cell = pwells_->wellCell(well, perf);
203  perf_wells_.push_back(well);
204  perf_cells_.push_back(cell);
205  }
206  }
207  int num_perf = perf_wells_.size();
208  perf_A_.resize(num_perf*numPhases*numComponents);
209  perf_mob_.resize(num_perf*numPhases);
210  perf_sat_.resize(num_perf);
211  }
212 
213 
214 
215 
216  double volumeDiscrepancyLimit() const
217  {
218  return max_relative_voldiscr_;
219  }
220 
221 
222 
223 
224  const std::vector<double>& faceTransmissibilities()
225  {
226  return psolver_.faceTransmissibilities();
227  }
228 
229 
230 
231 
232  bool volumeDiscrepancyAcceptable(const std::vector<typename FluidInterface::PhaseVec>& cell_pressure,
233  const std::vector<typename FluidInterface::PhaseVec>& face_pressure,
234  const std::vector<double>& well_perf_pressure,
235  const std::vector<typename FluidInterface::CompVec>& cell_z,
236  const double dt)
237  {
238  computeFluidProps(cell_pressure, face_pressure, well_perf_pressure, cell_z, dt);
239  double rel_voldiscr = *std::max_element(fp_.relvoldiscr.begin(), fp_.relvoldiscr.end());
240  if (rel_voldiscr > max_relative_voldiscr_) {
241  std::cout << " Relative volume discrepancy too large: " << rel_voldiscr << std::endl;
242  return false;
243  } else {
244  std::cout << " Relative volume discrepancy ok: " << rel_voldiscr << std::endl;
245  return true;
246  }
247  }
248 
250 
251 
290  ReturnCode solve(std::vector<typename FluidInterface::PhaseVec>& cell_pressure,
291  std::vector<typename FluidInterface::PhaseVec>& face_pressure,
292  const std::vector<typename FluidInterface::CompVec>& cell_z,
293  std::vector<double>& face_flux,
294  std::vector<double>& well_bhp_pressures,
295  std::vector<double>& well_perf_pressures,
296  std::vector<double>& well_perf_fluxes,
297  const std::vector<double>& src,
298  const double dt)
299  {
300  // Set up initial state.
301  // \TODO We are currently using Liquid phase pressure,
302  // what is correct with capillary pressure?
303  SolverState state;
304  int num_cells = cell_pressure.size();
305  int num_faces = face_pressure.size();
306  state.cell_pressure.resize(num_cells);
307  for (int cell = 0; cell < num_cells; ++cell) {
308  state.cell_pressure[cell] = cell_pressure[cell][FluidInterface::Liquid];
309  }
310  state.face_pressure.resize(num_faces);
311  for (int face = 0; face < num_faces; ++face) {
312  state.face_pressure[face] = face_pressure[face][FluidInterface::Liquid];
313  }
314  state.face_flux.clear();
315  state.face_flux.resize(num_faces);
316  state.well_bhp_pressure = well_bhp_pressures;
317  state.well_perf_pressure = well_perf_pressures;
318  state.well_perf_flux = well_perf_fluxes;
319 
320  // Run solver.
321  ReturnCode retcode;
322  if (nonlinear_residual_tolerance_ == 0.0) {
323  // Old version.
324  retcode = solveImpl(cell_z, src, dt, state);
325  } else {
326  // New version using residual reduction as
327  // convergence criterion.
328  retcode = solveImplNew(cell_z, src, dt, state);
329  }
330 
331  // Copy results to output variables.
332  if (retcode == SolveOk) {
333  // \TODO Currently all phase pressures will be equal,
334  // what is correct with capillary pressure?
335  for (int cell = 0; cell < num_cells; ++cell) {
336  cell_pressure[cell] = state.cell_pressure[cell];
337  }
338  for (int face = 0; face < num_faces; ++face) {
339  face_pressure[face] = state.face_pressure[face];
340  }
341  face_flux = state.face_flux;
342  well_bhp_pressures = state.well_bhp_pressure;
343  well_perf_pressures = state.well_perf_pressure;
344  well_perf_fluxes = state.well_perf_flux;
345  }
346  return retcode;
347  }
348 
349 
350 
353  {
354  return psolver_.explicitTimestepLimit(&fp_.face_data.state_matrix[0][0][0],
355  &fp_.face_data.mobility[0][0],
356  &fp_.face_data.mobility_deriv[0][0][0],
357  &(pfluid_->surfaceDensities()[0]));
358  }
359 
360 
361 
363  void doStepIMPES(std::vector<typename FluidInterface::CompVec>& cell_z,
364  const double dt)
365  {
366  psolver_.explicitTransport(dt, &(cell_z[0][0]));
367  }
368 
369 
370 
371  private:
372  const GridInterface* pgrid_;
373  const RockInterface* prock_;
374  const FluidInterface* pfluid_;
375  const WellsInterface* pwells_;
376  typename GridInterface::Vector gravity_;
377  // typename FluidInterface::FluidData fp_;
378  Opm::AllFluidData fp_;
379  std::vector<double> poro_;
380  PressureAssembler psolver_;
381  std::unique_ptr<LinearSolverIstl> linsolver_;
382  std::vector<PressureAssembler::FlowBCTypes> bctypes_;
383  std::vector<double> bcvalues_;
384 
385  typename FluidInterface::CompVec inflow_mixture_;
386  double flux_rel_tol_;
387  double press_rel_tol_;
388  int max_num_iter_;
389  double max_relative_voldiscr_;
390  double relax_time_voldiscr_;
391  double relax_weight_pressure_iteration_;
392  bool experimental_jacobian_;
393  bool output_residual_;
394  double nonlinear_residual_tolerance_;
395  double voldisc_factor_;
396 
397  typedef typename FluidInterface::PhaseVec PhaseVec;
398  typedef typename FluidInterface::CompVec CompVec;
399  enum { numPhases = FluidInterface::numPhases,
400  numComponents = FluidInterface::numComponents };
401 
402  std::vector<int> perf_wells_;
403  std::vector<int> perf_cells_;
404  std::vector<double> perf_A_; // Flat storage.
405  std::vector<double> perf_mob_; // Flat storage.
406  std::vector<PhaseVec> perf_sat_;
407  std::vector<double> perf_gpot_; // Flat storage.
408 
409  // Only intended to avoid extra allocations.
410  mutable std::vector<PhaseVec> helper_cell_pressure_;
411  mutable std::vector<PhaseVec> helper_face_pressure_;
412 
413  struct SolverState
414  {
415  std::vector<double> cell_pressure;
416  std::vector<double> face_pressure;
417  std::vector<double> face_flux;
418  std::vector<double> well_bhp_pressure;
419  std::vector<double> well_perf_pressure;
420  std::vector<double> well_perf_flux;
421  };
422 
423 
424  void computeFluidProps(const std::vector<typename FluidInterface::PhaseVec>& phase_pressure,
425  const std::vector<typename FluidInterface::PhaseVec>& phase_pressure_face,
426  const std::vector<double>& well_perf_pressure,
427  const std::vector<typename FluidInterface::CompVec>& cell_z,
428  const double dt)
429  {
430  fp_.computeNew(*pgrid_, *prock_, *pfluid_, gravity_, phase_pressure, phase_pressure_face, cell_z, inflow_mixture_, dt);
431  // Properties at well perforations.
432  // \TODO only need to recompute this once per pressure update.
433  // No, that is false, at production perforations the cell z is
434  // used, which may change every step.
435  unsigned int perfcount = 0;
436  int num_wells = pwells_->numWells();
437  for (int well = 0; well < num_wells; ++well) {
438  bool inj = pwells_->type(well) == WellsInterface::Injector;
439  int num_perf = pwells_->numPerforations(well);
440  for (int perf = 0; perf < num_perf; ++perf) {
441  int cell = pwells_->wellCell(well, perf);
442  // \TODO handle capillary in perforation pressure below?
443  PhaseVec well_pressure = inj ? PhaseVec(well_perf_pressure[perfcount]) : phase_pressure[cell];
444  CompVec well_mixture = inj ? pwells_->injectionMixture(cell) : cell_z[cell];
445  typename FluidInterface::FluidState state = pfluid_->computeState(well_pressure, well_mixture);
446  std::copy(&state.phase_to_comp_[0][0], &state.phase_to_comp_[0][0] + numComponents*numPhases,
447  &perf_A_[perfcount*numPhases*numComponents]);
448  std::copy(state.mobility_.begin(), state.mobility_.end(),
449  &perf_mob_[perfcount*numPhases]);
450  perf_sat_[perfcount] = state.saturation_;
451  ++perfcount;
452  }
453  }
454  assert(perfcount == perf_wells_.size());
455  }
456 
457 
458 
459  // Convert scalar pressures to phase pressures, then call computeFluidProps().
460  void computeFluidPropsScalarPress(const std::vector<double>& cell_pressure_scalar,
461  const std::vector<double>& face_pressure_scalar,
462  const std::vector<double>& well_perf_pressure,
463  const std::vector<typename FluidInterface::CompVec>& cell_z,
464  const double dt)
465  {
466  // Copy to phase pressures. \TODO handle capillary pressure.
467  int num_cells = pgrid_->numCells();
468  int num_faces = pgrid_->numFaces();
469  helper_cell_pressure_.resize(num_cells);
470  helper_face_pressure_.resize(num_faces);
471  for (int cell = 0; cell < num_cells; ++cell) {
472  helper_cell_pressure_[cell] = cell_pressure_scalar[cell];
473  }
474  for (int face = 0; face < num_faces; ++face) {
475  helper_face_pressure_[face] = face_pressure_scalar[face];
476  }
477  computeFluidProps(helper_cell_pressure_, helper_face_pressure_, well_perf_pressure, cell_z, dt);
478  }
479 
480 
481 
482  // Compute res = Ax - b.
483  void computeLinearResidual(const PressureAssembler::LinearSystem& s, std::vector<double>& res)
484  {
485  res.resize(s.n);
486  for (int row = 0; row < s.n; ++row) {
487  res[row] = -s.b[row];
488  for (int i = s.ia[row]; i < s.ia[row + 1]; ++i) {
489  res[row] += s.sa[i]*s.x[s.ja[i]];
490  }
491  }
492  }
493 
494 
495 
496  // Compute residual and Jacobian of the new formulation.
497  void computeResidualJacobian(const std::vector<double>& initial_voldiscr,
498  const std::vector<double>& cell_pressure_scalar,
499  const std::vector<double>& cell_pressure_scalar_initial,
500  const std::vector<double>& well_bhp,
501  const std::vector<double>& src,
502  const double dt,
503  PressureAssembler::LinearSystem& linsys,
504  std::vector<double>& res)
505  {
506  std::vector<double> zero(initial_voldiscr.size(), 0.0);
507  // Assemble system matrix and rhs.
508  psolver_.assemble(&src[0],
509  &bctypes_[0],
510  &bcvalues_[0],
511  dt,
513  &zero[0],
514  &fp_.cell_data.state_matrix[0][0][0],
515  &fp_.face_data.state_matrix[0][0][0],
516  &perf_A_[0],
517  &fp_.face_data.mobility[0][0],
518  &perf_mob_[0],
519  &cell_pressure_scalar_initial[0],
520  &fp_.face_data.gravity_potential[0][0],
521  &perf_gpot_[0],
522  &(pfluid_->surfaceDensities()[0]));
523  psolver_.linearSystem(linsys);
524  // The linear system is for direct evaluation, we want a residual based approach.
525  // First we compute the residual for the original code.
526  int num_cells = pgrid_->numCells();
527  std::copy(cell_pressure_scalar.begin(), cell_pressure_scalar.end(), linsys.x);
528  std::copy(well_bhp.begin(), well_bhp.end(), linsys.x + num_cells);
529  computeLinearResidual(linsys, res);
530  // Then we compute the residual we actually want by subtracting terms that do not
531  // appear in the new formulation and adding the new terms.
532  for (int cell = 0; cell < num_cells; ++cell) {
533  double tc = fp_.cell_data.total_compressibility[cell];
534  double dres = tc*(cell_pressure_scalar[cell] - cell_pressure_scalar_initial[cell]);
535  dres -= 1.0 - fp_.cell_data.total_phase_volume_density[cell];
536  dres *= pgrid_->cellVolume(cell)*prock_->porosity(cell)/dt;
537  res[cell] -= dres;
538  }
539  // Change the jacobian by adding/subtracting the necessary terms.
540  for (int cell = 0; cell < num_cells; ++cell) {
541  for (int i = linsys.ia[cell]; i < linsys.ia[cell + 1]; ++i) {
542  if (linsys.ja[i] == cell) {
543  double tc = fp_.cell_data.total_compressibility[cell];
544  linsys.sa[i] -= tc*pgrid_->cellVolume(cell)*prock_->porosity(cell)/dt;
545  double ex = fp_.cell_data.experimental_term[cell];
546  linsys.sa[i] += ex*pgrid_->cellVolume(cell)*prock_->porosity(cell)/dt;
547  }
548  }
549  }
550  }
551 
552 
553 
554  // Compute the well potentials. Assumes that the perforation variables
555  // have been set properly: perf_[wells_|cells_|A_].
556  void computeWellPotentials(std::vector<double>& wellperf_gpot) const
557  {
558  int num_perf = perf_cells_.size();
559  wellperf_gpot.resize(num_perf*numPhases);
560  for (int perf = 0; perf < num_perf; ++perf) {
561  int well = perf_wells_[perf];
562  int cell = perf_cells_[perf];
563  typename GridInterface::Vector pos = pgrid_->cellCentroid(cell);
564  // With wells, we assume that gravity is in the z-direction.
565  assert(gravity_[0] == 0.0 && gravity_[1] == 0.0);
566  double depth_delta = pos[2] - pwells_->referenceDepth(well);
567  double gh = gravity_[2]*depth_delta;
568  // At is already transposed since in Fortran order.
569  const double* At = &perf_A_[perf*numPhases*numComponents];
570  PhaseVec rho = pfluid_->phaseDensities(At);
571  for (int phase = 0; phase < numPhases; ++phase) {
572  // Gravity potential is (by phase) \rho_\alpha g h
573  wellperf_gpot[numPhases*perf + phase] = rho[phase]*gh;
574  }
575  }
576  }
577 
578 
579 
580  // Compute the relative changes in fluxes and pressures.
581  static std::pair<double, double>
582  computeFluxPressChanges(const std::vector<double>& face_flux,
583  const std::vector<double>& well_perf_fluxes,
584  const std::vector<double>& cell_pressure_scalar,
585  const std::vector<double>& start_face_flux,
586  const std::vector<double>& start_perf_flux,
587  const std::vector<double>& start_cell_press)
588  {
589  int num_faces = face_flux.size();
590  int num_perf = well_perf_fluxes.size();
591  int num_cells = cell_pressure_scalar.size();
592  double max_flux_face = std::max(std::fabs(*std::min_element(face_flux.begin(), face_flux.end())),
593  std::fabs(*std::max_element(face_flux.begin(), face_flux.end())));
594  double max_flux_perf = num_perf == 0 ? 0.0
595  : std::max(std::fabs(*std::min_element(well_perf_fluxes.begin(), well_perf_fluxes.end())),
596  std::fabs(*std::max_element(well_perf_fluxes.begin(), well_perf_fluxes.end())));
597  double max_flux = std::max(max_flux_face, max_flux_perf);
598  double max_press = std::max(std::fabs(*std::min_element(cell_pressure_scalar.begin(),
599  cell_pressure_scalar.end())),
600  std::fabs(*std::max_element(cell_pressure_scalar.begin(),
601  cell_pressure_scalar.end())));
602  double flux_change_infnorm = 0.0;
603  double press_change_infnorm = 0.0;
604  for (int face = 0; face < num_faces; ++face) {
605  flux_change_infnorm = std::max(flux_change_infnorm,
606  std::fabs(face_flux[face] - start_face_flux[face]));
607  }
608  for (int perf = 0; perf < num_perf; ++perf) {
609  flux_change_infnorm = std::max(flux_change_infnorm,
610  std::fabs(well_perf_fluxes[perf] - start_perf_flux[perf]));
611  }
612  for (int cell = 0; cell < num_cells; ++cell) {
613  press_change_infnorm = std::max(press_change_infnorm,
614  std::fabs(cell_pressure_scalar[cell] - start_cell_press[cell]));
615  }
616  double flux_rel_difference = flux_change_infnorm/max_flux;
617  double press_rel_difference = press_change_infnorm/max_press;
618  return std::make_pair(flux_rel_difference, press_rel_difference);
619  }
620 
621 
622 
623  // Compute well perforation pressures.
624  void computeWellPerfPressures(const std::vector<double>& well_perf_fluxes,
625  const std::vector<double>& well_bhp,
626  const std::vector<double>& wellperf_gpot,
627  std::vector<double>& well_perf_pressures) const
628  {
629  // Compute averaged saturations for each well. This code
630  // assumes that flow is either in or out of any single
631  // well, not both.
632  int num_perf = well_perf_fluxes.size();
633  std::vector<PhaseVec> well_sat(pwells_->numWells(), PhaseVec(0.0));
634  std::vector<double> well_flux(pwells_->numWells(), 0.0);
635  for (int perf = 0; perf < num_perf; ++perf) {
636  int well = perf_wells_[perf];
637  double flux = well_perf_fluxes[perf];
638  well_flux[well] += flux;
639  PhaseVec tmp = perf_sat_[perf];
640  tmp *= flux;
641  well_sat[well] += tmp;
642  }
643  for (int well = 0; well < pwells_->numWells(); ++well) {
644  well_sat[well] *= 1.0/well_flux[well];
645  }
646 
647  // Compute well_perf_pressures
648  for (int perf = 0; perf < num_perf; ++perf) {
649  well_perf_pressures[perf] = well_bhp[perf_wells_[perf]];
650  PhaseVec sat = well_sat[perf_wells_[perf]];
651  for (int phase = 0; phase < numPhases; ++phase) {
652  well_perf_pressures[perf]
653  += sat[phase]*wellperf_gpot[numPhases*perf + phase];
654  }
655  }
656  }
657 
658 
659 
660  // Sets the initial volume discrepancy, applying relaxation if requested.
661  bool getInitialVolumeDiscrepancy(const double dt, std::vector<double>& voldiscr_initial)
662  {
663  voldiscr_initial = fp_.voldiscr;
664  double rel_voldiscr = *std::max_element(fp_.relvoldiscr.begin(), fp_.relvoldiscr.end());
665  if (rel_voldiscr > max_relative_voldiscr_) {
666  std::cout << "Initial relative volume discrepancy too large: " << rel_voldiscr << std::endl;
667  return false;
668  }
669  if (relax_time_voldiscr_ > 0.0) {
670  double relax = std::min(1.0,dt/relax_time_voldiscr_);
671  std::transform(voldiscr_initial.begin(), voldiscr_initial.end(), voldiscr_initial.begin(),
672  std::binder1st<std::multiplies<double> >(std::multiplies<double>() , relax));
673  }
674  if (voldisc_factor_ != 1.0) {
675  std::transform(voldiscr_initial.begin(), voldiscr_initial.end(), voldiscr_initial.begin(),
676  std::binder1st<std::multiplies<double> >(std::multiplies<double>(), voldisc_factor_));
677  }
678  return true;
679  }
680 
681 
682 
683  // Modifies cell pressures, face pressures and face fluxes of
684  // state such that
685  // state <- weight*state + (1 - weight)*start_state
686  // So weight == 1.0 does nothing.
687  void relaxState(const double weight,
688  const SolverState& start_state,
689  SolverState& state)
690  {
691  int num_cells = pgrid_->numCells();
692  int num_faces = pgrid_->numFaces();
693  for (int cell = 0; cell < num_cells; ++cell) {
694  state.cell_pressure[cell] = weight*state.cell_pressure[cell] + (1.0-weight)*start_state.cell_pressure[cell];
695  }
696  for (int face = 0; face < num_faces; ++face) {
697  state.face_pressure[face] = weight*state.face_pressure[face] + (1.0-weight)*start_state.face_pressure[face];
698  state.face_flux[face] = weight*state.face_flux[face] + (1.0-weight)*start_state.face_flux[face];
699  }
700  }
701 
702 
703 
704  // Implements the main nonlinear loop of the pressure solver.
705  ReturnCode solveImpl(const std::vector<typename FluidInterface::CompVec>& cell_z,
706  const std::vector<double>& src,
707  const double dt,
708  SolverState& state)
709  {
710  int num_cells = pgrid_->numCells();
711  std::vector<double> cell_pressure_initial = state.cell_pressure;
712  std::vector<double> voldiscr_initial;
713  SolverState start_state;
714 
715  // ------------ Main iteration loop -------------
716  for (int iter = 0; iter < max_num_iter_; ++iter) {
717  start_state = state;
718  // (Re-)compute fluid properties.
719  computeFluidPropsScalarPress(state.cell_pressure, state.face_pressure, state.well_perf_pressure, cell_z, dt);
720 
721  // Initialization for the first iteration only.
722  if (iter == 0) {
723  bool voldisc_ok = getInitialVolumeDiscrepancy(dt, voldiscr_initial);
724  if (!voldisc_ok) {
726  }
727 
728  // perf_gpot_ is computed once per pressure solve,
729  // while perf_A_, perf_mob_ are recoomputed
730  // for every iteration.
731  computeWellPotentials(perf_gpot_);
732  }
733 
734  if (experimental_jacobian_) {
735  // Compute residual and jacobian.
736  PressureAssembler::LinearSystem s;
737  std::vector<double> residual;
738  computeResidualJacobian(voldiscr_initial, state.cell_pressure, cell_pressure_initial,
739  state.well_bhp_pressure, src, dt, s, residual);
740 
741  if (output_residual_) {
742  // Temporary hack to get output of residual.
743  static int psolve_iter = -1;
744  if (iter == 0) {
745  ++psolve_iter;
746  }
747  std::ostringstream oss;
748  oss << "residual-" << psolve_iter << '-' << iter << ".dat";
749  std::ofstream outres(oss.str().c_str());
750  std::copy(residual.begin(), residual.end(), std::ostream_iterator<double>(outres, "\n"));
751  }
752 
753  // Solve system for dp, that is, we use residual as the rhs.
754  LinearSolverIstl::LinearSolverReport result
755  = linsolver_->solve(s.n, s.nnz, s.ia, s.ja, s.sa, &residual[0], s.x);
756  if (!result.converged) {
757  OPM_THROW(std::runtime_error, "Linear solver failed to converge in " << result.iterations << " iterations.\n"
758  << "Residual reduction achieved is " << result.residual_reduction << '\n');
759  }
760  // Set x so that the call to computePressuresAndFluxes() will work.
761  // Recall that x now contains dp, and we want it to contain p - dp
762  for (int cell = 0; cell < num_cells; ++cell) {
763  s.x[cell] = state.cell_pressure[cell] - s.x[cell];
764  }
765  for (int well = 0; well < pwells_->numWells(); ++well) {
766  s.x[num_cells + well] = state.well_bhp_pressure[well] - s.x[num_cells + well];
767  }
768  } else {
769  // Assemble system matrix and rhs.
770  psolver_.assemble(&src[0],
771  &bctypes_[0],
772  &bcvalues_[0],
773  dt,
775  &voldiscr_initial[0],
776  &fp_.cell_data.state_matrix[0][0][0],
777  &fp_.face_data.state_matrix[0][0][0],
778  &perf_A_[0],
779  &fp_.face_data.mobility[0][0],
780  &perf_mob_[0],
781  &cell_pressure_initial[0],
782  &fp_.face_data.gravity_potential[0][0],
783  &perf_gpot_[0],
784  &(pfluid_->surfaceDensities()[0]));
785  PressureAssembler::LinearSystem s;
786  psolver_.linearSystem(s);
787  // Solve system.
788  LinearSolverIstl::LinearSolverReport res = linsolver_->solve(s.n, s.nnz, s.ia, s.ja, s.sa, s.b, s.x);
789  if (!res.converged) {
790  OPM_THROW(std::runtime_error, "Linear solver failed to converge in " << res.iterations << " iterations.\n"
791  << "Residual reduction achieved is " << res.residual_reduction << '\n');
792  }
793  }
794 
795  // Get pressures and face fluxes.
796  psolver_.computePressuresAndFluxes(state.cell_pressure, state.face_pressure, state.face_flux,
797  state.well_bhp_pressure, state.well_perf_flux);
798 
799  // Relaxation
800  if (relax_weight_pressure_iteration_ != 1.0) {
801  relaxState(relax_weight_pressure_iteration_, start_state, state);
802  }
803 
804  // Compute state.well_perf_pressure.
805  computeWellPerfPressures(state.well_perf_flux, state.well_bhp_pressure,
806  perf_gpot_, state.well_perf_pressure);
807 
808  // Compute relative changes for pressure and flux.
809  std::pair<double, double> rel_changes
810  = computeFluxPressChanges(state.face_flux, state.well_perf_flux, state.cell_pressure,
811  start_state.face_flux, start_state.well_perf_flux, start_state.cell_pressure);
812  double flux_rel_difference = rel_changes.first;
813  double press_rel_difference = rel_changes.second;
814 
815  // Test for convergence.
816  if (iter == 0) {
817  std::cout << "Iteration Rel. flux change Rel. pressure change\n";
818  }
819  std::cout.precision(5);
820  std::cout << std::setw(6) << iter
821  << std::setw(24) << flux_rel_difference
822  << std::setw(24) << press_rel_difference << std::endl;
823  std::cout.precision(16);
824 
825  if (flux_rel_difference < flux_rel_tol_ || press_rel_difference < press_rel_tol_) {
826  std::cout << "Pressure solver converged. Number of iterations: " << iter + 1 << '\n' << std::endl;
827  return SolveOk;
828  }
829  }
830 
831  return FailedToConverge;
832  }
833 
834 
835 
836 
837  // Implements the main nonlinear loop of the pressure solver.
838  ReturnCode solveImplNew(const std::vector<typename FluidInterface::CompVec>& cell_z,
839  const std::vector<double>& src,
840  const double dt,
841  SolverState& state)
842  {
843  int num_cells = pgrid_->numCells();
844  int num_faces = pgrid_->numFaces();
845  std::vector<double> cell_pressure_initial = state.cell_pressure;
846  std::vector<double> voldiscr_initial;
847  SolverState start_state;
848 
849  // ------------ Main iteration loop -------------
850  for (int iter = 0; iter < max_num_iter_; ++iter) {
851  start_state = state;
852  // (Re-)compute fluid properties.
853  computeFluidPropsScalarPress(state.cell_pressure, state.face_pressure, state.well_perf_pressure, cell_z, dt);
854 
855  // Initialization for the first iteration only.
856  if (iter == 0) {
857  bool voldisc_ok = getInitialVolumeDiscrepancy(dt, voldiscr_initial);
858  if (!voldisc_ok) {
860  }
861 
862  // perf_gpot_ is computed once per pressure solve,
863  // while perf_A_, perf_mob_ are recoomputed
864  // for every iteration.
865  computeWellPotentials(perf_gpot_);
866  }
867 
868  // Compute residual and jacobian.
869  PressureAssembler::LinearSystem s;
870  std::vector<double> residual;
871  computeResidualJacobian(voldiscr_initial, state.cell_pressure, cell_pressure_initial,
872  state.well_bhp_pressure, src, dt, s, residual);
873  if (output_residual_) {
874  // Temporary hack to get output of residual.
875  static int psolve_iter = -1;
876  if (iter == 0) {
877  ++psolve_iter;
878  }
879  std::ostringstream oss;
880  oss << "residual-" << psolve_iter << '-' << iter << ".dat";
881  std::ofstream outres(oss.str().c_str());
882  std::copy(residual.begin(), residual.end(), std::ostream_iterator<double>(outres, "\n"));
883  }
884 
885  // Find the maxnorm of the residual.
886  double maxres = std::max(std::fabs(*std::max_element(residual.begin(), residual.end())),
887  std::fabs(*std::min_element(residual.begin(), residual.end())));
888 
889  // Solve system for dp, that is, we use residual as the rhs.
890  LinearSolverIstl::LinearSolverReport result
891  = linsolver_->solve(s.n, s.nnz, s.ia, s.ja, s.sa, &residual[0], s.x);
892  if (!result.converged) {
893  OPM_THROW(std::runtime_error, "Linear solver failed to converge in " << result.iterations << " iterations.\n"
894  << "Residual reduction achieved is " << result.residual_reduction << '\n');
895  }
896 
897  // Set x so that the call to computePressuresAndFluxes() will work.
898  // Recall that x now contains dp, and we want it to contain p - dp
899  for (int cell = 0; cell < num_cells; ++cell) {
900  s.x[cell] = state.cell_pressure[cell] - s.x[cell];
901  }
902  for (int well = 0; well < pwells_->numWells(); ++well) {
903  s.x[num_cells + well] = state.well_bhp_pressure[well] - s.x[num_cells + well];
904  }
905 
906  // Get pressures and face fluxes.
907  psolver_.computePressuresAndFluxes(state.cell_pressure, state.face_pressure, state.face_flux,
908  state.well_bhp_pressure, state.well_perf_flux);
909 
910  // Relaxation
911  if (relax_weight_pressure_iteration_ != 1.0) {
912  double ww = relax_weight_pressure_iteration_;
913  for (int cell = 0; cell < num_cells; ++cell) {
914  state.cell_pressure[cell] = ww*state.cell_pressure[cell] + (1.0-ww)*start_state.cell_pressure[cell];
915  }
916  if (iter > 0) {
917  for (int face = 0; face < num_faces; ++face) {
918  state.face_pressure[face] = ww*state.face_pressure[face] + (1.0-ww)*start_state.face_pressure[face];
919  state.face_flux[face] = ww*state.face_flux[face] + (1.0-ww)*start_state.face_flux[face];
920  }
921  }
922  }
923 
924  // Compute state.well_perf_pressure.
925  computeWellPerfPressures(state.well_perf_flux, state.well_bhp_pressure,
926  perf_gpot_, state.well_perf_pressure);
927 
928  // Compute relative changes for pressure and flux.
929  std::pair<double, double> rel_changes
930  = computeFluxPressChanges(state.face_flux, state.well_perf_flux, state.cell_pressure,
931  start_state.face_flux, start_state.well_perf_flux, start_state.cell_pressure);
932  double flux_rel_difference = rel_changes.first;
933  double press_rel_difference = rel_changes.second;
934 
935  // Test for convergence.
936  if (iter == 0) {
937  std::cout << "Iteration Rel. flux change Rel. pressure change Residual\n";
938  }
939  std::cout.precision(5);
940  std::cout << std::setw(6) << iter
941  << std::setw(24) << flux_rel_difference
942  << std::setw(24) << press_rel_difference
943  << std::setw(24) << maxres << std::endl;
944  std::cout.precision(16);
945 
946  if (maxres < nonlinear_residual_tolerance_) {
947  std::cout << "Pressure solver converged. Number of iterations: " << iter + 1 << '\n' << std::endl;
948  return SolveOk;
949  }
950  }
951 
952  return FailedToConverge;
953  }
954 
955 
956  };
957 
958 
959 } // namespace Opm
960 
961 
962 
963 #endif // OPM_TPFACOMPRESSIBLE_HEADER_INCLUDED
double stableStepIMPES()
Call this function after solve().
Definition: TpfaCompressible.hpp:352
Definition: TpfaCompressibleAssembler.hpp:137
bool isNeumann() const
Type query.
Definition: BoundaryConditions.hpp:91
const std::vector< double > & faceTransmissibilities()
Definition: TpfaCompressible.hpp:224
std::vector< Scalar > total_compressibility
Definition: FluidStateBlackoil.hpp:78
std::vector< Scalar > experimental_term
Definition: FluidStateBlackoil.hpp:79
Definition: BlackoilFluid.hpp:31
TpfaCompressibleAssembler PressureAssembler
Definition: TpfaCompressible.hpp:48
Definition: TpfaCompressible.hpp:249
TpfaCompressible()
Default constructor. Does nothing.
Definition: TpfaCompressible.hpp:52
const std::vector< double > & faceTransmissibilities() const
Definition: TpfaCompressibleAssembler.hpp:389
ReturnCode
Definition: TpfaCompressible.hpp:249
std::vector< PhaseVec > gravity_potential
Definition: BlackoilFluid.hpp:401
std::vector< PhaseJacobian > mobility_deriv
Definition: BlackoilFluid.hpp:398
void init(const Grid &grid, const Wells &wells, const double *perm, const double *porosity, const typename Grid::Vector &gravity)
Initialize the solver's structures for a given grid, for well setup also call initWells().
Definition: TpfaCompressibleAssembler.hpp:73
void init(const Opm::parameter::ParameterGroup &param)
Initializes run-time parameters of the solver.
Definition: TpfaCompressible.hpp:60
std::vector< PhaseVec > mobility
Definition: BlackoilFluid.hpp:397
std::vector< double > relvoldiscr
Definition: BlackoilFluid.hpp:410
ReturnCode solve(std::vector< typename FluidInterface::PhaseVec > &cell_pressure, std::vector< typename FluidInterface::PhaseVec > &face_pressure, const std::vector< typename FluidInterface::CompVec > &cell_z, std::vector< double > &face_flux, std::vector< double > &well_bhp_pressures, std::vector< double > &well_perf_pressures, std::vector< double > &well_perf_fluxes, const std::vector< double > &src, const double dt)
Construct and solve system of linear equations for the phase pressure values on cells and faces...
Definition: TpfaCompressible.hpp:290
FaceFluidData face_data
Definition: BlackoilFluid.hpp:413
double explicitTimestepLimit(const double *faceA, const double *phasemobf, const double *phasemobf_deriv, const double *surf_dens)
Explicit IMPES time step limit.
Definition: TpfaCompressibleAssembler.hpp:310
bool isDirichlet() const
Type query.
Definition: BoundaryConditions.hpp:85
Definition: TpfaCompressible.hpp:249
Definition: TpfaCompressible.hpp:249
std::vector< double > voldiscr
Definition: BlackoilFluid.hpp:409
AllFluidStates cell_data
Definition: BlackoilFluid.hpp:408
void doStepIMPES(std::vector< typename FluidInterface::CompVec > &cell_z, const double dt)
Do an IMPES step using the facilities of the pressure solver.
Definition: TpfaCompressible.hpp:363
Definition: BlackoilFluid.hpp:405
double pressure() const
Query a Dirichlet condition.
Definition: BoundaryConditions.hpp:145
void setup(const GridInterface &grid, const RockInterface &rock, const FluidInterface &fluid, const WellsInterface &wells, const typename GridInterface::Vector &grav, const BCInterface &bc, const std::vector< typename FluidInterface::PhaseVec > *face_pressure=0)
Setup routine, does grid/rock-dependent initialization.
Definition: TpfaCompressible.hpp:137
double outflux() const
Query a Neumann condition.
Definition: BoundaryConditions.hpp:154
A class for representing a flow boundary condition.
Definition: BoundaryConditions.hpp:121
Definition: TpfaCompressibleAssembler.hpp:136
void explicitTransport(const double dt, double *cell_surfvols)
Explicit IMPES transport.
Definition: TpfaCompressibleAssembler.hpp:329
Definition: TpfaCompressibleAssembler.hpp:138
std::vector< PhaseToCompMatrix > state_matrix
Definition: FluidStateBlackoil.hpp:73
bool volumeDiscrepancyAcceptable(const std::vector< typename FluidInterface::PhaseVec > &cell_pressure, const std::vector< typename FluidInterface::PhaseVec > &face_pressure, const std::vector< double > &well_perf_pressure, const std::vector< typename FluidInterface::CompVec > &cell_z, const double dt)
Definition: TpfaCompressible.hpp:232
double volumeDiscrepancyLimit() const
Definition: TpfaCompressible.hpp:216
std::vector< PhaseToCompMatrix > state_matrix
Definition: BlackoilFluid.hpp:394
void computeNew(const Grid &grid, const Rock &rock, const BlackoilFluid &fluid, const typename Grid::Vector gravity, const std::vector< PhaseVec > &cell_pressure, const std::vector< PhaseVec > &face_pressure, const std::vector< CompVec > &cell_z, const CompVec &bdy_z, const double dt)
Definition: BlackoilFluid.hpp:417
std::vector< Scalar > total_phase_volume_density
Definition: FluidStateBlackoil.hpp:75
FluidInterface::CompVec inflowMixture() const
Accessor for the inflow mixture.
Definition: TpfaCompressible.hpp:101
Encapsulates the cfs_tpfa (= compressible flow solver two-point flux approximation) solver modules...
Definition: TpfaCompressibleAssembler.hpp:38
Definition: TpfaCompressible.hpp:45
void zero(Matrix &A)
Zero-fill a.
Definition: Matrix.hpp:603