2016-06-06 08:40:06 -05:00
/*
Copyright 2013 , 2015 SINTEF ICT , Applied Mathematics .
Copyright 2014 , 2015 Dr . Blatt - HPC - Simulation - Software & Services
Copyright 2014 , 2015 Statoil ASA .
Copyright 2015 NTNU
2017-11-22 07:39:42 -06:00
Copyright 2015 , 2016 , 2017 IRIS AS
2016-06-06 08:40:06 -05:00
This file is part of the Open Porous Media project ( OPM ) .
OPM is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
OPM is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with OPM . If not , see < http : //www.gnu.org/licenses/>.
*/
2024-01-31 07:14:50 -06:00
# ifndef OPM_BLACKOILMODEL_HEADER_INCLUDED
# define OPM_BLACKOILMODEL_HEADER_INCLUDED
2016-06-06 08:40:06 -05:00
2025-01-13 04:27:08 -06:00
# include <dune/common/timer.hh>
2023-05-03 06:17:20 -05:00
2016-06-06 08:40:06 -05:00
# include <opm/common/ErrorMacros.hpp>
# include <opm/common/Exceptions.hpp>
# include <opm/common/OpmLog/OpmLog.hpp>
2022-12-13 05:54:27 -06:00
2021-12-14 01:30:15 -06:00
# include <opm/input/eclipse/EclipseState/EclipseState.hpp>
# include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
2016-06-06 08:40:06 -05:00
2023-07-04 06:33:23 -05:00
# include <opm/simulators/aquifers/AquiferGridUtils.hpp>
2022-12-13 05:54:27 -06:00
# include <opm/simulators/aquifers/BlackoilAquiferModel.hpp>
2025-01-13 04:27:08 -06:00
2024-01-31 07:14:50 -06:00
# include <opm/simulators/flow/BlackoilModelNldd.hpp>
2024-01-31 07:14:50 -06:00
# include <opm/simulators/flow/BlackoilModelParameters.hpp>
2025-01-13 04:27:08 -06:00
# include <opm/simulators/flow/BlackoilModelProperties.hpp>
2022-12-13 05:54:27 -06:00
# include <opm/simulators/flow/countGlobalCells.hpp>
2024-09-11 07:25:34 -05:00
# include <opm/simulators/flow/FlowProblemBlackoil.hpp>
2024-01-31 07:14:50 -06:00
# include <opm/simulators/flow/NonlinearSolver.hpp>
2023-12-11 04:26:43 -06:00
# include <opm/simulators/flow/RSTConv.hpp>
2025-01-13 04:27:08 -06:00
2024-01-31 07:14:50 -06:00
# include <opm/simulators/timestepping/AdaptiveTimeStepping.hpp>
2023-03-01 06:40:54 -06:00
# include <opm/simulators/timestepping/ConvergenceReport.hpp>
2022-12-13 05:54:27 -06:00
# include <opm/simulators/timestepping/SimulatorReport.hpp>
# include <opm/simulators/timestepping/SimulatorTimer.hpp>
2024-06-25 03:20:41 -05:00
2023-06-30 04:18:27 -05:00
# include <opm/simulators/utils/ComponentName.hpp>
2022-12-13 05:54:27 -06:00
# include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
2023-03-03 06:21:36 -06:00
# include <opm/simulators/utils/ParallelCommunication.hpp>
2024-06-25 03:20:41 -05:00
# include <opm/simulators/utils/phaseUsageFromDeck.hpp>
2016-11-11 07:25:19 -06:00
2025-01-13 04:27:08 -06:00
# include <opm/simulators/wells/BlackoilWellModel.hpp>
2023-06-14 02:18:53 -05:00
2022-12-08 07:09:09 -06:00
# include <algorithm>
2016-06-06 08:40:06 -05:00
# include <cassert>
# include <cmath>
2023-10-23 08:55:51 -05:00
# include <filesystem>
# include <fstream>
2024-05-07 08:49:12 -05:00
# include <functional>
2016-06-06 08:40:06 -05:00
# include <iomanip>
2023-07-04 08:15:29 -05:00
# include <ios>
2016-06-06 08:40:06 -05:00
# include <limits>
2023-07-04 08:15:29 -05:00
# include <memory>
2024-05-07 08:49:12 -05:00
# include <numeric>
2023-07-04 08:15:29 -05:00
# include <sstream>
2023-03-03 06:49:51 -06:00
# include <tuple>
2022-12-08 07:09:09 -06:00
# include <utility>
2016-06-06 08:40:06 -05:00
# include <vector>
2025-01-13 04:27:08 -06:00
# include <fmt/format.h>
2024-06-28 05:17:13 -05:00
2016-06-06 08:40:06 -05:00
namespace Opm {
2023-07-04 08:15:29 -05:00
2016-06-06 08:40:06 -05:00
/// A model implementation for three-phase black oil.
///
/// The simulator is capable of handling three-phase problems
/// where gas can be dissolved in oil and vice versa. It
/// uses an industry-standard TPFA discretization with per-phase
/// upwind weighting of mobilities.
2017-06-07 02:29:31 -05:00
template < class TypeTag >
2024-01-31 07:14:50 -06:00
class BlackoilModel
2016-06-06 08:40:06 -05:00
{
public :
// --------- Types and enums ---------
2020-08-26 03:49:52 -05:00
using Simulator = GetPropType < TypeTag , Properties : : Simulator > ;
using Grid = GetPropType < TypeTag , Properties : : Grid > ;
using ElementContext = GetPropType < TypeTag , Properties : : ElementContext > ;
2023-07-03 03:11:45 -05:00
using IntensiveQuantities = GetPropType < TypeTag , Properties : : IntensiveQuantities > ;
2020-08-26 03:49:52 -05:00
using SparseMatrixAdapter = GetPropType < TypeTag , Properties : : SparseMatrixAdapter > ;
using SolutionVector = GetPropType < TypeTag , Properties : : SolutionVector > ;
using PrimaryVariables = GetPropType < TypeTag , Properties : : PrimaryVariables > ;
using FluidSystem = GetPropType < TypeTag , Properties : : FluidSystem > ;
using Indices = GetPropType < TypeTag , Properties : : Indices > ;
using MaterialLaw = GetPropType < TypeTag , Properties : : MaterialLaw > ;
using MaterialLawParams = GetPropType < TypeTag , Properties : : MaterialLawParams > ;
2023-03-03 06:25:46 -06:00
using Scalar = GetPropType < TypeTag , Properties : : Scalar > ;
2024-08-14 07:46:53 -05:00
using ModelParameters = BlackoilModelParameters < Scalar > ;
2016-08-25 08:25:01 -05:00
2023-03-03 06:28:55 -06:00
static constexpr int numEq = Indices : : numEq ;
static constexpr int contiSolventEqIdx = Indices : : contiSolventEqIdx ;
static constexpr int contiZfracEqIdx = Indices : : contiZfracEqIdx ;
static constexpr int contiPolymerEqIdx = Indices : : contiPolymerEqIdx ;
static constexpr int contiEnergyEqIdx = Indices : : contiEnergyEqIdx ;
static constexpr int contiPolymerMWEqIdx = Indices : : contiPolymerMWEqIdx ;
static constexpr int contiFoamEqIdx = Indices : : contiFoamEqIdx ;
static constexpr int contiBrineEqIdx = Indices : : contiBrineEqIdx ;
static constexpr int contiMicrobialEqIdx = Indices : : contiMicrobialEqIdx ;
static constexpr int contiOxygenEqIdx = Indices : : contiOxygenEqIdx ;
static constexpr int contiUreaEqIdx = Indices : : contiUreaEqIdx ;
static constexpr int contiBiofilmEqIdx = Indices : : contiBiofilmEqIdx ;
static constexpr int contiCalciteEqIdx = Indices : : contiCalciteEqIdx ;
static constexpr int solventSaturationIdx = Indices : : solventSaturationIdx ;
static constexpr int zFractionIdx = Indices : : zFractionIdx ;
static constexpr int polymerConcentrationIdx = Indices : : polymerConcentrationIdx ;
static constexpr int polymerMoleWeightIdx = Indices : : polymerMoleWeightIdx ;
static constexpr int temperatureIdx = Indices : : temperatureIdx ;
static constexpr int foamConcentrationIdx = Indices : : foamConcentrationIdx ;
static constexpr int saltConcentrationIdx = Indices : : saltConcentrationIdx ;
static constexpr int microbialConcentrationIdx = Indices : : microbialConcentrationIdx ;
static constexpr int oxygenConcentrationIdx = Indices : : oxygenConcentrationIdx ;
static constexpr int ureaConcentrationIdx = Indices : : ureaConcentrationIdx ;
static constexpr int biofilmConcentrationIdx = Indices : : biofilmConcentrationIdx ;
static constexpr int calciteConcentrationIdx = Indices : : calciteConcentrationIdx ;
2017-06-07 02:29:31 -05:00
2023-03-03 06:25:46 -06:00
using VectorBlockType = Dune : : FieldVector < Scalar , numEq > ;
using MatrixBlockType = typename SparseMatrixAdapter : : MatrixBlock ;
using Mat = typename SparseMatrixAdapter : : IstlMatrix ;
using BVector = Dune : : BlockVector < VectorBlockType > ;
2016-11-02 07:10:44 -05:00
2023-06-30 04:18:27 -05:00
using ComponentName = : : Opm : : ComponentName < FluidSystem , Indices > ;
2022-12-08 07:09:09 -06:00
2016-06-06 08:40:06 -05:00
// --------- Public methods ---------
/// Construct the model. It will retain references to the
/// arguments of this functions, and they are expected to
/// remain in scope for the lifetime of the solver.
/// \param[in] param parameters
/// \param[in] grid grid data structure
/// \param[in] wells well structure
/// \param[in] vfp_properties Vertical flow performance tables
/// \param[in] linsolver linear solver
/// \param[in] eclState eclipse state
/// \param[in] terminal_output request output to cout/cerr
2024-02-06 03:05:36 -06:00
BlackoilModel ( Simulator & simulator ,
2024-01-31 07:14:50 -06:00
const ModelParameters & param ,
BlackoilWellModel < TypeTag > & well_model ,
const bool terminal_output )
2024-02-06 03:05:36 -06:00
: simulator_ ( simulator )
, grid_ ( simulator_ . vanguard ( ) . grid ( ) )
2017-06-16 06:52:51 -05:00
, phaseUsage_ ( phaseUsageFromDeck ( eclState ( ) ) )
2016-06-06 08:40:06 -05:00
, param_ ( param )
2017-08-09 05:32:45 -05:00
, well_model_ ( well_model )
2024-02-02 04:03:48 -06:00
, rst_conv_ ( simulator_ . problem ( ) . eclWriter ( ) - > collectOnIORank ( ) . localIdxToGlobalIdxMapping ( ) ,
2023-12-11 04:26:43 -06:00
grid_ . comm ( ) )
2016-06-06 08:40:06 -05:00
, terminal_output_ ( terminal_output )
, current_relaxation_ ( 1.0 )
2024-02-06 03:05:36 -06:00
, dx_old_ ( simulator_ . model ( ) . numGridDof ( ) )
2016-06-06 08:40:06 -05:00
{
2016-11-02 10:59:08 -05:00
// compute global sum of number of cells
2017-03-17 04:26:00 -05:00
global_nc_ = detail : : countGlobalCells ( grid_ ) ;
2018-11-15 14:21:25 -06:00
convergence_reports_ . reserve ( 300 ) ; // Often insufficient, but avoids frequent moves.
2023-06-14 02:18:53 -05:00
// TODO: remember to fix!
if ( param_ . nonlinear_solver_ = = " nldd " ) {
if ( terminal_output ) {
OpmLog : : info ( " Using Non-Linear Domain Decomposition solver (nldd). " ) ;
}
2024-01-31 07:14:50 -06:00
nlddSolver_ = std : : make_unique < BlackoilModelNldd < TypeTag > > ( * this ) ;
2023-06-14 02:18:53 -05:00
} else if ( param_ . nonlinear_solver_ = = " newton " ) {
if ( terminal_output ) {
OpmLog : : info ( " Using Newton nonlinear solver. " ) ;
}
} else {
OPM_THROW ( std : : runtime_error , " Unknown nonlinear solver option: " + param_ . nonlinear_solver_ ) ;
}
2016-06-06 08:40:06 -05:00
}
2023-06-14 02:18:53 -05:00
2017-01-11 11:57:47 -06:00
bool isParallel ( ) const
{ return grid_ . comm ( ) . size ( ) > 1 ; }
2016-11-08 01:46:42 -06:00
2023-06-14 02:18:53 -05:00
2016-08-08 08:26:09 -05:00
const EclipseState & eclState ( ) const
2024-02-06 03:05:36 -06:00
{ return simulator_ . vanguard ( ) . eclState ( ) ; }
2016-08-08 08:26:09 -05:00
2023-06-14 02:18:53 -05:00
2016-06-06 08:40:06 -05:00
/// Called once before each time step.
/// \param[in] timer simulation timer
2020-12-10 06:39:01 -06:00
SimulatorReportSingle prepareStep ( const SimulatorTimerInterface & timer )
2016-06-06 08:40:06 -05:00
{
2020-12-10 06:39:01 -06:00
SimulatorReportSingle report ;
Dune : : Timer perfTimer ;
perfTimer . start ( ) ;
2024-02-06 03:05:36 -06:00
// update the solution variables in the model
2024-09-29 15:30:01 -05:00
int lastStepFailed = timer . lastStepFailed ( ) ;
if ( grid_ . comm ( ) . size ( ) > 1 & & lastStepFailed ! = grid_ . comm ( ) . min ( lastStepFailed ) ) {
OPM_THROW ( std : : runtime_error , fmt : : format ( " Misalignment of the parallel simulation run in prepareStep - the previous step succeeded on rank {} but failed on the other ranks. " , grid_ . comm ( ) . rank ( ) ) ) ;
}
if ( lastStepFailed ) {
2024-02-06 03:05:36 -06:00
simulator_ . model ( ) . updateFailed ( ) ;
2017-11-08 05:17:31 -06:00
} else {
2024-02-06 03:05:36 -06:00
simulator_ . model ( ) . advanceTimeLevel ( ) ;
2017-11-08 05:17:31 -06:00
}
2021-01-29 05:30:34 -06:00
// Set the timestep size, episode index, and non-linear iteration index
2024-02-06 03:05:36 -06:00
// for the model explicitly. The model needs to know the report step/episode index
2021-01-29 05:30:34 -06:00
// because of timing dependent data despite the fact that flow uses its
// own time stepper. (The length of the episode does not matter, though.)
2024-02-06 03:05:36 -06:00
simulator_ . setTime ( timer . simulationTimeElapsed ( ) ) ;
simulator_ . setTimeStepSize ( timer . currentStepLength ( ) ) ;
simulator_ . model ( ) . newtonMethod ( ) . setIterationIndex ( 0 ) ;
2017-11-24 03:49:23 -06:00
2024-02-06 03:05:36 -06:00
simulator_ . problem ( ) . beginTimeStep ( ) ;
2021-01-29 05:30:34 -06:00
2024-02-06 03:05:36 -06:00
unsigned numDof = simulator_ . model ( ) . numGridDof ( ) ;
2017-06-21 09:26:06 -05:00
wasSwitched_ . resize ( numDof ) ;
std : : fill ( wasSwitched_ . begin ( ) , wasSwitched_ . end ( ) , false ) ;
2017-11-08 06:57:36 -06:00
2017-11-24 03:49:23 -06:00
if ( param_ . update_equations_scaling_ ) {
2023-03-03 06:45:36 -06:00
OpmLog : : error ( " Equation scaling not supported " ) ;
2017-11-24 03:49:23 -06:00
//updateEquationsScaling();
}
2023-06-14 02:18:53 -05:00
2023-07-04 08:15:29 -05:00
if ( nlddSolver_ ) {
nlddSolver_ - > prepareStep ( ) ;
2023-06-14 02:18:53 -05:00
}
2020-12-10 06:39:01 -06:00
report . pre_post_time + = perfTimer . stop ( ) ;
2023-12-11 04:26:43 -06:00
auto getIdx = [ ] ( unsigned phaseIdx ) - > int
{
if ( FluidSystem : : phaseIsActive ( phaseIdx ) ) {
const unsigned sIdx = FluidSystem : : solventComponentIndex ( phaseIdx ) ;
return Indices : : canonicalToActiveComponentIndex ( sIdx ) ;
}
return - 1 ;
} ;
2024-02-06 03:05:36 -06:00
const auto & schedule = simulator_ . vanguard ( ) . schedule ( ) ;
rst_conv_ . init ( simulator_ . vanguard ( ) . globalNumCells ( ) ,
2023-12-11 04:26:43 -06:00
schedule [ timer . reportStepNum ( ) ] . rst_config ( ) ,
{ getIdx ( FluidSystem : : oilPhaseIdx ) ,
getIdx ( FluidSystem : : gasPhaseIdx ) ,
2024-01-22 02:58:06 -06:00
getIdx ( FluidSystem : : waterPhaseIdx ) ,
contiPolymerEqIdx ,
contiBrineEqIdx ,
contiSolventEqIdx } ) ;
2023-12-11 04:26:43 -06:00
2020-12-10 06:39:01 -06:00
return report ;
2016-06-06 08:40:06 -05:00
}
2023-07-04 08:37:50 -05:00
void initialLinearization ( SimulatorReportSingle & report ,
const int iteration ,
const int minIter ,
2024-02-09 04:20:47 -06:00
const int maxIter ,
2023-07-04 08:37:50 -05:00
const SimulatorTimerInterface & timer )
{
// ----------- Set up reports and timer -----------
failureReport_ = SimulatorReportSingle ( ) ;
Dune : : Timer perfTimer ;
perfTimer . start ( ) ;
report . total_linearizations = 1 ;
// ----------- Assemble -----------
try {
report + = assembleReservoir ( timer , iteration ) ;
report . assemble_time + = perfTimer . stop ( ) ;
}
catch ( . . . ) {
report . assemble_time + = perfTimer . stop ( ) ;
failureReport_ + = report ;
throw ; // continue throwing the stick
}
// ----------- Check if converged -----------
2024-02-21 02:45:18 -06:00
std : : vector < Scalar > residual_norms ;
2023-07-04 08:37:50 -05:00
perfTimer . reset ( ) ;
perfTimer . start ( ) ;
// the step is not considered converged until at least minIter iterations is done
{
2024-02-09 04:20:47 -06:00
auto convrep = getConvergence ( timer , iteration , maxIter , residual_norms ) ;
2024-01-05 08:43:34 -06:00
report . converged = convrep . converged ( ) & & iteration > = minIter ;
2023-07-04 08:37:50 -05:00
ConvergenceReport : : Severity severity = convrep . severityOfWorstFailure ( ) ;
convergence_reports_ . back ( ) . report . push_back ( std : : move ( convrep ) ) ;
// Throw if any NaN or too large residual found.
if ( severity = = ConvergenceReport : : Severity : : NotANumber ) {
2024-09-11 09:37:00 -05:00
failureReport_ + = report ;
2024-06-20 03:40:37 -05:00
OPM_THROW_PROBLEM ( NumericalProblem , " NaN residual found! " ) ;
2023-07-04 08:37:50 -05:00
} else if ( severity = = ConvergenceReport : : Severity : : TooLarge ) {
2024-09-11 09:37:00 -05:00
failureReport_ + = report ;
2023-07-04 08:37:50 -05:00
OPM_THROW_NOLOG ( NumericalProblem , " Too large residual found! " ) ;
2024-09-10 08:50:09 -05:00
} else if ( severity = = ConvergenceReport : : Severity : : ConvergenceMonitorFailure ) {
2024-09-11 09:37:00 -05:00
failureReport_ + = report ;
2024-09-10 08:50:09 -05:00
OPM_THROW_PROBLEM ( ConvergenceMonitorFailure , " Total penalty count exceeded cut-off-limit of " + std : : to_string ( param_ . convergence_monitoring_cutoff_ ) ) ;
2023-07-04 08:37:50 -05:00
}
}
report . update_time + = perfTimer . stop ( ) ;
residual_norms_history_ . push_back ( residual_norms ) ;
}
2016-06-06 08:40:06 -05:00
/// Called once per nonlinear iteration.
/// This model will perform a Newton-Raphson update, changing reservoir_state
/// and well_state. It will also use the nonlinear_solver to do relaxation of
/// updates if necessary.
/// \param[in] iteration should be 0 for the first call of a new timestep
/// \param[in] timer simulation timer
/// \param[in] nonlinear_solver nonlinear solver used (for oscillation/relaxation control)
template < class NonlinearSolverType >
2020-05-07 09:13:39 -05:00
SimulatorReportSingle nonlinearIteration ( const int iteration ,
const SimulatorTimerInterface & timer ,
NonlinearSolverType & nonlinear_solver )
2016-06-06 08:40:06 -05:00
{
if ( iteration = = 0 ) {
// For each iteration we store in a vector the norms of the residual of
// the mass balance for each active phase, the well flux and the well equations.
residual_norms_history_ . clear ( ) ;
2024-09-09 10:38:58 -05:00
total_penaltyCard_ . reset ( ) ;
2024-09-13 08:56:40 -05:00
prev_above_tolerance_ = 0 ;
2024-09-09 11:32:05 -05:00
prev_distance_ = std : : numeric_limits < double > : : infinity ( ) ;
2016-06-06 08:40:06 -05:00
current_relaxation_ = 1.0 ;
2016-09-07 04:21:51 -05:00
dx_old_ = 0.0 ;
2018-11-22 04:14:39 -06:00
convergence_reports_ . push_back ( { timer . reportStepNum ( ) , timer . currentStepNum ( ) , { } } ) ;
convergence_reports_ . back ( ) . report . reserve ( 11 ) ;
2016-06-06 08:40:06 -05:00
}
2016-11-17 04:55:30 -06:00
2023-12-11 04:26:43 -06:00
SimulatorReportSingle result ;
2023-10-10 11:25:24 -05:00
if ( ( this - > param_ . nonlinear_solver_ ! = " nldd " ) | |
( iteration < this - > param_ . nldd_num_initial_newton_iter_ ) )
{
2023-12-11 04:26:43 -06:00
result = this - > nonlinearIterationNewton ( iteration , timer , nonlinear_solver ) ;
2023-06-14 02:18:53 -05:00
}
2023-10-10 11:25:24 -05:00
else {
2023-12-11 04:26:43 -06:00
result = this - > nlddSolver_ - > nonlinearIterationNldd ( iteration , timer , nonlinear_solver ) ;
2023-06-14 02:18:53 -05:00
}
2023-12-11 04:26:43 -06:00
2024-02-06 03:05:36 -06:00
rst_conv_ . update ( simulator_ . model ( ) . linearizer ( ) . residual ( ) ) ;
2023-12-11 04:26:43 -06:00
return result ;
2023-06-14 02:18:53 -05:00
}
template < class NonlinearSolverType >
SimulatorReportSingle nonlinearIterationNewton ( const int iteration ,
const SimulatorTimerInterface & timer ,
NonlinearSolverType & nonlinear_solver )
{
// ----------- Set up reports and timer -----------
SimulatorReportSingle report ;
Dune : : Timer perfTimer ;
2024-02-09 04:20:47 -06:00
this - > initialLinearization ( report , iteration , nonlinear_solver . minIter ( ) , nonlinear_solver . maxIter ( ) , timer ) ;
2023-06-14 02:18:53 -05:00
// ----------- If not converged, solve linear system and do Newton update -----------
2016-12-05 05:14:28 -06:00
if ( ! report . converged ) {
2016-11-23 14:44:33 -06:00
perfTimer . reset ( ) ;
perfTimer . start ( ) ;
report . total_newton_iterations = 1 ;
2016-11-17 04:55:30 -06:00
2016-06-06 08:40:06 -05:00
// Compute the nonlinear update.
2024-02-06 03:05:36 -06:00
unsigned nc = simulator_ . model ( ) . numGridDof ( ) ;
2016-09-07 03:13:24 -05:00
BVector x ( nc ) ;
2016-11-17 04:55:30 -06:00
2019-04-05 04:21:36 -05:00
// Solve the linear system.
linear_solve_setup_time_ = 0.0 ;
2016-11-23 14:44:33 -06:00
try {
2023-06-14 02:18:53 -05:00
// Apply the Schur complement of the well model to
// the reservoir linearized equations.
2021-10-08 06:45:57 -05:00
// Note that linearize may throw for MSwells.
2024-02-06 03:05:36 -06:00
wellModel ( ) . linearize ( simulator ( ) . model ( ) . linearizer ( ) . jacobian ( ) ,
simulator ( ) . model ( ) . linearizer ( ) . residual ( ) ) ;
2021-10-08 06:45:57 -05:00
2023-06-14 02:18:53 -05:00
// ---- Solve linear system ----
2017-07-20 07:23:29 -05:00
solveJacobianSystem ( x ) ;
2023-06-14 02:18:53 -05:00
2019-04-05 04:21:36 -05:00
report . linear_solve_setup_time + = linear_solve_setup_time_ ;
2016-11-23 14:44:33 -06:00
report . linear_solve_time + = perfTimer . stop ( ) ;
report . total_linear_iterations + = linearIterationsLastSolve ( ) ;
}
catch ( . . . ) {
2019-04-05 04:21:36 -05:00
report . linear_solve_setup_time + = linear_solve_setup_time_ ;
2016-11-23 14:44:33 -06:00
report . linear_solve_time + = perfTimer . stop ( ) ;
report . total_linear_iterations + = linearIterationsLastSolve ( ) ;
2017-04-10 08:55:30 -05:00
failureReport_ + = report ;
2016-11-23 14:44:33 -06:00
throw ; // re-throw up
}
perfTimer . reset ( ) ;
perfTimer . start ( ) ;
2016-06-06 08:40:06 -05:00
2017-09-22 06:41:05 -05:00
// handling well state update before oscillation treatment is a decision based
// on observation to avoid some big performance degeneration under some circumstances.
// there is no theorectical explanation which way is better for sure.
2018-08-16 04:51:36 -05:00
wellModel ( ) . postSolve ( x ) ;
2017-09-22 06:41:05 -05:00
2017-06-21 09:26:06 -05:00
if ( param_ . use_update_stabilization_ ) {
2017-08-18 01:37:25 -05:00
// Stabilize the nonlinear update.
bool isOscillate = false ;
bool isStagnate = false ;
2023-06-14 02:18:53 -05:00
nonlinear_solver . detectOscillations ( residual_norms_history_ , residual_norms_history_ . size ( ) - 1 , isOscillate , isStagnate ) ;
2017-08-18 01:37:25 -05:00
if ( isOscillate ) {
current_relaxation_ - = nonlinear_solver . relaxIncrement ( ) ;
current_relaxation_ = std : : max ( current_relaxation_ , nonlinear_solver . relaxMax ( ) ) ;
if ( terminalOutputEnabled ( ) ) {
std : : string msg = " Oscillating behavior detected: Relaxation set to "
+ std : : to_string ( current_relaxation_ ) ;
OpmLog : : info ( msg ) ;
}
2016-06-06 08:40:06 -05:00
}
2017-08-18 01:37:25 -05:00
nonlinear_solver . stabilizeNonlinearUpdate ( x , dx_old_ , current_relaxation_ ) ;
2016-06-06 08:40:06 -05:00
}
2023-06-14 02:18:53 -05:00
// ---- Newton update ----
2016-11-23 14:44:33 -06:00
// Apply the update, with considering model-dependent limitations and
// chopping of the update.
2018-08-03 05:27:20 -05:00
updateSolution ( x ) ;
2017-07-21 08:30:34 -05:00
2016-11-23 14:44:33 -06:00
report . update_time + = perfTimer . stop ( ) ;
2016-11-17 04:55:30 -06:00
}
2016-11-23 14:44:33 -06:00
return report ;
2016-06-06 08:40:06 -05:00
}
2016-11-17 04:55:30 -06:00
2023-06-14 02:18:53 -05:00
2016-06-06 08:40:06 -05:00
/// Called once after each time step.
/// In this class, this function does nothing.
/// \param[in] timer simulation timer
2021-08-02 07:55:41 -05:00
SimulatorReportSingle afterStep ( const SimulatorTimerInterface & )
2016-06-06 08:40:06 -05:00
{
2020-12-10 06:39:01 -06:00
SimulatorReportSingle report ;
Dune : : Timer perfTimer ;
perfTimer . start ( ) ;
2024-02-06 03:05:36 -06:00
simulator_ . problem ( ) . endTimeStep ( ) ;
simulator_ . problem ( ) . setConvData ( rst_conv_ . getData ( ) ) ;
2020-12-10 06:39:01 -06:00
report . pre_post_time + = perfTimer . stop ( ) ;
return report ;
2016-06-06 08:40:06 -05:00
}
/// Assemble the residual and Jacobian of the nonlinear system.
2020-05-07 09:13:39 -05:00
SimulatorReportSingle assembleReservoir ( const SimulatorTimerInterface & /* timer */ ,
const int iterationIdx )
2016-06-06 08:40:06 -05:00
{
// -------- Mass balance equations --------
2024-02-06 03:05:36 -06:00
simulator_ . model ( ) . newtonMethod ( ) . setIterationIndex ( iterationIdx ) ;
simulator_ . problem ( ) . beginIteration ( ) ;
simulator_ . model ( ) . linearizer ( ) . linearizeDomain ( ) ;
simulator_ . problem ( ) . endIteration ( ) ;
2023-06-14 02:18:53 -05:00
return wellModel ( ) . lastReport ( ) ;
}
2018-06-27 05:13:49 -05:00
2017-06-21 09:26:06 -05:00
// compute the "relative" change of the solution between time steps
2024-02-21 02:45:18 -06:00
Scalar relativeChange ( ) const
2016-06-06 08:40:06 -05:00
{
2017-06-21 09:26:06 -05:00
Scalar resultDelta = 0.0 ;
Scalar resultDenom = 0.0 ;
2016-06-06 08:40:06 -05:00
2024-02-06 03:05:36 -06:00
const auto & elemMapper = simulator_ . model ( ) . elementMapper ( ) ;
const auto & gridView = simulator_ . gridView ( ) ;
2022-10-12 07:27:20 -05:00
for ( const auto & elem : elements ( gridView , Dune : : Partitions : : interior ) ) {
2018-05-29 07:16:19 -05:00
unsigned globalElemIdx = elemMapper . index ( elem ) ;
2024-02-06 03:05:36 -06:00
const auto & priVarsNew = simulator_ . model ( ) . solution ( /*timeIdx=*/ 0 ) [ globalElemIdx ] ;
2016-06-06 08:40:06 -05:00
2017-06-21 09:26:06 -05:00
Scalar pressureNew ;
2018-05-29 07:16:19 -05:00
pressureNew = priVarsNew [ Indices : : pressureSwitchIdx ] ;
2016-06-06 08:40:06 -05:00
2018-05-29 07:16:19 -05:00
Scalar saturationsNew [ FluidSystem : : numPhases ] = { 0.0 } ;
2017-08-08 01:07:09 -05:00
Scalar oilSaturationNew = 1.0 ;
2022-11-24 01:27:55 -06:00
if ( FluidSystem : : phaseIsActive ( FluidSystem : : waterPhaseIdx ) & &
FluidSystem : : numActivePhases ( ) > 1 & &
2022-11-25 05:36:40 -06:00
priVarsNew . primaryVarsMeaningWater ( ) = = PrimaryVariables : : WaterMeaning : : Sw ) {
2022-11-25 02:29:38 -06:00
saturationsNew [ FluidSystem : : waterPhaseIdx ] = priVarsNew [ Indices : : waterSwitchIdx ] ;
2017-08-08 01:07:09 -05:00
oilSaturationNew - = saturationsNew [ FluidSystem : : waterPhaseIdx ] ;
}
2017-10-16 09:46:28 -05:00
2021-10-06 12:32:35 -05:00
if ( FluidSystem : : phaseIsActive ( FluidSystem : : gasPhaseIdx ) & &
FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) & &
2022-11-25 05:36:40 -06:00
priVarsNew . primaryVarsMeaningGas ( ) = = PrimaryVariables : : GasMeaning : : Sg ) {
2021-08-01 11:09:54 -05:00
assert ( Indices : : compositionSwitchIdx > = 0 ) ;
2017-08-08 01:07:09 -05:00
saturationsNew [ FluidSystem : : gasPhaseIdx ] = priVarsNew [ Indices : : compositionSwitchIdx ] ;
2017-10-16 09:46:28 -05:00
oilSaturationNew - = saturationsNew [ FluidSystem : : gasPhaseIdx ] ;
2017-08-08 01:07:09 -05:00
}
2017-10-16 09:46:28 -05:00
2017-08-08 01:07:09 -05:00
if ( FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) ) {
saturationsNew [ FluidSystem : : oilPhaseIdx ] = oilSaturationNew ;
}
2016-06-06 08:40:06 -05:00
2024-02-06 03:05:36 -06:00
const auto & priVarsOld = simulator_ . model ( ) . solution ( /*timeIdx=*/ 1 ) [ globalElemIdx ] ;
2016-06-06 08:40:06 -05:00
2017-06-21 09:26:06 -05:00
Scalar pressureOld ;
2017-08-08 01:07:09 -05:00
pressureOld = priVarsOld [ Indices : : pressureSwitchIdx ] ;
2016-06-06 08:40:06 -05:00
2017-06-21 09:26:06 -05:00
Scalar saturationsOld [ FluidSystem : : numPhases ] = { 0.0 } ;
2017-08-08 01:07:09 -05:00
Scalar oilSaturationOld = 1.0 ;
2019-10-11 08:29:49 -05:00
2019-10-11 08:57:51 -05:00
// NB fix me! adding pressures changes to satutation changes does not make sense
2019-10-11 08:29:49 -05:00
Scalar tmp = pressureNew - pressureOld ;
resultDelta + = tmp * tmp ;
resultDenom + = pressureNew * pressureNew ;
2019-10-11 08:57:51 -05:00
if ( FluidSystem : : numActivePhases ( ) > 1 ) {
2022-11-25 05:36:40 -06:00
if ( priVarsOld . primaryVarsMeaningWater ( ) = = PrimaryVariables : : WaterMeaning : : Sw ) {
2022-11-25 02:29:38 -06:00
saturationsOld [ FluidSystem : : waterPhaseIdx ] = priVarsOld [ Indices : : waterSwitchIdx ] ;
2019-10-11 08:57:51 -05:00
oilSaturationOld - = saturationsOld [ FluidSystem : : waterPhaseIdx ] ;
}
2022-11-25 05:36:40 -06:00
if ( priVarsOld . primaryVarsMeaningGas ( ) = = PrimaryVariables : : GasMeaning : : Sg )
2019-10-11 08:57:51 -05:00
{
2021-08-01 11:09:54 -05:00
assert ( Indices : : compositionSwitchIdx > = 0 ) ;
2019-10-11 08:57:51 -05:00
saturationsOld [ FluidSystem : : gasPhaseIdx ] = priVarsOld [ Indices : : compositionSwitchIdx ] ;
oilSaturationOld - = saturationsOld [ FluidSystem : : gasPhaseIdx ] ;
}
if ( FluidSystem : : phaseIsActive ( FluidSystem : : oilPhaseIdx ) ) {
saturationsOld [ FluidSystem : : oilPhaseIdx ] = oilSaturationOld ;
}
for ( unsigned phaseIdx = 0 ; phaseIdx < FluidSystem : : numPhases ; + + phaseIdx ) {
2019-10-11 13:18:53 -05:00
Scalar tmpSat = saturationsNew [ phaseIdx ] - saturationsOld [ phaseIdx ] ;
resultDelta + = tmpSat * tmpSat ;
2019-10-11 08:57:51 -05:00
resultDenom + = saturationsNew [ phaseIdx ] * saturationsNew [ phaseIdx ] ;
assert ( std : : isfinite ( resultDelta ) ) ;
assert ( std : : isfinite ( resultDenom ) ) ;
}
}
2016-06-06 08:40:06 -05:00
}
2017-06-21 09:26:06 -05:00
resultDelta = gridView . comm ( ) . sum ( resultDelta ) ;
resultDenom = gridView . comm ( ) . sum ( resultDenom ) ;
2018-05-29 07:16:19 -05:00
if ( resultDenom > 0.0 )
2018-06-06 03:59:41 -05:00
return resultDelta / resultDenom ;
2018-05-29 07:16:19 -05:00
return 0.0 ;
2016-06-06 08:40:06 -05:00
}
/// Number of linear iterations used in last call to solveJacobianSystem().
int linearIterationsLastSolve ( ) const
{
2024-02-06 03:05:36 -06:00
return simulator_ . model ( ) . newtonMethod ( ) . linearSolver ( ) . iterations ( ) ;
2018-11-06 07:14:50 -06:00
}
2023-06-14 02:18:53 -05:00
2023-07-04 08:41:57 -05:00
// Obtain reference to linear solver setup time
double & linearSolveSetupTime ( )
{
return linear_solve_setup_time_ ;
}
2016-06-06 08:40:06 -05:00
/// Solve the Jacobian system Jx = r where J is the Jacobian and
/// r is the residual.
2018-11-12 04:03:54 -06:00
void solveJacobianSystem ( BVector & x )
2016-06-06 08:40:06 -05:00
{
2024-02-06 03:11:57 -06:00
auto & jacobian = simulator_ . model ( ) . linearizer ( ) . jacobian ( ) . istlMatrix ( ) ;
2024-02-06 03:11:57 -06:00
auto & residual = simulator_ . model ( ) . linearizer ( ) . residual ( ) ;
2024-02-06 03:11:57 -06:00
auto & linSolver = simulator_ . model ( ) . newtonMethod ( ) . linearSolver ( ) ;
2018-11-05 08:54:48 -06:00
2024-02-06 03:11:57 -06:00
const int numSolvers = linSolver . numAvailableSolvers ( ) ;
if ( ( numSolvers > 1 ) & & ( linSolver . getSolveCount ( ) % 100 = = 0 ) ) {
2016-11-02 07:10:44 -05:00
2022-11-09 09:54:07 -06:00
if ( terminal_output_ ) {
2023-08-31 03:04:18 -05:00
OpmLog : : debug ( " \n Running speed test for comparing available linear solvers. " ) ;
2022-11-09 09:54:07 -06:00
}
2023-08-31 03:04:18 -05:00
Dune : : Timer perfTimer ;
std : : vector < double > times ( numSolvers ) ;
std : : vector < double > setupTimes ( numSolvers ) ;
2022-11-09 09:54:07 -06:00
x = 0.0 ;
2023-08-31 03:04:18 -05:00
std : : vector < BVector > x_trial ( numSolvers , x ) ;
for ( int solver = 0 ; solver < numSolvers ; + + solver ) {
BVector x0 ( x ) ;
2024-02-06 03:11:57 -06:00
linSolver . setActiveSolver ( solver ) ;
2023-08-31 03:04:18 -05:00
perfTimer . start ( ) ;
2024-02-06 03:11:57 -06:00
linSolver . prepare ( jacobian , residual ) ;
2023-08-31 03:04:18 -05:00
setupTimes [ solver ] = perfTimer . stop ( ) ;
perfTimer . reset ( ) ;
2024-02-06 03:11:57 -06:00
linSolver . setResidual ( residual ) ;
2023-08-31 03:04:18 -05:00
perfTimer . start ( ) ;
2024-02-06 03:11:57 -06:00
linSolver . solve ( x_trial [ solver ] ) ;
2023-08-31 03:04:18 -05:00
times [ solver ] = perfTimer . stop ( ) ;
perfTimer . reset ( ) ;
if ( terminal_output_ ) {
OpmLog : : debug ( fmt : : format ( " Solver time {}: {} " , solver , times [ solver ] ) ) ;
}
2022-11-09 09:54:07 -06:00
}
2023-08-31 03:04:18 -05:00
int fastest_solver = std : : min_element ( times . begin ( ) , times . end ( ) ) - times . begin ( ) ;
// Use timing on rank 0 to determine fastest, must be consistent across ranks.
2022-11-09 09:54:07 -06:00
grid_ . comm ( ) . broadcast ( & fastest_solver , 1 , 0 ) ;
2023-08-31 03:04:18 -05:00
linear_solve_setup_time_ = setupTimes [ fastest_solver ] ;
x = x_trial [ fastest_solver ] ;
2024-02-06 03:11:57 -06:00
linSolver . setActiveSolver ( fastest_solver ) ;
2022-11-09 09:54:07 -06:00
} else {
// set initial guess
x = 0.0 ;
Dune : : Timer perfTimer ;
perfTimer . start ( ) ;
2024-02-06 03:11:57 -06:00
linSolver . prepare ( jacobian , residual ) ;
2022-11-09 09:54:07 -06:00
linear_solve_setup_time_ = perfTimer . stop ( ) ;
2024-02-06 03:11:57 -06:00
linSolver . setResidual ( residual ) ;
2022-11-09 09:54:07 -06:00
// actually, the error needs to be calculated after setResidual in order to
// account for parallelization properly. since the residual of ECFV
// discretizations does not need to be synchronized across processes to be
// consistent, this is not relevant for OPM-flow...
2024-02-06 03:11:57 -06:00
linSolver . solve ( x ) ;
2022-11-09 09:54:07 -06:00
}
2018-11-12 04:03:54 -06:00
}
2016-08-25 08:25:01 -05:00
2018-08-03 05:27:20 -05:00
/// Apply an update to the primary variables.
void updateSolution ( const BVector & dx )
2016-06-06 08:40:06 -05:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( updateSolution ) ;
2024-02-06 03:11:57 -06:00
auto & newtonMethod = simulator_ . model ( ) . newtonMethod ( ) ;
2024-02-06 03:05:36 -06:00
SolutionVector & solution = simulator_ . model ( ) . solution ( /*timeIdx=*/ 0 ) ;
2016-09-07 03:13:24 -05:00
2024-02-06 03:11:57 -06:00
newtonMethod . update_ ( /*nextSolution=*/ solution ,
/*curSolution=*/ solution ,
/*update=*/ dx ,
/*resid=*/ dx ) ; // the update routines of the black
// oil model do not care about the
// residual
2016-06-06 08:40:06 -05:00
2018-08-03 05:27:20 -05:00
// if the solution is updated, the intensive quantities need to be recalculated
2023-02-15 02:41:37 -06:00
{
OPM_TIMEBLOCK ( invalidateAndUpdateIntensiveQuantities ) ;
2024-02-06 03:05:36 -06:00
simulator_ . model ( ) . invalidateAndUpdateIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
2023-02-15 02:41:37 -06:00
}
2016-06-06 08:40:06 -05:00
}
/// Return true if output to cout is wanted.
bool terminalOutputEnabled ( ) const
{
return terminal_output_ ;
}
2024-02-21 02:45:18 -06:00
std : : tuple < Scalar , Scalar > convergenceReduction ( Parallel : : Communication comm ,
const Scalar pvSumLocal ,
const Scalar numAquiferPvSumLocal ,
2021-10-25 09:37:25 -05:00
std : : vector < Scalar > & R_sum ,
std : : vector < Scalar > & maxCoeff ,
std : : vector < Scalar > & B_avg )
2016-11-02 10:59:08 -05:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( convergenceReduction ) ;
2017-01-11 05:20:32 -06:00
// Compute total pore volume (use only owned entries)
2024-02-21 02:45:18 -06:00
Scalar pvSum = pvSumLocal ;
Scalar numAquiferPvSum = numAquiferPvSumLocal ;
2016-11-02 10:59:08 -05:00
if ( comm . size ( ) > 1 )
{
// global reduction
std : : vector < Scalar > sumBuffer ;
std : : vector < Scalar > maxBuffer ;
2017-06-22 07:37:43 -05:00
const int numComp = B_avg . size ( ) ;
2021-10-25 09:37:25 -05:00
sumBuffer . reserve ( 2 * numComp + 2 ) ; // +2 for (numAquifer)pvSum
2017-08-14 06:36:13 -05:00
maxBuffer . reserve ( numComp ) ;
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx )
2016-11-02 10:59:08 -05:00
{
2017-05-03 06:34:15 -05:00
sumBuffer . push_back ( B_avg [ compIdx ] ) ;
sumBuffer . push_back ( R_sum [ compIdx ] ) ;
maxBuffer . push_back ( maxCoeff [ compIdx ] ) ;
2016-11-02 10:59:08 -05:00
}
// Compute total pore volume
sumBuffer . push_back ( pvSum ) ;
2021-10-25 09:37:25 -05:00
sumBuffer . push_back ( numAquiferPvSum ) ;
2016-11-02 10:59:08 -05:00
// compute global sum
comm . sum ( sumBuffer . data ( ) , sumBuffer . size ( ) ) ;
// compute global max
comm . max ( maxBuffer . data ( ) , maxBuffer . size ( ) ) ;
// restore values to local variables
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 , buffIdx = 0 ; compIdx < numComp ; + + compIdx , + + buffIdx )
2016-11-02 10:59:08 -05:00
{
2017-05-03 06:34:15 -05:00
B_avg [ compIdx ] = sumBuffer [ buffIdx ] ;
2016-11-02 10:59:08 -05:00
+ + buffIdx ;
2017-05-03 06:34:15 -05:00
R_sum [ compIdx ] = sumBuffer [ buffIdx ] ;
2017-08-14 06:36:13 -05:00
}
2017-08-18 09:25:05 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx )
2017-08-14 06:36:13 -05:00
{
2017-08-18 09:25:05 -05:00
maxCoeff [ compIdx ] = maxBuffer [ compIdx ] ;
2016-11-02 10:59:08 -05:00
}
// restore global pore volume
2021-10-25 09:37:25 -05:00
pvSum = sumBuffer [ sumBuffer . size ( ) - 2 ] ;
numAquiferPvSum = sumBuffer . back ( ) ;
2016-11-02 10:59:08 -05:00
}
// return global pore volume
2021-10-25 09:37:25 -05:00
return { pvSum , numAquiferPvSum } ;
2016-11-02 10:59:08 -05:00
}
2016-06-06 08:40:06 -05:00
2021-10-25 09:37:25 -05:00
/// \brief Get reservoir quantities on this process needed for convergence calculations.
/// \return A pair of the local pore volume of interior cells and the pore volumes
/// of the cells associated with a numerical aquifer.
2024-02-21 02:45:18 -06:00
std : : pair < Scalar , Scalar > localConvergenceData ( std : : vector < Scalar > & R_sum ,
2023-06-26 04:13:45 -05:00
std : : vector < Scalar > & maxCoeff ,
std : : vector < Scalar > & B_avg ,
std : : vector < int > & maxCoeffCell )
2016-06-06 08:40:06 -05:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( localConvergenceData ) ;
2024-02-21 02:45:18 -06:00
Scalar pvSumLocal = 0.0 ;
Scalar numAquiferPvSumLocal = 0.0 ;
2024-02-06 03:11:57 -06:00
const auto & model = simulator_ . model ( ) ;
2024-02-06 03:11:57 -06:00
const auto & problem = simulator_ . problem ( ) ;
2016-10-20 10:47:45 -05:00
2024-02-06 03:11:57 -06:00
const auto & residual = simulator_ . model ( ) . linearizer ( ) . residual ( ) ;
2016-06-06 08:40:06 -05:00
2024-02-06 03:05:36 -06:00
ElementContext elemCtx ( simulator_ ) ;
const auto & gridView = simulator ( ) . gridView ( ) ;
2023-07-04 06:33:23 -05:00
IsNumericalAquiferCell isNumericalAquiferCell ( gridView . grid ( ) ) ;
2021-09-20 04:12:27 -05:00
OPM_BEGIN_PARALLEL_TRY_CATCH ( ) ;
2022-10-12 07:27:20 -05:00
for ( const auto & elem : elements ( gridView , Dune : : Partitions : : interior ) ) {
2017-03-20 15:01:36 -05:00
elemCtx . updatePrimaryStencil ( elem ) ;
2023-06-26 04:13:45 -05:00
elemCtx . updatePrimaryIntensiveQuantities ( /*timeIdx=*/ 0 ) ;
2023-06-14 02:18:53 -05:00
2017-03-20 15:01:36 -05:00
const unsigned cell_idx = elemCtx . globalSpaceIndex ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
2023-06-26 04:13:45 -05:00
const auto & intQuants = elemCtx . intensiveQuantities ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
2017-03-20 15:01:36 -05:00
const auto & fs = intQuants . fluidState ( ) ;
2016-10-20 10:47:45 -05:00
2024-02-06 03:11:57 -06:00
const auto pvValue = problem . referencePorosity ( cell_idx , /*timeIdx=*/ 0 ) *
2024-02-06 03:11:57 -06:00
model . dofTotalVolume ( cell_idx ) ;
2017-06-22 07:37:43 -05:00
pvSumLocal + = pvValue ;
2023-07-04 06:33:23 -05:00
if ( isNumericalAquiferCell ( elem ) )
2021-10-25 09:37:25 -05:00
{
numAquiferPvSumLocal + = pvValue ;
}
2024-02-06 03:11:57 -06:00
this - > getMaxCoeff ( cell_idx , intQuants , fs , residual , pvValue ,
2023-07-03 03:11:45 -05:00
B_avg , R_sum , maxCoeff , maxCoeffCell ) ;
2023-06-14 02:18:53 -05:00
}
2024-01-31 07:14:50 -06:00
OPM_END_PARALLEL_TRY_CATCH ( " BlackoilModel::localConvergenceData() failed: " , grid_ . comm ( ) ) ;
2023-06-14 02:18:53 -05:00
// compute local average in terms of global number of elements
const int bSize = B_avg . size ( ) ;
for ( int i = 0 ; i < bSize ; + + i )
{
B_avg [ i ] / = Scalar ( global_nc_ ) ;
}
return { pvSumLocal , numAquiferPvSumLocal } ;
}
2024-05-07 08:49:12 -05:00
/// \brief Compute pore-volume/cell count split among "converged",
/// "relaxed converged", "unconverged" cells based on CNV point
/// measures.
std : : pair < std : : vector < double > , std : : vector < int > >
characteriseCnvPvSplit ( const std : : vector < Scalar > & B_avg , const double dt )
2020-07-02 04:52:44 -05:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( computeCnvErrorPv ) ;
2020-07-02 04:52:44 -05:00
2024-05-07 08:49:12 -05:00
// 0: cnv <= tolerance_cnv
// 1: tolerance_cnv < cnv <= tolerance_cnv_relaxed
// 2: tolerance_cnv_relaxed < cnv
constexpr auto numPvGroups = std : : vector < double > : : size_type { 3 } ;
auto cnvPvSplit = std : : pair < std : : vector < double > , std : : vector < int > > {
std : : piecewise_construct ,
std : : forward_as_tuple ( numPvGroups ) ,
std : : forward_as_tuple ( numPvGroups )
} ;
auto maxCNV = [ & B_avg , dt ] ( const auto & residual , const double pvol )
2020-07-02 04:52:44 -05:00
{
2024-05-07 08:49:12 -05:00
return ( dt / pvol ) *
std : : inner_product ( residual . begin ( ) , residual . end ( ) ,
B_avg . begin ( ) , Scalar { 0 } ,
[ ] ( const Scalar m , const auto & x )
{
using std : : abs ;
return std : : max ( m , abs ( x ) ) ;
} , std : : multiplies < > { } ) ;
} ;
auto & [ splitPV , cellCntPV ] = cnvPvSplit ;
const auto & model = this - > simulator ( ) . model ( ) ;
const auto & problem = this - > simulator ( ) . problem ( ) ;
const auto & residual = model . linearizer ( ) . residual ( ) ;
const auto & gridView = this - > simulator ( ) . gridView ( ) ;
const IsNumericalAquiferCell isNumericalAquiferCell ( gridView . grid ( ) ) ;
ElementContext elemCtx ( this - > simulator ( ) ) ;
OPM_BEGIN_PARALLEL_TRY_CATCH ( ) ;
for ( const auto & elem : elements ( gridView , Dune : : Partitions : : interior ) ) {
2021-10-25 09:37:25 -05:00
// Skip cells of numerical Aquifer
2024-05-07 08:49:12 -05:00
if ( isNumericalAquiferCell ( elem ) ) {
2021-10-25 09:37:25 -05:00
continue ;
}
2024-05-07 08:49:12 -05:00
2020-07-02 04:52:44 -05:00
elemCtx . updatePrimaryStencil ( elem ) ;
2024-05-07 08:49:12 -05:00
2020-07-02 04:52:44 -05:00
const unsigned cell_idx = elemCtx . globalSpaceIndex ( /*spaceIdx=*/ 0 , /*timeIdx=*/ 0 ) ;
2024-05-07 08:49:12 -05:00
const auto pvValue = problem . referencePorosity ( cell_idx , /*timeIdx=*/ 0 )
* model . dofTotalVolume ( cell_idx ) ;
2020-07-02 04:52:44 -05:00
2024-05-07 08:49:12 -05:00
const auto maxCnv = maxCNV ( residual [ cell_idx ] , pvValue ) ;
2020-07-02 04:52:44 -05:00
2024-05-07 08:49:12 -05:00
const auto ix = ( maxCnv > this - > param_ . tolerance_cnv_ )
+ ( maxCnv > this - > param_ . tolerance_cnv_relaxed_ ) ;
splitPV [ ix ] + = static_cast < double > ( pvValue ) ;
+ + cellCntPV [ ix ] ;
2020-07-02 04:52:44 -05:00
}
2024-05-07 08:49:12 -05:00
OPM_END_PARALLEL_TRY_CATCH ( " BlackoilModel::characteriseCnvPvSplit() failed: " ,
this - > grid_ . comm ( ) ) ;
2021-09-20 04:12:27 -05:00
2024-05-07 08:49:12 -05:00
this - > grid_ . comm ( ) . sum ( splitPV . data ( ) , splitPV . size ( ) ) ;
this - > grid_ . comm ( ) . sum ( cellCntPV . data ( ) , cellCntPV . size ( ) ) ;
return cnvPvSplit ;
2020-07-02 04:52:44 -05:00
}
2023-05-03 06:17:20 -05:00
2024-05-07 08:49:12 -05:00
void updateTUNING ( const Tuning & tuning )
{
this - > param_ . tolerance_mb_ = tuning . XXXMBE ;
if ( terminal_output_ ) {
OpmLog : : debug ( fmt : : format ( " Setting BlackoilModel mass "
" balance limit (XXXMBE) to {:.2e} " ,
tuning . XXXMBE ) ) ;
2023-05-03 06:17:20 -05:00
}
}
2022-12-08 10:16:34 -06:00
ConvergenceReport getReservoirConvergence ( const double reportTime ,
const double dt ,
2018-11-15 13:38:25 -06:00
const int iteration ,
2024-02-09 04:20:47 -06:00
const int maxIter ,
2018-11-15 13:38:25 -06:00
std : : vector < Scalar > & B_avg ,
std : : vector < Scalar > & residual_norms )
2018-10-24 08:03:17 -05:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( getReservoirConvergence ) ;
2023-03-03 06:25:46 -06:00
using Vector = std : : vector < Scalar > ;
2017-08-14 06:36:13 -05:00
2024-05-07 08:49:12 -05:00
ConvergenceReport report { reportTime } ;
2018-10-24 08:03:17 -05:00
const int numComp = numEq ;
2024-05-07 08:49:12 -05:00
Vector R_sum ( numComp , Scalar { 0 } ) ;
Vector maxCoeff ( numComp , std : : numeric_limits < Scalar > : : lowest ( ) ) ;
2023-06-14 02:18:53 -05:00
std : : vector < int > maxCoeffCell ( numComp , - 1 ) ;
2024-05-07 08:49:12 -05:00
const auto [ pvSumLocal , numAquiferPvSumLocal ] =
2024-04-22 09:48:11 -05:00
this - > localConvergenceData ( R_sum , maxCoeff , B_avg , maxCoeffCell ) ;
2016-06-06 08:40:06 -05:00
2017-06-22 07:37:43 -05:00
// compute global sum and max of quantities
2024-05-07 08:49:12 -05:00
const auto & [ pvSum , numAquiferPvSum ] =
this - > convergenceReduction ( this - > grid_ . comm ( ) ,
pvSumLocal ,
numAquiferPvSumLocal ,
R_sum , maxCoeff , B_avg ) ;
report . setCnvPoreVolSplit ( this - > characteriseCnvPvSplit ( B_avg , dt ) ,
pvSum - numAquiferPvSum ) ;
// For each iteration, we need to determine whether to use the
// relaxed tolerances. To disable the usage of relaxed
// tolerances, you can set the relaxed tolerances as the strict
// tolerances. If min_strict_mb_iter = -1 (default) we use a
// relaxed tolerance for the mass balance for the last
// iterations. For positive values we use the relaxed tolerance
// after the given number of iterations
const bool relax_final_iteration_mb =
( this - > param_ . min_strict_mb_iter_ < 0 )
& & ( iteration = = maxIter ) ;
const bool use_relaxed_mb = relax_final_iteration_mb | |
( ( this - > param_ . min_strict_mb_iter_ > = 0 ) & &
( iteration > = this - > param_ . min_strict_mb_iter_ ) ) ;
// If min_strict_cnv_iter = -1 we use a relaxed tolerance for
// the cnv for the last iterations. For positive values we use
// the relaxed tolerance after the given number of iterations.
// We also use relaxed tolerances for cells with total
// pore-volume less than relaxed_max_pv_fraction_. Default
// value of relaxed_max_pv_fraction_ is 0.03
const bool relax_final_iteration_cnv =
( this - > param_ . min_strict_cnv_iter_ < 0 )
& & ( iteration = = maxIter ) ;
const bool relax_iter_cnv = ( this - > param_ . min_strict_cnv_iter_ > = 0 )
& & ( iteration > = this - > param_ . min_strict_cnv_iter_ ) ;
// Note trailing parentheses here, just before the final
// semicolon. This is an immediately invoked function
// expression which calculates a single boolean value.
const auto relax_pv_fraction_cnv =
2024-08-30 04:44:29 -05:00
[ & report , this , eligible = pvSum - numAquiferPvSum ] ( )
2024-05-07 08:49:12 -05:00
{
const auto & cnvPvSplit = report . cnvPvSplit ( ) . first ;
// [1]: tol < cnv <= relaxed
// [2]: relaxed < cnv
return static_cast < Scalar > ( cnvPvSplit [ 1 ] + cnvPvSplit [ 2 ] ) <
2024-08-30 04:44:29 -05:00
this - > param_ . relaxed_max_pv_fraction_ * eligible ;
2024-05-07 08:49:12 -05:00
} ( ) ;
const bool use_relaxed_cnv = relax_final_iteration_cnv
| | relax_pv_fraction_cnv
| | relax_iter_cnv ;
2024-02-15 02:22:42 -06:00
2024-05-07 08:49:12 -05:00
if ( ( relax_final_iteration_mb | | relax_final_iteration_cnv ) & &
this - > terminal_output_ )
{
std : : string message =
" Number of newton iterations reached its maximum "
" try to continue with relaxed tolerances: " ;
if ( relax_final_iteration_mb ) {
message + = fmt : : format ( " MB: {:.1e} " , param_ . tolerance_mb_relaxed_ ) ;
}
if ( relax_final_iteration_cnv ) {
message + = fmt : : format ( " CNV: {:.1e} " , param_ . tolerance_cnv_relaxed_ ) ;
2024-02-09 04:20:47 -06:00
}
2024-05-07 08:49:12 -05:00
OpmLog : : debug ( message ) ;
2024-02-09 04:20:47 -06:00
}
2024-05-07 08:49:12 -05:00
const auto tol_cnv = use_relaxed_cnv ? param_ . tolerance_cnv_relaxed_ : param_ . tolerance_cnv_ ;
const auto tol_mb = use_relaxed_mb ? param_ . tolerance_mb_relaxed_ : param_ . tolerance_mb_ ;
2024-09-11 05:43:24 -05:00
const auto tol_cnv_energy = use_relaxed_cnv ? param_ . tolerance_cnv_energy_relaxed_ : param_ . tolerance_cnv_energy_ ;
2024-09-16 07:38:28 -05:00
const auto tol_eb = use_relaxed_mb ? param_ . tolerance_energy_balance_relaxed_ : param_ . tolerance_energy_balance_ ;
2020-07-02 04:52:44 -05:00
2016-06-06 08:40:06 -05:00
// Finish computation
2018-11-15 13:38:25 -06:00
std : : vector < Scalar > CNV ( numComp ) ;
std : : vector < Scalar > mass_balance_residual ( numComp ) ;
2024-04-22 09:48:11 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx )
2016-06-06 08:40:06 -05:00
{
2017-05-03 06:34:15 -05:00
CNV [ compIdx ] = B_avg [ compIdx ] * dt * maxCoeff [ compIdx ] ;
mass_balance_residual [ compIdx ] = std : : abs ( B_avg [ compIdx ] * R_sum [ compIdx ] ) * dt / pvSum ;
residual_norms . push_back ( CNV [ compIdx ] ) ;
2016-06-06 08:40:06 -05:00
}
2018-11-15 13:38:25 -06:00
using CR = ConvergenceReport ;
2018-11-19 07:46:31 -06:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
2024-05-07 08:49:12 -05:00
const Scalar res [ 2 ] = {
mass_balance_residual [ compIdx ] , CNV [ compIdx ] ,
} ;
const CR : : ReservoirFailure : : Type types [ 2 ] = {
CR : : ReservoirFailure : : Type : : MassBalance ,
CR : : ReservoirFailure : : Type : : Cnv ,
} ;
2024-09-11 05:43:24 -05:00
Scalar tol [ 2 ] = { tol_mb , tol_cnv , } ;
2024-09-16 07:38:28 -05:00
if ( has_energy_ & & compIdx = = contiEnergyEqIdx ) {
tol [ 0 ] = tol_eb ;
2024-09-11 05:43:24 -05:00
tol [ 1 ] = tol_cnv_energy ;
2024-09-16 07:38:28 -05:00
}
2024-04-22 09:48:11 -05:00
2018-11-15 13:38:25 -06:00
for ( int ii : { 0 , 1 } ) {
if ( std : : isnan ( res [ ii ] ) ) {
2023-06-26 10:37:13 -05:00
report . setReservoirFailed ( { types [ ii ] , CR : : Severity : : NotANumber , compIdx } ) ;
2024-05-07 08:49:12 -05:00
if ( this - > terminal_output_ ) {
2022-12-08 07:09:09 -06:00
OpmLog : : debug ( " NaN residual for " + this - > compNames_ . name ( compIdx ) + " equation. " ) ;
2018-11-19 07:46:31 -06:00
}
2024-05-07 08:49:12 -05:00
}
else if ( res [ ii ] > maxResidualAllowed ( ) ) {
2023-06-26 10:37:13 -05:00
report . setReservoirFailed ( { types [ ii ] , CR : : Severity : : TooLarge , compIdx } ) ;
2024-05-07 08:49:12 -05:00
if ( this - > terminal_output_ ) {
2022-12-08 07:09:09 -06:00
OpmLog : : debug ( " Too large residual for " + this - > compNames_ . name ( compIdx ) + " equation. " ) ;
2018-11-19 07:46:31 -06:00
}
2024-05-07 08:49:12 -05:00
}
else if ( res [ ii ] < 0.0 ) {
2023-06-26 10:37:13 -05:00
report . setReservoirFailed ( { types [ ii ] , CR : : Severity : : Normal , compIdx } ) ;
2024-05-07 08:49:12 -05:00
if ( this - > terminal_output_ ) {
2022-12-08 07:09:09 -06:00
OpmLog : : debug ( " Negative residual for " + this - > compNames_ . name ( compIdx ) + " equation. " ) ;
2018-11-19 07:46:31 -06:00
}
2024-05-07 08:49:12 -05:00
}
else if ( res [ ii ] > tol [ ii ] ) {
2023-06-26 10:37:13 -05:00
report . setReservoirFailed ( { types [ ii ] , CR : : Severity : : Normal , compIdx } ) ;
2018-11-15 13:38:25 -06:00
}
2024-05-07 08:49:12 -05:00
2024-09-10 08:08:41 -05:00
report . setReservoirConvergenceMetric ( types [ ii ] , compIdx , res [ ii ] , tol [ ii ] ) ;
2018-11-15 13:38:25 -06:00
}
}
2016-06-06 08:40:06 -05:00
2018-11-15 13:38:25 -06:00
// Output of residuals.
2024-05-07 08:49:12 -05:00
if ( this - > terminal_output_ ) {
2016-06-06 08:40:06 -05:00
// Only rank 0 does print to std::cout
if ( iteration = = 0 ) {
std : : string msg = " Iter " ;
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
2018-11-19 07:46:31 -06:00
msg + = " MB( " ;
2022-12-08 07:09:09 -06:00
msg + = this - > compNames_ . name ( compIdx ) [ 0 ] ;
2018-11-19 07:46:31 -06:00
msg + = " ) " ;
2016-06-06 08:40:06 -05:00
}
2024-05-07 08:49:12 -05:00
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
2018-11-19 07:46:31 -06:00
msg + = " CNV( " ;
2022-12-08 07:09:09 -06:00
msg + = this - > compNames_ . name ( compIdx ) [ 0 ] ;
2018-11-19 07:46:31 -06:00
msg + = " ) " ;
2016-11-02 05:25:43 -05:00
}
2024-05-07 08:49:12 -05:00
2017-10-13 06:29:31 -05:00
OpmLog : : debug ( msg ) ;
2016-06-06 08:40:06 -05:00
}
2024-05-07 08:49:12 -05:00
2016-06-06 08:40:06 -05:00
std : : ostringstream ss ;
const std : : streamsize oprec = ss . precision ( 3 ) ;
const std : : ios : : fmtflags oflags = ss . setf ( std : : ios : : scientific ) ;
2024-05-07 08:49:12 -05:00
2016-06-06 08:40:06 -05:00
ss < < std : : setw ( 4 ) < < iteration ;
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
ss < < std : : setw ( 11 ) < < mass_balance_residual [ compIdx ] ;
2016-06-06 08:40:06 -05:00
}
2024-05-07 08:49:12 -05:00
2017-05-03 06:34:15 -05:00
for ( int compIdx = 0 ; compIdx < numComp ; + + compIdx ) {
ss < < std : : setw ( 11 ) < < CNV [ compIdx ] ;
2016-06-06 08:40:06 -05:00
}
2024-05-07 08:49:12 -05:00
2016-06-06 08:40:06 -05:00
ss . precision ( oprec ) ;
ss . flags ( oflags ) ;
2024-05-07 08:49:12 -05:00
2017-10-13 06:29:31 -05:00
OpmLog : : debug ( ss . str ( ) ) ;
2016-06-06 08:40:06 -05:00
}
2018-11-15 13:38:25 -06:00
return report ;
}
2016-06-06 08:40:06 -05:00
2024-09-09 06:00:51 -05:00
void checkCardPenalty ( ConvergenceReport & report , int iteration )
{
2024-09-13 08:56:40 -05:00
const auto & current_metrics = report . reservoirConvergence ( ) ;
auto distances = std : : vector < double > ( current_metrics . size ( ) , 0.0 ) ;
int current_above_tolerance = 0 ;
for ( size_t i = 0 ; i < current_metrics . size ( ) ; + + i ) {
distances [ i ] = std : : max ( std : : log10 ( current_metrics [ i ] . value ( ) / current_metrics [ i ] . tolerance ( ) ) , 0.0 ) ;
// Count number of metrics above tolerance
if ( current_metrics [ i ] . value ( ) > current_metrics [ i ] . tolerance ( ) ) {
current_above_tolerance + + ;
2024-09-09 06:00:51 -05:00
}
2024-09-13 08:56:40 -05:00
}
2024-09-10 08:08:41 -05:00
2024-09-13 08:56:40 -05:00
// use L1 norm of the distances vector
double current_distance = std : : accumulate ( distances . begin ( ) , distances . end ( ) , 0.0 ) ;
if ( iteration > 0 ) {
2024-09-10 08:08:41 -05:00
// Add penalty if number of metrics above tolerance has increased
if ( current_above_tolerance > prev_above_tolerance_ ) {
report . addNonConvergedPenalty ( ) ;
2024-09-09 06:00:51 -05:00
}
2024-09-10 08:08:41 -05:00
2024-09-10 02:57:15 -05:00
if ( current_distance > param_ . convergence_monitoring_decay_factor_ * prev_distance_ ) {
2024-09-09 11:32:05 -05:00
report . addDistanceDecayPenalty ( ) ;
}
2024-09-09 06:00:51 -05:00
}
2024-09-13 08:56:40 -05:00
prev_distance_ = current_distance ;
prev_above_tolerance_ = current_above_tolerance ;
2024-09-09 10:37:55 -05:00
if ( report . wellFailures ( ) . size ( ) > 0 ) {
report . addLargeWellResidualsPenalty ( ) ;
}
2024-09-09 06:00:51 -05:00
total_penaltyCard_ + = report . getPenaltyCard ( ) ;
2024-09-12 09:43:40 -05:00
if ( param_ . convergence_monitoring_ & & ( total_penaltyCard_ . total ( ) > param_ . convergence_monitoring_cutoff_ ) ) {
2024-09-10 02:57:15 -05:00
report . setReservoirFailed ( { ConvergenceReport : : ReservoirFailure : : Type : : ConvergenceMonitorFailure ,
2024-10-03 11:05:06 -05:00
ConvergenceReport : : Severity : : ConvergenceMonitorFailure ,
- 1 } ) ; // -1 indicates it's not specific to any component
2024-09-10 02:57:15 -05:00
}
2024-09-09 06:00:51 -05:00
}
2018-11-15 13:38:25 -06:00
/// Compute convergence based on total mass balance (tol_mb) and maximum
/// residual mass balance (tol_cnv).
/// \param[in] timer simulation timer
/// \param[in] iteration current iteration number
2024-02-09 04:20:47 -06:00
/// \param[in] maxIter maximum number of iterations
2018-11-15 13:38:25 -06:00
/// \param[out] residual_norms CNV residuals by phase
2018-11-15 14:21:25 -06:00
ConvergenceReport getConvergence ( const SimulatorTimerInterface & timer ,
const int iteration ,
2024-02-09 04:20:47 -06:00
const int maxIter ,
2024-02-21 02:45:18 -06:00
std : : vector < Scalar > & residual_norms )
2018-11-15 13:38:25 -06:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( getConvergence ) ;
2018-11-15 13:38:25 -06:00
// Get convergence reports for reservoir and wells.
std : : vector < Scalar > B_avg ( numEq , 0.0 ) ;
2022-12-08 10:16:34 -06:00
auto report = getReservoirConvergence ( timer . simulationTimeElapsed ( ) ,
timer . currentStepLength ( ) ,
2024-02-09 04:20:47 -06:00
iteration , maxIter , B_avg , residual_norms ) ;
2023-02-15 02:41:37 -06:00
{
OPM_TIMEBLOCK ( getWellConvergence ) ;
report + = wellModel ( ) . getWellConvergence ( B_avg , /*checkWellGroupControls*/ report . converged ( ) ) ;
}
2024-09-12 09:43:40 -05:00
checkCardPenalty ( report , iteration ) ;
2024-09-09 12:55:16 -05:00
2018-11-15 14:21:25 -06:00
return report ;
2016-06-06 08:40:06 -05:00
}
/// The number of active fluid phases in the model.
int numPhases ( ) const
{
2017-06-16 06:52:51 -05:00
return phaseUsage_ . num_phases ;
2016-06-06 08:40:06 -05:00
}
2017-02-06 09:51:22 -06:00
/// Wrapper required due to not following generic API
template < class T >
2024-02-21 02:45:18 -06:00
std : : vector < std : : vector < Scalar > >
2017-02-06 09:51:22 -06:00
computeFluidInPlace ( const T & , const std : : vector < int > & fipnum ) const
{
return computeFluidInPlace ( fipnum ) ;
}
2018-01-15 06:51:40 -06:00
/// Should not be called
2024-02-21 02:45:18 -06:00
std : : vector < std : : vector < Scalar > >
2018-01-15 06:51:40 -06:00
computeFluidInPlace ( const std : : vector < int > & /*fipnum*/ ) const
2018-12-21 06:51:13 -06:00
{
2023-02-15 02:41:37 -06:00
OPM_TIMEBLOCK ( computeFluidInPlace ) ;
2018-01-15 06:51:40 -06:00
//assert(true)
//return an empty vector
2024-02-21 02:45:18 -06:00
std : : vector < std : : vector < Scalar > > regionValues ( 0 , std : : vector < Scalar > ( 0 , 0.0 ) ) ;
2017-03-13 04:04:31 -05:00
return regionValues ;
2016-09-12 16:28:44 -05:00
}
2024-02-06 03:05:36 -06:00
const Simulator & simulator ( ) const
{ return simulator_ ; }
2016-06-06 08:40:06 -05:00
2024-02-06 03:05:36 -06:00
Simulator & simulator ( )
{ return simulator_ ; }
2018-06-06 03:59:41 -05:00
2017-04-10 08:55:30 -05:00
/// return the statistics if the nonlinearIteration() method failed
2020-05-07 09:13:39 -05:00
const SimulatorReportSingle & failureReport ( ) const
2017-04-10 08:55:30 -05:00
{ return failureReport_ ; }
2023-06-14 02:18:53 -05:00
/// return the statistics if the nonlinearIteration() method failed
2023-07-05 02:58:04 -05:00
SimulatorReportSingle localAccumulatedReports ( ) const
2023-07-04 08:15:29 -05:00
{
return nlddSolver_ ? nlddSolver_ - > localAccumulatedReports ( )
: SimulatorReportSingle { } ;
}
2023-06-14 02:18:53 -05:00
2018-11-22 04:14:39 -06:00
const std : : vector < StepReport > & stepReports ( ) const
{
return convergence_reports_ ;
}
2023-10-23 08:55:51 -05:00
void writePartitions ( const std : : filesystem : : path & odir ) const
{
if ( this - > nlddSolver_ ! = nullptr ) {
this - > nlddSolver_ - > writePartitions ( odir ) ;
return ;
}
2024-02-06 03:05:36 -06:00
const auto & elementMapper = this - > simulator ( ) . model ( ) . elementMapper ( ) ;
const auto & cartMapper = this - > simulator ( ) . vanguard ( ) . cartesianIndexMapper ( ) ;
2023-10-23 08:55:51 -05:00
2024-02-06 03:05:36 -06:00
const auto & grid = this - > simulator ( ) . vanguard ( ) . grid ( ) ;
2023-10-23 08:55:51 -05:00
const auto & comm = grid . comm ( ) ;
const auto nDigit = 1 + static_cast < int > ( std : : floor ( std : : log10 ( comm . size ( ) ) ) ) ;
std : : ofstream pfile { odir / fmt : : format ( " {1:0>{0}} " , nDigit , comm . rank ( ) ) } ;
for ( const auto & cell : elements ( grid . leafGridView ( ) , Dune : : Partitions : : interior ) ) {
pfile < < comm . rank ( ) < < ' '
< < cartMapper . cartesianIndex ( elementMapper . index ( cell ) ) < < ' '
< < comm . rank ( ) < < ' \n ' ;
}
}
2023-12-11 04:26:43 -06:00
const std : : vector < std : : vector < int > > & getConvCells ( ) const
{ return rst_conv_ . getData ( ) ; }
2017-03-13 04:04:27 -05:00
protected :
2016-06-06 08:40:06 -05:00
// --------- Data members ---------
2024-02-06 03:05:36 -06:00
Simulator & simulator_ ;
const Grid & grid_ ;
2017-06-16 06:52:51 -05:00
const PhaseUsage phaseUsage_ ;
2020-12-07 05:16:04 -06:00
static constexpr bool has_solvent_ = getPropValue < TypeTag , Properties : : EnableSolvent > ( ) ;
2020-12-04 06:40:14 -06:00
static constexpr bool has_extbo_ = getPropValue < TypeTag , Properties : : EnableExtbo > ( ) ;
2020-12-07 05:16:04 -06:00
static constexpr bool has_polymer_ = getPropValue < TypeTag , Properties : : EnablePolymer > ( ) ;
static constexpr bool has_polymermw_ = getPropValue < TypeTag , Properties : : EnablePolymerMW > ( ) ;
static constexpr bool has_energy_ = getPropValue < TypeTag , Properties : : EnableEnergy > ( ) ;
static constexpr bool has_foam_ = getPropValue < TypeTag , Properties : : EnableFoam > ( ) ;
static constexpr bool has_brine_ = getPropValue < TypeTag , Properties : : EnableBrine > ( ) ;
2021-10-06 12:32:35 -05:00
static constexpr bool has_micp_ = getPropValue < TypeTag , Properties : : EnableMICP > ( ) ;
2016-06-06 08:40:06 -05:00
ModelParameters param_ ;
2020-05-07 09:13:39 -05:00
SimulatorReportSingle failureReport_ ;
2016-06-06 08:40:06 -05:00
// Well Model
2017-09-26 03:52:05 -05:00
BlackoilWellModel < TypeTag > & well_model_ ;
2016-06-06 08:40:06 -05:00
2023-12-11 04:26:43 -06:00
RSTConv rst_conv_ ; //!< Helper class for RPTRST CONV
2016-06-06 08:40:06 -05:00
/// \brief Whether we print something to std::cout
bool terminal_output_ ;
/// \brief The number of cells of the global grid.
2016-11-02 10:59:08 -05:00
long int global_nc_ ;
2016-06-06 08:40:06 -05:00
2024-02-21 02:45:18 -06:00
std : : vector < std : : vector < Scalar > > residual_norms_history_ ;
Scalar current_relaxation_ ;
2016-09-07 03:13:24 -05:00
BVector dx_old_ ;
2016-06-06 08:40:06 -05:00
2018-11-22 04:14:39 -06:00
std : : vector < StepReport > convergence_reports_ ;
2022-12-08 07:09:09 -06:00
ComponentName compNames_ { } ;
2023-07-04 08:15:29 -05:00
2024-01-31 07:14:50 -06:00
std : : unique_ptr < BlackoilModelNldd < TypeTag > > nlddSolver_ ; //!< Non-linear DD solver
2022-12-08 07:09:09 -06:00
2016-06-06 08:40:06 -05:00
public :
/// return the StandardWells object
2017-09-26 03:52:05 -05:00
BlackoilWellModel < TypeTag > &
2017-04-04 03:56:26 -05:00
wellModel ( ) { return well_model_ ; }
2017-09-26 03:52:05 -05:00
const BlackoilWellModel < TypeTag > &
2017-04-04 03:56:26 -05:00
wellModel ( ) const { return well_model_ ; }
2016-06-06 08:40:06 -05:00
2019-06-26 02:50:56 -05:00
void beginReportStep ( )
2016-08-08 07:58:25 -05:00
{
2024-02-06 03:05:36 -06:00
simulator_ . problem ( ) . beginEpisode ( ) ;
2016-08-08 07:58:25 -05:00
}
void endReportStep ( )
{
2024-02-06 03:05:36 -06:00
simulator_ . problem ( ) . endEpisode ( ) ;
2016-08-08 07:58:25 -05:00
}
2023-07-03 03:11:45 -05:00
template < class FluidState , class Residual >
void getMaxCoeff ( const unsigned cell_idx ,
const IntensiveQuantities & intQuants ,
const FluidState & fs ,
2024-02-06 03:11:57 -06:00
const Residual & modelResid ,
2023-07-03 03:11:45 -05:00
const Scalar pvValue ,
std : : vector < Scalar > & B_avg ,
std : : vector < Scalar > & R_sum ,
std : : vector < Scalar > & maxCoeff ,
std : : vector < int > & maxCoeffCell )
{
for ( unsigned phaseIdx = 0 ; phaseIdx < FluidSystem : : numPhases ; + + phaseIdx )
{
if ( ! FluidSystem : : phaseIsActive ( phaseIdx ) ) {
continue ;
}
const unsigned compIdx = Indices : : canonicalToActiveComponentIndex ( FluidSystem : : solventComponentIndex ( phaseIdx ) ) ;
B_avg [ compIdx ] + = 1.0 / fs . invB ( phaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ compIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ compIdx ] + = R2 ;
2024-02-21 02:45:18 -06:00
const Scalar Rval = std : : abs ( R2 ) / pvValue ;
2023-07-03 03:11:45 -05:00
if ( Rval > maxCoeff [ compIdx ] ) {
maxCoeff [ compIdx ] = Rval ;
maxCoeffCell [ compIdx ] = cell_idx ;
}
}
if constexpr ( has_solvent_ ) {
B_avg [ contiSolventEqIdx ] + = 1.0 / intQuants . solventInverseFormationVolumeFactor ( ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiSolventEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiSolventEqIdx ] + = R2 ;
maxCoeff [ contiSolventEqIdx ] = std : : max ( maxCoeff [ contiSolventEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_extbo_ ) {
B_avg [ contiZfracEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : gasPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiZfracEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiZfracEqIdx ] + = R2 ;
maxCoeff [ contiZfracEqIdx ] = std : : max ( maxCoeff [ contiZfracEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_polymer_ ) {
B_avg [ contiPolymerEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiPolymerEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiPolymerEqIdx ] + = R2 ;
maxCoeff [ contiPolymerEqIdx ] = std : : max ( maxCoeff [ contiPolymerEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_foam_ ) {
B_avg [ contiFoamEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : gasPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiFoamEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiFoamEqIdx ] + = R2 ;
maxCoeff [ contiFoamEqIdx ] = std : : max ( maxCoeff [ contiFoamEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_brine_ ) {
B_avg [ contiBrineEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiBrineEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiBrineEqIdx ] + = R2 ;
maxCoeff [ contiBrineEqIdx ] = std : : max ( maxCoeff [ contiBrineEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_polymermw_ ) {
static_assert ( has_polymer_ ) ;
B_avg [ contiPolymerMWEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
// the residual of the polymer molecular equation is scaled down by a 100, since molecular weight
// can be much bigger than 1, and this equation shares the same tolerance with other mass balance equations
// TODO: there should be a more general way to determine the scaling-down coefficient
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiPolymerMWEqIdx ] / 100. ;
2023-07-03 03:11:45 -05:00
R_sum [ contiPolymerMWEqIdx ] + = R2 ;
maxCoeff [ contiPolymerMWEqIdx ] = std : : max ( maxCoeff [ contiPolymerMWEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_energy_ ) {
B_avg [ contiEnergyEqIdx ] + = 1.0 / ( 4.182e1 ) ; // converting J -> RM3 (entalpy / (cp * deltaK * rho) assuming change of 1e-5K of water
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiEnergyEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiEnergyEqIdx ] + = R2 ;
maxCoeff [ contiEnergyEqIdx ] = std : : max ( maxCoeff [ contiEnergyEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
}
if constexpr ( has_micp_ ) {
B_avg [ contiMicrobialEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R1 = modelResid [ cell_idx ] [ contiMicrobialEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiMicrobialEqIdx ] + = R1 ;
maxCoeff [ contiMicrobialEqIdx ] = std : : max ( maxCoeff [ contiMicrobialEqIdx ] ,
std : : abs ( R1 ) / pvValue ) ;
B_avg [ contiOxygenEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R2 = modelResid [ cell_idx ] [ contiOxygenEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiOxygenEqIdx ] + = R2 ;
maxCoeff [ contiOxygenEqIdx ] = std : : max ( maxCoeff [ contiOxygenEqIdx ] ,
std : : abs ( R2 ) / pvValue ) ;
B_avg [ contiUreaEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R3 = modelResid [ cell_idx ] [ contiUreaEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiUreaEqIdx ] + = R3 ;
maxCoeff [ contiUreaEqIdx ] = std : : max ( maxCoeff [ contiUreaEqIdx ] ,
std : : abs ( R3 ) / pvValue ) ;
B_avg [ contiBiofilmEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R4 = modelResid [ cell_idx ] [ contiBiofilmEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiBiofilmEqIdx ] + = R4 ;
maxCoeff [ contiBiofilmEqIdx ] = std : : max ( maxCoeff [ contiBiofilmEqIdx ] ,
std : : abs ( R4 ) / pvValue ) ;
B_avg [ contiCalciteEqIdx ] + = 1.0 / fs . invB ( FluidSystem : : waterPhaseIdx ) . value ( ) ;
2024-02-06 03:11:57 -06:00
const auto R5 = modelResid [ cell_idx ] [ contiCalciteEqIdx ] ;
2023-07-03 03:11:45 -05:00
R_sum [ contiCalciteEqIdx ] + = R5 ;
maxCoeff [ contiCalciteEqIdx ] = std : : max ( maxCoeff [ contiCalciteEqIdx ] ,
std : : abs ( R5 ) / pvValue ) ;
}
}
2023-07-05 03:25:06 -05:00
//! \brief Returns const reference to model parameters.
const ModelParameters & param ( ) const
{
return param_ ;
}
2023-07-05 03:25:06 -05:00
//! \brief Returns const reference to component names.
const ComponentName & compNames ( ) const
{
return compNames_ ;
}
2023-07-04 08:37:50 -05:00
private :
2024-02-21 02:45:18 -06:00
Scalar dpMaxRel ( ) const { return param_ . dp_max_rel_ ; }
Scalar dsMax ( ) const { return param_ . ds_max_ ; }
Scalar drMaxRel ( ) const { return param_ . dr_max_rel_ ; }
Scalar maxResidualAllowed ( ) const { return param_ . max_residual_allowed_ ; }
2019-04-04 05:20:30 -05:00
double linear_solve_setup_time_ ;
2024-09-09 06:00:51 -05:00
ConvergenceReport : : PenaltyCard total_penaltyCard_ ;
2024-09-09 11:32:05 -05:00
double prev_distance_ = std : : numeric_limits < double > : : infinity ( ) ;
2024-09-10 08:08:41 -05:00
int prev_above_tolerance_ = 0 ;
2017-06-21 09:26:06 -05:00
std : : vector < bool > wasSwitched_ ;
2016-06-06 08:40:06 -05:00
} ;
2024-07-06 03:22:47 -05:00
2016-06-06 08:40:06 -05:00
} // namespace Opm
2024-01-31 07:14:50 -06:00
# endif // OPM_BLACKOILMODEL_HEADER_INCLUDED