24#ifndef OPM_BLACKOILMODEL_IMPL_HEADER_INCLUDED
25#define OPM_BLACKOILMODEL_IMPL_HEADER_INCLUDED
27#ifndef OPM_BLACKOILMODEL_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 : simulator_(simulator)
83 , grid_(simulator_.vanguard().grid())
85 , well_model_ (well_model)
86 , terminal_output_ (terminal_output)
87 , current_relaxation_(1.0)
88 , dx_old_(simulator_.model().numGridDof())
89 , conv_monitor_(param_.monitor_params_)
96 if (terminal_output) {
97 OpmLog::info(
"Using Non-Linear Domain Decomposition solver (nldd).");
99 nlddSolver_ = std::make_unique<BlackoilModelNldd<TypeTag>>(*this);
101 if (terminal_output) {
102 OpmLog::info(
"Using Newton nonlinear solver.");
105 OPM_THROW(std::runtime_error,
"Unknown nonlinear solver option: " +
110template <
class TypeTag>
116 Dune::Timer perfTimer;
120 if (grid_.comm().size() > 1 && grid_.comm().max(lastStepFailed) != grid_.comm().min(lastStepFailed)) {
121 OPM_THROW(std::runtime_error,
"Misalignment of the parallel simulation run in prepareStep " +
122 "- the previous step succeeded on some ranks but failed on others.");
124 if (lastStepFailed) {
125 simulator_.model().updateFailed();
128 simulator_.model().advanceTimeLevel();
138 simulator_.problem().resetIterationForNewTimestep();
140 simulator_.problem().beginTimeStep();
142 unsigned numDof = simulator_.model().numGridDof();
143 wasSwitched_.resize(numDof);
144 std::fill(wasSwitched_.begin(), wasSwitched_.end(),
false);
146 if (param_.update_equations_scaling_) {
147 OpmLog::error(
"Equation scaling not supported");
151 if (hasNlddSolver()) {
152 nlddSolver_->prepareStep();
157 auto getIdx = [](
unsigned phaseIdx) ->
int
159 if (FluidSystem::phaseIsActive(phaseIdx)) {
160 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
161 return FluidSystem::canonicalToActiveCompIdx(sIdx);
166 const auto& schedule = simulator_.vanguard().schedule();
167 auto& rst_conv = simulator_.problem().eclWriter().mutableOutputModule().getConv();
168 rst_conv.init(simulator_.vanguard().globalNumCells(),
170 {getIdx(FluidSystem::oilPhaseIdx),
171 getIdx(FluidSystem::gasPhaseIdx),
172 getIdx(FluidSystem::waterPhaseIdx),
180template <
class TypeTag>
190 Dune::Timer perfTimer;
197 report += assembleReservoir(timer);
201 simulator_.problem().markTimestepInitialized();
205 failureReport_ += report;
210 std::vector<Scalar> residual_norms;
215 auto convrep = getConvergence(timer, maxIter, residual_norms);
216 report.
converged = convrep.converged() &&
217 simulator_.problem().iterationContext().iteration() >= minIter;
219 convergence_reports_.back().report.push_back(std::move(convrep));
223 failureReport_ += report;
224 OPM_THROW_PROBLEM(NumericalProblem,
"NaN residual found!");
226 failureReport_ += report;
227 OPM_THROW_NOLOG(NumericalProblem,
"Too large residual found!");
229 failureReport_ += report;
230 OPM_THROW_PROBLEM(ConvergenceMonitorFailure,
232 "Total penalty count exceeded cut-off-limit of {}",
233 param_.monitor_params_.cutoff_
238 residual_norms_history_.push_back(residual_norms);
241template <
class TypeTag>
242template <
class NonlinearSolverType>
246 NonlinearSolverType& nonlinear_solver)
251 if (simulator_.problem().iterationContext().needsTimestepInit()) {
252 residual_norms_history_.clear();
253 conv_monitor_.reset();
254 current_relaxation_ = 1.0;
257 convergence_reports_.back().report.reserve(11);
261 if (this->param_.nonlinear_solver_ !=
"nldd")
263 result = this->nonlinearIterationNewton(timer, nonlinear_solver);
266 result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
269 auto& rst_conv = simulator_.problem().eclWriter().mutableOutputModule().getConv();
270 rst_conv.update(simulator_.model().linearizer().residual());
272 simulator_.problem().advanceIteration();
276template <
class TypeTag>
277template <
class NonlinearSolverType>
281 NonlinearSolverType& nonlinear_solver)
285 Dune::Timer perfTimer;
287 this->initialLinearization(report,
288 this->param_.newton_min_iter_,
289 this->param_.newton_max_iter_,
299 unsigned nc = simulator_.model().numGridDof();
303 linear_solve_setup_time_ = 0.0;
308 wellModel().linearize(simulator().model().linearizer().jacobian(),
309 simulator().model().linearizer().residual());
312 solveJacobianSystem(x);
323 failureReport_ += report;
333 wellModel().postSolve(x);
335 if (param_.use_update_stabilization_) {
337 bool isOscillate =
false;
338 bool isStagnate =
false;
339 nonlinear_solver.detectOscillations(residual_norms_history_,
340 residual_norms_history_.size() - 1,
344 current_relaxation_ -= nonlinear_solver.relaxIncrement();
345 current_relaxation_ = std::max(current_relaxation_,
346 nonlinear_solver.relaxMax());
347 if (terminalOutputEnabled()) {
348 std::string msg =
" Oscillating behavior detected: Relaxation set to "
353 nonlinear_solver.stabilizeNonlinearUpdate(x, dx_old_, current_relaxation_);
367template <
class TypeTag>
373 simulator_.problem().beginIteration();
374 simulator_.model().linearizer().linearizeDomain();
375 simulator_.problem().endIteration();
376 return wellModel().lastReport();
379template <
class TypeTag>
387 const auto& elemMapper = simulator_.model().elementMapper();
388 const auto& gridView = simulator_.gridView();
389 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
390 unsigned globalElemIdx = elemMapper.index(elem);
391 const auto& priVarsNew = simulator_.model().solution(0)[globalElemIdx];
394 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
396 Scalar saturationsNew[FluidSystem::numPhases] = { 0.0 };
397 Scalar oilSaturationNew = 1.0;
398 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) &&
399 FluidSystem::numActivePhases() > 1 &&
400 priVarsNew.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
402 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
403 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
406 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
407 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
408 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
410 assert(Indices::compositionSwitchIdx >= 0);
411 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
412 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
415 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
416 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
419 const auto& priVarsOld = simulator_.model().solution(1)[globalElemIdx];
422 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
424 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
425 Scalar oilSaturationOld = 1.0;
428 Scalar tmp = pressureNew - pressureOld;
429 resultDelta += tmp*tmp;
430 resultDenom += pressureNew*pressureNew;
432 if (FluidSystem::numActivePhases() > 1) {
433 if (priVarsOld.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
434 saturationsOld[FluidSystem::waterPhaseIdx] =
435 priVarsOld[Indices::waterSwitchIdx];
436 oilSaturationOld -= saturationsOld[FluidSystem::waterPhaseIdx];
439 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
441 assert(Indices::compositionSwitchIdx >= 0 );
442 saturationsOld[FluidSystem::gasPhaseIdx] =
443 priVarsOld[Indices::compositionSwitchIdx];
444 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
447 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
448 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
450 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++ phaseIdx) {
451 Scalar tmpSat = saturationsNew[phaseIdx] - saturationsOld[phaseIdx];
452 resultDelta += tmpSat*tmpSat;
453 resultDenom += saturationsNew[phaseIdx]*saturationsNew[phaseIdx];
454 assert(std::isfinite(resultDelta));
455 assert(std::isfinite(resultDenom));
460 resultDelta = gridView.comm().sum(resultDelta);
461 resultDenom = gridView.comm().sum(resultDenom);
463 return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0;
466template <
class TypeTag>
471 auto& jacobian = simulator_.model().linearizer().jacobian().istlMatrix();
472 auto& residual = simulator_.model().linearizer().residual();
473 auto& linSolver = simulator_.model().newtonMethod().linearSolver();
475 const int numSolvers = linSolver.numAvailableSolvers();
476 if (numSolvers > 1 && (linSolver.getSolveCount() % 100 == 0)) {
477 if (terminal_output_) {
478 OpmLog::debug(
"\nRunning speed test for comparing available linear solvers.");
481 Dune::Timer perfTimer;
482 std::vector<double> times(numSolvers);
483 std::vector<double> setupTimes(numSolvers);
486 std::vector<BVector> x_trial(numSolvers, x);
487 for (
int solver = 0; solver < numSolvers; ++solver) {
489 linSolver.setActiveSolver(solver);
491 linSolver.prepare(jacobian, residual);
492 setupTimes[solver] = perfTimer.stop();
494 linSolver.setResidual(residual);
496 linSolver.solve(x_trial[solver]);
497 times[solver] = perfTimer.stop();
499 if (terminal_output_) {
500 OpmLog::debug(fmt::format(fmt::runtime(
"Solver time {}: {}"), solver, times[solver]));
504 int fastest_solver = std::ranges::min_element(times) - times.begin();
506 grid_.comm().broadcast(&fastest_solver, 1, 0);
507 linear_solve_setup_time_ = setupTimes[fastest_solver];
508 x = x_trial[fastest_solver];
509 linSolver.setActiveSolver(fastest_solver);
515 Dune::Timer perfTimer;
517 linSolver.prepare(jacobian, residual);
518 linear_solve_setup_time_ = perfTimer.stop();
519 linSolver.setResidual(residual);
528template <
class TypeTag>
533 OPM_TIMEBLOCK(updateSolution);
535 if (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
536 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0) {
537 prepareStoringSolutionUpdate();
540 auto& newtonMethod = simulator_.model().newtonMethod();
543 newtonMethod.update_(solution,
552 OPM_TIMEBLOCK(invalidateAndUpdateIntensiveQuantities);
553 simulator_.model().invalidateAndUpdateIntensiveQuantities(0);
557 if (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
558 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0) {
559 storeSolutionUpdate(dx);
563template <
class TypeTag>
569 unsigned nc = simulator_.model().numGridDof();
572 const auto& elemMapper = simulator_.model().elementMapper();
573 const auto& gridView = simulator_.gridView();
574 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
576 unsigned globalElemIdx = elemMapper.index(elem);
577 solUpd_[globalElemIdx] = simulator_.model().solution(0)[globalElemIdx];
580 std::ranges::fill(solUpd_[globalElemIdx], 0.0);
584template <
class TypeTag>
589 const auto& elemMapper = simulator_.model().elementMapper();
590 const auto& gridView = simulator_.gridView();
591 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
593 unsigned globalElemIdx = elemMapper.index(elem);
594 auto& value = solUpd_[globalElemIdx];
595 const auto& update = dx[globalElemIdx];
596 assert(value.size() == update.size());
599 std::ranges::copy(update, value.begin());
603template <
class TypeTag>
608 static constexpr bool enableSolvent = Indices::solventSaturationIdx >= 0;
609 static constexpr bool enableBrine = Indices::saltConcentrationIdx >= 0;
618 for (
const auto& ix : ixCells) {
619 const auto& value = solUpd_[ix];
620 for (
int pvIdx = 0; pvIdx < static_cast<int>(value.size()); ++pvIdx) {
621 if (pvIdx == Indices::pressureSwitchIdx) {
622 dPMax = std::max(dPMax, std::abs(value[pvIdx]));
624 else if ( (pvIdx == Indices::waterSwitchIdx
625 && value.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw)
626 || (pvIdx == Indices::compositionSwitchIdx
627 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
628 || (enableSolvent && pvIdx == Indices::solventSaturationIdx
629 && value.primaryVarsMeaningSolvent() == PrimaryVariables::SolventMeaning::Ss)
630 || (enableBrine && enableSaltPrecipitation && pvIdx == Indices::saltConcentrationIdx
631 && value.primaryVarsMeaningBrine() == PrimaryVariables::BrineMeaning::Sp) ) {
632 dSMax = std::max(dSMax, std::abs(value[pvIdx]));
634 else if (pvIdx == Indices::compositionSwitchIdx
635 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
636 dRsMax = std::max(dRsMax, std::abs(value[pvIdx]));
638 else if (pvIdx == Indices::compositionSwitchIdx
639 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
640 dRvMax = std::max(dRvMax, std::abs(value[pvIdx]));
646 dPMax = this->grid_.comm().max(dPMax);
647 dSMax = this->grid_.comm().max(dSMax);
648 dRsMax = this->grid_.comm().max(dRsMax);
649 dRvMax = this->grid_.comm().max(dRvMax);
651 return { dPMax, dSMax, dRsMax, dRvMax };
654template <
class TypeTag>
655std::tuple<typename BlackoilModel<TypeTag>::Scalar,
660 const Scalar numAquiferPvSumLocal,
661 std::vector< Scalar >& R_sum,
662 std::vector< Scalar >& maxCoeff,
663 std::vector< Scalar >& B_avg)
665 OPM_TIMEBLOCK(convergenceReduction);
667 Scalar pvSum = pvSumLocal;
668 Scalar numAquiferPvSum = numAquiferPvSumLocal;
670 if (comm.size() > 1) {
672 std::vector< Scalar > sumBuffer;
673 std::vector< Scalar > maxBuffer;
674 const int numComp = B_avg.size();
675 sumBuffer.reserve( 2*numComp + 2 );
676 maxBuffer.reserve( numComp );
677 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
678 sumBuffer.push_back(B_avg[compIdx]);
679 sumBuffer.push_back(R_sum[compIdx]);
680 maxBuffer.push_back(maxCoeff[ compIdx]);
684 sumBuffer.push_back( pvSum );
685 sumBuffer.push_back( numAquiferPvSum );
688 comm.sum( sumBuffer.data(), sumBuffer.size() );
691 comm.max( maxBuffer.data(), maxBuffer.size() );
694 for (
int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx) {
695 B_avg[compIdx] = sumBuffer[buffIdx];
698 R_sum[compIdx] = sumBuffer[buffIdx];
701 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
702 maxCoeff[compIdx] = maxBuffer[compIdx];
706 pvSum = sumBuffer[sumBuffer.size()-2];
707 numAquiferPvSum = sumBuffer.back();
711 return {pvSum, numAquiferPvSum};
714template <
class TypeTag>
715std::pair<typename BlackoilModel<TypeTag>::Scalar,
719 std::vector<Scalar>& maxCoeff,
720 std::vector<Scalar>& B_avg,
721 std::vector<int>& maxCoeffCell)
723 OPM_TIMEBLOCK(localConvergenceData);
725 Scalar numAquiferPvSumLocal = 0.0;
726 const auto& model = simulator_.model();
727 const auto& problem = simulator_.problem();
729 const auto& residual = simulator_.model().linearizer().residual();
732 const auto& gridView = simulator().gridView();
735 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
736 elemCtx.updatePrimaryStencil(elem);
737 elemCtx.updatePrimaryIntensiveQuantities(0);
739 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
740 const auto& intQuants = elemCtx.intensiveQuantities(0, 0);
741 const auto& fs = intQuants.fluidState();
743 const auto pvValue = problem.referencePorosity(cell_idx, 0) *
744 model.dofTotalVolume(cell_idx);
745 pvSumLocal += pvValue;
747 if (isNumericalAquiferCell(elem)) {
748 numAquiferPvSumLocal += pvValue;
751 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
752 B_avg, R_sum, maxCoeff, maxCoeffCell);
758 const int bSize = B_avg.size();
759 for (
int i = 0; i < bSize; ++i) {
760 B_avg[i] /=
Scalar(global_nc_);
763 return {pvSumLocal, numAquiferPvSumLocal};
766template <
class TypeTag>
771 OPM_TIMEBLOCK(computeCnvErrorPv);
776 constexpr auto numPvGroups = std::vector<double>::size_type{3};
778 auto cnvPvSplit = std::pair<std::vector<double>, std::vector<int>> {
779 std::piecewise_construct,
780 std::forward_as_tuple(numPvGroups),
781 std::forward_as_tuple(numPvGroups)
784 auto maxCNV = [&B_avg, dt](
const auto& residual,
const double pvol)
787 std::inner_product(residual.begin(), residual.end(),
789 [](
const Scalar m,
const auto& x)
792 return std::max(m, abs(x));
793 }, std::multiplies<>{});
796 auto& [splitPV, cellCntPV] = cnvPvSplit;
798 const auto& model = this->simulator().model();
799 const auto& problem = this->simulator().problem();
800 const auto& residual = model.linearizer().residual();
801 const auto& gridView = this->simulator().gridView();
807 std::vector<unsigned> ixCells;
810 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
812 if (isNumericalAquiferCell(elem)) {
816 elemCtx.updatePrimaryStencil(elem);
818 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
819 const auto pvValue = problem.referencePorosity(cell_idx, 0)
820 * model.dofTotalVolume(cell_idx);
822 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
824 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
825 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
827 splitPV[ix] +=
static_cast<double>(pvValue);
832 (this->param_.tolerance_max_dp_ > 0.0 || this->param_.tolerance_max_ds_ > 0.0
833 || this->param_.tolerance_max_drs_ > 0.0 || this->param_.tolerance_max_drv_ > 0.0 ) ) {
834 ixCells.push_back(cell_idx);
841 this->grid_.comm().sum(splitPV .data(), splitPV .size());
842 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
844 return { cnvPvSplit, ixCells };
847template <
class TypeTag>
852 this->param_.tolerance_cnv_ = tuning.TRGCNV;
853 this->param_.tolerance_cnv_relaxed_ = tuning.XXXCNV;
854 this->param_.tolerance_mb_ = tuning.TRGMBE;
855 this->param_.tolerance_mb_relaxed_ = tuning.XXXMBE;
856 this->param_.newton_max_iter_ = tuning.NEWTMX;
857 this->param_.newton_min_iter_ = tuning.NEWTMN;
860template <
class TypeTag>
866 this->param_.tolerance_max_dp_ = tuning_dp.TRGDDP;
867 this->param_.tolerance_max_ds_ = tuning_dp.TRGDDS;
868 this->param_.tolerance_max_drs_ = tuning_dp.TRGDDRS;
869 this->param_.tolerance_max_drv_ = tuning_dp.TRGDDRV;
872template <
class TypeTag>
878 std::vector<Scalar>& B_avg,
879 std::vector<Scalar>& residual_norms)
881 OPM_TIMEBLOCK(getReservoirConvergence);
882 using Vector = std::vector<Scalar>;
884 const auto& iterCtx = simulator_.problem().iterationContext();
888 const int numComp = numEq;
890 Vector R_sum(numComp,
Scalar{0});
891 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
892 std::vector<int> maxCoeffCell(numComp, -1);
894 const auto [pvSumLocal, numAquiferPvSumLocal] =
895 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
898 const auto& [pvSum, numAquiferPvSum] =
899 this->convergenceReduction(this->grid_.comm(),
901 numAquiferPvSumLocal,
902 R_sum, maxCoeff, B_avg);
904 auto cnvSplitData = this->characteriseCnvPvSplit(B_avg, dt);
905 report.setCnvPoreVolSplit(cnvSplitData.cnvPvSplit,
906 pvSum - numAquiferPvSum);
915 const bool relax_final_iteration_mb =
916 this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
918 const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
919 iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
921 const bool use_relaxed_mb = relax_final_iteration_mb
930 const bool relax_final_iteration_cnv =
931 this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
933 const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
934 iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
939 const auto relax_pv_fraction_cnv =
940 [&report,
this, eligible = pvSum - numAquiferPvSum]()
942 const auto& cnvPvSplit = report.cnvPvSplit().first;
946 Scalar cnvPvSum =
static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]);
947 return cnvPvSum < this->param_.relaxed_max_pv_fraction_ * eligible &&
955 const bool use_dp_tol = this->param_.tolerance_max_dp_ > 0.0;
956 const bool use_ds_tol = this->param_.tolerance_max_ds_ > 0.0;
957 const bool use_drs_tol = this->param_.tolerance_max_drs_ > 0.0;
958 const bool use_drv_tol = this->param_.tolerance_max_drv_ > 0.0;
959 const bool use_dsol_tol = use_dp_tol || use_ds_tol || use_drs_tol || use_drv_tol;
960 bool relax_dsol_cnv =
false;
961 if (!iterCtx.isFirstGlobalIteration() && use_dsol_tol) {
962 maxSolUpd = getMaxSolutionUpdate(cnvSplitData.ixCells);
964 (!use_dp_tol || (maxSolUpd.
dPMax > 0.0 && maxSolUpd.
dPMax < this->param_.tolerance_max_dp_)) &&
965 (!use_ds_tol || (maxSolUpd.
dSMax > 0.0 && maxSolUpd.
dSMax < this->param_.tolerance_max_ds_)) &&
966 (!use_drs_tol || (maxSolUpd.
dRsMax > 0.0 && maxSolUpd.
dRsMax < this->param_.tolerance_max_drs_)) &&
967 (!use_drv_tol || (maxSolUpd.
dRvMax > 0.0 && maxSolUpd.
dRvMax < this->param_.tolerance_max_drv_));
971 const bool use_relaxed_cnv = relax_final_iteration_cnv
972 || relax_pv_fraction_cnv
978 Scalar tolerance_cnv_relaxed = relax_dsol_cnv ? 1e20 : param_.tolerance_cnv_relaxed_;
980 const auto tol_cnv = use_relaxed_cnv ? tolerance_cnv_relaxed : param_.tolerance_cnv_;
981 const auto tol_mb = use_relaxed_mb ? param_.tolerance_mb_relaxed_ : param_.tolerance_mb_;
982 const auto tol_cnv_energy = use_relaxed_cnv ? param_.tolerance_cnv_energy_relaxed_ : param_.tolerance_cnv_energy_;
983 const auto tol_eb = use_relaxed_mb ? param_.tolerance_energy_balance_relaxed_ : param_.tolerance_energy_balance_;
986 std::vector<Scalar> CNV(numComp);
987 std::vector<Scalar> mass_balance_residual(numComp);
988 for (
int compIdx = 0; compIdx < numComp; ++compIdx)
990 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
991 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
992 residual_norms.push_back(CNV[compIdx]);
996 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
998 mass_balance_residual[compIdx], CNV[compIdx],
1001 const CR::ReservoirFailure::Type types[2] = {
1002 CR::ReservoirFailure::Type::MassBalance,
1003 CR::ReservoirFailure::Type::Cnv,
1006 Scalar tol[2] = { tol_mb, tol_cnv, };
1007 if (has_energy_ && compIdx == contiEnergyEqIdx) {
1009 tol[1] = tol_cnv_energy;
1012 for (
int ii : {0, 1}) {
1013 if (std::isnan(res[ii])) {
1015 if (this->terminal_output_) {
1016 OpmLog::debug(
"NaN residual for " + this->compNames_.name(compIdx) +
" equation.");
1019 else if (res[ii] > maxResidualAllowed()) {
1020 report.setReservoirFailed({types[ii], CR::Severity::TooLarge, compIdx});
1021 if (this->terminal_output_) {
1022 OpmLog::debug(
"Too large residual for " + this->compNames_.name(compIdx) +
" equation.");
1025 else if (res[ii] < 0.0) {
1026 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
1027 if (this->terminal_output_) {
1028 OpmLog::debug(
"Negative residual for " + this->compNames_.name(compIdx) +
" equation.");
1031 else if (res[ii] > tol[ii]) {
1032 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
1035 report.setReservoirConvergenceMetric(types[ii], compIdx, res[ii], tol[ii]);
1040 this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
1043 if (this->terminal_output_) {
1045 if (iterCtx.isFirstGlobalIteration()) {
1046 std::string msg =
"Iter";
1047 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1049 msg += this->compNames_.name(compIdx)[0];
1053 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1055 msg += this->compNames_.name(compIdx)[0];
1060 msg += use_dp_tol ?
" DP " :
"";
1061 msg += use_ds_tol ?
" DS " :
"";
1062 msg += use_drs_tol ?
" DRS " :
"";
1063 msg += use_drv_tol ?
" DRV " :
"";
1072 std::ostringstream ss;
1073 const std::streamsize oprec = ss.precision(3);
1074 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
1076 ss << std::setw(4) << iterCtx.iteration();
1077 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1078 ss << std::setw(11) << mass_balance_residual[compIdx];
1081 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1082 ss << std::setw(11) << CNV[compIdx];
1087 [&] (
bool use_tol,
Scalar dsol) {
1091 if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
1092 ss << std::string(5,
' ') <<
"-" << std::string(5,
' ');
1095 ss << std::setw(11) << dsol;
1098 print_dsol(use_dp_tol, maxSolUpd.
dPMax);
1099 print_dsol(use_ds_tol, maxSolUpd.
dSMax);
1100 print_dsol(use_drs_tol, maxSolUpd.
dRsMax);
1101 print_dsol(use_drv_tol, maxSolUpd.
dRvMax);
1104 const auto mb_flag = use_relaxed_mb
1105 ? DebugFlags::RELAXED
1106 : DebugFlags::STRICT;
1108 const auto cnv_flag = relax_dsol_cnv ?
1109 DebugFlags::TUNINGDP
1111 ? DebugFlags::RELAXED
1112 : DebugFlags::STRICT);
1114 ss << std::setw(9) << make_string<TypeTag>(mb_flag)
1115 << std::setw(9) << make_string<TypeTag>(cnv_flag);
1117 ss.precision(oprec);
1120 OpmLog::debug(ss.str());
1126template <
class TypeTag>
1131 const double tol_cnv,
1132 const double tol_cnv_energy)
1134 auto& rst_conv = simulator_.problem().eclWriter().mutableOutputModule().getConv();
1135 if (!rst_conv.hasConv()) {
1139 if (simulator_.problem().iterationContext().isFirstGlobalIteration()) {
1140 rst_conv.prepareConv();
1143 const auto& residual = simulator_.model().linearizer().residual();
1144 const auto& gridView = this->simulator().gridView();
1147 std::vector<int> convNewt(residual.size(), 0);
1150 const int numComp = B_avg.size();
1151 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
1152 elemCtx.updatePrimaryStencil(elem);
1154 const unsigned cell_idx = elemCtx.globalSpaceIndex(0, 0);
1155 const auto pvValue = simulator_.problem().referencePorosity(cell_idx, 0) *
1156 simulator_.model().dofTotalVolume(cell_idx);
1157 for (
int compIdx = 0; compIdx < numComp; ++compIdx) {
1158 const auto tol = (has_energy_ && compIdx == contiEnergyEqIdx) ? tol_cnv_energy : tol_cnv;
1159 const Scalar cnv = std::abs(B_avg[compIdx] * residual[cell_idx][compIdx]) * dt / pvValue;
1160 if (std::isnan(cnv) || cnv > maxResidualAllowed() || cnv < 0.0 || cnv > tol) {
1168 this->grid_.comm());
1169 rst_conv.updateNewton(convNewt);
1172template <
class TypeTag>
1177 std::vector<Scalar>& residual_norms)
1179 OPM_TIMEBLOCK(getConvergence);
1181 std::vector<Scalar> B_avg(numEq, 0.0);
1184 maxIter, B_avg, residual_norms);
1186 OPM_TIMEBLOCK(getWellConvergence);
1187 report += wellModel().getWellConvergence(B_avg,
1188 report.converged());
1191 conv_monitor_.checkPenaltyCard(report, simulator_.problem().iterationContext().iteration());
1196template <
class TypeTag>
1197std::vector<std::vector<typename BlackoilModel<TypeTag>::Scalar> >
1201 OPM_TIMEBLOCK(computeFluidInPlace);
1204 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1205 return regionValues;
1208template <
class TypeTag>
1213 if (!hasNlddSolver()) {
1214 OPM_THROW(std::runtime_error,
"Cannot get local reports from a model without NLDD solver");
1216 return nlddSolver_->localAccumulatedReports();
1219template <
class TypeTag>
1220const std::vector<SimulatorReport>&
1225 OPM_THROW(std::runtime_error,
"Cannot get domain reports from a model without NLDD solver");
1226 return nlddSolver_->domainAccumulatedReports();
1229template <
class TypeTag>
1234 if (hasNlddSolver()) {
1235 nlddSolver_->writeNonlinearIterationsPerCell(odir);
1239template <
class TypeTag>
1244 if (hasNlddSolver()) {
1245 nlddSolver_->writePartitions(odir);
1249 const auto& elementMapper = this->simulator().model().elementMapper();
1250 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1252 const auto& grid = this->simulator().vanguard().grid();
1253 const auto& comm = grid.comm();
1254 const auto nDigit = 1 +
static_cast<int>(std::floor(std::log10(comm.size())));
1256 std::ofstream pfile {odir / fmt::format(
"{1:0>{0}}", nDigit, comm.rank())};
1258 for (
const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1259 pfile << comm.rank() <<
' '
1260 << cartMapper.cartesianIndex(elementMapper.index(cell)) <<
' '
1261 << comm.rank() <<
'\n';
1265template <
class TypeTag>
1266template<
class Flu
idState,
class Res
idual>
1271 const FluidState& fs,
1272 const Residual& modelResid,
1274 std::vector<Scalar>& B_avg,
1275 std::vector<Scalar>& R_sum,
1276 std::vector<Scalar>& maxCoeff,
1277 std::vector<int>& maxCoeffCell)
1279 for (
unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1281 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1285 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1286 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1288 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1289 const auto R2 = modelResid[cell_idx][compIdx];
1291 R_sum[compIdx] += R2;
1292 const Scalar Rval = std::abs(R2) / pvValue;
1293 if (Rval > maxCoeff[compIdx]) {
1294 maxCoeff[compIdx] = Rval;
1295 maxCoeffCell[compIdx] = cell_idx;
1299 if constexpr (has_solvent_) {
1300 B_avg[contiSolventEqIdx] +=
1301 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1302 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1303 R_sum[contiSolventEqIdx] += R2;
1304 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1305 std::abs(R2) / pvValue);
1307 if constexpr (has_extbo_) {
1308 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1309 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1310 R_sum[ contiZfracEqIdx ] += R2;
1311 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1312 std::abs(R2) / pvValue);
1314 if constexpr (has_polymer_) {
1315 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1316 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1317 R_sum[contiPolymerEqIdx] += R2;
1318 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1319 std::abs(R2) / pvValue);
1321 if constexpr (has_foam_) {
1322 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1323 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1324 R_sum[contiFoamEqIdx] += R2;
1325 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1326 std::abs(R2) / pvValue);
1328 if constexpr (has_brine_) {
1329 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1330 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1331 R_sum[contiBrineEqIdx] += R2;
1332 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1333 std::abs(R2) / pvValue);
1336 if constexpr (has_polymermw_) {
1337 static_assert(has_polymer_);
1339 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1343 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1344 R_sum[contiPolymerMWEqIdx] += R2;
1345 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1346 std::abs(R2) / pvValue);
1349 if constexpr (has_energy_) {
1350 B_avg[contiEnergyEqIdx] += 1.0;
1351 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1352 R_sum[contiEnergyEqIdx] += R2;
1353 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1354 std::abs(R2) / pvValue);
1357 if constexpr (has_bioeffects_) {
1358 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1359 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1360 R_sum[contiMicrobialEqIdx] += R1;
1361 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1362 std::abs(R1) / pvValue);
1363 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1364 const auto R2 = modelResid[cell_idx][contiBiofilmEqIdx];
1365 R_sum[contiBiofilmEqIdx] += R2;
1366 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1367 std::abs(R2) / pvValue);
1368 if constexpr (has_micp_) {
1369 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1370 const auto R3 = modelResid[cell_idx][contiOxygenEqIdx];
1371 R_sum[contiOxygenEqIdx] += R3;
1372 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1373 std::abs(R3) / pvValue);
1374 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1375 const auto R4 = modelResid[cell_idx][contiUreaEqIdx];
1376 R_sum[contiUreaEqIdx] += R4;
1377 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1378 std::abs(R4) / pvValue);
1379 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1380 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1381 R_sum[contiCalciteEqIdx] += R5;
1382 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1383 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
BlackoilModel(Simulator &simulator, const ModelParameters ¶m, BlackoilWellModel< TypeTag > &well_model, const bool terminal_output)
Definition: BlackoilModel_impl.hpp:78
std::vector< std::vector< Scalar > > computeFluidInPlace(const T &, const std::vector< int > &fipnum) const
Wrapper required due to not following generic API.
Definition: BlackoilModel.hpp:264
ModelParameters param_
Definition: BlackoilModel.hpp:354
void writeNonlinearIterationsPerCell(const std::filesystem::path &odir) const
Write the number of nonlinear iterations per cell to a file in ResInsight compatible format.
Definition: BlackoilModel_impl.hpp:1232
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: BlackoilModel.hpp:67
SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: BlackoilModel_impl.hpp:280
const std::vector< SimulatorReport > & domainAccumulatedReports() const
return the statistics of local solves accumulated for each domain on this rank
Definition: BlackoilModel_impl.hpp:1222
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: BlackoilModel_impl.hpp:718
GetPropType< TypeTag, Properties::ElementContext > ElementContext
Definition: BlackoilModel.hpp:66
std::vector< StepReport > convergence_reports_
Definition: BlackoilModel.hpp:371
const SimulatorReport & localAccumulatedReports() const
return the statistics of local solves accumulated for this rank
Definition: BlackoilModel_impl.hpp:1211
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: BlackoilModel_impl.hpp:658
const Grid & grid_
Definition: BlackoilModel.hpp:343
GetPropType< TypeTag, Properties::SolutionVector > SolutionVector
Definition: BlackoilModel.hpp:69
void updateSolution(const BVector &dx)
Apply an update to the primary variables.
Definition: BlackoilModel_impl.hpp:531
long int global_nc_
The number of cells of the global grid.
Definition: BlackoilModel.hpp:363
DebugFlags
Definition: BlackoilModel.hpp:127
void updateTUNING(const Tuning &tuning)
Definition: BlackoilModel_impl.hpp:850
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: BlackoilModel_impl.hpp:1269
std::unique_ptr< BlackoilModelNldd< TypeTag > > nlddSolver_
Non-linear DD solver.
Definition: BlackoilModel.hpp:374
void writePartitions(const std::filesystem::path &odir) const
Definition: BlackoilModel_impl.hpp:1242
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int maxIter, std::vector< Scalar > &residual_norms)
Definition: BlackoilModel_impl.hpp:1175
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: BlackoilModel.hpp:64
void updateTUNINGDP(const TuningDp &tuning_dp)
Definition: BlackoilModel_impl.hpp:863
void initialLinearization(SimulatorReportSingle &report, const int minIter, const int maxIter, const SimulatorTimerInterface &timer)
Definition: BlackoilModel_impl.hpp:183
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: BlackoilModel_impl.hpp:769
SimulatorReportSingle assembleReservoir(const SimulatorTimerInterface &timer)
Assemble the residual and Jacobian of the nonlinear system.
Definition: BlackoilModel_impl.hpp:370
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: BlackoilModel_impl.hpp:1129
SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: BlackoilModel_impl.hpp:245
void storeSolutionUpdate(const BVector &dx)
Definition: BlackoilModel_impl.hpp:587
void solveJacobianSystem(BVector &x)
Definition: BlackoilModel_impl.hpp:469
MaxSolutionUpdateData getMaxSolutionUpdate(const std::vector< unsigned > &ixCells)
Definition: BlackoilModel_impl.hpp:606
SimulatorReportSingle prepareStep(const SimulatorTimerInterface &timer)
Definition: BlackoilModel_impl.hpp:113
ConvergenceReport getReservoirConvergence(const double reportTime, const double dt, const int maxIter, std::vector< Scalar > &B_avg, std::vector< Scalar > &residual_norms)
Definition: BlackoilModel_impl.hpp:875
Dune::BlockVector< VectorBlockType > BVector
Definition: BlackoilModel.hpp:109
void prepareStoringSolutionUpdate()
Get solution update vector as a PrimaryVariable.
Definition: BlackoilModel_impl.hpp:566
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: BlackoilModel.hpp:75
Scalar relativeChange() const
Definition: BlackoilModel_impl.hpp:382
Class for handling the blackoil well model.
Definition: BlackoilWellModel.hpp:98
Definition: ConvergenceReport.hpp:38
Severity
Definition: ConvergenceReport.hpp:49
@ ConvergenceMonitorFailure
void setReservoirFailed(const ReservoirFailure &rf)
Definition: ConvergenceReport.hpp:266
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 bool lastStepFailed() const =0
Return true if last time step failed.
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)
Definition: BlackoilModel.hpp:114
Definition: BlackoilModel.hpp:119
Scalar dRvMax
Definition: BlackoilModel.hpp:123
Scalar dPMax
Definition: BlackoilModel.hpp:120
Scalar dSMax
Definition: BlackoilModel.hpp:121
Scalar dRsMax
Definition: BlackoilModel.hpp:122
Solver parameters for the BlackoilModel.
Definition: BlackoilModelParameters.hpp:194
std::string nonlinear_solver_
Nonlinear solver type: newton or nldd.
Definition: BlackoilModelParameters.hpp:358
Definition: AquiferGridUtils.hpp:35
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
double assemble_time
Definition: SimulatorReport.hpp:39
bool converged
Definition: SimulatorReport.hpp:55
double pre_post_time
Definition: SimulatorReport.hpp:40
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_linearizations
Definition: SimulatorReport.hpp:49
unsigned int total_linear_iterations
Definition: SimulatorReport.hpp:51