BlackoilModel.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_BLACKOILMODEL_HEADER_INCLUDED
25#define OPM_BLACKOILMODEL_HEADER_INCLUDED
26
27#include <fmt/format.h>
28
29#include <opm/common/ErrorMacros.hpp>
30#include <opm/common/Exceptions.hpp>
31#include <opm/common/OpmLog/OpmLog.hpp>
32
33#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
34#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
35
48
50
55
56#include <dune/common/timer.hh>
57
58#include <fmt/format.h>
59
60#include <algorithm>
61#include <cassert>
62#include <cmath>
63#include <filesystem>
64#include <fstream>
65#include <functional>
66#include <iomanip>
67#include <ios>
68#include <limits>
69#include <memory>
70#include <numeric>
71#include <sstream>
72#include <tuple>
73#include <utility>
74#include <vector>
75
76namespace Opm::Properties {
77
78namespace TTag {
79
80struct FlowProblem { using InheritsFrom = std::tuple<FlowBaseProblemBlackoil, BlackOilModel>; };
81
82}
83
84// default in flow is to formulate the equations in surface volumes
85template<class TypeTag>
87{ static constexpr bool value = true; };
88
89template<class TypeTag>
90struct UseVolumetricResidual<TypeTag, TTag::FlowProblem>
91{ static constexpr bool value = false; };
92
93template<class TypeTag>
94struct AquiferModel<TypeTag, TTag::FlowProblem>
96
97// disable all extensions supported by black oil model. this should not really be
98// necessary but it makes things a bit more explicit
99template<class TypeTag>
100struct EnablePolymer<TypeTag, TTag::FlowProblem>
101{ static constexpr bool value = false; };
102
103template<class TypeTag>
104struct EnableSolvent<TypeTag, TTag::FlowProblem>
105{ static constexpr bool value = false; };
106
107template<class TypeTag>
108struct EnableTemperature<TypeTag, TTag::FlowProblem>
109{ static constexpr bool value = true; };
110
111template<class TypeTag>
112struct EnableEnergy<TypeTag, TTag::FlowProblem>
113{ static constexpr bool value = false; };
114
115template<class TypeTag>
116struct EnableFoam<TypeTag, TTag::FlowProblem>
117{ static constexpr bool value = false; };
118
119template<class TypeTag>
120struct EnableBrine<TypeTag, TTag::FlowProblem>
121{ static constexpr bool value = false; };
122
123template<class TypeTag>
125{ static constexpr bool value = false; };
126
127template<class TypeTag>
128struct EnableMICP<TypeTag, TTag::FlowProblem>
129{ static constexpr bool value = false; };
130
131template<class TypeTag>
132struct EnableDispersion<TypeTag, TTag::FlowProblem>
133{ static constexpr bool value = false; };
134
135template<class TypeTag>
137{ static constexpr bool value = true; };
138
139template<class TypeTag>
140struct WellModel<TypeTag, TTag::FlowProblem>
142
143template<class TypeTag>
144struct LinearSolverSplice<TypeTag, TTag::FlowProblem>
146
147template<class TypeTag>
148struct EnableDebuggingChecks<TypeTag, TTag::FlowProblem>
149{ static constexpr bool value = false; };
150
151} // namespace Opm::Properties
152
153namespace Opm {
154
161 template <class TypeTag>
163 {
164 public:
165 // --------- Types and enums ---------
179
180 static constexpr int numEq = Indices::numEq;
181 static constexpr int contiSolventEqIdx = Indices::contiSolventEqIdx;
182 static constexpr int contiZfracEqIdx = Indices::contiZfracEqIdx;
183 static constexpr int contiPolymerEqIdx = Indices::contiPolymerEqIdx;
184 static constexpr int contiEnergyEqIdx = Indices::contiEnergyEqIdx;
185 static constexpr int contiPolymerMWEqIdx = Indices::contiPolymerMWEqIdx;
186 static constexpr int contiFoamEqIdx = Indices::contiFoamEqIdx;
187 static constexpr int contiBrineEqIdx = Indices::contiBrineEqIdx;
188 static constexpr int contiMicrobialEqIdx = Indices::contiMicrobialEqIdx;
189 static constexpr int contiOxygenEqIdx = Indices::contiOxygenEqIdx;
190 static constexpr int contiUreaEqIdx = Indices::contiUreaEqIdx;
191 static constexpr int contiBiofilmEqIdx = Indices::contiBiofilmEqIdx;
192 static constexpr int contiCalciteEqIdx = Indices::contiCalciteEqIdx;
193 static constexpr int solventSaturationIdx = Indices::solventSaturationIdx;
194 static constexpr int zFractionIdx = Indices::zFractionIdx;
195 static constexpr int polymerConcentrationIdx = Indices::polymerConcentrationIdx;
196 static constexpr int polymerMoleWeightIdx = Indices::polymerMoleWeightIdx;
197 static constexpr int temperatureIdx = Indices::temperatureIdx;
198 static constexpr int foamConcentrationIdx = Indices::foamConcentrationIdx;
199 static constexpr int saltConcentrationIdx = Indices::saltConcentrationIdx;
200 static constexpr int microbialConcentrationIdx = Indices::microbialConcentrationIdx;
201 static constexpr int oxygenConcentrationIdx = Indices::oxygenConcentrationIdx;
202 static constexpr int ureaConcentrationIdx = Indices::ureaConcentrationIdx;
203 static constexpr int biofilmConcentrationIdx = Indices::biofilmConcentrationIdx;
204 static constexpr int calciteConcentrationIdx = Indices::calciteConcentrationIdx;
205
206 using VectorBlockType = Dune::FieldVector<Scalar, numEq>;
207 using MatrixBlockType = typename SparseMatrixAdapter::MatrixBlock;
208 using Mat = typename SparseMatrixAdapter::IstlMatrix;
209 using BVector = Dune::BlockVector<VectorBlockType>;
210
212
213 // --------- Public methods ---------
214
226 const ModelParameters& param,
227 BlackoilWellModel<TypeTag>& well_model,
228 const bool terminal_output)
230 , grid_(simulator_.vanguard().grid())
232 , param_( param )
233 , well_model_ (well_model)
234 , rst_conv_(simulator_.problem().eclWriter()->collectOnIORank().localIdxToGlobalIdxMapping(),
235 grid_.comm())
236 , terminal_output_ (terminal_output)
238 , dx_old_(simulator_.model().numGridDof())
239 {
240 // compute global sum of number of cells
242 convergence_reports_.reserve(300); // Often insufficient, but avoids frequent moves.
243 // TODO: remember to fix!
244 if (param_.nonlinear_solver_ == "nldd") {
246 OPM_THROW(std::runtime_error, "The --nonlinear-solver=nldd option can only be used with --matrix-add-well-contributions=true");
247 }
248 if (terminal_output) {
249 OpmLog::info("Using Non-Linear Domain Decomposition solver (nldd).");
250 }
251 nlddSolver_ = std::make_unique<BlackoilModelNldd<TypeTag>>(*this);
252 } else if (param_.nonlinear_solver_ == "newton") {
253 if (terminal_output) {
254 OpmLog::info("Using Newton nonlinear solver.");
255 }
256 } else {
257 OPM_THROW(std::runtime_error, "Unknown nonlinear solver option: " + param_.nonlinear_solver_);
258 }
259 }
260
261
262 bool isParallel() const
263 { return grid_.comm().size() > 1; }
264
265
266 const EclipseState& eclState() const
267 { return simulator_.vanguard().eclState(); }
268
269
273 {
275 Dune::Timer perfTimer;
276 perfTimer.start();
277 // update the solution variables in the model
278 if ( timer.lastStepFailed() ) {
279 simulator_.model().updateFailed();
280 } else {
281 simulator_.model().advanceTimeLevel();
282 }
283
284 // Set the timestep size, episode index, and non-linear iteration index
285 // for the model explicitly. The model needs to know the report step/episode index
286 // because of timing dependent data despite the fact that flow uses its
287 // own time stepper. (The length of the episode does not matter, though.)
288 simulator_.setTime(timer.simulationTimeElapsed());
289 simulator_.setTimeStepSize(timer.currentStepLength());
290 simulator_.model().newtonMethod().setIterationIndex(0);
291
292 simulator_.problem().beginTimeStep();
293
294 unsigned numDof = simulator_.model().numGridDof();
295 wasSwitched_.resize(numDof);
296 std::fill(wasSwitched_.begin(), wasSwitched_.end(), false);
297
299 OpmLog::error("Equation scaling not supported");
300 //updateEquationsScaling();
301 }
302
303 if (nlddSolver_) {
304 nlddSolver_->prepareStep();
305 }
306
307 report.pre_post_time += perfTimer.stop();
308
309 auto getIdx = [](unsigned phaseIdx) -> int
310 {
311 if (FluidSystem::phaseIsActive(phaseIdx)) {
312 const unsigned sIdx = FluidSystem::solventComponentIndex(phaseIdx);
313 return Indices::canonicalToActiveComponentIndex(sIdx);
314 }
315
316 return -1;
317 };
318 const auto& schedule = simulator_.vanguard().schedule();
319 rst_conv_.init(simulator_.vanguard().globalNumCells(),
320 schedule[timer.reportStepNum()].rst_config(),
321 {getIdx(FluidSystem::oilPhaseIdx),
322 getIdx(FluidSystem::gasPhaseIdx),
323 getIdx(FluidSystem::waterPhaseIdx),
324 contiPolymerEqIdx,
325 contiBrineEqIdx,
326 contiSolventEqIdx});
327
328 return report;
329 }
330
331
333 const int iteration,
334 const int minIter,
335 const int maxIter,
336 const SimulatorTimerInterface& timer)
337 {
338 // ----------- Set up reports and timer -----------
340 Dune::Timer perfTimer;
341
342 perfTimer.start();
343 report.total_linearizations = 1;
344
345 // ----------- Assemble -----------
346 try {
347 report += assembleReservoir(timer, iteration);
348 report.assemble_time += perfTimer.stop();
349 }
350 catch (...) {
351 report.assemble_time += perfTimer.stop();
352 failureReport_ += report;
353 throw; // continue throwing the stick
354 }
355
356 // ----------- Check if converged -----------
357 std::vector<Scalar> residual_norms;
358 perfTimer.reset();
359 perfTimer.start();
360 // the step is not considered converged until at least minIter iterations is done
361 {
362 auto convrep = getConvergence(timer, iteration, maxIter, residual_norms);
363 report.converged = convrep.converged() && iteration >= minIter;
364 ConvergenceReport::Severity severity = convrep.severityOfWorstFailure();
365 convergence_reports_.back().report.push_back(std::move(convrep));
366
367 // Throw if any NaN or too large residual found.
369 OPM_THROW_PROBLEM(NumericalProblem, "NaN residual found!");
370 } else if (severity == ConvergenceReport::Severity::TooLarge) {
371 OPM_THROW_NOLOG(NumericalProblem, "Too large residual found!");
372 }
373 }
374 report.update_time += perfTimer.stop();
375 residual_norms_history_.push_back(residual_norms);
376 }
377
378
386 template <class NonlinearSolverType>
388 const SimulatorTimerInterface& timer,
389 NonlinearSolverType& nonlinear_solver)
390 {
391 if (iteration == 0) {
392 // For each iteration we store in a vector the norms of the residual of
393 // the mass balance for each active phase, the well flux and the well equations.
396 dx_old_ = 0.0;
397 convergence_reports_.push_back({timer.reportStepNum(), timer.currentStepNum(), {}});
398 convergence_reports_.back().report.reserve(11);
399 }
400
402 if ((this->param_.nonlinear_solver_ != "nldd") ||
403 (iteration < this->param_.nldd_num_initial_newton_iter_))
404 {
405 result = this->nonlinearIterationNewton(iteration, timer, nonlinear_solver);
406 }
407 else {
408 result = this->nlddSolver_->nonlinearIterationNldd(iteration, timer, nonlinear_solver);
409 }
410
411 rst_conv_.update(simulator_.model().linearizer().residual());
412
413 return result;
414 }
415
416
417 template <class NonlinearSolverType>
419 const SimulatorTimerInterface& timer,
420 NonlinearSolverType& nonlinear_solver)
421 {
422
423 // ----------- Set up reports and timer -----------
425 Dune::Timer perfTimer;
426
427 this->initialLinearization(report, iteration, nonlinear_solver.minIter(), nonlinear_solver.maxIter(), timer);
428
429 // ----------- If not converged, solve linear system and do Newton update -----------
430 if (!report.converged) {
431 perfTimer.reset();
432 perfTimer.start();
433 report.total_newton_iterations = 1;
434
435 // Compute the nonlinear update.
436 unsigned nc = simulator_.model().numGridDof();
437 BVector x(nc);
438
439 // Solve the linear system.
440 linear_solve_setup_time_ = 0.0;
441 try {
442 // Apply the Schur complement of the well model to
443 // the reservoir linearized equations.
444 // Note that linearize may throw for MSwells.
445 wellModel().linearize(simulator().model().linearizer().jacobian(),
446 simulator().model().linearizer().residual());
447
448 // ---- Solve linear system ----
450
451 report.linear_solve_setup_time += linear_solve_setup_time_;
452 report.linear_solve_time += perfTimer.stop();
454 }
455 catch (...) {
456 report.linear_solve_setup_time += linear_solve_setup_time_;
457 report.linear_solve_time += perfTimer.stop();
459
460 failureReport_ += report;
461 throw; // re-throw up
462 }
463
464 perfTimer.reset();
465 perfTimer.start();
466
467 // handling well state update before oscillation treatment is a decision based
468 // on observation to avoid some big performance degeneration under some circumstances.
469 // there is no theorectical explanation which way is better for sure.
470 wellModel().postSolve(x);
471
473 // Stabilize the nonlinear update.
474 bool isOscillate = false;
475 bool isStagnate = false;
476 nonlinear_solver.detectOscillations(residual_norms_history_, residual_norms_history_.size() - 1, isOscillate, isStagnate);
477 if (isOscillate) {
478 current_relaxation_ -= nonlinear_solver.relaxIncrement();
479 current_relaxation_ = std::max(current_relaxation_, nonlinear_solver.relaxMax());
480 if (terminalOutputEnabled()) {
481 std::string msg = " Oscillating behavior detected: Relaxation set to "
483 OpmLog::info(msg);
484 }
485 }
486 nonlinear_solver.stabilizeNonlinearUpdate(x, dx_old_, current_relaxation_);
487 }
488
489 // ---- Newton update ----
490 // Apply the update, with considering model-dependent limitations and
491 // chopping of the update.
493
494 report.update_time += perfTimer.stop();
495 }
496
497 return report;
498 }
499
500
505 {
507 Dune::Timer perfTimer;
508 perfTimer.start();
509 simulator_.problem().endTimeStep();
510 simulator_.problem().setConvData(rst_conv_.getData());
511 report.pre_post_time += perfTimer.stop();
512 return report;
513 }
514
517 const int iterationIdx)
518 {
519 // -------- Mass balance equations --------
520 simulator_.model().newtonMethod().setIterationIndex(iterationIdx);
521 simulator_.problem().beginIteration();
522 simulator_.model().linearizer().linearizeDomain();
523 simulator_.problem().endIteration();
524 return wellModel().lastReport();
525 }
526
527 // compute the "relative" change of the solution between time steps
529 {
530 Scalar resultDelta = 0.0;
531 Scalar resultDenom = 0.0;
532
533 const auto& elemMapper = simulator_.model().elementMapper();
534 const auto& gridView = simulator_.gridView();
535 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
536 unsigned globalElemIdx = elemMapper.index(elem);
537 const auto& priVarsNew = simulator_.model().solution(/*timeIdx=*/0)[globalElemIdx];
538
539 Scalar pressureNew;
540 pressureNew = priVarsNew[Indices::pressureSwitchIdx];
541
542 Scalar saturationsNew[FluidSystem::numPhases] = { 0.0 };
543 Scalar oilSaturationNew = 1.0;
544 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx) &&
545 FluidSystem::numActivePhases() > 1 &&
546 priVarsNew.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
547 saturationsNew[FluidSystem::waterPhaseIdx] = priVarsNew[Indices::waterSwitchIdx];
548 oilSaturationNew -= saturationsNew[FluidSystem::waterPhaseIdx];
549 }
550
551 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx) &&
552 FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) &&
553 priVarsNew.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg) {
554 assert(Indices::compositionSwitchIdx >= 0 );
555 saturationsNew[FluidSystem::gasPhaseIdx] = priVarsNew[Indices::compositionSwitchIdx];
556 oilSaturationNew -= saturationsNew[FluidSystem::gasPhaseIdx];
557 }
558
559 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
560 saturationsNew[FluidSystem::oilPhaseIdx] = oilSaturationNew;
561 }
562
563 const auto& priVarsOld = simulator_.model().solution(/*timeIdx=*/1)[globalElemIdx];
564
565 Scalar pressureOld;
566 pressureOld = priVarsOld[Indices::pressureSwitchIdx];
567
568 Scalar saturationsOld[FluidSystem::numPhases] = { 0.0 };
569 Scalar oilSaturationOld = 1.0;
570
571 // NB fix me! adding pressures changes to satutation changes does not make sense
572 Scalar tmp = pressureNew - pressureOld;
573 resultDelta += tmp*tmp;
574 resultDenom += pressureNew*pressureNew;
575
576 if (FluidSystem::numActivePhases() > 1) {
577 if (priVarsOld.primaryVarsMeaningWater() == PrimaryVariables::WaterMeaning::Sw) {
578 saturationsOld[FluidSystem::waterPhaseIdx] = priVarsOld[Indices::waterSwitchIdx];
579 oilSaturationOld -= saturationsOld[FluidSystem::waterPhaseIdx];
580 }
581
582 if (priVarsOld.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Sg)
583 {
584 assert(Indices::compositionSwitchIdx >= 0 );
585 saturationsOld[FluidSystem::gasPhaseIdx] = priVarsOld[Indices::compositionSwitchIdx];
586 oilSaturationOld -= saturationsOld[FluidSystem::gasPhaseIdx];
587 }
588
589 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
590 saturationsOld[FluidSystem::oilPhaseIdx] = oilSaturationOld;
591 }
592 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++ phaseIdx) {
593 Scalar tmpSat = saturationsNew[phaseIdx] - saturationsOld[phaseIdx];
594 resultDelta += tmpSat*tmpSat;
595 resultDenom += saturationsNew[phaseIdx]*saturationsNew[phaseIdx];
596 assert(std::isfinite(resultDelta));
597 assert(std::isfinite(resultDenom));
598 }
599 }
600 }
601
602 resultDelta = gridView.comm().sum(resultDelta);
603 resultDenom = gridView.comm().sum(resultDenom);
604
605 if (resultDenom > 0.0)
606 return resultDelta/resultDenom;
607 return 0.0;
608 }
609
610
613 {
614 return simulator_.model().newtonMethod().linearSolver().iterations ();
615 }
616
617
618 // Obtain reference to linear solver setup time
620 {
621 return linear_solve_setup_time_;
622 }
623
624
628 {
629 auto& jacobian = simulator_.model().linearizer().jacobian().istlMatrix();
630 auto& residual = simulator_.model().linearizer().residual();
631 auto& linSolver = simulator_.model().newtonMethod().linearSolver();
632
633 const int numSolvers = linSolver.numAvailableSolvers();
634 if ((numSolvers > 1) && (linSolver.getSolveCount() % 100 == 0)) {
635
636 if ( terminal_output_ ) {
637 OpmLog::debug("\nRunning speed test for comparing available linear solvers.");
638 }
639
640 Dune::Timer perfTimer;
641 std::vector<double> times(numSolvers);
642 std::vector<double> setupTimes(numSolvers);
643
644 x = 0.0;
645 std::vector<BVector> x_trial(numSolvers, x);
646 for (int solver = 0; solver < numSolvers; ++solver) {
647 BVector x0(x);
648 linSolver.setActiveSolver(solver);
649 perfTimer.start();
650 linSolver.prepare(jacobian, residual);
651 setupTimes[solver] = perfTimer.stop();
652 perfTimer.reset();
653 linSolver.setResidual(residual);
654 perfTimer.start();
655 linSolver.solve(x_trial[solver]);
656 times[solver] = perfTimer.stop();
657 perfTimer.reset();
658 if (terminal_output_) {
659 OpmLog::debug(fmt::format("Solver time {}: {}", solver, times[solver]));
660 }
661 }
662
663 int fastest_solver = std::min_element(times.begin(), times.end()) - times.begin();
664 // Use timing on rank 0 to determine fastest, must be consistent across ranks.
665 grid_.comm().broadcast(&fastest_solver, 1, 0);
666 linear_solve_setup_time_ = setupTimes[fastest_solver];
667 x = x_trial[fastest_solver];
668 linSolver.setActiveSolver(fastest_solver);
669 } else {
670 // set initial guess
671 x = 0.0;
672
673 Dune::Timer perfTimer;
674 perfTimer.start();
675 linSolver.prepare(jacobian, residual);
676 linear_solve_setup_time_ = perfTimer.stop();
677 linSolver.setResidual(residual);
678 // actually, the error needs to be calculated after setResidual in order to
679 // account for parallelization properly. since the residual of ECFV
680 // discretizations does not need to be synchronized across processes to be
681 // consistent, this is not relevant for OPM-flow...
682 linSolver.solve(x);
683 }
684 }
685
686
688 void updateSolution(const BVector& dx)
689 {
690 OPM_TIMEBLOCK(updateSolution);
691 auto& newtonMethod = simulator_.model().newtonMethod();
692 SolutionVector& solution = simulator_.model().solution(/*timeIdx=*/0);
693
694 newtonMethod.update_(/*nextSolution=*/solution,
695 /*curSolution=*/solution,
696 /*update=*/dx,
697 /*resid=*/dx); // the update routines of the black
698 // oil model do not care about the
699 // residual
700
701 // if the solution is updated, the intensive quantities need to be recalculated
702 {
703 OPM_TIMEBLOCK(invalidateAndUpdateIntensiveQuantities);
704 simulator_.model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
705 simulator_.problem().eclWriter()->mutableOutputModule().invalidateLocalData();
706 }
707 }
708
711 {
712 return terminal_output_;
713 }
714
715 std::tuple<Scalar,Scalar> convergenceReduction(Parallel::Communication comm,
716 const Scalar pvSumLocal,
717 const Scalar numAquiferPvSumLocal,
718 std::vector< Scalar >& R_sum,
719 std::vector< Scalar >& maxCoeff,
720 std::vector< Scalar >& B_avg)
721 {
722 OPM_TIMEBLOCK(convergenceReduction);
723 // Compute total pore volume (use only owned entries)
724 Scalar pvSum = pvSumLocal;
725 Scalar numAquiferPvSum = numAquiferPvSumLocal;
726
727 if( comm.size() > 1 )
728 {
729 // global reduction
730 std::vector< Scalar > sumBuffer;
731 std::vector< Scalar > maxBuffer;
732 const int numComp = B_avg.size();
733 sumBuffer.reserve( 2*numComp + 2 ); // +2 for (numAquifer)pvSum
734 maxBuffer.reserve( numComp );
735 for( int compIdx = 0; compIdx < numComp; ++compIdx )
736 {
737 sumBuffer.push_back( B_avg[ compIdx ] );
738 sumBuffer.push_back( R_sum[ compIdx ] );
739 maxBuffer.push_back( maxCoeff[ compIdx ] );
740 }
741
742 // Compute total pore volume
743 sumBuffer.push_back( pvSum );
744 sumBuffer.push_back( numAquiferPvSum );
745
746 // compute global sum
747 comm.sum( sumBuffer.data(), sumBuffer.size() );
748
749 // compute global max
750 comm.max( maxBuffer.data(), maxBuffer.size() );
751
752 // restore values to local variables
753 for( int compIdx = 0, buffIdx = 0; compIdx < numComp; ++compIdx, ++buffIdx )
754 {
755 B_avg[ compIdx ] = sumBuffer[ buffIdx ];
756 ++buffIdx;
757
758 R_sum[ compIdx ] = sumBuffer[ buffIdx ];
759 }
760
761 for( int compIdx = 0; compIdx < numComp; ++compIdx )
762 {
763 maxCoeff[ compIdx ] = maxBuffer[ compIdx ];
764 }
765
766 // restore global pore volume
767 pvSum = sumBuffer[sumBuffer.size()-2];
768 numAquiferPvSum = sumBuffer.back();
769 }
770
771 // return global pore volume
772 return {pvSum, numAquiferPvSum};
773 }
774
778 std::pair<Scalar,Scalar> localConvergenceData(std::vector<Scalar>& R_sum,
779 std::vector<Scalar>& maxCoeff,
780 std::vector<Scalar>& B_avg,
781 std::vector<int>& maxCoeffCell)
782 {
783 OPM_TIMEBLOCK(localConvergenceData);
784 Scalar pvSumLocal = 0.0;
785 Scalar numAquiferPvSumLocal = 0.0;
786 const auto& model = simulator_.model();
787 const auto& problem = simulator_.problem();
788
789 const auto& residual = simulator_.model().linearizer().residual();
790
791 ElementContext elemCtx(simulator_);
792 const auto& gridView = simulator().gridView();
793 IsNumericalAquiferCell isNumericalAquiferCell(gridView.grid());
795 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
796 elemCtx.updatePrimaryStencil(elem);
797 elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
798
799 const unsigned cell_idx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
800 const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
801 const auto& fs = intQuants.fluidState();
802
803 const auto pvValue = problem.referencePorosity(cell_idx, /*timeIdx=*/0) *
804 model.dofTotalVolume(cell_idx);
805 pvSumLocal += pvValue;
806
807 if (isNumericalAquiferCell(elem))
808 {
809 numAquiferPvSumLocal += pvValue;
810 }
811
812 this->getMaxCoeff(cell_idx, intQuants, fs, residual, pvValue,
813 B_avg, R_sum, maxCoeff, maxCoeffCell);
814 }
815
816 OPM_END_PARALLEL_TRY_CATCH("BlackoilModel::localConvergenceData() failed: ", grid_.comm());
817
818 // compute local average in terms of global number of elements
819 const int bSize = B_avg.size();
820 for ( int i = 0; i<bSize; ++i )
821 {
822 B_avg[ i ] /= Scalar( global_nc_ );
823 }
824
825 return {pvSumLocal, numAquiferPvSumLocal};
826 }
827
828
832 std::pair<std::vector<double>, std::vector<int>>
833 characteriseCnvPvSplit(const std::vector<Scalar>& B_avg, const double dt)
834 {
835 OPM_TIMEBLOCK(computeCnvErrorPv);
836
837 // 0: cnv <= tolerance_cnv
838 // 1: tolerance_cnv < cnv <= tolerance_cnv_relaxed
839 // 2: tolerance_cnv_relaxed < cnv
840 constexpr auto numPvGroups = std::vector<double>::size_type{3};
841
842 auto cnvPvSplit = std::pair<std::vector<double>, std::vector<int>> {
843 std::piecewise_construct,
844 std::forward_as_tuple(numPvGroups),
845 std::forward_as_tuple(numPvGroups)
846 };
847
848 auto maxCNV = [&B_avg, dt](const auto& residual, const double pvol)
849 {
850 return (dt / pvol) *
851 std::inner_product(residual.begin(), residual.end(),
852 B_avg.begin(), Scalar{0},
853 [](const Scalar m, const auto& x)
854 {
855 using std::abs;
856 return std::max(m, abs(x));
857 }, std::multiplies<>{});
858 };
859
860 auto& [splitPV, cellCntPV] = cnvPvSplit;
861
862 const auto& model = this->simulator().model();
863 const auto& problem = this->simulator().problem();
864 const auto& residual = model.linearizer().residual();
865 const auto& gridView = this->simulator().gridView();
866
867 const IsNumericalAquiferCell isNumericalAquiferCell(gridView.grid());
868
869 ElementContext elemCtx(this->simulator());
870
872 for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
873 // Skip cells of numerical Aquifer
874 if (isNumericalAquiferCell(elem)) {
875 continue;
876 }
877
878 elemCtx.updatePrimaryStencil(elem);
879
880 const unsigned cell_idx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
881 const auto pvValue = problem.referencePorosity(cell_idx, /*timeIdx=*/0)
882 * model.dofTotalVolume(cell_idx);
883
884 const auto maxCnv = maxCNV(residual[cell_idx], pvValue);
885
886 const auto ix = (maxCnv > this->param_.tolerance_cnv_)
887 + (maxCnv > this->param_.tolerance_cnv_relaxed_);
888
889 splitPV[ix] += static_cast<double>(pvValue);
890 ++cellCntPV[ix];
891 }
892
893 OPM_END_PARALLEL_TRY_CATCH("BlackoilModel::characteriseCnvPvSplit() failed: ",
894 this->grid_.comm());
895
896 this->grid_.comm().sum(splitPV .data(), splitPV .size());
897 this->grid_.comm().sum(cellCntPV.data(), cellCntPV.size());
898
899 return cnvPvSplit;
900 }
901
902
903 void updateTUNING(const Tuning& tuning)
904 {
905 this->param_.tolerance_mb_ = tuning.XXXMBE;
906
907 if (terminal_output_) {
908 OpmLog::debug(fmt::format("Setting BlackoilModel mass "
909 "balance limit (XXXMBE) to {:.2e}",
910 tuning.XXXMBE));
911 }
912 }
913
914
916 const double dt,
917 const int iteration,
918 const int maxIter,
919 std::vector<Scalar>& B_avg,
920 std::vector<Scalar>& residual_norms)
921 {
922 OPM_TIMEBLOCK(getReservoirConvergence);
923 using Vector = std::vector<Scalar>;
924
925 ConvergenceReport report{reportTime};
926
927 const int numComp = numEq;
928
929 Vector R_sum(numComp, Scalar{0});
930 Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest());
931 std::vector<int> maxCoeffCell(numComp, -1);
932
933 const auto [pvSumLocal, numAquiferPvSumLocal] =
934 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
935
936 // compute global sum and max of quantities
937 const auto& [pvSum, numAquiferPvSum] =
938 this->convergenceReduction(this->grid_.comm(),
939 pvSumLocal,
940 numAquiferPvSumLocal,
941 R_sum, maxCoeff, B_avg);
942
943 report.setCnvPoreVolSplit(this->characteriseCnvPvSplit(B_avg, dt),
944 pvSum - numAquiferPvSum);
945
946 // For each iteration, we need to determine whether to use the
947 // relaxed tolerances. To disable the usage of relaxed
948 // tolerances, you can set the relaxed tolerances as the strict
949 // tolerances. If min_strict_mb_iter = -1 (default) we use a
950 // relaxed tolerance for the mass balance for the last
951 // iterations. For positive values we use the relaxed tolerance
952 // after the given number of iterations
953 const bool relax_final_iteration_mb =
954 (this->param_.min_strict_mb_iter_ < 0)
955 && (iteration == maxIter);
956
957 const bool use_relaxed_mb = relax_final_iteration_mb ||
958 ((this->param_.min_strict_mb_iter_ >= 0) &&
959 (iteration >= this->param_.min_strict_mb_iter_));
960
961 // If min_strict_cnv_iter = -1 we use a relaxed tolerance for
962 // the cnv for the last iterations. For positive values we use
963 // the relaxed tolerance after the given number of iterations.
964 // We also use relaxed tolerances for cells with total
965 // pore-volume less than relaxed_max_pv_fraction_. Default
966 // value of relaxed_max_pv_fraction_ is 0.03
967 const bool relax_final_iteration_cnv =
968 (this->param_.min_strict_cnv_iter_ < 0)
969 && (iteration == maxIter);
970
971 const bool relax_iter_cnv = (this->param_.min_strict_cnv_iter_ >= 0)
972 && (iteration >= this->param_.min_strict_cnv_iter_);
973
974 // Note trailing parentheses here, just before the final
975 // semicolon. This is an immediately invoked function
976 // expression which calculates a single boolean value.
977 const auto relax_pv_fraction_cnv =
978 [&report, this, eligible = pvSum - numAquiferPvSum]()
979 {
980 const auto& cnvPvSplit = report.cnvPvSplit().first;
981
982 // [1]: tol < cnv <= relaxed
983 // [2]: relaxed < cnv
984 return static_cast<Scalar>(cnvPvSplit[1] + cnvPvSplit[2]) <
985 this->param_.relaxed_max_pv_fraction_ * eligible;
986 }();
987
988 const bool use_relaxed_cnv = relax_final_iteration_cnv
989 || relax_pv_fraction_cnv
990 || relax_iter_cnv;
991
992 if ((relax_final_iteration_mb || relax_final_iteration_cnv) &&
993 this->terminal_output_)
994 {
995 std::string message =
996 "Number of newton iterations reached its maximum "
997 "try to continue with relaxed tolerances:";
998
999 if (relax_final_iteration_mb) {
1000 message += fmt::format(" MB: {:.1e}", param_.tolerance_mb_relaxed_);
1001 }
1002
1003 if (relax_final_iteration_cnv) {
1004 message += fmt::format(" CNV: {:.1e}", param_.tolerance_cnv_relaxed_);
1005 }
1006
1007 OpmLog::debug(message);
1008 }
1009
1010 const auto tol_cnv = use_relaxed_cnv ? param_.tolerance_cnv_relaxed_ : param_.tolerance_cnv_;
1011 const auto tol_mb = use_relaxed_mb ? param_.tolerance_mb_relaxed_ : param_.tolerance_mb_;
1012 const auto tol_cnv_energy = use_relaxed_cnv ? param_.tolerance_cnv_energy_relaxed_ : param_.tolerance_cnv_energy_;
1013 const auto tol_eb = use_relaxed_mb ? param_.tolerance_energy_balance_relaxed_ : param_.tolerance_energy_balance_;
1014
1015 // Finish computation
1016 std::vector<Scalar> CNV(numComp);
1017 std::vector<Scalar> mass_balance_residual(numComp);
1018 for (int compIdx = 0; compIdx < numComp; ++compIdx)
1019 {
1020 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
1021 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
1022 residual_norms.push_back(CNV[compIdx]);
1023 }
1024
1025 using CR = ConvergenceReport;
1026 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1027 const Scalar res[2] = {
1028 mass_balance_residual[compIdx], CNV[compIdx],
1029 };
1030
1031 const CR::ReservoirFailure::Type types[2] = {
1032 CR::ReservoirFailure::Type::MassBalance,
1033 CR::ReservoirFailure::Type::Cnv,
1034 };
1035
1036 Scalar tol[2] = { tol_mb, tol_cnv, };
1037 if (has_energy_ && compIdx == contiEnergyEqIdx) {
1038 tol[0] = tol_eb;
1039 tol[1] = tol_cnv_energy;
1040 }
1041
1042 for (int ii : {0, 1}) {
1043 if (std::isnan(res[ii])) {
1044 report.setReservoirFailed({types[ii], CR::Severity::NotANumber, compIdx});
1045 if (this->terminal_output_) {
1046 OpmLog::debug("NaN residual for " + this->compNames_.name(compIdx) + " equation.");
1047 }
1048 }
1049 else if (res[ii] > maxResidualAllowed()) {
1050 report.setReservoirFailed({types[ii], CR::Severity::TooLarge, compIdx});
1051 if (this->terminal_output_) {
1052 OpmLog::debug("Too large residual for " + this->compNames_.name(compIdx) + " equation.");
1053 }
1054 }
1055 else if (res[ii] < 0.0) {
1056 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
1057 if (this->terminal_output_) {
1058 OpmLog::debug("Negative residual for " + this->compNames_.name(compIdx) + " equation.");
1059 }
1060 }
1061 else if (res[ii] > tol[ii]) {
1062 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
1063 }
1064
1065 report.setReservoirConvergenceMetric(types[ii], compIdx, res[ii]);
1066 }
1067 }
1068
1069 // Output of residuals.
1070 if (this->terminal_output_) {
1071 // Only rank 0 does print to std::cout
1072 if (iteration == 0) {
1073 std::string msg = "Iter";
1074 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1075 msg += " MB(";
1076 msg += this->compNames_.name(compIdx)[0];
1077 msg += ") ";
1078 }
1079
1080 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1081 msg += " CNV(";
1082 msg += this->compNames_.name(compIdx)[0];
1083 msg += ") ";
1084 }
1085
1086 OpmLog::debug(msg);
1087 }
1088
1089 std::ostringstream ss;
1090 const std::streamsize oprec = ss.precision(3);
1091 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
1092
1093 ss << std::setw(4) << iteration;
1094 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1095 ss << std::setw(11) << mass_balance_residual[compIdx];
1096 }
1097
1098 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1099 ss << std::setw(11) << CNV[compIdx];
1100 }
1101
1102 ss.precision(oprec);
1103 ss.flags(oflags);
1104
1105 OpmLog::debug(ss.str());
1106 }
1107
1108 return report;
1109 }
1110
1118 const int iteration,
1119 const int maxIter,
1120 std::vector<Scalar>& residual_norms)
1121 {
1122 OPM_TIMEBLOCK(getConvergence);
1123 // Get convergence reports for reservoir and wells.
1124 std::vector<Scalar> B_avg(numEq, 0.0);
1125 auto report = getReservoirConvergence(timer.simulationTimeElapsed(),
1126 timer.currentStepLength(),
1127 iteration, maxIter, B_avg, residual_norms);
1128 {
1129 OPM_TIMEBLOCK(getWellConvergence);
1130 report += wellModel().getWellConvergence(B_avg, /*checkWellGroupControls*/report.converged());
1131 }
1132 return report;
1133 }
1134
1135
1137 int numPhases() const
1138 {
1139 return phaseUsage_.num_phases;
1140 }
1141
1143 template<class T>
1144 std::vector<std::vector<Scalar> >
1145 computeFluidInPlace(const T&, const std::vector<int>& fipnum) const
1146 {
1147 return computeFluidInPlace(fipnum);
1148 }
1149
1151 std::vector<std::vector<Scalar> >
1152 computeFluidInPlace(const std::vector<int>& /*fipnum*/) const
1153 {
1154 OPM_TIMEBLOCK(computeFluidInPlace);
1155 //assert(true)
1156 //return an empty vector
1157 std::vector<std::vector<Scalar> > regionValues(0, std::vector<Scalar>(0,0.0));
1158 return regionValues;
1159 }
1160
1161 const Simulator& simulator() const
1162 { return simulator_; }
1163
1165 { return simulator_; }
1166
1169 { return failureReport_; }
1170
1173 {
1174 return nlddSolver_ ? nlddSolver_->localAccumulatedReports()
1176 }
1177
1178 const std::vector<StepReport>& stepReports() const
1179 {
1180 return convergence_reports_;
1181 }
1182
1183 void writePartitions(const std::filesystem::path& odir) const
1184 {
1185 if (this->nlddSolver_ != nullptr) {
1186 this->nlddSolver_->writePartitions(odir);
1187 return;
1188 }
1189
1190 const auto& elementMapper = this->simulator().model().elementMapper();
1191 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1192
1193 const auto& grid = this->simulator().vanguard().grid();
1194 const auto& comm = grid.comm();
1195 const auto nDigit = 1 + static_cast<int>(std::floor(std::log10(comm.size())));
1196
1197 std::ofstream pfile { odir / fmt::format("{1:0>{0}}", nDigit, comm.rank()) };
1198
1199 for (const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1200 pfile << comm.rank() << ' '
1201 << cartMapper.cartesianIndex(elementMapper.index(cell)) << ' '
1202 << comm.rank() << '\n';
1203 }
1204 }
1205
1206 const std::vector<std::vector<int>>& getConvCells() const
1207 { return rst_conv_.getData(); }
1208
1209 protected:
1210 // --------- Data members ---------
1211
1213 const Grid& grid_;
1215 static constexpr bool has_solvent_ = getPropValue<TypeTag, Properties::EnableSolvent>();
1216 static constexpr bool has_extbo_ = getPropValue<TypeTag, Properties::EnableExtbo>();
1217 static constexpr bool has_polymer_ = getPropValue<TypeTag, Properties::EnablePolymer>();
1218 static constexpr bool has_polymermw_ = getPropValue<TypeTag, Properties::EnablePolymerMW>();
1219 static constexpr bool has_energy_ = getPropValue<TypeTag, Properties::EnableEnergy>();
1220 static constexpr bool has_foam_ = getPropValue<TypeTag, Properties::EnableFoam>();
1221 static constexpr bool has_brine_ = getPropValue<TypeTag, Properties::EnableBrine>();
1222 static constexpr bool has_micp_ = getPropValue<TypeTag, Properties::EnableMICP>();
1223
1226
1227 // Well Model
1229
1231
1235 long int global_nc_;
1236
1237 std::vector<std::vector<Scalar>> residual_norms_history_;
1240
1241 std::vector<StepReport> convergence_reports_;
1243
1244 std::unique_ptr<BlackoilModelNldd<TypeTag>> nlddSolver_;
1245
1246 public:
1250
1252 wellModel() const { return well_model_; }
1253
1255 {
1256 simulator_.problem().beginEpisode();
1257 }
1258
1260 {
1261 simulator_.problem().endEpisode();
1262 }
1263
1264 template<class FluidState, class Residual>
1265 void getMaxCoeff(const unsigned cell_idx,
1266 const IntensiveQuantities& intQuants,
1267 const FluidState& fs,
1268 const Residual& modelResid,
1269 const Scalar pvValue,
1270 std::vector<Scalar>& B_avg,
1271 std::vector<Scalar>& R_sum,
1272 std::vector<Scalar>& maxCoeff,
1273 std::vector<int>& maxCoeffCell)
1274 {
1275 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1276 {
1277 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1278 continue;
1279 }
1280
1281 const unsigned compIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
1282
1283 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1284 const auto R2 = modelResid[cell_idx][compIdx];
1285
1286 R_sum[compIdx] += R2;
1287 const Scalar Rval = std::abs(R2) / pvValue;
1288 if (Rval > maxCoeff[compIdx]) {
1289 maxCoeff[compIdx] = Rval;
1290 maxCoeffCell[compIdx] = cell_idx;
1291 }
1292 }
1293
1294 if constexpr (has_solvent_) {
1295 B_avg[contiSolventEqIdx] += 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1296 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1297 R_sum[contiSolventEqIdx] += R2;
1298 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1299 std::abs(R2) / pvValue);
1300 }
1301 if constexpr (has_extbo_) {
1302 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1303 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1304 R_sum[ contiZfracEqIdx ] += R2;
1305 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1306 std::abs(R2) / pvValue);
1307 }
1308 if constexpr (has_polymer_) {
1309 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1310 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1311 R_sum[contiPolymerEqIdx] += R2;
1312 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1313 std::abs(R2) / pvValue);
1314 }
1315 if constexpr (has_foam_) {
1316 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1317 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1318 R_sum[contiFoamEqIdx] += R2;
1319 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1320 std::abs(R2) / pvValue);
1321 }
1322 if constexpr (has_brine_) {
1323 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1324 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1325 R_sum[contiBrineEqIdx] += R2;
1326 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1327 std::abs(R2) / pvValue);
1328 }
1329
1330 if constexpr (has_polymermw_) {
1331 static_assert(has_polymer_);
1332
1333 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1334 // the residual of the polymer molecular equation is scaled down by a 100, since molecular weight
1335 // can be much bigger than 1, and this equation shares the same tolerance with other mass balance equations
1336 // TODO: there should be a more general way to determine the scaling-down coefficient
1337 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1338 R_sum[contiPolymerMWEqIdx] += R2;
1339 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1340 std::abs(R2) / pvValue);
1341 }
1342
1343 if constexpr (has_energy_) {
1344 B_avg[contiEnergyEqIdx] += 1.0 / (4.182e1); // converting J -> RM3 (entalpy / (cp * deltaK * rho) assuming change of 1e-5K of water
1345 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1346 R_sum[contiEnergyEqIdx] += R2;
1347 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1348 std::abs(R2) / pvValue);
1349 }
1350
1351 if constexpr (has_micp_) {
1352 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1353 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1354 R_sum[contiMicrobialEqIdx] += R1;
1355 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1356 std::abs(R1) / pvValue);
1357 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1358 const auto R2 = modelResid[cell_idx][contiOxygenEqIdx];
1359 R_sum[contiOxygenEqIdx] += R2;
1360 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1361 std::abs(R2) / pvValue);
1362 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1363 const auto R3 = modelResid[cell_idx][contiUreaEqIdx];
1364 R_sum[contiUreaEqIdx] += R3;
1365 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1366 std::abs(R3) / pvValue);
1367 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1368 const auto R4 = modelResid[cell_idx][contiBiofilmEqIdx];
1369 R_sum[contiBiofilmEqIdx] += R4;
1370 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1371 std::abs(R4) / pvValue);
1372 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1373 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1374 R_sum[contiCalciteEqIdx] += R5;
1375 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1376 std::abs(R5) / pvValue);
1377 }
1378 }
1379
1381 const ModelParameters& param() const
1382 {
1383 return param_;
1384 }
1385
1388 {
1389 return compNames_;
1390 }
1391
1392 private:
1393 Scalar dpMaxRel() const { return param_.dp_max_rel_; }
1394 Scalar dsMax() const { return param_.ds_max_; }
1395 Scalar drMaxRel() const { return param_.dr_max_rel_; }
1396 Scalar maxResidualAllowed() const { return param_.max_residual_allowed_; }
1397 double linear_solve_setup_time_;
1398
1399 public:
1400 std::vector<bool> wasSwitched_;
1401 };
1402
1403} // namespace Opm
1404
1405#endif // OPM_BLACKOILMODEL_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 aquifer model.
Definition: BlackoilAquiferModel.hpp:50
Definition: BlackoilModel.hpp:163
static constexpr int contiEnergyEqIdx
Definition: BlackoilModel.hpp:184
SimulatorReportSingle failureReport_
Definition: BlackoilModel.hpp:1225
BlackoilModel(Simulator &simulator, const ModelParameters &param, BlackoilWellModel< TypeTag > &well_model, const bool terminal_output)
Definition: BlackoilModel.hpp:225
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:1145
static constexpr bool has_energy_
Definition: BlackoilModel.hpp:1219
ModelParameters param_
Definition: BlackoilModel.hpp:1224
int linearIterationsLastSolve() const
Number of linear iterations used in last call to solveJacobianSystem().
Definition: BlackoilModel.hpp:612
SimulatorReportSingle nonlinearIteration(const int iteration, const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: BlackoilModel.hpp:387
static constexpr bool has_extbo_
Definition: BlackoilModel.hpp:1216
SimulatorReportSingle assembleReservoir(const SimulatorTimerInterface &, const int iterationIdx)
Assemble the residual and Jacobian of the nonlinear system.
Definition: BlackoilModel.hpp:516
GetPropType< TypeTag, Properties::SparseMatrixAdapter > SparseMatrixAdapter
Definition: BlackoilModel.hpp:170
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int iteration, const int maxIter, std::vector< Scalar > &residual_norms)
Definition: BlackoilModel.hpp:1117
ConvergenceReport getReservoirConvergence(const double reportTime, const double dt, const int iteration, const int maxIter, std::vector< Scalar > &B_avg, std::vector< Scalar > &residual_norms)
Definition: BlackoilModel.hpp:915
GetPropType< TypeTag, Properties::IntensiveQuantities > IntensiveQuantities
Definition: BlackoilModel.hpp:169
static constexpr bool has_foam_
Definition: BlackoilModel.hpp:1220
static constexpr int foamConcentrationIdx
Definition: BlackoilModel.hpp:198
Scalar current_relaxation_
Definition: BlackoilModel.hpp:1238
GetPropType< TypeTag, Properties::FluidSystem > FluidSystem
Definition: BlackoilModel.hpp:173
static constexpr int microbialConcentrationIdx
Definition: BlackoilModel.hpp:200
static constexpr int numEq
Definition: BlackoilModel.hpp:180
static constexpr bool has_polymermw_
Definition: BlackoilModel.hpp:1218
static constexpr int biofilmConcentrationIdx
Definition: BlackoilModel.hpp:203
static constexpr int contiFoamEqIdx
Definition: BlackoilModel.hpp:186
static constexpr int polymerMoleWeightIdx
Definition: BlackoilModel.hpp:196
void endReportStep()
Definition: BlackoilModel.hpp:1259
static constexpr int contiCalciteEqIdx
Definition: BlackoilModel.hpp:192
const std::vector< std::vector< int > > & getConvCells() const
Definition: BlackoilModel.hpp:1206
GetPropType< TypeTag, Properties::ElementContext > ElementContext
Definition: BlackoilModel.hpp:168
bool isParallel() const
Definition: BlackoilModel.hpp:262
const std::vector< StepReport > & stepReports() const
Definition: BlackoilModel.hpp:1178
Scalar relativeChange() const
Definition: BlackoilModel.hpp:528
std::vector< StepReport > convergence_reports_
Definition: BlackoilModel.hpp:1241
static constexpr int contiMicrobialEqIdx
Definition: BlackoilModel.hpp:188
bool terminalOutputEnabled() const
Return true if output to cout is wanted.
Definition: BlackoilModel.hpp:710
double & linearSolveSetupTime()
Definition: BlackoilModel.hpp:619
int numPhases() const
The number of active fluid phases in the model.
Definition: BlackoilModel.hpp:1137
SimulatorReportSingle afterStep(const SimulatorTimerInterface &)
Definition: BlackoilModel.hpp:504
static constexpr int contiBiofilmEqIdx
Definition: BlackoilModel.hpp:191
const Grid & grid_
Definition: BlackoilModel.hpp:1213
static constexpr int contiUreaEqIdx
Definition: BlackoilModel.hpp:190
static constexpr int calciteConcentrationIdx
Definition: BlackoilModel.hpp:204
static constexpr int temperatureIdx
Definition: BlackoilModel.hpp:197
GetPropType< TypeTag, Properties::SolutionVector > SolutionVector
Definition: BlackoilModel.hpp:171
const ComponentName & compNames() const
Returns const reference to component names.
Definition: BlackoilModel.hpp:1387
GetPropType< TypeTag, Properties::PrimaryVariables > PrimaryVariables
Definition: BlackoilModel.hpp:172
void initialLinearization(SimulatorReportSingle &report, const int iteration, const int minIter, const int maxIter, const SimulatorTimerInterface &timer)
Definition: BlackoilModel.hpp:332
void updateSolution(const BVector &dx)
Apply an update to the primary variables.
Definition: BlackoilModel.hpp:688
long int global_nc_
The number of cells of the global grid.
Definition: BlackoilModel.hpp:1235
GetPropType< TypeTag, Properties::Indices > Indices
Definition: BlackoilModel.hpp:174
void updateTUNING(const Tuning &tuning)
Definition: BlackoilModel.hpp:903
typename SparseMatrixAdapter::MatrixBlock MatrixBlockType
Definition: BlackoilModel.hpp:207
const ModelParameters & param() const
Returns const reference to model parameters.
Definition: BlackoilModel.hpp:1381
static constexpr int contiOxygenEqIdx
Definition: BlackoilModel.hpp:189
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.hpp:1265
std::unique_ptr< BlackoilModelNldd< TypeTag > > nlddSolver_
Non-linear DD solver.
Definition: BlackoilModel.hpp:1244
SimulatorReportSingle nonlinearIterationNewton(const int iteration, const SimulatorTimerInterface &timer, NonlinearSolverType &nonlinear_solver)
Definition: BlackoilModel.hpp:418
void writePartitions(const std::filesystem::path &odir) const
Definition: BlackoilModel.hpp:1183
static constexpr int solventSaturationIdx
Definition: BlackoilModel.hpp:193
std::pair< std::vector< double >, std::vector< int > > 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.hpp:833
void beginReportStep()
Definition: BlackoilModel.hpp:1254
static constexpr int polymerConcentrationIdx
Definition: BlackoilModel.hpp:195
GetPropType< TypeTag, Properties::Simulator > Simulator
Definition: BlackoilModel.hpp:166
const SimulatorReportSingle & failureReport() const
return the statistics if the nonlinearIteration() method failed
Definition: BlackoilModel.hpp:1168
Simulator & simulator()
Definition: BlackoilModel.hpp:1164
GetPropType< TypeTag, Properties::MaterialLaw > MaterialLaw
Definition: BlackoilModel.hpp:175
static constexpr int contiPolymerMWEqIdx
Definition: BlackoilModel.hpp:185
static constexpr bool has_micp_
Definition: BlackoilModel.hpp:1222
const Simulator & simulator() const
Definition: BlackoilModel.hpp:1161
BlackoilWellModel< TypeTag > & well_model_
Definition: BlackoilModel.hpp:1228
BlackoilWellModel< TypeTag > & wellModel()
return the StandardWells object
Definition: BlackoilModel.hpp:1249
std::vector< std::vector< Scalar > > residual_norms_history_
Definition: BlackoilModel.hpp:1237
typename SparseMatrixAdapter::IstlMatrix Mat
Definition: BlackoilModel.hpp:208
Dune::FieldVector< Scalar, numEq > VectorBlockType
Definition: BlackoilModel.hpp:206
static constexpr bool has_solvent_
Definition: BlackoilModel.hpp:1215
const BlackoilWellModel< TypeTag > & wellModel() const
Definition: BlackoilModel.hpp:1252
BVector dx_old_
Definition: BlackoilModel.hpp:1239
Simulator & simulator_
Definition: BlackoilModel.hpp:1212
std::vector< std::vector< Scalar > > computeFluidInPlace(const std::vector< int > &) const
Should not be called.
Definition: BlackoilModel.hpp:1152
SimulatorReportSingle localAccumulatedReports() const
return the statistics if the nonlinearIteration() method failed
Definition: BlackoilModel.hpp:1172
static constexpr bool has_polymer_
Definition: BlackoilModel.hpp:1217
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.hpp:778
const PhaseUsage phaseUsage_
Definition: BlackoilModel.hpp:1214
static constexpr bool has_brine_
Definition: BlackoilModel.hpp:1221
void solveJacobianSystem(BVector &x)
Definition: BlackoilModel.hpp:627
static constexpr int oxygenConcentrationIdx
Definition: BlackoilModel.hpp:201
ComponentName compNames_
Definition: BlackoilModel.hpp:1242
SimulatorReportSingle prepareStep(const SimulatorTimerInterface &timer)
Definition: BlackoilModel.hpp:272
RSTConv rst_conv_
Helper class for RPTRST CONV.
Definition: BlackoilModel.hpp:1230
static constexpr int ureaConcentrationIdx
Definition: BlackoilModel.hpp:202
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.hpp:715
GetPropType< TypeTag, Properties::Grid > Grid
Definition: BlackoilModel.hpp:167
Dune::BlockVector< VectorBlockType > BVector
Definition: BlackoilModel.hpp:209
GetPropType< TypeTag, Properties::Scalar > Scalar
Definition: BlackoilModel.hpp:177
static constexpr int saltConcentrationIdx
Definition: BlackoilModel.hpp:199
static constexpr int zFractionIdx
Definition: BlackoilModel.hpp:194
static constexpr int contiBrineEqIdx
Definition: BlackoilModel.hpp:187
static constexpr int contiZfracEqIdx
Definition: BlackoilModel.hpp:182
static constexpr int contiSolventEqIdx
Definition: BlackoilModel.hpp:181
GetPropType< TypeTag, Properties::MaterialLawParams > MaterialLawParams
Definition: BlackoilModel.hpp:176
static constexpr int contiPolymerEqIdx
Definition: BlackoilModel.hpp:183
std::vector< bool > wasSwitched_
Definition: BlackoilModel.hpp:1400
const EclipseState & eclState() const
Definition: BlackoilModel.hpp:266
bool terminal_output_
Whether we print something to std::cout.
Definition: BlackoilModel.hpp:1233
Class for handling the blackoil well model.
Definition: BlackoilWellModel.hpp:98
Definition: ComponentName.hpp:34
const std::string & name(const int compIdx) const
Definition: ComponentName.hpp:38
Definition: ConvergenceReport.hpp:38
Severity
Definition: ConvergenceReport.hpp:49
void setReservoirFailed(const ReservoirFailure &rf)
Definition: ConvergenceReport.hpp:191
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
Definition: FlowProblem.hpp:91
Class computing RPTRST CONV output.
Definition: RSTConv.hpp:34
const std::vector< std::vector< int > > & getData() const
Obtain a const-ref to the accumulated data.
Definition: RSTConv.hpp:58
void update(const ResidualVector &resid)
Adds the CONV output for given residual vector.
void init(const std::size_t numCells, const RSTConfig &rst_config, const std::array< int, 6 > &compIdx)
Init state at beginning of step.
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:50
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
Definition: blackoilmodel.hh:72
std::size_t countGlobalCells(const Grid &grid)
Get the number of cells of a global grid.
Definition: countGlobalCells.hpp:82
Definition: blackoilboundaryratevector.hh:37
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:235
PhaseUsage phaseUsageFromDeck(const EclipseState &eclipseState)
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
Solver parameters for the BlackoilModel.
Definition: BlackoilModelParameters.hpp:147
Scalar tolerance_mb_relaxed_
Relaxed mass balance tolerance (can be used when iter >= min_strict_mb_iter_).
Definition: BlackoilModelParameters.hpp:161
Scalar tolerance_energy_balance_
Relative energy balance tolerance (total energy balance error).
Definition: BlackoilModelParameters.hpp:163
bool matrix_add_well_contributions_
Whether to add influences of wells between cells to the matrix and preconditioner matrix.
Definition: BlackoilModelParameters.hpp:244
bool update_equations_scaling_
Update scaling factors for mass balance equations.
Definition: BlackoilModelParameters.hpp:228
Scalar tolerance_energy_balance_relaxed_
Relaxed energy balance tolerance (can be used when iter >= min_strict_mb_iter_).
Definition: BlackoilModelParameters.hpp:165
int min_strict_mb_iter_
Minimum number of Newton iterations before we can use relaxed MB convergence criterion.
Definition: BlackoilModelParameters.hpp:222
int min_strict_cnv_iter_
Minimum number of Newton iterations before we can use relaxed CNV convergence criterion.
Definition: BlackoilModelParameters.hpp:219
bool use_update_stabilization_
Try to detect oscillation or stagnation.
Definition: BlackoilModelParameters.hpp:231
int nldd_num_initial_newton_iter_
Definition: BlackoilModelParameters.hpp:279
Scalar tolerance_cnv_energy_
Local energy convergence tolerance (max of local energy errors).
Definition: BlackoilModelParameters.hpp:171
Scalar max_residual_allowed_
Absolute max limit for residuals.
Definition: BlackoilModelParameters.hpp:154
Scalar tolerance_cnv_energy_relaxed_
Relaxed local energy convergence tolerance (can be used when iter >= min_strict_cnv_iter_ && cnvViola...
Definition: BlackoilModelParameters.hpp:173
Scalar tolerance_mb_
Relative mass balance tolerance (total mass balance error).
Definition: BlackoilModelParameters.hpp:159
Scalar tolerance_cnv_
Local convergence tolerance (max of local saturation errors).
Definition: BlackoilModelParameters.hpp:167
Scalar tolerance_cnv_relaxed_
Relaxed local convergence tolerance (can be used when iter >= min_strict_cnv_iter_ && cnvViolatedPV <...
Definition: BlackoilModelParameters.hpp:169
Scalar relaxed_max_pv_fraction_
Definition: BlackoilModelParameters.hpp:157
std::string nonlinear_solver_
Nonlinear solver type: newton or nldd.
Definition: BlackoilModelParameters.hpp:270
Definition: AquiferGridUtils.hpp:35
Definition: BlackoilPhases.hpp:46
int num_phases
Definition: BlackoilPhases.hpp:54
Definition: FlowBaseProblemProperties.hpp:62
Enable surface volume scaling.
Definition: blackoilproperties.hh:54
Enable the ECL-blackoil extension for salt.
Definition: blackoilproperties.hh:60
Enable convective mixing?
Definition: multiphasebaseproperties.hh:85
Definition: FlowBaseProblemProperties.hpp:73
Enable dispersive fluxes?
Definition: multiphasebaseproperties.hh:82
Specify whether energy should be considered as a conservation quantity or not.
Definition: multiphasebaseproperties.hh:76
Enable the ECL-blackoil extension for foam.
Definition: blackoilproperties.hh:57
Enable the ECL-blackoil extension for MICP.
Definition: blackoilproperties.hh:72
Enable the ECL-blackoil extension for polymer.
Definition: blackoilproperties.hh:48
Enable the ECL-blackoil extension for salt precipitation.
Definition: blackoilproperties.hh:63
Enable the ECL-blackoil extension for solvents. ("Second gas")
Definition: blackoilproperties.hh:42
Definition: blackoilproperties.hh:78
Definition: fvbaseproperties.hh:53
Definition: ISTLSolver.hpp:63
Definition: BlackoilModel.hpp:80
std::tuple< FlowBaseProblemBlackoil, BlackOilModel > InheritsFrom
Definition: BlackoilModel.hpp:80
Specify whether to use volumetric residuals or not.
Definition: fvbaseproperties.hh:237
Definition: FlowBaseProblemProperties.hpp:82
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:54
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:49
double update_time
Definition: SimulatorReport.hpp:44
unsigned int total_linearizations
Definition: SimulatorReport.hpp:48
unsigned int total_linear_iterations
Definition: SimulatorReport.hpp:50