mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #1658 from atgeirr/shutdown-chopping-wells
Shut time-chopping wells
This commit is contained in:
@@ -243,8 +243,8 @@ namespace Opm {
|
||||
residual_norms_history_.clear();
|
||||
current_relaxation_ = 1.0;
|
||||
dx_old_ = 0.0;
|
||||
convergence_reports_.push_back({});
|
||||
convergence_reports_.back().reserve(11);
|
||||
convergence_reports_.push_back({timer.reportStepNum(), timer.currentStepNum(), {}});
|
||||
convergence_reports_.back().report.reserve(11);
|
||||
}
|
||||
|
||||
report.total_linearizations = 1;
|
||||
@@ -267,7 +267,7 @@ namespace Opm {
|
||||
{
|
||||
auto convrep = getConvergence(timer, iteration,residual_norms);
|
||||
report.converged = convrep.converged() && iteration > nonlinear_solver.minIter();;
|
||||
convergence_reports_.back().push_back(std::move(convrep));
|
||||
convergence_reports_.back().report.push_back(std::move(convrep));
|
||||
}
|
||||
|
||||
// checking whether the group targets are converged
|
||||
@@ -965,6 +965,18 @@ namespace Opm {
|
||||
const SimulatorReport& failureReport() const
|
||||
{ return failureReport_; }
|
||||
|
||||
struct StepReport
|
||||
{
|
||||
int report_step;
|
||||
int current_step;
|
||||
std::vector<ConvergenceReport> report;
|
||||
};
|
||||
|
||||
const std::vector<StepReport>& stepReports() const
|
||||
{
|
||||
return convergence_reports_;
|
||||
}
|
||||
|
||||
protected:
|
||||
const ISTLSolverType& istlSolver() const
|
||||
{
|
||||
@@ -1002,7 +1014,7 @@ namespace Opm {
|
||||
std::unique_ptr<Mat> matrix_for_preconditioner_;
|
||||
std::vector<std::pair<int,std::vector<int>>> overlapRowAndColumns_;
|
||||
|
||||
std::vector<std::vector<ConvergenceReport>> convergence_reports_;
|
||||
std::vector<StepReport> convergence_reports_;
|
||||
public:
|
||||
/// return the StandardWells object
|
||||
BlackoilWellModel<TypeTag>&
|
||||
|
||||
@@ -236,6 +236,12 @@ namespace Opm {
|
||||
// called at the beginning of a report step
|
||||
void beginReportStep(const int time_step);
|
||||
|
||||
/// Return true if any well has a THP constraint.
|
||||
bool hasTHPConstraints() const;
|
||||
|
||||
/// Shut down any single well, but only if it is in prediction mode.
|
||||
void forceShutWellByNameIfPredictionMode(const std::string& wellname, const double simulation_time);
|
||||
|
||||
protected:
|
||||
|
||||
void extractLegacyPressure_(std::vector<double>& cellPressure) const
|
||||
|
||||
@@ -116,6 +116,52 @@ namespace Opm {
|
||||
}
|
||||
|
||||
|
||||
/// Return true if any well has a THP constraint.
|
||||
template<typename TypeTag>
|
||||
bool
|
||||
BlackoilWellModel<TypeTag>::
|
||||
hasTHPConstraints() const
|
||||
{
|
||||
for (const auto& well : well_container_) {
|
||||
if (well->wellHasTHPConstraints()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// Return true if any well has a THP constraint.
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
forceShutWellByNameIfPredictionMode(const std::string& wellname,
|
||||
const double simulation_time)
|
||||
{
|
||||
// Only add the well to the closed list on the
|
||||
// process that owns it.
|
||||
for (const auto& well : well_container_) {
|
||||
if (well->name() == wellname) {
|
||||
if (well->underPredictionMode()) {
|
||||
wellTestState_.addClosedWell(wellname, WellTestConfig::Reason::PHYSICAL, simulation_time);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only log a message on the output rank.
|
||||
if (terminal_output_) {
|
||||
const std::string msg = "Well " + wellname
|
||||
+ " will be shut because it cannot get converged.";
|
||||
OpmLog::info(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
|
||||
@@ -228,6 +228,12 @@ namespace Opm
|
||||
// whether the well is operable
|
||||
bool isOperable() const;
|
||||
|
||||
/// Returns true if the well has one or more THP limits/constraints.
|
||||
bool wellHasTHPConstraints() const;
|
||||
|
||||
/// Returns true if the well is currently in prediction mode (i.e. not history mode).
|
||||
bool underPredictionMode() const;
|
||||
|
||||
protected:
|
||||
|
||||
// to indicate a invalid completion
|
||||
@@ -322,10 +328,6 @@ namespace Opm
|
||||
bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
|
||||
const WellState& well_state) const;
|
||||
|
||||
bool underPredictionMode() const;
|
||||
|
||||
bool wellHasTHPConstraints() const;
|
||||
|
||||
double getTHPConstraint() const;
|
||||
|
||||
int getTHPControlIndex() const;
|
||||
|
||||
@@ -298,6 +298,13 @@ namespace Opm {
|
||||
restarts = 0;
|
||||
}
|
||||
|
||||
// Further restrict time step size if we are in
|
||||
// prediction mode with THP constraints.
|
||||
if (solver.model().wellModel().hasTHPConstraints()) {
|
||||
const double maxPredictionTHPTimestep = 16.0 * unit::day;
|
||||
dtEstimate = std::min(dtEstimate, maxPredictionTHPTimestep);
|
||||
}
|
||||
|
||||
if (timestepVerbose_) {
|
||||
std::ostringstream ss;
|
||||
substepReport.reportStep(ss);
|
||||
@@ -343,16 +350,50 @@ namespace Opm {
|
||||
}
|
||||
|
||||
const double newTimeStep = restartFactor_ * dt;
|
||||
// we need to revise this
|
||||
substepTimer.provideTimeStepEstimate(newTimeStep);
|
||||
if (solverVerbose_) {
|
||||
std::string msg;
|
||||
msg = causeOfFailure + "\nTimestep chopped to "
|
||||
+ std::to_string(unit::convert::to(substepTimer.currentStepLength(), unit::day)) + " days\n";
|
||||
OpmLog::problem(msg);
|
||||
}
|
||||
const double minimumChoppedTimestep = 0.25 * unit::day;
|
||||
if (newTimeStep > minimumChoppedTimestep) {
|
||||
// Chop the timestep.
|
||||
substepTimer.provideTimeStepEstimate(newTimeStep);
|
||||
if (solverVerbose_) {
|
||||
std::string msg;
|
||||
msg = causeOfFailure + "\nTimestep chopped to "
|
||||
+ std::to_string(unit::convert::to(substepTimer.currentStepLength(), unit::day)) + " days\n";
|
||||
OpmLog::problem(msg);
|
||||
}
|
||||
++restarts;
|
||||
} else {
|
||||
// We are below the threshold, and will check if there are any
|
||||
// wells we should close rather than chopping again.
|
||||
std::set<std::string> failing_wells = consistentlyFailingWells(solver.model().stepReports());
|
||||
if (failing_wells.empty()) {
|
||||
// Found no wells to close, chop the timestep as above.
|
||||
substepTimer.provideTimeStepEstimate(newTimeStep);
|
||||
if (solverVerbose_) {
|
||||
std::string msg;
|
||||
msg = causeOfFailure + "\nTimestep chopped to "
|
||||
+ std::to_string(unit::convert::to(substepTimer.currentStepLength(), unit::day)) + " days\n";
|
||||
OpmLog::problem(msg);
|
||||
}
|
||||
++restarts;
|
||||
} else {
|
||||
// Close all consistently failing wells.
|
||||
for (const auto& well : failing_wells) {
|
||||
solver.model().wellModel().forceShutWellByNameIfPredictionMode(well, substepTimer.simulationTimeElapsed());
|
||||
}
|
||||
substepTimer.provideTimeStepEstimate(dt);
|
||||
if (solverVerbose_) {
|
||||
std::string msg;
|
||||
msg = "\nProblematic well(s) were shut: ";
|
||||
for (const auto& well : failing_wells) {
|
||||
msg += well;
|
||||
msg += " ";
|
||||
}
|
||||
msg += "(retrying timestep)\n";
|
||||
OpmLog::problem(msg);
|
||||
}
|
||||
}
|
||||
|
||||
++restarts;
|
||||
}
|
||||
}
|
||||
ebosProblem.setNextTimeStepSize(substepTimer.currentStepLength());
|
||||
}
|
||||
@@ -433,6 +474,54 @@ namespace Opm {
|
||||
assert(growthFactor_ >= 1.0);
|
||||
}
|
||||
|
||||
|
||||
template <class StepReportVector>
|
||||
std::set<std::string> consistentlyFailingWells(const StepReportVector& sr)
|
||||
{
|
||||
// If there are wells that cause repeated failures, we
|
||||
// close them, and restart the un-chopped timestep.
|
||||
std::ostringstream msg;
|
||||
msg << " Excessive chopping detected in report step "
|
||||
<< sr.back().report_step << ", substep " << sr.back().current_step << "\n";
|
||||
const auto& wfs = sr.back().report.back().wellFailures();
|
||||
for (const auto& wf : wfs) {
|
||||
msg << " Well that failed: " << wf.wellName() << "\n";
|
||||
}
|
||||
msg.flush();
|
||||
OpmLog::debug(msg.str());
|
||||
|
||||
// Check the last few step reports.
|
||||
const int num_steps = 3;
|
||||
const int rep_step = sr.back().report_step;
|
||||
const int sub_step = sr.back().current_step;
|
||||
const int sr_size = sr.size();
|
||||
std::set<std::string> failing_wells;
|
||||
for (const auto& wf : wfs) {
|
||||
failing_wells.insert(wf.wellName());
|
||||
}
|
||||
if (sr_size >= num_steps) {
|
||||
for (int step = 1; step < num_steps; ++step) {
|
||||
const auto& srep = sr[sr_size - 1 - step];
|
||||
// Report must be from same report step and substep, otherwise we have
|
||||
// not chopped/retried enough times on this step.
|
||||
if (srep.report_step != rep_step || srep.current_step != sub_step) {
|
||||
break;
|
||||
}
|
||||
// Get the failing wells for this step, that also failed all other steps.
|
||||
std::set<std::string> failing_wells_step;
|
||||
for (const auto& wf : srep.report.back().wellFailures()) {
|
||||
if (failing_wells.count(wf.wellName()) > 0) {
|
||||
failing_wells_step.insert(wf.wellName());
|
||||
}
|
||||
}
|
||||
failing_wells.swap(failing_wells_step);
|
||||
}
|
||||
}
|
||||
return failing_wells;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef std::unique_ptr<TimeStepControlInterface> TimeStepControlType;
|
||||
|
||||
SimulatorReport failureReport_; //!< statistics for the failed substeps of the last timestep
|
||||
|
||||
Reference in New Issue
Block a user