106 using Toolbox = MathToolbox<Evaluation>;
108 using TabulatedFunction =
typename BlackOilBioeffectsParams<Scalar>::TabulatedFunction;
110 enum { gasCompIdx = FluidSystem::gasCompIdx };
112 static constexpr unsigned microbialConcentrationIdx = Indices::microbialConcentrationIdx;
113 static constexpr unsigned oxygenConcentrationIdx = Indices::oxygenConcentrationIdx;
114 static constexpr unsigned ureaConcentrationIdx = Indices::ureaConcentrationIdx;
115 static constexpr unsigned biofilmVolumeFractionIdx = Indices::biofilmVolumeFractionIdx;
116 static constexpr unsigned calciteVolumeFractionIdx = Indices::calciteVolumeFractionIdx;
117 static constexpr unsigned contiMicrobialEqIdx = Indices::contiMicrobialEqIdx;
118 static constexpr unsigned contiOxygenEqIdx = Indices::contiOxygenEqIdx;
119 static constexpr unsigned contiUreaEqIdx = Indices::contiUreaEqIdx;
120 static constexpr unsigned contiBiofilmEqIdx = Indices::contiBiofilmEqIdx;
121 static constexpr unsigned contiCalciteEqIdx = Indices::contiCalciteEqIdx;
122 static constexpr unsigned waterPhaseIdx = FluidSystem::waterPhaseIdx;
124 static constexpr unsigned enableBioeffects = enableBioeffectsV;
125 static constexpr bool enableMICP = Indices::enableMICP;
141 if constexpr (enableBioeffects)
149 Simulator& simulator)
151 if constexpr (enableBioeffects)
155 static bool eqApplies(
unsigned eqIdx)
157 if constexpr (enableBioeffects)
158 if constexpr (enableMICP)
159 return eqIdx == contiMicrobialEqIdx || eqIdx == contiOxygenEqIdx || eqIdx == contiUreaEqIdx
160 || eqIdx == contiBiofilmEqIdx || eqIdx == contiCalciteEqIdx;
162 return eqIdx == contiMicrobialEqIdx || eqIdx == contiBiofilmEqIdx;
167 static Scalar eqWeight([[maybe_unused]]
unsigned eqIdx)
169 assert(eqApplies(eqIdx));
172 return static_cast<Scalar
>(1.0);
176 template <
class LhsEval>
177 static void addStorage(Dune::FieldVector<LhsEval, numEq>& storage,
178 const IntensiveQuantities& intQuants)
180 if constexpr (enableBioeffects) {
181 const auto& fs = intQuants.fluidState();
182 LhsEval surfaceVolumeWater = Toolbox::template decay<LhsEval>(fs.saturation(waterPhaseIdx)) *
183 Toolbox::template decay<LhsEval>(fs.invB(waterPhaseIdx)) *
184 Toolbox::template decay<LhsEval>(intQuants.porosity());
186 surfaceVolumeWater = max(surfaceVolumeWater, 1e-10);
188 const LhsEval accumulationMicrobes = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.microbialConcentration());
189 storage[contiMicrobialEqIdx] += accumulationMicrobes;
191 const LhsEval accumulationBiofilm = Toolbox::template decay<LhsEval>(intQuants.biofilmVolumeFraction());
192 storage[contiBiofilmEqIdx] += accumulationBiofilm;
193 if constexpr (enableMICP) {
195 const LhsEval accumulationOxygen = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.oxygenConcentration());
196 storage[contiOxygenEqIdx] += accumulationOxygen;
198 const LhsEval accumulationUrea = surfaceVolumeWater * Toolbox::template decay<LhsEval>(intQuants.ureaConcentration());
199 storage[contiUreaEqIdx] += accumulationUrea;
202 const LhsEval accumulationCalcite = Toolbox::template decay<LhsEval>(intQuants.calciteVolumeFraction());
203 storage[contiCalciteEqIdx] += accumulationCalcite;
208 template <
class UpEval>
209 static void addBioeffectsFluxes_(RateVector& flux,
211 const Evaluation& volumeFlux,
212 const IntensiveQuantities& upFs)
214 if (phaseIdx == waterPhaseIdx) {
215 if constexpr (enableBioeffects) {
216 flux[contiMicrobialEqIdx] =
217 decay<UpEval>(upFs.microbialConcentration())
218 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
220 if constexpr (enableMICP) {
221 flux[contiOxygenEqIdx] =
222 decay<UpEval>(upFs.oxygenConcentration())
223 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
225 flux[contiUreaEqIdx] =
226 decay<UpEval>(upFs.ureaConcentration())
227 * decay<UpEval>(upFs.fluidState().invB(waterPhaseIdx))
235 static void applyScaling(RateVector& flux)
237 if constexpr (enableMICP) {
242 static void computeFlux([[maybe_unused]] RateVector& flux,
243 [[maybe_unused]]
const ElementContext& elemCtx,
244 [[maybe_unused]]
unsigned scvfIdx,
245 [[maybe_unused]]
unsigned timeIdx)
247 if constexpr (enableBioeffects) {
248 const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
249 unsigned focusIdx = elemCtx.focusDofIndex();
250 unsigned upIdx = extQuants.upstreamIndex(waterPhaseIdx);
251 flux[contiMicrobialEqIdx] = 0.0;
252 if constexpr (enableMICP) {
253 flux[contiOxygenEqIdx] = 0.0;
254 flux[contiUreaEqIdx] = 0.0;
256 if (upIdx == focusIdx)
257 addBioeffectsFluxes_<Evaluation>(flux, elemCtx, scvfIdx, timeIdx);
259 addBioeffectsFluxes_<Scalar>(flux, elemCtx, scvfIdx, timeIdx);
263 template <
class UpstreamEval>
264 static void addBioeffectsFluxes_(RateVector& flux,
265 const ElementContext& elemCtx,
269 const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
270 unsigned upIdx = extQuants.upstreamIndex(waterPhaseIdx);
271 const auto& up = elemCtx.intensiveQuantities(upIdx, timeIdx);
272 const auto& volFlux = extQuants.volumeFlux(waterPhaseIdx);
273 addBioeffectsFluxes_<UpstreamEval>(flux, waterPhaseIdx, volFlux, up);
276 static void addSource(RateVector& source,
277 const Problem& problem,
278 const IntensiveQuantities& intQuants,
279 unsigned globalSpaceIdex)
281 if constexpr (enableBioeffects) {
282 const auto b = intQuants.fluidState().invB(waterPhaseIdx);
283 unsigned satnumIdx = problem.satnumRegionIndex(globalSpaceIdex);
284 Scalar rho_b = densityBiofilm(satnumIdx);
285 Scalar k_d = microbialDeathRate(satnumIdx);
286 Scalar mu = maximumGrowthRate(satnumIdx);
287 Scalar k_n = halfVelocityGrowth(satnumIdx);
288 Scalar Y = yieldGrowthCoefficient(satnumIdx);
289 Scalar k_a = microbialAttachmentRate(satnumIdx);
290 Scalar k_str = detachmentRate(satnumIdx);
291 Scalar eta = detachmentExponent(satnumIdx);
292 const auto& velocityInf = problem.model().linearizer().getVelocityInfo();
293 auto velocityInfos = velocityInf[globalSpaceIdex];
294 Scalar normVelocityCell =
295 std::accumulate(velocityInfos.begin(), velocityInfos.end(), 0.0,
296 [](
const auto acc,
const auto& info)
297 { return max(acc, std::abs(info.velocity[waterPhaseIdx])); });
298 if constexpr (enableMICP) {
299 Scalar rho_c = densityCalcite(satnumIdx);
300 Scalar k_u = halfVelocityUrea(satnumIdx);
301 Scalar mu_u = maximumUreaUtilization(satnumIdx);
302 Scalar F = oxygenConsumptionFactor(satnumIdx);
303 Scalar Y_uc = yieldUreaToCalciteCoefficient(satnumIdx);
307 Evaluation k_g = mu * intQuants.oxygenConcentration() / (k_n + intQuants.oxygenConcentration());
308 Evaluation k_c = mu_u * intQuants.ureaConcentration() / (k_u + intQuants.ureaConcentration());
309 if (intQuants.oxygenConcentration() < 0) {
310 k_g = mu * intQuants.oxygenConcentration() / k_n;
312 if (intQuants.ureaConcentration() < 0) {
313 k_c = mu_u * intQuants.ureaConcentration() / k_u;
318 source[Indices::contiMicrobialEqIdx] += intQuants.microbialConcentration() * intQuants.porosity() *
319 b * (Y * k_g - k_d - k_a) +
320 rho_b * intQuants.biofilmVolumeFraction() * k_str * pow(normVelocityCell / intQuants.porosity(), eta);
322 source[Indices::contiOxygenEqIdx] -= (intQuants.microbialConcentration() * intQuants.porosity() *
323 b + rho_b * intQuants.biofilmVolumeFraction()) * F * k_g;
325 source[Indices::contiUreaEqIdx] -= rho_b * intQuants.biofilmVolumeFraction() * k_c;
327 source[Indices::contiBiofilmEqIdx] += intQuants.biofilmVolumeFraction() * (Y * k_g - k_d -
328 k_str * pow(normVelocityCell / intQuants.porosity(), eta) - Y_uc * (rho_b / rho_c) *
329 intQuants.biofilmVolumeFraction() * k_c / (intQuants.porosity() +
330 intQuants.biofilmVolumeFraction())) + k_a * intQuants.microbialConcentration() *
331 intQuants.porosity() * b / rho_b;
333 source[Indices::contiCalciteEqIdx] += (rho_b / rho_c) * intQuants.biofilmVolumeFraction() * Y_uc * k_c;
339 const Scalar normVelocityCellG =
340 std::accumulate(velocityInfos.begin(), velocityInfos.end(), 0.0,
341 [](
const auto acc,
const auto& info)
342 { return max(acc, std::abs(info.velocity[1])); });
343 normVelocityCell = max(normVelocityCellG, normVelocityCell);
345 const auto& fs = intQuants.fluidState();
346 const auto& Sw = fs.saturation(waterPhaseIdx);
347 const auto& Rsw = fs.Rsw();
348 const auto& rhow = fs.density(waterPhaseIdx);
349 unsigned pvtRegionIndex = fs.pvtRegionIndex();
351 const auto& xG = RswToMassFraction(pvtRegionIndex, Rsw);
354 const Evaluation& poro = intQuants.porosity();
355 Scalar rho_gRef = FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, pvtRegionIndex);
358 Evaluation k_g = mu * (xG * rhow * poro * Sw / (xG * rhow * poro * Sw + k_n));
360 k_g = mu * (xG * rhow * poro * Sw / k_n);
365 source[contiMicrobialEqIdx] += Sw * intQuants.microbialConcentration() * intQuants.porosity() * b
367 + intQuants.biofilmVolumeFraction() * rho_b * k_str
368 * pow(normVelocityCell / intQuants.porosity(), eta);
370 source[contiBiofilmEqIdx] += (k_g - k_d - k_str * pow(normVelocityCell / intQuants.porosity(), eta))
371 * intQuants.biofilmVolumeFraction()
372 + k_a * Sw * intQuants.microbialConcentration() * intQuants.porosity() * b / rho_b;
375 unsigned activeGasCompIdx = FluidSystem::canonicalToActiveCompIdx(gasCompIdx);
376 source[activeGasCompIdx] -= intQuants.biofilmVolumeFraction() * rho_b * k_g / (Y * rho_gRef);
381 static void addSource([[maybe_unused]] RateVector& source,
382 [[maybe_unused]]
const ElementContext& elemCtx,
383 [[maybe_unused]]
unsigned dofIdx,
384 [[maybe_unused]]
unsigned timeIdx)
386 if constexpr (enableMICP) {
387 const auto& problem = elemCtx.problem();
388 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, timeIdx);
389 addSource(source, problem, intQuants, dofIdx);
393 static const Scalar densityBiofilm(
unsigned satnumRegionIdx)
395 return params_.densityBiofilm_[satnumRegionIdx];
398 static const Scalar densityCalcite(
unsigned satnumRegionIdx)
400 return params_.densityCalcite_[satnumRegionIdx];
403 static const Scalar detachmentRate(
unsigned satnumRegionIdx)
405 return params_.detachmentRate_[satnumRegionIdx];
408 static const Scalar detachmentExponent(
unsigned satnumRegionIdx)
410 return params_.detachmentExponent_[satnumRegionIdx];
413 static const Scalar halfVelocityGrowth(
unsigned satnumRegionIdx)
415 return params_.halfVelocityGrowth_[satnumRegionIdx];
418 static const Scalar halfVelocityUrea(
unsigned satnumRegionIdx)
420 return params_.halfVelocityUrea_[satnumRegionIdx];
423 static const Scalar maximumGrowthRate(
unsigned satnumRegionIdx)
425 return params_.maximumGrowthRate_[satnumRegionIdx];
428 static const Scalar maximumUreaUtilization(
unsigned satnumRegionIdx)
430 return params_.maximumUreaUtilization_[satnumRegionIdx];
433 static const Scalar microbialAttachmentRate(
unsigned satnumRegionIdx)
435 return params_.microbialAttachmentRate_[satnumRegionIdx];
438 static const Scalar microbialDeathRate(
unsigned satnumRegionIdx)
440 return params_.microbialDeathRate_[satnumRegionIdx];
443 static const Scalar oxygenConsumptionFactor(
unsigned satnumRegionIdx)
445 return params_.oxygenConsumptionFactor_[satnumRegionIdx];
448 static const Scalar yieldGrowthCoefficient(
unsigned satnumRegionIdx)
450 return params_.yieldGrowthCoefficient_[satnumRegionIdx];
453 static const Scalar yieldUreaToCalciteCoefficient(
unsigned satnumRegionIdx)
455 return params_.yieldUreaToCalciteCoefficient_[satnumRegionIdx];
458 static const Scalar bioDiffCoefficient(
unsigned pvtRegionIdx,
unsigned compIdx)
460 return params_.bioDiffCoefficient_[pvtRegionIdx][compIdx];
463 static const TabulatedFunction& permfactTable(
const ElementContext& elemCtx,
467 unsigned satnumRegionIdx = elemCtx.problem().satnumRegionIndex(elemCtx, scvIdx, timeIdx);
468 return params_.permfactTable_[satnumRegionIdx];
471 static const TabulatedFunction& permfactTable(
unsigned satnumRegionIdx)
473 return params_.permfactTable_[satnumRegionIdx];
476 static const TabulatedFunction& pcfactTable(
unsigned satnumRegionIdx)
478 return params_.pcfactTable_[satnumRegionIdx];
481 static bool hasPcfactTables()
483 if constexpr (enableBioeffects && !enableMICP) {
484 return !params_.pcfactTable_.empty();
492 static BlackOilBioeffectsParams<Scalar> params_;
494 static Evaluation RswToMassFraction(
unsigned regionIdx,
const Evaluation& Rsw) {
495 Scalar rho_wRef = FluidSystem::referenceDensity(FluidSystem::waterPhaseIdx, regionIdx);
496 Scalar rho_gRef = FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, regionIdx);
498 const Evaluation rho_oG = Rsw * rho_gRef;
500 return rho_oG / (rho_wRef + rho_oG);