Merge pull request #5302 from bska/more-infostep-cnv

Report CNV Violation Pore-Volume Fraction to INFOITER
This commit is contained in:
Atgeirr Flø Rasmussen 2024-05-06 14:13:21 +02:00 committed by GitHub
commit ae7307d2c3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 190 additions and 61 deletions

View File

@ -897,7 +897,8 @@ namespace Opm {
Vector R_sum(numComp, 0.0 );
Vector maxCoeff(numComp, std::numeric_limits< Scalar >::lowest() );
std::vector<int> maxCoeffCell(numComp, -1);
const auto [ pvSumLocal, numAquiferPvSumLocal] = localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
const auto [ pvSumLocal, numAquiferPvSumLocal] =
this->localConvergenceData(R_sum, maxCoeff, B_avg, maxCoeffCell);
// compute global sum and max of quantities
const auto [ pvSum, numAquiferPvSum ] =
@ -905,9 +906,9 @@ namespace Opm {
numAquiferPvSumLocal,
R_sum, maxCoeff, B_avg);
auto cnvErrorPvFraction = computeCnvErrorPv(B_avg, dt);
cnvErrorPvFraction /= (pvSum - numAquiferPvSum);
const auto cnvErrorPvFraction =
computeCnvErrorPv(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.
@ -927,7 +928,7 @@ namespace Opm {
const bool relax_pv_fraction_cnv = cnvErrorPvFraction < param_.relaxed_max_pv_fraction_;
const bool use_relaxed_cnv = relax_final_iteration_cnv || relax_pv_fraction_cnv || relax_iter_cnv;
if (relax_final_iteration_mb || relax_final_iteration_cnv) {
if (relax_final_iteration_mb || relax_final_iteration_cnv) {
if ( terminal_output_ ) {
std::string message = "Number of newton iterations reached its maximum try to continue with relaxed tolerances:";
if (relax_final_iteration_mb)
@ -944,7 +945,7 @@ namespace Opm {
// Finish computation
std::vector<Scalar> CNV(numComp);
std::vector<Scalar> mass_balance_residual(numComp);
for ( int compIdx = 0; compIdx < numComp; ++compIdx )
for (int compIdx = 0; compIdx < numComp; ++compIdx)
{
CNV[compIdx] = B_avg[compIdx] * dt * maxCoeff[compIdx];
mass_balance_residual[compIdx] = std::abs(B_avg[compIdx]*R_sum[compIdx]) * dt / pvSum;
@ -953,12 +954,15 @@ namespace Opm {
// Create convergence report.
ConvergenceReport report{reportTime};
report.setPoreVolCnvViolationFraction(cnvErrorPvFraction, pvSum - numAquiferPvSum);
using CR = ConvergenceReport;
for (int compIdx = 0; compIdx < numComp; ++compIdx) {
double res[2] = { mass_balance_residual[compIdx], CNV[compIdx] };
CR::ReservoirFailure::Type types[2] = { CR::ReservoirFailure::Type::MassBalance,
const double res[2] = { mass_balance_residual[compIdx], CNV[compIdx] };
const CR::ReservoirFailure::Type types[2] = { CR::ReservoirFailure::Type::MassBalance,
CR::ReservoirFailure::Type::Cnv };
double tol[2] = { tol_mb, tol_cnv };
const double tol[2] = { tol_mb, tol_cnv };
for (int ii : {0, 1}) {
if (std::isnan(res[ii])) {
report.setReservoirFailed({types[ii], CR::Severity::NotANumber, compIdx});

View File

@ -656,6 +656,7 @@ private:
} else if (res[ii] > tol[ii]) {
report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
}
report.setReservoirConvergenceMetric(types[ii], compIdx, res[ii]);
}
}

View File

@ -24,6 +24,7 @@
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
#include <algorithm>
#include <array>
#include <cassert>
#include <condition_variable>
#include <cstddef>
@ -40,12 +41,44 @@
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace {
auto fixedHeaders() noexcept
{
using namespace std::literals::string_literals;
return std::array {
"ReportStep"s,
"TimeStep"s,
"Time"s,
"CnvErrPvFrac"s,
"Iteration"s,
};
}
template <typename HeaderSequence>
auto maxHeaderSize(const HeaderSequence& headers)
{
using sz_t = std::remove_cv_t<std::remove_reference_t<
decltype(std::declval<HeaderSequence>().front().size())>>;
if (headers.empty()) {
return sz_t{0};
}
return std::accumulate(headers.begin(), headers.end(), sz_t{1},
[](const auto m, const auto& header)
{
return std::max(m, header.size() + 1);
});
}
std::string
formatMetricColumn(const Opm::ConvergenceOutputThread::ComponentToPhaseName& getPhaseName,
const Opm::ConvergenceReport::ReservoirConvergenceMetric& metric)
@ -65,45 +98,49 @@ namespace {
[&getPhaseName](const std::string::size_type maxChar,
const Opm::ConvergenceReport::ReservoirConvergenceMetric& metric)
{
return std::max(maxChar, formatMetricColumn(getPhaseName, metric).size());
return std::max(maxChar, formatMetricColumn(getPhaseName, metric).size() + 1);
});
}
std::string::size_type
std::pair<std::string::size_type, std::string::size_type>
writeConvergenceHeader(std::ostream& os,
const Opm::ConvergenceOutputThread::ComponentToPhaseName& getPhaseName,
const Opm::ConvergenceReportQueue::OutputRequest& firstRequest)
{
const auto minColSize = std::string::size_type{11};
const auto initial_headers = fixedHeaders();
const auto minColSize = maxHeaderSize(initial_headers);
os << std::right << std::setw(minColSize) << "ReportStep" << ' '
<< std::right << std::setw(minColSize) << "TimeStep" << ' '
<< std::right << std::setw(minColSize) << "Time" << ' '
<< std::right << std::setw(minColSize) << "Iteration";
{
auto leadingSpace = false;
const auto& metrics = firstRequest.reports.front().reservoirConvergence();
const auto maxChar = maxColHeaderSize(minColSize, getPhaseName, metrics);
for (const auto& columnHeader : initial_headers) {
if (leadingSpace) { os << ' '; }
os << std::right << std::setw(minColSize) << columnHeader;
leadingSpace = true;
}
}
const auto& metrics = firstRequest.reports.front().reservoirConvergence();
const auto headerSize = maxColHeaderSize(minColSize, getPhaseName, metrics);
for (const auto& metric : metrics) {
os << std::right << std::setw(maxChar + 1)
os << std::right << std::setw(headerSize)
<< formatMetricColumn(getPhaseName, metric);
}
// Note: Newline character intentionally placed in separate output
// request to not influence right-justification of column header.
os << std::right << std::setw(maxChar + 1) << "WellStatus" << '\n';
os << std::right << std::setw(headerSize) << "WellStatus" << '\n';
return maxChar;
return { minColSize, headerSize };
}
void writeConvergenceRequest(std::ostream& os,
const Opm::ConvergenceOutputThread::ConvertToTimeUnits& convertTime,
std::string::size_type colSize,
const std::string::size_type firstColSize,
const std::string::size_type colSize,
const Opm::ConvergenceReportQueue::OutputRequest& request)
{
const auto firstColSize = std::string::size_type{11};
os.setf(std::ios_base::scientific);
auto iter = 0;
@ -112,19 +149,23 @@ namespace {
<< std::setw(firstColSize) << request.currentStep << ' '
<< std::setprecision(4) << std::setw(firstColSize)
<< convertTime(report.reportTime()) << ' '
<< std::setprecision(4) << std::setw(firstColSize)
<< report.cnvViolatedPvFraction() << ' '
<< std::setw(firstColSize) << iter;
for (const auto& metric : report.reservoirConvergence()) {
os << std::setprecision(4) << std::setw(colSize + 1) << metric.value();
os << std::setprecision(4) << std::setw(colSize) << metric.value();
}
os << std::right << std::setw(colSize + 1)
os << std::right << std::setw(colSize)
<< (report.wellFailed() ? "FAIL" : "CONV");
if (report.wellFailed()) {
for (const auto& wf : report.wellFailures()) {
os << " " << to_string(wf);
os << ' ' << to_string(wf);
}
}
os << '\n';
++iter;
@ -160,6 +201,7 @@ private:
ComponentToPhaseName getPhaseName_{};
ConvertToTimeUnits convertTime_{};
std::optional<std::ofstream> infoIter_{};
std::string::size_type firstColSize_{0};
std::string::size_type colSize_{0};
bool haveOutputIterHeader_{false};
bool finalRequestWritten_{false};
@ -203,7 +245,7 @@ writeIterInfo(const std::vector<ConvergenceReportQueue::OutputRequest>& requests
}
if (! this->haveOutputIterHeader_) {
this->colSize_ =
std::tie(this->firstColSize_, this->colSize_) =
writeConvergenceHeader(this->infoIter_.value(),
this->getPhaseName_,
requests.front());
@ -213,6 +255,7 @@ writeIterInfo(const std::vector<ConvergenceReportQueue::OutputRequest>& requests
for (const auto& request : requests) {
writeConvergenceRequest(this->infoIter_.value(),
this->convertTime_,
this->firstColSize_,
this->colSize_,
request);

View File

@ -363,13 +363,16 @@ public:
+ schedule().seconds(timer.currentStepNum()),
timer.currentStepLength());
simulator_.setEpisodeIndex(timer.currentStepNum());
if (serializer_.shouldLoad()) {
wellModel_().prepareDeserialize(serializer_.loadStep() - 1);
serializer_.loadState();
simulator_.model().invalidateAndUpdateIntensiveQuantities(/*timeIdx=*/0);
}
solver_->model().beginReportStep();
bool enableTUNING = Parameters::get<TypeTag, Properties::EnableTuning>();
this->solver_->model().beginReportStep();
const bool enableTUNING = Parameters::get<TypeTag, Properties::EnableTuning>();
// If sub stepping is enabled allow the solver to sub cycle
// in case the report steps are too large for the solver to converge
@ -442,10 +445,17 @@ public:
report_.success.solver_time += solverTimer_->secsSinceStart();
if (this->grid().comm().rank() == 0) {
// Grab the step convergence reports that are new since last we were here.
const auto& reps = solver_->model().stepReports();
this->writeConvergenceOutput(std::vector<StepReport>{reps.begin() + already_reported_steps_, reps.end()});
already_reported_steps_ = reps.size();
// Grab the step convergence reports that are new since last we
// were here.
const auto& reps = this->solver_->model().stepReports();
auto reports = std::vector<StepReport> {
reps.begin() + this->already_reported_steps_, reps.end()
};
this->writeConvergenceOutput(std::move(reports));
this->already_reported_steps_ = reps.size();
}
// Increment timer, remember well state.

View File

@ -40,56 +40,78 @@ namespace Opm
// ----------- Types -----------
enum Status { AllGood = 0,
ReservoirFailed = 1 << 0,
WellFailed = 1 << 1 };
enum struct Severity { None = 0,
Normal = 1,
TooLarge = 2,
NotANumber = 3 };
enum Status {
AllGood = 0,
ReservoirFailed = 1 << 0,
WellFailed = 1 << 1,
};
enum struct Severity {
None = 0,
Normal = 1,
TooLarge = 2,
NotANumber = 3,
};
class ReservoirFailure
{
public:
enum struct Type { Invalid, MassBalance, Cnv };
ReservoirFailure(Type t, Severity s, int phase)
: type_(t), severity_(s), phase_(phase)
{
}
{}
Type type() const { return type_; }
Severity severity() const { return severity_; }
int phase() const { return phase_; }
private:
Type type_;
Severity severity_;
int phase_;
};
class ReservoirConvergenceMetric
{
public:
ReservoirConvergenceMetric(ReservoirFailure::Type t, int phase, double value)
: type_(t), phase_(phase), value_(value)
{
}
{}
ReservoirFailure::Type type() const { return type_; }
int phase() const { return phase_; }
double value() const { return value_; }
private:
ReservoirFailure::Type type_;
int phase_;
double value_;
};
class WellFailure
{
public:
enum struct Type { Invalid, MassBalance, Pressure, ControlBHP, ControlTHP, ControlRate, Unsolvable, WrongFlowDirection };
enum struct Type {
Invalid,
MassBalance,
Pressure,
ControlBHP,
ControlTHP,
ControlRate,
Unsolvable,
WrongFlowDirection,
};
WellFailure(Type t, Severity s, int phase, const std::string& well_name)
: type_(t), severity_(s), phase_(phase), well_name_(well_name)
{
}
{}
Type type() const { return type_; }
Severity severity() const { return severity_; }
int phase() const { return phase_; }
const std::string& wellName() const { return well_name_; }
private:
Type type_;
Severity severity_;
@ -101,8 +123,7 @@ namespace Opm
ConvergenceReport()
: ConvergenceReport{0.0}
{
}
{}
explicit ConvergenceReport(const double reportTime)
: reportTime_{reportTime}
@ -110,8 +131,7 @@ namespace Opm
, res_failures_{}
, well_failures_{}
, wellGroupTargetsViolated_(false)
{
}
{}
void clear()
{
@ -144,6 +164,13 @@ namespace Opm
wellGroupTargetsViolated_ = wellGroupTargetsViolated;
}
void setPoreVolCnvViolationFraction(const double cnvErrorPvFraction,
const double cnvErrorPvFractionDenom)
{
this->pvFracCnvViol_ = cnvErrorPvFraction;
this->pvFracCnvViolDenom_ = cnvErrorPvFractionDenom;
}
ConvergenceReport& operator+=(const ConvergenceReport& other)
{
reportTime_ = std::max(reportTime_, other.reportTime_);
@ -154,6 +181,21 @@ namespace Opm
assert(reservoirFailed() != res_failures_.empty());
assert(wellFailed() != well_failures_.empty());
wellGroupTargetsViolated_ = (wellGroupTargetsViolated_ || other.wellGroupTargetsViolated_);
if ((this->pvFracCnvViolDenom_ > 0.0) ||
(other.pvFracCnvViolDenom_ > 0.0))
{
this->pvFracCnvViol_ = (this->pvFracCnvViol_ * this->pvFracCnvViolDenom_ +
other.pvFracCnvViol_ * other.pvFracCnvViolDenom_)
/ (this->pvFracCnvViolDenom_ + other.pvFracCnvViolDenom_);
this->pvFracCnvViolDenom_ += other.pvFracCnvViolDenom_;
}
else {
this->pvFracCnvViol_ = 0.0;
this->pvFracCnvViolDenom_ = 0.0;
}
return *this;
}
@ -164,6 +206,16 @@ namespace Opm
return reportTime_;
}
double cnvViolatedPvFraction() const
{
return this->pvFracCnvViol_;
}
std::pair<double, double> cnvViolatedPvFractionPack() const
{
return { this->pvFracCnvViol_, this->pvFracCnvViolDenom_ };
}
bool converged() const
{
return (status_ == AllGood) && !wellGroupTargetsViolated_;
@ -211,7 +263,6 @@ namespace Opm
}
private:
// ----------- Member variables -----------
double reportTime_;
Status status_;
@ -219,6 +270,8 @@ namespace Opm
std::vector<WellFailure> well_failures_;
std::vector<ReservoirConvergenceMetric> res_convergence_;
bool wellGroupTargetsViolated_;
double pvFracCnvViol_{};
double pvFracCnvViolDenom_{};
};
struct StepReport
@ -228,7 +281,6 @@ namespace Opm
std::vector<ConvergenceReport> report;
};
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t);
std::string to_string(const ConvergenceReport::Severity s);

View File

@ -22,6 +22,8 @@
#include <opm/simulators/timestepping/gatherConvergenceReport.hpp>
#include <utility>
#if HAVE_MPI
#include <mpi.h>
@ -76,15 +78,24 @@ namespace
{
// Pack the data.
// Status will not be packed, it is possible to deduce from the other data.
// Reservoir failures.
double reportTime = local_report.reportTime();
MPI_Pack(&reportTime, 1, MPI_DOUBLE, buf.data(), buf.size(), &offset, mpi_communicator);
{
const auto cnvPvFrac = local_report.cnvViolatedPvFractionPack();
MPI_Pack(&cnvPvFrac.first , 1, MPI_DOUBLE, buf.data(), buf.size(), &offset, mpi_communicator);
MPI_Pack(&cnvPvFrac.second, 1, MPI_DOUBLE, buf.data(), buf.size(), &offset, mpi_communicator);
}
// Reservoir failures.
const auto rf = local_report.reservoirFailures();
int num_rf = rf.size();
MPI_Pack(&num_rf, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
for (const auto& f : rf) {
packReservoirFailure(f, buf, offset, mpi_communicator);
}
// Reservoir convergence metrics.
const auto rm = local_report.reservoirConvergence();
int num_rm = rm.size();
@ -92,6 +103,7 @@ namespace
for (const auto& m : rm) {
packReservoirConvergenceMetric(m, buf, offset, mpi_communicator);
}
// Well failures.
const auto wf = local_report.wellFailures();
int num_wf = wf.size();
@ -114,7 +126,7 @@ namespace
for (const auto& f : local_report.wellFailures()) {
wellnames_length += (f.wellName().size() + 1);
}
return (3 + 3*num_rf + 2*num_rm + 4*num_wf)*int_pack_size + (1 + 1*num_rm)*double_pack_size + wellnames_length;
return (3 + 3*num_rf + 2*num_rm + 4*num_wf)*int_pack_size + (3 + 1*num_rm)*double_pack_size + wellnames_length;
}
ConvergenceReport::ReservoirFailure unpackReservoirFailure(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator)
@ -169,7 +181,15 @@ namespace
auto* data = const_cast<char*>(recv_buffer.data());
double reportTime{0.0};
MPI_Unpack(data, recv_buffer.size(), &offset, &reportTime, 1, MPI_DOUBLE, mpi_communicator);
ConvergenceReport cr{reportTime};
auto cnvPvFrac = std::pair { 0.0, 0.0 };
MPI_Unpack(data, recv_buffer.size(), &offset, &cnvPvFrac.first , 1, MPI_DOUBLE, mpi_communicator);
MPI_Unpack(data, recv_buffer.size(), &offset, &cnvPvFrac.second, 1, MPI_DOUBLE, mpi_communicator);
cr.setPoreVolCnvViolationFraction(cnvPvFrac.first, cnvPvFrac.second);
int num_rf = -1;
MPI_Unpack(data, recv_buffer.size(), &offset, &num_rf, 1, MPI_INT, mpi_communicator);
for (int rf = 0; rf < num_rf; ++rf) {

View File

@ -149,9 +149,8 @@ BOOST_AUTO_TEST_CASE(CheckMassBalanceWithinXXXMBE)
BOOST_TEST_MESSAGE("---------------------------------------------------------------------------");
BOOST_CHECK( max_mb[0] < 1.0e-6 );
BOOST_CHECK( max_mb[1] < 1.0e-8 );
BOOST_CHECK( max_mb[2] < 1.0e-10 );
BOOST_CHECK_MESSAGE( max_mb[0] < 1.0e-6, "max_mb[0] (= " << max_mb[0] << ") is not strictly less than 1.0e-6" );
BOOST_CHECK_MESSAGE( max_mb[1] < 1.0e-8, "max_mb[1] (= " << max_mb[1] << ") is not strictly less than 1.0e-8" );
BOOST_CHECK_MESSAGE( max_mb[2] < 1.0e-10, "max_mb[2] (= " << max_mb[1] << ") is not strictly less than 1.0e-10" );
}