24#ifndef OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_IMPL_HEADER_INCLUDED
25#define OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_IMPL_HEADER_INCLUDED
27#ifndef OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_HEADER_INCLUDED
32#include <dune/common/timer.hh>
34#include <opm/common/ErrorMacros.hpp>
35#include <opm/common/OpmLog/OpmLog.hpp>
55#include <fmt/format.h>
58 template <
typename TypeTag>
65 case F::STRICT:
return "Strict";
66 case F::RELAXED:
return "Relaxed";
67 case F::TUNINGDP:
return "TuningDP";
76template <
class TypeTag>
81 const bool terminal_output)
82 :
ParentType(simulator, param, well_model, terminal_output)
83 , conv_monitor_(param.monitor_params_)
90 if (terminal_output) {
91 OpmLog::info(
"Using Non-Linear Domain Decomposition solver (nldd).");
93 nlddSolver_ = std::make_unique<NonlinearSystemNldd<TypeTag>>(*this);
95 if (terminal_output) {
96 OpmLog::info(
"Using Newton nonlinear solver.");
99 OPM_THROW(std::runtime_error,
"Unknown nonlinear solver option: " +
104template <
class TypeTag>
110 auto report = ParentType::prepareStep(timer);
112 Dune::Timer perfTimer;
115 unsigned numDof = this->simulator_.model().numGridDof();
116 wasSwitched_.resize(numDof);
117 std::fill(wasSwitched_.begin(), wasSwitched_.end(),
false);
119 if (this->param_.update_equations_scaling_) {
120 OpmLog::error(
"Equation scaling not supported");
124 if (hasNlddSolver()) {
125 nlddSolver_->prepareStep();
128 report.pre_post_time += perfTimer.stop();
130 auto getIdx = [](
unsigned phaseIdx) ->
int
132 if (FluidSystem::phaseIsActive(phaseIdx)) {
133 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
134 return FluidSystem::canonicalToActiveCompIdx(sIdx);
139 const auto& schedule = this->simulator_.vanguard().schedule();
140 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
141 rst_conv.init(this->simulator_.vanguard().globalNumCells(),
143 {getIdx(FluidSystem::oilPhaseIdx),
144 getIdx(FluidSystem::gasPhaseIdx),
145 getIdx(FluidSystem::waterPhaseIdx),
153template <
class TypeTag>
161 ParentType::initialLinearization(report,
167 std::vector<Scalar> residual_norms;
168 Dune::Timer perfTimer;
173 auto convrep = getConvergence(timer, maxIter, residual_norms);
174 report.
converged = convrep.converged() &&
175 this->simulator_.problem().iterationContext().iteration() >= minIter;
182 this->convergence_reports_.back().report.push_back(std::move(convrep));
186 this->failureReport_ += report;
187 OPM_THROW_PROBLEM(NumericalProblem,
"NaN residual found!");
189 this->failureReport_ += report;
190 OPM_THROW_NOLOG(NumericalProblem,
"Too large residual found!");
192 this->failureReport_ += report;
193 OPM_THROW_PROBLEM(ConvergenceMonitorFailure,
195 "Total penalty count exceeded cut-off-limit of {}",
196 this->param_.monitor_params_.cutoff_
201 this->residual_norms_history_.push_back(residual_norms);
204template <
class TypeTag>
205template <
class NonlinearSolverType>
209 NonlinearSolverType& nonlinear_solver)
214 if (this->simulator_.problem().iterationContext().needsTimestepInit()) {
215 this->residual_norms_history_.clear();
216 this->conv_monitor_.reset();
217 this->current_relaxation_ = 1.0;
220 this->convergence_reports_.back().report.reserve(11);
224 if (this->param_.nonlinear_solver_ !=
"nldd") {
225 result = this->nonlinearIterationNewton(timer, nonlinear_solver);
228 result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
231 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
232 rst_conv.update(this->simulator_.model().linearizer().residual());
234 this->simulator_.problem().advanceIteration();
238template <
class TypeTag>
239template <
class NonlinearSolverType>
243 NonlinearSolverType& nonlinear_solver)
248 Dune::Timer perfTimer;
250 this->initialLinearization(report,
251 this->param_.newton_min_iter_,
252 this->param_.newton_max_iter_,
260 const unsigned nc = this->simulator_.model().numGridDof();
263 linear_solve_setup_time_ = 0.0;
265 this->wellModel().linearize(this->simulator().model().linearizer().jacobian(),
266 this->simulator().model().linearizer().residual());
268 solveJacobianSystem(x);
279 this->failureReport_ += report;
286 this->wellModel().postSolve(x);
288 if (this->param_.use_update_stabilization_) {
289 bool isOscillate =
false;
290 bool isStagnate =
false;
291 nonlinear_solver.detectOscillations(this->residual_norms_history_,
292 this->residual_norms_history_.size() - 1,
296 this->current_relaxation_ -= nonlinear_solver.relaxIncrement();
297 this->current_relaxation_ = std::max(this->current_relaxation_, nonlinear_solver.relaxMax());
303 if (!this->convergence_reports_.empty() &&
304 !this->convergence_reports_.back().report.empty())
306 auto& convrep = this->convergence_reports_.back().report.back();
308 convrep.setOscillationSource(source);
310 if (this->terminalOutputEnabled()) {
311 OpmLog::info(
" Oscillating behavior detected (" +
to_string(source)
312 +
"): Relaxation set to "
316 nonlinear_solver.stabilizeNonlinearUpdate(x, this->dx_old_, this->current_relaxation_);
319 this->updateSolution(x);
326template <
class TypeTag>
334 const auto& elemMapper = this->simulator_.model().elementMapper();
335 const auto& gridView = this->simulator_.gridView();
336 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
337 unsigned globalElemIdx = elemMapper.index(elem);
338 const auto& priVarsNew = this->simulator_.model().solution(0)[globalElemIdx];
341 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
343 Scalar saturationsNew[FluidSystem::numPhases] = { 0.0 };
344 Scalar oilSaturationNew = 1.0;
345 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) &&
346 FluidSystem::numActivePhases() > 1 &&
347 priVarsNew.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
349 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
350 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
353 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
354 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
355 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
357 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
358 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
359 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
362 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
363 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
366 const auto& priVarsOld = this->simulator_.model().solution(1)[globalElemIdx];
369 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
371 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
372 Scalar oilSaturationOld = 1.0;
375 Scalar tmp = pressureNew - pressureOld;
376 resultDelta += tmp*tmp;
377 resultDenom += pressureNew*pressureNew;
379 if (FluidSystem::numActivePhases() > 1) {
380 if (priVarsOld.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
381 saturationsOld[FluidSystem::waterPhaseIdx] =
382 priVarsOld[Indices::waterSwitchIdx];
383 oilSaturationOld -= saturationsOld[FluidSystem::waterPhaseIdx];
386 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
388 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
389 saturationsOld[FluidSystem::gasPhaseIdx] =
390 priVarsOld[Indices::compositionSwitchIdx];
391 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
394 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
395 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
397 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++ phaseIdx) {
398 Scalar tmpSat = saturationsNew[phaseIdx] - saturationsOld[phaseIdx];
399 resultDelta += tmpSat*tmpSat;
400 resultDenom += saturationsNew[phaseIdx]*saturationsNew[phaseIdx];
401 assert(std::isfinite(resultDelta));
402 assert(std::isfinite(resultDenom));
407 resultDelta = gridView.comm().sum(resultDelta);
408 resultDenom = gridView.comm().sum(resultDenom);
410 return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0;
413template <
class TypeTag>
418 auto& jacobian = this->simulator_.model().linearizer().jacobian().istlMatrix();
419 auto& residual = this->simulator_.model().linearizer().residual();
420 auto& linSolver = this->simulator_.model().newtonMethod().linearSolver();
422 const int numSolvers = linSolver.numAvailableSolvers();
423 if (numSolvers > 1 && (linSolver.getSolveCount() % 100 == 0)) {
424 if (this->terminal_output_) {
425 OpmLog::debug(
"\nRunning speed test for comparing available linear solvers.");
428 Dune::Timer perfTimer;
429 std::vector<double> times(numSolvers);
430 std::vector<double> setupTimes(numSolvers);
433 std::vector<BVector> x_trial(numSolvers, x);
434 for (
int solver = 0; solver < numSolvers; ++solver) {
435 linSolver.setActiveSolver(solver);
437 linSolver.prepare(jacobian, residual);
438 setupTimes[solver] = perfTimer.stop();
440 linSolver.setResidual(residual);
442 linSolver.solve(x_trial[solver]);
443 times[solver] = setupTimes[solver] + perfTimer.stop();
445 if (this->terminal_output_) {
446 OpmLog::debug(fmt::format(fmt::runtime(
"Solver time {}: {}"), solver, times[solver]));
450 int fastest_solver = std::ranges::min_element(times) - times.begin();
452 this->grid_.comm().broadcast(&fastest_solver, 1, 0);
453 this->linear_solve_setup_time_ = setupTimes[fastest_solver];
454 x = x_trial[fastest_solver];
455 linSolver.setActiveSolver(fastest_solver);
460 Dune::Timer perfTimer;
462 linSolver.prepare(jacobian, residual);
463 this->linear_solve_setup_time_ = perfTimer.stop();
464 linSolver.setResidual(residual);
473template <
class TypeTag>
478 return this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
479 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0;
482template <
class TypeTag>
488 unsigned nc = this->simulator_.model().numGridDof();
491 const auto& elemMapper = this->simulator_.model().elementMapper();
492 const auto& gridView = this->simulator_.gridView();
493 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
495 unsigned globalElemIdx = elemMapper.index(elem);
496 solUpd_[globalElemIdx] = this->simulator_.model().solution(0)[globalElemIdx];
499 std::ranges::fill(solUpd_[globalElemIdx], 0.0);
503template <
class TypeTag>
508 const auto& elemMapper = this->simulator_.model().elementMapper();
509 const auto& gridView = this->simulator_.gridView();
510 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
512 unsigned globalElemIdx = elemMapper.index(elem);
513 auto& value = solUpd_[globalElemIdx];
514 const auto& update = dx[globalElemIdx];
515 assert(value.size() == update.size());
518 std::ranges::copy(update, value.begin());
522template <
class TypeTag>
527 static constexpr bool enableSolvent =
528 Indices::solventSaturationIdx != std::numeric_limits<unsigned>::max();
529 static constexpr bool enableBrine =
530 Indices::saltConcentrationIdx != std::numeric_limits<unsigned>::max();
539 for (
const auto& ix : ixCells) {
540 const auto& value = solUpd_[ix];
541 for (
unsigned pvIdx = 0; pvIdx < value.size(); ++pvIdx) {
542 if (pvIdx == Indices::pressureSwitchIdx) {
543 dPMax = std::max(dPMax, std::abs(value[pvIdx]));
545 else if ((pvIdx == Indices::waterSwitchIdx
546 && value.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
547 || (pvIdx == Indices::compositionSwitchIdx
548 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
549 || (enableSolvent && pvIdx == Indices::solventSaturationIdx
550 && value.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss)
551 || (enableBrine && enableSaltPrecipitation && pvIdx == Indices::saltConcentrationIdx
552 && value.primaryVarsMeaningBrine() == PrimaryVariables::BrineMeaning::Sp) ) {
553 dSMax = std::max(dSMax, std::abs(value[pvIdx]));
555 else if (pvIdx == Indices::compositionSwitchIdx
556 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
557 dRsMax = std::max(dRsMax, std::abs(value[pvIdx]));
559 else if (pvIdx == Indices::compositionSwitchIdx
560 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
561 dRvMax = std::max(dRvMax, std::abs(value[pvIdx]));
567 dPMax = this->grid_.comm().max(dPMax);
568 dSMax = this->grid_.comm().max(dSMax);
569 dRsMax = this->grid_.comm().max(dRsMax);
570 dRvMax = this->grid_.comm().max(dRvMax);
572 return { dPMax, dSMax, dRsMax, dRvMax };
575template <
class TypeTag>
576std::tuple<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
581 const Scalar numAquiferPvSumLocal,
582 std::vector< Scalar >& R_sum,
583 std::vector< Scalar >& maxCoeff,
584 std::vector< Scalar >& B_avg)
586 return ParentType::convergenceReduction(comm,
588 numAquiferPvSumLocal,
594template <
class TypeTag>
595std::pair<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
599 std::vector<Scalar>& maxCoeff,
600 std::vector<Scalar>& B_avg,
601 std::vector<int>& maxCoeffCell)
603 OPM_TIMEBLOCK(localConvergenceData);
605 Scalar numAquiferPvSumLocal = 0.0;
606 const auto& model = this->simulator_.model();
607 const auto& problem = this->simulator_.problem();
609 const auto& residual = this->simulator_.model().linearizer().residual();
612 const auto& gridView = this->simulator().gridView();
615 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
616 elemCtx.updatePrimaryStencil(elem);
617 elemCtx.updatePrimaryIntensiveQuantities(0);
619 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
620 const auto& intQuants = elemCtx.intensiveQuantities(0, 0);
621 const auto& fs = intQuants.fluidState();
623 const auto pvValue = problem.referencePorosity(cell_idx, 0) *
624 model.dofTotalVolume(cell_idx);
625 pvSumLocal += pvValue;
627 if (isNumericalAquiferCell(elem)) {
628 numAquiferPvSumLocal += pvValue;
631 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
632 B_avg, R_sum, maxCoeff, maxCoeffCell);
638 const int bSize = B_avg.size();
639 for (
int i = 0; i < bSize; ++i) {
640 B_avg[i] /=
Scalar(this->global_nc_);
643 return {pvSumLocal, numAquiferPvSumLocal};
646template <
class TypeTag>
651 OPM_TIMEBLOCK(computeCnvErrorPv);
656 constexpr auto numPvGroups = std::vector<double>::size_type{3};
658 auto cnvPvSplit = std::pair<std::vector<double>, std::vector<int>> {
659 std::piecewise_construct,
660 std::forward_as_tuple(numPvGroups),
661 std::forward_as_tuple(numPvGroups)
664 auto maxCNV = [&B_avg, dt](
const auto& residual,
const double pvol)
667 std::inner_product(residual.begin(), residual.end(),
669 [](
const Scalar m,
const auto& x)
672 return std::max(m, abs(x));
673 }, std::multiplies<>{});
676 auto& [splitPV, cellCntPV] = cnvPvSplit;
678 const auto& model = this->simulator().model();
679 const auto& problem = this->simulator().problem();
680 const auto& residual = model.linearizer().residual();
681 const auto& gridView = this->simulator().gridView();
687 std::vector<unsigned> ixCells;
690 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
692 if (isNumericalAquiferCell(elem)) {
696 elemCtx.updatePrimaryStencil(elem);
698 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
699 const auto pvValue = problem.referencePorosity(cell_idx, 0)
700 * model.dofTotalVolume(cell_idx);
702 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
704 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
705 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
707 splitPV[ix] +=
static_cast<double>(pvValue);
712 (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
713 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0 ) ) {
714 ixCells.push_back(cell_idx);
721 this->grid_.comm().sum(splitPV .data(), splitPV .size());
722 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
724 return { cnvPvSplit, ixCells };
727template <
class TypeTag>
733 std::vector<Scalar>& B_avg,
734 std::vector<Scalar>& residual_norms)
736 OPM_TIMEBLOCK(getReservoirConvergence);
737 using Vector = std::vector<Scalar>;
739 const auto& iterCtx = this->simulator_.problem().iterationContext();
743 const int numComp = numEq;
745 Vector R_sum(numComp,
Scalar{0});
746 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
747 std::vector<int> maxCoeffCell(numComp, -1);
749 const auto [pvSumLocal, numAquiferPvSumLocal] =
750 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
753 const auto& [pvSum, numAquiferPvSum] =
754 this->convergenceReduction(this->grid_.comm(),
756 numAquiferPvSumLocal,
757 R_sum, maxCoeff, B_avg);
759 auto cnvSplitData = this->characteriseCnvPvSplit(B_avg, dt);
760 report.setCnvPoreVolSplit(cnvSplitData.cnvPvSplit,
761 pvSum - numAquiferPvSum);
770 const bool relax_final_iteration_mb =
771 this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
773 const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
774 iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
776 const bool use_relaxed_mb = relax_final_iteration_mb
785 const bool relax_final_iteration_cnv =
786 this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
788 const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
789 iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
794 const auto relax_pv_fraction_cnv =
795 [&report,
this, eligible = pvSum - numAquiferPvSum]()
797 const auto& cnvPvSplit = report.cnvPvSplit().first;
801 Scalar cnvPvSum =
static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]);
802 return cnvPvSum < this->param_.relaxed_max_pv_fraction_ * eligible &&
810 const bool use_dp_tol = this->param_.tolerance_max_dp_ > 0.0;
811 const bool use_ds_tol = this->param_.tolerance_max_ds_ > 0.0;
812 const bool use_drs_tol = this->param_.tolerance_max_drs_ > 0.0;
813 const bool use_drv_tol = this->param_.tolerance_max_drv_ > 0.0;
814 const bool use_dsol_tol = use_dp_tol || use_ds_tol || use_drs_tol || use_drv_tol;
815 bool relax_dsol_cnv =
false;
816 if (!iterCtx.isFirstGlobalIteration() && use_dsol_tol) {
817 maxSolUpd = getMaxSolutionUpdate(cnvSplitData.ixCells);
819 (!use_dp_tol || (maxSolUpd.
dPMax > 0.0 && maxSolUpd.
dPMax < this->param_.tolerance_max_dp_)) &&
820 (!use_ds_tol || (maxSolUpd.
dSMax > 0.0 && maxSolUpd.
dSMax < this->param_.tolerance_max_ds_)) &&
821 (!use_drs_tol || (maxSolUpd.
dRsMax > 0.0 && maxSolUpd.
dRsMax < this->param_.tolerance_max_drs_)) &&
822 (!use_drv_tol || (maxSolUpd.
dRvMax > 0.0 && maxSolUpd.
dRvMax < this->param_.tolerance_max_drv_));
826 const bool use_relaxed_cnv = relax_final_iteration_cnv
827 || relax_pv_fraction_cnv
833 Scalar tolerance_cnv_relaxed = relax_dsol_cnv ? 1e20 : this->param_.tolerance_cnv_relaxed_;
835 const auto tol_cnv = use_relaxed_cnv ? tolerance_cnv_relaxed : this->param_.tolerance_cnv_;
836 const auto tol_mb = use_relaxed_mb ? this->param_.tolerance_mb_relaxed_ : this->param_.tolerance_mb_;
842 const auto source = relax_dsol_cnv ? RS::SolChange
843 : relax_pv_fraction_cnv ? RS::PvFraction
844 : relax_final_iteration_cnv ? RS::FinalIter
845 : relax_iter_cnv ? RS::IterCount
847 report.setCnvRelaxation(source,
static_cast<double>(tol_cnv));
849 const auto tol_cnv_energy = use_relaxed_cnv ? this->param_.tolerance_cnv_energy_relaxed_ : this->param_.tolerance_cnv_energy_;
850 const auto tol_eb = use_relaxed_mb ? this->param_.tolerance_energy_balance_relaxed_ : this->param_.tolerance_energy_balance_;
853 std::vector<Scalar> CNV(numComp);
854 std::vector<Scalar> mass_balance_residual(numComp);
855 for (
int compIdx = 0; compIdx < numComp; ++compIdx)
857 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
858 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
859 residual_norms.push_back(CNV[compIdx]);
863 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
865 mass_balance_residual[compIdx], CNV[compIdx],
868 const CR::ReservoirFailure::Type types[2] = {
869 CR::ReservoirFailure::Type::MassBalance,
870 CR::ReservoirFailure::Type::Cnv,
873 Scalar tol[2] = { tol_mb, tol_cnv, };
874 if (has_energy_ && compIdx == contiEnergyEqIdx) {
876 tol[1] = tol_cnv_energy;
879 this->addReservoirConvergenceMetrics(
882 this->compNames_.name(compIdx),
883 std::span<const Scalar>{res},
884 std::span<const CR::ReservoirFailure::Type>{types},
885 std::span<const Scalar>{tol},
886 maxResidualAllowed(),
887 [
this](
const std::string& message)
889 if (this->terminal_output_) {
890 OpmLog::debug(message);
896 this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
899 if (this->terminal_output_) {
901 if (iterCtx.isFirstGlobalIteration()) {
902 std::string msg =
"Iter";
903 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
905 msg += this->compNames_.name(compIdx)[0];
909 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
911 msg += this->compNames_.name(compIdx)[0];
916 msg += use_dp_tol ?
" DP " :
"";
917 msg += use_ds_tol ?
" DS " :
"";
918 msg += use_drs_tol ?
" DRS " :
"";
919 msg += use_drv_tol ?
" DRV " :
"";
928 std::ostringstream ss;
929 const std::streamsize oprec = ss.precision(3);
930 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
932 ss << std::setw(4) << iterCtx.iteration();
933 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
934 ss << std::setw(11) << mass_balance_residual[compIdx];
937 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
938 ss << std::setw(11) << CNV[compIdx];
943 [&] (
bool use_tol,
Scalar dsol) {
947 if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
948 ss << std::string(5,
' ') <<
"-" << std::string(5,
' ');
951 ss << std::setw(11) << dsol;
954 print_dsol(use_dp_tol, maxSolUpd.
dPMax);
955 print_dsol(use_ds_tol, maxSolUpd.
dSMax);
956 print_dsol(use_drs_tol, maxSolUpd.
dRsMax);
957 print_dsol(use_drv_tol, maxSolUpd.
dRvMax);
960 const auto mb_flag = use_relaxed_mb
961 ? DebugFlags::RELAXED
962 : DebugFlags::STRICT;
964 const auto cnv_flag = relax_dsol_cnv ?
967 ? DebugFlags::RELAXED
968 : DebugFlags::STRICT);
970 ss << std::setw(9) << make_string<TypeTag>(mb_flag)
971 << std::setw(9) << make_string<TypeTag>(cnv_flag);
976 OpmLog::debug(ss.str());
982template <
class TypeTag>
987 const double tol_cnv,
988 const double tol_cnv_energy)
990 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
991 if (!rst_conv.hasConv()) {
995 if (this->simulator_.problem().iterationContext().isFirstGlobalIteration()) {
996 rst_conv.prepareConv();
999 const auto& residual = this->simulator_.model().linearizer().residual();
1000 const auto& gridView = this->simulator_.gridView();
1003 std::vector<int> convNewt(residual.size(), 0);
1006 const int numComp = B_avg.size();
1007 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
1008 elemCtx.updatePrimaryStencil(elem);
1010 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
1011 const auto pvValue = this->simulator_.problem().referencePorosity(cell_idx, 0) *
1012 this->simulator_.model().dofTotalVolume(cell_idx);
1013 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1014 const auto tol = (has_energy_ && compIdx == contiEnergyEqIdx) ? tol_cnv_energy : tol_cnv;
1015 const Scalar cnv = std::abs(B_avg[compIdx] * residual[cell_idx][compIdx]) * dt / pvValue;
1016 if (std::isnan(cnv) || cnv > maxResidualAllowed() || cnv < 0.0 || cnv > tol) {
1024 this->grid_.comm());
1025 rst_conv.updateNewton(convNewt);
1028template <
class TypeTag>
1033 std::vector<Scalar>& residual_norms)
1035 OPM_TIMEBLOCK(getConvergence);
1037 std::vector<Scalar> B_avg(numEq, 0.0);
1040 maxIter, B_avg, residual_norms);
1042 OPM_TIMEBLOCK(getWellConvergence);
1043 report += this->wellModel().getWellConvergence(B_avg,
1044 report.converged());
1047 conv_monitor_.checkPenaltyCard(report, this->simulator_.problem().iterationContext().iteration());
1052template <
class TypeTag>
1053std::vector<std::vector<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar> >
1057 OPM_TIMEBLOCK(computeFluidInPlace);
1060 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1061 return regionValues;
1064template <
class TypeTag>
1069 if (!hasNlddSolver()) {
1070 OPM_THROW(std::runtime_error,
"Cannot get local reports from a model without NLDD solver");
1072 return nlddSolver_->localAccumulatedReports();
1075template <
class TypeTag>
1076const std::vector<SimulatorReport>&
1081 OPM_THROW(std::runtime_error,
"Cannot get domain reports from a model without NLDD solver");
1082 return nlddSolver_->domainAccumulatedReports();
1085template <
class TypeTag>
1090 if (hasNlddSolver()) {
1091 nlddSolver_->writeNonlinearIterationsPerCell(odir);
1095template <
class TypeTag>
1100 if (hasNlddSolver()) {
1101 nlddSolver_->writePartitions(odir);
1105 const auto& elementMapper = this->simulator().model().elementMapper();
1106 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1108 const auto& grid = this->simulator().vanguard().grid();
1109 const auto& comm = grid.comm();
1110 const auto nDigit = 1 +
static_cast<int>(std::floor(std::log10(comm.size())));
1112 std::ofstream pfile {odir / fmt::format(
"{1:0>{0}}", nDigit, comm.rank())};
1114 for (
const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1115 pfile << comm.rank() <<
' '
1116 << cartMapper.cartesianIndex(elementMapper.index(cell)) <<
' '
1117 << comm.rank() <<
'\n';
1121template <
class TypeTag>
1122template<
class Flu
idState,
class Res
idual>
1127 const FluidState& fs,
1128 const Residual& modelResid,
1130 std::vector<Scalar>& B_avg,
1131 std::vector<Scalar>& R_sum,
1132 std::vector<Scalar>& maxCoeff,
1133 std::vector<int>& maxCoeffCell)
1135 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1137 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1141 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1142 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1144 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1145 const auto R2 = modelResid[cell_idx][compIdx];
1147 R_sum[compIdx] += R2;
1148 const Scalar Rval = std::abs(R2) / pvValue;
1149 if (Rval > maxCoeff[compIdx]) {
1150 maxCoeff[compIdx] = Rval;
1151 maxCoeffCell[compIdx] = cell_idx;
1155 if constexpr (has_solvent_) {
1156 B_avg[contiSolventEqIdx] +=
1157 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1158 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1159 R_sum[contiSolventEqIdx] += R2;
1160 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1161 std::abs(R2) / pvValue);
1163 if constexpr (has_extbo_) {
1164 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1165 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1166 R_sum[ contiZfracEqIdx ] += R2;
1167 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1168 std::abs(R2) / pvValue);
1170 if constexpr (has_polymer_) {
1171 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1172 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1173 R_sum[contiPolymerEqIdx] += R2;
1174 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1175 std::abs(R2) / pvValue);
1177 if constexpr (has_foam_) {
1178 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1179 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1180 R_sum[contiFoamEqIdx] += R2;
1181 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1182 std::abs(R2) / pvValue);
1184 if constexpr (has_brine_) {
1185 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1186 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1187 R_sum[contiBrineEqIdx] += R2;
1188 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1189 std::abs(R2) / pvValue);
1192 if constexpr (has_polymermw_) {
1193 static_assert(has_polymer_);
1195 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1199 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1200 R_sum[contiPolymerMWEqIdx] += R2;
1201 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1202 std::abs(R2) / pvValue);
1205 if constexpr (has_energy_) {
1206 B_avg[contiEnergyEqIdx] += 1.0;
1207 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1208 R_sum[contiEnergyEqIdx] += R2;
1209 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1210 std::abs(R2) / pvValue);
1213 if constexpr (has_bioeffects_) {
1214 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1215 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1216 R_sum[contiMicrobialEqIdx] += R1;
1217 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1218 std::abs(R1) / pvValue);
1219 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1220 const auto R2 = modelResid[cell_idx][contiBiofilmEqIdx];
1221 R_sum[contiBiofilmEqIdx] += R2;
1222 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1223 std::abs(R2) / pvValue);
1224 if constexpr (has_micp_) {
1225 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1226 const auto R3 = modelResid[cell_idx][contiOxygenEqIdx];
1227 R_sum[contiOxygenEqIdx] += R3;
1228 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1229 std::abs(R3) / pvValue);
1230 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1231 const auto R4 = modelResid[cell_idx][contiUreaEqIdx];
1232 R_sum[contiUreaEqIdx] += R4;
1233 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1234 std::abs(R4) / pvValue);
1235 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1236 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1237 R_sum[contiCalciteEqIdx] += R5;
1238 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1239 std::abs(R5) / pvValue);
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:197
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:160
Definition: ConvergenceReport.hpp:38
Severity
Definition: ConvergenceReport.hpp:49
@ ConvergenceMonitorFailure
CnvRelaxSource
Definition: ConvergenceReport.hpp:246
@ None
strict tolerance applied
void prepareSolutionUpdate() override
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:485
SimulatorReportSingle prepareStep(const SimulatorTimerInterface &timer)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:107
GetPropType< TypeTag, Properties::ElementContext > ElementContext
Definition: NonlinearSystemBlackOilReservoir.hpp:69
void storeSolutionUpdate(const GlobalEqVector &dx) override
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:506
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: NonlinearSystemBlackOilReservoir.hpp:70
std::tuple< Scalar, Scalar > convergenceReduction(Parallel::Communication comm, const Scalar pvSumLocal, const Scalar numAquiferPvSumLocal, std::vector< Scalar > &R_sum, std::vector< Scalar > &maxCoeff, std::vector< Scalar > &B_avg)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:579
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: NonlinearSystemBlackOilReservoir.hpp:78
std::pair< Scalar, Scalar > localConvergenceData(std::vector< Scalar > &R_sum, std::vector< Scalar > &maxCoeff, std::vector< Scalar > &B_avg, std::vector< int > &maxCoeffCell)
Get reservoir quantities on this process needed for convergence calculations.
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:598
void convergencePerCell(const std::vector< Scalar > &B_avg, const double dt, const double tol_cnv, const double tol_cnv_energy)
Compute the number of Newtons required by each cell in order to satisfy the solution change convergen...
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:985
const SimulatorReport & localAccumulatedReports() const
return the statistics of local solves accumulated for this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1067
SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:208
MaxSolutionUpdateData getMaxSolutionUpdate(const std::vector< unsigned > &ixCells)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:525
const std::vector< SimulatorReport > & domainAccumulatedReports() const
return the statistics of local solves accumulated for each domain on this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1078
bool shouldStoreSolutionUpdate() const override
Get solution update vector as a PrimaryVariable.
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:476
ConvergenceReport getReservoirConvergence(const double reportTime, const double dt, const int maxIter, std::vector< Scalar > &B_avg, std::vector< Scalar > &residual_norms)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:730
Dune::BlockVector< VectorBlockType > BVector
Definition: NonlinearSystemBlackOilReservoir.hpp:113
std::unique_ptr< NonlinearSystemNldd< TypeTag > > nlddSolver_
Non-linear DD solver.
Definition: NonlinearSystemBlackOilReservoir.hpp:306
CnvPvSplitData characteriseCnvPvSplit(const std::vector< Scalar > &B_avg, const double dt)
Compute pore-volume/cell count split among "converged", "relaxed converged", "unconverged" cells base...
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:649
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int maxIter, std::vector< Scalar > &residual_norms)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1031
NonlinearSystemBlackOilReservoir(Simulator &simulator, const ModelParameters ¶m, typename ParentType::WellModel &well_model, const bool terminal_output)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:78
void writeNonlinearIterationsPerCell(const std::filesystem::path &odir) const
Write the number of nonlinear iterations per cell to a file in ResInsight compatible format.
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1088
Scalar relativeChange() const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:329
std::vector< std::vector< Scalar > > computeFluidInPlace(const T &, const std::vector< int > &fipnum) const
Wrapper required due to not following generic API.
Definition: NonlinearSystemBlackOilReservoir.hpp:249
long int global_nc_
The number of cells of the global grid.
Definition: NonlinearSystemBlackOilReservoir.hpp:302
DebugFlags
Definition: NonlinearSystemBlackOilReservoir.hpp:131
void initialLinearization(SimulatorReportSingle &report, const int minIter, const int maxIter, const SimulatorTimerInterface &timer) override
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:156
void getMaxCoeff(const unsigned cell_idx, const IntensiveQuantities &intQuants, const FluidState &fs, const Residual &modelResid, const Scalar pvValue, std::vector< Scalar > &B_avg, std::vector< Scalar > &R_sum, std::vector< Scalar > &maxCoeff, std::vector< int > &maxCoeffCell)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1125
void solveJacobianSystem(BVector &x)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:416
SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:242
void writePartitions(const std::filesystem::path &odir) const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1098
Definition: NonlinearSystem.hpp:44
const Grid & grid_
Definition: NonlinearSystem.hpp:164
std::vector< StepReport > convergence_reports_
Definition: NonlinearSystem.hpp:169
ModelParameters param_
Definition: NonlinearSystem.hpp:166
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: NonlinearSystem.hpp:47
GetPropType< TypeTag, Properties::GlobalEqVector > GlobalEqVector
Definition: NonlinearSystem.hpp:52
GetPropType< TypeTag, Properties::WellModel > WellModel
Definition: NonlinearSystem.hpp:54
Interface class for SimulatorTimer objects, to be improved.
Definition: SimulatorTimerInterface.hpp:34
virtual int reportStepNum() const
Current report step number. This might differ from currentStepNum in case of sub stepping.
Definition: SimulatorTimerInterface.hpp:109
virtual double currentStepLength() const =0
virtual double simulationTimeElapsed() const =0
virtual int currentStepNum() const =0
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
std::size_t countGlobalCells(const Grid &grid)
Get the number of cells of a global grid.
Definition: countGlobalCells.hpp:80
Definition: blackoilbioeffectsmodules.hh:45
ConvergenceReport::OscillationSource classifyOscillationSource(const ConvergenceReport &report)
Classify what was unsatisfied in a report, for oscillation-source reporting.
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Solver parameters for the NonlinearSystemBlackOilReservoir.
Definition: BlackoilModelParameters.hpp:207
std::string nonlinear_solver_
Nonlinear solver type: newton or nldd.
Definition: BlackoilModelParameters.hpp:378
Definition: AquiferGridUtils.hpp:35
Definition: NonlinearSystemBlackOilReservoir.hpp:118
Definition: NonlinearSystemBlackOilReservoir.hpp:123
Scalar dRsMax
Definition: NonlinearSystemBlackOilReservoir.hpp:126
Scalar dPMax
Definition: NonlinearSystemBlackOilReservoir.hpp:124
Scalar dSMax
Definition: NonlinearSystemBlackOilReservoir.hpp:125
Scalar dRvMax
Definition: NonlinearSystemBlackOilReservoir.hpp:127
Definition: SimulatorReport.hpp:125
A struct for returning timing data from a simulator to its caller.
Definition: SimulatorReport.hpp:34
double linear_solve_time
Definition: SimulatorReport.hpp:43
bool converged
Definition: SimulatorReport.hpp:57
double linear_solve_setup_time
Definition: SimulatorReport.hpp:42
unsigned int total_newton_iterations
Definition: SimulatorReport.hpp:50
double update_time
Definition: SimulatorReport.hpp:45
unsigned int relaxed_cnv_acceptances
Definition: SimulatorReport.hpp:55
unsigned int total_linear_iterations
Definition: SimulatorReport.hpp:51