Refactor RFT File Writing Code
In preparation of supporting new data types (PLT and segment). Introduce a wrapper class, WellRFTOutputData, which knows about all supported data types and how to emit the RFT record header. Defer specialised data type handling to dedicated record types and, to this end, rename the existing WellRFT class to RFTRecord.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2019 Equinor ASA
|
||||
Copyright (c) 2019, 2022 Equinor ASA
|
||||
Copyright (c) 2016 Statoil ASA
|
||||
Copyright (c) 2013-2015 Andreas Lauser
|
||||
Copyright (c) 2013 SINTEF ICT, Applied Mathematics.
|
||||
@@ -34,13 +34,17 @@
|
||||
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Schedule.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/Connection.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
|
||||
|
||||
#include <opm/input/eclipse/Units/UnitSystem.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -156,32 +160,21 @@ namespace {
|
||||
}
|
||||
} // namespace RftUnits
|
||||
|
||||
class WellRFT
|
||||
// =======================================================================
|
||||
|
||||
class RFTRecord
|
||||
{
|
||||
public:
|
||||
explicit WellRFT(const std::size_t nconn = 0);
|
||||
explicit RFTRecord(const std::size_t nconn = 0);
|
||||
|
||||
void add(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::Connection& conn,
|
||||
const ::Opm::data::Connection& xcon,
|
||||
const double depth);
|
||||
void collectRecordData(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Well& well,
|
||||
const ::Opm::data::Well& wellSol);
|
||||
|
||||
std::size_t nConn() const { return this->i_.size(); }
|
||||
|
||||
const std::vector<int>& conI() const { return this->i_; }
|
||||
const std::vector<int>& conJ() const { return this->j_; }
|
||||
const std::vector<int>& conK() const { return this->k_; }
|
||||
|
||||
const std::vector<float>& depth() const { return this->depth_; }
|
||||
const std::vector<float>& pressure() const { return this->press_; }
|
||||
const std::vector<float>& swat() const { return this->swat_; }
|
||||
const std::vector<float>& sgas() const { return this->sgas_; }
|
||||
|
||||
const std::vector<Opm::EclIO::PaddedOutputString<8>>&
|
||||
hostgrid() const
|
||||
{
|
||||
return this->host_;
|
||||
}
|
||||
void write(::Opm::EclIO::OutputStream::RFT& rftFile) const;
|
||||
|
||||
private:
|
||||
std::vector<int> i_;
|
||||
@@ -194,9 +187,14 @@ namespace {
|
||||
std::vector<float> sgas_;
|
||||
|
||||
std::vector<Opm::EclIO::PaddedOutputString<8>> host_;
|
||||
|
||||
void addConnection(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::Connection& conn,
|
||||
const ::Opm::data::Connection& xcon,
|
||||
const double depth);
|
||||
};
|
||||
|
||||
WellRFT::WellRFT(const std::size_t nconn)
|
||||
RFTRecord::RFTRecord(const std::size_t nconn)
|
||||
{
|
||||
if (nconn == 0) { return; }
|
||||
|
||||
@@ -212,10 +210,54 @@ namespace {
|
||||
this->host_.reserve(nconn);
|
||||
}
|
||||
|
||||
void WellRFT::add(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::Connection& conn,
|
||||
const ::Opm::data::Connection& xcon,
|
||||
const double depth)
|
||||
void RFTRecord::collectRecordData(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Well& well,
|
||||
const ::Opm::data::Well& wellSol)
|
||||
{
|
||||
const auto& xcon = wellSol.connections;
|
||||
|
||||
for (const auto& connection : well.getConnections()) {
|
||||
const auto ix = connection.global_index();
|
||||
|
||||
if (! grid.cellActive(ix)) {
|
||||
// Inactive cell. Ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto xconPos = std::find_if(xcon.begin(), xcon.end(),
|
||||
[ix](const ::Opm::data::Connection& c)
|
||||
{
|
||||
return c.index == ix;
|
||||
});
|
||||
|
||||
if (xconPos == xcon.end()) {
|
||||
// RFT data not available for this connection. Unexpected.
|
||||
continue;
|
||||
}
|
||||
|
||||
this->addConnection(usys, connection, *xconPos, grid.getCellDepth(ix));
|
||||
}
|
||||
}
|
||||
|
||||
void RFTRecord::write(::Opm::EclIO::OutputStream::RFT& rftFile) const
|
||||
{
|
||||
rftFile.write("CONIPOS", this->i_);
|
||||
rftFile.write("CONJPOS", this->j_);
|
||||
rftFile.write("CONKPOS", this->k_);
|
||||
|
||||
rftFile.write("HOSTGRID", this->host_);
|
||||
|
||||
rftFile.write("DEPTH" , this->depth_);
|
||||
rftFile.write("PRESSURE", this->press_);
|
||||
rftFile.write("SWAT" , this->swat_);
|
||||
rftFile.write("SGAS" , this->sgas_);
|
||||
}
|
||||
|
||||
void RFTRecord::addConnection(const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::Connection& conn,
|
||||
const ::Opm::data::Connection& xcon,
|
||||
const double depth)
|
||||
{
|
||||
this->i_.push_back(conn.getI() + 1);
|
||||
this->j_.push_back(conn.getJ() + 1);
|
||||
@@ -236,48 +278,158 @@ namespace {
|
||||
this->host_.emplace_back();
|
||||
}
|
||||
|
||||
WellRFT
|
||||
createWellRFT(const int reportStep,
|
||||
const std::string& wname,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Schedule& sched,
|
||||
const std::vector<Opm::data::Connection>& xcon)
|
||||
// =======================================================================
|
||||
|
||||
class WellRFTOutputData
|
||||
{
|
||||
auto rft = WellRFT{ xcon.size() };
|
||||
public:
|
||||
enum class DataTypes {
|
||||
RFT,
|
||||
};
|
||||
|
||||
for (const auto& conn : sched.getWell(wname, reportStep).getConnections()) {
|
||||
const auto i = static_cast<std::size_t>(conn.getI());
|
||||
const auto j = static_cast<std::size_t>(conn.getJ());
|
||||
const auto k = static_cast<std::size_t>(conn.getK());
|
||||
explicit WellRFTOutputData(const std::vector<DataTypes>& types,
|
||||
const double elapsed,
|
||||
const ::Opm::RestartIO::InteHEAD::TimePoint& timePoint,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Well& well);
|
||||
|
||||
if (! grid.cellActive(i, j, k)) {
|
||||
// Inactive cell. Ignore.
|
||||
void addDynamicData(const Opm::data::Well& wellSol);
|
||||
|
||||
void write(::Opm::EclIO::OutputStream::RFT& rftFile) const;
|
||||
|
||||
private:
|
||||
using DataHandler = std::function<
|
||||
void(const Opm::data::Well& wellSol)
|
||||
>;
|
||||
|
||||
using RecordWriter = std::function<
|
||||
void(::Opm::EclIO::OutputStream::RFT& rftFile)
|
||||
>;
|
||||
|
||||
using CreateTypeHandler = void (WellRFTOutputData::*)();
|
||||
|
||||
std::reference_wrapper<const Opm::UnitSystem> usys_;
|
||||
std::reference_wrapper<const Opm::EclipseGrid> grid_;
|
||||
std::reference_wrapper<const Opm::Well> well_;
|
||||
double elapsed_{};
|
||||
Opm::RestartIO::InteHEAD::TimePoint timeStamp_{};
|
||||
|
||||
std::optional<RFTRecord> rft_{};
|
||||
|
||||
std::vector<DataHandler> dataHandlers_{};
|
||||
std::vector<RecordWriter> recordWriters_{};
|
||||
|
||||
static std::map<DataTypes, CreateTypeHandler> creators_;
|
||||
|
||||
void initialiseRFTHandlers();
|
||||
bool haveOutputData() const;
|
||||
bool haveRFTData() const;
|
||||
|
||||
void writeHeader(::Opm::EclIO::OutputStream::RFT& rftFile) const;
|
||||
|
||||
std::vector<Opm::EclIO::PaddedOutputString<8>> wellETC() const;
|
||||
std::string dataTypeString() const;
|
||||
std::string wellTypeString() const;
|
||||
};
|
||||
|
||||
WellRFTOutputData::WellRFTOutputData(const std::vector<DataTypes>& types,
|
||||
const double elapsed,
|
||||
const ::Opm::RestartIO::InteHEAD::TimePoint& timeStamp,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Well& well)
|
||||
: usys_ { std::cref(usys) }
|
||||
, grid_ { std::cref(grid) }
|
||||
, well_ { std::cref(well) }
|
||||
, elapsed_ { elapsed }
|
||||
, timeStamp_{ timeStamp }
|
||||
{
|
||||
for (const auto& type : types) {
|
||||
auto handler = creators_.find(type);
|
||||
if (handler == creators_.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ix = grid.getGlobalIndex(i, j, k);
|
||||
auto xconPos = std::find_if(xcon.begin(), xcon.end(),
|
||||
[ix](const ::Opm::data::Connection& c)
|
||||
{
|
||||
return c.index == ix;
|
||||
});
|
||||
(this->*handler->second)();
|
||||
}
|
||||
}
|
||||
|
||||
if (xconPos == xcon.end()) {
|
||||
// RFT data not available for this connection. Unexpected.
|
||||
continue;
|
||||
}
|
||||
bool WellRFTOutputData::haveOutputData() const
|
||||
{
|
||||
return this->haveRFTData();
|
||||
}
|
||||
|
||||
rft.add(usys, conn, *xconPos, grid.getCellDepth(ix));
|
||||
void WellRFTOutputData::addDynamicData(const Opm::data::Well& wellSol)
|
||||
{
|
||||
for (const auto& handler : this->dataHandlers_) {
|
||||
handler(wellSol);
|
||||
}
|
||||
}
|
||||
|
||||
void WellRFTOutputData::write(::Opm::EclIO::OutputStream::RFT& rftFile) const
|
||||
{
|
||||
if (! this->haveOutputData()) {
|
||||
return;
|
||||
}
|
||||
|
||||
return rft;
|
||||
this->writeHeader(rftFile);
|
||||
|
||||
for (const auto& recordWriter : this->recordWriters_) {
|
||||
recordWriter(rftFile);
|
||||
}
|
||||
}
|
||||
|
||||
void WellRFTOutputData::initialiseRFTHandlers()
|
||||
{
|
||||
if (this->well_.get().getConnections().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->rft_ = RFTRecord{ this->well_.get().getConnections().size() };
|
||||
|
||||
this->dataHandlers_.emplace_back(
|
||||
[this](const Opm::data::Well& wellSol)
|
||||
{
|
||||
this->rft_->collectRecordData(this->usys_, this->grid_,
|
||||
this->well_, wellSol);
|
||||
});
|
||||
|
||||
this->recordWriters_.emplace_back(
|
||||
[this](::Opm::EclIO::OutputStream::RFT& rftFile)
|
||||
{
|
||||
this->rft_->write(rftFile);
|
||||
});
|
||||
}
|
||||
|
||||
bool WellRFTOutputData::haveRFTData() const
|
||||
{
|
||||
return this->rft_.has_value()
|
||||
&& (this->rft_->nConn() > std::size_t{0});
|
||||
}
|
||||
|
||||
void WellRFTOutputData::writeHeader(::Opm::EclIO::OutputStream::RFT& rftFile) const
|
||||
{
|
||||
{
|
||||
const auto time = this->usys_.get()
|
||||
.from_si(::Opm::UnitSystem::measure::time, this->elapsed_);
|
||||
|
||||
rftFile.write("TIME", std::vector<float> {
|
||||
static_cast<float>(time)
|
||||
});
|
||||
}
|
||||
|
||||
rftFile.write("DATE", std::vector<int> {
|
||||
this->timeStamp_.day, // 1..31
|
||||
this->timeStamp_.month, // 1..12
|
||||
this->timeStamp_.year,
|
||||
});
|
||||
|
||||
rftFile.write("WELLETC", this->wellETC());
|
||||
}
|
||||
|
||||
std::vector<Opm::EclIO::PaddedOutputString<8>>
|
||||
wellETC(const std::string& wellName,
|
||||
const std::string& dataType,
|
||||
const ::Opm::UnitSystem& usys)
|
||||
WellRFTOutputData::wellETC() const
|
||||
{
|
||||
using UT = ::Opm::UnitSystem::UnitType;
|
||||
auto ret = std::vector<Opm::EclIO::PaddedOutputString<8>>(16);
|
||||
@@ -285,17 +437,17 @@ namespace {
|
||||
// Note: ret[etcIx::LGR] is well's LGR. Default constructed
|
||||
// (i.e., blank) string is sufficient to represent no LGR.
|
||||
|
||||
ret[etcIx::Well] = wellName;
|
||||
ret[etcIx::Well] = this->well_.get().name();
|
||||
|
||||
// 'P' -> PLT, 'R' -> RFT, 'S' -> Segment
|
||||
ret[etcIx::DataType] = dataType;
|
||||
// 'P' -> PLT, 'R' -> RFT, 'S' -> Segment
|
||||
ret[etcIx::DataType] = this->dataTypeString();
|
||||
|
||||
// We support "standard" well type only.
|
||||
ret[etcIx::WellType] = "STANDARD";
|
||||
// STANDARD or MULTISEG only.
|
||||
ret[etcIx::WellType] = this->wellTypeString();
|
||||
|
||||
RftUnits::fill(usys, ret);
|
||||
RftUnits::fill(this->usys_, ret);
|
||||
|
||||
switch (usys.getType()) {
|
||||
switch (this->usys_.get().getType()) {
|
||||
case UT::UNIT_TYPE_METRIC:
|
||||
RftUnits::exceptions::metric(ret);
|
||||
break;
|
||||
@@ -320,68 +472,29 @@ namespace {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void writeWellHeader(const double elapsed,
|
||||
const std::string& wellName,
|
||||
const ::Opm::Schedule& sched,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
::Opm::EclIO::OutputStream::RFT& rftFile)
|
||||
std::string WellRFTOutputData::dataTypeString() const
|
||||
{
|
||||
{
|
||||
const auto time =
|
||||
usys.from_si(::Opm::UnitSystem::measure::time, elapsed);
|
||||
auto tstring = std::string{};
|
||||
if (this->haveRFTData()) { tstring += 'R';}
|
||||
|
||||
rftFile.write("TIME", std::vector<float> {
|
||||
static_cast<float>(time)
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const auto timePoint = ::Opm::RestartIO::
|
||||
getSimulationTimePoint(sched.getStartTime(), elapsed);
|
||||
|
||||
rftFile.write("DATE", std::vector<int> {
|
||||
timePoint.day, // 1..31
|
||||
timePoint.month, // 1..12
|
||||
timePoint.year,
|
||||
});
|
||||
}
|
||||
|
||||
rftFile.write("WELLETC", wellETC(wellName, "R", usys));
|
||||
return tstring;
|
||||
}
|
||||
|
||||
void write(const WellRFT& rft, ::Opm::EclIO::OutputStream::RFT& rftFile)
|
||||
std::string WellRFTOutputData::wellTypeString() const
|
||||
{
|
||||
rftFile.write("CONIPOS", rft.conI());
|
||||
rftFile.write("CONJPOS", rft.conJ());
|
||||
rftFile.write("CONKPOS", rft.conK());
|
||||
|
||||
rftFile.write("HOSTGRID", rft.hostgrid());
|
||||
|
||||
rftFile.write("DEPTH" , rft.depth());
|
||||
rftFile.write("PRESSURE", rft.pressure());
|
||||
rftFile.write("SWAT" , rft.swat());
|
||||
rftFile.write("SGAS" , rft.sgas());
|
||||
return this->well_.get().isMultiSegment()
|
||||
? "MULTISEG"
|
||||
: "STANDARD";
|
||||
}
|
||||
|
||||
void writeWellRFT(const int reportStep,
|
||||
const double elapsed,
|
||||
const std::string& wname,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Schedule& sched,
|
||||
const std::vector<Opm::data::Connection>& xcon,
|
||||
::Opm::EclIO::OutputStream::RFT& rftFile)
|
||||
{
|
||||
const auto rft =
|
||||
createWellRFT(reportStep, wname, usys, grid, sched, xcon);
|
||||
|
||||
if (rft.nConn() > std::size_t{0}) {
|
||||
writeWellHeader(elapsed, wname, sched, usys, rftFile);
|
||||
write(rft, rftFile);
|
||||
}
|
||||
}
|
||||
std::map<WellRFTOutputData::DataTypes, WellRFTOutputData::CreateTypeHandler>
|
||||
WellRFTOutputData::creators_{
|
||||
{ WellRFTOutputData::DataTypes::RFT, &WellRFTOutputData::initialiseRFTHandlers },
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
void Opm::RftIO::write(const int reportStep,
|
||||
const double elapsed,
|
||||
const ::Opm::UnitSystem& usys,
|
||||
@@ -396,17 +509,39 @@ void Opm::RftIO::write(const int reportStep,
|
||||
return;
|
||||
}
|
||||
|
||||
const auto timePoint = ::Opm::RestartIO::
|
||||
getSimulationTimePoint(schedule.getStartTime(), elapsed);
|
||||
|
||||
for (const auto& wname : schedule.wellNames(reportStep)) {
|
||||
if (! (rftCfg.rft(wname) ||
|
||||
rftCfg.plt(wname)))
|
||||
{
|
||||
auto rftTypes = std::vector<WellRFTOutputData::DataTypes>{};
|
||||
|
||||
if (rftCfg.rft(wname) || rftCfg.plt(wname)) {
|
||||
rftTypes.push_back(WellRFTOutputData::DataTypes::RFT);
|
||||
}
|
||||
|
||||
if (rftTypes.empty()) {
|
||||
// RFT output not requested for 'wname' at this time.
|
||||
continue;
|
||||
}
|
||||
|
||||
// RFT output requested for 'wname' at this time.
|
||||
writeWellRFT(reportStep, elapsed, wname, usys, grid,
|
||||
schedule, wellSol.at(wname).connections,
|
||||
rftFile);
|
||||
auto xwPos = wellSol.find(wname);
|
||||
if (xwPos == wellSol.end()) {
|
||||
// No dynamic data available for 'wname' at this time.
|
||||
continue;
|
||||
}
|
||||
|
||||
// RFT output requested for 'wname' at this time and dynamic data is
|
||||
// available. Collect requisite information.
|
||||
auto rftOutput = WellRFTOutputData {
|
||||
rftTypes, elapsed, timePoint, usys, grid,
|
||||
schedule[reportStep].wells(wname)
|
||||
};
|
||||
|
||||
rftOutput.addDynamicData(xwPos->second);
|
||||
|
||||
// Emit RFT record for 'wname'. This transparently handles wells
|
||||
// without connections--e.g., if the well is only connected in
|
||||
// inactive/deactivated cells.
|
||||
rftOutput.write(rftFile);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user