Support Retrieving Well Vectors from {I,X}{WEL,CON}
This commit extends function RestartIO::load() to retrieve most of the well restart data (aggregate flow rates per well connection, or per well) from the standard ECL restart vectors IWEL, XWEL, ICON, and XCON. We still prefer the dedicated OPM_* variants if available, but this commit brings us closer to being able to retire OPM_XWEL. While here, also add WELLDIMS to FIRST_SIM_THPRES to restore the STORE_THPRES unit test.
This commit is contained in:
parent
980f55271f
commit
a3b37eee4f
@ -25,6 +25,7 @@
|
||||
|
||||
#include <opm/output/eclipse/RestartValue.hpp>
|
||||
|
||||
#include <opm/output/eclipse/VectorItems/connection.hpp>
|
||||
#include <opm/output/eclipse/VectorItems/intehead.hpp>
|
||||
#include <opm/output/eclipse/VectorItems/well.hpp>
|
||||
|
||||
@ -46,230 +47,602 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
Opm::UnitSystem::UnitType
|
||||
getUnitSystem(const Opm::RestartIO::ecl_kw_type* intehead)
|
||||
{
|
||||
using UType = Opm::UnitSystem::UnitType;
|
||||
namespace VI = ::Opm::RestartIO::Helpers::VectorItems;
|
||||
|
||||
//switch (Opm::RestartIO::ecl_kw_iget_int(intehead, INTEHEAD_UNIT_INDEX)) {
|
||||
switch (Opm::RestartIO::ecl_kw_iget_type<int>(intehead, Opm::RestartIO::ECL_INT_TYPE, INTEHEAD_UNIT_INDEX)) {
|
||||
case 1: return UType::UNIT_TYPE_METRIC;
|
||||
case 2: return UType::UNIT_TYPE_FIELD;
|
||||
case 3: return UType::UNIT_TYPE_LAB;
|
||||
case 4: return UType::UNIT_TYPE_PVT_M;
|
||||
class RestartFileView
|
||||
{
|
||||
public:
|
||||
explicit RestartFileView(const std::string& filename,
|
||||
const int report_step);
|
||||
|
||||
~RestartFileView() = default;
|
||||
|
||||
RestartFileView(const RestartFileView& rhs) = delete;
|
||||
RestartFileView(RestartFileView&& rhs);
|
||||
|
||||
RestartFileView& operator=(const RestartFileView& rhs) = delete;
|
||||
RestartFileView& operator=(RestartFileView&& rhs);
|
||||
|
||||
std::size_t simStep() const
|
||||
{
|
||||
return this->sim_step_;
|
||||
}
|
||||
|
||||
return UType::UNIT_TYPE_METRIC; // questionable…
|
||||
operator const Opm::RestartIO::ecl_file_view_type*() const
|
||||
{
|
||||
return this->step_view_;
|
||||
}
|
||||
|
||||
const Opm::RestartIO::ecl_kw_type* getKeyword(const char* kw) const
|
||||
{
|
||||
namespace Load = Opm::RestartIO;
|
||||
|
||||
// Main grid only. Does not handle/support LGR.
|
||||
return Load::ecl_file_view_has_kw (*this, kw)
|
||||
? Load::ecl_file_view_iget_named_kw(*this, kw, 0)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
using RstFile = Opm::RestartIO::ert_unique_ptr<
|
||||
Opm::RestartIO::ecl_file_type,
|
||||
Opm::RestartIO::ecl_file_close>;
|
||||
|
||||
std::size_t sim_step_;
|
||||
RstFile rst_file_;
|
||||
Opm::RestartIO::ecl_file_view_type* step_view_ = nullptr;
|
||||
|
||||
operator Opm::RestartIO::ecl_file_type*()
|
||||
{
|
||||
return this->rst_file_.get();
|
||||
}
|
||||
|
||||
operator const Opm::RestartIO::ecl_file_type*() const
|
||||
{
|
||||
return this->rst_file_.get();
|
||||
}
|
||||
};
|
||||
|
||||
RestartFileView::RestartFileView(const std::string& filename,
|
||||
const int report_step)
|
||||
: sim_step_(std::max(report_step - 1, 0))
|
||||
, rst_file_(Opm::RestartIO::ecl_file_open(filename.c_str(), 0))
|
||||
{
|
||||
namespace Load = Opm::RestartIO;
|
||||
|
||||
if (this->rst_file_ == nullptr) {
|
||||
throw std::invalid_argument {
|
||||
"Unable to open Restart File '" + filename
|
||||
+ "' at Report Step " + std::to_string(report_step)
|
||||
};
|
||||
}
|
||||
|
||||
this->step_view_ =
|
||||
(Load::EclFiletype(filename) == Load::ECL_UNIFIED_RESTART_FILE)
|
||||
? Load::ecl_file_get_restart_view(*this, -1, report_step, -1, -1)
|
||||
: Load::ecl_file_get_global_view (*this); // Separate
|
||||
|
||||
if (this->step_view_ == nullptr) {
|
||||
throw std::runtime_error {
|
||||
"Unable to acquire restart information for report step "
|
||||
+ std::to_string(report_step)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace Opm { namespace RestartIO {
|
||||
RestartFileView::RestartFileView(RestartFileView&& rhs)
|
||||
: sim_step_ (rhs.sim_step_) // Scalar (size_t)
|
||||
, rst_file_ (std::move(rhs.rst_file_))
|
||||
, step_view_(rhs.step_view_) // Pointer
|
||||
{}
|
||||
|
||||
//namespace {
|
||||
RestartFileView& RestartFileView::operator=(RestartFileView&& rhs)
|
||||
{
|
||||
this->sim_step_ = rhs.sim_step_; // Scalar (size_t)
|
||||
this->rst_file_ = std::move(rhs.rst_file_);
|
||||
this->step_view_ = rhs.step_view_; // Pointer copy
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline int to_ert_welltype( const Well& well, size_t timestep ) {
|
||||
if( well.isProducer( timestep ) ) return IWEL_PRODUCER;
|
||||
|
||||
switch( well.getInjectionProperties( timestep ).injectorType ) {
|
||||
case WellInjector::WATER:
|
||||
return IWEL_WATER_INJECTOR;
|
||||
case WellInjector::GAS:
|
||||
return IWEL_GAS_INJECTOR;
|
||||
case WellInjector::OIL:
|
||||
return IWEL_OIL_INJECTOR;
|
||||
default:
|
||||
return IWEL_UNDOCUMENTED_ZERO;
|
||||
namespace {
|
||||
void throwIfMissingRequired(const Opm::RestartKey& rst_key)
|
||||
{
|
||||
if (rst_key.required) {
|
||||
throw std::runtime_error {
|
||||
"Requisite restart vector '"
|
||||
+ rst_key.key +
|
||||
"' is not available in restart file"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> double_vector( const ::Opm::RestartIO::ecl_kw_type * ecl_kw ) {
|
||||
size_t size = ::Opm::RestartIO::ecl_kw_get_size( ecl_kw );
|
||||
std::vector<double>
|
||||
double_vector(const ::Opm::RestartIO::ecl_kw_type* ecl_kw)
|
||||
{
|
||||
namespace Load = ::Opm::RestartIO;
|
||||
|
||||
const auto size = static_cast<std::size_t>(
|
||||
Load::ecl_kw_get_size(ecl_kw));
|
||||
|
||||
if (Load::ecl_type_get_type(Load::ecl_kw_get_data_type(ecl_kw)) == Load::ECL_DOUBLE_TYPE) {
|
||||
const double* ecl_data = Load::ecl_kw_get_type_ptr<double>(ecl_kw, Load::ECL_DOUBLE_TYPE);
|
||||
|
||||
if (::Opm::RestartIO::ecl_type_get_type( ::Opm::RestartIO::ecl_kw_get_data_type( ecl_kw ) ) == ECL_DOUBLE_TYPE ) {
|
||||
//const double * ecl_data = ::Opm::RestartIO::ecl_kw_get_double_ptr( ecl_kw );
|
||||
const double * ecl_data = ::Opm::RestartIO::ecl_kw_get_type_ptr<double>( ecl_kw, ECL_DOUBLE_TYPE );
|
||||
return { ecl_data , ecl_data + size };
|
||||
} else {
|
||||
//const float * ecl_data = ::Opm::RestartIO::ecl_kw_get_float_ptr( ecl_kw );
|
||||
const float * ecl_data = ::Opm::RestartIO::ecl_kw_get_type_ptr<float>( ecl_kw, ECL_FLOAT_TYPE );
|
||||
return { ecl_data , ecl_data + size };
|
||||
}
|
||||
else {
|
||||
const float* ecl_data = Load::ecl_kw_get_type_ptr<float>(ecl_kw, Load::ECL_FLOAT_TYPE);
|
||||
|
||||
return { ecl_data , ecl_data + size };
|
||||
}
|
||||
}
|
||||
|
||||
Opm::data::Solution
|
||||
restoreSOLUTION(const RestartFileView& rst_view,
|
||||
const std::vector<Opm::RestartKey>& solution_keys,
|
||||
const int numcells)
|
||||
{
|
||||
Opm::data::Solution sol(/* init_si = */ false);
|
||||
|
||||
inline data::Solution restoreSOLUTION( ::Opm::RestartIO::ecl_file_view_type* file_view,
|
||||
const std::vector<RestartKey>& solution_keys,
|
||||
int numcells) {
|
||||
|
||||
data::Solution sol( false );
|
||||
for (const auto& value : solution_keys) {
|
||||
const std::string& key = value.key;
|
||||
UnitSystem::measure dim = value.dim;
|
||||
bool required = value.required;
|
||||
const auto& vector = value.key;
|
||||
const auto* kw = rst_view.getKeyword(vector.c_str());
|
||||
|
||||
if( !::Opm::RestartIO::ecl_file_view_has_kw( file_view, key.c_str() ) ) {
|
||||
if (required)
|
||||
throw std::runtime_error("Read of restart file: "
|
||||
"File does not contain "
|
||||
+ key
|
||||
+ " data" );
|
||||
else
|
||||
if (kw == nullptr) {
|
||||
throwIfMissingRequired(value);
|
||||
|
||||
// Requested vector not available, but caller does not
|
||||
// actually require the vector for restart purposes.
|
||||
// Skip this.
|
||||
continue;
|
||||
}
|
||||
|
||||
const Opm::RestartIO::ecl_kw_type * ecl_kw = ::Opm::RestartIO::ecl_file_view_iget_named_kw( file_view , key.c_str() , 0 );
|
||||
if( Opm::RestartIO::ecl_kw_get_size(ecl_kw) != numcells)
|
||||
throw std::runtime_error("Restart file: Could not restore "
|
||||
+ std::string( Opm::RestartIO::ecl_kw_get_header( ecl_kw ) )
|
||||
+ ", mismatched number of cells" );
|
||||
if (Opm::RestartIO::ecl_kw_get_size(kw) != numcells) {
|
||||
throw std::runtime_error {
|
||||
"Restart file: Could not restore "
|
||||
+ std::string(Opm::RestartIO::ecl_kw_get_header(kw))
|
||||
+ ", mismatched number of cells"
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<double> data = ::Opm::RestartIO::double_vector( ecl_kw );
|
||||
sol.insert( key, dim, data , data::TargetType::RESTART_SOLUTION );
|
||||
sol.insert(vector, value.dim, double_vector(kw),
|
||||
Opm::data::TargetType::RESTART_SOLUTION);
|
||||
}
|
||||
|
||||
return sol;
|
||||
}
|
||||
|
||||
|
||||
using rt = data::Rates::opt;
|
||||
data::Wells restore_wells( const ::Opm::RestartIO::ecl_kw_type * opm_xwel,
|
||||
const ::Opm::RestartIO::ecl_kw_type * opm_iwel,
|
||||
int sim_step,
|
||||
const EclipseState& es,
|
||||
const EclipseGrid& grid,
|
||||
const Schedule& schedule) {
|
||||
|
||||
const auto& sched_wells = schedule.getWells( sim_step );
|
||||
std::vector< rt > phases;
|
||||
void restoreExtra(const RestartFileView& rst_view,
|
||||
const std::vector<Opm::RestartKey>& extra_keys,
|
||||
const Opm::UnitSystem& usys,
|
||||
Opm::RestartValue& rst_value)
|
||||
{
|
||||
const auto& phase = es.runspec().phases();
|
||||
if( phase.active( Phase::WATER ) ) phases.push_back( rt::wat );
|
||||
if( phase.active( Phase::OIL ) ) phases.push_back( rt::oil );
|
||||
if( phase.active( Phase::GAS ) ) phases.push_back( rt::gas );
|
||||
for (const auto& extra : extra_keys) {
|
||||
const auto& vector = extra.key;
|
||||
const auto* kw = rst_view.getKeyword(vector.c_str());
|
||||
|
||||
if (kw == nullptr) {
|
||||
throwIfMissingRequired(extra);
|
||||
|
||||
// Requested vector not available, but caller does not
|
||||
// actually require the vector for restart purposes.
|
||||
// Skip this.
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto well_size = [&]( size_t acc, const Well* w ) {
|
||||
// Requisite vector available in result set. Recover data.
|
||||
rst_value.addExtra(vector, extra.dim, double_vector(kw));
|
||||
}
|
||||
|
||||
for (auto& extra_value : rst_value.extra) {
|
||||
const auto& restart_key = extra_value.first;
|
||||
auto& data = extra_value.second;
|
||||
|
||||
usys.to_si(restart_key.dim, data);
|
||||
}
|
||||
}
|
||||
|
||||
void checkWellVectorSizes(const ::Opm::RestartIO::ecl_kw_type* opm_xwel,
|
||||
const ::Opm::RestartIO::ecl_kw_type* opm_iwel,
|
||||
const int sim_step,
|
||||
const std::vector<Opm::data::Rates::opt>& phases,
|
||||
const std::vector<const Opm::Well*>& sched_wells)
|
||||
{
|
||||
const auto expected_xwel_size =
|
||||
std::accumulate(sched_wells.begin(), sched_wells.end(),
|
||||
std::size_t(0),
|
||||
[&phases, sim_step](const std::size_t acc, const Opm::Well* w)
|
||||
-> std::size_t
|
||||
{
|
||||
return acc
|
||||
+ 2 + phases.size()
|
||||
+ ( w->getConnections( sim_step ).size()
|
||||
* (phases.size() + data::Connection::restart_size) );
|
||||
};
|
||||
+ (w->getConnections(sim_step).size()
|
||||
* (phases.size() + Opm::data::Connection::restart_size));
|
||||
});
|
||||
|
||||
const auto expected_xwel_size = std::accumulate( sched_wells.begin(),
|
||||
sched_wells.end(),
|
||||
0,
|
||||
well_size );
|
||||
|
||||
if( ::Opm::RestartIO::ecl_kw_get_size( opm_xwel ) != expected_xwel_size ) {
|
||||
throw std::runtime_error(
|
||||
if (static_cast<std::size_t>(::Opm::RestartIO::ecl_kw_get_size(opm_xwel)) != expected_xwel_size)
|
||||
{
|
||||
throw std::runtime_error {
|
||||
"Mismatch between OPM_XWEL and deck; "
|
||||
"OPM_XWEL size was " + std::to_string( ::Opm::RestartIO::ecl_kw_get_size( opm_xwel ) ) +
|
||||
", expected " + std::to_string( expected_xwel_size ) );
|
||||
"OPM_XWEL size was " + std::to_string(::Opm::RestartIO::ecl_kw_get_size(opm_xwel)) +
|
||||
", expected " + std::to_string(expected_xwel_size)
|
||||
};
|
||||
}
|
||||
|
||||
if( ::Opm::RestartIO::ecl_kw_get_size( opm_iwel ) != int(sched_wells.size()) )
|
||||
throw std::runtime_error(
|
||||
if (::Opm::RestartIO::ecl_kw_get_size(opm_iwel) != int(sched_wells.size())) {
|
||||
throw std::runtime_error {
|
||||
"Mismatch between OPM_IWEL and deck; "
|
||||
"OPM_IWEL size was " + std::to_string( ::Opm::RestartIO::ecl_kw_get_size( opm_iwel ) ) +
|
||||
", expected " + std::to_string( sched_wells.size() ) );
|
||||
"OPM_IWEL size was " + std::to_string(::Opm::RestartIO::ecl_kw_get_size(opm_iwel)) +
|
||||
", expected " + std::to_string(sched_wells.size())
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
data::Wells wells;
|
||||
const double * opm_xwel_data = ::Opm::RestartIO::ecl_kw_get_type_ptr<double>( opm_xwel, ECL_DOUBLE_TYPE );
|
||||
const int * opm_iwel_data = ::Opm::RestartIO::ecl_kw_get_type_ptr<int>( opm_iwel, ECL_INT_TYPE );
|
||||
for( const auto* sched_well : sched_wells ) {
|
||||
data::Well& well = wells[ sched_well->name() ];
|
||||
Opm::data::Wells
|
||||
restore_wells_opm(const RestartFileView& rst_view,
|
||||
const ::Opm::EclipseState& es,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Schedule& schedule)
|
||||
{
|
||||
namespace Load = ::Opm::RestartIO;
|
||||
|
||||
const auto* opm_iwel = rst_view.getKeyword("OPM_IWEL");
|
||||
const auto* opm_xwel = rst_view.getKeyword("OPM_XWEL");
|
||||
|
||||
if ((opm_xwel == nullptr) || (opm_iwel == nullptr)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
using rt = Opm::data::Rates::opt;
|
||||
|
||||
const auto& sched_wells = schedule.getWells(rst_view.simStep());
|
||||
std::vector<rt> phases;
|
||||
{
|
||||
const auto& phase = es.runspec().phases();
|
||||
|
||||
if (phase.active(Opm::Phase::WATER)) { phases.push_back(rt::wat); }
|
||||
if (phase.active(Opm::Phase::OIL)) { phases.push_back(rt::oil); }
|
||||
if (phase.active(Opm::Phase::GAS)) { phases.push_back(rt::gas); }
|
||||
}
|
||||
|
||||
checkWellVectorSizes(opm_xwel, opm_iwel,
|
||||
rst_view.simStep(),
|
||||
phases, sched_wells);
|
||||
|
||||
Opm::data::Wells wells;
|
||||
const auto* opm_xwel_data = Load::ecl_kw_get_type_ptr<double>(opm_xwel, Load::ECL_DOUBLE_TYPE);
|
||||
const auto* opm_iwel_data = Load::ecl_kw_get_type_ptr<int>(opm_iwel, Load::ECL_INT_TYPE);
|
||||
|
||||
for (const auto* sched_well : sched_wells) {
|
||||
auto& well = wells[ sched_well->name() ];
|
||||
|
||||
well.bhp = *opm_xwel_data++;
|
||||
well.temperature = *opm_xwel_data++;
|
||||
well.control = *opm_iwel_data++;
|
||||
|
||||
for( auto phase : phases )
|
||||
well.rates.set( phase, *opm_xwel_data++ );
|
||||
for (const auto& phase : phases) {
|
||||
well.rates.set(phase, *opm_xwel_data++);
|
||||
}
|
||||
|
||||
for( const auto& sc : sched_well->getConnections( sim_step ) ) {
|
||||
for (const auto& sc : sched_well->getConnections(rst_view.simStep())) {
|
||||
const auto i = sc.getI(), j = sc.getJ(), k = sc.getK();
|
||||
if( !grid.cellActive( i, j, k ) || sc.state == WellCompletion::SHUT ) {
|
||||
opm_xwel_data += data::Connection::restart_size + phases.size();
|
||||
|
||||
if (!grid.cellActive(i, j, k) || sc.state == Opm::WellCompletion::SHUT) {
|
||||
opm_xwel_data += Opm::data::Connection::restart_size + phases.size();
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto active_index = grid.activeIndex( i, j, k );
|
||||
const auto active_index = grid.activeIndex(i, j, k);
|
||||
|
||||
well.connections.emplace_back();
|
||||
auto& connection = well.connections.back();
|
||||
|
||||
connection.index = active_index;
|
||||
connection.pressure = *opm_xwel_data++;
|
||||
connection.reservoir_rate = *opm_xwel_data++;
|
||||
for( auto phase : phases )
|
||||
connection.rates.set( phase, *opm_xwel_data++ );
|
||||
|
||||
for (const auto& phase : phases) {
|
||||
connection.rates.set(phase, *opm_xwel_data++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wells;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* getPtr(const ::Opm::RestartIO::ecl_kw_type* kw)
|
||||
{
|
||||
return (kw == nullptr) ? nullptr
|
||||
: static_cast<const T*>(ecl_kw_iget_ptr(kw /* <- ADL */, 0));
|
||||
}
|
||||
|
||||
//* should take grid as argument because it may be modified from the simulator */
|
||||
RestartValue load( const std::string& filename,
|
||||
int getInteHeadElem(const ::Opm::RestartIO::ecl_kw_type* intehead,
|
||||
const std::vector<int>::size_type i)
|
||||
{
|
||||
return getPtr<int>(intehead)[i];
|
||||
}
|
||||
|
||||
struct WellArrayDim
|
||||
{
|
||||
explicit WellArrayDim(const ::Opm::RestartIO::ecl_kw_type* intehead);
|
||||
|
||||
std::size_t maxConnPerWell;
|
||||
std::size_t numIWelElem;
|
||||
std::size_t numXWelElem;
|
||||
std::size_t numIConElm;
|
||||
std::size_t numXConElm;
|
||||
};
|
||||
|
||||
WellArrayDim::WellArrayDim(const ::Opm::RestartIO::ecl_kw_type* intehead)
|
||||
: maxConnPerWell(getInteHeadElem(intehead, VI::intehead::NCWMAX))
|
||||
, numIWelElem (getInteHeadElem(intehead, VI::intehead::NIWELZ))
|
||||
, numXWelElem (getInteHeadElem(intehead, VI::intehead::NXWELZ))
|
||||
, numIConElm (getInteHeadElem(intehead, VI::intehead::NICONZ))
|
||||
, numXConElm (getInteHeadElem(intehead, VI::intehead::NXCONZ))
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
boost::iterator_range<const T*>
|
||||
getDataWindow(const T* arr,
|
||||
const std::size_t windowSize,
|
||||
const std::size_t well,
|
||||
const std::size_t conn = 0,
|
||||
const std::size_t maxConnPerWell = 1)
|
||||
{
|
||||
const auto off = windowSize * (conn + maxConnPerWell*well);
|
||||
const auto* begin = arr + off;
|
||||
const auto* end = begin + windowSize;
|
||||
|
||||
return { begin, end };
|
||||
}
|
||||
|
||||
boost::iterator_range<const int*>
|
||||
getIWelWindow(const int* iwel,
|
||||
const WellArrayDim& wdim,
|
||||
const std::size_t well)
|
||||
{
|
||||
return getDataWindow(iwel, wdim.numIWelElem, well);
|
||||
}
|
||||
|
||||
boost::iterator_range<const double*>
|
||||
getXWelWindow(const double* xwel,
|
||||
const WellArrayDim& wdim,
|
||||
const std::size_t well)
|
||||
{
|
||||
return getDataWindow(xwel, wdim.numXWelElem, well);
|
||||
}
|
||||
|
||||
boost::iterator_range<const int*>
|
||||
getIConWindow(const int* icon,
|
||||
const WellArrayDim& wdim,
|
||||
const std::size_t well,
|
||||
const std::size_t conn)
|
||||
{
|
||||
return getDataWindow(icon, wdim.numIConElm, well,
|
||||
conn, wdim.maxConnPerWell);
|
||||
}
|
||||
|
||||
boost::iterator_range<const double*>
|
||||
getXConWindow(const double* xcon,
|
||||
const WellArrayDim& wdim,
|
||||
const std::size_t well,
|
||||
const std::size_t conn)
|
||||
{
|
||||
return getDataWindow(xcon, wdim.numXConElm, well,
|
||||
conn, wdim.maxConnPerWell);
|
||||
}
|
||||
|
||||
std::unordered_map<std::size_t, boost::iterator_range<const double*>::size_type>
|
||||
seqID_to_resID(const WellArrayDim& wdim,
|
||||
const std::size_t wellID,
|
||||
const std::size_t nConn,
|
||||
const int* icon_full)
|
||||
{
|
||||
using SizeT = boost::iterator_range<const double*>::size_type;
|
||||
auto seqToRes = std::unordered_map<std::size_t, SizeT>{};
|
||||
|
||||
for (auto connID = 0*nConn; connID < nConn; ++connID) {
|
||||
const auto icon =
|
||||
getIConWindow(icon_full, wdim, wellID, connID);
|
||||
|
||||
seqToRes.emplace(icon[VI::IConn::index::SeqIndex] - 1, connID);
|
||||
}
|
||||
|
||||
return seqToRes;
|
||||
}
|
||||
|
||||
void restoreConnRates(const Opm::Well& well,
|
||||
const std::size_t wellID,
|
||||
const std::size_t sim_step,
|
||||
const Opm::EclipseGrid& grid,
|
||||
const WellArrayDim& wdim,
|
||||
const Opm::UnitSystem& usys,
|
||||
const Opm::Phases& phases,
|
||||
const int* iwel_full,
|
||||
const int* icon_full,
|
||||
const double* xcon_full,
|
||||
Opm::data::Well& xw)
|
||||
{
|
||||
using M = ::Opm::UnitSystem::measure;
|
||||
|
||||
const auto iwel = getIWelWindow(iwel_full, wdim, wellID);
|
||||
const auto nConn = static_cast<std::size_t>(
|
||||
iwel[VI::IWell::index::NConn]);
|
||||
|
||||
xw.connections.resize(nConn, Opm::data::Connection{});
|
||||
|
||||
if ((icon_full == nullptr) || (xcon_full == nullptr)) {
|
||||
// Result set does not provide certain pieces of
|
||||
// information which are needed to reconstruct
|
||||
// connection flow rates. Nothing to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto oil = phases.active(Opm::Phase::OIL);
|
||||
const auto gas = phases.active(Opm::Phase::GAS);
|
||||
const auto wat = phases.active(Opm::Phase::WATER);
|
||||
|
||||
const auto conns = well.getActiveConnections(sim_step, grid);
|
||||
const auto seq_to_res =
|
||||
seqID_to_resID(wdim, wellID, nConn, icon_full);
|
||||
|
||||
auto linConnID = std::size_t{0};
|
||||
for (const auto& conn : conns) {
|
||||
const auto connID = seq_to_res.at(conn.getSeqIndex());
|
||||
const auto xcon =
|
||||
getXConWindow(xcon_full, wdim, wellID, connID);
|
||||
|
||||
auto& xc = xw.connections[linConnID++];
|
||||
|
||||
if (wat) {
|
||||
xc.rates.set(Opm::data::Rates::opt::wat,
|
||||
- usys.to_si(M::liquid_surface_rate,
|
||||
xcon[VI::XConn::index::WaterRate]));
|
||||
}
|
||||
|
||||
if (oil) {
|
||||
xc.rates.set(Opm::data::Rates::opt::oil,
|
||||
- usys.to_si(M::liquid_surface_rate,
|
||||
xcon[VI::XConn::index::OilRate]));
|
||||
}
|
||||
|
||||
if (gas) {
|
||||
xc.rates.set(Opm::data::Rates::opt::gas,
|
||||
- usys.to_si(M::gas_surface_rate,
|
||||
xcon[VI::XConn::index::GasRate]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Opm::data::Well
|
||||
restore_well(const Opm::Well& well,
|
||||
const std::size_t wellID,
|
||||
const std::size_t sim_step,
|
||||
const Opm::EclipseGrid& grid,
|
||||
const WellArrayDim& wdim,
|
||||
const Opm::UnitSystem& usys,
|
||||
const Opm::Phases& phases,
|
||||
const int* iwel_full,
|
||||
const double* xwel_full,
|
||||
const int* icon_full,
|
||||
const double* xcon_full)
|
||||
{
|
||||
if ((iwel_full == nullptr) || (xwel_full == nullptr)) {
|
||||
// Result set does not provide well information.
|
||||
// No wells? In any case, nothing to do here.
|
||||
return {};
|
||||
}
|
||||
|
||||
using M = ::Opm::UnitSystem::measure;
|
||||
|
||||
const auto xwel = getXWelWindow(xwel_full, wdim, wellID);
|
||||
|
||||
const auto oil = phases.active(Opm::Phase::OIL);
|
||||
const auto gas = phases.active(Opm::Phase::GAS);
|
||||
const auto wat = phases.active(Opm::Phase::WATER);
|
||||
|
||||
auto xw = ::Opm::data::Well{};
|
||||
|
||||
// 1) Restore well rates (xw.rates)
|
||||
if (wat) {
|
||||
xw.rates.set(Opm::data::Rates::opt::wat,
|
||||
- usys.to_si(M::liquid_surface_rate,
|
||||
xwel[VI::XWell::index::WatPrRate]));
|
||||
}
|
||||
|
||||
if (oil) {
|
||||
xw.rates.set(Opm::data::Rates::opt::oil,
|
||||
- usys.to_si(M::liquid_surface_rate,
|
||||
xwel[VI::XWell::index::OilPrRate]));
|
||||
}
|
||||
|
||||
if (gas) {
|
||||
xw.rates.set(Opm::data::Rates::opt::gas,
|
||||
- usys.to_si(M::gas_surface_rate,
|
||||
xwel[VI::XWell::index::GasPrRate]));
|
||||
}
|
||||
|
||||
// 2) Restore other well quantities (really only xw.bhp)
|
||||
xw.bhp = usys.to_si(M::pressure, xwel[VI::XWell::index::FlowBHP]);
|
||||
xw.thp = xw.temperature = 0.0;
|
||||
|
||||
// 3) Restore connection flow rates (xw.connections[i].rates)
|
||||
restoreConnRates(well, wellID, sim_step, grid, wdim, usys, phases,
|
||||
iwel_full, icon_full, xcon_full, xw);
|
||||
|
||||
return xw;
|
||||
}
|
||||
|
||||
Opm::data::Wells
|
||||
restore_wells_ecl(const RestartFileView& rst_view,
|
||||
const ::Opm::EclipseState& es,
|
||||
const ::Opm::EclipseGrid& grid,
|
||||
const ::Opm::Schedule& schedule)
|
||||
{
|
||||
auto soln = ::Opm::data::Wells{};
|
||||
|
||||
const auto* intehead = rst_view.getKeyword("INTEHEAD");
|
||||
|
||||
if (intehead == nullptr) {
|
||||
// Result set does not provide indexing information.
|
||||
// Can't do anything here.
|
||||
return soln;
|
||||
}
|
||||
|
||||
const auto wdim = WellArrayDim{ intehead };
|
||||
const auto& units = es.getUnits();
|
||||
const auto& phases = es.runspec().phases();
|
||||
|
||||
const auto* iwel_full = getPtr<int> (rst_view.getKeyword("IWEL"));
|
||||
const auto* xwel_full = getPtr<double>(rst_view.getKeyword("XWEL"));
|
||||
const auto* icon_full = getPtr<int> (rst_view.getKeyword("ICON"));
|
||||
const auto* xcon_full = getPtr<double>(rst_view.getKeyword("XCON"));
|
||||
|
||||
const auto sim_step = rst_view.simStep();
|
||||
const auto& wells = schedule.getWells(sim_step);
|
||||
for (auto nWells = wells.size(), wellID = 0*nWells;
|
||||
wellID < nWells; ++wellID)
|
||||
{
|
||||
const auto* well = wells[wellID];
|
||||
|
||||
soln[well->name()] =
|
||||
restore_well(*well, wellID, sim_step, grid, wdim, units, phases,
|
||||
iwel_full, xwel_full, icon_full, xcon_full);
|
||||
}
|
||||
|
||||
return soln;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Opm { namespace RestartIO {
|
||||
|
||||
RestartValue
|
||||
load(const std::string& filename,
|
||||
int report_step,
|
||||
const std::vector<RestartKey>& solution_keys,
|
||||
const EclipseState& es,
|
||||
const EclipseGrid& grid,
|
||||
const Schedule& schedule,
|
||||
const std::vector<RestartKey>& extra_keys) {
|
||||
const std::vector<RestartKey>& extra_keys)
|
||||
{
|
||||
const auto rst_view = RestartFileView{ filename, report_step };
|
||||
|
||||
int sim_step = std::max(report_step - 1, 0);
|
||||
const bool unified = ( ::Opm::RestartIO::EclFiletype( filename ) == ::Opm::RestartIO::ECL_UNIFIED_RESTART_FILE );
|
||||
::Opm::RestartIO::ert_unique_ptr< ::Opm::RestartIO::ecl_file_type, ::Opm::RestartIO::ecl_file_close > file(::Opm::RestartIO::ecl_file_open( filename.c_str(), 0 ));
|
||||
::Opm::RestartIO::ecl_file_view_type * file_view;
|
||||
auto xr = restoreSOLUTION(rst_view, solution_keys,
|
||||
grid.getNumActive());
|
||||
|
||||
if( !file )
|
||||
throw std::runtime_error( "Restart file " + filename + " not found!" );
|
||||
xr.convertToSI(es.getUnits());
|
||||
|
||||
if( unified ) {
|
||||
file_view = ::Opm::RestartIO::ecl_file_get_restart_view( file.get() , -1 , report_step , -1 , -1 );
|
||||
if (!file_view)
|
||||
throw std::runtime_error( "Restart file " + filename
|
||||
+ " does not contain data for report step "
|
||||
+ std::to_string( report_step ) + "!" );
|
||||
} else
|
||||
file_view = ::Opm::RestartIO::ecl_file_get_global_view( file.get() );
|
||||
auto xw = Opm::RestartIO::ecl_file_view_has_kw(rst_view, "OPM_XWEL")
|
||||
? restore_wells_opm(rst_view, es, grid, schedule)
|
||||
: restore_wells_ecl(rst_view, es, grid, schedule);
|
||||
|
||||
const ::Opm::RestartIO::ecl_kw_type * intehead = ::Opm::RestartIO::ecl_file_view_iget_named_kw( file_view , "INTEHEAD", 0 );
|
||||
const ::Opm::RestartIO::ecl_kw_type * opm_xwel = ::Opm::RestartIO::ecl_file_view_iget_named_kw( file_view , "OPM_XWEL", 0 );
|
||||
const ::Opm::RestartIO::ecl_kw_type * opm_iwel = ::Opm::RestartIO::ecl_file_view_iget_named_kw( file_view, "OPM_IWEL", 0 );
|
||||
auto rst_value = RestartValue{ std::move(xr), std::move(xw) };
|
||||
|
||||
UnitSystem units(getUnitSystem(intehead));
|
||||
RestartValue rst_value( ::Opm::RestartIO::restoreSOLUTION( file_view, solution_keys, grid.getNumActive( )),
|
||||
::Opm::RestartIO::restore_wells( opm_xwel, opm_iwel, sim_step , es, grid, schedule));
|
||||
|
||||
for (const auto& extra : extra_keys) {
|
||||
const std::string& key = extra.key;
|
||||
bool required = extra.required;
|
||||
|
||||
if (ecl_file_view_has_kw( file_view , key.c_str())) {
|
||||
const ::Opm::RestartIO::ecl_kw_type * ecl_kw = ::Opm::RestartIO::ecl_file_view_iget_named_kw( file_view , key.c_str() , 0 );
|
||||
const double * data_ptr = ::Opm::RestartIO::ecl_kw_get_type_ptr<double>( ecl_kw, ECL_DOUBLE_TYPE );
|
||||
const double * end_ptr = data_ptr + ::Opm::RestartIO::ecl_kw_get_size( ecl_kw );
|
||||
rst_value.addExtra(key, extra.dim, {data_ptr, end_ptr});
|
||||
} else if (required)
|
||||
throw std::runtime_error("No such key in file: " + key);
|
||||
}
|
||||
|
||||
// Convert solution fields and extra data from user units to SI
|
||||
rst_value.solution.convertToSI(units);
|
||||
for (auto & extra_value : rst_value.extra) {
|
||||
const auto& restart_key = extra_value.first;
|
||||
auto & data = extra_value.second;
|
||||
|
||||
units.to_si(restart_key.dim, data);
|
||||
if (! extra_keys.empty()) {
|
||||
restoreExtra(rst_view, extra_keys, es.getUnits(), rst_value);
|
||||
}
|
||||
|
||||
return rst_value;
|
||||
}
|
||||
|
||||
}
|
||||
}} // Opm::RestartIO
|
||||
|
@ -13,10 +13,18 @@ UNIFIN
|
||||
DIMENS
|
||||
10 10 10 /
|
||||
|
||||
WELLDIMS
|
||||
-- Item 1: NWMAX (Maximum number of wells in model)
|
||||
-- Item 2: NCWMAX (Maximum number of connections per well)
|
||||
-- Item 3: NGMAX (Maximum number of groups in model--excluding FIELD)
|
||||
-- Item 4: NWGMAX (Maximum number of wells or child groups per group)
|
||||
-- NWMAX NCWMAX NGMAX NWGMAX
|
||||
6 3 1 6
|
||||
/
|
||||
|
||||
EQLDIMS
|
||||
10 /
|
||||
|
||||
|
||||
GRID
|
||||
DXV
|
||||
10*0.25 /
|
||||
|
Loading…
Reference in New Issue
Block a user