opm-simulators
blackoilsolventmodules.hh
Go to the documentation of this file.
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
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 2 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  Consult the COPYING file in the top-level source directory of this
20  module for the precise wording of the license and the list of
21  copyright holders.
22 */
28 #ifndef EWOMS_BLACK_OIL_SOLVENT_MODULE_HH
29 #define EWOMS_BLACK_OIL_SOLVENT_MODULE_HH
30 
31 #include <dune/common/fvector.hh>
32 
33 #include <opm/common/Exceptions.hpp>
34 #include <opm/common/utility/gpuDecorators.hpp>
35 
36 #include <opm/material/common/MathToolbox.hpp>
37 #include <opm/material/common/Valgrind.hpp>
38 #include <opm/material/fluidsystems/blackoilpvt/SolventPvt.hpp>
39 
43 
46 
48 
50 
51 #include <algorithm>
52 #include <array>
53 #include <cassert>
54 #include <cmath>
55 #include <istream>
56 #include <memory>
57 #include <ostream>
58 #include <string>
59 
60 namespace Opm {
61 
67 template <class TypeTag>
68 class BlackOilSolventModule<TypeTag, true>
69 {
81  using Toolbox = MathToolbox<Evaluation>;
82  using SolventPvt = typename BlackOilSolventParams<Scalar>::SolventPvt;
83  using Co2GasPvt = typename BlackOilSolventParams<Scalar>::Co2GasPvt;
84  using H2GasPvt = typename BlackOilSolventParams<Scalar>::H2GasPvt;
85  using BrineCo2Pvt = typename BlackOilSolventParams<Scalar>::BrineCo2Pvt;
86  using BrineH2Pvt = typename BlackOilSolventParams<Scalar>::BrineH2Pvt;
87 
88  using TabulatedFunction = typename BlackOilSolventParams<Scalar>::TabulatedFunction;
89 
90  static constexpr unsigned solventSaturationIdx = Indices::solventSaturationIdx;
91  static constexpr unsigned contiSolventEqIdx = Indices::contiSolventEqIdx;
92  static constexpr unsigned enableSolvent = true;
93  static constexpr unsigned numEq = getPropValue<TypeTag, Properties::NumEq>();
94  static constexpr bool blackoilConserveSurfaceVolume =
95  getPropValue<TypeTag, Properties::BlackoilConserveSurfaceVolume>();
96  static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx;
97 
98 public:
99  static constexpr double cutOff = 1e-12;
102  { params_ = params; }
103 
107  static void setSolventPvt(const SolventPvt& value)
108  { params_.solventPvt_ = value; }
109 
110  static void setIsMiscible(const bool isMiscible)
111  { params_.isMiscible_ = isMiscible; }
112 
116  static void registerParameters()
117  {
119  }
120 
124  static void registerOutputModules(Model& model,
125  Simulator& simulator)
126  {
127  model.addOutputModule(std::make_unique<VtkBlackOilSolventModule<TypeTag>>(simulator));
128  }
129 
130  static bool primaryVarApplies(unsigned pvIdx)
131  {
132  return pvIdx == solventSaturationIdx;
133  }
134 
135  static std::string primaryVarName([[maybe_unused]] unsigned pvIdx)
136  {
137  assert(primaryVarApplies(pvIdx));
138 
139  return "saturation_solvent";
140  }
141 
142  static Scalar primaryVarWeight([[maybe_unused]] unsigned pvIdx)
143  {
144  assert(primaryVarApplies(pvIdx));
145 
146  // TODO: it may be beneficial to chose this differently.
147  return static_cast<Scalar>(1.0);
148  }
149 
150  static bool eqApplies(unsigned eqIdx)
151  {
152  return eqIdx == contiSolventEqIdx;
153  }
154 
155  static std::string eqName([[maybe_unused]] unsigned eqIdx)
156  {
157  assert(eqApplies(eqIdx));
158 
159  return "conti^solvent";
160  }
161 
162  static Scalar eqWeight([[maybe_unused]] unsigned eqIdx)
163  {
164  assert(eqApplies(eqIdx));
165 
166  // TODO: it may be beneficial to chose this differently.
167  return static_cast<Scalar>(1.0);
168  }
169 
170  template <class StorageType>
171  OPM_HOST_DEVICE static void addStorage(StorageType& storage,
172  const IntensiveQuantities& intQuants)
173  {
174  using LhsEval = typename StorageType::value_type;
175 
176  if constexpr (blackoilConserveSurfaceVolume) {
177  storage[contiSolventEqIdx] +=
178  Toolbox::template decay<LhsEval>(intQuants.porosity()) *
179  Toolbox::template decay<LhsEval>(intQuants.solventSaturation()) *
180  Toolbox::template decay<LhsEval>(intQuants.solventInverseFormationVolumeFactor());
181  if (isSolubleInWater()) {
182  storage[contiSolventEqIdx] +=
183  Toolbox::template decay<LhsEval>(intQuants.porosity()) *
184  Toolbox::template decay<LhsEval>(intQuants.fluidState().saturation(waterPhaseIdx)) *
185  Toolbox::template decay<LhsEval>(intQuants.fluidState().invB(waterPhaseIdx)) *
186  Toolbox::template decay<LhsEval>(intQuants.rsSolw());
187  }
188  }
189  else {
190  storage[contiSolventEqIdx] +=
191  Toolbox::template decay<LhsEval>(intQuants.porosity()) *
192  Toolbox::template decay<LhsEval>(intQuants.solventSaturation()) *
193  Toolbox::template decay<LhsEval>(intQuants.solventDensity());
194  if (isSolubleInWater()) {
195  storage[contiSolventEqIdx] +=
196  Toolbox::template decay<LhsEval>(intQuants.porosity()) *
197  Toolbox::template decay<LhsEval>(intQuants.fluidState().saturation(waterPhaseIdx)) *
198  Toolbox::template decay<LhsEval>(intQuants.fluidState().density(waterPhaseIdx)) *
199  Toolbox::template decay<LhsEval>(intQuants.rsSolw());
200  }
201  }
202  }
203 
204  static void computeFlux(RateVector& flux,
205  const ElementContext& elemCtx,
206  unsigned scvfIdx,
207  unsigned timeIdx)
208 
209  {
210  const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
211 
212  const unsigned upIdx = extQuants.solventUpstreamIndex();
213  const unsigned inIdx = extQuants.interiorIndex();
214  const auto& up = elemCtx.intensiveQuantities(upIdx, timeIdx);
215 
216  if constexpr (blackoilConserveSurfaceVolume) {
217  if (upIdx == inIdx) {
218  flux[contiSolventEqIdx] = extQuants.solventVolumeFlux() *
219  up.solventInverseFormationVolumeFactor();
220  }
221  else {
222  flux[contiSolventEqIdx] = extQuants.solventVolumeFlux() *
223  decay<Scalar>(up.solventInverseFormationVolumeFactor());
224  }
225 
226 
227  if (isSolubleInWater()) {
228  if (upIdx == inIdx) {
229  flux[contiSolventEqIdx] +=
230  extQuants.volumeFlux(waterPhaseIdx) *
231  up.fluidState().invB(waterPhaseIdx) *
232  up.rsSolw();
233  }
234  else {
235  flux[contiSolventEqIdx] +=
236  extQuants.volumeFlux(waterPhaseIdx) *
237  decay<Scalar>(up.fluidState().invB(waterPhaseIdx)) *
238  decay<Scalar>(up.rsSolw());
239  }
240  }
241  }
242  else {
243  if (upIdx == inIdx) {
244  flux[contiSolventEqIdx] = extQuants.solventVolumeFlux() * up.solventDensity();
245  }
246  else {
247  flux[contiSolventEqIdx] = extQuants.solventVolumeFlux() * decay<Scalar>(up.solventDensity());
248  }
249 
250  if (isSolubleInWater()) {
251  if (upIdx == inIdx) {
252  flux[contiSolventEqIdx] +=
253  extQuants.volumeFlux(waterPhaseIdx) *
254  up.fluidState().density(waterPhaseIdx) *
255  up.rsSolw();
256  }
257  else {
258  flux[contiSolventEqIdx] +=
259  extQuants.volumeFlux(waterPhaseIdx) *
260  decay<Scalar>(up.fluidState().density(waterPhaseIdx)) *
261  decay<Scalar>(up.rsSolw());
262  }
263  }
264  }
265  }
266 
270  static void assignPrimaryVars(PrimaryVariables& priVars,
271  Scalar solventSaturation,
272  Scalar solventRsw)
273  {
274  // Determine the meaning of the solvent primary variables
275  if (solventSaturation > 0 || !isSolubleInWater()) {
276  priVars.setPrimaryVarsMeaningSolvent(PrimaryVariables::SolventMeaning::Ss);
277  priVars[solventSaturationIdx] = solventSaturation;
278  } else {
279  priVars.setPrimaryVarsMeaningSolvent(PrimaryVariables::SolventMeaning::Rsolw);
280  priVars[solventSaturationIdx] = solventRsw;
281  }
282  }
283 
287  static void updatePrimaryVars(PrimaryVariables& newPv,
288  const PrimaryVariables& oldPv,
289  const EqVector& delta)
290  {
291  // do a plain unchopped Newton update
292  newPv[solventSaturationIdx] = oldPv[solventSaturationIdx] - delta[solventSaturationIdx];
293  }
294 
298  static Scalar computeUpdateError(const PrimaryVariables&,
299  const EqVector&)
300  {
301  // do not consider consider the cange of solvent primary variables for
302  // convergence
303  // TODO: maybe this should be changed
304  return static_cast<Scalar>(0.0);
305  }
306 
310  static Scalar computeResidualError(const EqVector& resid)
311  {
312  // do not weight the residual of solvents when it comes to convergence
313  return std::abs(Toolbox::scalarValue(resid[contiSolventEqIdx]));
314  }
315 
316  template <class DofEntity>
317  static void serializeEntity(const Model& model, std::ostream& outstream, const DofEntity& dof)
318  {
319  const unsigned dofIdx = model.dofMapper().index(dof);
320 
321  const PrimaryVariables& priVars = model.solution(/*timeIdx=*/0)[dofIdx];
322  outstream << priVars[solventSaturationIdx];
323  }
324 
325  template <class DofEntity>
326  static void deserializeEntity(Model& model, std::istream& instream, const DofEntity& dof)
327  {
328  const unsigned dofIdx = model.dofMapper().index(dof);
329 
330  PrimaryVariables& priVars0 = model.solution(/*timeIdx=*/0)[dofIdx];
331  PrimaryVariables& priVars1 = model.solution(/*timeIdx=*/1)[dofIdx];
332 
333  instream >> priVars0[solventSaturationIdx];
334 
335  // set the primary variables for the beginning of the current time step.
336  priVars1 = priVars0[solventSaturationIdx];
337  }
338 
339  static const SolventPvt& solventPvt()
340  { return params_.solventPvt_; }
341 
342  static const Co2GasPvt& co2GasPvt()
343  { return params_.co2GasPvt_; }
344 
345  static const H2GasPvt& h2GasPvt()
346  { return params_.h2GasPvt_; }
347 
348  static const BrineCo2Pvt& brineCo2Pvt()
349  { return params_.brineCo2Pvt_; }
350 
351  static const BrineH2Pvt& brineH2Pvt()
352  { return params_.brineH2Pvt_; }
353 
354  template <class ElemContext>
355  static const TabulatedFunction& ssfnKrg(const ElemContext& elemCtx,
356  unsigned scvIdx,
357  unsigned timeIdx)
358  {
359  const unsigned satnumRegionIdx =
360  elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
361  return params_.ssfnKrg_[satnumRegionIdx];
362  }
363 
364  template <class ElemContext>
365  static const TabulatedFunction& ssfnKrs(const ElemContext& elemCtx,
366  unsigned scvIdx,
367  unsigned timeIdx)
368  {
369  const unsigned satnumRegionIdx =
370  elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
371  return params_.ssfnKrs_[satnumRegionIdx];
372  }
373 
374  static const TabulatedFunction& ssfnKrg(const unsigned satnumRegionIdx)
375  {
376  return params_.ssfnKrg_[satnumRegionIdx];
377  }
378 
379  static const TabulatedFunction& ssfnKrs(const unsigned satnumRegionIdx)
380  {
381  return params_.ssfnKrs_[satnumRegionIdx];
382  }
383 
384  template <class ElemContext>
385  static const TabulatedFunction& sof2Krn(const ElemContext& elemCtx,
386  unsigned scvIdx,
387  unsigned timeIdx)
388  {
389  const unsigned satnumRegionIdx =
390  elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
391  return params_.sof2Krn_[satnumRegionIdx];
392  }
393 
394  template <class ElemContext>
395  static const TabulatedFunction& misc(const ElemContext& elemCtx,
396  unsigned scvIdx,
397  unsigned timeIdx)
398  {
399  const unsigned miscnumRegionIdx =
400  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
401  return params_.misc_[miscnumRegionIdx];
402  }
403 
404  template <class ElemContext>
405  static const TabulatedFunction& pmisc(const ElemContext& elemCtx,
406  unsigned scvIdx,
407  unsigned timeIdx)
408  {
409  const unsigned miscnumRegionIdx =
410  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
411  return params_.pmisc_[miscnumRegionIdx];
412  }
413 
414  template <class ElemContext>
415  static const TabulatedFunction& msfnKrsg(const ElemContext& elemCtx,
416  unsigned scvIdx,
417  unsigned timeIdx)
418  {
419  const unsigned satnumRegionIdx =
420  elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
421  return params_.msfnKrsg_[satnumRegionIdx];
422  }
423 
424  template <class ElemContext>
425  static const TabulatedFunction& msfnKro(const ElemContext& elemCtx,
426  unsigned scvIdx,
427  unsigned timeIdx)
428  {
429  const unsigned satnumRegionIdx =
430  elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
431  return params_.msfnKro_[satnumRegionIdx];
432  }
433 
434  template <class ElemContext>
435  static const TabulatedFunction& sorwmis(const ElemContext& elemCtx,
436  unsigned scvIdx,
437  unsigned timeIdx)
438  {
439  const unsigned miscnumRegionIdx =
440  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
441  return params_.sorwmis_[miscnumRegionIdx];
442  }
443 
444  template <class ElemContext>
445  static const TabulatedFunction& sgcwmis(const ElemContext& elemCtx,
446  unsigned scvIdx,
447  unsigned timeIdx)
448  {
449  const unsigned miscnumRegionIdx =
450  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
451  return params_.sgcwmis_[miscnumRegionIdx];
452  }
453 
454  template <class ElemContext>
455  static const TabulatedFunction& tlPMixTable(const ElemContext& elemCtx,
456  unsigned scvIdx,
457  unsigned timeIdx)
458  {
459  const unsigned miscnumRegionIdx =
460  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
461  return params_.tlPMixTable_[miscnumRegionIdx];
462  }
463 
464  template <class ElemContext>
465  static Scalar tlMixParamViscosity(const ElemContext& elemCtx,
466  unsigned scvIdx,
467  unsigned timeIdx)
468  {
469  const unsigned miscnumRegionIdx =
470  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
471  return params_.tlMixParamViscosity_[miscnumRegionIdx];
472  }
473 
474 
475  template <class ElemContext>
476  static Scalar tlMixParamDensity(const ElemContext& elemCtx,
477  unsigned scvIdx,
478  unsigned timeIdx)
479  {
480  const unsigned miscnumRegionIdx =
481  elemCtx.problem().miscnumRegionIndex(elemCtx, scvIdx, timeIdx);
482  return params_.tlMixParamDensity_[miscnumRegionIdx];
483  }
484 
485  static bool isMiscible()
486  { return params_.isMiscible_; }
487 
488  template <class Value>
489  static Value solubilityLimit(unsigned pvtIdx, const Value& temperature,
490  const Value& pressure, const Value& saltConcentration)
491  {
492  if (!isSolubleInWater()) {
493  return 0.0;
494  }
495 
496  assert(isCO2Sol() || isH2Sol());
497  if (isCO2Sol()) {
498  return brineCo2Pvt().saturatedGasDissolutionFactor(pvtIdx, temperature,
499  pressure, saltConcentration);
500  }
501  else {
502  return brineH2Pvt().saturatedGasDissolutionFactor(pvtIdx, temperature,
503  pressure, saltConcentration);
504  }
505  }
506 
507  static bool isSolubleInWater()
508  { return params_.rsSolw_active_; }
509 
510  static bool isCO2Sol()
511  { return params_.co2sol_; }
512 
513  static bool isH2Sol()
514  { return params_.h2sol_; }
515 
516 private:
517  static BlackOilSolventParams<Scalar> params_; // the krg(Fs) column of the SSFN table
518 };
519 
520 template <class TypeTag>
521 BlackOilSolventParams<typename BlackOilSolventModule<TypeTag, true>::Scalar>
522 BlackOilSolventModule<TypeTag, true>::params_;
523 
531 template <class TypeTag>
532 class BlackOilSolventIntensiveQuantities<TypeTag, /*enableSolventV=*/true>
533 {
535 
544 
546 
547  enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
548  static constexpr unsigned solventSaturationIdx = Indices::solventSaturationIdx;
549  static constexpr int oilPhaseIdx = FluidSystem::oilPhaseIdx;
550  static constexpr int gasPhaseIdx = FluidSystem::gasPhaseIdx;
551  static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx;
552  static constexpr double cutOff = SolventModule::cutOff;
553 
554 public:
555 
562  void solventPreSatFuncUpdate_(const ElementContext& elemCtx,
563  unsigned dofIdx,
564  unsigned timeIdx)
565  {
566  const PrimaryVariables& priVars = elemCtx.primaryVars(dofIdx, timeIdx);
567  this->solventPreSatFuncUpdate_(priVars, timeIdx, elemCtx.linearizationType());
568  }
569 
570  void solventPreSatFuncUpdate_(const PrimaryVariables& priVars,
571  const unsigned timeIdx,
572  const LinearizationType linearizationType)
573  {
574  auto& fs = asImp_().fluidState_;
575  Evaluation solventSat{0.0};
576  if (priVars.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss) {
577  solventSat = priVars.makeEvaluation(solventSaturationIdx, timeIdx, linearizationType);
578  }
579 
580  fs.setSolventSaturation(solventSat);
581 
582  hydrocarbonSaturation_ = fs.saturation(gasPhaseIdx);
583 
584  // apply a cut-off. Don't waste calculations if no solvent
585  if (solventSaturation().value() < cutOff) {
586  return;
587  }
588 
589  // make the saturation of the gas phase which is used by the saturation functions
590  // the sum of the solvent "saturation" and the saturation the hydrocarbon gas.
591  fs.setSaturation(gasPhaseIdx, hydrocarbonSaturation_ + solventSat);
592  }
593 
601  void solventPostSatFuncUpdate_(const ElementContext& elemCtx,
602  unsigned dofIdx,
603  unsigned timeIdx)
604  {
605  const PrimaryVariables& priVars = elemCtx.primaryVars(dofIdx, timeIdx);
606  this->solventPostSatFuncUpdate_(elemCtx.problem(),
607  priVars,
608  elemCtx.globalSpaceIndex(dofIdx, timeIdx),
609  timeIdx,
610  elemCtx.linearizationType());
611  }
612 
613 private:
614  // This class is a private implementation detail.
615  template <class Problem>
616  struct ProblemAndCellIndexOnlyContext
617  {
618  const Problem& problem_;
619  unsigned int index_;
620  const Problem& problem() const { return problem_; }
621  unsigned int globalSpaceIndex([[maybe_unused]] const unsigned int spaceIdx,
622  [[maybe_unused]] const unsigned int timeIdx) const
623  {
624  return index_;
625  }
626  };
627 
628 public:
629  void solventPostSatFuncUpdate_(const Problem& problem,
630  const PrimaryVariables& priVars,
631  const unsigned globalSpaceIdx,
632  const unsigned timeIdx,
633  const LinearizationType linearizationType)
634  {
635  // revert the gas "saturation" of the fluid state back to the saturation of the
636  // hydrocarbon gas.
637  auto& fs = asImp_().fluidState_;
638  fs.setSaturation(gasPhaseIdx, hydrocarbonSaturation_);
639 
640  // update rsSolw. This needs to be done after the pressure is defined in the fluid state.
641  Evaluation rsSolw{0.0};
642  if (priVars.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss) {
643  rsSolw = SolventModule::solubilityLimit(asImp_().pvtRegionIndex(),
644  fs.temperature(waterPhaseIdx),
645  fs.pressure(waterPhaseIdx),
646  fs.saltConcentration());
647  }
648  else if (priVars.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Rsolw) {
649  rsSolw = priVars.makeEvaluation(solventSaturationIdx, timeIdx, linearizationType);
650  }
651 
652  fs.setRsSolw(rsSolw);
653 
654  solventMobility_ = 0.0;
655 
656  // apply a cut-off. Don't waste calculations if no solvent
657  if (solventSaturation().value() < cutOff) {
658  return;
659  }
660 
661  ProblemAndCellIndexOnlyContext<Problem> elemCtx{problem, globalSpaceIdx};
662  unsigned dofIdx = 0; // Dummy
663 
664  // Pressure effects on capillary pressure miscibility
665  if (SolventModule::isMiscible()) {
666  const Evaluation& p =
667  FluidSystem::phaseIsActive(oilPhaseIdx)
668  ? fs.pressure(oilPhaseIdx)
669  : fs.pressure(gasPhaseIdx);
670  const Evaluation pmisc =
671  SolventModule::pmisc(elemCtx, dofIdx, timeIdx).eval(p, /*extrapolate=*/true);
672  const Evaluation& pgImisc = fs.pressure(gasPhaseIdx);
673 
674  // compute capillary pressure for miscible fluid
675  Evaluation pgMisc = 0.0;
676  std::array<Evaluation, numPhases> pC;
677  const auto& materialParams = problem.materialLawParams(elemCtx, dofIdx, timeIdx);
678  MaterialLaw::capillaryPressures(pC, materialParams, fs);
679 
680  // oil is the reference phase for pressure
681  if (priVars.primaryVarsMeaningPressure() == PrimaryVariables::PressureMeaning::Pg) {
682  pgMisc = priVars.makeEvaluation(Indices::pressureSwitchIdx, timeIdx,
683  linearizationType);
684  }
685  else {
686  const Evaluation& po =
687  priVars.makeEvaluation(Indices::pressureSwitchIdx,
688  timeIdx, linearizationType);
689  pgMisc = po + (pC[gasPhaseIdx] - pC[oilPhaseIdx]);
690  }
691 
692  fs.setPressure(gasPhaseIdx, pmisc * pgMisc + (1.0 - pmisc) * pgImisc);
693  }
694 
695  const Evaluation gasSolventSat = hydrocarbonSaturation_ + solventSaturation();
696 
697  if (gasSolventSat.value() < cutOff) { // avoid division by zero
698  return;
699  }
700 
701  const Evaluation Fhydgas = hydrocarbonSaturation_ / gasSolventSat;
702  const Evaluation Fsolgas = solventSaturation() / gasSolventSat;
703 
704  // account for miscibility of oil and solvent
705  if (SolventModule::isMiscible() && FluidSystem::phaseIsActive(oilPhaseIdx)) {
706  const auto& misc = SolventModule::misc(elemCtx, dofIdx, timeIdx);
707  const auto& pmisc = SolventModule::pmisc(elemCtx, dofIdx, timeIdx);
708  const Evaluation& p =
709  FluidSystem::phaseIsActive(oilPhaseIdx)
710  ? fs.pressure(oilPhaseIdx)
711  : fs.pressure(gasPhaseIdx);
712  const Evaluation miscibility =
713  misc.eval(Fsolgas, /*extrapolate=*/true) *
714  pmisc.eval(p, /*extrapolate=*/true);
715 
716  // TODO adjust endpoints of sn and ssg
717  const unsigned cellIdx = elemCtx.globalSpaceIndex(dofIdx, timeIdx);
718  const auto& materialLawManager = elemCtx.problem().materialLawManager();
719  const auto& scaledDrainageInfo =
720  materialLawManager->oilWaterScaledEpsInfoDrainage(cellIdx);
721 
722  const Scalar sogcr = scaledDrainageInfo.Sogcr;
723  Evaluation sor = sogcr;
724  if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
725  const Evaluation& sw = fs.saturation(waterPhaseIdx);
726  const auto& sorwmis = SolventModule::sorwmis(elemCtx, dofIdx, timeIdx);
727  sor = miscibility *
728  sorwmis.eval(sw, /*extrapolate=*/true) + (1.0 - miscibility) * sogcr;
729  }
730  const Scalar sgcr = scaledDrainageInfo.Sgcr;
731  Evaluation sgc = sgcr;
732  if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
733  const Evaluation& sw = fs.saturation(waterPhaseIdx);
734  const auto& sgcwmis = SolventModule::sgcwmis(elemCtx, dofIdx, timeIdx);
735  sgc = miscibility * sgcwmis.eval(sw, /*extrapolate=*/true) + (1.0 - miscibility) * sgcr;
736  }
737 
738  Evaluation oilGasSolventSat = gasSolventSat;
739  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
740  oilGasSolventSat += fs.saturation(oilPhaseIdx);
741  }
742  const Evaluation zero = 0.0;
743  const Evaluation oilGasSolventEffSat = std::max(oilGasSolventSat - sor - sgc, zero);
744 
745  Evaluation F_totalGas = 0.0;
746  if (oilGasSolventEffSat.value() > cutOff) {
747  const Evaluation gasSolventEffSat = std::max(gasSolventSat - sgc, zero);
748  F_totalGas = gasSolventEffSat / oilGasSolventEffSat;
749  }
750  const auto& msfnKro = SolventModule::msfnKro(elemCtx, dofIdx, timeIdx);
751  const auto& msfnKrsg = SolventModule::msfnKrsg(elemCtx, dofIdx, timeIdx);
752  const auto& sof2Krn = SolventModule::sof2Krn(elemCtx, dofIdx, timeIdx);
753 
754  const Evaluation mkrgt = msfnKrsg.eval(F_totalGas, /*extrapolate=*/true) *
755  sof2Krn.eval(oilGasSolventSat, /*extrapolate=*/true);
756  const Evaluation mkro = msfnKro.eval(F_totalGas, /*extrapolate=*/true) *
757  sof2Krn.eval(oilGasSolventSat, /*extrapolate=*/true);
758 
759  Evaluation& kro = asImp_().mobility_[oilPhaseIdx];
760  Evaluation& krg = asImp_().mobility_[gasPhaseIdx];
761 
762  // combine immiscible and miscible part of the relperm
763  krg *= 1.0 - miscibility;
764  krg += miscibility * mkrgt;
765  kro *= 1.0 - miscibility;
766  kro += miscibility * mkro;
767  }
768 
769  // compute the mobility of the solvent "phase" and modify the gas phase
770  const auto& ssfnKrg = SolventModule::ssfnKrg(elemCtx, dofIdx, timeIdx);
771  const auto& ssfnKrs = SolventModule::ssfnKrs(elemCtx, dofIdx, timeIdx);
772 
773  Evaluation& krg = asImp_().mobility_[gasPhaseIdx];
774  solventMobility_ = krg * ssfnKrs.eval(Fsolgas, /*extrapolate=*/true);
775  krg *= ssfnKrg.eval(Fhydgas, /*extrapolate=*/true);
776  }
777 
784  void solventPvtUpdate_(const ElementContext& elemCtx,
785  unsigned scvIdx,
786  unsigned timeIdx)
787  {
788  const auto& iq = asImp_();
789  auto& fs = asImp_().fluidState_;
790  const unsigned pvtRegionIdx = iq.pvtRegionIndex();
791  const Evaluation& T = iq.fluidState().temperature(gasPhaseIdx);
792  const Evaluation& p = iq.fluidState().pressure(gasPhaseIdx);
793 
794  Evaluation solventInvB;
795  const Evaluation rv = 0.0;
796  const Evaluation rvw = 0.0;
797  if (SolventModule::isCO2Sol() || SolventModule::isH2Sol() ){
798  if (SolventModule::isCO2Sol()) {
799  const auto& co2gasPvt = SolventModule::co2GasPvt();
800  solventInvB =
801  co2gasPvt.inverseFormationVolumeFactor(pvtRegionIdx, T, p, rv, rvw);
802  solventRefDensity_ = co2gasPvt.gasReferenceDensity(pvtRegionIdx);
803  solventViscosity_ = co2gasPvt.viscosity(pvtRegionIdx, T, p, rv, rvw);
804 
805  const auto& brineCo2Pvt = SolventModule::brineCo2Pvt();
806  const auto& bw = brineCo2Pvt.inverseFormationVolumeFactor(pvtRegionIdx, T, p, rsSolw());
807 
808  const auto denw = bw * brineCo2Pvt.waterReferenceDensity(pvtRegionIdx) +
809  rsSolw() * bw * brineCo2Pvt.gasReferenceDensity(pvtRegionIdx);
810  fs.setDensity(waterPhaseIdx, denw);
811  fs.setInvB(waterPhaseIdx, bw);
812  Evaluation& mobw = asImp_().mobility_[waterPhaseIdx];
813  const auto& muWat = fs.viscosity(waterPhaseIdx);
814  const auto& muWatEff = brineCo2Pvt.viscosity(pvtRegionIdx, T, p, rsSolw());
815  mobw *= muWat / muWatEff;
816  } else {
817  const auto& h2gasPvt = SolventModule::h2GasPvt();
818  solventInvB =
819  h2gasPvt.inverseFormationVolumeFactor(pvtRegionIdx, T, p, rv, rvw);
820  solventRefDensity_ = h2gasPvt.gasReferenceDensity(pvtRegionIdx);
821  solventViscosity_ = h2gasPvt.viscosity(pvtRegionIdx, T, p, rv, rvw);
822 
823  const auto& brineH2Pvt = SolventModule::brineH2Pvt();
824  const auto& bw = brineH2Pvt.inverseFormationVolumeFactor(pvtRegionIdx, T, p, rsSolw());
825 
826  const auto denw = bw * brineH2Pvt.waterReferenceDensity(pvtRegionIdx) +
827  rsSolw() * bw * brineH2Pvt.gasReferenceDensity(pvtRegionIdx);
828  fs.setDensity(waterPhaseIdx, denw);
829  fs.setInvB(waterPhaseIdx, bw);
830  Evaluation& mobw = asImp_().mobility_[waterPhaseIdx];
831  const auto& muWat = fs.viscosity(waterPhaseIdx);
832  const auto& muWatEff = brineH2Pvt.viscosity(pvtRegionIdx, T, p, rsSolw());
833  mobw *= muWat / muWatEff;
834  }
835  } else {
836  const auto& solventPvt = SolventModule::solventPvt();
837  solventInvB = solventPvt.inverseFormationVolumeFactor(pvtRegionIdx, T, p);
838  solventRefDensity_ = solventPvt.referenceDensity(pvtRegionIdx);
839  solventViscosity_ = solventPvt.viscosity(pvtRegionIdx, T, p);
840  }
841 
842  // store solvent PVT properties on the fluid state before effectiveProperties
843  fs.setSolventInvB(solventInvB);
844  fs.setSolventDensity(solventInvB * solventRefDensity_);
845 
846  effectiveProperties(elemCtx, scvIdx, timeIdx);
847 
848  solventMobility_ /= solventViscosity_;
849  }
850 
851  Evaluation solventSaturation() const
852  { return asImp_().fluidState_.solventSaturation(); }
853 
854  Evaluation rsSolw() const
855  { return asImp_().fluidState_.rsSolw(); }
856 
857  Evaluation solventDensity() const
858  { return asImp_().fluidState_.solventDensity(); }
859 
860  const Evaluation& solventViscosity() const
861  { return solventViscosity_; }
862 
863  const Evaluation& solventMobility() const
864  { return solventMobility_; }
865 
866  Evaluation solventInverseFormationVolumeFactor() const
867  { return asImp_().fluidState_.solventInvB(); }
868 
869  // This could be stored pr pvtRegion instead
870  Scalar solventRefDensity() const
871  { return solventRefDensity_; }
872 
873 private:
874  // Computes the effective properties based on
875  // Todd-Longstaff mixing model.
876  void effectiveProperties(const ElementContext& elemCtx,
877  unsigned scvIdx,
878  unsigned timeIdx)
879  {
880  if (!SolventModule::isMiscible()) {
881  return;
882  }
883 
884  // Don't waste calculations if no solvent
885  // Apply a cut-off for small and negative solvent saturations
886  if (solventSaturation() < cutOff) {
887  return;
888  }
889 
890  // We plan to update the fluidstate with the effective
891  // properties
892  auto& fs = asImp_().fluidState_;
893 
894  // Compute effective saturations
895  Evaluation oilEffSat = 0.0;
896  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
897  oilEffSat = fs.saturation(oilPhaseIdx);
898  }
899  Evaluation gasEffSat = fs.saturation(gasPhaseIdx);
900  Evaluation solventEffSat = solventSaturation();
901  if (FluidSystem::phaseIsActive(waterPhaseIdx)) {
902  const auto& sorwmis = SolventModule::sorwmis(elemCtx, scvIdx, timeIdx);
903  const auto& sgcwmis = SolventModule::sgcwmis(elemCtx, scvIdx, timeIdx);
904  const Evaluation zero = 0.0;
905  const Evaluation& sw = fs.saturation(waterPhaseIdx);
906  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
907  oilEffSat = std::max(oilEffSat - sorwmis.eval(sw, /*extrapolate=*/true), zero);
908  }
909  gasEffSat = std::max(gasEffSat - sgcwmis.eval(sw, /*extrapolate=*/true), zero);
910  solventEffSat = std::max(solventEffSat - sgcwmis.eval(sw, /*extrapolate=*/true), zero);
911  }
912  const Evaluation oilGasSolventEffSat = oilEffSat + gasEffSat + solventEffSat;
913  const Evaluation oilSolventEffSat = oilEffSat + solventEffSat;
914  const Evaluation solventGasEffSat = solventEffSat + gasEffSat;
915 
916  // Compute effective viscosities
917 
918  // Mixing parameter for viscosity
919  // The pressureMixingParameter represent the miscibility of the solvent while the mixingParameterViscosity the effect of the porous media.
920  // The pressureMixingParameter is not implemented in ecl100.
921  const Evaluation& p =
922  FluidSystem::phaseIsActive(oilPhaseIdx)
923  ? fs.pressure(oilPhaseIdx)
924  : fs.pressure(gasPhaseIdx);
925  // account for pressure effects
926  const auto& pmiscTable = SolventModule::pmisc(elemCtx, scvIdx, timeIdx);
927  const Evaluation pmisc = pmiscTable.eval(p, /*extrapolate=*/true);
928  const auto& tlPMixTable = SolventModule::tlPMixTable(elemCtx, scvIdx, timeIdx);
929  const Evaluation tlMixParamMu = SolventModule::tlMixParamViscosity(elemCtx, scvIdx, timeIdx) *
930  tlPMixTable.eval(p, /*extrapolate=*/true);
931 
932  const Evaluation& muGas = fs.viscosity(gasPhaseIdx);
933  const Evaluation& muSolvent = solventViscosity_;
934 
935  assert(muGas.value() > 0);
936  assert(muSolvent.value() > 0);
937  const Evaluation muGasPow = pow(muGas, 0.25);
938  const Evaluation muSolventPow = pow(muSolvent, 0.25);
939 
940  Evaluation muMixSolventGas = muGas;
941  if (solventGasEffSat > cutOff) {
942  muMixSolventGas *= muSolvent / pow(((gasEffSat / solventGasEffSat) * muSolventPow) +
943  ((solventEffSat / solventGasEffSat) * muGasPow), 4.0);
944  }
945 
946  Evaluation muOil = 1.0;
947  Evaluation muOilPow = 1.0;
948  Evaluation muMixOilSolvent = 1.0;
949  Evaluation muOilEff = 1.0;
950  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
951  muOil = fs.viscosity(oilPhaseIdx);
952  assert(muOil.value() > 0);
953  muOilPow = pow(muOil, 0.25);
954  muMixOilSolvent = muOil;
955  if (oilSolventEffSat > cutOff) {
956  muMixOilSolvent *= muSolvent / pow(((oilEffSat / oilSolventEffSat) * muSolventPow) +
957  ((solventEffSat / oilSolventEffSat) * muOilPow), 4.0);
958  }
959 
960  muOilEff = pow(muOil,1.0 - tlMixParamMu) * pow(muMixOilSolvent, tlMixParamMu);
961  }
962  Evaluation muMixSolventGasOil = muOil;
963  if (oilGasSolventEffSat > cutOff) {
964  muMixSolventGasOil *= muSolvent * muGas / pow(((oilEffSat / oilGasSolventEffSat) *
965  muSolventPow * muGasPow) +
966  ((solventEffSat / oilGasSolventEffSat) *
967  muOilPow * muGasPow) +
968  ((gasEffSat / oilGasSolventEffSat) *
969  muSolventPow * muOilPow), 4.0);
970  }
971 
972  Evaluation muGasEff = pow(muGas,1.0 - tlMixParamMu) * pow(muMixSolventGas, tlMixParamMu);
973  const Evaluation muSolventEff = pow(muSolvent,1.0 - tlMixParamMu) * pow(muMixSolventGasOil, tlMixParamMu);
974 
975  // Compute effective densities
976  const Evaluation& rhoGas = fs.density(gasPhaseIdx);
977  Evaluation rhoOil = 0.0;
978  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
979  rhoOil = fs.density(oilPhaseIdx);
980  }
981 
982  Evaluation rhoSolvent = solventDensity();
983 
984  // Mixing parameter for density
985  // The pressureMixingParameter represent the miscibility of the
986  // solvent while the mixingParameterDenisty the effect of the porous media.
987  // The pressureMixingParameter is not implemented in ecl100.
988  const Evaluation tlMixParamRho = SolventModule::tlMixParamDensity(elemCtx, scvIdx, timeIdx) *
989  tlPMixTable.eval(p, /*extrapolate=*/true);
990 
991  // compute effective viscosities for density calculations. These have to
992  // be recomputed as a different mixing parameter may be used.
993  const Evaluation muOilEffPow = pow(pow(muOil, 1.0 - tlMixParamRho) *
994  pow(muMixOilSolvent, tlMixParamRho), 0.25);
995  const Evaluation muGasEffPow = pow(pow(muGas, 1.0 - tlMixParamRho) *
996  pow(muMixSolventGas, tlMixParamRho), 0.25);
997  const Evaluation muSolventEffPow = pow(pow(muSolvent, 1.0 - tlMixParamRho) *
998  pow(muMixSolventGasOil, tlMixParamRho), 0.25);
999 
1000  const Evaluation oilGasEffSaturation = oilEffSat + gasEffSat;
1001  Evaluation sof = 0.0;
1002  Evaluation sgf = 0.0;
1003  if (oilGasEffSaturation.value() > cutOff) {
1004  sof = oilEffSat / oilGasEffSaturation;
1005  sgf = gasEffSat / oilGasEffSaturation;
1006  }
1007 
1008  const Evaluation muSolventOilGasPow = muSolventPow * ((sgf * muOilPow) + (sof * muGasPow));
1009 
1010  Evaluation rhoMixSolventGasOil = 0.0;
1011  if (oilGasSolventEffSat.value() > cutOff) {
1012  rhoMixSolventGasOil = (rhoOil * oilEffSat / oilGasSolventEffSat) +
1013  (rhoGas * gasEffSat / oilGasSolventEffSat) +
1014  (rhoSolvent * solventEffSat / oilGasSolventEffSat);
1015  }
1016 
1017  Evaluation rhoGasEff = 0.0;
1018  if (std::abs(muSolventPow.value() - muGasPow.value()) < cutOff) {
1019  rhoGasEff = ((1.0 - tlMixParamRho) * rhoGas) +
1020  (tlMixParamRho * rhoMixSolventGasOil);
1021  }
1022  else {
1023  const Evaluation solventGasEffFraction = (muGasPow * (muSolventPow - muGasEffPow)) /
1024  (muGasEffPow * (muSolventPow - muGasPow));
1025  rhoGasEff = (rhoGas * solventGasEffFraction) + (rhoSolvent * (1.0 - solventGasEffFraction));
1026  }
1027 
1028  Evaluation rhoSolventEff = 0.0;
1029  if (std::abs((muSolventOilGasPow.value() - (muOilPow.value() * muGasPow.value()))) < cutOff) {
1030  rhoSolventEff = ((1.0 - tlMixParamRho) * rhoSolvent) + (tlMixParamRho * rhoMixSolventGasOil);
1031  }
1032  else {
1033  const Evaluation sfraction_se = (muSolventOilGasPow -
1034  muOilPow * muGasPow * muSolventPow / muSolventEffPow) /
1035  (muSolventOilGasPow - (muOilPow * muGasPow));
1036  rhoSolventEff = (rhoSolvent * sfraction_se) +
1037  (rhoGas * sgf * (1.0 - sfraction_se)) +
1038  (rhoOil * sof * (1.0 - sfraction_se));
1039  }
1040 
1041  // compute invB from densities.
1042  const unsigned pvtRegionIdx = asImp_().pvtRegionIndex();
1043  Evaluation bGasEff = rhoGasEff;
1044  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1045  bGasEff /= (FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx) +
1046  FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx) * fs.Rv());
1047  } else {
1048  bGasEff /= (FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx));
1049  }
1050  const Evaluation bSolventEff = rhoSolventEff / solventRefDensity();
1051 
1052  // copy the unmodified invB factors
1053  const Evaluation bg = fs.invB(gasPhaseIdx);
1054  const Evaluation bs = solventInverseFormationVolumeFactor();
1055  const Evaluation bg_eff = pmisc * bGasEff + (1.0 - pmisc) * bg;
1056  const Evaluation bs_eff = pmisc * bSolventEff + (1.0 - pmisc) * bs;
1057 
1058  // set the densities
1059  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1060  fs.setDensity(gasPhaseIdx,
1061  bg_eff *
1062  (FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx) +
1063  FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx)*fs.Rv()));
1064  } else {
1065  fs.setDensity(gasPhaseIdx,
1066  bg_eff * FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx));
1067  }
1068  rhoSolvent = bs_eff * solventRefDensity();
1069  fs.setSolventDensity(rhoSolvent);
1070 
1071  // set the mobility
1072  Evaluation& mobg = asImp_().mobility_[gasPhaseIdx];
1073  muGasEff = bg_eff / (pmisc * bGasEff / muGasEff + (1.0 - pmisc) * bg / muGas);
1074  mobg *= muGas / muGasEff;
1075 
1076  // Update viscosity of solvent
1077  solventViscosity_ = bs_eff / (pmisc * bSolventEff / muSolventEff +
1078  (1.0 - pmisc) * bs / muSolvent);
1079 
1080  if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
1081  Evaluation rhoOilEff = 0.0;
1082  if (std::abs(muOilPow.value() - muSolventPow.value()) < cutOff) {
1083  rhoOilEff = ((1.0 - tlMixParamRho) * rhoOil) + (tlMixParamRho * rhoMixSolventGasOil);
1084  }
1085  else {
1086  const Evaluation solventOilEffFraction = (muOilPow * (muOilEffPow - muSolventPow)) /
1087  (muOilEffPow * (muOilPow - muSolventPow));
1088  rhoOilEff = (rhoOil * solventOilEffFraction) + (rhoSolvent * (1.0 - solventOilEffFraction));
1089  }
1090  const Evaluation bOilEff =
1091  rhoOilEff / (FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx) +
1092  FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx) * fs.Rs());
1093  const Evaluation bo = fs.invB(oilPhaseIdx);
1094  const Evaluation bo_eff = pmisc * bOilEff + (1.0 - pmisc) * bo;
1095  fs.setDensity(oilPhaseIdx,
1096  bo_eff * (FluidSystem::referenceDensity(oilPhaseIdx, pvtRegionIdx) +
1097  FluidSystem::referenceDensity(gasPhaseIdx, pvtRegionIdx) * fs.Rs()));
1098 
1099  // keep the mu*b interpolation
1100  Evaluation& mobo = asImp_().mobility_[oilPhaseIdx];
1101  muOilEff = bo_eff / (pmisc * bOilEff / muOilEff + (1.0 - pmisc) * bo / muOil);
1102  mobo *= muOil / muOilEff;
1103  }
1104  }
1105 
1106 protected:
1107  Implementation& asImp_()
1108  { return *static_cast<Implementation*>(this); }
1109 
1110  const Implementation& asImp_() const
1111  { return *static_cast<const Implementation*>(this); }
1112 
1113  Evaluation hydrocarbonSaturation_;
1114  Evaluation solventViscosity_;
1115  Evaluation solventMobility_;
1116 
1117  Scalar solventRefDensity_;
1118 };
1119 
1127 template <class TypeTag>
1129 {
1131 
1134  using ElementContext = GetPropType<TypeTag, Properties::ElementContext>;
1135  using IntensiveQuantities = GetPropType<TypeTag, Properties::IntensiveQuantities>;
1136  using ExtensiveQuantities = GetPropType<TypeTag, Properties::ExtensiveQuantities>;
1139 
1140  using Toolbox = MathToolbox<Evaluation>;
1141 
1142  static constexpr unsigned gasPhaseIdx = FluidSystem::gasPhaseIdx;
1143  static constexpr int dimWorld = GridView::dimensionworld;
1144 
1145  using DimVector = Dune::FieldVector<Scalar, dimWorld>;
1146  using DimEvalVector = Dune::FieldVector<Evaluation, dimWorld>;
1147 
1148 public:
1153  template <class Dummy = bool> // we need to make this method a template to avoid
1154  // compiler errors if it is not instantiated!
1155  void updateVolumeFluxPerm(const ElementContext& elemCtx,
1156  unsigned scvfIdx,
1157  unsigned timeIdx)
1158  {
1159  const auto& gradCalc = elemCtx.gradientCalculator();
1160  PressureCallback<TypeTag> pressureCallback(elemCtx);
1161 
1162  const auto& scvf = elemCtx.stencil(timeIdx).interiorFace(scvfIdx);
1163  const auto& faceNormal = scvf.normal();
1164 
1165  const unsigned i = scvf.interiorIndex();
1166  const unsigned j = scvf.exteriorIndex();
1167 
1168  // calculate the "raw" pressure gradient
1169  DimEvalVector solventPGrad;
1170  pressureCallback.setPhaseIndex(gasPhaseIdx);
1171  gradCalc.calculateGradient(solventPGrad,
1172  elemCtx,
1173  scvfIdx,
1174  pressureCallback);
1175  Valgrind::CheckDefined(solventPGrad);
1176 
1177  // correct the pressure gradients by the gravitational acceleration
1178  if (Parameters::Get<Parameters::EnableGravity>()) {
1179  // estimate the gravitational acceleration at a given SCV face
1180  // using the arithmetic mean
1181  const auto& gIn = elemCtx.problem().gravity(elemCtx, i, timeIdx);
1182  const auto& gEx = elemCtx.problem().gravity(elemCtx, j, timeIdx);
1183 
1184  const auto& intQuantsIn = elemCtx.intensiveQuantities(i, timeIdx);
1185  const auto& intQuantsEx = elemCtx.intensiveQuantities(j, timeIdx);
1186 
1187  const auto& posIn = elemCtx.pos(i, timeIdx);
1188  const auto& posEx = elemCtx.pos(j, timeIdx);
1189  const auto& posFace = scvf.integrationPos();
1190 
1191  // the distance between the centers of the control volumes
1192  DimVector distVecIn(posIn);
1193  DimVector distVecEx(posEx);
1194  DimVector distVecTotal(posEx);
1195 
1196  distVecIn -= posFace;
1197  distVecEx -= posFace;
1198  distVecTotal -= posIn;
1199  const Scalar absDistTotalSquared = distVecTotal.two_norm2();
1200 
1201  // calculate the hydrostatic pressure at the integration point of the face
1202  auto rhoIn = intQuantsIn.solventDensity();
1203  auto pStatIn = - rhoIn * (gIn * distVecIn);
1204 
1205  // the quantities on the exterior side of the face do not influence the
1206  // result for the TPFA scheme, so they can be treated as scalar values.
1207  Scalar rhoEx = Toolbox::value(intQuantsEx.solventDensity());
1208  Scalar pStatEx = - rhoEx * (gEx * distVecEx);
1209 
1210  // compute the hydrostatic gradient between the two control volumes (this
1211  // gradient exhibitis the same direction as the vector between the two
1212  // control volume centers and the length (pStaticExterior -
1213  // pStaticInterior)/distanceInteriorToExterior
1214  DimEvalVector f(distVecTotal);
1215  f *= (pStatEx - pStatIn) / absDistTotalSquared;
1216 
1217  // calculate the final potential gradient
1218  for (unsigned dimIdx = 0; dimIdx < dimWorld; ++dimIdx) {
1219  solventPGrad[dimIdx] += f[dimIdx];
1220 
1221  if (!isfinite(solventPGrad[dimIdx])) {
1222  throw NumericalProblem("Non-finite potential gradient for solvent 'phase'");
1223  }
1224  }
1225  }
1226 
1227  // determine the upstream and downstream DOFs
1228  Evaluation solventPGradNormal = 0.0;
1229  for (unsigned dimIdx = 0; dimIdx < faceNormal.size(); ++dimIdx) {
1230  solventPGradNormal += solventPGrad[dimIdx]*faceNormal[dimIdx];
1231  }
1232 
1233  if (solventPGradNormal > 0) {
1234  solventUpstreamDofIdx_ = j;
1235  solventDownstreamDofIdx_ = i;
1236  }
1237  else {
1238  solventUpstreamDofIdx_ = i;
1239  solventDownstreamDofIdx_ = j;
1240  }
1241 
1242  const auto& up = elemCtx.intensiveQuantities(solventUpstreamDofIdx_, timeIdx);
1243 
1244  // this is also slightly hacky because it assumes that the derivative of the
1245  // flux between two DOFs only depends on the primary variables in the
1246  // upstream direction. For non-TPFA flux approximation schemes, this is not
1247  // true...
1248  if (solventUpstreamDofIdx_ == i) {
1249  solventVolumeFlux_ = solventPGradNormal * up.solventMobility();
1250  }
1251  else {
1252  solventVolumeFlux_ = solventPGradNormal * scalarValue(up.solventMobility());
1253  }
1254  }
1255 
1260  template <class Dummy = bool> // we need to make this method a template to avoid
1261  // compiler errors if it is not instantiated!
1262  void updateVolumeFluxTrans(const ElementContext& elemCtx,
1263  unsigned scvfIdx,
1264  unsigned timeIdx)
1265  {
1266  const ExtensiveQuantities& extQuants = asImp_();
1267 
1268  const unsigned interiorDofIdx = extQuants.interiorIndex();
1269  const unsigned exteriorDofIdx = extQuants.exteriorIndex();
1270  assert(interiorDofIdx != exteriorDofIdx);
1271 
1272  const auto& intQuantsIn = elemCtx.intensiveQuantities(interiorDofIdx, timeIdx);
1273  const auto& intQuantsEx = elemCtx.intensiveQuantities(exteriorDofIdx, timeIdx);
1274 
1275  const unsigned I = elemCtx.globalSpaceIndex(interiorDofIdx, timeIdx);
1276  const unsigned J = elemCtx.globalSpaceIndex(exteriorDofIdx, timeIdx);
1277 
1278  const Scalar thpresInToEx = elemCtx.problem().thresholdPressure(I, J);
1279  const Scalar thpresExToIn = elemCtx.problem().thresholdPressure(J, I);
1280  const Scalar trans = elemCtx.problem().transmissibility(elemCtx, interiorDofIdx, exteriorDofIdx);
1281  const Scalar g = elemCtx.problem().gravity()[dimWorld - 1];
1282 
1283  const Scalar zIn = elemCtx.problem().dofCenterDepth(elemCtx, interiorDofIdx, timeIdx);
1284  const Scalar zEx = elemCtx.problem().dofCenterDepth(elemCtx, exteriorDofIdx, timeIdx);
1285  const Scalar distZ = zIn - zEx;
1286 
1287  const Evaluation rhoIn = intQuantsIn.solventDensity();
1288  const Scalar rhoEx = Toolbox::value(intQuantsEx.solventDensity());
1289  const Evaluation rhoAvg = rhoIn * 0.5 + rhoEx * 0.5;
1290 
1291  const Evaluation& pressureInterior = intQuantsIn.fluidState().pressure(gasPhaseIdx);
1292  Evaluation pressureExterior = Toolbox::value(intQuantsEx.fluidState().pressure(gasPhaseIdx));
1293  pressureExterior += distZ * g * rhoAvg;
1294 
1295  Evaluation pressureDiffSolvent = pressureExterior - pressureInterior;
1296  const Scalar thpres = pressureDiffSolvent < 0.0 ? thpresInToEx : thpresExToIn;
1297  if (thpres > 0.0) {
1298  if (std::abs(scalarValue(pressureDiffSolvent)) > thpres) {
1299  if (pressureDiffSolvent < 0.0) {
1300  pressureDiffSolvent += thpres;
1301  }
1302  else {
1303  pressureDiffSolvent -= thpres;
1304  }
1305  }
1306  else {
1307  pressureDiffSolvent = 0.0;
1308  }
1309  }
1310 
1311  if (pressureDiffSolvent > 0.0) {
1312  solventUpstreamDofIdx_ = exteriorDofIdx;
1313  solventDownstreamDofIdx_ = interiorDofIdx;
1314  }
1315  else if (pressureDiffSolvent < 0.0) {
1316  solventUpstreamDofIdx_ = interiorDofIdx;
1317  solventDownstreamDofIdx_ = exteriorDofIdx;
1318  }
1319  else {
1320  // pressure potential gradient is zero; force consistent upstream and
1321  // downstream indices over the intersection regardless of the side which it
1322  // is looked at.
1323  solventUpstreamDofIdx_ = std::min(interiorDofIdx, exteriorDofIdx);
1324  solventDownstreamDofIdx_ = std::max(interiorDofIdx, exteriorDofIdx);
1325  solventVolumeFlux_ = 0.0;
1326  return;
1327  }
1328 
1329  const Scalar faceArea = elemCtx.stencil(timeIdx).interiorFace(scvfIdx).area();
1330  const IntensiveQuantities& up = elemCtx.intensiveQuantities(solventUpstreamDofIdx_, timeIdx);
1331  if (solventUpstreamDofIdx_ == interiorDofIdx) {
1332  solventVolumeFlux_ = up.solventMobility() * (-trans / faceArea) * pressureDiffSolvent;
1333  }
1334  else {
1335  solventVolumeFlux_ =
1336  scalarValue(up.solventMobility()) * (-trans / faceArea) * pressureDiffSolvent;
1337  }
1338  }
1339 
1340  unsigned solventUpstreamIndex() const
1341  { return solventUpstreamDofIdx_; }
1342 
1343  unsigned solventDownstreamIndex() const
1344  { return solventDownstreamDofIdx_; }
1345 
1346  const Evaluation& solventVolumeFlux() const
1347  { return solventVolumeFlux_; }
1348 
1349  void setSolventVolumeFlux(const Evaluation& solventVolumeFlux)
1350  { solventVolumeFlux_ = solventVolumeFlux; }
1351 
1352 private:
1353  Implementation& asImp_()
1354  { return *static_cast<Implementation*>(this); }
1355 
1356  Evaluation solventVolumeFlux_;
1357  unsigned solventUpstreamDofIdx_;
1358  unsigned solventDownstreamDofIdx_;
1359 };
1360 
1361 } // namespace Opm
1362 
1363 #endif
static void assignPrimaryVars(PrimaryVariables &priVars, Scalar solventSaturation, Scalar solventRsw)
Assign the solvent specific primary variables to a PrimaryVariables object.
Definition: blackoilsolventmodules.hh:270
Provides the solvent specific extensive quantities to the generic black-oil module&#39;s extensive quanti...
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...))
Definition: propertysystem.hh:233
static void registerParameters()
Register all run-time parameters for the black-oil solvent module.
Definition: blackoilsolventmodules.hh:116
VTK output module for the black oil model&#39;s solvent related quantities.
static void registerOutputModules(Model &model, Simulator &simulator)
Register all solvent specific VTK and ECL output modules.
Definition: blackoilsolventmodules.hh:124
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Contains classes extending the black-oil model.
static void setSolventPvt(const SolventPvt &value)
Specify the solvent PVT of a all PVT regions.
Definition: blackoilsolventmodules.hh:107
Declares the properties required by the black oil model.
Contains the parameters required to extend the black-oil model by solvents.
VTK output module for the black oil model&#39;s solvent related quantities.
Definition: vtkblackoilsolventmodule.hpp:53
Definition: linearizationtype.hh:33
This method contains all callback classes for quantities that are required by some extensive quantiti...
void updateVolumeFluxTrans(const ElementContext &elemCtx, unsigned scvfIdx, unsigned timeIdx)
Method which calculates the volume flux of the polymer "phase" using the gas pressure potential diffe...
Definition: blackoilsolventmodules.hh:1262
The common code for the linearizers of non-linear systems of equations.
static void updatePrimaryVars(PrimaryVariables &newPv, const PrimaryVariables &oldPv, const EqVector &delta)
Do a Newton-Raphson update the primary variables of the solvents.
Definition: blackoilsolventmodules.hh:287
Contains the high level supplements required to extend the black oil model by solvents.
Definition: blackoilsolventmodules.hh:68
void solventPvtUpdate_(const ElementContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Update the intensive PVT properties needed to handle solvents from the primary variables.
Definition: blackoilsolventmodules.hh:784
Callback class for a phase pressure.
Definition: quantitycallbacks.hh:84
Struct holding the parameters for the BlackOilSolventModule class.
Definition: blackoilsolventparams.hpp:46
void solventPostSatFuncUpdate_(const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx)
Called after the saturation functions have been doing their magic.
Definition: blackoilsolventmodules.hh:601
static void setParams(BlackOilSolventParams< Scalar > &&params)
Set parameters.
Definition: blackoilsolventmodules.hh:101
Provides the volumetric quantities required for the equations needed by the solvents extension of the...
static Scalar computeResidualError(const EqVector &resid)
Return how much a residual is considered an error.
Definition: blackoilsolventmodules.hh:310
void updateVolumeFluxPerm(const ElementContext &elemCtx, unsigned scvfIdx, unsigned timeIdx)
Method which calculates the volume flux of the polymer "phase" using the pressure potential gradient ...
Definition: blackoilsolventmodules.hh:1155
static Scalar computeUpdateError(const PrimaryVariables &, const EqVector &)
Return how much a Newton-Raphson update is considered an error.
Definition: blackoilsolventmodules.hh:298
static void registerParameters()
Register all run-time parameters for the multi-phase VTK output module.
Definition: vtkblackoilsolventmodule.hpp:84
void solventPreSatFuncUpdate_(const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx)
Called before the saturation functions are doing their magic.
Definition: blackoilsolventmodules.hh:562
Defines the common parameters for the porous medium multi-phase models.
void setPhaseIndex(unsigned phaseIdx)
Set the index of the fluid phase for which the pressure should be returned.
Definition: quantitycallbacks.hh:109