2012-03-06 06:59:51 -06:00
|
|
|
/*
|
|
|
|
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
2016-04-04 01:10:24 -05:00
|
|
|
Copyright 2016 IRIS AS
|
2012-03-06 06:59:51 -06:00
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2013-04-10 05:56:14 -05:00
|
|
|
#include "config.h"
|
2014-01-07 09:10:45 -06:00
|
|
|
|
|
|
|
|
2012-06-05 08:42:49 -05:00
|
|
|
#include <opm/core/wells/WellsManager.hpp>
|
2012-03-06 06:59:51 -06:00
|
|
|
#include <opm/core/grid.h>
|
2013-03-18 04:33:34 -05:00
|
|
|
#include <opm/core/wells.h>
|
2014-01-05 08:03:30 -06:00
|
|
|
#include <opm/core/well_controls.h>
|
2015-10-08 04:42:15 -05:00
|
|
|
#include <opm/common/ErrorMacros.hpp>
|
2012-06-05 08:42:49 -05:00
|
|
|
#include <opm/core/wells/WellCollection.hpp>
|
2016-04-08 08:47:49 -05:00
|
|
|
#include <opm/core/wells/WellsGroup.hpp>
|
2013-03-14 04:29:42 -05:00
|
|
|
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2014-01-30 01:02:13 -06:00
|
|
|
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
|
|
|
|
2012-06-18 08:21:27 -05:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
2012-06-18 17:01:30 -05:00
|
|
|
#include <cmath>
|
|
|
|
#include <cstddef>
|
2012-06-18 17:30:32 -05:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
2013-08-28 07:19:12 -05:00
|
|
|
#include <iostream>
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2015-08-17 01:47:33 -05:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
static double invalid_alq = -1e100;
|
|
|
|
static double invalid_vfp = -2147483647;
|
|
|
|
} //Namespace
|
2012-03-06 06:59:51 -06:00
|
|
|
|
|
|
|
// Helper structs and functions for the implementation.
|
2014-02-25 10:52:51 -06:00
|
|
|
namespace WellsManagerDetail
|
2012-03-06 06:59:51 -06:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
namespace ProductionControl
|
2012-03-06 06:59:51 -06:00
|
|
|
{
|
2012-06-18 17:30:32 -05:00
|
|
|
namespace Details {
|
|
|
|
std::map<std::string, Mode>
|
|
|
|
init_mode_map() {
|
|
|
|
std::map<std::string, Mode> m;
|
|
|
|
|
|
|
|
m.insert(std::make_pair("ORAT", ORAT));
|
|
|
|
m.insert(std::make_pair("WRAT", WRAT));
|
|
|
|
m.insert(std::make_pair("GRAT", GRAT));
|
|
|
|
m.insert(std::make_pair("LRAT", LRAT));
|
|
|
|
m.insert(std::make_pair("CRAT", CRAT));
|
|
|
|
m.insert(std::make_pair("RESV", RESV));
|
|
|
|
m.insert(std::make_pair("BHP" , BHP ));
|
|
|
|
m.insert(std::make_pair("THP" , THP ));
|
|
|
|
m.insert(std::make_pair("GRUP", GRUP));
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
} // namespace Details
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
Mode mode(const std::string& control)
|
|
|
|
{
|
2012-06-18 17:30:32 -05:00
|
|
|
static std::map<std::string, Mode>
|
|
|
|
mode_map = Details::init_mode_map();
|
2012-06-18 08:21:27 -05:00
|
|
|
|
2012-06-18 17:30:32 -05:00
|
|
|
std::map<std::string, Mode>::iterator
|
|
|
|
p = mode_map.find(control);
|
|
|
|
|
|
|
|
if (p != mode_map.end()) {
|
|
|
|
return p->second;
|
|
|
|
}
|
|
|
|
else {
|
2013-08-28 06:59:03 -05:00
|
|
|
OPM_THROW(std::runtime_error, "Unknown well control mode = "
|
2012-06-18 17:30:32 -05:00
|
|
|
<< control << " in input file");
|
2012-05-02 02:38:18 -05:00
|
|
|
}
|
|
|
|
}
|
2014-01-30 08:55:17 -06:00
|
|
|
|
|
|
|
|
|
|
|
Mode mode(Opm::WellProducer::ControlModeEnum controlMode)
|
|
|
|
{
|
|
|
|
switch( controlMode ) {
|
|
|
|
case Opm::WellProducer::ORAT:
|
|
|
|
return ORAT;
|
|
|
|
case Opm::WellProducer::WRAT:
|
|
|
|
return WRAT;
|
|
|
|
case Opm::WellProducer::GRAT:
|
|
|
|
return GRAT;
|
|
|
|
case Opm::WellProducer::LRAT:
|
|
|
|
return LRAT;
|
|
|
|
case Opm::WellProducer::CRAT:
|
|
|
|
return CRAT;
|
|
|
|
case Opm::WellProducer::RESV:
|
|
|
|
return RESV;
|
|
|
|
case Opm::WellProducer::BHP:
|
|
|
|
return BHP;
|
|
|
|
case Opm::WellProducer::THP:
|
|
|
|
return THP;
|
|
|
|
case Opm::WellProducer::GRUP:
|
|
|
|
return GRUP;
|
|
|
|
default:
|
|
|
|
throw std::invalid_argument("unhandled enum value");
|
|
|
|
}
|
|
|
|
}
|
2012-05-02 02:38:18 -05:00
|
|
|
} // namespace ProductionControl
|
2012-03-06 06:59:51 -06:00
|
|
|
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
namespace InjectionControl
|
2012-03-06 06:59:51 -06:00
|
|
|
{
|
2012-06-18 08:21:27 -05:00
|
|
|
|
2012-06-18 17:30:32 -05:00
|
|
|
namespace Details {
|
|
|
|
std::map<std::string, Mode>
|
|
|
|
init_mode_map() {
|
|
|
|
std::map<std::string, Mode> m;
|
|
|
|
|
|
|
|
m.insert(std::make_pair("RATE", RATE));
|
|
|
|
m.insert(std::make_pair("RESV", RESV));
|
|
|
|
m.insert(std::make_pair("BHP" , BHP ));
|
|
|
|
m.insert(std::make_pair("THP" , THP ));
|
|
|
|
m.insert(std::make_pair("GRUP", GRUP));
|
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
} // namespace Details
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
Mode mode(const std::string& control)
|
|
|
|
{
|
2012-06-18 17:30:32 -05:00
|
|
|
static std::map<std::string, Mode>
|
|
|
|
mode_map = Details::init_mode_map();
|
|
|
|
|
|
|
|
std::map<std::string, Mode>::iterator
|
|
|
|
p = mode_map.find(control);
|
2012-05-02 02:38:18 -05:00
|
|
|
|
2012-06-18 17:30:32 -05:00
|
|
|
if (p != mode_map.end()) {
|
|
|
|
return p->second;
|
|
|
|
}
|
|
|
|
else {
|
2013-08-28 06:59:03 -05:00
|
|
|
OPM_THROW(std::runtime_error, "Unknown well control mode = "
|
2012-06-18 17:30:32 -05:00
|
|
|
<< control << " in input file");
|
2012-05-02 02:38:18 -05:00
|
|
|
}
|
|
|
|
}
|
2014-01-30 01:02:13 -06:00
|
|
|
|
|
|
|
Mode mode(Opm::WellInjector::ControlModeEnum controlMode)
|
|
|
|
{
|
|
|
|
switch ( controlMode ) {
|
|
|
|
case Opm::WellInjector::GRUP:
|
|
|
|
return GRUP;
|
|
|
|
case Opm::WellInjector::RESV:
|
|
|
|
return RESV;
|
|
|
|
case Opm::WellInjector::RATE:
|
|
|
|
return RATE;
|
|
|
|
case Opm::WellInjector::THP:
|
|
|
|
return THP;
|
|
|
|
case Opm::WellInjector::BHP:
|
|
|
|
return BHP;
|
|
|
|
default:
|
|
|
|
throw std::invalid_argument("unhandled enum value");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
} // namespace InjectionControl
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2014-08-29 09:57:10 -05:00
|
|
|
// Compute direction permutation corresponding to completion's
|
|
|
|
// direction. First two elements of return value are directions
|
|
|
|
// perpendicular to completion while last element is direction
|
|
|
|
// along completion.
|
|
|
|
inline std::array< std::array<double,3>::size_type, 3 >
|
2015-01-12 04:57:27 -06:00
|
|
|
directionIndices(const Opm::WellCompletion::DirectionEnum direction)
|
2014-08-29 09:57:10 -05:00
|
|
|
{
|
|
|
|
typedef std::array<double,3>::size_type idx_t;
|
|
|
|
typedef std::array<idx_t,3> permutation;
|
|
|
|
|
|
|
|
switch (direction) {
|
2015-01-12 04:57:27 -06:00
|
|
|
case Opm::WellCompletion::DirectionEnum::X:
|
2014-09-01 03:09:27 -05:00
|
|
|
return permutation {{ idx_t(1), idx_t(2), idx_t(0) }};
|
2014-08-29 09:57:10 -05:00
|
|
|
|
2015-01-12 04:57:27 -06:00
|
|
|
case Opm::WellCompletion::DirectionEnum::Y:
|
2014-09-01 03:09:27 -05:00
|
|
|
return permutation {{ idx_t(2), idx_t(0), idx_t(1) }};
|
2014-08-29 09:57:10 -05:00
|
|
|
|
2015-01-12 04:57:27 -06:00
|
|
|
case Opm::WellCompletion::DirectionEnum::Z:
|
2014-09-01 03:09:27 -05:00
|
|
|
return permutation {{ idx_t(0), idx_t(1), idx_t(2) }};
|
2014-08-29 09:57:10 -05:00
|
|
|
}
|
2014-11-19 10:31:36 -06:00
|
|
|
// All enum values should be handled above. Therefore
|
|
|
|
// we should never reach this one. Anyway for the sake
|
|
|
|
// of reduced warnings we throw an exception.
|
|
|
|
throw std::invalid_argument("unhandled enum value");
|
|
|
|
|
2014-08-29 09:57:10 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Permute (diagonal) permeability components according to
|
|
|
|
// completion's direction.
|
|
|
|
inline std::array<double,3>
|
2015-01-12 04:57:27 -06:00
|
|
|
permComponents(const Opm::WellCompletion::DirectionEnum direction,
|
2014-08-29 09:57:10 -05:00
|
|
|
const double* perm)
|
|
|
|
{
|
|
|
|
const auto p = directionIndices(direction);
|
|
|
|
const std::array<double,3>::size_type d = 3;
|
|
|
|
|
|
|
|
std::array<double,3>
|
2014-09-01 03:09:27 -05:00
|
|
|
K = {{ perm[ p[0]*(d + 1) ] ,
|
|
|
|
perm[ p[1]*(d + 1) ] ,
|
|
|
|
perm[ p[2]*(d + 1) ] }};
|
2014-08-29 09:57:10 -05:00
|
|
|
|
|
|
|
return K;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Permute cell's geometric extent according to completion's
|
|
|
|
// direction. Honour net-to-gross ratio.
|
|
|
|
//
|
|
|
|
// Note: 'extent' is intentionally accepted by modifiable value
|
|
|
|
// rather than reference-to-const to support NTG manipulation.
|
|
|
|
inline std::array<double,3>
|
2015-01-12 04:57:27 -06:00
|
|
|
effectiveExtent(const Opm::WellCompletion::DirectionEnum direction,
|
2014-08-29 09:57:10 -05:00
|
|
|
const double ntg,
|
|
|
|
std::array<double,3> extent)
|
|
|
|
{
|
|
|
|
// Vertical extent affected by net-to-gross ratio.
|
|
|
|
extent[2] *= ntg;
|
|
|
|
|
|
|
|
const auto p = directionIndices(direction);
|
|
|
|
|
|
|
|
std::array<double,3>
|
2014-09-01 06:30:32 -05:00
|
|
|
D = {{ extent[ p[0] ] ,
|
|
|
|
extent[ p[1] ] ,
|
|
|
|
extent[ p[2] ] }};
|
2014-08-29 09:57:10 -05:00
|
|
|
|
|
|
|
return D;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute Peaceman's effective radius of single completion.
|
|
|
|
inline double
|
|
|
|
effectiveRadius(const std::array<double,3>& K,
|
|
|
|
const std::array<double,3>& D)
|
|
|
|
{
|
|
|
|
const double K01 = K[0] / K[1];
|
|
|
|
const double K10 = K[1] / K[0];
|
|
|
|
|
|
|
|
const double D0_sq = D[0] * D[0];
|
|
|
|
const double D1_sq = D[1] * D[1];
|
|
|
|
|
2014-09-02 08:12:50 -05:00
|
|
|
const double num = std::sqrt((std::sqrt(K10) * D0_sq) +
|
|
|
|
(std::sqrt(K01) * D1_sq));
|
2014-08-29 09:57:10 -05:00
|
|
|
const double den = std::pow(K01, 0.25) + std::pow(K10, 0.25);
|
|
|
|
|
|
|
|
// Note: Analytic constant 0.28 derived for infintely sized
|
|
|
|
// formation with repeating well placement.
|
|
|
|
return 0.28 * (num / den);
|
|
|
|
}
|
|
|
|
|
2012-03-06 06:59:51 -06:00
|
|
|
// Use the Peaceman well model to compute well indices.
|
|
|
|
// radius is the radius of the well.
|
|
|
|
// cubical contains [dx, dy, dz] of the cell.
|
|
|
|
// (Note that the well model asumes that each cell is a cuboid).
|
|
|
|
// cell_permeability is the permeability tensor of the given cell.
|
|
|
|
// returns the well index of the cell.
|
2014-08-29 09:57:10 -05:00
|
|
|
double
|
|
|
|
computeWellIndex(const double radius,
|
|
|
|
const std::array<double, 3>& cubical,
|
|
|
|
const double* cell_permeability,
|
|
|
|
const double skin_factor,
|
2015-01-12 04:57:27 -06:00
|
|
|
const Opm::WellCompletion::DirectionEnum direction,
|
2014-08-29 09:57:10 -05:00
|
|
|
const double ntg)
|
2012-03-06 06:59:51 -06:00
|
|
|
{
|
2014-08-29 09:57:10 -05:00
|
|
|
const std::array<double,3>& K =
|
|
|
|
permComponents(direction, cell_permeability);
|
|
|
|
|
|
|
|
const std::array<double,3>& D =
|
|
|
|
effectiveExtent(direction, ntg, cubical);
|
|
|
|
|
|
|
|
const double r0 = effectiveRadius(K, D);
|
|
|
|
const double Kh = std::sqrt(K[0] * K[1]) * D[2];
|
|
|
|
|
|
|
|
// Angle of completion exposed to flow. We assume centre
|
|
|
|
// placement so there's complete exposure (= 2\pi).
|
|
|
|
const double angle =
|
|
|
|
6.2831853071795864769252867665590057683943387987502116419498;
|
|
|
|
|
|
|
|
double rw = radius;
|
|
|
|
if (r0 < rw) {
|
|
|
|
std::cerr << "Completion radius exceeds effective radius\n"
|
|
|
|
<< "Reset to effective\n";
|
|
|
|
|
|
|
|
rw = r0;
|
2012-05-02 02:38:18 -05:00
|
|
|
}
|
2014-08-29 09:57:10 -05:00
|
|
|
|
|
|
|
// NOTE: The formula is originally derived and valid for
|
|
|
|
// Cartesian grids only.
|
|
|
|
return (angle * Kh) / (std::log(r0 / rw) + skin_factor);
|
2012-03-06 06:59:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace Opm
|
|
|
|
{
|
|
|
|
|
|
|
|
|
2012-03-09 07:25:52 -06:00
|
|
|
/// Default constructor.
|
|
|
|
WellsManager::WellsManager()
|
2015-05-08 12:44:50 -05:00
|
|
|
: w_(0), is_parallel_run_(false)
|
2012-03-09 07:25:52 -06:00
|
|
|
{
|
|
|
|
}
|
2013-07-28 06:34:13 -05:00
|
|
|
|
2014-03-29 04:12:40 -05:00
|
|
|
/// Construct from existing wells object.
|
|
|
|
WellsManager::WellsManager(struct Wells* W)
|
2015-05-08 12:44:50 -05:00
|
|
|
: w_(clone_wells(W)), is_parallel_run_(false)
|
2014-03-29 04:12:40 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-01-07 09:10:45 -06:00
|
|
|
/// Construct wells from deck.
|
2016-10-13 09:03:35 -05:00
|
|
|
WellsManager::WellsManager(const Opm::EclipseState& eclipseState,
|
2014-01-07 09:10:45 -06:00
|
|
|
const size_t timeStep,
|
|
|
|
const UnstructuredGrid& grid,
|
|
|
|
const double* permeability)
|
2015-05-08 12:44:50 -05:00
|
|
|
: w_(0), is_parallel_run_(false)
|
2012-03-06 06:59:51 -06:00
|
|
|
{
|
2016-06-30 09:20:38 -05:00
|
|
|
std::vector<double> dummy_well_potentials;
|
2016-06-24 09:01:12 -05:00
|
|
|
// TODO: not sure about the usage of this WellsManager constructor
|
|
|
|
// TODO: not sure whether this is the correct thing to do here.
|
|
|
|
DynamicListEconLimited dummy_list_econ_limited;
|
2014-03-27 09:27:11 -05:00
|
|
|
init(eclipseState, timeStep, UgGridHelpers::numCells(grid),
|
|
|
|
UgGridHelpers::globalCell(grid), UgGridHelpers::cartDims(grid),
|
2015-02-17 03:28:11 -06:00
|
|
|
UgGridHelpers::dimensions(grid),
|
2014-03-27 09:27:11 -05:00
|
|
|
UgGridHelpers::cell2Faces(grid), UgGridHelpers::beginFaceCentroids(grid),
|
2016-09-08 14:18:53 -05:00
|
|
|
permeability, dummy_list_econ_limited, dummy_well_potentials,
|
2016-09-12 05:03:39 -05:00
|
|
|
std::unordered_set<std::string>());
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2014-01-07 09:10:45 -06:00
|
|
|
}
|
2012-03-06 06:59:51 -06:00
|
|
|
|
|
|
|
/// Destructor.
|
|
|
|
WellsManager::~WellsManager()
|
|
|
|
{
|
2012-05-02 02:38:18 -05:00
|
|
|
destroy_wells(w_);
|
2012-03-06 06:59:51 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-09 09:09:13 -05:00
|
|
|
/// Does the "deck" define any wells?
|
|
|
|
bool WellsManager::empty() const
|
|
|
|
{
|
|
|
|
return (w_ == 0) || (w_->number_of_wells == 0);
|
|
|
|
}
|
|
|
|
|
2012-03-06 06:59:51 -06:00
|
|
|
|
|
|
|
|
|
|
|
/// Access the managed Wells.
|
|
|
|
/// The method is named similarly to c_str() in std::string,
|
|
|
|
/// to make it clear that we are returning a C-compatible struct.
|
|
|
|
const Wells* WellsManager::c_wells() const
|
|
|
|
{
|
2012-05-02 02:38:18 -05:00
|
|
|
return w_;
|
2012-03-06 06:59:51 -06:00
|
|
|
}
|
|
|
|
|
2012-04-12 07:25:39 -05:00
|
|
|
const WellCollection& WellsManager::wellCollection() const
|
|
|
|
{
|
|
|
|
return well_collection_;
|
|
|
|
}
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2016-09-30 05:59:10 -05:00
|
|
|
WellCollection& WellsManager::wellCollection() {
|
|
|
|
return well_collection_;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-05-02 02:38:18 -05:00
|
|
|
bool WellsManager::conditionsMet(const std::vector<double>& well_bhp,
|
2012-05-02 06:02:59 -05:00
|
|
|
const std::vector<double>& well_reservoirrates_phase,
|
|
|
|
const std::vector<double>& well_surfacerates_phase)
|
2012-04-23 06:24:47 -05:00
|
|
|
{
|
2012-05-02 06:02:59 -05:00
|
|
|
return well_collection_.conditionsMet(well_bhp,
|
|
|
|
well_reservoirrates_phase,
|
|
|
|
well_surfacerates_phase);
|
2012-04-23 06:24:47 -05:00
|
|
|
}
|
2012-03-06 06:59:51 -06:00
|
|
|
|
2012-05-09 06:03:37 -05:00
|
|
|
/// Applies explicit reinjection controls. This must be called at each timestep to be correct.
|
|
|
|
/// \param[in] well_reservoirrates_phase
|
|
|
|
/// A vector containing reservoir rates by phase for each well.
|
|
|
|
/// Is assumed to be ordered the same way as the related Wells-struct,
|
|
|
|
/// with all phase rates of a single well adjacent in the array.
|
|
|
|
/// \param[in] well_surfacerates_phase
|
|
|
|
/// A vector containing surface rates by phase for each well.
|
|
|
|
/// Is assumed to be ordered the same way as the related Wells-struct,
|
|
|
|
/// with all phase rates of a single well adjacent in the array.
|
|
|
|
|
|
|
|
void WellsManager::applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
|
|
|
const std::vector<double>& well_surfacerates_phase)
|
|
|
|
{
|
|
|
|
well_collection_.applyExplicitReinjectionControls(well_reservoirrates_phase, well_surfacerates_phase);
|
|
|
|
}
|
|
|
|
|
2014-02-17 06:23:01 -06:00
|
|
|
void WellsManager::setupCompressedToCartesian(const int* global_cell, int number_of_cells,
|
|
|
|
std::map<int,int>& cartesian_to_compressed ) {
|
2014-02-03 01:58:54 -06:00
|
|
|
// global_cell is a map from compressed cells to Cartesian grid cells.
|
|
|
|
// We must make the inverse lookup.
|
|
|
|
|
|
|
|
if (global_cell) {
|
2014-02-17 06:23:01 -06:00
|
|
|
for (int i = 0; i < number_of_cells; ++i) {
|
2014-02-03 01:58:54 -06:00
|
|
|
cartesian_to_compressed.insert(std::make_pair(global_cell[i], i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2014-02-17 06:23:01 -06:00
|
|
|
for (int i = 0; i < number_of_cells; ++i) {
|
2014-02-03 01:58:54 -06:00
|
|
|
cartesian_to_compressed.insert(std::make_pair(i, i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-03 02:07:58 -06:00
|
|
|
|
2016-06-16 02:33:43 -05:00
|
|
|
void WellsManager::setupWellControls(std::vector< const Well* >& wells, size_t timeStep,
|
2015-05-26 13:45:08 -05:00
|
|
|
std::vector<std::string>& well_names, const PhaseUsage& phaseUsage,
|
2016-06-28 02:08:37 -05:00
|
|
|
const std::vector<int>& wells_on_proc,
|
|
|
|
const DynamicListEconLimited& list_econ_limited) {
|
2014-02-03 02:07:58 -06:00
|
|
|
int well_index = 0;
|
2015-05-26 13:45:08 -05:00
|
|
|
auto well_on_proc = wells_on_proc.begin();
|
|
|
|
|
|
|
|
for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter, ++well_on_proc) {
|
|
|
|
if( ! *well_on_proc )
|
|
|
|
{
|
|
|
|
// Wells not stored on the process are not in the list
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-29 07:46:24 -05:00
|
|
|
const auto* well = (*wellIter);
|
2014-02-06 09:31:35 -06:00
|
|
|
|
2014-10-03 06:12:31 -05:00
|
|
|
if (well->getStatus(timeStep) == WellCommon::SHUT) {
|
2014-10-27 01:20:03 -05:00
|
|
|
//SHUT wells are not added to the well list
|
2014-10-03 06:12:31 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-30 09:20:38 -05:00
|
|
|
if (list_econ_limited.wellShutEconLimited(well->name())) {
|
2016-06-28 02:08:37 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-06-29 07:46:24 -05:00
|
|
|
if (well->getStatus(timeStep) == WellCommon::STOP || list_econ_limited.wellStoppedEconLimited(well->name())) {
|
|
|
|
// Stopped wells are kept in the well list but marked as stopped.
|
|
|
|
well_controls_stop_well(w_->ctrls[well_index]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-03 02:07:58 -06:00
|
|
|
if (well->isInjector(timeStep)) {
|
2014-03-20 18:35:15 -05:00
|
|
|
const WellInjectionProperties& injectionProperties = well->getInjectionProperties(timeStep);
|
2014-02-03 02:07:58 -06:00
|
|
|
int ok = 1;
|
|
|
|
int control_pos[5] = { -1, -1, -1, -1, -1 };
|
2014-03-20 18:35:15 -05:00
|
|
|
|
|
|
|
clear_well_controls(well_index, w_);
|
|
|
|
if (injectionProperties.hasInjectionControl(WellInjector::RATE)) {
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
2014-03-20 18:35:15 -05:00
|
|
|
WellInjector::TypeEnum injectorType = injectionProperties.injectorType;
|
2014-02-03 02:07:58 -06:00
|
|
|
|
|
|
|
if (injectorType == WellInjector::TypeEnum::WATER) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::TypeEnum::OIL) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::TypeEnum::GAS) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = append_well_controls(SURFACE_RATE,
|
2014-03-20 18:35:15 -05:00
|
|
|
injectionProperties.surfaceInjectionRate,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && injectionProperties.hasInjectionControl(WellInjector::RESV)) {
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
2014-03-20 18:35:15 -05:00
|
|
|
WellInjector::TypeEnum injectorType = injectionProperties.injectorType;
|
2014-02-03 02:07:58 -06:00
|
|
|
|
|
|
|
if (injectorType == WellInjector::TypeEnum::WATER) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::TypeEnum::OIL) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::TypeEnum::GAS) {
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok = append_well_controls(RESERVOIR_RATE,
|
2014-03-20 18:35:15 -05:00
|
|
|
injectionProperties.reservoirInjectionRate,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && injectionProperties.hasInjectionControl(WellInjector::BHP)) {
|
2014-03-27 09:27:11 -05:00
|
|
|
control_pos[WellsManagerDetail::InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
ok = append_well_controls(BHP,
|
2015-07-08 10:40:10 -05:00
|
|
|
injectionProperties.BHPLimit,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
NULL,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && injectionProperties.hasInjectionControl(WellInjector::THP)) {
|
2015-08-11 09:26:56 -05:00
|
|
|
control_pos[WellsManagerDetail::InjectionControl::THP] = well_controls_get_num(w_->ctrls[well_index]);
|
2015-08-11 05:51:18 -05:00
|
|
|
const double thp_limit = injectionProperties.THPLimit;
|
|
|
|
const int vfp_number = injectionProperties.VFPTableNumber;
|
|
|
|
ok = append_well_controls(THP,
|
|
|
|
thp_limit,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
2015-08-11 05:51:18 -05:00
|
|
|
vfp_number,
|
|
|
|
NULL,
|
|
|
|
well_index,
|
|
|
|
w_);
|
2014-02-03 02:07:58 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]);
|
|
|
|
}
|
|
|
|
|
2014-10-14 05:42:55 -05:00
|
|
|
if (injectionProperties.controlMode != WellInjector::CMODE_UNDEFINED) {
|
2014-03-27 09:27:11 -05:00
|
|
|
WellsManagerDetail::InjectionControl::Mode mode = WellsManagerDetail::InjectionControl::mode( injectionProperties.controlMode );
|
2014-02-03 02:07:58 -06:00
|
|
|
int cpos = control_pos[mode];
|
2014-02-25 10:52:51 -06:00
|
|
|
if (cpos == -1 && mode != WellsManagerDetail::InjectionControl::GRUP) {
|
2014-02-03 02:07:58 -06:00
|
|
|
OPM_THROW(std::runtime_error, "Control not specified in well " << well_names[well_index]);
|
|
|
|
}
|
2014-10-17 01:05:13 -05:00
|
|
|
|
2014-02-03 02:07:58 -06:00
|
|
|
set_current_control(well_index, cpos, w_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set well component fraction.
|
|
|
|
double cf[3] = { 0.0, 0.0, 0.0 };
|
2014-03-20 18:35:15 -05:00
|
|
|
{
|
|
|
|
WellInjector::TypeEnum injectorType = injectionProperties.injectorType;
|
|
|
|
|
|
|
|
if (injectorType == WellInjector::WATER) {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not used, yet found water-injecting well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::OIL) {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-injecting well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
|
|
|
} else if (injectorType == WellInjector::GAS) {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-injecting well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
2014-02-03 02:07:58 -06:00
|
|
|
}
|
2014-03-20 18:35:15 -05:00
|
|
|
std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases);
|
2014-02-03 02:07:58 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (well->isProducer(timeStep)) {
|
|
|
|
// Add all controls that are present in well.
|
|
|
|
// First we must clear existing controls, in case the
|
|
|
|
// current WCONPROD line is modifying earlier controls.
|
2014-03-20 18:35:15 -05:00
|
|
|
const WellProductionProperties& productionProperties = well->getProductionProperties(timeStep);
|
2014-02-03 02:07:58 -06:00
|
|
|
int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
|
|
|
int ok = 1;
|
2014-03-20 18:35:15 -05:00
|
|
|
|
|
|
|
clear_well_controls(well_index, w_);
|
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::ORAT)) {
|
2014-02-03 02:07:58 -06:00
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified.");
|
|
|
|
}
|
|
|
|
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
|
|
|
ok = append_well_controls(SURFACE_RATE,
|
2015-07-08 10:40:10 -05:00
|
|
|
-productionProperties.OilRate,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
2015-08-17 01:47:33 -05:00
|
|
|
well_index,
|
2014-02-03 02:07:58 -06:00
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::WRAT)) {
|
2014-02-03 02:07:58 -06:00
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified.");
|
|
|
|
}
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
ok = append_well_controls(SURFACE_RATE,
|
2014-03-20 18:35:15 -05:00
|
|
|
-productionProperties.WaterRate,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::GRAT)) {
|
2014-02-03 02:07:58 -06:00
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified.");
|
|
|
|
}
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
|
|
|
ok = append_well_controls(SURFACE_RATE,
|
2014-03-20 18:35:15 -05:00
|
|
|
-productionProperties.GasRate,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::LRAT)) {
|
2014-02-03 02:07:58 -06:00
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not active and LRAT control specified.");
|
|
|
|
}
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not active and LRAT control specified.");
|
|
|
|
}
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
|
|
|
ok = append_well_controls(SURFACE_RATE,
|
2015-08-17 01:47:33 -05:00
|
|
|
-productionProperties.LiquidRate,
|
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2014-03-20 18:35:15 -05:00
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::RESV)) {
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
double distr[3] = { 1.0, 1.0, 1.0 };
|
|
|
|
ok = append_well_controls(RESERVOIR_RATE,
|
2015-08-17 01:47:33 -05:00
|
|
|
-productionProperties.ResVRate,
|
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
distr,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2015-07-08 10:40:10 -05:00
|
|
|
if (ok && productionProperties.hasProductionControl(WellProducer::THP)) {
|
|
|
|
const double thp_limit = productionProperties.THPLimit;
|
|
|
|
const double alq_value = productionProperties.ALQValue;
|
|
|
|
const int vfp_number = productionProperties.VFPTableNumber;
|
|
|
|
control_pos[WellsManagerDetail::ProductionControl::THP] = well_controls_get_num(w_->ctrls[well_index]);
|
|
|
|
ok = append_well_controls(THP,
|
|
|
|
thp_limit,
|
|
|
|
alq_value,
|
|
|
|
vfp_number,
|
|
|
|
NULL,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
2015-02-04 03:30:15 -06:00
|
|
|
if (ok) {
|
|
|
|
// Always append a BHP control.
|
|
|
|
// If no explicit BHP control given, use a 1 atm control.
|
|
|
|
const bool has_explicit_limit = productionProperties.hasProductionControl(WellProducer::BHP);
|
|
|
|
const double bhp_limit = has_explicit_limit ? productionProperties.BHPLimit : unit::convert::from(1.0, unit::atm);
|
2014-02-25 10:52:51 -06:00
|
|
|
control_pos[WellsManagerDetail::ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]);
|
2014-02-03 02:07:58 -06:00
|
|
|
ok = append_well_controls(BHP,
|
2015-02-04 03:30:15 -06:00
|
|
|
bhp_limit,
|
2015-08-17 01:47:33 -05:00
|
|
|
invalid_alq,
|
|
|
|
invalid_vfp,
|
2014-02-03 02:07:58 -06:00
|
|
|
NULL,
|
|
|
|
well_index,
|
|
|
|
w_);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ok) {
|
|
|
|
OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]);
|
|
|
|
}
|
|
|
|
|
2014-10-14 05:42:55 -05:00
|
|
|
if (productionProperties.controlMode != WellProducer::CMODE_UNDEFINED) {
|
|
|
|
WellsManagerDetail::ProductionControl::Mode mode = WellsManagerDetail::ProductionControl::mode(productionProperties.controlMode);
|
|
|
|
int cpos = control_pos[mode];
|
|
|
|
if (cpos == -1 && mode != WellsManagerDetail::ProductionControl::GRUP) {
|
|
|
|
OPM_THROW(std::runtime_error, "Control mode type " << mode << " not present in well " << well_names[well_index]);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_current_control(well_index, cpos, w_);
|
|
|
|
}
|
2014-10-03 06:12:31 -05:00
|
|
|
}
|
2014-04-04 05:15:49 -05:00
|
|
|
|
2014-04-06 16:33:43 -05:00
|
|
|
// Set well component fraction to match preferred phase for the well.
|
|
|
|
double cf[3] = { 0.0, 0.0, 0.0 };
|
|
|
|
{
|
|
|
|
switch (well->getPreferredPhase()) {
|
|
|
|
case Phase::WATER:
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not used, yet found water-preferring well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
|
|
|
break;
|
|
|
|
case Phase::OIL:
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil-preferring well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
2014-04-24 08:43:54 -05:00
|
|
|
break;
|
2014-04-06 16:33:43 -05:00
|
|
|
case Phase::GAS:
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas-preferring well.");
|
|
|
|
}
|
|
|
|
cf[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
2014-04-24 08:43:54 -05:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
OPM_THROW(std::logic_error, "Unknown preferred phase: " << well->getPreferredPhase());
|
2014-04-06 16:33:43 -05:00
|
|
|
}
|
|
|
|
std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases);
|
|
|
|
}
|
2014-02-03 02:07:58 -06:00
|
|
|
}
|
|
|
|
well_index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2014-02-14 06:43:25 -06:00
|
|
|
|
2016-06-16 02:33:43 -05:00
|
|
|
void WellsManager::setupGuideRates(std::vector< const Well* >& wells, const size_t timeStep, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index,
|
2016-04-01 07:08:48 -05:00
|
|
|
const PhaseUsage& phaseUsage, const std::vector<double>& well_potentials)
|
2014-02-24 08:24:33 -06:00
|
|
|
{
|
2016-04-01 07:08:48 -05:00
|
|
|
const int np = phaseUsage.num_phases;
|
2014-02-24 08:24:33 -06:00
|
|
|
for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) {
|
2016-06-16 02:33:43 -05:00
|
|
|
const auto* well = *wellIter;
|
2016-04-08 08:47:49 -05:00
|
|
|
|
|
|
|
if (well->getStatus(timeStep) == WellCommon::SHUT) {
|
|
|
|
//SHUT wells does not need guide rates
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-02-24 08:24:33 -06:00
|
|
|
const int wix = well_names_to_index[well->name()];
|
|
|
|
WellNode& wellnode = *well_collection_.getLeafNodes()[wix];
|
|
|
|
|
|
|
|
if (well->getGuideRatePhase(timeStep) != GuideRate::UNDEFINED) {
|
|
|
|
if (well_data[wix].type == PRODUCER) {
|
|
|
|
wellnode.prodSpec().guide_rate_ = well->getGuideRate(timeStep);
|
|
|
|
if (well->getGuideRatePhase(timeStep) == GuideRate::OIL) {
|
|
|
|
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Guide rate type " << GuideRate::GuideRatePhaseEnum2String(well->getGuideRatePhase(timeStep)) << " specified for producer "
|
|
|
|
<< well->name() << " in WGRUPCON, cannot handle.");
|
|
|
|
}
|
|
|
|
} else if (well_data[wix].type == INJECTOR) {
|
|
|
|
wellnode.injSpec().guide_rate_ = well->getGuideRate(timeStep);
|
|
|
|
if (well->getGuideRatePhase(timeStep) == GuideRate::RAT) {
|
|
|
|
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Guide rate type " << GuideRate::GuideRatePhaseEnum2String(well->getGuideRatePhase(timeStep)) << " specified for injector "
|
|
|
|
<< well->name() << " in WGRUPCON, cannot handle.");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Unknown well type " << well_data[wix].type << " for well " << well->name());
|
|
|
|
}
|
2016-04-04 01:10:24 -05:00
|
|
|
} else if (well_potentials.size() > 0) { // default: calculate guide rates from well potentials
|
2016-04-01 07:08:48 -05:00
|
|
|
|
|
|
|
// Note: Modification of the guide rate using GUIDERAT is not supported
|
2016-04-15 05:56:04 -05:00
|
|
|
|
2016-04-08 08:47:49 -05:00
|
|
|
// only set the guide rates if there is a parent group with valied control
|
2016-04-15 05:56:04 -05:00
|
|
|
if (wellnode.getParent() != nullptr) {
|
|
|
|
const WellsGroupInterface& group = *wellnode.getParent();
|
2016-04-01 07:08:48 -05:00
|
|
|
if ( well->isProducer(timeStep) ) {
|
2016-04-08 08:47:49 -05:00
|
|
|
// The guide rates is calculated based on the group control
|
|
|
|
// Currently only supporting WRAT, ORAT and GRAT.
|
2016-09-22 09:16:32 -05:00
|
|
|
ProductionSpecification::ControlMode control_mode = group.prodSpec().control_mode_;
|
|
|
|
if (control_mode == ProductionSpecification::FLD) {
|
|
|
|
if (group.getParent() != nullptr) {
|
|
|
|
const WellsGroupInterface& higher_group = *(group.getParent());
|
|
|
|
control_mode = higher_group.prodSpec().control_mode_;
|
|
|
|
} else {
|
|
|
|
OPM_THROW(std::runtime_error, "Group " << group.name() << " is under FLD control while no higher level of group is specified.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (control_mode) {
|
2016-04-08 08:47:49 -05:00
|
|
|
case ProductionSpecification::WRAT: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not used, yet found water rate controlled well.");
|
|
|
|
}
|
|
|
|
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
|
|
|
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + water_index];
|
|
|
|
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::WATER;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ProductionSpecification::ORAT: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil rate controlled well.");
|
|
|
|
}
|
|
|
|
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
|
|
|
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + oil_index];
|
|
|
|
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ProductionSpecification::GRAT: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas rate controlled well.");
|
|
|
|
}
|
|
|
|
const int gas_index = phaseUsage.phase_pos[BlackoilPhases::Vapour];
|
|
|
|
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + gas_index];
|
|
|
|
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::GAS;
|
|
|
|
break;
|
|
|
|
}
|
2016-09-22 09:16:32 -05:00
|
|
|
case ProductionSpecification::FLD: {
|
|
|
|
OPM_THROW(std::logic_error, "Not support more than one continous level of FLD control");
|
|
|
|
}
|
|
|
|
case ProductionSpecification::LRAT: {
|
|
|
|
double guide_rate = 0;
|
|
|
|
if (phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
|
|
|
const double potential_oil = well_potentials[np*wix + oil_index];
|
|
|
|
guide_rate += potential_oil;
|
|
|
|
}
|
|
|
|
if (phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
|
|
|
const double potential_water = well_potentials[np*wix + water_index];
|
|
|
|
guide_rate += potential_water;
|
|
|
|
}
|
|
|
|
// not sure if no water and no oil, what will happen here, zero guide_rate?
|
|
|
|
wellnode.prodSpec().guide_rate_ = guide_rate;
|
|
|
|
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::LIQ;
|
|
|
|
}
|
2016-04-08 08:47:49 -05:00
|
|
|
case ProductionSpecification::NONE: {
|
|
|
|
// Group control is not in use for this group.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
OPM_THROW(std::logic_error, "Not supported control_mode for guide rate computed" <<
|
|
|
|
" from well potentials: " << group.prodSpec().control_mode_);
|
|
|
|
}
|
2016-04-01 07:08:48 -05:00
|
|
|
} else {
|
2016-04-08 08:47:49 -05:00
|
|
|
// The guide rates is calculated based on the group injector type
|
|
|
|
switch (group.injSpec().injector_type_) {
|
|
|
|
case InjectionSpecification::WATER: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Water phase not used, yet found water injecting well.");
|
|
|
|
}
|
|
|
|
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
|
|
|
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + water_index];
|
|
|
|
// Guide rates applies to the phase that the well is injecting i.e water
|
|
|
|
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case InjectionSpecification::OIL: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil injecting well.");
|
|
|
|
}
|
|
|
|
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
|
|
|
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + oil_index];
|
|
|
|
// Guide rates applies to the phase that the well is injecting i.e. oil
|
|
|
|
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case InjectionSpecification::GAS: {
|
|
|
|
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
|
|
|
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas injecting well.");
|
|
|
|
}
|
|
|
|
const int gas_index = phaseUsage.phase_pos[BlackoilPhases::Vapour];
|
|
|
|
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + gas_index];
|
|
|
|
// Guide rates applies to the phase that the well is injecting i.e gas
|
|
|
|
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
OPM_THROW(std::logic_error, "Not supported injector type for guide rate computed" <<
|
|
|
|
" from well potentials: " << group.injSpec().injector_type_);
|
|
|
|
}
|
2016-04-01 07:08:48 -05:00
|
|
|
}
|
|
|
|
|
2016-04-08 08:47:49 -05:00
|
|
|
}
|
2016-04-01 07:08:48 -05:00
|
|
|
} // if neither WGRUPCON nor well_potentials is given, distribute the flow equaly
|
2014-02-24 08:24:33 -06:00
|
|
|
}
|
2016-04-01 07:08:48 -05:00
|
|
|
|
2014-02-24 08:24:33 -06:00
|
|
|
}
|
|
|
|
|
2012-03-06 06:59:51 -06:00
|
|
|
} // namespace Opm
|