Capture Timestep's Non-Linear Convergence History

This enables outputting a formatted record of the limiting MB and
CNV quantities as time and non-linear iterations progress.  This, in
turn, is intended for diagnostic and analysis purposes and will not
be output unless specifically requested.

In particular, add a new type,

    ConvergenceReport::ReservoirConvergenceMetric

which captures the convergence metric type (MB or CNV) along with
the associate phase and numerical value of the convergence metric.
We add a vector of these convergence metric objects as a new data
member of the ConvergenceReport.

Finally, foreshadowing the intended use case, also store the report
time in the ConvergenceReport object.
This commit is contained in:
Bård Skaflestad 2022-12-08 17:16:34 +01:00
parent 8997437ede
commit 3c63a7aa6d
3 changed files with 99 additions and 8 deletions

View File

@ -888,7 +888,8 @@ namespace Opm {
return grid_.comm().sum(errorPV); return grid_.comm().sum(errorPV);
} }
ConvergenceReport getReservoirConvergence(const double dt, ConvergenceReport getReservoirConvergence(const double reportTime,
const double dt,
const int iteration, const int iteration,
std::vector<Scalar>& B_avg, std::vector<Scalar>& B_avg,
std::vector<Scalar>& residual_norms) std::vector<Scalar>& residual_norms)
@ -927,7 +928,7 @@ namespace Opm {
} }
// Create convergence report. // Create convergence report.
ConvergenceReport report; ConvergenceReport report{reportTime};
using CR = ConvergenceReport; using CR = ConvergenceReport;
for (int compIdx = 0; compIdx < numComp; ++compIdx) { for (int compIdx = 0; compIdx < numComp; ++compIdx) {
double res[2] = { mass_balance_residual[compIdx], CNV[compIdx] }; double res[2] = { mass_balance_residual[compIdx], CNV[compIdx] };
@ -953,6 +954,7 @@ namespace Opm {
} else if (res[ii] > tol[ii]) { } else if (res[ii] > tol[ii]) {
report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx}); report.setReservoirFailed({types[ii], CR::Severity::Normal, compIdx});
} }
report.setReservoirConvergenceMetric(types[ii], compIdx, res[ii]);
} }
} }
@ -1003,7 +1005,9 @@ namespace Opm {
{ {
// Get convergence reports for reservoir and wells. // Get convergence reports for reservoir and wells.
std::vector<Scalar> B_avg(numEq, 0.0); std::vector<Scalar> B_avg(numEq, 0.0);
auto report = getReservoirConvergence(timer.currentStepLength(), iteration, B_avg, residual_norms); auto report = getReservoirConvergence(timer.simulationTimeElapsed(),
timer.currentStepLength(),
iteration, B_avg, residual_norms);
report += wellModel().getWellConvergence(B_avg, /*checkWellGroupControls*/report.converged()); report += wellModel().getWellConvergence(B_avg, /*checkWellGroupControls*/report.converged());
return report; return report;

View File

@ -21,9 +21,11 @@
#ifndef OPM_CONVERGENCEREPORT_HEADER_INCLUDED #ifndef OPM_CONVERGENCEREPORT_HEADER_INCLUDED
#define OPM_CONVERGENCEREPORT_HEADER_INCLUDED #define OPM_CONVERGENCEREPORT_HEADER_INCLUDED
#include <algorithm>
#include <cassert> #include <cassert>
#include <numeric> #include <numeric>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace Opm namespace Opm
@ -61,6 +63,21 @@ namespace Opm
Severity severity_; Severity severity_;
int phase_; 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 class WellFailure
{ {
public: public:
@ -83,7 +100,13 @@ namespace Opm
// ----------- Mutating member functions ----------- // ----------- Mutating member functions -----------
ConvergenceReport() ConvergenceReport()
: status_{AllGood} : ConvergenceReport{0.0}
{
}
explicit ConvergenceReport(const double reportTime)
: reportTime_{reportTime}
, status_{AllGood}
, res_failures_{} , res_failures_{}
, well_failures_{} , well_failures_{}
, wellGroupTargetsViolated_(false) , wellGroupTargetsViolated_(false)
@ -110,6 +133,12 @@ namespace Opm
well_failures_.push_back(wf); well_failures_.push_back(wf);
} }
template <typename... Args>
void setReservoirConvergenceMetric(Args&&... args)
{
this->res_convergence_.emplace_back(std::forward<Args>(args)...);
}
void setWellGroupTargetsViolated(const bool wellGroupTargetsViolated) void setWellGroupTargetsViolated(const bool wellGroupTargetsViolated)
{ {
wellGroupTargetsViolated_ = wellGroupTargetsViolated; wellGroupTargetsViolated_ = wellGroupTargetsViolated;
@ -117,9 +146,11 @@ namespace Opm
ConvergenceReport& operator+=(const ConvergenceReport& other) ConvergenceReport& operator+=(const ConvergenceReport& other)
{ {
reportTime_ = std::max(reportTime_, other.reportTime_);
status_ = static_cast<Status>(status_ | other.status_); status_ = static_cast<Status>(status_ | other.status_);
res_failures_.insert(res_failures_.end(), other.res_failures_.begin(), other.res_failures_.end()); res_failures_.insert(res_failures_.end(), other.res_failures_.begin(), other.res_failures_.end());
well_failures_.insert(well_failures_.end(), other.well_failures_.begin(), other.well_failures_.end()); well_failures_.insert(well_failures_.end(), other.well_failures_.begin(), other.well_failures_.end());
res_convergence_.insert(res_convergence_.end(), other.res_convergence_.begin(), other.res_convergence_.end());
assert(reservoirFailed() != res_failures_.empty()); assert(reservoirFailed() != res_failures_.empty());
assert(wellFailed() != well_failures_.empty()); assert(wellFailed() != well_failures_.empty());
wellGroupTargetsViolated_ = (wellGroupTargetsViolated_ || other.wellGroupTargetsViolated_); wellGroupTargetsViolated_ = (wellGroupTargetsViolated_ || other.wellGroupTargetsViolated_);
@ -128,6 +159,11 @@ namespace Opm
// ----------- Const member functions (queries) ----------- // ----------- Const member functions (queries) -----------
double reportTime() const
{
return reportTime_;
}
bool converged() const bool converged() const
{ {
return (status_ == AllGood) && !wellGroupTargetsViolated_; return (status_ == AllGood) && !wellGroupTargetsViolated_;
@ -148,6 +184,11 @@ namespace Opm
return res_failures_; return res_failures_;
} }
const std::vector<ReservoirConvergenceMetric>& reservoirConvergence() const
{
return res_convergence_;
}
const std::vector<WellFailure>& wellFailures() const const std::vector<WellFailure>& wellFailures() const
{ {
return well_failures_; return well_failures_;
@ -172,9 +213,11 @@ namespace Opm
private: private:
// ----------- Member variables ----------- // ----------- Member variables -----------
double reportTime_;
Status status_; Status status_;
std::vector<ReservoirFailure> res_failures_; std::vector<ReservoirFailure> res_failures_;
std::vector<WellFailure> well_failures_; std::vector<WellFailure> well_failures_;
std::vector<ReservoirConvergenceMetric> res_convergence_;
bool wellGroupTargetsViolated_; bool wellGroupTargetsViolated_;
}; };

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2018, 2022 Equinor ASA.
Copyright 2018 SINTEF Digital, Mathematics and Cybernetics. Copyright 2018 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2018 Equinor.
This file is part of the Open Porous Media project (OPM). This file is part of the Open Porous Media project (OPM).
@ -43,6 +43,18 @@ namespace
MPI_Pack(&phase, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator); MPI_Pack(&phase, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
} }
void packReservoirConvergenceMetric(const ConvergenceReport::ReservoirConvergenceMetric& m,
std::vector<char>& buf,
int& offset, MPI_Comm mpi_communicator)
{
int type = static_cast<int>(m.type());
int phase = m.phase();
double value = m.value();
MPI_Pack(&type, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
MPI_Pack(&phase, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
MPI_Pack(&value, 1, MPI_DOUBLE, buf.data(), buf.size(), &offset, mpi_communicator);
}
void packWellFailure(const ConvergenceReport::WellFailure& f, void packWellFailure(const ConvergenceReport::WellFailure& f,
std::vector<char>& buf, std::vector<char>& buf,
int& offset, MPI_Comm mpi_communicator) int& offset, MPI_Comm mpi_communicator)
@ -65,12 +77,21 @@ namespace
// Pack the data. // Pack the data.
// Status will not be packed, it is possible to deduce from the other data. // Status will not be packed, it is possible to deduce from the other data.
// Reservoir failures. // Reservoir failures.
double reportTime = local_report.reportTime();
MPI_Pack(&reportTime, 1, MPI_DOUBLE, buf.data(), buf.size(), &offset, mpi_communicator);
const auto rf = local_report.reservoirFailures(); const auto rf = local_report.reservoirFailures();
int num_rf = rf.size(); int num_rf = rf.size();
MPI_Pack(&num_rf, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator); MPI_Pack(&num_rf, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
for (const auto& f : rf) { for (const auto& f : rf) {
packReservoirFailure(f, buf, offset, mpi_communicator); packReservoirFailure(f, buf, offset, mpi_communicator);
} }
// Reservoir convergence metrics.
const auto rm = local_report.reservoirConvergence();
int num_rm = rm.size();
MPI_Pack(&num_rm, 1, MPI_INT, buf.data(), buf.size(), &offset, mpi_communicator);
for (const auto& m : rm) {
packReservoirConvergenceMetric(m, buf, offset, mpi_communicator);
}
// Well failures. // Well failures.
const auto wf = local_report.wellFailures(); const auto wf = local_report.wellFailures();
int num_wf = wf.size(); int num_wf = wf.size();
@ -84,13 +105,16 @@ namespace
{ {
int int_pack_size = 0; int int_pack_size = 0;
MPI_Pack_size(1, MPI_INT, mpi_communicator, &int_pack_size); MPI_Pack_size(1, MPI_INT, mpi_communicator, &int_pack_size);
int double_pack_size = 0;
MPI_Pack_size(1, MPI_DOUBLE, mpi_communicator, &double_pack_size);
const int num_rf = local_report.reservoirFailures().size(); const int num_rf = local_report.reservoirFailures().size();
const int num_rm = local_report.reservoirConvergence().size();
const int num_wf = local_report.wellFailures().size(); const int num_wf = local_report.wellFailures().size();
int wellnames_length = 0; int wellnames_length = 0;
for (const auto& f : local_report.wellFailures()) { for (const auto& f : local_report.wellFailures()) {
wellnames_length += (f.wellName().size() + 1); wellnames_length += (f.wellName().size() + 1);
} }
return (2 + 3*num_rf + 4*num_wf) * int_pack_size + wellnames_length; return (3 + 3*num_rf + 2*num_rm + 4*num_wf)*int_pack_size + (1 + 1*num_rm)*double_pack_size + wellnames_length;
} }
ConvergenceReport::ReservoirFailure unpackReservoirFailure(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator) ConvergenceReport::ReservoirFailure unpackReservoirFailure(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator)
@ -107,6 +131,19 @@ namespace
phase); phase);
} }
ConvergenceReport::ReservoirConvergenceMetric
unpackReservoirConvergenceMetric(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator)
{
int type = -1;
int phase = -1;
double value = -1.0;
auto* data = const_cast<char*>(recv_buffer.data());
MPI_Unpack(data, recv_buffer.size(), &offset, &type, 1, MPI_INT, mpi_communicator);
MPI_Unpack(data, recv_buffer.size(), &offset, &phase, 1, MPI_INT, mpi_communicator);
MPI_Unpack(data, recv_buffer.size(), &offset, &value, 1, MPI_DOUBLE, mpi_communicator);
return { static_cast<ConvergenceReport::ReservoirFailure::Type>(type), phase, value };
}
ConvergenceReport::WellFailure unpackWellFailure(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator) ConvergenceReport::WellFailure unpackWellFailure(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator)
{ {
int type = -1; int type = -1;
@ -129,14 +166,21 @@ namespace
ConvergenceReport unpackSingleConvergenceReport(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator) ConvergenceReport unpackSingleConvergenceReport(const std::vector<char>& recv_buffer, int& offset, MPI_Comm mpi_communicator)
{ {
ConvergenceReport cr;
int num_rf = -1;
auto* data = const_cast<char*>(recv_buffer.data()); 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};
int num_rf = -1;
MPI_Unpack(data, recv_buffer.size(), &offset, &num_rf, 1, MPI_INT, mpi_communicator); MPI_Unpack(data, recv_buffer.size(), &offset, &num_rf, 1, MPI_INT, mpi_communicator);
for (int rf = 0; rf < num_rf; ++rf) { for (int rf = 0; rf < num_rf; ++rf) {
ConvergenceReport::ReservoirFailure f = unpackReservoirFailure(recv_buffer, offset, mpi_communicator); ConvergenceReport::ReservoirFailure f = unpackReservoirFailure(recv_buffer, offset, mpi_communicator);
cr.setReservoirFailed(f); cr.setReservoirFailed(f);
} }
int num_rm = -1;
MPI_Unpack(data, recv_buffer.size(), &offset, &num_rm, 1, MPI_INT, mpi_communicator);
for (int rm = 0; rm < num_rm; ++rm) {
cr.setReservoirConvergenceMetric(unpackReservoirConvergenceMetric(recv_buffer, offset, mpi_communicator));
}
int num_wf = -1; int num_wf = -1;
MPI_Unpack(data, recv_buffer.size(), &offset, &num_wf, 1, MPI_INT, mpi_communicator); MPI_Unpack(data, recv_buffer.size(), &offset, &num_wf, 1, MPI_INT, mpi_communicator);
for (int wf = 0; wf < num_wf; ++wf) { for (int wf = 0; wf < num_wf; ++wf) {