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] =
901 this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
902
903 // compute global sum and max of quantities
904 const auto [ pvSum, numAquiferPvSum ] =
905 convergenceReduction(grid_.comm(), pvSumLocal,
906 numAquiferPvSumLocal,
907 R_sum, maxCoeff, B_avg);
908
909 const auto cnvErrorPvFraction =
910 computeCnvErrorPv(B_avg, dt)
911 / (pvSum - numAquiferPvSum);
912
913 // For each iteration, we need to determine whether to use the relaxed tolerances.
914 // To disable the usage of relaxed tolerances, you can set the relaxed tolerances as the strict tolerances.
915
916 // If min_strict_mb_iter = -1 (default) we use a relaxed tolerance for the mass balance for the last iterations
917 // For positive values we use the relaxed tolerance after the given number of iterations
918 const bool relax_final_iteration_mb = (param_.min_strict_mb_iter_ < 0) && (iteration == maxIter);
919 const bool use_relaxed_mb = relax_final_iteration_mb || (param_.min_strict_mb_iter_ >= 0 && iteration >= param_.min_strict_mb_iter_);
920
921
922 // If min_strict_cnv_iter = -1 we use a relaxed tolerance for the cnv for the last iterations
923 // For positive values we use the relaxed tolerance after the given number of iterations
924 // We also use relaxed tolerances for cells with total poro volume less than relaxed_max_pv_fraction_
925 // Default value of relaxed_max_pv_fraction_ is 0.03
926 const bool relax_final_iteration_cnv = (param_.min_strict_cnv_iter_ < 0) && (iteration == maxIter);
927 const bool relax_iter_cnv = param_.min_strict_mb_iter_ >= 0 && iteration >= param_.min_strict_mb_iter_;
928 const bool relax_pv_fraction_cnv = cnvErrorPvFraction < param_.relaxed_max_pv_fraction_;
929 const bool use_relaxed_cnv = relax_final_iteration_cnv || relax_pv_fraction_cnv || relax_iter_cnv;
930
931 if (relax_final_iteration_mb || relax_final_iteration_cnv) {
932 if ( terminal_output_ ) {
933 std::string message = "Number of newton iterations reached its maximum try to continue with relaxed tolerances:";
934 if (relax_final_iteration_mb)
935 message += fmt::format(" MB: {:.1e}", param_.tolerance_mb_relaxed_);
936 if (relax_final_iteration_cnv)
937 message += fmt::format(" CNV: {:.1e}", param_.tolerance_cnv_relaxed_);
938
939 OpmLog::debug(message);
940 }
941 }
942 const double tol_cnv = use_relaxed_cnv ? param_.tolerance_cnv_relaxed_ : param_.tolerance_cnv_;
943 const double tol_mb = use_relaxed_mb ? param_.tolerance_mb_relaxed_ : param_.tolerance_mb_;
944
945 // Finish computation
946 std::vector<Scalar> CNV(numComp);
947 std::vector<Scalar> mass_balance_residual(numComp);
948 for (int compIdx = 0; compIdx < numComp; ++compIdx)
949 {
950 CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
951 mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
952 residual_norms.push_back(CNV[compIdx]);
953 }
954
955 // Create convergence report.
956 ConvergenceReport report{reportTime};
957 report.setPoreVolCnvViolationFraction(cnvErrorPvFraction, pvSum - numAquiferPvSum);
958
959 using CR = ConvergenceReport;
960 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
961 const double res[2] = { mass_balance_residual[compIdx], CNV[compIdx] };
962 const CR::ReservoirFailure::Type types[2] = { CR::ReservoirFailure::Type::MassBalance,
963 CR::ReservoirFailure::Type::Cnv };
964 const double tol[2] = { tol_mb, tol_cnv };
965
966 for (int ii : {0, 1}) {
967 if (std::isnan(res[ii])) {
968 report.setReservoirFailed({types[ii], CR::Severity::NotANumber, compIdx});
969 if ( terminal_output_ ) {
970 OpmLog::debug("NaN residual for " + this->compNames_.name(compIdx) + " equation.");
971 }
972 } else if (res[ii] > maxResidualAllowed()) {
973 report.setReservoirFailed({types[ii], CR::Severity::TooLarge, compIdx});
974 if ( terminal_output_ ) {
975 OpmLog::debug("Too large residual for " + this->compNames_.name(compIdx) + " equation.");
976 }
977 } else if (res[ii] < 0.0) {
978 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
979 if ( terminal_output_ ) {
980 OpmLog::debug("Negative residual for " + this->compNames_.name(compIdx) + " equation.");
981 }
982 } else if (res[ii] > tol[ii]) {
983 report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
984 }
985 report.setReservoirConvergenceMetric(types[ii], compIdx, res[ii]);
986 }
987 }
988
989 // Output of residuals.
990 if ( terminal_output_ )
991 {
992 // Only rank 0 does print to std::cout
993 if (iteration == 0) {
994 std::string msg = "Iter";
995 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
996 msg += " MB(";
997 msg += this->compNames_.name(compIdx)[0];
998 msg += ") ";
999 }
1000 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1001 msg += " CNV(";
1002 msg += this->compNames_.name(compIdx)[0];
1003 msg += ") ";
1004 }
1005 OpmLog::debug(msg);
1006 }
1007 std::ostringstream ss;
1008 const std::streamsize oprec = ss.precision(3);
1009 const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
1010 ss << std::setw(4) << iteration;
1011 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1012 ss << std::setw(11) << mass_balance_residual[compIdx];
1013 }
1014 for (int compIdx = 0; compIdx < numComp; ++compIdx) {
1015 ss << std::setw(11) << CNV[compIdx];
1016 }
1017 ss.precision(oprec);
1018 ss.flags(oflags);
1019 OpmLog::debug(ss.str());
1020 }
1021
1022 return report;
1023 }
1024
1032 const int iteration,
1033 const int maxIter,
1034 std::vector<double>& residual_norms)
1035 {
1036 OPM_TIMEBLOCK(getConvergence);
1037 // Get convergence reports for reservoir and wells.
1038 std::vector<Scalar> B_avg(numEq, 0.0);
1039 auto report = getReservoirConvergence(timer.simulationTimeElapsed(),
1040 timer.currentStepLength(),
1041 iteration, maxIter, B_avg, residual_norms);
1042 {
1043 OPM_TIMEBLOCK(getWellConvergence);
1044 report += wellModel().getWellConvergence(B_avg, /*checkWellGroupControls*/report.converged());
1045 }
1046 return report;
1047 }
1048
1049
1051 int numPhases() const
1052 {
1053 return phaseUsage_.num_phases;
1054 }
1055
1057 template<class T>
1058 std::vector<std::vector<double> >
1059 computeFluidInPlace(const T&, const std::vector<int>& fipnum) const
1060 {
1061 return computeFluidInPlace(fipnum);
1062 }
1063
1065 std::vector<std::vector<double> >
1066 computeFluidInPlace(const std::vector<int>& /*fipnum*/) const
1067 {
1068 OPM_TIMEBLOCK(computeFluidInPlace);
1069 //assert(true)
1070 //return an empty vector
1071 std::vector<std::vector<double> > regionValues(0, std::vector<double>(0,0.0));
1072 return regionValues;
1073 }
1074
1075 const Simulator& simulator() const
1076 { return simulator_; }
1077
1079 { return simulator_; }
1080
1083 { return failureReport_; }
1084
1087 {
1088 return nlddSolver_ ? nlddSolver_->localAccumulatedReports()
1090 }
1091
1092 const std::vector<StepReport>& stepReports() const
1093 {
1094 return convergence_reports_;
1095 }
1096
1097 void writePartitions(const std::filesystem::path& odir) const
1098 {
1099 if (this->nlddSolver_ != nullptr) {
1100 this->nlddSolver_->writePartitions(odir);
1101 return;
1102 }
1103
1104 const auto& elementMapper = this->simulator().model().elementMapper();
1105 const auto& cartMapper = this->simulator().vanguard().cartesianIndexMapper();
1106
1107 const auto& grid = this->simulator().vanguard().grid();
1108 const auto& comm = grid.comm();
1109 const auto nDigit = 1 + static_cast<int>(std::floor(std::log10(comm.size())));
1110
1111 std::ofstream pfile { odir / fmt::format("{1:0>{0}}", nDigit, comm.rank()) };
1112
1113 for (const auto& cell : elements(grid.leafGridView(), Dune::Partitions::interior)) {
1114 pfile << comm.rank() << ' '
1115 << cartMapper.cartesianIndex(elementMapper.index(cell)) << ' '
1116 << comm.rank() << '\n';
1117 }
1118 }
1119
1120 const std::vector<std::vector<int>>& getConvCells() const
1121 { return rst_conv_.getData(); }
1122
1123 protected:
1124 // --------- Data members ---------
1125
1127 const Grid& grid_;
1129 static constexpr bool has_solvent_ = getPropValue<TypeTag, Properties::EnableSolvent>();
1130 static constexpr bool has_extbo_ = getPropValue<TypeTag, Properties::EnableExtbo>();
1131 static constexpr bool has_polymer_ = getPropValue<TypeTag, Properties::EnablePolymer>();
1132 static constexpr bool has_polymermw_ = getPropValue<TypeTag, Properties::EnablePolymerMW>();
1133 static constexpr bool has_energy_ = getPropValue<TypeTag, Properties::EnableEnergy>();
1134 static constexpr bool has_foam_ = getPropValue<TypeTag, Properties::EnableFoam>();
1135 static constexpr bool has_brine_ = getPropValue<TypeTag, Properties::EnableBrine>();
1136 static constexpr bool has_micp_ = getPropValue<TypeTag, Properties::EnableMICP>();
1137
1140
1141 // Well Model
1143
1145
1149 long int global_nc_;
1150
1151 std::vector<std::vector<double>> residual_norms_history_;
1154
1155 std::vector<StepReport> convergence_reports_;
1157
1158 std::unique_ptr<BlackoilModelNldd<TypeTag>> nlddSolver_;
1159
1160 public:
1164
1166 wellModel() const { return well_model_; }
1167
1169 {
1170 simulator_.problem().beginEpisode();
1171 }
1172
1174 {
1175 simulator_.problem().endEpisode();
1176 }
1177
1178 template<class FluidState, class Residual>
1179 void getMaxCoeff(const unsigned cell_idx,
1180 const IntensiveQuantities& intQuants,
1181 const FluidState& fs,
1182 const Residual& modelResid,
1183 const Scalar pvValue,
1184 std::vector<Scalar>& B_avg,
1185 std::vector<Scalar>& R_sum,
1186 std::vector<Scalar>& maxCoeff,
1187 std::vector<int>& maxCoeffCell)
1188 {
1189 for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
1190 {
1191 if (!FluidSystem::phaseIsActive(phaseIdx)) {
1192 continue;
1193 }
1194
1195 const unsigned compIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
1196
1197 B_avg[compIdx] += 1.0 / fs.invB(phaseIdx).value();
1198 const auto R2 = modelResid[cell_idx][compIdx];
1199
1200 R_sum[compIdx] += R2;
1201 const double Rval = std::abs(R2) / pvValue;
1202 if (Rval > maxCoeff[compIdx]) {
1203 maxCoeff[compIdx] = Rval;
1204 maxCoeffCell[compIdx] = cell_idx;
1205 }
1206 }
1207
1208 if constexpr (has_solvent_) {
1209 B_avg[contiSolventEqIdx] += 1.0 / intQuants.solventInverseFormationVolumeFactor().value();
1210 const auto R2 = modelResid[cell_idx][contiSolventEqIdx];
1211 R_sum[contiSolventEqIdx] += R2;
1212 maxCoeff[contiSolventEqIdx] = std::max(maxCoeff[contiSolventEqIdx],
1213 std::abs(R2) / pvValue);
1214 }
1215 if constexpr (has_extbo_) {
1216 B_avg[contiZfracEqIdx] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1217 const auto R2 = modelResid[cell_idx][contiZfracEqIdx];
1218 R_sum[ contiZfracEqIdx ] += R2;
1219 maxCoeff[contiZfracEqIdx] = std::max(maxCoeff[contiZfracEqIdx],
1220 std::abs(R2) / pvValue);
1221 }
1222 if constexpr (has_polymer_) {
1223 B_avg[contiPolymerEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1224 const auto R2 = modelResid[cell_idx][contiPolymerEqIdx];
1225 R_sum[contiPolymerEqIdx] += R2;
1226 maxCoeff[contiPolymerEqIdx] = std::max(maxCoeff[contiPolymerEqIdx],
1227 std::abs(R2) / pvValue);
1228 }
1229 if constexpr (has_foam_) {
1230 B_avg[ contiFoamEqIdx ] += 1.0 / fs.invB(FluidSystem::gasPhaseIdx).value();
1231 const auto R2 = modelResid[cell_idx][contiFoamEqIdx];
1232 R_sum[contiFoamEqIdx] += R2;
1233 maxCoeff[contiFoamEqIdx] = std::max(maxCoeff[contiFoamEqIdx],
1234 std::abs(R2) / pvValue);
1235 }
1236 if constexpr (has_brine_) {
1237 B_avg[ contiBrineEqIdx ] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1238 const auto R2 = modelResid[cell_idx][contiBrineEqIdx];
1239 R_sum[contiBrineEqIdx] += R2;
1240 maxCoeff[contiBrineEqIdx] = std::max(maxCoeff[contiBrineEqIdx],
1241 std::abs(R2) / pvValue);
1242 }
1243
1244 if constexpr (has_polymermw_) {
1245 static_assert(has_polymer_);
1246
1247 B_avg[contiPolymerMWEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1248 // the residual of the polymer molecular equation is scaled down by a 100, since molecular weight
1249 // can be much bigger than 1, and this equation shares the same tolerance with other mass balance equations
1250 // TODO: there should be a more general way to determine the scaling-down coefficient
1251 const auto R2 = modelResid[cell_idx][contiPolymerMWEqIdx] / 100.;
1252 R_sum[contiPolymerMWEqIdx] += R2;
1253 maxCoeff[contiPolymerMWEqIdx] = std::max(maxCoeff[contiPolymerMWEqIdx],
1254 std::abs(R2) / pvValue);
1255 }
1256
1257 if constexpr (has_energy_) {
1258 B_avg[contiEnergyEqIdx] += 1.0 / (4.182e1); // converting J -> RM3 (entalpy / (cp * deltaK * rho) assuming change of 1e-5K of water
1259 const auto R2 = modelResid[cell_idx][contiEnergyEqIdx];
1260 R_sum[contiEnergyEqIdx] += R2;
1261 maxCoeff[contiEnergyEqIdx] = std::max(maxCoeff[contiEnergyEqIdx],
1262 std::abs(R2) / pvValue);
1263 }
1264
1265 if constexpr (has_micp_) {
1266 B_avg[contiMicrobialEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1267 const auto R1 = modelResid[cell_idx][contiMicrobialEqIdx];
1268 R_sum[contiMicrobialEqIdx] += R1;
1269 maxCoeff[contiMicrobialEqIdx] = std::max(maxCoeff[contiMicrobialEqIdx],
1270 std::abs(R1) / pvValue);
1271 B_avg[contiOxygenEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1272 const auto R2 = modelResid[cell_idx][contiOxygenEqIdx];
1273 R_sum[contiOxygenEqIdx] += R2;
1274 maxCoeff[contiOxygenEqIdx] = std::max(maxCoeff[contiOxygenEqIdx],
1275 std::abs(R2) / pvValue);
1276 B_avg[contiUreaEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1277 const auto R3 = modelResid[cell_idx][contiUreaEqIdx];
1278 R_sum[contiUreaEqIdx] += R3;
1279 maxCoeff[contiUreaEqIdx] = std::max(maxCoeff[contiUreaEqIdx],
1280 std::abs(R3) / pvValue);
1281 B_avg[contiBiofilmEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1282 const auto R4 = modelResid[cell_idx][contiBiofilmEqIdx];
1283 R_sum[contiBiofilmEqIdx] += R4;
1284 maxCoeff[contiBiofilmEqIdx] = std::max(maxCoeff[contiBiofilmEqIdx],
1285 std::abs(R4) / pvValue);
1286 B_avg[contiCalciteEqIdx] += 1.0 / fs.invB(FluidSystem::waterPhaseIdx).value();
1287 const auto R5 = modelResid[cell_idx][contiCalciteEqIdx];
1288 R_sum[contiCalciteEqIdx] += R5;
1289 maxCoeff[contiCalciteEqIdx] = std::max(maxCoeff[contiCalciteEqIdx],
1290 std::abs(R5) / pvValue);
1291 }
1292 }
1293
1295 const ModelParameters& param() const
1296 {
1297 return param_;
1298 }
1299
1302 {
1303 return compNames_;
1304 }
1305
1306 private:
1307 double dpMaxRel() const { return param_.dp_max_rel_; }
1308 double dsMax() const { return param_.ds_max_; }
1309 double drMaxRel() const { return param_.dr_max_rel_; }
1310 double maxResidualAllowed() const { return param_.max_residual_allowed_; }
1311 double linear_solve_setup_time_;
1312
1313 public:
1314 std::vector<bool> wasSwitched_;
1315 };
1316} // namespace Opm
1317
1318#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:1139
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:1133
ModelParameters param_
Definition: BlackoilModel.hpp:1138
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:1130
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:1134
ConvergenceReport getConvergence(const SimulatorTimerInterface &timer, const int iteration, const int maxIter, std::vector< double > &residual_norms)
Definition: BlackoilModel.hpp:1031
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:1059
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:1132
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:1173
static constexpr int contiCalciteEqIdx
Definition: BlackoilModel.hpp:194
const std::vector< std::vector< int > > & getConvCells() const
Definition: BlackoilModel.hpp:1120
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:1092
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:1155
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:1051
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:1127
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:1301
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:1149
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:1295
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:1179
std::unique_ptr< BlackoilModelNldd< TypeTag > > nlddSolver_
Non-linear DD solver.
Definition: BlackoilModel.hpp:1158
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:1097
static constexpr int solventSaturationIdx
Definition: BlackoilModel.hpp:195
void beginReportStep()
Definition: BlackoilModel.hpp:1168
std::vector< std::vector< double > > computeFluidInPlace(const std::vector< int > &) const
Should not be called.
Definition: BlackoilModel.hpp:1066
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:1082
Simulator & simulator()
Definition: BlackoilModel.hpp:1078
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:1136
const Simulator & simulator() const
Definition: BlackoilModel.hpp:1075
BlackoilWellModel< TypeTag > & well_model_
Definition: BlackoilModel.hpp:1142
BlackoilWellModel< TypeTag > & wellModel()
return the StandardWells object
Definition: BlackoilModel.hpp:1163
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:1129
const BlackoilWellModel< TypeTag > & wellModel() const
Definition: BlackoilModel.hpp:1166
BVector dx_old_
Definition: BlackoilModel.hpp:1153
Simulator & simulator_
Definition: BlackoilModel.hpp:1126
double current_relaxation_
Definition: BlackoilModel.hpp:1152
SimulatorReportSingle localAccumulatedReports() const
return the statistics if the nonlinearIteration() method failed
Definition: BlackoilModel.hpp:1086
static constexpr bool has_polymer_
Definition: BlackoilModel.hpp:1131
const PhaseUsage phaseUsage_
Definition: BlackoilModel.hpp:1128
static constexpr bool has_brine_
Definition: BlackoilModel.hpp:1135
void solveJacobianSystem(BVector &x)
Definition: BlackoilModel.hpp:629
static constexpr int oxygenConcentrationIdx
Definition: BlackoilModel.hpp:203
ComponentName compNames_
Definition: BlackoilModel.hpp:1156
SimulatorReportSingle prepareStep(const SimulatorTimerInterface &timer)
Definition: BlackoilModel.hpp:274
RSTConv rst_conv_
Helper class for RPTRST CONV.
Definition: BlackoilModel.hpp:1144
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:1151
GetPropType< TypeTag, Properties::MaterialLawParams > MaterialLawParams
Definition: BlackoilModel.hpp:179
static constexpr int contiPolymerEqIdx
Definition: BlackoilModel.hpp:185
std::vector< bool > wasSwitched_
Definition: BlackoilModel.hpp:1314
const EclipseState & eclState() const
Definition: BlackoilModel.hpp:268
bool terminal_output_
Whether we print something to std::cout.
Definition: BlackoilModel.hpp:1147
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:49
void setPoreVolCnvViolationFraction(const double cnvErrorPvFraction, const double cnvErrorPvFractionDenom)
Definition: ConvergenceReport.hpp:167
void setReservoirFailed(const ReservoirFailure &rf)
Definition: ConvergenceReport.hpp:144
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:485
double relaxed_max_pv_fraction_
Definition: BlackoilModelParameters.hpp:498
bool update_equations_scaling_
Update scaling factors for mass balance equations.
Definition: BlackoilModelParameters.hpp:561
double tolerance_cnv_relaxed_
Relaxed local convergence tolerance (can be used when iter >= min_strict_cnv_iter_ && cnvViolatedPV <...
Definition: BlackoilModelParameters.hpp:506
double max_residual_allowed_
Absolute max limit for residuals.
Definition: BlackoilModelParameters.hpp:495
int min_strict_mb_iter_
Minimum number of Newton iterations before we can use relaxed MB convergence criterion.
Definition: BlackoilModelParameters.hpp:555
double tolerance_cnv_
Local convergence tolerance (max of local saturation errors).
Definition: BlackoilModelParameters.hpp:504
bool matrix_add_well_contributions_
Whether to add influences of wells between cells to the matrix and preconditioner matrix.
Definition: BlackoilModelParameters.hpp:577
int nldd_num_initial_newton_iter_
Definition: BlackoilModelParameters.hpp:612
bool use_update_stabilization_
Try to detect oscillation or stagnation.
Definition: BlackoilModelParameters.hpp:564
std::string nonlinear_solver_
Nonlinear solver type: newton or nldd.
Definition: BlackoilModelParameters.hpp:603
double tolerance_mb_relaxed_
Relaxed mass balance tolerance (can be used when iter >= min_strict_mb_iter_).
Definition: BlackoilModelParameters.hpp:502
int min_strict_cnv_iter_
Minimum number of Newton iterations before we can use relaxed CNV convergence criterion.
Definition: BlackoilModelParameters.hpp:552
double tolerance_mb_
Relative mass balance tolerance (total mass balance error).
Definition: BlackoilModelParameters.hpp:500
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:38
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