2015-12-01 07:15:35 -06:00
|
|
|
/*
|
|
|
|
Copyright 2015, 2016 SINTEF ICT, Applied Mathematics.
|
|
|
|
Copyright 2016 Statoil AS.
|
|
|
|
|
|
|
|
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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef OPM_BLACKOILSEQUENTIALMODEL_HEADER_INCLUDED
|
|
|
|
#define OPM_BLACKOILSEQUENTIALMODEL_HEADER_INCLUDED
|
|
|
|
|
|
|
|
|
|
|
|
#include <opm/autodiff/BlackoilModelBase.hpp>
|
|
|
|
#include <opm/autodiff/BlackoilPressureModel.hpp>
|
|
|
|
#include <opm/autodiff/BlackoilTransportModel.hpp>
|
|
|
|
#include <opm/core/simulator/BlackoilState.hpp>
|
|
|
|
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
|
|
|
#include <opm/autodiff/BlackoilModelParameters.hpp>
|
2017-02-10 09:07:25 -06:00
|
|
|
#include <opm/simulators/timestepping/SimulatorTimerInterface.hpp>
|
2015-12-01 07:15:35 -06:00
|
|
|
|
|
|
|
namespace Opm {
|
|
|
|
|
|
|
|
struct BlackoilSequentialModelParameters : public BlackoilModelParameters
|
|
|
|
{
|
|
|
|
bool iterate_to_fully_implicit;
|
|
|
|
explicit BlackoilSequentialModelParameters( const parameter::ParameterGroup& param )
|
|
|
|
: BlackoilModelParameters(param),
|
|
|
|
iterate_to_fully_implicit(param.getDefault("iterate_to_fully_implicit", false))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/// A sequential splitting model implementation for three-phase black oil.
|
|
|
|
template<class Grid, class WellModel>
|
|
|
|
class BlackoilSequentialModel
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
typedef BlackoilState ReservoirState;
|
|
|
|
typedef WellStateFullyImplicitBlackoil WellState;
|
|
|
|
typedef BlackoilSequentialModelParameters ModelParameters;
|
|
|
|
typedef DefaultBlackoilSolutionState SolutionState;
|
|
|
|
|
2016-09-02 07:15:10 -05:00
|
|
|
typedef BlackoilPressureModel<Grid, WellModel> PressureModel;
|
2016-09-02 02:38:25 -05:00
|
|
|
typedef BlackoilTransportModel<Grid, WellModel> TransportModel;
|
2016-09-02 07:15:10 -05:00
|
|
|
typedef typename TransportModel::SimulatorData SimulatorData;
|
2017-02-09 06:54:42 -06:00
|
|
|
typedef typename TransportModel::FIPDataType FIPDataType;
|
2016-09-02 02:38:25 -05:00
|
|
|
|
2015-12-01 07:15:35 -06:00
|
|
|
/// 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] fluid fluid properties
|
|
|
|
/// \param[in] geo rock properties
|
|
|
|
/// \param[in] rock_comp_props if non-null, rock compressibility properties
|
|
|
|
/// \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] has_disgas turn on dissolved gas
|
|
|
|
/// \param[in] has_vapoil turn on vaporized oil feature
|
|
|
|
/// \param[in] terminal_output request output to cout/cerr
|
|
|
|
BlackoilSequentialModel(const ModelParameters& param,
|
|
|
|
const Grid& grid ,
|
2016-12-29 09:35:24 -06:00
|
|
|
const BlackoilPropsAdFromDeck& fluid,
|
2015-12-01 07:15:35 -06:00
|
|
|
const DerivedGeology& geo ,
|
|
|
|
const RockCompressibility* rock_comp_props,
|
|
|
|
const WellModel well_model,
|
|
|
|
const NewtonIterationBlackoilInterface& linsolver,
|
2016-10-14 02:23:26 -05:00
|
|
|
std::shared_ptr< const EclipseState > eclState,
|
2015-12-01 07:15:35 -06:00
|
|
|
const bool has_disgas,
|
|
|
|
const bool has_vapoil,
|
|
|
|
const bool terminal_output)
|
|
|
|
: pressure_model_(new PressureModel(param, grid, fluid, geo, rock_comp_props, well_model,
|
|
|
|
linsolver, eclState, has_disgas, has_vapoil, terminal_output)),
|
|
|
|
transport_model_(new TransportModel(param, grid, fluid, geo, rock_comp_props, well_model,
|
|
|
|
linsolver, eclState, has_disgas, has_vapoil, terminal_output)),
|
|
|
|
pressure_solver_(typename PressureSolver::SolverParameters(), std::move(pressure_model_)),
|
|
|
|
transport_solver_(typename TransportSolver::SolverParameters(), std::move(transport_model_)),
|
|
|
|
initial_reservoir_state_(0, 0, 0), // will be overwritten
|
|
|
|
iterate_to_fully_implicit_(param.iterate_to_fully_implicit)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Called once before each time step.
|
2016-07-05 05:20:19 -05:00
|
|
|
/// \param[in] timer simulation timer
|
2015-12-01 07:15:35 -06:00
|
|
|
/// \param[in] reservoir_state reservoir state variables
|
|
|
|
/// \param[in] well_state well state variables
|
2016-07-05 05:20:19 -05:00
|
|
|
void prepareStep(const SimulatorTimerInterface& /*timer*/,
|
2015-12-01 07:15:35 -06:00
|
|
|
const ReservoirState& reservoir_state,
|
|
|
|
const WellState& well_state)
|
|
|
|
{
|
|
|
|
initial_reservoir_state_ = reservoir_state;
|
|
|
|
initial_well_state_ = well_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Called once per nonlinear iteration.
|
|
|
|
/// This model will first solve the pressure model to convergence, then the
|
|
|
|
/// transport model.
|
|
|
|
/// \param[in] iteration should be 0 for the first call of a new timestep
|
2016-07-05 05:20:19 -05:00
|
|
|
/// \param[in] timer simulation timer
|
2015-12-01 07:15:35 -06:00
|
|
|
/// \param[in] nonlinear_solver nonlinear solver used (for oscillation/relaxation control)
|
|
|
|
/// \param[in, out] reservoir_state reservoir state variables
|
|
|
|
/// \param[in, out] well_state well state variables
|
|
|
|
template <class NonlinearSolverType>
|
2016-11-23 14:44:33 -06:00
|
|
|
SimulatorReport nonlinearIteration(const int iteration,
|
2016-07-05 05:20:19 -05:00
|
|
|
const SimulatorTimerInterface& timer,
|
2015-12-01 07:15:35 -06:00
|
|
|
NonlinearSolverType& /* nonlinear_solver */,
|
|
|
|
ReservoirState& reservoir_state,
|
|
|
|
WellState& well_state)
|
|
|
|
{
|
|
|
|
if (!iterate_to_fully_implicit_) {
|
|
|
|
// Do a single pressure solve, followed by a single transport solve.
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Using sequential model.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pressure solve.
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Solving the pressure equation.");
|
|
|
|
}
|
|
|
|
ReservoirState initial_state = reservoir_state;
|
2016-11-23 14:44:33 -06:00
|
|
|
const SimulatorReport pressure_report = pressure_solver_.step(timer, reservoir_state, well_state);
|
|
|
|
const int pressure_liniter = pressure_report.total_linear_iterations;
|
2015-12-01 07:15:35 -06:00
|
|
|
if (pressure_liniter == -1) {
|
|
|
|
OPM_THROW(std::runtime_error, "Pressure solver failed to converge.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transport solve.
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Solving the transport equations.");
|
|
|
|
}
|
2016-11-23 14:44:33 -06:00
|
|
|
const SimulatorReport transport_report = transport_solver_.step(timer, initial_state, well_state, reservoir_state, well_state);
|
|
|
|
const int transport_liniter = transport_report.total_linear_iterations;
|
2015-12-01 07:15:35 -06:00
|
|
|
if (transport_liniter == -1) {
|
|
|
|
OPM_THROW(std::runtime_error, "Transport solver failed to converge.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Report and return.
|
2016-11-23 14:44:33 -06:00
|
|
|
SimulatorReport report;
|
|
|
|
report.converged = true;
|
|
|
|
report.total_linear_iterations = pressure_liniter + transport_liniter;
|
|
|
|
return report;
|
2015-12-01 07:15:35 -06:00
|
|
|
} else {
|
|
|
|
// Iterate to fully implicit solution.
|
|
|
|
// This call is just for a single iteration (one pressure and one transport solve),
|
|
|
|
// we return a 'false' converged status if more are needed
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Using sequential model in iterative mode, outer iteration " + std::to_string(iteration));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pressure solve.
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Solving the pressure equation.");
|
|
|
|
}
|
2016-11-23 14:44:33 -06:00
|
|
|
const SimulatorReport& pressure_report = pressure_solver_.step(timer, initial_reservoir_state_, initial_well_state_, reservoir_state, well_state);
|
|
|
|
const int pressure_liniter = pressure_report.total_linear_iterations;
|
2015-12-01 07:15:35 -06:00
|
|
|
if (pressure_liniter == -1) {
|
|
|
|
OPM_THROW(std::runtime_error, "Pressure solver failed to converge.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Transport solve.
|
|
|
|
if (terminalOutputEnabled()) {
|
|
|
|
OpmLog::info("Solving the transport equations.");
|
|
|
|
}
|
2016-11-23 14:44:33 -06:00
|
|
|
const SimulatorReport& transport_report = transport_solver_.step(timer, initial_reservoir_state_, initial_well_state_, reservoir_state, well_state);
|
|
|
|
const int transport_liniter = transport_report.total_linear_iterations;
|
2015-12-01 07:15:35 -06:00
|
|
|
if (transport_liniter == -1) {
|
|
|
|
OPM_THROW(std::runtime_error, "Transport solver failed to converge.");
|
|
|
|
}
|
|
|
|
|
2016-11-23 14:44:33 -06:00
|
|
|
SimulatorReport report;
|
|
|
|
report.converged = iteration >= 3; // TODO: replace this with a proper convergence check
|
|
|
|
report.total_linear_iterations = pressure_liniter + transport_liniter;
|
|
|
|
return report;
|
2015-12-01 07:15:35 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Called once after each time step.
|
|
|
|
/// In this class, this function does nothing.
|
2016-07-05 05:20:19 -05:00
|
|
|
/// \param[in] timer simulation timer
|
2015-12-01 07:15:35 -06:00
|
|
|
/// \param[in, out] reservoir_state reservoir state variables
|
|
|
|
/// \param[in, out] well_state well state variables
|
2016-07-05 05:20:19 -05:00
|
|
|
void afterStep(const SimulatorTimerInterface& /* timer */,
|
2015-12-01 07:15:35 -06:00
|
|
|
ReservoirState& /* reservoir_state */,
|
|
|
|
WellState& /* well_state */)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// \brief Set threshold pressures that prevent or reduce flow.
|
|
|
|
/// This prevents flow across faces if the potential
|
|
|
|
/// difference is less than the threshold. If the potential
|
|
|
|
/// difference is greater, the threshold value is subtracted
|
|
|
|
/// before calculating flow. This is treated symmetrically, so
|
|
|
|
/// flow is prevented or reduced in both directions equally.
|
|
|
|
/// \param[in] threshold_pressures_by_face array of size equal to the number of faces
|
|
|
|
/// of the grid passed in the constructor.
|
|
|
|
void setThresholdPressures(const std::vector<double>& threshold_pressures_by_face)
|
|
|
|
{
|
|
|
|
pressure_solver_.model().setThresholdPressures(threshold_pressures_by_face);
|
|
|
|
transport_solver_.model().setThresholdPressures(threshold_pressures_by_face);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Return true if output to cout is wanted.
|
|
|
|
bool terminalOutputEnabled() const
|
|
|
|
{
|
|
|
|
return pressure_solver_.model().terminalOutputEnabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Return the relative change in variables relevant to this model.
|
|
|
|
double relativeChange(const SimulationDataContainer& previous,
|
|
|
|
const SimulationDataContainer& current ) const
|
|
|
|
{
|
|
|
|
// TODO: this is a quick stopgap implementation, and should be evaluated more carefully.
|
|
|
|
return std::max(pressure_solver_.model().relativeChange(previous, current),
|
|
|
|
transport_solver_.model().relativeChange(previous, current));
|
|
|
|
}
|
|
|
|
|
2016-06-30 04:36:09 -05:00
|
|
|
/// Return the well model
|
|
|
|
const WellModel& wellModel() const
|
|
|
|
{
|
2016-10-27 06:20:10 -05:00
|
|
|
return pressure_solver_.model().wellModel();
|
2016-06-30 04:36:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-17 19:50:50 -05:00
|
|
|
/// Compute fluid in place.
|
2016-08-09 02:30:25 -05:00
|
|
|
/// \param[in] ReservoirState
|
|
|
|
/// \param[in] FIPNUM for active cells not global cells.
|
|
|
|
/// \return fluid in place, number of fip regions, each region contains 5 values which are liquid, vapour, water, free gas and dissolved gas.
|
2016-12-08 09:42:39 -06:00
|
|
|
std::vector<std::vector<double> >
|
2016-07-18 20:33:03 -05:00
|
|
|
computeFluidInPlace(const ReservoirState& x,
|
|
|
|
const std::vector<int>& fipnum) const
|
2016-07-17 19:50:50 -05:00
|
|
|
{
|
2016-08-30 06:34:18 -05:00
|
|
|
return transport_solver_.computeFluidInPlace(x, fipnum);
|
2016-07-17 19:50:50 -05:00
|
|
|
}
|
2015-12-01 07:15:35 -06:00
|
|
|
|
|
|
|
|
2016-09-02 07:15:10 -05:00
|
|
|
/// Return reservoir simulation data (for output functionality)
|
2017-03-16 04:22:27 -05:00
|
|
|
const SimulatorData& getSimulatorData(const SimulationDataContainer& localState) const {
|
|
|
|
return transport_solver_.model().getSimulatorData(localState);
|
2016-09-01 04:38:04 -05:00
|
|
|
}
|
|
|
|
|
2017-02-09 06:54:42 -06:00
|
|
|
/// Return fluid-in-place data (for output functionality)
|
|
|
|
FIPDataType getFIPData() const {
|
|
|
|
return transport_solver_.model().getFIPData();
|
|
|
|
}
|
|
|
|
|
2017-04-10 08:55:30 -05:00
|
|
|
/// return the statistics if the nonlinearIteration() method failed.
|
|
|
|
///
|
|
|
|
/// NOTE: for the flow_legacy simulator family this method is a stub, i.e. the
|
|
|
|
/// failure report object will *not* contain any meaningful data.
|
|
|
|
const SimulatorReport& failureReport() const
|
|
|
|
{ return failureReport_; }
|
2015-12-01 07:15:35 -06:00
|
|
|
|
|
|
|
protected:
|
|
|
|
typedef NonlinearSolver<PressureModel> PressureSolver;
|
|
|
|
typedef NonlinearSolver<TransportModel> TransportSolver;
|
|
|
|
|
2017-04-10 08:55:30 -05:00
|
|
|
SimulatorReport failureReport_;
|
|
|
|
|
2015-12-01 07:15:35 -06:00
|
|
|
std::unique_ptr<PressureModel> pressure_model_;
|
|
|
|
std::unique_ptr<TransportModel> transport_model_;
|
|
|
|
PressureSolver pressure_solver_;
|
|
|
|
TransportSolver transport_solver_;
|
|
|
|
|
|
|
|
ReservoirState initial_reservoir_state_;
|
|
|
|
WellState initial_well_state_;
|
|
|
|
|
|
|
|
bool iterate_to_fully_implicit_;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace Opm
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif // OPM_BLACKOILSEQUENTIALMODEL_HEADER_INCLUDED
|