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);
118 if (this->enable_state_rollback_) {
119 this->simulator_.model().newtonMethod().resetPrimaryVariableSwitches();
122 if (this->param_.update_equations_scaling_) {
123 OpmLog::error(
"Equation scaling not supported");
127 if (hasNlddSolver()) {
128 nlddSolver_->prepareStep();
131 report.pre_post_time += perfTimer.stop();
133 auto getIdx = [](
unsigned phaseIdx) ->
int
135 if (FluidSystem::phaseIsActive(phaseIdx)) {
136 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
137 return FluidSystem::canonicalToActiveCompIdx(sIdx);
142 const auto& schedule = this->simulator_.vanguard().schedule();
143 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
144 rst_conv.init(this->simulator_.vanguard().globalNumCells(),
146 {getIdx(FluidSystem::oilPhaseIdx),
147 getIdx(FluidSystem::gasPhaseIdx),
148 getIdx(FluidSystem::waterPhaseIdx),
156template <
class TypeTag>
164 ParentType::initialLinearization(report,
170 std::vector<Scalar> residual_norms;
171 Dune::Timer perfTimer;
176 auto convrep = getConvergence(timer, maxIter, residual_norms);
177 report.
converged = convrep.converged() &&
178 this->simulator_.problem().iterationContext().iteration() >= minIter;
185 this->convergence_reports_.back().report.push_back(std::move(convrep));
189 this->failureReport_ += report;
190 OPM_THROW_PROBLEM(NumericalProblem,
"NaN residual found!");
192 this->failureReport_ += report;
193 OPM_THROW_NOLOG(NumericalProblem,
"Too large residual found!");
195 this->failureReport_ += report;
196 OPM_THROW_PROBLEM(ConvergenceMonitorFailure,
198 "Total penalty count exceeded cut-off-limit of {}",
199 this->param_.monitor_params_.cutoff_
204 this->residual_norms_history_.push_back(residual_norms);
207template <
class TypeTag>
208template <
class NonlinearSolverType>
212 NonlinearSolverType& nonlinear_solver)
217 if (this->simulator_.problem().iterationContext().needsTimestepInit()) {
218 this->residual_norms_history_.clear();
219 this->conv_monitor_.reset();
220 this->current_relaxation_ = 1.0;
223 this->convergence_reports_.back().report.reserve(11);
227 if (this->param_.nonlinear_solver_ !=
"nldd") {
228 result = this->nonlinearIterationNewton(timer, nonlinear_solver);
231 result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
234 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
235 rst_conv.update(this->simulator_.model().linearizer().residual());
237 this->simulator_.problem().advanceIteration();
241template <
class TypeTag>
242template <
class NonlinearSolverType>
246 NonlinearSolverType& nonlinear_solver)
251 Dune::Timer perfTimer;
253 this->initialLinearization(report,
254 this->param_.newton_min_iter_,
255 this->param_.newton_max_iter_,
263 const unsigned nc = this->simulator_.model().numGridDof();
266 linear_solve_setup_time_ = 0.0;
268 this->wellModel().linearize(this->simulator().model().linearizer().jacobian(),
269 this->simulator().model().linearizer().residual());
271 solveJacobianSystem(x);
282 this->failureReport_ += report;
289 this->wellModel().postSolve(x);
291 if (this->param_.use_update_stabilization_) {
292 bool isOscillate =
false;
293 bool isStagnate =
false;
294 nonlinear_solver.detectOscillations(this->residual_norms_history_,
295 this->residual_norms_history_.size() - 1,
299 this->current_relaxation_ -= nonlinear_solver.relaxIncrement();
300 this->current_relaxation_ = std::max(this->current_relaxation_, nonlinear_solver.relaxMax());
306 if (!this->convergence_reports_.empty() &&
307 !this->convergence_reports_.back().report.empty())
309 auto& convrep = this->convergence_reports_.back().report.back();
311 convrep.setOscillationSource(source);
313 if (this->terminalOutputEnabled()) {
314 OpmLog::info(
" Oscillating behavior detected (" +
to_string(source)
315 +
"): Relaxation set to "
319 nonlinear_solver.stabilizeNonlinearUpdate(x, this->dx_old_, this->current_relaxation_);
322 this->updateSolution(x);
329template <
class TypeTag>
337 const auto& elemMapper = this->simulator_.model().elementMapper();
338 const auto& gridView = this->simulator_.gridView();
339 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
340 unsigned globalElemIdx = elemMapper.index(elem);
341 const auto& priVarsNew = this->simulator_.model().solution(0)[globalElemIdx];
344 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
346 Scalar saturationsNew[FluidSystem::numPhases] = { 0.0 };
347 Scalar oilSaturationNew = 1.0;
348 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) &&
349 FluidSystem::numActivePhases() > 1 &&
350 priVarsNew.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
352 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
353 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
356 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
357 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
358 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
360 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
361 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
362 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
365 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
366 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
369 const auto& priVarsOld = this->simulator_.model().solution(1)[globalElemIdx];
372 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
374 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
375 Scalar oilSaturationOld = 1.0;
378 Scalar tmp = pressureNew - pressureOld;
379 resultDelta += tmp*tmp;
380 resultDenom += pressureNew*pressureNew;
382 if (FluidSystem::numActivePhases() > 1) {
383 if (priVarsOld.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
384 saturationsOld[FluidSystem::waterPhaseIdx] =
385 priVarsOld[Indices::waterSwitchIdx];
386 oilSaturationOld -= saturationsOld[FluidSystem::waterPhaseIdx];
389 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
391 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
392 saturationsOld[FluidSystem::gasPhaseIdx] =
393 priVarsOld[Indices::compositionSwitchIdx];
394 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
397 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
398 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
400 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++ phaseIdx) {
401 Scalar tmpSat = saturationsNew[phaseIdx] - saturationsOld[phaseIdx];
402 resultDelta += tmpSat*tmpSat;
403 resultDenom += saturationsNew[phaseIdx]*saturationsNew[phaseIdx];
406 if (!std::isfinite(resultDelta) || !std::isfinite(resultDenom)) {
407 OPM_THROW(std::runtime_error,
408 "Non-finite solution change in the convergence measure");
414 resultDelta = gridView.comm().sum(resultDelta);
415 resultDenom = gridView.comm().sum(resultDenom);
417 return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0;
420template <
class TypeTag>
425 auto& jacobian = this->simulator_.model().linearizer().jacobian().istlMatrix();
426 auto& residual = this->simulator_.model().linearizer().residual();
427 auto& linSolver = this->simulator_.model().newtonMethod().linearSolver();
429 const int numSolvers = linSolver.numAvailableSolvers();
430 if (numSolvers > 1 && (linSolver.getSolveCount() % 100 == 0)) {
431 if (this->terminal_output_) {
432 OpmLog::debug(
"\nRunning speed test for comparing available linear solvers.");
435 Dune::Timer perfTimer;
436 std::vector<double> times(numSolvers);
437 std::vector<double> setupTimes(numSolvers);
440 std::vector<BVector> x_trial(numSolvers, x);
441 for (
int solver = 0; solver < numSolvers; ++solver) {
442 linSolver.setActiveSolver(solver);
444 linSolver.prepare(jacobian, residual);
445 setupTimes[solver] = perfTimer.stop();
447 linSolver.setResidual(residual);
449 linSolver.solve(x_trial[solver]);
450 times[solver] = setupTimes[solver] + perfTimer.stop();
452 if (this->terminal_output_) {
453 OpmLog::debug(fmt::format(fmt::runtime(
"Solver time {}: {}"), solver, times[solver]));
457 int fastest_solver = std::ranges::min_element(times) - times.begin();
459 this->grid_.comm().broadcast(&fastest_solver, 1, 0);
460 this->linear_solve_setup_time_ = setupTimes[fastest_solver];
461 x = x_trial[fastest_solver];
462 linSolver.setActiveSolver(fastest_solver);
467 Dune::Timer perfTimer;
469 linSolver.prepare(jacobian, residual);
470 this->linear_solve_setup_time_ = perfTimer.stop();
471 linSolver.setResidual(residual);
480template <
class TypeTag>
485 return this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
486 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0;
489template <
class TypeTag>
495 unsigned nc = this->simulator_.model().numGridDof();
498 const auto& elemMapper = this->simulator_.model().elementMapper();
499 const auto& gridView = this->simulator_.gridView();
500 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
502 unsigned globalElemIdx = elemMapper.index(elem);
503 solUpd_[globalElemIdx] = this->simulator_.model().solution(0)[globalElemIdx];
506 std::ranges::fill(solUpd_[globalElemIdx], 0.0);
510template <
class TypeTag>
515 const auto& elemMapper = this->simulator_.model().elementMapper();
516 const auto& gridView = this->simulator_.gridView();
517 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
519 unsigned globalElemIdx = elemMapper.index(elem);
520 auto& value = solUpd_[globalElemIdx];
521 const auto& update = dx[globalElemIdx];
522 assert(value.size() == update.size());
525 std::ranges::copy(update, value.begin());
529template <
class TypeTag>
534 static constexpr bool enableSolvent =
535 Indices::solventSaturationIdx != std::numeric_limits<unsigned>::max();
536 static constexpr bool enableBrine =
537 Indices::saltConcentrationIdx != std::numeric_limits<unsigned>::max();
546 for (
const auto& ix : ixCells) {
547 const auto& value = solUpd_[ix];
548 for (
unsigned pvIdx = 0; pvIdx < value.size(); ++pvIdx) {
549 if (pvIdx == Indices::pressureSwitchIdx) {
550 dPMax = std::max(dPMax, std::abs(value[pvIdx]));
552 else if ((pvIdx == Indices::waterSwitchIdx
553 && value.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
554 || (pvIdx == Indices::compositionSwitchIdx
555 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
556 || (enableSolvent && pvIdx == Indices::solventSaturationIdx
557 && value.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss)
558 || (enableBrine && enableSaltPrecipitation && pvIdx == Indices::saltConcentrationIdx
559 && value.primaryVarsMeaningBrine() == PrimaryVariables::BrineMeaning::Sp) ) {
560 dSMax = std::max(dSMax, std::abs(value[pvIdx]));
562 else if (pvIdx == Indices::compositionSwitchIdx
563 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
564 dRsMax = std::max(dRsMax, std::abs(value[pvIdx]));
566 else if (pvIdx == Indices::compositionSwitchIdx
567 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
568 dRvMax = std::max(dRvMax, std::abs(value[pvIdx]));
574 dPMax = this->grid_.comm().max(dPMax);
575 dSMax = this->grid_.comm().max(dSMax);
576 dRsMax = this->grid_.comm().max(dRsMax);
577 dRvMax = this->grid_.comm().max(dRvMax);
579 return { dPMax, dSMax, dRsMax, dRvMax };
582template <
class TypeTag>
583std::tuple<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
588 const Scalar numAquiferPvSumLocal,
589 std::vector< Scalar >& R_sum,
590 std::vector< Scalar >& maxCoeff,
591 std::vector< Scalar >& B_avg)
593 return ParentType::convergenceReduction(comm,
595 numAquiferPvSumLocal,
601template <
class TypeTag>
602std::pair<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
606 std::vector<Scalar>& maxCoeff,
607 std::vector<Scalar>& B_avg,
608 std::vector<int>& maxCoeffCell)
610 OPM_TIMEBLOCK(localConvergenceData);
612 Scalar numAquiferPvSumLocal = 0.0;
613 const auto& model = this->simulator_.model();
614 const auto& problem = this->simulator_.problem();
616 const auto& residual = this->simulator_.model().linearizer().residual();
619 const auto& gridView = this->simulator().gridView();
622 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
623 elemCtx.updatePrimaryStencil(elem);
624 elemCtx.updatePrimaryIntensiveQuantities(0);
626 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
627 const auto& intQuants = elemCtx.intensiveQuantities(0, 0);
628 const auto& fs = intQuants.fluidState();
630 const auto pvValue = problem.referencePorosity(cell_idx, 0) *
631 model.dofTotalVolume(cell_idx);
632 pvSumLocal += pvValue;
634 if (isNumericalAquiferCell(elem)) {
635 numAquiferPvSumLocal += pvValue;
638 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
639 B_avg, R_sum, maxCoeff, maxCoeffCell);
645 const int bSize = B_avg.size();
646 for (
int i = 0; i < bSize; ++i) {
647 B_avg[i] /=
Scalar(this->global_nc_);
650 return {pvSumLocal, numAquiferPvSumLocal};
653template <
class TypeTag>
658 OPM_TIMEBLOCK(computeCnvErrorPv);
663 constexpr auto numPvGroups = std::vector<double>::size_type{3};
665 auto cnvPvSplit = std::pair<std::vector<double>, std::vector<int>> {
666 std::piecewise_construct,
667 std::forward_as_tuple(numPvGroups),
668 std::forward_as_tuple(numPvGroups)
671 auto maxCNV = [&B_avg, dt](
const auto& residual,
const double pvol)
674 std::inner_product(residual.begin(), residual.end(),
676 [](
const Scalar m,
const auto& x)
679 return std::max(m, abs(x));
680 }, std::multiplies<>{});
683 auto& [splitPV, cellCntPV] = cnvPvSplit;
685 const auto& model = this->simulator().model();
686 const auto& problem = this->simulator().problem();
687 const auto& residual = model.linearizer().residual();
688 const auto& gridView = this->simulator().gridView();
694 std::vector<unsigned> ixCells;
697 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
699 if (isNumericalAquiferCell(elem)) {
703 elemCtx.updatePrimaryStencil(elem);
705 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
706 const auto pvValue = problem.referencePorosity(cell_idx, 0)
707 * model.dofTotalVolume(cell_idx);
709 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
711 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
712 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
714 splitPV[ix] +=
static_cast<double>(pvValue);
719 (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
720 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0 ) ) {
721 ixCells.push_back(cell_idx);
728 this->grid_.comm().sum(splitPV .data(), splitPV .size());
729 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
731 return { cnvPvSplit, ixCells };
734template <
class TypeTag>
740 std::vector<Scalar>& B_avg,
741 std::vector<Scalar>& residual_norms)
743 OPM_TIMEBLOCK(getReservoirConvergence);
744 using Vector = std::vector<Scalar>;
746 const auto& iterCtx = this->simulator_.problem().iterationContext();
750 const int numComp = numEq;
752 Vector R_sum(numComp,
Scalar{0});
753 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
754 std::vector<int> maxCoeffCell(numComp, -1);
756 const auto [pvSumLocal, numAquiferPvSumLocal] =
757 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
760 const auto& [pvSum, numAquiferPvSum] =
761 this->convergenceReduction(this->grid_.comm(),
763 numAquiferPvSumLocal,
764 R_sum, maxCoeff, B_avg);
766 auto cnvSplitData = this->characteriseCnvPvSplit(B_avg, dt);
767 report.setCnvPoreVolSplit(cnvSplitData.cnvPvSplit,
768 pvSum - numAquiferPvSum);
777 const bool relax_final_iteration_mb =
778 this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
780 const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
781 iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
783 const bool use_relaxed_mb = relax_final_iteration_mb
792 const bool relax_final_iteration_cnv =
793 this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
795 const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
796 iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
801 const auto relax_pv_fraction_cnv =
802 [&report,
this, eligible = pvSum - numAquiferPvSum]()
804 const auto& cnvPvSplit = report.cnvPvSplit().first;
808 Scalar cnvPvSum =
static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]);
809 return cnvPvSum < this->param_.relaxed_max_pv_fraction_ * eligible &&
817 const bool use_dp_tol = this->param_.tolerance_max_dp_ > 0.0;
818 const bool use_ds_tol = this->param_.tolerance_max_ds_ > 0.0;
819 const bool use_drs_tol = this->param_.tolerance_max_drs_ > 0.0;
820 const bool use_drv_tol = this->param_.tolerance_max_drv_ > 0.0;
821 const bool use_dsol_tol = use_dp_tol || use_ds_tol || use_drs_tol || use_drv_tol;
822 bool relax_dsol_cnv =
false;
823 if (!iterCtx.isFirstGlobalIteration() && use_dsol_tol) {
824 maxSolUpd = getMaxSolutionUpdate(cnvSplitData.ixCells);
826 (!use_dp_tol || (maxSolUpd.
dPMax > 0.0 && maxSolUpd.
dPMax < this->param_.tolerance_max_dp_)) &&
827 (!use_ds_tol || (maxSolUpd.
dSMax > 0.0 && maxSolUpd.
dSMax < this->param_.tolerance_max_ds_)) &&
828 (!use_drs_tol || (maxSolUpd.
dRsMax > 0.0 && maxSolUpd.
dRsMax < this->param_.tolerance_max_drs_)) &&
829 (!use_drv_tol || (maxSolUpd.
dRvMax > 0.0 && maxSolUpd.
dRvMax < this->param_.tolerance_max_drv_));
833 const bool use_relaxed_cnv = relax_final_iteration_cnv
834 || relax_pv_fraction_cnv
840 Scalar tolerance_cnv_relaxed = relax_dsol_cnv ? 1e20 : this->param_.tolerance_cnv_relaxed_;
842 const auto tol_cnv = use_relaxed_cnv ? tolerance_cnv_relaxed : this->param_.tolerance_cnv_;
843 const auto tol_mb = use_relaxed_mb ? this->param_.tolerance_mb_relaxed_ : this->param_.tolerance_mb_;
849 const auto source = relax_dsol_cnv ? RS::SolChange
850 : relax_pv_fraction_cnv ? RS::PvFraction
851 : relax_final_iteration_cnv ? RS::FinalIter
852 : relax_iter_cnv ? RS::IterCount
854 report.setCnvRelaxation(source,
static_cast<double>(tol_cnv));
856 const auto tol_cnv_energy = use_relaxed_cnv ? this->param_.tolerance_cnv_energy_relaxed_ : this->param_.tolerance_cnv_energy_;
857 const auto tol_eb = use_relaxed_mb ? this->param_.tolerance_energy_balance_relaxed_ : this->param_.tolerance_energy_balance_;
860 std::vector<Scalar> CNV(numComp);
861 std::vector<Scalar> mass_balance_residual(numComp);
862 for (
int compIdx = 0; compIdx < numComp; ++compIdx)
864 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
865 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
866 residual_norms.push_back(CNV[compIdx]);
870 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
872 mass_balance_residual[compIdx], CNV[compIdx],
875 const CR::ReservoirFailure::Type types[2] = {
876 CR::ReservoirFailure::Type::MassBalance,
877 CR::ReservoirFailure::Type::Cnv,
880 Scalar tol[2] = { tol_mb, tol_cnv, };
881 if (has_energy_ && compIdx == contiEnergyEqIdx) {
883 tol[1] = tol_cnv_energy;
886 this->addReservoirConvergenceMetrics(
889 this->compNames_.name(compIdx),
890 std::span<const Scalar>{res},
891 std::span<const CR::ReservoirFailure::Type>{types},
892 std::span<const Scalar>{tol},
893 maxResidualAllowed(),
894 [
this](
const std::string& message)
896 if (this->terminal_output_) {
897 OpmLog::debug(message);
903 this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
906 if (this->terminal_output_) {
908 if (iterCtx.isFirstGlobalIteration()) {
909 std::string msg =
"Iter";
910 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
912 msg += this->compNames_.name(compIdx)[0];
916 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
918 msg += this->compNames_.name(compIdx)[0];
923 msg += use_dp_tol ?
" DP " :
"";
924 msg += use_ds_tol ?
" DS " :
"";
925 msg += use_drs_tol ?
" DRS " :
"";
926 msg += use_drv_tol ?
" DRV " :
"";
935 std::ostringstream ss;
936 const std::streamsize oprec = ss.precision(3);
937 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
939 ss << std::setw(4) << iterCtx.iteration();
940 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
941 ss << std::setw(11) << mass_balance_residual[compIdx];
944 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
945 ss << std::setw(11) << CNV[compIdx];
950 [&] (
bool use_tol,
Scalar dsol) {
954 if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
955 ss << std::string(5,
' ') <<
"-" << std::string(5,
' ');
958 ss << std::setw(11) << dsol;
961 print_dsol(use_dp_tol, maxSolUpd.
dPMax);
962 print_dsol(use_ds_tol, maxSolUpd.
dSMax);
963 print_dsol(use_drs_tol, maxSolUpd.
dRsMax);
964 print_dsol(use_drv_tol, maxSolUpd.
dRvMax);
967 const auto mb_flag = use_relaxed_mb
968 ? DebugFlags::RELAXED
969 : DebugFlags::STRICT;
971 const auto cnv_flag = relax_dsol_cnv ?
974 ? DebugFlags::RELAXED
975 : DebugFlags::STRICT);
977 ss << std::setw(9) << make_string<TypeTag>(mb_flag)
978 << std::setw(9) << make_string<TypeTag>(cnv_flag);
983 OpmLog::debug(ss.str());
989template <
class TypeTag>
994 const double tol_cnv,
995 const double tol_cnv_energy)
997 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
998 if (!rst_conv.hasConv()) {
1002 if (this->simulator_.problem().iterationContext().isFirstGlobalIteration()) {
1003 rst_conv.prepareConv();
1006 const auto& residual = this->simulator_.model().linearizer().residual();
1007 const auto& gridView = this->simulator_.gridView();
1010 std::vector<int> convNewt(residual.size(), 0);
1013 const int numComp = B_avg.size();
1014 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
1015 elemCtx.updatePrimaryStencil(elem);
1017 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
1018 const auto pvValue = this->simulator_.problem().referencePorosity(cell_idx, 0) *
1019 this->simulator_.model().dofTotalVolume(cell_idx);
1020 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1021 const auto tol = (has_energy_ && compIdx == contiEnergyEqIdx) ? tol_cnv_energy : tol_cnv;
1022 const Scalar cnv = std::abs(B_avg[compIdx] * residual[cell_idx][compIdx]) * dt / pvValue;
1023 if (std::isnan(cnv) || cnv > maxResidualAllowed() || cnv < 0.0 || cnv > tol) {
1031 this->grid_.comm());
1032 rst_conv.updateNewton(convNewt);
1035template <
class TypeTag>
1040 std::vector<Scalar>& residual_norms)
1042 OPM_TIMEBLOCK(getConvergence);
1044 std::vector<Scalar> B_avg(numEq, 0.0);
1047 maxIter, B_avg, residual_norms);
1049 OPM_TIMEBLOCK(getWellConvergence);
1050 report += this->wellModel().getWellConvergence(B_avg,
1051 report.converged());
1054 conv_monitor_.checkPenaltyCard(report, this->simulator_.problem().iterationContext().iteration());
1059template <
class TypeTag>
1060std::vector<std::vector<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar> >
1064 OPM_TIMEBLOCK(computeFluidInPlace);
1067 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1068 return regionValues;
1071template <
class TypeTag>
1076 if (!hasNlddSolver()) {
1077 OPM_THROW(std::runtime_error,
"Cannot get local reports from a model without NLDD solver");
1079 return nlddSolver_->localAccumulatedReports();
1082template <
class TypeTag>
1083const std::vector<SimulatorReport>&
1088 OPM_THROW(std::runtime_error,
"Cannot get domain reports from a model without NLDD solver");
1089 return nlddSolver_->domainAccumulatedReports();
1092template <
class TypeTag>
1097 if (hasNlddSolver()) {
1098 nlddSolver_->writeNonlinearIterationsPerCell(odir);
1102template <
class TypeTag>
1107 if (hasNlddSolver()) {
1108 nlddSolver_->writePartitions(odir);
1112 const auto& elementMapper = this->simulator().model().elementMapper();
1113 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1115 const auto& grid = this->simulator().vanguard().grid();
1116 const auto& comm = grid.comm();
1117 const auto nDigit = 1 +
static_cast<int>(std::floor(std::log10(comm.size())));
1119 std::ofstream pfile {odir / fmt::format(
"{1:0>{0}}", nDigit, comm.rank())};
1121 for (
const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1122 pfile << comm.rank() <<
' '
1123 << cartMapper.cartesianIndex(elementMapper.index(cell)) <<
' '
1124 << comm.rank() <<
'\n';
1128template <
class TypeTag>
1129template<
class Flu
idState,
class Res
idual>
1134 const FluidState& fs,
1135 const Residual& modelResid,
1137 std::vector<Scalar>& B_avg,
1138 std::vector<Scalar>& R_sum,
1139 std::vector<Scalar>& maxCoeff,
1140 std::vector<int>& maxCoeffCell)
1142 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1144 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1148 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1149 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1151 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1152 const auto R2 = modelResid[cell_idx][compIdx];
1154 R_sum[compIdx] += R2;
1155 const Scalar Rval = std::abs(R2) / pvValue;
1156 if (Rval > maxCoeff[compIdx]) {
1157 maxCoeff[compIdx] = Rval;
1158 maxCoeffCell[compIdx] = cell_idx;
1162 if constexpr (has_solvent_) {
1163 B_avg[contiSolventEqIdx] +=
1164 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1165 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1166 R_sum[contiSolventEqIdx] += R2;
1167 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1168 std::abs(R2) / pvValue);
1170 if constexpr (has_extbo_) {
1171 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1172 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1173 R_sum[ contiZfracEqIdx ] += R2;
1174 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1175 std::abs(R2) / pvValue);
1177 if constexpr (has_polymer_) {
1178 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1179 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1180 R_sum[contiPolymerEqIdx] += R2;
1181 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1182 std::abs(R2) / pvValue);
1184 if constexpr (has_foam_) {
1185 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1186 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1187 R_sum[contiFoamEqIdx] += R2;
1188 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1189 std::abs(R2) / pvValue);
1191 if constexpr (has_brine_) {
1192 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1193 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1194 R_sum[contiBrineEqIdx] += R2;
1195 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1196 std::abs(R2) / pvValue);
1199 if constexpr (has_polymermw_) {
1200 static_assert(has_polymer_);
1202 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1206 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1207 R_sum[contiPolymerMWEqIdx] += R2;
1208 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1209 std::abs(R2) / pvValue);
1212 if constexpr (has_energy_) {
1213 B_avg[contiEnergyEqIdx] += 1.0;
1214 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1215 R_sum[contiEnergyEqIdx] += R2;
1216 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1217 std::abs(R2) / pvValue);
1220 if constexpr (has_bioeffects_) {
1221 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1222 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1223 R_sum[contiMicrobialEqIdx] += R1;
1224 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1225 std::abs(R1) / pvValue);
1226 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1227 const auto R2 = modelResid[cell_idx][contiBiofilmEqIdx];
1228 R_sum[contiBiofilmEqIdx] += R2;
1229 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1230 std::abs(R2) / pvValue);
1231 if constexpr (has_micp_) {
1232 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1233 const auto R3 = modelResid[cell_idx][contiOxygenEqIdx];
1234 R_sum[contiOxygenEqIdx] += R3;
1235 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1236 std::abs(R3) / pvValue);
1237 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1238 const auto R4 = modelResid[cell_idx][contiUreaEqIdx];
1239 R_sum[contiUreaEqIdx] += R4;
1240 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1241 std::abs(R4) / pvValue);
1242 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1243 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1244 R_sum[contiCalciteEqIdx] += R5;
1245 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1246 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:492
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:513
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:586
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:605
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:992
const SimulatorReport & localAccumulatedReports() const
return the statistics of local solves accumulated for this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1074
SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:211
MaxSolutionUpdateData getMaxSolutionUpdate(const std::vector< unsigned > &ixCells)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:532
const std::vector< SimulatorReport > & domainAccumulatedReports() const
return the statistics of local solves accumulated for each domain on this rank
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1085
bool shouldStoreSolutionUpdate() const override
Get solution update vector as a PrimaryVariable.
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:483
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:737
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:656
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int maxIter, std::vector< Scalar > &residual_norms)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1038
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:1095
Scalar relativeChange() const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:332
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:159
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:1132
void solveJacobianSystem(BVector &x)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:423
SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:245
void writePartitions(const std::filesystem::path &odir) const
Definition: NonlinearSystemBlackOilReservoir_impl.hpp:1105
Definition: NonlinearSystem.hpp:44
const Grid & grid_
Definition: NonlinearSystem.hpp:169
std::vector< StepReport > convergence_reports_
Definition: NonlinearSystem.hpp:175
ModelParameters param_
Definition: NonlinearSystem.hpp:172
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:202
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