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
60namespace Opm {
61
67template <class TypeTag>
68class 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
98public:
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
516private:
517 static BlackOilSolventParams<Scalar> params_; // the krg(Fs) column of the SSFN table
518};
519
520template <class TypeTag>
521BlackOilSolventParams<typename BlackOilSolventModule<TypeTag, true>::Scalar>
522BlackOilSolventModule<TypeTag, true>::params_;
523
531template <class TypeTag>
532class 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
554public:
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
613private:
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
628public:
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
867 { return asImp_().fluidState_.solventInvB(); }
868
869 // This could be stored pr pvtRegion instead
870 Scalar solventRefDensity() const
871 { return solventRefDensity_; }
872
873private:
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
1106protected:
1107 Implementation& asImp_()
1108 { return *static_cast<Implementation*>(this); }
1109
1110 const Implementation& asImp_() const
1111 { return *static_cast<const Implementation*>(this); }
1112
1116
1118};
1119
1127template <class TypeTag>
1129{
1131
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
1148public:
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
1352private:
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
Contains classes extending the black-oil model. \detail This file holds dummy definitions,...
Declares the properties required by the black oil model.
Contains the parameters required to extend the black-oil model by solvents.
const Evaluation & solventVolumeFlux() const
Definition: blackoilsolventmodules.hh:1346
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
unsigned solventDownstreamIndex() const
Definition: blackoilsolventmodules.hh:1343
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
void setSolventVolumeFlux(const Evaluation &solventVolumeFlux)
Definition: blackoilsolventmodules.hh:1349
unsigned solventUpstreamIndex() const
Definition: blackoilsolventmodules.hh:1340
Provides the solvent specific extensive quantities to the generic black-oil module's extensive quanti...
void solventPreSatFuncUpdate_(const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx)
Called before the saturation functions are doing their magic.
Definition: blackoilsolventmodules.hh:562
Implementation & asImp_()
Definition: blackoilsolventmodules.hh:1107
Evaluation solventViscosity_
Definition: blackoilsolventmodules.hh:1114
const Evaluation & solventViscosity() const
Definition: blackoilsolventmodules.hh:860
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
const Implementation & asImp_() const
Definition: blackoilsolventmodules.hh:1110
void solventPostSatFuncUpdate_(const Problem &problem, const PrimaryVariables &priVars, const unsigned globalSpaceIdx, const unsigned timeIdx, const LinearizationType linearizationType)
Definition: blackoilsolventmodules.hh:629
Evaluation solventDensity() const
Definition: blackoilsolventmodules.hh:857
const Evaluation & solventMobility() const
Definition: blackoilsolventmodules.hh:863
Evaluation hydrocarbonSaturation_
Definition: blackoilsolventmodules.hh:1113
void solventPreSatFuncUpdate_(const PrimaryVariables &priVars, const unsigned timeIdx, const LinearizationType linearizationType)
Definition: blackoilsolventmodules.hh:570
void solventPostSatFuncUpdate_(const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx)
Called after the saturation functions have been doing their magic.
Definition: blackoilsolventmodules.hh:601
Evaluation solventSaturation() const
Definition: blackoilsolventmodules.hh:851
Scalar solventRefDensity_
Definition: blackoilsolventmodules.hh:1117
Scalar solventRefDensity() const
Definition: blackoilsolventmodules.hh:870
Evaluation rsSolw() const
Definition: blackoilsolventmodules.hh:854
Evaluation solventMobility_
Definition: blackoilsolventmodules.hh:1115
Evaluation solventInverseFormationVolumeFactor() const
Definition: blackoilsolventmodules.hh:866
Provides the volumetric quantities required for the equations needed by the solvents extension of the...
Contains the high level supplements required to extend the black oil model by solvents.
Definition: blackoilsolventmodules.hh:69
static void deserializeEntity(Model &model, std::istream &instream, const DofEntity &dof)
Definition: blackoilsolventmodules.hh:326
static Scalar tlMixParamDensity(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:476
static void setSolventPvt(const SolventPvt &value)
Specify the solvent PVT of a all PVT regions.
Definition: blackoilsolventmodules.hh:107
static bool isCO2Sol()
Definition: blackoilsolventmodules.hh:510
static const TabulatedFunction & misc(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:395
static const TabulatedFunction & msfnKrsg(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:415
static bool isH2Sol()
Definition: blackoilsolventmodules.hh:513
static const TabulatedFunction & sorwmis(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:435
static void computeFlux(RateVector &flux, const ElementContext &elemCtx, unsigned scvfIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:204
static Scalar tlMixParamViscosity(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:465
static const TabulatedFunction & sgcwmis(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:445
static bool eqApplies(unsigned eqIdx)
Definition: blackoilsolventmodules.hh:150
static const TabulatedFunction & pmisc(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:405
static bool primaryVarApplies(unsigned pvIdx)
Definition: blackoilsolventmodules.hh:130
static const TabulatedFunction & ssfnKrs(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:365
static void setIsMiscible(const bool isMiscible)
Definition: blackoilsolventmodules.hh:110
static Scalar computeResidualError(const EqVector &resid)
Return how much a residual is considered an error.
Definition: blackoilsolventmodules.hh:310
static const BrineH2Pvt & brineH2Pvt()
Definition: blackoilsolventmodules.hh:351
static void assignPrimaryVars(PrimaryVariables &priVars, Scalar solventSaturation, Scalar solventRsw)
Assign the solvent specific primary variables to a PrimaryVariables object.
Definition: blackoilsolventmodules.hh:270
static std::string primaryVarName(unsigned pvIdx)
Definition: blackoilsolventmodules.hh:135
static const SolventPvt & solventPvt()
Definition: blackoilsolventmodules.hh:339
static Scalar computeUpdateError(const PrimaryVariables &, const EqVector &)
Return how much a Newton-Raphson update is considered an error.
Definition: blackoilsolventmodules.hh:298
static const H2GasPvt & h2GasPvt()
Definition: blackoilsolventmodules.hh:345
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
static bool isSolubleInWater()
Definition: blackoilsolventmodules.hh:507
static std::string eqName(unsigned eqIdx)
Definition: blackoilsolventmodules.hh:155
static const TabulatedFunction & ssfnKrs(const unsigned satnumRegionIdx)
Definition: blackoilsolventmodules.hh:379
static void setParams(BlackOilSolventParams< Scalar > &&params)
Set parameters.
Definition: blackoilsolventmodules.hh:101
static const TabulatedFunction & sof2Krn(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:385
static void registerParameters()
Register all run-time parameters for the black-oil solvent module.
Definition: blackoilsolventmodules.hh:116
static Value solubilityLimit(unsigned pvtIdx, const Value &temperature, const Value &pressure, const Value &saltConcentration)
Definition: blackoilsolventmodules.hh:489
static Scalar primaryVarWeight(unsigned pvIdx)
Definition: blackoilsolventmodules.hh:142
static const BrineCo2Pvt & brineCo2Pvt()
Definition: blackoilsolventmodules.hh:348
static void serializeEntity(const Model &model, std::ostream &outstream, const DofEntity &dof)
Definition: blackoilsolventmodules.hh:317
static bool isMiscible()
Definition: blackoilsolventmodules.hh:485
static const TabulatedFunction & tlPMixTable(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:455
static OPM_HOST_DEVICE void addStorage(StorageType &storage, const IntensiveQuantities &intQuants)
Definition: blackoilsolventmodules.hh:171
static Scalar eqWeight(unsigned eqIdx)
Definition: blackoilsolventmodules.hh:162
static const Co2GasPvt & co2GasPvt()
Definition: blackoilsolventmodules.hh:342
static void registerOutputModules(Model &model, Simulator &simulator)
Register all solvent specific VTK and ECL output modules.
Definition: blackoilsolventmodules.hh:124
static const TabulatedFunction & msfnKro(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:425
static const TabulatedFunction & ssfnKrg(const ElemContext &elemCtx, unsigned scvIdx, unsigned timeIdx)
Definition: blackoilsolventmodules.hh:355
static const TabulatedFunction & ssfnKrg(const unsigned satnumRegionIdx)
Definition: blackoilsolventmodules.hh:374
Callback class for a phase pressure.
Definition: quantitycallbacks.hh:85
void setPhaseIndex(unsigned phaseIdx)
Set the index of the fluid phase for which the pressure should be returned.
Definition: quantitycallbacks.hh:109
VTK output module for the black oil model's solvent related quantities.
Definition: vtkblackoilsolventmodule.hpp:54
static void registerParameters()
Register all run-time parameters for the multi-phase VTK output module.
Definition: vtkblackoilsolventmodule.hpp:84
Defines the common parameters for the porous medium multi-phase models.
Definition: blackoilbioeffectsmodules.hh:45
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
This method contains all callback classes for quantities that are required by some extensive quantiti...
Struct holding the parameters for the BlackOilSolventModule class.
Definition: blackoilsolventparams.hpp:47
::Opm::SolventPvt< Scalar > SolventPvt
Definition: blackoilsolventparams.hpp:52
::Opm::H2GasPvt< Scalar > H2GasPvt
Definition: blackoilsolventparams.hpp:51
::Opm::BrineCo2Pvt< Scalar > BrineCo2Pvt
Definition: blackoilsolventparams.hpp:48
::Opm::Co2GasPvt< Scalar > Co2GasPvt
Definition: blackoilsolventparams.hpp:50
Tabulated1DFunction< Scalar > TabulatedFunction
Definition: blackoilsolventparams.hpp:53
::Opm::BrineH2Pvt< Scalar > BrineH2Pvt
Definition: blackoilsolventparams.hpp:49
Definition: linearizationtype.hh:34