108 using Toolbox = MathToolbox<Evaluation>;
110 using TabulatedFunction =
typename BlackOilBioeffectsParams<Scalar>::TabulatedFunction;
112 enum { gasCompIdx = FluidSystem::gasCompIdx };
114 static constexpr unsigned microbialConcentrationIdx = Indices::microbialConcentrationIdx;
115 static constexpr unsigned oxygenConcentrationIdx = Indices::oxygenConcentrationIdx;
116 static constexpr unsigned ureaConcentrationIdx = Indices::ureaConcentrationIdx;
117 static constexpr unsigned biofilmVolumeFractionIdx = Indices::biofilmVolumeFractionIdx;
118 static constexpr unsigned calciteVolumeFractionIdx = Indices::calciteVolumeFractionIdx;
119 static constexpr unsigned contiMicrobialEqIdx = Indices::contiMicrobialEqIdx;
120 static constexpr unsigned contiOxygenEqIdx = Indices::contiOxygenEqIdx;
121 static constexpr unsigned contiUreaEqIdx = Indices::contiUreaEqIdx;
122 static constexpr unsigned contiBiofilmEqIdx = Indices::contiBiofilmEqIdx;
123 static constexpr unsigned contiCalciteEqIdx = Indices::contiCalciteEqIdx;
124 static constexpr unsigned waterPhaseIdx = FluidSystem::waterPhaseIdx;
126 static constexpr unsigned enableBioeffects = enableBioeffectsV;
127 static constexpr bool enableMICP = Indices::enableMICP;
143 if constexpr (enableBioeffects)
151 Simulator& simulator)
153 if constexpr (enableBioeffects)
157 static bool eqApplies(
unsigned eqIdx)
159 if constexpr (enableBioeffects)
160 if constexpr (enableMICP)
161 return eqIdx == contiMicrobialEqIdx || eqIdx == contiOxygenEqIdx || eqIdx == contiUreaEqIdx
162 || eqIdx == contiBiofilmEqIdx || eqIdx == contiCalciteEqIdx;
164 return eqIdx == contiMicrobialEqIdx || eqIdx == contiBiofilmEqIdx;
169 static Scalar eqWeight([[maybe_unused]]
unsigned eqIdx)
171 assert(eqApplies(eqIdx));
174 return static_cast<Scalar
>(1.0);
178 template <
class StorageType>
179 OPM_HOST_DEVICE
static void addStorage(StorageType& storage,
180 const IntensiveQuantities& intQuants)
182 using LhsEval =
typename StorageType::value_type;
183 if constexpr (enableBioeffects) {
184 const auto& fs = intQuants.fluidState();
185 LhsEval surfaceVolumeWater = Toolbox::template decay<LhsEval>(fs.saturation(waterPhaseIdx)) *
186 Toolbox::template decay<LhsEval>(fs.invB(waterPhaseIdx)) *
187 Toolbox::template decay<LhsEval>(intQuants.porosity());
189 surfaceVolumeWater = max(surfaceVolumeWater, 1e-10);
191 const LhsEval accumulationMicrobes = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.microbialConcentration());
192 storage[contiMicrobialEqIdx] += accumulationMicrobes;
194 const LhsEval accumulationBiofilm = Toolbox::template decay<LhsEval>(intQuants.biofilmVolumeFraction());
195 storage[contiBiofilmEqIdx] += accumulationBiofilm;
196 if constexpr (enableMICP) {
198 const LhsEval accumulationOxygen = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.oxygenConcentration());
199 storage[contiOxygenEqIdx] += accumulationOxygen;
201 const LhsEval accumulationUrea = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.ureaConcentration());
202 storage[contiUreaEqIdx] += accumulationUrea;
205 const LhsEval accumulationCalcite = Toolbox::template decay<LhsEval>(intQuants.calciteVolumeFraction());
206 storage[contiCalciteEqIdx] += accumulationCalcite;
211 template <
class UpEval>
212 static void addBioeffectsFluxes_(RateVector& flux,
214 const Evaluation& volumeFlux,
215 const IntensiveQuantities& upFs)
217 if (phaseIdx == waterPhaseIdx) {
218 if constexpr (enableBioeffects) {
219 flux[contiMicrobialEqIdx] =
220 decay<UpEval>(upFs.microbialConcentration())
221 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
223 if constexpr (enableMICP) {
224 flux[contiOxygenEqIdx] =
225 decay<UpEval>(upFs.oxygenConcentration())
226 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
228 flux[contiUreaEqIdx] =
229 decay<UpEval>(upFs.ureaConcentration())
230 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
238 static void applyScaling(RateVector& flux)
240 if constexpr (enableMICP) {
245 static void computeFlux([[maybe_unused]] RateVector& flux,
246 [[maybe_unused]]
const ElementContext& elemCtx,
247 [[maybe_unused]]
unsigned scvfIdx,
248 [[maybe_unused]]
unsigned timeIdx)
250 if constexpr (enableBioeffects) {
251 const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
252 unsigned focusIdx = elemCtx.focusDofIndex();
253 unsigned upIdx = extQuants.upstreamIndex(waterPhaseIdx);
254 flux[contiMicrobialEqIdx] = 0.0;
255 if constexpr (enableMICP) {
256 flux[contiOxygenEqIdx] = 0.0;
257 flux[contiUreaEqIdx] = 0.0;
259 if (upIdx == focusIdx)
260 addBioeffectsFluxes_<Evaluation>(flux, elemCtx, scvfIdx, timeIdx);
262 addBioeffectsFluxes_<Scalar>(flux, elemCtx, scvfIdx, timeIdx);
266 template <
class UpstreamEval>
267 static void addBioeffectsFluxes_(RateVector& flux,
268 const ElementContext& elemCtx,
272 const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
273 unsigned upIdx = extQuants.upstreamIndex(waterPhaseIdx);
274 const auto& up = elemCtx.intensiveQuantities(upIdx, timeIdx);
275 const auto& volFlux = extQuants.volumeFlux(waterPhaseIdx);
276 addBioeffectsFluxes_<UpstreamEval>(flux, waterPhaseIdx, volFlux, up);
279 static void addSource(RateVector& source,
280 const Problem& problem,
281 const IntensiveQuantities& intQuants,
282 unsigned globalSpaceIdex)
284 if constexpr (enableBioeffects) {
285 const auto b = intQuants.fluidState().invB(waterPhaseIdx);
286 unsigned satnumIdx = problem.satnumRegionIndex(globalSpaceIdex);
287 Scalar rho_b = densityBiofilm(satnumIdx);
288 Scalar k_d = microbialDeathRate(satnumIdx);
289 Scalar mu = maximumGrowthRate(satnumIdx);
290 Scalar k_n = halfVelocityGrowth(satnumIdx);
291 Scalar Y = yieldGrowthCoefficient(satnumIdx);
292 Scalar k_a = microbialAttachmentRate(satnumIdx);
293 Scalar k_str = detachmentRate(satnumIdx);
294 Scalar eta = detachmentExponent(satnumIdx);
295 const auto& velocityInf = problem.model().linearizer().getVelocityInfo();
296 auto velocityInfos = velocityInf[globalSpaceIdex];
297 Scalar normVelocityCell =
298 std::accumulate(velocityInfos.begin(), velocityInfos.end(), 0.0,
299 [](
const auto acc,
const auto& info)
300 { return max(acc, std::abs(info.velocity[waterPhaseIdx])); });
301 if constexpr (enableMICP) {
302 Scalar rho_c = densityCalcite(satnumIdx);
303 Scalar k_u = halfVelocityUrea(satnumIdx);
304 Scalar mu_u = maximumUreaUtilization(satnumIdx);
305 Scalar F = oxygenConsumptionFactor(satnumIdx);
306 Scalar Y_uc = yieldUreaToCalciteCoefficient(satnumIdx);
310 Evaluation k_g = mu * intQuants.oxygenConcentration() / (k_n + intQuants.oxygenConcentration());
311 Evaluation k_c = mu_u * intQuants.ureaConcentration() / (k_u + intQuants.ureaConcentration());
312 if (intQuants.oxygenConcentration() < 0) {
313 k_g = mu * intQuants.oxygenConcentration() / k_n;
315 if (intQuants.ureaConcentration() < 0) {
316 k_c = mu_u * intQuants.ureaConcentration() / k_u;
321 source[Indices::contiMicrobialEqIdx] += intQuants.microbialConcentration() * intQuants.porosity() *
322 b * (Y * k_g - k_d - k_a) +
323 rho_b * intQuants.biofilmVolumeFraction() * k_str * pow(normVelocityCell / intQuants.porosity(), eta);
325 source[Indices::contiOxygenEqIdx] -= (intQuants.microbialConcentration() * intQuants.porosity() *
326 b + rho_b * intQuants.biofilmVolumeFraction()) * F * k_g;
328 source[Indices::contiUreaEqIdx] -= rho_b * intQuants.biofilmVolumeFraction() * k_c;
330 source[Indices::contiBiofilmEqIdx] += intQuants.biofilmVolumeFraction() * (Y * k_g - k_d -
331 k_str * pow(normVelocityCell / intQuants.porosity(), eta) - Y_uc * (rho_b / rho_c) *
332 intQuants.biofilmVolumeFraction() * k_c / (intQuants.porosity() +
333 intQuants.biofilmVolumeFraction())) + k_a * intQuants.microbialConcentration() *
334 intQuants.porosity() * b / rho_b;
336 source[Indices::contiCalciteEqIdx] += (rho_b / rho_c) * intQuants.biofilmVolumeFraction() * Y_uc * k_c;
342 const Scalar normVelocityCellG =
343 std::accumulate(velocityInfos.begin(), velocityInfos.end(), 0.0,
344 [](
const auto acc,
const auto& info)
345 { return max(acc, std::abs(info.velocity[1])); });
346 normVelocityCell = max(normVelocityCellG, normVelocityCell);
348 const auto& fs = intQuants.fluidState();
349 const auto& Sw = fs.saturation(waterPhaseIdx);
350 const auto& Rsw = fs.Rsw();
351 const auto& rhow = fs.density(waterPhaseIdx);
352 unsigned pvtRegionIndex = fs.pvtRegionIndex();
354 const auto& xG = RswToMassFraction(pvtRegionIndex, Rsw);
357 const Evaluation& poro = intQuants.porosity();
358 Scalar rho_gRef = FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIndex);
361 Evaluation k_g = mu * (xG * rhow * poro * Sw / (xG * rhow * poro * Sw + k_n));
363 k_g = mu * (xG * rhow * poro * Sw / k_n);
368 source[contiMicrobialEqIdx] += Sw * intQuants.microbialConcentration() * intQuants.porosity() * b
370 + intQuants.biofilmVolumeFraction() * rho_b * k_str
371 * pow(normVelocityCell / intQuants.porosity(), eta);
373 source[contiBiofilmEqIdx] += (k_g - k_d - k_str * pow(normVelocityCell / intQuants.porosity(), eta))
374 * intQuants.biofilmVolumeFraction()
375 + k_a * Sw * intQuants.microbialConcentration() * intQuants.porosity() * b / rho_b;
378 unsigned activeGasCompIdx = FluidSystem::canonicalToActiveCompIdx(gasCompIdx);
379 source[activeGasCompIdx] -= intQuants.biofilmVolumeFraction() * rho_b * k_g / (Y * rho_gRef);
384 static void addSource([[maybe_unused]] RateVector& source,
385 [[maybe_unused]]
const ElementContext& elemCtx,
386 [[maybe_unused]]
unsigned dofIdx,
387 [[maybe_unused]]
unsigned timeIdx)
389 if constexpr (enableMICP) {
390 const auto& problem = elemCtx.problem();
391 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, timeIdx);
392 addSource(source, problem, intQuants, dofIdx);
396 static const Scalar densityBiofilm(
unsigned satnumRegionIdx)
398 return params_.densityBiofilm_[satnumRegionIdx];
401 static const Scalar densityCalcite(
unsigned satnumRegionIdx)
403 return params_.densityCalcite_[satnumRegionIdx];
406 static const Scalar detachmentRate(
unsigned satnumRegionIdx)
408 return params_.detachmentRate_[satnumRegionIdx];
411 static const Scalar detachmentExponent(
unsigned satnumRegionIdx)
413 return params_.detachmentExponent_[satnumRegionIdx];
416 static const Scalar halfVelocityGrowth(
unsigned satnumRegionIdx)
418 return params_.halfVelocityGrowth_[satnumRegionIdx];
421 static const Scalar halfVelocityUrea(
unsigned satnumRegionIdx)
423 return params_.halfVelocityUrea_[satnumRegionIdx];
426 static const Scalar maximumGrowthRate(
unsigned satnumRegionIdx)
428 return params_.maximumGrowthRate_[satnumRegionIdx];
431 static const Scalar maximumUreaUtilization(
unsigned satnumRegionIdx)
433 return params_.maximumUreaUtilization_[satnumRegionIdx];
436 static const Scalar microbialAttachmentRate(
unsigned satnumRegionIdx)
438 return params_.microbialAttachmentRate_[satnumRegionIdx];
441 static const Scalar microbialDeathRate(
unsigned satnumRegionIdx)
443 return params_.microbialDeathRate_[satnumRegionIdx];
446 static const Scalar oxygenConsumptionFactor(
unsigned satnumRegionIdx)
448 return params_.oxygenConsumptionFactor_[satnumRegionIdx];
451 static const Scalar yieldGrowthCoefficient(
unsigned satnumRegionIdx)
453 return params_.yieldGrowthCoefficient_[satnumRegionIdx];
456 static const Scalar yieldUreaToCalciteCoefficient(
unsigned satnumRegionIdx)
458 return params_.yieldUreaToCalciteCoefficient_[satnumRegionIdx];
461 static const Scalar bioDiffCoefficient(
unsigned pvtRegionIdx,
unsigned compIdx)
463 return params_.bioDiffCoefficient_[pvtRegionIdx][compIdx];
466 static const TabulatedFunction& permfactTable(
const ElementContext& elemCtx,
470 unsigned satnumRegionIdx = elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
471 return params_.permfactTable_[satnumRegionIdx];
474 static const TabulatedFunction& permfactTable(
unsigned satnumRegionIdx)
476 return params_.permfactTable_[satnumRegionIdx];
479 static const TabulatedFunction& pcfactTable(
unsigned satnumRegionIdx)
481 return params_.pcfactTable_[satnumRegionIdx];
484 static bool hasPcfactTables()
486 if constexpr (enableBioeffects && !enableMICP) {
487 return !params_.pcfactTable_.empty();
495 static BlackOilBioeffectsParams<Scalar> params_;
497 static Evaluation RswToMassFraction(
unsigned regionIdx,
const Evaluation& Rsw) {
498 Scalar rho_wRef = FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, regionIdx);
499 Scalar rho_gRef = FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, regionIdx);
501 const Evaluation rho_oG = Rsw * rho_gRef;
503 return rho_oG / (rho_wRef + rho_oG);