Use ConvergenceStatus in well subsystem.

This commit is contained in:
Atgeirr Flø Rasmussen 2018-10-23 14:05:19 +02:00
parent 37d4327ce3
commit f9fae47f23
8 changed files with 109 additions and 137 deletions

View File

@ -234,8 +234,6 @@ namespace Opm {
// a vector of all the wells.
std::vector<WellInterfacePtr > well_container_;
using ConvergenceReport = typename WellInterface<TypeTag>::ConvergenceReport;
// create the well container
std::vector<WellInterfacePtr > createWellContainer(const int time_step);

View File

@ -660,45 +660,53 @@ namespace Opm {
BlackoilWellModel<TypeTag>::
getWellConvergence(const std::vector<Scalar>& B_avg) const
{
ConvergenceReport report;
ConvergenceStatus report;
for (const auto& well : well_container_) {
report += well->getWellConvergence(B_avg);
}
auto severity = report.severityOfWorstFailure();
// checking NaN residuals
{
bool nan_residual_found = report.nan_residual_found;
// Debug reporting.
for (const auto& f : report.wellFailures()) {
if (f.severity == ConvergenceStatus::Severity::NotANumber) {
OpmLog::debug("NaN residual found with phase " + std::to_string(f.phase) + " for well " + f.well_name);
}
}
// Throw if any nan residual found.
bool nan_residual_found = (severity == ConvergenceStatus::Severity::NotANumber);
const auto& grid = ebosSimulator_.vanguard().grid();
int value = nan_residual_found ? 1 : 0;
nan_residual_found = grid.comm().max(value);
if (nan_residual_found) {
for (const auto& well : report.nan_residual_wells) {
OpmLog::debug("NaN residual found with phase " + well.phase_name + " for well " + well.well_name);
}
OPM_THROW(Opm::NumericalIssue, "NaN residual found!");
}
}
// checking too large residuals
{
bool too_large_residual_found = report.too_large_residual_found;
// Debug reporting.
for (const auto& f : report.wellFailures()) {
if (f.severity == ConvergenceStatus::Severity::TooLarge) {
OpmLog::debug("Too large residual found with phase " + std::to_string(f.phase) + " for well " + f.well_name);
}
}
// Throw if any too large residual found.
bool too_large_residual_found = (severity == ConvergenceStatus::Severity::TooLarge);
const auto& grid = ebosSimulator_.vanguard().grid();
int value = too_large_residual_found ? 1 : 0;
too_large_residual_found = grid.comm().max(value);
if (too_large_residual_found) {
for (const auto& well : report.too_large_residual_wells) {
OpmLog::debug("Too large residual found with phase " + well.phase_name + " fow well " + well.well_name);
}
OPM_THROW(Opm::NumericalIssue, "Too large residual found!");
}
}
// checking convergence
bool converged_well = report.converged;
bool converged_well = report.converged();
{
const auto& grid = ebosSimulator_.vanguard().grid();
int value = converged_well ? 1 : 0;

View File

@ -71,7 +71,6 @@ namespace Opm
static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq : numEq + 1;
using typename Base::Scalar;
using typename Base::ConvergenceReport;
/// the matrix and vector types for the reservoir
using typename Base::Mat;
@ -123,7 +122,7 @@ namespace Opm
virtual void updateWellStateWithTarget(WellState& well_state) const;
/// check whether the well equations get converged for this well
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg) const;
virtual ConvergenceStatus getWellConvergence(const std::vector<double>& B_avg) const;
/// Ax = Ax - C D^-1 B x
virtual void apply(const BVector& x, BVector& Ax) const;

View File

@ -405,7 +405,7 @@ namespace Opm
template <typename TypeTag>
typename MultisegmentWell<TypeTag>::ConvergenceReport
ConvergenceStatus
MultisegmentWell<TypeTag>::
getWellConvergence(const std::vector<double>& B_avg) const
{
@ -419,62 +419,77 @@ namespace Opm
}
}
using CS = ConvergenceStatus;
CS::WellFailure::Type ctrltype = CS::WellFailure::Type::Mb;
switch(well_controls_get_current_type(well_controls_)) {
case THP:
ctrltype = CS::WellFailure::Type::CtrlTHP;
break;
case BHP:
ctrltype = CS::WellFailure::Type::CtrlBHP;
break;
case RESERVOIR_RATE:
case SURFACE_RATE:
ctrltype = CS::WellFailure::Type::CtrlRate;
break;
default:
OPM_THROW(std::runtime_error, "Unknown well control control types for well " << name());
}
std::vector<double> maximum_residual(numWellEq, 0.0);
ConvergenceReport report;
ConvergenceStatus report;
// TODO: the following is a little complicated, maybe can be simplified in some way?
for (int seg = 0; seg < numberOfSegments(); ++seg) {
for (int eq_idx = 0; eq_idx < numWellEq; ++eq_idx) {
for (int eq_idx = 0; eq_idx < numWellEq; ++eq_idx) {
for (int seg = 0; seg < numberOfSegments(); ++seg) {
if (eq_idx < num_components_) { // phase or component mass equations
const double flux_residual = B_avg[eq_idx] * abs_residual[seg][eq_idx];
// TODO: the report can not handle the segment number yet.
if (std::isnan(flux_residual)) {
report.nan_residual_found = true;
const auto& compName = FluidSystem::componentName(Indices::activeToCanonicalComponentIndex(eq_idx));
const typename ConvergenceReport::ProblemWell problem_well = {name(), compName};
report.nan_residual_wells.push_back(problem_well);
} else if (flux_residual > param_.max_residual_allowed_) {
report.too_large_residual_found = true;
const auto& compName = FluidSystem::componentName(Indices::activeToCanonicalComponentIndex(eq_idx));
const typename ConvergenceReport::ProblemWell problem_well = {name(), compName};
report.nan_residual_wells.push_back(problem_well);
} else { // it is a normal residual
if (flux_residual > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = flux_residual;
}
if (flux_residual > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = flux_residual;
}
} else { // pressure equation
// TODO: we should distinguish the rate control equations, bhp control equations
// and the oridnary pressure equations
const double pressure_residal = abs_residual[seg][eq_idx];
const std::string eq_name("Pressure");
if (std::isnan(pressure_residal)) {
report.nan_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), eq_name};
report.nan_residual_wells.push_back(problem_well);
} else if (std::isinf(pressure_residal)) {
report.too_large_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), eq_name};
report.nan_residual_wells.push_back(problem_well);
} else { // it is a normal residual
if (pressure_residal > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = pressure_residal;
} else { // pressure or control equation
if (seg == 0) {
// Control equation
const double control_residual = abs_residual[seg][eq_idx];
if (std::isnan(control_residual)) {
report.setWellFailed({ctrltype, CS::Severity::NotANumber, -1, name()});
} else if (control_residual > param_.max_residual_allowed_) {
report.setWellFailed({ctrltype, CS::Severity::TooLarge, -1, name()});
} else if (control_residual > param_.tolerance_wells_) {
report.setWellFailed({ctrltype, CS::Severity::Normal, -1, name()});
}
} else {
// Pressure equation
const double pressure_residual = abs_residual[seg][eq_idx];
if (pressure_residual > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = pressure_residual;
}
}
}
}
}
if ( !(report.nan_residual_found || report.too_large_residual_found) ) { // no abnormal residual value found
// check convergence for flux residuals
for ( int comp_idx = 0; comp_idx < num_components_; ++comp_idx)
{
report.converged = report.converged && (maximum_residual[comp_idx] < param_.tolerance_wells_);
for (int eq_idx = 0; eq_idx < numWellEq; ++eq_idx) {
if (eq_idx < num_components_) { // phase or component mass equations
const double flux_residual = maximum_residual[eq_idx];
// TODO: the report can not handle the segment number yet.
if (std::isnan(flux_residual)) {
report.setWellFailed({CS::WellFailure::Type::Mb, CS::Severity::NotANumber, eq_idx, name()});
} else if (flux_residual > param_.max_residual_allowed_) {
report.setWellFailed({CS::WellFailure::Type::Mb, CS::Severity::TooLarge, eq_idx, name()});
} else if (flux_residual > param_.tolerance_wells_) {
report.setWellFailed({CS::WellFailure::Type::Mb, CS::Severity::Normal, eq_idx, name()});
}
} else { // pressure equation
const double pressure_residual = maximum_residual[eq_idx];
if (std::isnan(pressure_residual)) {
report.setWellFailed({CS::WellFailure::Type::Pressure, CS::Severity::NotANumber, -1, name()});
} else if (pressure_residual > param_.max_residual_allowed_) {
report.setWellFailed({CS::WellFailure::Type::Pressure, CS::Severity::TooLarge, -1, name()});
} else if (pressure_residual > param_.tolerance_pressure_ms_wells_) {
report.setWellFailed({CS::WellFailure::Type::Pressure, CS::Severity::Normal, -1, name()});
}
}
report.converged = report.converged && (maximum_residual[SPres] < param_.tolerance_pressure_ms_wells_);
} else { // abnormal values found and no need to check the convergence
report.converged = false;
}
return report;
@ -1726,9 +1741,8 @@ namespace Opm
// const std::vector<double> B {0.8, 0.8, 0.008};
const std::vector<double> B {0.5, 0.5, 0.005};
const ConvergenceReport report = getWellConvergence(B);
if (report.converged) {
const auto report = getWellConvergence(B);
if (report.converged()) {
break;
}

View File

@ -86,7 +86,6 @@ namespace Opm
static const int Bhp = numWellEq - numWellControlEq;
using typename Base::Scalar;
using typename Base::ConvergenceReport;
using Base::name;
@ -145,7 +144,7 @@ namespace Opm
virtual void updateWellStateWithTarget(WellState& well_state) const;
/// check whether the well equations get converged for this well
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg) const;
virtual ConvergenceStatus getWellConvergence(const std::vector<double>& B_avg) const;
/// Ax = Ax - C D^-1 B x
virtual void apply(const BVector& x, BVector& Ax) const;

View File

@ -1483,7 +1483,7 @@ namespace Opm
template<typename TypeTag>
typename StandardWell<TypeTag>::ConvergenceReport
ConvergenceStatus
StandardWell<TypeTag>::
getWellConvergence(const std::vector<double>& B_avg) const
{
@ -1508,7 +1508,9 @@ namespace Opm
well_flux_residual[compIdx] = B_avg[compIdx] * res[compIdx];
}
ConvergenceReport report;
ConvergenceStatus report;
using CS = ConvergenceStatus;
CS::WellFailure::Type type = CS::WellFailure::Type::Mb;
// checking if any NaN or too large residuals found
for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx) {
if (!FluidSystem::phaseIsActive(phaseIdx)) {
@ -1517,62 +1519,46 @@ namespace Opm
const unsigned canonicalCompIdx = FluidSystem::solventComponentIndex(phaseIdx);
const std::string& compName = FluidSystem::componentName(canonicalCompIdx);
const unsigned compIdx = Indices::canonicalToActiveComponentIndex(canonicalCompIdx);
const int compIdx = Indices::canonicalToActiveComponentIndex(canonicalCompIdx);
if (std::isnan(well_flux_residual[compIdx])) {
report.nan_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), compName};
report.nan_residual_wells.push_back(problem_well);
} else {
if (well_flux_residual[compIdx] > maxResidualAllowed) {
report.too_large_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), compName};
report.too_large_residual_wells.push_back(problem_well);
}
report.setWellFailed({type, CS::Severity::NotANumber, compIdx, name()});
} else if (well_flux_residual[compIdx] > maxResidualAllowed) {
report.setWellFailed({type, CS::Severity::TooLarge, compIdx, name()});
} else if (well_flux_residual[compIdx] > tol_wells) {
report.setWellFailed({type, CS::Severity::Normal, compIdx, name()});
}
}
// processing the residual of the well control equation
const double well_control_residual = res[numWellEq - 1];
// TODO: we should have better way to specify the control equation tolerance
double control_tolerance = 0.;
switch(well_controls_get_current_type(well_controls_)) {
case THP:
type = CS::WellFailure::Type::CtrlTHP;
control_tolerance = 1.e3; // 0.01 bar
break;
case BHP: // pressure type of control
type = CS::WellFailure::Type::CtrlBHP;
control_tolerance = 1.e3; // 0.01 bar
break;
case RESERVOIR_RATE:
case SURFACE_RATE:
type = CS::WellFailure::Type::CtrlRate;
control_tolerance = 1.e-4; // smaller tolerance for rate control
break;
default:
OPM_THROW(std::runtime_error, "Unknown well control control types for well " << name());
}
const bool control_eq_converged = well_control_residual < control_tolerance;
const int dummy_component = -1;
if (std::isnan(well_control_residual)) {
report.nan_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), "control"};
report.nan_residual_wells.push_back(problem_well);
} else {
// TODO: for pressure control equations, it can be pretty big during Newton iteration
if (well_control_residual > maxResidualAllowed * 10.) {
report.too_large_residual_found = true;
const typename ConvergenceReport::ProblemWell problem_well = {name(), "control"};
report.too_large_residual_wells.push_back(problem_well);
}
}
if ( !(report.nan_residual_found || report.too_large_residual_found) ) { // no abnormal residual value found
// check convergence
for ( int compIdx = 0; compIdx < num_components_; ++compIdx )
{
report.converged = report.converged && (well_flux_residual[compIdx] < tol_wells) && control_eq_converged;
}
} else { // abnormal values found and no need to check the convergence
report.converged = false;
report.setWellFailed({type, CS::Severity::NotANumber, dummy_component, name()});
} else if (well_control_residual > maxResidualAllowed * 10.) {
report.setWellFailed({type, CS::Severity::TooLarge, dummy_component, name()});
} else if ( well_control_residual > control_tolerance) {
report.setWellFailed({type, CS::Severity::Normal, dummy_component, name()});
}
return report;

