NonlinearSystemBlackOilReservoir_impl.hpp
Go to the documentation of this file.
1/*
2 Copyright 2013, 2015 SINTEF ICT, Applied Mathematics.
3 Copyright 2014, 2015 Dr. Blatt - HPC-Simulation-Software & Services
4 Copyright 2014, 2015 Statoil ASA.
5 Copyright 2015 NTNU
6 Copyright 2015, 2016, 2017 IRIS AS
7
8 This file is part of the Open Porous Media project (OPM).
9
10 OPM is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 OPM is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with OPM. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#ifndef OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_IMPL_HEADER_INCLUDED
25#define OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_IMPL_HEADER_INCLUDED
26
27#ifndef OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_HEADER_INCLUDED
28#include <config.h>
30#endif
31
32#include <dune/common/timer.hh>
33
34#include <opm/common/ErrorMacros.hpp>
35#include <opm/common/OpmLog/OpmLog.hpp>
36
38
39#include <algorithm>
40#include <cmath>
41#include <filesystem>
42#include <functional>
43#include <iomanip>
44#include <limits>
45#include <memory>
46#include <numeric>
47#include <sstream>
48#include <stdexcept>
49#include <string>
50#include <string_view>
51#include <tuple>
52#include <utility>
53#include <vector>
54
55#include <fmt/format.h>
56
57namespace {
58 template <typename TypeTag>
59 std::string_view
61 {
63
64 switch (f) {
65 case F::STRICT: return "Strict";
66 case F::RELAXED: return "Relaxed";
67 case F::TUNINGDP: return "TuningDP";
68 }
69
70 return "< ??? >";
71 }
72} // Anonymous namespace
73
74namespace Opm {
75
76template <class TypeTag>
79 const ModelParameters& param,
81 const bool terminal_output)
82 : ParentType(simulator, param, well_model, terminal_output)
83 , conv_monitor_(param.monitor_params_)
84{
85 // compute global sum of number of cells
87 this->convergence_reports_.reserve(300); // Often insufficient, but avoids frequent moves.
88 // TODO: remember to fix!
89 if (this->param_.nonlinear_solver_ == "nldd") {
90 if (terminal_output) {
91 OpmLog::info("Using Non-Linear Domain Decomposition solver (nldd).");
92 }
93 nlddSolver_ = std::make_unique<NonlinearSystemNldd<TypeTag>>(*this);
94 } else if (this->param_.nonlinear_solver_ == "newton") {
95 if (terminal_output) {
96 OpmLog::info("Using Newton nonlinear solver.");
97 }
98 } else {
99 OPM_THROW(std::runtime_error, "Unknown nonlinear solver option: " +
101 }
102}
103
104template <class TypeTag>
108{
109 OPM_TIMEFUNCTION();
110 auto report = ParentType::prepareStep(timer);
111
112 Dune::Timer perfTimer;
113 perfTimer.start();
114
115 unsigned numDof = this->simulator_.model().numGridDof();
116 wasSwitched_.resize(numDof);
117 std::fill(wasSwitched_.begin(), wasSwitched_.end(), false);
118
119 if (this->param_.update_equations_scaling_) {
120 OpmLog::error("Equation scaling not supported");
121 //updateEquationsScaling();
122 }
123
124 if (hasNlddSolver()) {
125 nlddSolver_->prepareStep();
126 }
127
128 report.pre_post_time += perfTimer.stop();
129
130 auto getIdx = [](unsigned phaseIdx) -> int
131 {
132 if (FluidSystem::phaseIsActive(phaseIdx)) {
133 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
134 return FluidSystem::canonicalToActiveCompIdx(sIdx);
135 }
136
137 return -1;
138 };
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(),
142 schedule[timer.reportStepNum()].rst_config(),
143 {getIdx(FluidSystem::oilPhaseIdx),
144 getIdx(FluidSystem::gasPhaseIdx),
145 getIdx(FluidSystem::waterPhaseIdx),
146 contiPolymerEqIdx,
147 contiBrineEqIdx,
148 contiSolventEqIdx});
149
150 return report;
151}
152
153template <class TypeTag>
154void
157 const int minIter,
158 const int maxIter,
159 const SimulatorTimerInterface& timer)
160{
161 ParentType::initialLinearization(report,
162 minIter,
163 maxIter,
164 timer);
165
166 // ----------- Check if converged -----------
167 std::vector<Scalar> residual_norms;
168 Dune::Timer perfTimer;
169 perfTimer.reset();
170 perfTimer.start();
171 // the step is not considered converged until at least minIter iterations is done
172 {
173 auto convrep = getConvergence(timer, maxIter, residual_norms);
174 report.converged = convrep.converged() &&
175 this->simulator_.problem().iterationContext().iteration() >= minIter;
176 ConvergenceReport::Severity severity = convrep.severityOfWorstFailure();
177 this->convergence_reports_.back().report.push_back(std::move(convrep));
178
179 // Throw if any NaN or too large residual found.
181 this->failureReport_ += report;
182 OPM_THROW_PROBLEM(NumericalProblem, "NaN residual found!");
183 } else if (severity == ConvergenceReport::Severity::TooLarge) {
184 this->failureReport_ += report;
185 OPM_THROW_NOLOG(NumericalProblem, "Too large residual found!");
187 this->failureReport_ += report;
188 OPM_THROW_PROBLEM(ConvergenceMonitorFailure,
189 fmt::format(
190 "Total penalty count exceeded cut-off-limit of {}",
191 this->param_.monitor_params_.cutoff_
192 ));
193 }
194 }
195 report.update_time += perfTimer.stop();
196 this->residual_norms_history_.push_back(residual_norms);
197}
198
199template <class TypeTag>
200template <class NonlinearSolverType>
204 NonlinearSolverType& nonlinear_solver)
205{
206 // Model-level timestep initialization (once per timestep).
207 // markTimestepInitialized() is called later in initialLinearization(),
208 // after assembleReservoir() has triggered the well model's prepareTimeStep().
209 if (this->simulator_.problem().iterationContext().needsTimestepInit()) {
210 this->residual_norms_history_.clear();
211 this->conv_monitor_.reset();
212 this->current_relaxation_ = 1.0;
213 this->dx_old_ = 0.0;
214 this->convergence_reports_.push_back({timer.reportStepNum(), timer.currentStepNum(), {}});
215 this->convergence_reports_.back().report.reserve(11);
216 }
217
219 if (this->param_.nonlinear_solver_ != "nldd") {
220 result = this->nonlinearIterationNewton(timer, nonlinear_solver);
221 }
222 else {
223 result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
224 }
225
226 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
227 rst_conv.update(this->simulator_.model().linearizer().residual());
228
229 this->simulator_.problem().advanceIteration();
230 return result;
231}
232
233template <class TypeTag>
234template <class NonlinearSolverType>
238 NonlinearSolverType& nonlinear_solver)
239{
240 OPM_TIMEFUNCTION();
241
243 Dune::Timer perfTimer;
244
245 this->initialLinearization(report,
246 this->param_.newton_min_iter_,
247 this->param_.newton_max_iter_,
248 timer);
249
250 if (!report.converged) {
251 perfTimer.reset();
252 perfTimer.start();
253 report.total_newton_iterations = 1;
254
255 const unsigned nc = this->simulator_.model().numGridDof();
256 BVector x(nc);
257
258 linear_solve_setup_time_ = 0.0;
259 try {
260 this->wellModel().linearize(this->simulator().model().linearizer().jacobian(),
261 this->simulator().model().linearizer().residual());
262
263 solveJacobianSystem(x);
264
265 report.linear_solve_setup_time += linear_solve_setup_time_;
266 report.linear_solve_time += perfTimer.stop();
267 report.total_linear_iterations += linearIterationsLastSolve();
268 }
269 catch (...) {
270 report.linear_solve_setup_time += linear_solve_setup_time_;
271 report.linear_solve_time += perfTimer.stop();
272 report.total_linear_iterations += linearIterationsLastSolve();
273
274 this->failureReport_ += report;
275 throw;
276 }
277
278 perfTimer.reset();
279 perfTimer.start();
280
281 this->wellModel().postSolve(x);
282
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,
288 isOscillate,
289 isStagnate);
290 if (isOscillate) {
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 "
295 + std::to_string(this->current_relaxation_));
296 }
297 }
298 nonlinear_solver.stabilizeNonlinearUpdate(x, this->dx_old_, this->current_relaxation_);
299 }
300
301 this->updateSolution(x);
302 report.update_time += perfTimer.stop();
303 }
304
305 return report;
306}
307
308template <class TypeTag>
311relativeChange() const
312{
313 Scalar resultDelta = 0.0;
314 Scalar resultDenom = 0.0;
315
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(/*timeIdx=*/0)[globalElemIdx];
321
322 Scalar pressureNew;
323 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
324
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)
330 {
331 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
332 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
333 }
334
335 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
336 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
337 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
338 {
339 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
340 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
341 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
342 }
343
344 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
345 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
346 }
347
348 const auto& priVarsOld = this->simulator_.model().solution(/*timeIdx=*/1)[globalElemIdx];
349
350 Scalar pressureOld;
351 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
352
353 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
354 Scalar oilSaturationOld = 1.0;
355
356 // NB fix me! adding pressures changes to saturation changes does not make sense
357 Scalar tmp = pressureNew - pressureOld;
358 resultDelta += tmp*tmp;
359 resultDenom += pressureNew*pressureNew;
360
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];
366 }
367
368 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
369 {
370 assert(Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max());
371 saturationsOld[FluidSystem::gasPhaseIdx] =
372 priVarsOld[Indices::compositionSwitchIdx];
373 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
374 }
375
376 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
377 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
378 }
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));
385 }
386 }
387 }
388
389 resultDelta = gridView.comm().sum(resultDelta);
390 resultDenom = gridView.comm().sum(resultDenom);
391
392 return resultDenom > 0.0 ? resultDelta / resultDenom : 0.0;
393}
394
395template <class TypeTag>
396void
399{
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();
403
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.");
408 }
409
410 Dune::Timer perfTimer;
411 std::vector<double> times(numSolvers);
412 std::vector<double> setupTimes(numSolvers);
413
414 x = 0.0;
415 std::vector<BVector> x_trial(numSolvers, x);
416 for (int solver = 0; solver < numSolvers; ++solver) {
417 linSolver.setActiveSolver(solver);
418 perfTimer.start();
419 linSolver.prepare(jacobian, residual);
420 setupTimes[solver] = perfTimer.stop();
421 perfTimer.reset();
422 linSolver.setResidual(residual);
423 perfTimer.start();
424 linSolver.solve(x_trial[solver]);
425 times[solver] = perfTimer.stop();
426 perfTimer.reset();
427 if (this->terminal_output_) {
428 OpmLog::debug(fmt::format(fmt::runtime("Solver time {}: {}"), solver, times[solver]));
429 }
430 }
431
432 int fastest_solver = std::ranges::min_element(times) - times.begin();
433 // Use timing on rank 0 to determine fastest, must be consistent across ranks.
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);
438 }
439 else {
440 x = 0.0;
441
442 Dune::Timer perfTimer;
443 perfTimer.start();
444 linSolver.prepare(jacobian, residual);
445 this->linear_solve_setup_time_ = perfTimer.stop();
446 linSolver.setResidual(residual);
447 // actually, the error needs to be calculated after setResidual in order to
448 // account for parallelization properly. since the residual of ECFV
449 // discretizations does not need to be synchronized across processes to be
450 // consistent, this is not relevant for OPM-flow...
451 linSolver.solve(x);
452 }
453}
454
455template <class TypeTag>
456bool
459{
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;
462}
463
464template <class TypeTag>
465void
468{
469 // Init. solution update vector
470 unsigned nc = this->simulator_.model().numGridDof();
471 solUpd_.resize(nc);
472
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)) {
476 // Copy solution vector to transfer primary variables meaning
477 unsigned globalElemIdx = elemMapper.index(elem);
478 solUpd_[globalElemIdx] = this->simulator_.model().solution(/*timeIdx=*/0)[globalElemIdx];
479
480 // Ensure each element is zero
481 std::ranges::fill(solUpd_[globalElemIdx], 0.0);
482 }
483}
484
485template <class TypeTag>
486void
489{
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)) {
493 // Get cell vectors
494 unsigned globalElemIdx = elemMapper.index(elem);
495 auto& value = solUpd_[globalElemIdx];
496 const auto& update = dx[globalElemIdx];
497 assert(value.size() == update.size());
498
499 // Transfer update from dx to solution update container (SolutionVector type)
500 std::ranges::copy(update, value.begin());
501 }
502}
503
504template <class TypeTag>
507getMaxSolutionUpdate(const std::vector<unsigned>& ixCells)
508{
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();
513
514 // Init output
515 Scalar dPMax = 0.0;
516 Scalar dSMax = 0.0;
517 Scalar dRsMax = 0.0;
518 Scalar dRvMax = 0.0;
519
520 // Loop over solution update, get the correct variables and calculate 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]));
526 }
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]));
536 }
537 else if (pvIdx == Indices::compositionSwitchIdx
538 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
539 dRsMax = std::max(dRsMax, std::abs(value[pvIdx]));
540 }
541 else if (pvIdx == Indices::compositionSwitchIdx
542 && value.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
543 dRvMax = std::max(dRvMax, std::abs(value[pvIdx]));
544 }
545 }
546 }
547
548 // Communicate max values
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);
553
554 return { dPMax, dSMax, dRsMax, dRvMax };
555}
556
557template <class TypeTag>
558std::tuple<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
562 const Scalar pvSumLocal,
563 const Scalar numAquiferPvSumLocal,
564 std::vector< Scalar >& R_sum,
565 std::vector< Scalar >& maxCoeff,
566 std::vector< Scalar >& B_avg)
567{
568 return ParentType::convergenceReduction(comm,
569 pvSumLocal,
570 numAquiferPvSumLocal,
571 R_sum,
572 maxCoeff,
573 B_avg);
574}
575
576template <class TypeTag>
577std::pair<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar,
580localConvergenceData(std::vector<Scalar>& R_sum,
581 std::vector<Scalar>& maxCoeff,
582 std::vector<Scalar>& B_avg,
583 std::vector<int>& maxCoeffCell)
584{
585 OPM_TIMEBLOCK(localConvergenceData);
586 Scalar pvSumLocal = 0.0;
587 Scalar numAquiferPvSumLocal = 0.0;
588 const auto& model = this->simulator_.model();
589 const auto& problem = this->simulator_.problem();
590
591 const auto& residual = this->simulator_.model().linearizer().residual();
592
593 ElementContext elemCtx(this->simulator_);
594 const auto& gridView = this->simulator().gridView();
595 IsNumericalAquiferCell isNumericalAquiferCell(gridView.grid());
597 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
598 elemCtx.updatePrimaryStencil(elem);
599 elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
600
601 const unsigned cell_idx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
602 const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
603 const auto& fs = intQuants.fluidState();
604
605 const auto pvValue = problem.referencePorosity(cell_idx, /*timeIdx=*/0) *
606 model.dofTotalVolume(cell_idx);
607 pvSumLocal += pvValue;
608
609 if (isNumericalAquiferCell(elem)) {
610 numAquiferPvSumLocal += pvValue;
611 }
612
613 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
614 B_avg, R_sum, maxCoeff, maxCoeffCell);
615 }
616
617 OPM_END_PARALLEL_TRY_CATCH("NonlinearSystemBlackOilReservoir::localConvergenceData() failed: ", this->grid_.comm());
618
619 // compute local average in terms of global number of elements
620 const int bSize = B_avg.size();
621 for (int i = 0; i < bSize; ++i) {
622 B_avg[i] /= Scalar(this->global_nc_);
623 }
624
625 return {pvSumLocal, numAquiferPvSumLocal};
626}
627
628template <class TypeTag>
631characteriseCnvPvSplit(const std::vector<Scalar>& B_avg, const double dt)
632{
633 OPM_TIMEBLOCK(computeCnvErrorPv);
634
635 // 0: cnv <= tolerance_cnv
636 // 1: tolerance_cnv < cnv <= tolerance_cnv_relaxed
637 // 2: tolerance_cnv_relaxed < cnv
638 constexpr auto numPvGroups = std::vector<double>::size_type{3};
639
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)
644 };
645
646 auto maxCNV = [&B_avg, dt](const auto& residual, const double pvol)
647 {
648 return (dt / pvol) *
649 std::inner_product(residual.begin(), residual.end(),
650 B_avg.begin(), Scalar{0},
651 [](const Scalar m, const auto& x)
652 {
653 using std::abs;
654 return std::max(m, abs(x));
655 }, std::multiplies<>{});
656 };
657
658 auto& [splitPV, cellCntPV] = cnvPvSplit;
659
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();
664
665 const IsNumericalAquiferCell isNumericalAquiferCell(gridView.grid());
666
667 ElementContext elemCtx(this->simulator());
668
669 std::vector<unsigned> ixCells;
670
672 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
673 // Skip cells of numerical Aquifer
674 if (isNumericalAquiferCell(elem)) {
675 continue;
676 }
677
678 elemCtx.updatePrimaryStencil(elem);
679
680 const unsigned cell_idx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
681 const auto pvValue = problem.referencePorosity(cell_idx, /*timeIdx=*/0)
682 * model.dofTotalVolume(cell_idx);
683
684 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
685
686 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
687 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
688
689 splitPV[ix] += static_cast<double>(pvValue);
690 ++cellCntPV[ix];
691
692 // For dP and dS check, we need cell indices of [1] violations
693 if ( ix > 0 &&
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);
697 }
698 }
699
700 OPM_END_PARALLEL_TRY_CATCH("NonlinearSystemBlackOilReservoir::characteriseCnvPvSplit() failed: ",
701 this->grid_.comm());
702
703 this->grid_.comm().sum(splitPV .data(), splitPV .size());
704 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
705
706 return { cnvPvSplit, ixCells };
707}
708
709template <class TypeTag>
712getReservoirConvergence(const double reportTime,
713 const double dt,
714 const int maxIter,
715 std::vector<Scalar>& B_avg,
716 std::vector<Scalar>& residual_norms)
717{
718 OPM_TIMEBLOCK(getReservoirConvergence);
719 using Vector = std::vector<Scalar>;
720
721 const auto& iterCtx = this->simulator_.problem().iterationContext();
722
723 ConvergenceReport report{reportTime};
724
725 const int numComp = numEq;
726
727 Vector R_sum(numComp, Scalar{0});
728 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
729 std::vector<int> maxCoeffCell(numComp, -1);
730
731 const auto [pvSumLocal, numAquiferPvSumLocal] =
732 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
733
734 // compute global sum and max of quantities
735 const auto& [pvSum, numAquiferPvSum] =
736 this->convergenceReduction(this->grid_.comm(),
737 pvSumLocal,
738 numAquiferPvSumLocal,
739 R_sum, maxCoeff, B_avg);
740
741 auto cnvSplitData = this->characteriseCnvPvSplit(B_avg, dt);
742 report.setCnvPoreVolSplit(cnvSplitData.cnvPvSplit,
743 pvSum - numAquiferPvSum);
744
745 // For each iteration, we need to determine whether to use the
746 // relaxed tolerances. To disable the usage of relaxed
747 // tolerances, you can set the relaxed tolerances as the strict
748 // tolerances. If min_strict_mb_iter = -1 (default) we use a
749 // relaxed tolerance for the mass balance for the last
750 // iterations. For positive values we use the relaxed tolerance
751 // after the given number of iterations
752 const bool relax_final_iteration_mb =
753 this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
754
755 const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
756 iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
757
758 const bool use_relaxed_mb = relax_final_iteration_mb
759 || relax_iter_mb;
760
761 // If min_strict_cnv_iter = -1 we use a relaxed tolerance for
762 // the cnv for the last iterations. For positive values we use
763 // the relaxed tolerance after the given number of iterations.
764 // We also use relaxed tolerances for cells with total
765 // pore-volume less than relaxed_max_pv_fraction_. Default
766 // value of relaxed_max_pv_fraction_ is 0.03
767 const bool relax_final_iteration_cnv =
768 this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
769
770 const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
771 iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
772
773 // Note trailing parentheses here, just before the final
774 // semicolon. This is an immediately invoked function
775 // expression which calculates a single boolean value.
776 const auto relax_pv_fraction_cnv =
777 [&report, this, eligible = pvSum - numAquiferPvSum]()
778 {
779 const auto& cnvPvSplit = report.cnvPvSplit().first;
780
781 // [1]: tol < cnv <= relaxed
782 // [2]: relaxed < cnv
783 Scalar cnvPvSum = static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]);
784 return cnvPvSum < this->param_.relaxed_max_pv_fraction_ * eligible &&
785 cnvPvSum > 0.0;
786 }();
787
788 // If tolerances for solution changes are met, we use the
789 // relaxed cnv tolerance. Note that all tolerances > 0.0
790 // must be met to enable use of relaxed cnv tolerance.
791 MaxSolutionUpdateData maxSolUpd;
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);
800 relax_dsol_cnv =
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_));
805 }
806
807 // Determine if relaxed CNV tolerances should be used
808 const bool use_relaxed_cnv = relax_final_iteration_cnv
809 || relax_pv_fraction_cnv
810 || relax_iter_cnv
811 || relax_dsol_cnv;
812
813 // Ensure that CNV convergence criteria is met when max.
814 // solution change tolerances have been fulfilled
815 Scalar tolerance_cnv_relaxed = relax_dsol_cnv ? 1e20 : this->param_.tolerance_cnv_relaxed_;
816
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_;
821
822 // Finish computation
823 std::vector<Scalar> CNV(numComp);
824 std::vector<Scalar> mass_balance_residual(numComp);
825 for (int compIdx = 0; compIdx < numComp; ++compIdx)
826 {
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]);
830 }
831
832 using CR = ConvergenceReport;
833 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
834 const Scalar res[2] = {
835 mass_balance_residual[compIdx], CNV[compIdx],
836 };
837
838 const CR::ReservoirFailure::Type types[2] = {
839 CR::ReservoirFailure::Type::MassBalance,
840 CR::ReservoirFailure::Type::Cnv,
841 };
842
843 Scalar tol[2] = { tol_mb, tol_cnv, };
844 if (has_energy_ && compIdx == contiEnergyEqIdx) {
845 tol[0] = tol_eb;
846 tol[1] = tol_cnv_energy;
847 }
848
849 this->addReservoirConvergenceMetrics(
850 report,
851 compIdx,
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)
858 {
859 if (this->terminal_output_) {
860 OpmLog::debug(message);
861 }
862 });
863 }
864
865 // Compute the Newton convergence per cell.
866 this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
867
868 // Output of residuals.
869 if (this->terminal_output_) {
870 // Only rank 0 does print to std::cout
871 if (iterCtx.isFirstGlobalIteration()) {
872 std::string msg = "Iter";
873 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
874 msg += " MB(";
875 msg += this->compNames_.name(compIdx)[0];
876 msg += ") ";
877 }
878
879 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
880 msg += " CNV(";
881 msg += this->compNames_.name(compIdx)[0];
882 msg += ") ";
883 }
884
885 if (use_dsol_tol) {
886 msg += use_dp_tol ? " DP " : "";
887 msg += use_ds_tol ? " DS " : "";
888 msg += use_drs_tol ? " DRS " : "";
889 msg += use_drv_tol ? " DRV " : "";
890 }
891
892 msg += " MBFLAG";
893 msg += " CNVFLAG";
894
895 OpmLog::debug(msg);
896 }
897
898 std::ostringstream ss;
899 const std::streamsize oprec = ss.precision(3);
900 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
901
902 ss << std::setw(4) << iterCtx.iteration();
903 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
904 ss << std::setw(11) << mass_balance_residual[compIdx];
905 }
906
907 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
908 ss << std::setw(11) << CNV[compIdx];
909 }
910
911 if (use_dsol_tol) {
912 auto print_dsol =
913 [&] (bool use_tol, Scalar dsol) {
914 if (!use_tol) {
915 return;
916 }
917 if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
918 ss << std::string(5, ' ') << "-" << std::string(5, ' ');
919 }
920 else {
921 ss << std::setw(11) << dsol;
922 }
923 };
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);
928 }
929
930 const auto mb_flag = use_relaxed_mb
931 ? DebugFlags::RELAXED
932 : DebugFlags::STRICT;
933
934 const auto cnv_flag = relax_dsol_cnv ?
935 DebugFlags::TUNINGDP
936 : (use_relaxed_cnv
937 ? DebugFlags::RELAXED
938 : DebugFlags::STRICT);
939
940 ss << std::setw(9) << make_string<TypeTag>(mb_flag)
941 << std::setw(9) << make_string<TypeTag>(cnv_flag);
942
943 ss.precision(oprec);
944 ss.flags(oflags);
945
946 OpmLog::debug(ss.str());
947 }
948
949 return report;
950}
951
952template <class TypeTag>
953void
955convergencePerCell(const std::vector<Scalar>& B_avg,
956 const double dt,
957 const double tol_cnv,
958 const double tol_cnv_energy)
959{
960 auto& rst_conv = this->simulator_.problem().eclWriter().mutableOutputModule().getConv();
961 if (!rst_conv.hasConv()) {
962 return;
963 }
964
965 if (this->simulator_.problem().iterationContext().isFirstGlobalIteration()) {
966 rst_conv.prepareConv();
967 }
968
969 const auto& residual = this->simulator_.model().linearizer().residual();
970 const auto& gridView = this->simulator_.gridView();
971 const IsNumericalAquiferCell isNumericalAquiferCell(gridView.grid());
972 ElementContext elemCtx(this->simulator());
973 std::vector<int> convNewt(residual.size(), 0);
975 unsigned idx = 0;
976 const int numComp = B_avg.size();
977 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
978 elemCtx.updatePrimaryStencil(elem);
979
980 const unsigned cell_idx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
981 const auto pvValue = this->simulator_.problem().referencePorosity(cell_idx, /*timeIdx=*/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) {
987 convNewt[idx] = 1;
988 break;
989 }
990 }
991 ++idx;
992 }
993 OPM_END_PARALLEL_TRY_CATCH("NonlinearSystemBlackOilReservoir::convergencePerCell() failed: ",
994 this->grid_.comm());
995 rst_conv.updateNewton(convNewt);
996}
997
998template <class TypeTag>
1002 const int maxIter,
1003 std::vector<Scalar>& residual_norms)
1004{
1005 OPM_TIMEBLOCK(getConvergence);
1006 // Get convergence reports for reservoir and wells.
1007 std::vector<Scalar> B_avg(numEq, 0.0);
1008 auto report = getReservoirConvergence(timer.simulationTimeElapsed(),
1009 timer.currentStepLength(),
1010 maxIter, B_avg, residual_norms);
1011 {
1012 OPM_TIMEBLOCK(getWellConvergence);
1013 report += this->wellModel().getWellConvergence(B_avg,
1014 /*checkWellGroupControlsAndNetwork*/report.converged());
1015 }
1016
1017 conv_monitor_.checkPenaltyCard(report, this->simulator_.problem().iterationContext().iteration());
1018
1019 return report;
1020}
1021
1022template <class TypeTag>
1023std::vector<std::vector<typename NonlinearSystemBlackOilReservoir<TypeTag>::Scalar> >
1025computeFluidInPlace(const std::vector<int>& /*fipnum*/) const
1026{
1027 OPM_TIMEBLOCK(computeFluidInPlace);
1028 // assert(true)
1029 // return an empty vector
1030 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1031 return regionValues;
1032}
1033
1034template <class TypeTag>
1035const SimulatorReport&
1038{
1039 if (!hasNlddSolver()) {
1040 OPM_THROW(std::runtime_error, "Cannot get local reports from a model without NLDD solver");
1041 }
1042 return nlddSolver_->localAccumulatedReports();
1043}
1044
1045template <class TypeTag>
1046const std::vector<SimulatorReport>&
1049{
1050 if (!nlddSolver_)
1051 OPM_THROW(std::runtime_error, "Cannot get domain reports from a model without NLDD solver");
1052 return nlddSolver_->domainAccumulatedReports();
1053}
1054
1055template <class TypeTag>
1056void
1058writeNonlinearIterationsPerCell(const std::filesystem::path& odir) const
1059{
1060 if (hasNlddSolver()) {
1061 nlddSolver_->writeNonlinearIterationsPerCell(odir);
1062 }
1063}
1064
1065template <class TypeTag>
1066void
1068writePartitions(const std::filesystem::path& odir) const
1069{
1070 if (hasNlddSolver()) {
1071 nlddSolver_->writePartitions(odir);
1072 return;
1073 }
1074
1075 const auto& elementMapper = this->simulator().model().elementMapper();
1076 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1077
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())));
1081
1082 std::ofstream pfile {odir / fmt::format("{1:0>{0}}", nDigit, comm.rank())};
1083
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';
1088 }
1089}
1090
1091template <class TypeTag>
1092template<class FluidState, class Residual>
1093void
1095getMaxCoeff(const unsigned cell_idx,
1096 const IntensiveQuantities& intQuants,
1097 const FluidState& fs,
1098 const Residual& modelResid,
1099 const Scalar pvValue,
1100 std::vector<Scalar>& B_avg,
1101 std::vector<Scalar>& R_sum,
1102 std::vector<Scalar>& maxCoeff,
1103 std::vector<int>& maxCoeffCell)
1104{
1105 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1106 {
1107 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1108 continue;
1109 }
1110
1111 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
1112 const unsigned compIdx = FluidSystem::canonicalToActiveCompIdx(sIdx);
1113
1114 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1115 const auto R2 = modelResid[cell_idx][compIdx];
1116
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;
1122 }
1123 }
1124
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);
1132 }
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);
1139 }
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);
1146 }
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);
1153 }
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);
1160 }
1161
1162 if constexpr (has_polymermw_) {
1163 static_assert(has_polymer_);
1164
1165 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1166 // the residual of the polymer molecular equation is scaled down by a 100, since molecular weight
1167 // can be much bigger than 1, and this equation shares the same tolerance with other mass balance equations
1168 // TODO: there should be a more general way to determine the scaling-down coefficient
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);
1173 }
1174
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);
1181 }
1182
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);
1210 }
1211 }
1212}
1213
1214} // namespace Opm
1215
1216#endif // OPM_NONLINEAR_SYSTEM_BLACK_OIL_RESERVOIR_IMPL_HEADER_INCLUDED
#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
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 &param, 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