opm-simulators
blackoillocalresidualtpfa.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_LOCAL_TPFA_RESIDUAL_HH
29 #define EWOMS_BLACK_OIL_LOCAL_TPFA_RESIDUAL_HH
30 
31 #include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
32 #include <opm/input/eclipse/Schedule/BCProp.hpp>
33 
34 #include <opm/material/common/ConditionalStorage.hpp>
35 #include <opm/material/common/MathToolbox.hpp>
36 #include <opm/material/fluidstates/BlackOilFluidState.hpp>
37 
40 #include <opm/models/blackoil/blackoilmoduleparams.hh>
42 
44 
45 #include <opm/common/ErrorMacros.hpp>
46 #include <opm/common/utility/gpuDecorators.hpp>
47 #include <opm/common/utility/gpuistl_if_available.hpp>
48 
49 #include <array>
50 #include <cassert>
51 #include <limits>
52 #include <stdexcept>
53 #include <string>
54 
55 #include <opm/common/utility/gpuistl_if_available.hpp>
56 
57 namespace Opm
58 {
65 template <class TypeTag>
66 class BlackOilLocalResidualTPFA : public GetPropType<TypeTag, Properties::DiscLocalResidual>
67 {
78  using FluidState = typename IntensiveQuantities::FluidState;
79 
80  enum { conti0EqIdx = Indices::conti0EqIdx };
81  enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
82  enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
83  enum { numComponents = getPropValue<TypeTag, Properties::NumComponents>() };
84 
85  enum { dimWorld = GridView::dimensionworld };
86  enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
87  enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
88  enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
89 
90  enum { gasCompIdx = FluidSystem::gasCompIdx };
91  enum { oilCompIdx = FluidSystem::oilCompIdx };
92  enum { waterCompIdx = FluidSystem::waterCompIdx };
93  static constexpr unsigned compositionSwitchIdx = Indices::compositionSwitchIdx;
94 
95  static constexpr bool waterEnabled = Indices::waterEnabled;
96  static constexpr bool gasEnabled = Indices::gasEnabled;
97  static constexpr bool oilEnabled = Indices::oilEnabled;
98  static constexpr bool compositionSwitchEnabled =
99  compositionSwitchIdx != std::numeric_limits<unsigned>::max();
100 
101  static constexpr bool blackoilConserveSurfaceVolume
102  = getPropValue<TypeTag, Properties::BlackoilConserveSurfaceVolume>();
103 
104  static constexpr bool enableSolvent = getPropValue<TypeTag, Properties::EnableSolvent>();
105  static constexpr bool enableExtbo = getPropValue<TypeTag, Properties::EnableExtbo>();
106  static constexpr bool enablePolymer = getPropValue<TypeTag, Properties::EnablePolymer>();
107  static constexpr EnergyModules energyModuleType =
108  getPropValue<TypeTag, Properties::EnergyModuleType>();
109  static constexpr bool enableFullyImplicitThermal =
110  energyModuleType == EnergyModules::FullyImplicitThermal;
111  static constexpr bool enableFoam = getPropValue<TypeTag, Properties::EnableFoam>();
112  static constexpr bool enableBrine = getPropValue<TypeTag, Properties::EnableBrine>();
113  static constexpr bool enableDiffusion = getPropValue<TypeTag, Properties::EnableDiffusion>();
114  static constexpr bool enableDispersion = getPropValue<TypeTag, Properties::EnableDispersion>();
115  static constexpr bool enableConvectiveMixing
116  = getPropValue<TypeTag, Properties::EnableConvectiveMixing>();
117  static constexpr bool enableBioeffects = getPropValue<TypeTag, Properties::EnableBioeffects>();
118  static constexpr bool enableSaltPrecipitation
119  = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>();
120  static constexpr bool enableMICP = Indices::enableMICP;
121  static constexpr bool runAssemblyOnGpu = getPropValue<TypeTag, Properties::RunAssemblyOnGpu>();
122 
123  using BioeffectsModule = BlackOilBioeffectsModule<TypeTag, enableBioeffects>;
124  using BrineModule = BlackOilBrineModule<TypeTag, enableBrine>;
125  using ConvectiveMixingModule = BlackOilConvectiveMixingModule<TypeTag, enableConvectiveMixing>;
129  using ExtboModule = BlackOilExtboModule<TypeTag, enableExtbo>;
131  using SolventModule = BlackOilSolventModule<TypeTag, enableSolvent>;
132  using PolymerModule = BlackOilPolymerModule<TypeTag, enablePolymer>;
133 
135  using Toolbox = MathToolbox<Evaluation>;
136 
137 public:
138  struct ResidualNBInfo {
139  double trans;
140  double faceArea;
141  double thpresInToEx;
142  double thpresExToIn;
143  double dZg;
144  FaceDir::DirEnum faceDir;
145  double Vin;
146  double Vex;
147  ConditionalStorage<enableFullyImplicitThermal, double> inAlpha;
148  ConditionalStorage<enableFullyImplicitThermal, double> outAlpha;
149  ConditionalStorage<enableDiffusion, double> diffusivity;
150  ConditionalStorage<enableDispersion, double> dispersivity;
151  };
152 
156  template <class LhsEval>
157  void computeStorage(Dune::FieldVector<LhsEval, numEq>& storage,
158  const ElementContext& elemCtx,
159  unsigned dofIdx,
160  unsigned timeIdx) const
161  {
162  const IntensiveQuantities& intQuants = elemCtx.intensiveQuantities(dofIdx, timeIdx);
163  computeStorage<LhsEval>(storage, intQuants);
164  }
165 
166  template <class LhsEval, class StorageType, class IntensiveQuantitiesType = IntensiveQuantities>
167  OPM_HOST_DEVICE static void computeStorage(StorageType& storage,
168  const IntensiveQuantitiesType& intQuants)
169  {
170  OPM_TIMEBLOCK_LOCAL(computeStorage, Subsystem::Assembly);
171  // retrieve the intensive quantities for the SCV at the specified point in time
172  const auto& fs = intQuants.fluidState();
173  storage = 0.0;
174 
175  const FluidSystem& fsys = intQuants.getFluidSystem();
176 
177  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
178  if (!fsys.phaseIsActive(phaseIdx)) {
179  continue;
180  }
181  unsigned activeCompIdx
182  = fsys.canonicalToActiveCompIdx(fsys.solventComponentIndex(phaseIdx));
183  LhsEval surfaceVolume = Toolbox::template decay<LhsEval>(fs.saturation(phaseIdx))
184  * Toolbox::template decay<LhsEval>(fs.invB(phaseIdx))
185  * Toolbox::template decay<LhsEval>(intQuants.porosity());
186 
187  storage[conti0EqIdx + activeCompIdx] += surfaceVolume;
188 
189  // account for dissolved gas
190  if (phaseIdx == oilPhaseIdx && fsys.enableDissolvedGas()) {
191  unsigned activeGasCompIdx = fsys.canonicalToActiveCompIdx(gasCompIdx);
192  storage[conti0EqIdx + activeGasCompIdx]
193  += Toolbox::template decay<LhsEval>(intQuants.fluidState().Rs())
194  * surfaceVolume;
195  }
196 
197  // account for dissolved gas in water
198  if (phaseIdx == waterPhaseIdx && fsys.enableDissolvedGasInWater()) {
199  unsigned activeGasCompIdx = fsys.canonicalToActiveCompIdx(gasCompIdx);
200  storage[conti0EqIdx + activeGasCompIdx]
201  += Toolbox::template decay<LhsEval>(intQuants.fluidState().Rsw())
202  * surfaceVolume;
203  }
204 
205  // account for vaporized oil
206  if (phaseIdx == gasPhaseIdx && fsys.enableVaporizedOil()) {
207  unsigned activeOilCompIdx = fsys.canonicalToActiveCompIdx(oilCompIdx);
208  storage[conti0EqIdx + activeOilCompIdx]
209  += Toolbox::template decay<LhsEval>(intQuants.fluidState().Rv())
210  * surfaceVolume;
211  }
212 
213  // account for vaporized water
214  if (phaseIdx == gasPhaseIdx && fsys.enableVaporizedWater()) {
215  unsigned activeWaterCompIdx = fsys.canonicalToActiveCompIdx(waterCompIdx);
216  storage[conti0EqIdx + activeWaterCompIdx]
217  += Toolbox::template decay<LhsEval>(intQuants.fluidState().Rvw())
218  * surfaceVolume;
219  }
220  }
221 
222  adaptMassConservationQuantities_(storage, intQuants.pvtRegionIndex(), fsys);
223 
224  // deal with solvents (if present)
225  if constexpr (enableSolvent) {
226  SolventModule::addStorage(storage, intQuants);
227  }
228 
229  // deal with zFracton (if present)
230  if constexpr (enableExtbo) {
231  ExtboModule::addStorage(storage, intQuants);
232  }
233 
234  // deal with polymer (if present)
235  if constexpr (enablePolymer) {
236  PolymerModule::addStorage(storage, intQuants);
237  }
238 
239  // deal with energy (if present)
240  if constexpr (enableFullyImplicitThermal) {
241  EnergyModule::addStorage(storage, intQuants);
242  }
243 
244  // deal with foam (if present)
245  if constexpr (enableFoam) {
246  FoamModule::addStorage(storage, intQuants);
247  }
248 
249  // deal with salt (if present)
250  if constexpr (enableBrine) {
251  BrineModule::addStorage(storage, intQuants);
252  }
253 
254  // deal with bioeffects (if present)
255  if constexpr (enableBioeffects) {
256  BioeffectsModule::addStorage(storage, intQuants);
257  }
258  }
259 
265  template <class ModuleParamsT,
266  class RateVectorT,
267  class IntensiveQuantitiesT,
268  class ResidualNBInfoT>
269  OPM_HOST_DEVICE static void computeFlux(RateVectorT& flux,
270  RateVectorT& darcy,
271  const unsigned globalIndexIn,
272  const unsigned globalIndexEx,
273  const IntensiveQuantitiesT& intQuantsIn,
274  const IntensiveQuantitiesT& intQuantsEx,
275  const ResidualNBInfoT& nbInfo,
276  const ModuleParamsT& moduleParams)
277  {
278  OPM_TIMEBLOCK_LOCAL(computeFlux, Subsystem::Assembly);
279  flux = 0.0;
280  darcy = 0.0;
281 
282  calculateFluxes_(flux,
283  darcy,
284  intQuantsIn,
285  intQuantsEx,
286  globalIndexIn,
287  globalIndexEx,
288  nbInfo,
289  moduleParams);
290  }
291 
292  // This function demonstrates compatibility with the ElementContext-based interface.
293  // Actually using it will lead to double work since the element context already contains
294  // fluxes through its stored ExtensiveQuantities.
295  static void
296  computeFlux(RateVector& flux, const ElementContext& elemCtx, unsigned scvfIdx, unsigned timeIdx)
297  {
298  OPM_TIMEBLOCK_LOCAL(computeFlux, Subsystem::Assembly);
299  assert(timeIdx == 0);
300 
301  flux = 0.0;
302  RateVector darcy = 0.0;
303  // need for dary flux calculation
304  const auto& problem = elemCtx.problem();
305  const auto& stencil = elemCtx.stencil(timeIdx);
306  const auto& scvf = stencil.interiorFace(scvfIdx);
307 
308  unsigned interiorDofIdx = scvf.interiorIndex();
309  unsigned exteriorDofIdx = scvf.exteriorIndex();
310  assert(interiorDofIdx != exteriorDofIdx);
311 
312  // unsigned I = stencil.globalSpaceIndex(interiorDofIdx);
313  // unsigned J = stencil.globalSpaceIndex(exteriorDofIdx);
314  Scalar Vin = elemCtx.dofVolume(interiorDofIdx, /*timeIdx=*/0);
315  Scalar Vex = elemCtx.dofVolume(exteriorDofIdx, /*timeIdx=*/0);
316  const auto& globalIndexIn = stencil.globalSpaceIndex(interiorDofIdx);
317  const auto& globalIndexEx = stencil.globalSpaceIndex(exteriorDofIdx);
318  Scalar trans = problem.transmissibility(elemCtx, interiorDofIdx, exteriorDofIdx);
319  Scalar faceArea = scvf.area();
320  const auto faceDir = faceDirFromDirId(scvf.dirId());
321  Scalar thpresInToEx = problem.thresholdPressure(globalIndexIn, globalIndexEx);
322  Scalar thpresExToIn = problem.thresholdPressure(globalIndexEx, globalIndexIn);
323 
324  // estimate the gravity correction: for performance reasons we use a simplified
325  // approach for this flux module that assumes that gravity is constant and always
326  // acts into the downwards direction. (i.e., no centrifuge experiments, sorry.)
327  const Scalar g = problem.gravity()[dimWorld - 1];
328  const auto& intQuantsIn = elemCtx.intensiveQuantities(interiorDofIdx, timeIdx);
329  const auto& intQuantsEx = elemCtx.intensiveQuantities(exteriorDofIdx, timeIdx);
330 
331  // this is quite hacky because the dune grid interface does not provide a
332  // cellCenterDepth() method (so we ask the problem to provide it). The "good"
333  // solution would be to take the Z coordinate of the element centroids, but since
334  // ECL seems to like to be inconsistent on that front, it needs to be done like
335  // here...
336  const Scalar zIn = problem.dofCenterDepth(elemCtx, interiorDofIdx, timeIdx);
337  const Scalar zEx = problem.dofCenterDepth(elemCtx, exteriorDofIdx, timeIdx);
338  // the distances from the DOF's depths. (i.e., the additional depth of the
339  // exterior DOF)
340  const Scalar distZ = zIn - zEx;
341  // for thermal harmonic mean of half trans
342  const Scalar inAlpha = problem.thermalHalfTransmissibility(globalIndexIn, globalIndexEx);
343  const Scalar outAlpha = problem.thermalHalfTransmissibility(globalIndexEx, globalIndexIn);
344  const Scalar diffusivity = problem.diffusivity(globalIndexEx, globalIndexIn);
345  const Scalar dispersivity = problem.dispersivity(globalIndexEx, globalIndexIn);
346 
347  const ResidualNBInfo res_nbinfo {trans,
348  faceArea,
349  thpresInToEx,
350  thpresExToIn,
351  distZ * g,
352  faceDir,
353  Vin,
354  Vex,
355  inAlpha,
356  outAlpha,
357  diffusivity,
358  dispersivity};
359 
360  calculateFluxes_(flux,
361  darcy,
362  intQuantsIn,
363  intQuantsEx,
364  globalIndexIn,
365  globalIndexEx,
366  res_nbinfo,
367  problem.moduleParams());
368  }
369 
370  template <class RateVectorT,
371  class IntensiveQuantitiesT,
372  class ResidualNBInfoT,
373  class ModuleParamsT>
374  OPM_HOST_DEVICE static void calculateFluxes_(RateVectorT& flux,
375  RateVectorT& darcy,
376  const IntensiveQuantitiesT& intQuantsIn,
377  const IntensiveQuantitiesT& intQuantsEx,
378  const unsigned& globalIndexIn,
379  const unsigned& globalIndexEx,
380  const ResidualNBInfoT& nbInfo,
381  const ModuleParamsT& moduleParams)
382  {
383  OPM_TIMEBLOCK_LOCAL(calculateFluxes, Subsystem::Assembly);
384  const Scalar Vin = nbInfo.Vin;
385  const Scalar Vex = nbInfo.Vex;
386  const Scalar distZg = nbInfo.dZg;
387  const Scalar thpresInToEx = nbInfo.thpresInToEx;
388  const Scalar thpresExToIn = nbInfo.thpresExToIn;
389  const Scalar trans = nbInfo.trans;
390  const Scalar faceArea = nbInfo.faceArea;
391  FaceDir::DirEnum facedir = nbInfo.faceDir;
392 
393  const FluidSystem& fsys = intQuantsIn.getFluidSystem();
394 
395  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
396  if (!fsys.phaseIsActive(phaseIdx)) {
397  continue;
398  }
399  // darcy flux calculation
400  short dnIdx;
401  //
402  short upIdx;
403  // fake intices should only be used to get upwind anc compatibility with old functions
404  short interiorDofIdx = 0; // NB
405  short exteriorDofIdx = 1; // NB
406  Evaluation pressureDifference;
407  ExtensiveQuantities::calculatePhasePressureDiff_(upIdx,
408  dnIdx,
409  pressureDifference,
410  intQuantsIn,
411  intQuantsEx,
412  phaseIdx, // input
413  interiorDofIdx, // input
414  exteriorDofIdx, // input
415  Vin,
416  Vex,
417  globalIndexIn,
418  globalIndexEx,
419  distZg,
420  thpresInToEx,
421  thpresExToIn,
422  moduleParams);
423 
424  const IntensiveQuantities& up = (upIdx == interiorDofIdx) ? intQuantsIn : intQuantsEx;
425  unsigned globalUpIndex = (upIdx == interiorDofIdx) ? globalIndexIn : globalIndexEx;
426  // Use arithmetic average (more accurate with harmonic, but that requires recomputing
427  // the transmissbility)
428  Evaluation transMult = (intQuantsIn.rockCompTransMultiplier()
429  + Toolbox::value(intQuantsEx.rockCompTransMultiplier()))
430  / 2;
431  if constexpr (enableBioeffects || enableSaltPrecipitation) {
432  transMult
433  *= (intQuantsIn.permFactor() + Toolbox::value(intQuantsEx.permFactor())) / 2;
434  }
435 
436  Evaluation darcyFlux;
437  if (globalUpIndex == globalIndexIn) {
438  darcyFlux = pressureDifference * up.mobility(phaseIdx, facedir) * transMult
439  * (-trans / faceArea);
440  } else {
441  darcyFlux = pressureDifference
442  * (Toolbox::value(up.mobility(phaseIdx, facedir)) * transMult
443  * (-trans / faceArea));
444  }
445 
446  unsigned activeCompIdx
447  = fsys.canonicalToActiveCompIdx(fsys.solventComponentIndex(phaseIdx));
448  // NB! For the FLORES fluxes without derivatives
449  darcy[conti0EqIdx + activeCompIdx] = darcyFlux.value() * faceArea;
450 
451  unsigned pvtRegionIdx = up.pvtRegionIndex();
452  // if (upIdx == globalFocusDofIdx){
453  if (globalUpIndex == globalIndexIn) {
454  const auto& invB = getInvB_<FluidSystem, FluidState, Evaluation>(
455  up.fluidState(), phaseIdx, pvtRegionIdx, fsys);
456  const auto& surfaceVolumeFlux = invB * darcyFlux;
457 
458  evalPhaseFluxes_<Evaluation>(
459  flux, phaseIdx, pvtRegionIdx, surfaceVolumeFlux, up.fluidState());
460  if constexpr (enableFullyImplicitThermal) {
461  EnergyModule::template addPhaseEnthalpyFluxes_<Evaluation>(
462  flux, phaseIdx, darcyFlux, up.fluidState());
463  }
464  if constexpr (enableBioeffects) {
465  BioeffectsModule::template addBioeffectsFluxes_<Evaluation>(
466  flux, phaseIdx, darcyFlux, up);
467  }
468  if constexpr (enableBrine) {
469  BrineModule::template addBrineFluxes_<Evaluation, FluidState>(
470  flux, phaseIdx, darcyFlux, up.fluidState());
471  }
472  } else {
473  const auto& invB = getInvB_<FluidSystem, FluidState, Scalar>(
474  up.fluidState(), phaseIdx, pvtRegionIdx, fsys);
475  const auto& surfaceVolumeFlux = invB * darcyFlux;
476  evalPhaseFluxes_<Scalar>(
477  flux, phaseIdx, pvtRegionIdx, surfaceVolumeFlux, up.fluidState());
478  if constexpr (enableFullyImplicitThermal) {
479  EnergyModule::template addPhaseEnthalpyFluxes_<Scalar>(
480  flux, phaseIdx, darcyFlux, up.fluidState());
481  }
482  if constexpr (enableBioeffects) {
483  BioeffectsModule::template addBioeffectsFluxes_<Scalar>(
484  flux, phaseIdx, darcyFlux, up);
485  }
486  if constexpr (enableBrine) {
487  BrineModule::template addBrineFluxes_<Scalar, FluidState>(
488  flux, phaseIdx, darcyFlux, up.fluidState());
489  }
490  }
491  }
492 
493  // deal with solvents (if present)
494  static_assert(
495  !enableSolvent,
496  "Relevant computeFlux() method must be implemented for this module before enabling.");
497  // SolventModule::computeFlux(flux, elemCtx, scvfIdx, timeIdx);
498 
499  // deal with zFracton (if present)
500  static_assert(
501  !enableExtbo,
502  "Relevant computeFlux() method must be implemented for this module before enabling.");
503  // ExtboModule::computeFlux(flux, elemCtx, scvfIdx, timeIdx);
504 
505  // deal with polymer (if present)
506  static_assert(
507  !enablePolymer,
508  "Relevant computeFlux() method must be implemented for this module before enabling.");
509  // PolymerModule::computeFlux(flux, elemCtx, scvfIdx, timeIdx);
510 
511  // deal with convective mixing
512  if constexpr (enableConvectiveMixing) {
513  ConvectiveMixingModule::addConvectiveMixingFlux(
514  flux,
515  intQuantsIn,
516  intQuantsEx,
517  globalIndexIn,
518  globalIndexEx,
519  nbInfo.dZg,
520  nbInfo.trans,
521  nbInfo.faceArea,
522  moduleParams.convectiveMixingModuleParam);
523  }
524 
525  // deal with energy (if present)
526  if constexpr (enableFullyImplicitThermal) {
527  const Scalar inAlpha = nbInfo.inAlpha;
528  const Scalar outAlpha = nbInfo.outAlpha;
529  Evaluation heatFlux;
530 
531  short interiorDofIdx = 0; // NB
532  short exteriorDofIdx = 1; // NB
533 
534  EnergyModule::ExtensiveQuantities::updateEnergy(heatFlux,
535  interiorDofIdx, // focusDofIndex,
536  interiorDofIdx,
537  exteriorDofIdx,
538  intQuantsIn,
539  intQuantsEx,
540  intQuantsIn.fluidState(),
541  intQuantsEx.fluidState(),
542  inAlpha,
543  outAlpha,
544  faceArea);
545  EnergyModule::addHeatFlux(flux, heatFlux);
546  }
547  // NB need to be tha last energy call since it does scaling
548  // EnergyModule::computeFlux(flux, elemCtx, scvfIdx, timeIdx);
549 
550  // deal with foam (if present)
551  static_assert(
552  !enableFoam,
553  "Relevant computeFlux() method must be implemented for this module before enabling.");
554  // FoamModule::computeFlux(flux, elemCtx, scvfIdx, timeIdx);
555 
556  // deal with diffusion (if present). opm-models expects per area flux (added in the
557  // tmpdiffusivity).
558  if constexpr (enableDiffusion) {
559  typename DiffusionModule::ExtensiveQuantities::EvaluationArray
560  effectiveDiffusionCoefficient;
561  DiffusionModule::ExtensiveQuantities::update(
562  effectiveDiffusionCoefficient, intQuantsIn, intQuantsEx);
563  const Scalar diffusivity = nbInfo.diffusivity;
564  const Scalar tmpdiffusivity = diffusivity / faceArea;
565  DiffusionModule::addDiffusiveFlux(
566  flux, intQuantsIn, intQuantsEx, tmpdiffusivity, effectiveDiffusionCoefficient);
567  }
568 
569  // deal with dispersion (if present). opm-models expects per area flux (added in the
570  // tmpdispersivity).
571  if constexpr (enableDispersion) {
572  typename DispersionModule::ExtensiveQuantities::ScalarArray normVelocityAvg;
573  DispersionModule::ExtensiveQuantities::update(
574  normVelocityAvg, intQuantsIn, intQuantsEx);
575  const Scalar dispersivity = nbInfo.dispersivity;
576  const Scalar tmpdispersivity = dispersivity / faceArea;
577  DispersionModule::addDispersiveFlux(
578  flux, intQuantsIn, intQuantsEx, tmpdispersivity, normVelocityAvg);
579  }
580 
581  // apply the scaling for the urea equation in MICP
582  if constexpr (enableMICP) {
583  BioeffectsModule::applyScaling(flux);
584  }
585  }
586 
587  template <class BoundaryConditionData>
588  static void computeBoundaryFluxRate(RateVector& bdyFlux, const BoundaryConditionData& bdyInfo)
589  {
590  bdyFlux.setMassRate(bdyInfo.massRate, bdyInfo.pvtRegionIdx);
591  }
592 
593  template <class BoundaryConditionData>
594  static void computeBoundaryFluxFree(const Problem& problem,
595  RateVector& bdyFlux,
596  const BoundaryConditionData& bdyInfo,
597  const IntensiveQuantities& insideIntQuants,
598  unsigned globalSpaceIdx)
599  {
600  OPM_TIMEBLOCK_LOCAL(computeBoundaryFluxFree, Subsystem::Assembly);
601  std::array<short, numPhases> upIdx;
602  std::array<short, numPhases> dnIdx;
603  std::array<Evaluation, numPhases> volumeFlux;
604  std::array<Evaluation, numPhases> pressureDifference;
605 
606  ExtensiveQuantities::calculateBoundaryGradients_(problem,
607  globalSpaceIdx,
608  insideIntQuants,
609  bdyInfo.boundaryFaceIndex,
610  bdyInfo.faceArea,
611  bdyInfo.faceZCoord,
612  bdyInfo.exFluidState,
613  upIdx,
614  dnIdx,
615  volumeFlux,
616  pressureDifference);
617 
619  // advective fluxes of all components in all phases
621  bdyFlux = 0.0;
622  for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++phaseIdx) {
623  if (!FluidSystem::phaseIsActive(phaseIdx)) {
624  continue;
625  }
626  const auto& pBoundary = bdyInfo.exFluidState.pressure(phaseIdx);
627  const Evaluation& pInside = insideIntQuants.fluidState().pressure(phaseIdx);
628  const unsigned pvtRegionIdx = insideIntQuants.pvtRegionIndex();
629 
630  RateVector tmp(0.0);
631  const auto& darcyFlux = volumeFlux[phaseIdx];
632  // mass conservation
633  if (pBoundary < pInside) {
634  // outflux
635  const auto& invB = getInvB_<FluidSystem, FluidState, Evaluation>(
636  insideIntQuants.fluidState(), phaseIdx, pvtRegionIdx);
637  Evaluation surfaceVolumeFlux = invB * darcyFlux;
638  evalPhaseFluxes_<Evaluation>(tmp,
639  phaseIdx,
640  insideIntQuants.pvtRegionIndex(),
641  surfaceVolumeFlux,
642  insideIntQuants.fluidState());
643  if constexpr (enableFullyImplicitThermal) {
644  EnergyModule::template addPhaseEnthalpyFluxes_<Evaluation>(
645  tmp, phaseIdx, darcyFlux, insideIntQuants.fluidState());
646  }
647  } else if (pBoundary > pInside) {
648  // influx
649  using ScalarFluidState = decltype(bdyInfo.exFluidState);
650  const auto& invB = getInvB_<FluidSystem, ScalarFluidState, Scalar>(
651  bdyInfo.exFluidState, phaseIdx, pvtRegionIdx);
652  Evaluation surfaceVolumeFlux = invB * darcyFlux;
653  evalPhaseFluxes_<Scalar>(tmp,
654  phaseIdx,
655  insideIntQuants.pvtRegionIndex(),
656  surfaceVolumeFlux,
657  bdyInfo.exFluidState);
658  if constexpr (enableFullyImplicitThermal) {
659  EnergyModule::template addPhaseEnthalpyFluxes_<Scalar>(
660  tmp, phaseIdx, darcyFlux, bdyInfo.exFluidState);
661  }
662  }
663 
664  for (unsigned i = 0; i < tmp.size(); ++i) {
665  bdyFlux[i] += tmp[i];
666  }
667  }
668 
669  // conductive heat flux from boundary
670  if constexpr (enableFullyImplicitThermal) {
671  Evaluation heatFlux;
672  // avoid overload of functions with same number of elements in eclproblem
673  Scalar alpha = problem.eclTransmissibilities().thermalHalfTransBoundary(
674  globalSpaceIdx, bdyInfo.boundaryFaceIndex);
675  unsigned inIdx = 0; // dummy
676  // always calculated with derivatives of this cell
677  EnergyModule::ExtensiveQuantities::updateEnergyBoundary(heatFlux,
678  insideIntQuants,
679  /*focusDofIndex*/ inIdx,
680  inIdx,
681  alpha,
682  bdyInfo.exFluidState);
683  EnergyModule::addHeatFlux(bdyFlux, heatFlux);
684  }
685 
686  static_assert(
687  !enableSolvent,
688  "Relevant treatment of boundary conditions must be implemented before enabling.");
689  static_assert(
690  !enablePolymer,
691  "Relevant treatment of boundary conditions must be implemented before enabling.");
692 
693  const FluidSystem& fsys = insideIntQuants.getFluidSystem();
694 
695  // make sure that the right mass conservation quantities are used
696  adaptMassConservationQuantities_(bdyFlux, insideIntQuants.pvtRegionIndex(), fsys);
697 
698 #ifndef NDEBUG
699  for (unsigned i = 0; i < numEq; ++i) {
700  Valgrind::CheckDefined(bdyFlux[i]);
701  }
702  Valgrind::CheckDefined(bdyFlux);
703 #endif
704  }
705 
706  template <class ProblemLocal, class BoundaryConditionData, class RateVectorLocal>
707  OPM_HOST_DEVICE static void computeBoundaryThermal(const ProblemLocal& problem,
708  RateVectorLocal& bdyFlux,
709  const BoundaryConditionData& bdyInfo,
710  const IntensiveQuantities& insideIntQuants,
711  [[maybe_unused]] unsigned globalSpaceIdx)
712  {
713  OPM_TIMEBLOCK_LOCAL(computeBoundaryThermal, Subsystem::Assembly);
714  // only heat is allowed to flow through this boundary
715  bdyFlux = 0.0;
716 
717  // conductive heat flux from boundary
718  if constexpr (enableFullyImplicitThermal) {
719  Evaluation heatFlux;
720  // avoid overload of functions with same numeber of elements in eclproblem
721  Scalar alpha;
722  if constexpr (!std::is_empty_v<GetPropType<TypeTag, Properties::FluidSystem>>) {
723  alpha = problem.getAlpha(globalSpaceIdx, bdyInfo.boundaryFaceIndex);
724  } else {
725  alpha = problem.eclTransmissibilities().thermalHalfTransBoundary(globalSpaceIdx, bdyInfo.boundaryFaceIndex);
726  }
727  unsigned inIdx = 0; // dummy
728  // always calculated with derivatives of this cell
729  EnergyModule::ExtensiveQuantities::updateEnergyBoundary(heatFlux,
730  insideIntQuants,
731  /*focusDofIndex*/ inIdx,
732  inIdx,
733  alpha,
734  bdyInfo.exFluidState);
735  EnergyModule::addHeatFlux(bdyFlux, heatFlux);
736  }
737 
738 #ifndef NDEBUG
739  for (unsigned i = 0; i < numEq; ++i) {
740  Valgrind::CheckDefined(bdyFlux[i]);
741  }
742  Valgrind::CheckDefined(bdyFlux);
743 #endif
744  }
745 
746  template <class BoundaryConditionData, class RateVectorLocal, class LocalProblem>
747  OPM_HOST_DEVICE static void computeBoundaryFlux(RateVectorLocal& bdyFlux,
748  const LocalProblem& problem,
749  const BoundaryConditionData& bdyInfo,
750  const IntensiveQuantities& insideIntQuants,
751  unsigned globalSpaceIdx)
752  {
753  if constexpr (std::is_empty_v<GetPropType<TypeTag, Properties::FluidSystem>>) {
754  switch (bdyInfo.type) {
755  case BCType::NONE:
756  bdyFlux = 0.0;
757  break;
758  case BCType::RATE:
759  computeBoundaryFluxRate(bdyFlux, bdyInfo);
760  break;
761  case BCType::FREE:
762  case BCType::DIRICHLET:
763  computeBoundaryFluxFree(problem, bdyFlux, bdyInfo, insideIntQuants, globalSpaceIdx);
764  break;
765  case BCType::THERMAL:
766  computeBoundaryThermal(problem, bdyFlux, bdyInfo, insideIntQuants, globalSpaceIdx);
767  break;
768  default:
769  OPM_THROW(std::logic_error, "Unknown boundary condition type "
770  + std::to_string(static_cast<int>(bdyInfo.type))
771  + " in computeBoundaryFlux().");
772  }
773  } else { // Non-static fluid system used in GPU assembly
774  switch (bdyInfo.type) {
775  case BCType::NONE:
776  bdyFlux = 0.0;
777  break;
778  case BCType::THERMAL:
779  computeBoundaryThermal(problem, bdyFlux, bdyInfo, insideIntQuants, globalSpaceIdx);
780  break;
781  default:
782  OPM_THROW(std::logic_error,
783  "Boundary condition type " + std::to_string(static_cast<int>(bdyInfo.type))
784  + " is not supported for GPU fluid systems in computeBoundaryFlux().");
785  }
786  }
787  }
788 
789  static void computeSource(RateVector& source,
790  const Problem& problem,
791  const IntensiveQuantities& insideIntQuants,
792  unsigned globalSpaceIdex,
793  unsigned timeIdx)
794  {
795  OPM_TIMEBLOCK_LOCAL(computeSource, Subsystem::Assembly);
796  // retrieve the source term intrinsic to the problem
797  problem.source(source, globalSpaceIdex, timeIdx);
798 
799  // deal with bioeffects (if present)
800  if constexpr (enableBioeffects) {
801  BioeffectsModule::addSource(source, problem, insideIntQuants, globalSpaceIdex);
802  }
803 
804  // scale the source term of the energy equation
805  if constexpr (enableFullyImplicitThermal) {
806  source[Indices::contiEnergyEqIdx]
807  *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
808  }
809  }
810 
811  static void computeSourceDense(RateVector& source,
812  const Problem& problem,
813  const IntensiveQuantities& insideIntQuants,
814  unsigned globalSpaceIdex,
815  unsigned timeIdx)
816  {
817  source = 0.0;
818  problem.addToSourceDense(source, globalSpaceIdex, timeIdx);
819 
820  // deal with bioeffects (if present)
821  if constexpr (enableBioeffects) {
822  BioeffectsModule::addSource(source, problem, insideIntQuants, globalSpaceIdex);
823  }
824 
825  // scale the source term of the energy equation
826  if constexpr (enableFullyImplicitThermal) {
827  source[Indices::contiEnergyEqIdx]
828  *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
829  }
830  }
831 
835  void computeSource(RateVector& source,
836  const ElementContext& elemCtx,
837  unsigned dofIdx,
838  unsigned timeIdx) const
839  {
840  OPM_TIMEBLOCK_LOCAL(computeSource, Subsystem::Assembly);
841  // retrieve the source term intrinsic to the problem
842  elemCtx.problem().source(source, elemCtx, dofIdx, timeIdx);
843 
844  // deal with bioeffects (if present)
845  if constexpr (enableBioeffects) {
846  BioeffectsModule::addSource(source, elemCtx, dofIdx, timeIdx);
847  }
848 
849  // scale the source term of the energy equation
850  if constexpr (enableFullyImplicitThermal) {
851  source[Indices::contiEnergyEqIdx]
852  *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
853  }
854  }
855 
856  template <class UpEval, class FluidState>
857  static void evalPhaseFluxes_(RateVector& flux,
858  unsigned phaseIdx,
859  unsigned pvtRegionIdx,
860  const ExtensiveQuantities& extQuants,
861  const FluidState& upFs)
862  {
863  const auto& invB = getInvB_<FluidSystem, FluidState, UpEval>(upFs, phaseIdx, pvtRegionIdx);
864  const auto& surfaceVolumeFlux = invB * extQuants.volumeFlux(phaseIdx);
865  evalPhaseFluxes_<UpEval>(flux, phaseIdx, pvtRegionIdx, surfaceVolumeFlux, upFs);
866  }
867 
872  template <class UpEval, class Eval, class FluidState, class RateVectorT = RateVector>
873  OPM_HOST_DEVICE static void evalPhaseFluxes_(RateVectorT& flux,
874  unsigned phaseIdx,
875  unsigned pvtRegionIdx,
876  const Eval& surfaceVolumeFlux,
877  const FluidState& upFs)
878  {
879  const FluidSystem& fsys = upFs.fluidSystem();
880 
881  unsigned activeCompIdx
882  = fsys.canonicalToActiveCompIdx(fsys.solventComponentIndex(phaseIdx));
883 
884  if constexpr (blackoilConserveSurfaceVolume) {
885  flux[conti0EqIdx + activeCompIdx] += surfaceVolumeFlux;
886  } else {
887  flux[conti0EqIdx + activeCompIdx]
888  += surfaceVolumeFlux * fsys.referenceDensity(phaseIdx, pvtRegionIdx);
889  }
890 
891  if (phaseIdx == oilPhaseIdx) {
892  // dissolved gas (in the oil phase).
893  if (fsys.enableDissolvedGas()) {
894  const auto& Rs
895  = BlackOil::getRs_<FluidSystem, FluidState, UpEval>(upFs, pvtRegionIdx);
896 
897  const unsigned activeGasCompIdx = fsys.canonicalToActiveCompIdx(gasCompIdx);
898  if constexpr (blackoilConserveSurfaceVolume) {
899  flux[conti0EqIdx + activeGasCompIdx] += Rs * surfaceVolumeFlux;
900  } else {
901  flux[conti0EqIdx + activeGasCompIdx] += Rs * surfaceVolumeFlux
902  * fsys.referenceDensity(gasPhaseIdx, pvtRegionIdx);
903  }
904  }
905  } else if (phaseIdx == waterPhaseIdx) {
906  // dissolved gas (in the water phase).
907  if (fsys.enableDissolvedGasInWater()) {
908  const auto& Rsw
909  = BlackOil::getRsw_<FluidSystem, FluidState, UpEval>(upFs, pvtRegionIdx);
910 
911  const unsigned activeGasCompIdx = fsys.canonicalToActiveCompIdx(gasCompIdx);
912  if constexpr (blackoilConserveSurfaceVolume) {
913  flux[conti0EqIdx + activeGasCompIdx] += Rsw * surfaceVolumeFlux;
914  } else {
915  flux[conti0EqIdx + activeGasCompIdx] += Rsw * surfaceVolumeFlux
916  * fsys.referenceDensity(gasPhaseIdx, pvtRegionIdx);
917  }
918  }
919  } else if (phaseIdx == gasPhaseIdx) {
920  // vaporized oil (in the gas phase).
921  if (fsys.enableVaporizedOil()) {
922  const auto& Rv
923  = BlackOil::getRv_<FluidSystem, FluidState, UpEval>(upFs, pvtRegionIdx);
924 
925  const unsigned activeOilCompIdx = fsys.canonicalToActiveCompIdx(oilCompIdx);
926  if constexpr (blackoilConserveSurfaceVolume) {
927  flux[conti0EqIdx + activeOilCompIdx] += Rv * surfaceVolumeFlux;
928  } else {
929  flux[conti0EqIdx + activeOilCompIdx] += Rv * surfaceVolumeFlux
930  * fsys.referenceDensity(oilPhaseIdx, pvtRegionIdx);
931  }
932  }
933  // vaporized water (in the gas phase).
934  if (fsys.enableVaporizedWater()) {
935  const auto& Rvw
936  = BlackOil::getRvw_<FluidSystem, FluidState, UpEval>(upFs, pvtRegionIdx);
937 
938  const unsigned activeWaterCompIdx = fsys.canonicalToActiveCompIdx(waterCompIdx);
939  if constexpr (blackoilConserveSurfaceVolume) {
940  flux[conti0EqIdx + activeWaterCompIdx] += Rvw * surfaceVolumeFlux;
941  } else {
942  flux[conti0EqIdx + activeWaterCompIdx] += Rvw * surfaceVolumeFlux
943  * fsys.referenceDensity(waterPhaseIdx, pvtRegionIdx);
944  }
945  }
946  }
947  }
948 
952  template <class ScalarVector>
953  OPM_HOST_DEVICE static void adaptMassConservationQuantities_(ScalarVector& container,
954  unsigned pvtRegionIdx)
955  {
956  // Delegate to the generic overload using a default-constructed static FluidSystem
957  // instance. Valid because the static FluidSystem is stateless (std::is_empty_v).
958  adaptMassConservationQuantities_(container, pvtRegionIdx, FluidSystem{});
959  }
960 
975  template <class ScalarVector, class FsysType>
976  OPM_HOST_DEVICE static void adaptMassConservationQuantities_(ScalarVector& container,
977  unsigned pvtRegionIdx,
978  const FsysType& fsys)
979  {
980  if constexpr (!blackoilConserveSurfaceVolume) {
981  // convert "surface volume" to mass. this is complicated a bit by the fact that
982  // not all phases are necessarily enabled. (we here assume that if a fluid phase
983  // is disabled, its respective "main" component is not considered as well.)
984 
985  if constexpr (waterEnabled) {
986  const unsigned activeWaterCompIdx = fsys.canonicalToActiveCompIdx(waterCompIdx);
987  container[conti0EqIdx + activeWaterCompIdx]
988  *= fsys.referenceDensity(waterPhaseIdx, pvtRegionIdx);
989  }
990 
991  if constexpr (gasEnabled) {
992  const unsigned activeGasCompIdx = fsys.canonicalToActiveCompIdx(gasCompIdx);
993  container[conti0EqIdx + activeGasCompIdx]
994  *= fsys.referenceDensity(gasPhaseIdx, pvtRegionIdx);
995  }
996 
997  if constexpr (oilEnabled) {
998  const unsigned activeOilCompIdx = fsys.canonicalToActiveCompIdx(oilCompIdx);
999  container[conti0EqIdx + activeOilCompIdx]
1000  *= fsys.referenceDensity(oilPhaseIdx, pvtRegionIdx);
1001  }
1002  }
1003  }
1004 
1005  // NNC does not have a direction
1006  static FaceDir::DirEnum faceDirFromDirId(const int dirId)
1007  {
1008  return dirId < 0 ? FaceDir::DirEnum::Unknown : FaceDir::FromIntersectionIndex(dirId);
1009  }
1010 };
1011 
1012 } // namespace Opm
1013 
1014 #endif
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
Definition: blackoilfoammodules.hh:57
Calculates the local residual of the black oil model.
Definition: blackoillocalresidualtpfa.hh:66
static OPM_HOST_DEVICE void adaptMassConservationQuantities_(ScalarVector &container, unsigned pvtRegionIdx)
Wrapper of adaptMassConservationQuantities_ that does not take a fluid system instance.
Definition: blackoillocalresidualtpfa.hh:953
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.
Definition: blackoilmodules.hpp:62
Classes required for dynamic convective mixing.
Declares the properties required by the black oil model.
Declare the properties used by the infrastructure code of the finite volume discretizations.
static OPM_HOST_DEVICE void computeFlux(RateVectorT &flux, RateVectorT &darcy, const unsigned globalIndexIn, const unsigned globalIndexEx, const IntensiveQuantitiesT &intQuantsIn, const IntensiveQuantitiesT &intQuantsEx, const ResidualNBInfoT &nbInfo, const ModuleParamsT &moduleParams)
This function works like the ElementContext-based version with one main difference: The darcy flux is...
Definition: blackoillocalresidualtpfa.hh:269
Definition: blackoillocalresidualtpfa.hh:138
static OPM_HOST_DEVICE void evalPhaseFluxes_(RateVectorT &flux, unsigned phaseIdx, unsigned pvtRegionIdx, const Eval &surfaceVolumeFlux, const FluidState &upFs)
Helper function to calculate the flux of mass in terms of conservation quantities via specific fluid ...
Definition: blackoillocalresidualtpfa.hh:873
void computeStorage(Dune::FieldVector< LhsEval, numEq > &storage, const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx) const
Evaluate the amount all conservation quantities (e.g.
Definition: blackoillocalresidualtpfa.hh:157
Provides the auxiliary methods required for consideration of the dispersion equation.
void computeSource(RateVector &source, const ElementContext &elemCtx, unsigned dofIdx, unsigned timeIdx) const
Calculate the source term of the equation.
Definition: blackoillocalresidualtpfa.hh:835
static OPM_HOST_DEVICE void adaptMassConservationQuantities_(ScalarVector &container, unsigned pvtRegionIdx, const FsysType &fsys)
Helper function to convert the mass-related parts of a vector that stores conservation quantities in ...
Definition: blackoillocalresidualtpfa.hh:976
Provides the auxiliary methods required for consideration of the diffusion equation.