View File

@ -44,6 +44,7 @@
#include <opm/autodiff/BlackoilModelParametersEbos.hpp>
#include <opm/autodiff/RateConverter.hpp>
#include <opm/simulators/timestepping/ConvergenceStatus.hpp>
#include <opm/simulators/WellSwitchingLogger.hpp>
#include<dune/common/fmatrix.hh>
@ -137,38 +138,7 @@ namespace Opm
virtual void initPrimaryVariablesEvaluation() const = 0;
/// a struct to collect information about the convergence checking
struct ConvergenceReport {
struct ProblemWell {
std::string well_name;
std::string phase_name;
};
bool converged = true;
bool nan_residual_found = false;
std::vector<ProblemWell> nan_residual_wells;
// We consider Inf is large residual here
bool too_large_residual_found = false;
std::vector<ProblemWell> too_large_residual_wells;
ConvergenceReport& operator+=(const ConvergenceReport& rhs) {
converged = converged && rhs.converged;
nan_residual_found = nan_residual_found || rhs.nan_residual_found;
if (rhs.nan_residual_found) {
for (const ProblemWell& well : rhs.nan_residual_wells) {
nan_residual_wells.push_back(well);
}
}
too_large_residual_found = too_large_residual_found || rhs.too_large_residual_found;
if (rhs.too_large_residual_found) {
for (const ProblemWell& well : rhs.too_large_residual_wells) {
too_large_residual_wells.push_back(well);
}
}
return *this;
}
};
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg) const = 0;
virtual ConvergenceStatus getWellConvergence(const std::vector<double>& B_avg) const = 0;
virtual void solveEqAndUpdateWellState(WellState& well_state) = 0;

View File

@ -932,10 +932,8 @@ namespace Opm
do {
assembleWellEq(ebosSimulator, dt, well_state, true);
ConvergenceReport report;
report = getWellConvergence(B_avg);
converged = report.converged;
auto report = getWellConvergence(B_avg);
converged = report.converged();
if (converged) {
break;
}