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;
177 this->convergence_reports_.back().report.push_back(std::move(convrep));
181 this->failureReport_ += report;
182 OPM_THROW_PROBLEM(NumericalProblem,
"NaN residual found!");
184 this->failureReport_ += report;
185 OPM_THROW_NOLOG(NumericalProblem,
"Too large residual found!");
187 this->failureReport_ += report;
188 OPM_THROW_PROBLEM(ConvergenceMonitorFailure,
190 "Total penalty count exceeded cut-off-limit of {}",
191 this->param_.monitor_params_.cutoff_
196 this->residual_norms_history_.push_back(residual_norms);
199template <
class TypeTag>
200template <
class NonlinearSolverType>
204 NonlinearSolverType& nonlinear_solver)
209 if (this->simulator_.problem().iterationContext().needsTimestepInit()) {
210 this->residual_norms_history_.clear();
211 this->conv_monitor_.reset();
212 this->current_relaxation_ = 1.0;
215 this->convergence_reports_.back().report.reserve(11);
219 if (this->param_.nonlinear_solver_ !=
"nldd") {
220 result = this->nonlinearIterationNewton(timer, nonlinear_solver);
223 result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
226 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
227 rst_conv.update(this->simulator_.model().linearizer().residual());
229 this->simulator_.problem().advanceIteration();
233template <
class TypeTag>
234template <
class NonlinearSolverType>
238 NonlinearSolverType& nonlinear_solver)
243 Dune::Timer perfTimer;
245 this->initialLinearization(report,
246 this->param_.newton_min_iter_,
247 this->param_.newton_max_iter_,
255 const unsigned nc = this->simulator_.model().numGridDof();
258 linear_solve_setup_time_ = 0.0;
260 this->wellModel().linearize(this->simulator().model().linearizer().jacobian(),
261 this->simulator().model().linearizer().residual());
263 solveJacobianSystem(x);
274 this->failureReport_ += report;
281 this->wellModel().postSolve(x);
283 if (this->param_.use_update_stabilization_) {
284 bool isOscillate =
false;
285 bool isStagnate =
false;
286 nonlinear_solver.detectOscillations(this->residual_norms_history_,
287 this->residual_norms_history_.size() - 1,
291 this->current_relaxation_ -= nonlinear_solver.relaxIncrement();
292 this->current_relaxation_ = std::max(this->current_relaxation_, nonlinear_solver.relaxMax());
293 if (this->terminalOutputEnabled()) {
294 OpmLog::info(
" Oscillating behavior detected: Relaxation set to "
298 nonlinear_solver.stabilizeNonlinearUpdate(x, this->dx_old_, this->current_relaxation_);
301 this->updateSolution(x);
308template <
class TypeTag>
316 const auto& elemMapper = this->simulator_.model().elementMapper();
317 const auto& gridView = this->simulator_.gridView();
318 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
319 unsigned globalElemIdx = elemMapper.index(elem);
320 const auto& priVarsNew = this->simulator_.model().solution(0)[globalElemIdx];
323 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
325 Scalar saturationsNew[FluidSystem::numPhases] = { 0.0 };
326 Scalar oilSaturationNew = 1.0;
327 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) &&
328 FluidSystem::numActivePhases() > 1 &&
329 priVarsNew.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
331 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
332 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
335 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
336 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
337 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
339 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
340 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
341 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
344 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
345 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
348 const auto& priVarsOld = this->simulator_.model().solution(1)[globalElemIdx];
351 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
353 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
354 Scalar oilSaturationOld = 1.0;
357 Scalar tmp = pressureNew - pressureOld;
358 resultDelta += tmp*tmp;
359 resultDenom += pressureNew*pressureNew;
361 if (FluidSystem::numActivePhases() > 1) {
362 if (priVarsOld.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
363 saturationsOld[FluidSystem::waterPhaseIdx] =
364 priVarsOld[Indices::waterSwitchIdx];
365 oilSaturationOld -= saturationsOld[FluidSystem::waterPhaseIdx];
368 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
370 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
371 saturationsOld[FluidSystem::gasPhaseIdx] =
372 priVarsOld[Indices::compositionSwitchIdx];
373 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
376 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
377 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
379 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++ phaseIdx) {
380 Scalar tmpSat = saturationsNew[phaseIdx] - saturationsOld[phaseIdx];
381 resultDelta += tmpSat*tmpSat;
382 resultDenom += saturationsNew[phaseIdx]*saturationsNew[phaseIdx];
383 assert(std::isfinite(resultDelta));
384 assert(std::isfinite(resultDenom));
389 resultDelta = gridView.comm().sum(resultDelta);
390 resultDenom = gridView.comm().sum(resultDenom);
392 return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0;
395template <
class TypeTag>
400 auto& jacobian = this->simulator_.model().linearizer().jacobian().istlMatrix();
401 auto& residual = this->simulator_.model().linearizer().residual();
402 auto& linSolver = this->simulator_.model().newtonMethod().linearSolver();
404 const int numSolvers = linSolver.numAvailableSolvers();
405 if (numSolvers > 1 && (linSolver.getSolveCount() % 100 == 0)) {
406 if (this->terminal_output_) {
407 OpmLog::debug(
"\nRunning speed test for comparing available linear solvers.");
410 Dune::Timer perfTimer;
411 std::vector<double> times(numSolvers);
412 std::vector<double> setupTimes(numSolvers);
415 std::vector<BVector> x_trial(numSolvers, x);
416 for (
int solver = 0; solver < numSolvers; ++solver) {
417 linSolver.setActiveSolver(solver);
419 linSolver.prepare(jacobian, residual);
420 setupTimes[solver] = perfTimer.stop();
422 linSolver.setResidual(residual);
424 linSolver.solve(x_trial[solver]);
425 times[solver] = perfTimer.stop();
427 if (this->terminal_output_) {
428 OpmLog::debug(fmt::format(fmt::runtime(
"Solver time {}: {}"), solver, times[solver]));
432 int fastest_solver = std::ranges::min_element(times) - times.begin();
434 this->grid_.comm().broadcast(&fastest_solver, 1, 0);
435 this->linear_solve_setup_time_ = setupTimes[fastest_solver];
436 x = x_trial[fastest_solver];
437 linSolver.setActiveSolver(fastest_solver);
442 Dune::Timer perfTimer;
444 linSolver.prepare(jacobian, residual);
445 this->linear_solve_setup_time_ = perfTimer.stop();
446 linSolver.setResidual(residual);
455template <
class TypeTag>
460 return this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
461 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0;
464template <
class TypeTag>
470 unsigned nc = this->simulator_.model().numGridDof();
473 const auto& elemMapper = this->simulator_.model().elementMapper();
474 const auto& gridView = this->simulator_.gridView();
475 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
477 unsigned globalElemIdx = elemMapper.index(elem);
478 solUpd_[globalElemIdx] = this->simulator_.model().solution(0)[globalElemIdx];
481 std::ranges::fill(solUpd_[globalElemIdx], 0.0);
485template <
class TypeTag>
490 const auto& elemMapper = this->simulator_.model().elementMapper();
491 const auto& gridView = this->simulator_.gridView();
492 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
494 unsigned globalElemIdx = elemMapper.index(elem);
495 auto& value = solUpd_[globalElemIdx];
496 const auto& update = dx[globalElemIdx];
497 assert(value.size() == update.size());
500 std::ranges::copy(update, value.begin());
504template <
class TypeTag>
509 static constexpr bool enableSolvent =
510 Indices::solventSaturationIdx != std::numeric_limits<unsigned>::max();
511 static constexpr bool enableBrine =
512 Indices::saltConcentrationIdx != std::numeric_limits<unsigned>::max();
521 for (
const auto& ix : ixCells) {
522 const auto& value = solUpd_[ix];
523 for (
unsigned pvIdx = 0; pvIdx < value.size(); ++pvIdx) {
524 if (pvIdx == Indices::pressureSwitchIdx) {
525 dPMax = std::max(dPMax, std::abs(value[pvIdx]));
527 else if ((pvIdx == Indices::waterSwitchIdx
528 && value.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
529 || (pvIdx == Indices::compositionSwitchIdx
530 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
531 || (enableSolvent && pvIdx == Indices::solventSaturationIdx
532 && value.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss)
533 || (enableBrine && enableSaltPrecipitation && pvIdx == Indices::saltConcentrationIdx
534 && value.primaryVarsMeaningBrine() == PrimaryVariables::BrineMeaning::Sp) ) {
535 dSMax = std::max(dSMax, std::abs(value[pvIdx]));
537 else if (pvIdx == Indices::compositionSwitchIdx
538 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
539 dRsMax = std::max(dRsMax, std::abs(value[pvIdx]));
541 else if (pvIdx == Indices::compositionSwitchIdx
542 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
543 dRvMax = std::max(dRvMax, std::abs(value[pvIdx]));
549 dPMax = this->grid_.comm().max(dPMax);
550 dSMax = this->grid_.comm().max(dSMax);
551 dRsMax = this->grid_.comm().max(dRsMax);
552 dRvMax = this->grid_.comm().max(dRvMax);
554 return { dPMax, dSMax, dRsMax, dRvMax };
557template <
class TypeTag>
558std::tuple<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
563 const Scalar numAquiferPvSumLocal,
564 std::vector< Scalar >& R_sum,
565 std::vector< Scalar >& maxCoeff,
566 std::vector< Scalar >& B_avg)
568 return ParentType::convergenceReduction(comm,
570 numAquiferPvSumLocal,
576template <
class TypeTag>
577std::pair<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
581 std::vector<Scalar>& maxCoeff,
582 std::vector<Scalar>& B_avg,
583 std::vector<int>& maxCoeffCell)
585 OPM_TIMEBLOCK(localConvergenceData);
587 Scalar numAquiferPvSumLocal = 0.0;
588 const auto& model = this->simulator_.model();
589 const auto& problem = this->simulator_.problem();
591 const auto& residual = this->simulator_.model().linearizer().residual();
594 const auto& gridView = this->simulator().gridView();
597 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
598 elemCtx.updatePrimaryStencil(elem);
599 elemCtx.updatePrimaryIntensiveQuantities(0);
601 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
602 const auto& intQuants = elemCtx.intensiveQuantities(0, 0);
603 const auto& fs = intQuants.fluidState();
605 const auto pvValue = problem.referencePorosity(cell_idx, 0) *
606 model.dofTotalVolume(cell_idx);
607 pvSumLocal += pvValue;
609 if (isNumericalAquiferCell(elem)) {
610 numAquiferPvSumLocal += pvValue;
613 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
614 B_avg, R_sum, maxCoeff, maxCoeffCell);
620 const int bSize = B_avg.size();
621 for (
int i = 0; i < bSize; ++i) {
622 B_avg[i] /=
Scalar(this->global_nc_);
625 return {pvSumLocal, numAquiferPvSumLocal};
628template <
class TypeTag>
633 OPM_TIMEBLOCK(computeCnvErrorPv);
638 constexpr auto numPvGroups = std::vector<double>::size_type{3};
640 auto cnvPvSplit = std::pair<std::vector<double>, std::vector<int>> {
641 std::piecewise_construct,
642 std::forward_as_tuple(numPvGroups),
643 std::forward_as_tuple(numPvGroups)
646 auto maxCNV = [&B_avg, dt](
const auto& residual,
const double pvol)
649 std::inner_product(residual.begin(), residual.end(),
651 [](
const Scalar m,
const auto& x)
654 return std::max(m, abs(x));
655 }, std::multiplies<>{});
658 auto& [splitPV, cellCntPV] = cnvPvSplit;
660 const auto& model = this->simulator().model();
661 const auto& problem = this->simulator().problem();
662 const auto& residual = model.linearizer().residual();
663 const auto& gridView = this->simulator().gridView();
669 std::vector<unsigned> ixCells;
672 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
674 if (isNumericalAquiferCell(elem)) {
678 elemCtx.updatePrimaryStencil(elem);
680 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
681 const auto pvValue = problem.referencePorosity(cell_idx, 0)
682 * model.dofTotalVolume(cell_idx);
684 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
686 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
687 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
689 splitPV[ix] +=
static_cast<double>(pvValue);
694 (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
695 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0 ) ) {
696 ixCells.push_back(cell_idx);
703 this->grid_.comm().sum(splitPV .data(), splitPV .size());
704 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
706 return { cnvPvSplit, ixCells };
709template <
class TypeTag>
715 std::vector<Scalar>& B_avg,
716 std::vector<Scalar>& residual_norms)
718 OPM_TIMEBLOCK(getReservoirConvergence);
719 using Vector = std::vector<Scalar>;
721 const auto& iterCtx = this->simulator_.problem().iterationContext();
725 const int numComp = numEq;
727 Vector R_sum(numComp,
Scalar{0});
728 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
729 std::vector<int> maxCoeffCell(numComp, -1);
731 const auto [pvSumLocal, numAquiferPvSumLocal] =
732 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
735 const auto& [pvSum, numAquiferPvSum] =
736 this->convergenceReduction(this->grid_.comm(),
738 numAquiferPvSumLocal,
739 R_sum, maxCoeff, B_avg);
741 auto cnvSplitData = this->characteriseCnvPvSplit(B_avg, dt);
742 report.setCnvPoreVolSplit(cnvSplitData.cnvPvSplit,
743 pvSum - numAquiferPvSum);
752 const bool relax_final_iteration_mb =
753 this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
755 const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
756 iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
758 const bool use_relaxed_mb = relax_final_iteration_mb
767 const bool relax_final_iteration_cnv =
768 this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
770 const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
771 iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
776 const auto relax_pv_fraction_cnv =
777 [&report,
this, eligible = pvSum - numAquiferPvSum]()
779 const auto& cnvPvSplit = report.cnvPvSplit().first;
783 Scalar cnvPvSum =
static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]);
784 return cnvPvSum < this->param_.relaxed_max_pv_fraction_ * eligible &&
792 const bool use_dp_tol = this->param_.tolerance_max_dp_ > 0.0;
793 const bool use_ds_tol = this->param_.tolerance_max_ds_ > 0.0;
794 const bool use_drs_tol = this->param_.tolerance_max_drs_ > 0.0;
795 const bool use_drv_tol = this->param_.tolerance_max_drv_ > 0.0;
796 const bool use_dsol_tol = use_dp_tol || use_ds_tol || use_drs_tol || use_drv_tol;
797 bool relax_dsol_cnv =
false;
798 if (!iterCtx.isFirstGlobalIteration() && use_dsol_tol) {
799 maxSolUpd = getMaxSolutionUpdate(cnvSplitData.ixCells);
801 (!use_dp_tol || (maxSolUpd.
dPMax > 0.0 && maxSolUpd.
dPMax < this->param_.tolerance_max_dp_)) &&
802 (!use_ds_tol || (maxSolUpd.
dSMax > 0.0 && maxSolUpd.
dSMax < this->param_.tolerance_max_ds_)) &&
803 (!use_drs_tol || (maxSolUpd.
dRsMax > 0.0 && maxSolUpd.
dRsMax < this->param_.tolerance_max_drs_)) &&
804 (!use_drv_tol || (maxSolUpd.
dRvMax > 0.0 && maxSolUpd.
dRvMax < this->param_.tolerance_max_drv_));
808 const bool use_relaxed_cnv = relax_final_iteration_cnv
809 || relax_pv_fraction_cnv
815 Scalar tolerance_cnv_relaxed = relax_dsol_cnv ? 1e20 : this->param_.tolerance_cnv_relaxed_;
817 const auto tol_cnv = use_relaxed_cnv ? tolerance_cnv_relaxed : this->param_.tolerance_cnv_;
818 const auto tol_mb = use_relaxed_mb ? this->param_.tolerance_mb_relaxed_ : this->param_.tolerance_mb_;
819 const auto tol_cnv_energy = use_relaxed_cnv ? this->param_.tolerance_cnv_energy_relaxed_ : this->param_.tolerance_cnv_energy_;
820 const auto tol_eb = use_relaxed_mb ? this->param_.tolerance_energy_balance_relaxed_ : this->param_.tolerance_energy_balance_;
823 std::vector<Scalar> CNV(numComp);
824 std::vector<Scalar> mass_balance_residual(numComp);
825 for (
int compIdx = 0; compIdx < numComp; ++compIdx)
827 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
828 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
829 residual_norms.push_back(CNV[compIdx]);
833 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
835 mass_balance_residual[compIdx], CNV[compIdx],
838 const CR::ReservoirFailure::Type types[2] = {
839 CR::ReservoirFailure::Type::MassBalance,
840 CR::ReservoirFailure::Type::Cnv,
843 Scalar tol[2] = { tol_mb, tol_cnv, };
844 if (has_energy_ && compIdx == contiEnergyEqIdx) {
846 tol[1] = tol_cnv_energy;
849 this->addReservoirConvergenceMetrics(
852 this->compNames_.name(compIdx),
853 std::span<const Scalar>{res},
854 std::span<const CR::ReservoirFailure::Type>{types},
855 std::span<const Scalar>{tol},
856 maxResidualAllowed(),
857 [
this](
const std::string& message)
859 if (this->terminal_output_) {
860 OpmLog::debug(message);
866 this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
869 if (this->terminal_output_) {
871 if (iterCtx.isFirstGlobalIteration()) {
872 std::string msg =
"Iter";
873 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
875 msg += this->compNames_.name(compIdx)[0];
879 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
881 msg += this->compNames_.name(compIdx)[0];
886 msg += use_dp_tol ?
" DP " :
"";
887 msg += use_ds_tol ?
" DS " :
"";
888 msg += use_drs_tol ?
" DRS " :
"";
889 msg += use_drv_tol ?
" DRV " :
"";
898 std::ostringstream ss;
899 const std::streamsize oprec = ss.precision(3);
900 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
902 ss << std::setw(4) << iterCtx.iteration();
903 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
904 ss << std::setw(11) << mass_balance_residual[compIdx];
907 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
908 ss << std::setw(11) << CNV[compIdx];
913 [&] (
bool use_tol,
Scalar dsol) {
917 if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
918 ss << std::string(5,
' ') <<
"-" << std::string(5,
' ');
921 ss << std::setw(11) << dsol;
924 print_dsol(use_dp_tol, maxSolUpd.
dPMax);
925 print_dsol(use_ds_tol, maxSolUpd.
dSMax);
926 print_dsol(use_drs_tol, maxSolUpd.
dRsMax);
927 print_dsol(use_drv_tol, maxSolUpd.
dRvMax);
930 const auto mb_flag = use_relaxed_mb
931 ? DebugFlags::RELAXED
932 : DebugFlags::STRICT;
934 const auto cnv_flag = relax_dsol_cnv ?
937 ? DebugFlags::RELAXED
938 : DebugFlags::STRICT);
940 ss << std::setw(9) << make_string<TypeTag>(mb_flag)
941 << std::setw(9) << make_string<TypeTag>(cnv_flag);
946 OpmLog::debug(ss.str());
952template <
class TypeTag>
957 const double tol_cnv,
958 const double tol_cnv_energy)
960 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
961 if (!rst_conv.hasConv()) {
965 if (this->simulator_.problem().iterationContext().isFirstGlobalIteration()) {
966 rst_conv.prepareConv();
969 const auto& residual = this->simulator_.model().linearizer().residual();
970 const auto& gridView = this->simulator_.gridView();
973 std::vector<int> convNewt(residual.size(), 0);
976 const int numComp = B_avg.size();
977 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
978 elemCtx.updatePrimaryStencil(elem);
980 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
981 const auto pvValue = this->simulator_.problem().referencePorosity(cell_idx, 0) *
982 this->simulator_.model().dofTotalVolume(cell_idx);
983 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
984 const auto tol = (has_energy_ && compIdx == contiEnergyEqIdx) ? tol_cnv_energy : tol_cnv;
985 const Scalar cnv = std::abs(B_avg[compIdx] * residual[cell_idx][compIdx]) * dt / pvValue;
986 if (std::isnan(cnv) || cnv > maxResidualAllowed() || cnv < 0.0 || cnv > tol) {
995 rst_conv.updateNewton(convNewt);
998template <
class TypeTag>
1003 std::vector<Scalar>& residual_norms)
1005 OPM_TIMEBLOCK(getConvergence);
1007 std::vector<Scalar> B_avg(numEq, 0.0);
1010 maxIter, B_avg, residual_norms);
1012 OPM_TIMEBLOCK(getWellConvergence);
1013 report += this->wellModel().getWellConvergence(B_avg,
1014 report.converged());
1017 conv_monitor_.checkPenaltyCard(report, this->simulator_.problem().iterationContext().iteration());
1022template <
class TypeTag>
1023std::vector<std::vector<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar> >
1027 OPM_TIMEBLOCK(computeFluidInPlace);
1030 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1031 return regionValues;
1034template <
class TypeTag>
1039 if (!hasNlddSolver()) {
1040 OPM_THROW(std::runtime_error,
"Cannot get local reports from a model without NLDD solver");
1042 return nlddSolver_->localAccumulatedReports();
1045template <
class TypeTag>
1046const std::vector<SimulatorReport>&
1051 OPM_THROW(std::runtime_error,
"Cannot get domain reports from a model without NLDD solver");
1052 return nlddSolver_->domainAccumulatedReports();
1055template <
class TypeTag>
1060 if (hasNlddSolver()) {
1061 nlddSolver_->writeNonlinearIterationsPerCell(odir);
1065template <
class TypeTag>
1070 if (hasNlddSolver()) {
1071 nlddSolver_->writePartitions(odir);
1075 const auto& elementMapper = this->simulator().model().elementMapper();
1076 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1078 const auto& grid = this->simulator().vanguard().grid();
1079 const auto& comm = grid.comm();
1080 const auto nDigit = 1 +
static_cast<int>(std::floor(std::log10(comm.size())));
1082 std::ofstream pfile {odir / fmt::format(
"{1:0>{0}}", nDigit, comm.rank())};
1084 for (
const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1085 pfile << comm.rank() <<
' '
1086 << cartMapper.cartesianIndex(elementMapper.index(cell)) <<
' '
1087 << comm.rank() <<
'\n';
1091template <
class TypeTag>
1092template<
class Flu
idState,
class Res
idual>
1097 const FluidState& fs,
1098 const Residual& modelResid,
1100 std::vector<Scalar>& B_avg,
1101 std::vector<Scalar>& R_sum,
1102 std::vector<Scalar>& maxCoeff,
1103 std::vector<int>& maxCoeffCell)
1105 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1107 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1111 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1112 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1114 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1115 const auto R2 = modelResid[cell_idx][compIdx];
1117 R_sum[compIdx] += R2;
1118 const Scalar Rval = std::abs(R2) / pvValue;
1119 if (Rval > maxCoeff[compIdx]) {
1120 maxCoeff[compIdx] = Rval;
1121 maxCoeffCell[compIdx] = cell_idx;
1125 if constexpr (has_solvent_) {
1126 B_avg[contiSolventEqIdx] +=
1127 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1128 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1129 R_sum[contiSolventEqIdx] += R2;
1130 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1131 std::abs(R2) / pvValue);
1133 if constexpr (has_extbo_) {
1134 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1135 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1136 R_sum[ contiZfracEqIdx ] += R2;
1137 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1138 std::abs(R2) / pvValue);
1140 if constexpr (has_polymer_) {
1141 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1142 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1143 R_sum[contiPolymerEqIdx] += R2;
1144 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1145 std::abs(R2) / pvValue);
1147 if constexpr (has_foam_) {
1148 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1149 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1150 R_sum[contiFoamEqIdx] += R2;
1151 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1152 std::abs(R2) / pvValue);
1154 if constexpr (has_brine_) {
1155 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1156 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1157 R_sum[contiBrineEqIdx] += R2;
1158 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1159 std::abs(R2) / pvValue);
1162 if constexpr (has_polymermw_) {
1163 static_assert(has_polymer_);
1165 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1169 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1170 R_sum[contiPolymerMWEqIdx] += R2;
1171 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1172 std::abs(R2) / pvValue);
1175 if constexpr (has_energy_) {
1176 B_avg[contiEnergyEqIdx] += 1.0;
1177 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1178 R_sum[contiEnergyEqIdx] += R2;
1179 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1180 std::abs(R2) / pvValue);
1183 if constexpr (has_bioeffects_) {
1184 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1185 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1186 R_sum[contiMicrobialEqIdx] += R1;
1187 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1188 std::abs(R1) / pvValue);
1189 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1190 const auto R2 = modelResid[cell_idx][contiBiofilmEqIdx];
1191 R_sum[contiBiofilmEqIdx] += R2;
1192 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1193 std::abs(R2) / pvValue);
1194 if constexpr (has_micp_) {
1195 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1196 const auto R3 = modelResid[cell_idx][contiOxygenEqIdx];
1197 R_sum[contiOxygenEqIdx] += R3;
1198 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1199 std::abs(R3) / pvValue);
1200 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1201 const auto R4 = modelResid[cell_idx][contiUreaEqIdx];
1202 R_sum[contiUreaEqIdx] += R4;
1203 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1204 std::abs(R4) / pvValue);
1205 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1206 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1207 R_sum[contiCalciteEqIdx] += R5;
1208 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1209 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:192
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:158
Class for handling the blackoil well model.
Definition: BlackoilWellModel.hpp:101
Definition: ConvergenceReport.hpp:38
Severity
Definition: ConvergenceReport.hpp:49
@ ConvergenceMonitorFailure
void prepareSolutionUpdate() override
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:467
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:488
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: NonlinearSystemBlackOilReservoir.hpp:70
NonlinearSystemBlackOilReservoir(Simulator &simulator, const ModelParameters ¶m, BlackoilWellModel< TypeTag > &well_model, const bool terminal_output)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:78
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:561
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:580
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:955
const SimulatorReport & localAccumulatedReports() const
return the statistics of local solves accumulated for this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1037
SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:203
MaxSolutionUpdateData getMaxSolutionUpdate(const std::vector< unsigned > &ixCells)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:507
const std::vector< SimulatorReport > & domainAccumulatedReports() const
return the statistics of local solves accumulated for each domain on this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1048
bool shouldStoreSolutionUpdate() const override
Get solution update vector as a PrimaryVariable.
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:458
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:712
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:631
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int maxIter, std::vector< Scalar > &residual_norms)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1001
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:1058
Scalar relativeChange() const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:311
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:1095
void solveJacobianSystem(BVector &x)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:398
SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:237
void writePartitions(const std::filesystem::path &odir) const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1068
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
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
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Solver parameters for the NonlinearSystemBlackOilReservoir.
Definition: BlackoilModelParameters.hpp:201
std::string nonlinear_solver_
Nonlinear solver type: newton or nldd.
Definition: BlackoilModelParameters.hpp:372
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:122
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:55
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 total_linear_iterations
Definition: SimulatorReport.hpp:51