/*
Copyright 2016 SINTEF ICT, Applied Mathematics.
Copyright 2016 Statoil ASA.
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see .
*/
#if HAVE_CONFIG_H
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace Opm
{
namespace {
// --------- Restart file keywords. ---------
// Note:
// This struct is more complete (containing more fields)
// than currently needed, but we should expect that more
// fields could be needed in the future.
struct INTEHEAD
{
// Unit codes used in INTEHEAD
enum {
Metric = 1,
Field = 2,
Lab = 3
};
explicit INTEHEAD(const std::vector& v)
: unit (v[INTEHEAD_UNIT_INDEX])
, nx (v[INTEHEAD_NX_INDEX])
, ny (v[INTEHEAD_NY_INDEX])
, nz (v[INTEHEAD_NZ_INDEX])
, nactive(v[INTEHEAD_NACTIVE_INDEX])
, iphs (v[INTEHEAD_PHASE_INDEX])
, nwell (v[INTEHEAD_NWELLS_INDEX])
, ncwma (v[INTEHEAD_NCWMAX_INDEX])
, nwgmax (v[INTEHEAD_NWGMAX_INDEX])
, ngmaxz (v[INTEHEAD_NGMAXZ_INDEX])
, niwel (v[INTEHEAD_NIWELZ_INDEX])
, nswel (v[INTEHEAD_NSWELZ_INDEX])
, nxwel (v[INTEHEAD_NXWELZ_INDEX])
, nzwel (v[INTEHEAD_NZWELZ_INDEX])
, nicon (v[INTEHEAD_NICONZ_INDEX])
, nscon (v[INTEHEAD_NSCONZ_INDEX])
, nxcon (v[INTEHEAD_NXCONZ_INDEX])
, nigrpz (v[INTEHEAD_NIGRPZ_INDEX])
, iday (v[INTEHEAD_DAY_INDEX])
, imon (v[INTEHEAD_MONTH_INDEX])
, iyear (v[INTEHEAD_YEAR_INDEX])
, iprog (v[INTEHEAD_IPROG_INDEX])
{
}
int unit; // Unit system. 1:metric, 2:field, 3:lab.
int nx; // Cartesian size i-direction.
int ny; // Cartesian size j-direction.
int nz; // Cartesian size k-direction.
int nactive; // Number of active cells.
int iphs; // Phase. 1:o, 2:w, 3:ow, 4:g, 5:og, 6:wg, 7:owg.
int nwell; // Number of wells.
int ncwma; // Maximum number of completions per well.
int nwgmax; // Maximum number of wells in any group.
int ngmaxz; // Maximum number of groups in field.
int niwel; // Number of elements pr. well in the IWEL array.
int nswel; // Number of elements pr. well in the SWEL array.
int nxwel; // Number of elements pr. well in the XWEL array.
int nzwel; // Number of 8 character words pr. well in ZWEL.
int nicon; // Number of elements pr completion in the ICON array.
int nscon; // Number of elements pr completion in the SCON array.
int nxcon; // Number of elements pr completion in the XCON array.
int nigrpz; // Number of elements pr group in the IGRP array.
int iday; // Report day.
int imon; // Report month.
int iyear; // Report year.
int iprog; // Eclipse program type. 100, 300 or 500.
};
// Reservoir rate units from code used in INTEHEAD.
double resRateUnit(const int unit_code)
{
return ECLUnits::createUnitSystem(unit_code)->reservoirRate();
}
// Return input string with spaces stripped of the right end.
std::string trimSpacesRight(const std::string& s)
{
return std::string(s.begin(), s.begin() + s.find_last_not_of(' ') + 1);
}
// Constants not provided by ert.
enum { XWEL_RESV_INDEX = 4 };
enum { IWEL_TYPE_PRODUCER = 1 };
} // anonymous namespace
ECLWellSolution::ECLWellSolution(const double rate_threshold,
const bool disallow_crossflow)
: rate_threshold_(rate_threshold)
, disallow_crossflow_(disallow_crossflow)
{
}
std::vector
ECLWellSolution::solution(const ECLResultData& restart,
const int num_grids) const
{
// Read well data for global grid.
std::vector all_wd{};
for (int grid_index = 0; grid_index < num_grids; ++grid_index) {
std::vector wd = readWellData(restart, grid_index);
// Append to set of all well data.
all_wd.insert(all_wd.end(), wd.begin(), wd.end());
}
return all_wd;
}
std::vector
ECLWellSolution::readWellData(const ECLResultData& restart, const int grid_index) const
{
// Note: this function is expected to be called in a context
// where the correct restart block using the ert block mechanisms.
// Read header, return if trivial.
INTEHEAD ih(restart.keywordData(INTEHEAD_KW, grid_index));
if (ih.nwell == 0) {
return {};
}
const double qr_unit = resRateUnit(ih.unit);
// Read necessary keywords.
auto zwel = restart.keywordData(ZWEL_KW, grid_index);
auto iwel = restart.keywordData (IWEL_KW, grid_index);
auto xwel = restart.keywordData ("XWEL" , grid_index);
auto icon = restart.keywordData (ICON_KW, grid_index);
auto xcon = restart.keywordData ("XCON" , grid_index);
// Create well data.
std::vector wd_vec;
wd_vec.reserve(ih.nwell);
for (int well = 0; well < ih.nwell; ++well) {
// Skip if total rate below threshold (for wells that are
// shut or stopped for example).
const double well_reservoir_inflow_rate = -unit::convert::from(xwel[well * ih.nxwel + XWEL_RESV_INDEX], qr_unit);
if (std::fabs(well_reservoir_inflow_rate) < rate_threshold_) {
continue;
}
// Otherwise: add data for this well.
WellData wd;
wd.name = trimSpacesRight(zwel[well * ih.nzwel]);
const bool is_producer = (iwel[well * ih.niwel + IWEL_TYPE_INDEX] == IWEL_TYPE_PRODUCER);
wd.is_injector_well = !is_producer;
const int ncon = iwel[well * ih.niwel + IWEL_CONNECTIONS_INDEX];
wd.completions.reserve(ncon);
for (int comp_index = 0; comp_index < ncon; ++comp_index) {
const int icon_offset = (well*ih.ncwma + comp_index) * ih.nicon;
const int xcon_offset = (well*ih.ncwma + comp_index) * ih.nxcon;
WellData::Completion completion;
// Note: subtracting 1 from indices (Fortran -> C convention).
completion.grid_index = grid_index;
completion.ijk = { icon[icon_offset + ICON_I_INDEX] - 1,
icon[icon_offset + ICON_J_INDEX] - 1,
icon[icon_offset + ICON_K_INDEX] - 1 };
// Note: taking the negative input, to get inflow rate.
completion.reservoir_inflow_rate = -unit::convert::from(xcon[xcon_offset + XCON_QR_INDEX], qr_unit);
if (disallow_crossflow_) {
// Add completion only if not cross-flowing (injecting producer or producing injector).
if (completion.reservoir_inflow_rate < 0.0 == is_producer) {
wd.completions.push_back(completion);
}
} else {
// Always add completion.
wd.completions.push_back(completion);
}
}
wd_vec.push_back(wd);
}
return wd_vec;
}
} // namespace Opm