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