mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-01-19 16:12:58 -06:00
Remove unused code.
This commit is contained in:
parent
bddeaba880
commit
e718bf3ccf
@ -26,13 +26,6 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
ebos/nncsorter.cpp
|
||||
opm/core/props/satfunc/RelpermDiagnostics.cpp
|
||||
opm/simulators/timestepping/SimulatorReport.cpp
|
||||
opm/core/wells/InjectionSpecification.cpp
|
||||
opm/core/wells/ProductionSpecification.cpp
|
||||
opm/core/wells/WellCollection.cpp
|
||||
opm/core/wells/WellsGroup.cpp
|
||||
opm/core/wells/WellsManager.cpp
|
||||
opm/core/wells/well_controls.cpp
|
||||
opm/core/wells/wells.c
|
||||
opm/simulators/flow/MissingFeatures.cpp
|
||||
opm/simulators/linalg/ExtractParallelGridInformationToISTL.cpp
|
||||
opm/simulators/linalg/setupPropertyTree.cpp
|
||||
@ -66,11 +59,6 @@ list (APPEND TEST_SOURCE_FILES
|
||||
tests/test_deferredlogger.cpp
|
||||
tests/test_timer.cpp
|
||||
tests/test_invert.cpp
|
||||
tests/test_wells.cpp
|
||||
tests/test_wellsmanager.cpp
|
||||
tests/test_wellcontrols.cpp
|
||||
tests/test_wellsgroup.cpp
|
||||
tests/test_wellcollection.cpp
|
||||
tests/test_stoppedwells.cpp
|
||||
tests/test_relpermdiagnostics.cpp
|
||||
tests/test_norne_pvt.cpp
|
||||
@ -135,14 +123,6 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/props/satfunc/RelpermDiagnostics_impl.hpp
|
||||
opm/simulators/timestepping/SimulatorReport.hpp
|
||||
opm/simulators/wells/WellState.hpp
|
||||
opm/core/well_controls.h
|
||||
opm/core/wells.h
|
||||
opm/core/wells/InjectionSpecification.hpp
|
||||
opm/core/wells/ProductionSpecification.hpp
|
||||
opm/core/wells/WellCollection.hpp
|
||||
opm/core/wells/WellsGroup.hpp
|
||||
opm/core/wells/WellsManager.hpp
|
||||
opm/core/wells/WellsManager_impl.hpp
|
||||
opm/simulators/aquifers/AquiferInterface.hpp
|
||||
opm/simulators/aquifers/AquiferCarterTracy.hpp
|
||||
opm/simulators/aquifers/AquiferFetkovich.hpp
|
||||
|
@ -1,138 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELL_CONTROLS_H_INCLUDED
|
||||
#define OPM_WELL_CONTROLS_H_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for managing sets of well controls.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum WellControlType {
|
||||
BHP, /**< Well constrained by BHP target */
|
||||
THP, /**< Well constrained by THP target */
|
||||
RESERVOIR_RATE, /**< Well constrained by reservoir volume flow rate */
|
||||
SURFACE_RATE /**< Well constrained by surface volume flow rate */
|
||||
};
|
||||
|
||||
struct WellControls;
|
||||
|
||||
bool
|
||||
well_controls_equal(const struct WellControls *ctrls1, const struct WellControls *ctrls2 , bool verbose);
|
||||
|
||||
struct WellControls *
|
||||
well_controls_create(void);
|
||||
|
||||
/**
|
||||
* Create deep copy (clone) of an existing set of well controls.
|
||||
*
|
||||
* @param[in] ctrl Existing set of well controls.
|
||||
*
|
||||
* @return Deep copy of @c ctrl.
|
||||
*/
|
||||
struct WellControls *
|
||||
well_controls_clone(const struct WellControls *ctrl);
|
||||
|
||||
void
|
||||
well_controls_destroy(struct WellControls *ctrl);
|
||||
|
||||
|
||||
int
|
||||
well_controls_get_num(const struct WellControls *ctrl);
|
||||
|
||||
int
|
||||
well_controls_get_current( const struct WellControls * ctrl);
|
||||
|
||||
void
|
||||
well_controls_set_current( struct WellControls * ctrl, int current);
|
||||
|
||||
|
||||
bool
|
||||
well_controls_well_is_stopped(const struct WellControls * ctrl);
|
||||
|
||||
bool
|
||||
well_controls_well_is_open(const struct WellControls * ctrl);
|
||||
|
||||
void
|
||||
well_controls_open_well( struct WellControls * ctrl);
|
||||
|
||||
void
|
||||
well_controls_stop_well( struct WellControls * ctrl);
|
||||
|
||||
int
|
||||
well_controls_add_new(enum WellControlType type , double target , double alq , int vfp , const double * distr , struct WellControls * ctrl);
|
||||
|
||||
enum WellControlType
|
||||
well_controls_iget_type(const struct WellControls * ctrl, int control_index);
|
||||
|
||||
enum WellControlType
|
||||
well_controls_get_current_type(const struct WellControls * ctrl);
|
||||
|
||||
void
|
||||
well_controls_iset_type( struct WellControls * ctrls , int control_index , enum WellControlType type);
|
||||
|
||||
void
|
||||
well_controls_iset_target(struct WellControls * ctrl, int control_index , double target);
|
||||
|
||||
double
|
||||
well_controls_iget_target(const struct WellControls * ctrl, int control_index);
|
||||
|
||||
void
|
||||
well_controls_iset_alq(struct WellControls * ctrl, int control_index , double alq);
|
||||
|
||||
double
|
||||
well_controls_iget_alq(const struct WellControls * ctrl, int control_index );
|
||||
|
||||
void
|
||||
well_controls_iset_vfp(struct WellControls * ctrl, int control_index , int vfp);
|
||||
|
||||
int
|
||||
well_controls_iget_vfp(const struct WellControls * ctrl, int control_index );
|
||||
|
||||
double
|
||||
well_controls_get_current_target(const struct WellControls * ctrl);
|
||||
|
||||
const double *
|
||||
well_controls_iget_distr(const struct WellControls * ctrl, int control_index);
|
||||
|
||||
void
|
||||
well_controls_iset_distr(const struct WellControls * ctrl, int control_index, const double * distr);
|
||||
|
||||
const double *
|
||||
well_controls_get_current_distr(const struct WellControls * ctrl);
|
||||
|
||||
void
|
||||
well_controls_assert_number_of_phases(struct WellControls * ctrl , int number_of_phases);
|
||||
|
||||
void
|
||||
well_controls_clear(struct WellControls * ctrl);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_WELL_CONTROLS_H_INCLUDED */
|
352
opm/core/wells.h
352
opm/core/wells.h
@ -1,352 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELLS_H_INCLUDED
|
||||
#define OPM_WELLS_H_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Main OPM-Core well data structure along with functions
|
||||
* to create, populate and destroy it.
|
||||
*/
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Well type indicates desired/expected well behaviour.
|
||||
*/
|
||||
enum WellType {
|
||||
INJECTOR, /**< Well is an injector */
|
||||
PRODUCER /**< Well is a producer */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Data structure aggregating static information about all wells in a scenario.
|
||||
*/
|
||||
struct Wells
|
||||
{
|
||||
int number_of_wells; /**< Number of wells. */
|
||||
int number_of_phases; /**< Number of phases. */
|
||||
|
||||
/**
|
||||
* Array of well types.
|
||||
*/
|
||||
enum WellType *type;
|
||||
|
||||
/**
|
||||
* Array of well reference depths.
|
||||
*/
|
||||
double *depth_ref;
|
||||
|
||||
/**
|
||||
* Component fractions for each well. Array of size
|
||||
* <CODE>number_of_wells * number_of_phases</CODE>.
|
||||
* For injection wells, this gives the injected component mix.
|
||||
* For production wells the component fractions of the wellbore
|
||||
* will vary and cannot be specified a priori, the component mix
|
||||
* given here should be considered a default or preferred mix.
|
||||
*/
|
||||
double *comp_frac;
|
||||
|
||||
/**
|
||||
* Array of indices into well_cells (and WI). For a well @c w,
|
||||
* <CODE>well_connpos[w]</CODE> and <CODE>well_connpos[w+1]</CODE> are start
|
||||
* and one-beyond-end indices into the @c well_cells array for accessing
|
||||
* @c w's perforation cell indices.
|
||||
*/
|
||||
int *well_connpos;
|
||||
|
||||
/**
|
||||
* Array of perforation cell indices.
|
||||
* Size is number of perforations (== well_connpos[number_of_wells]).
|
||||
*/
|
||||
int *well_cells;
|
||||
|
||||
/**
|
||||
* Well productivity index, same size and structure as well_cells.
|
||||
*/
|
||||
double *WI;
|
||||
|
||||
/**
|
||||
* Saturation table number , same size and structure as well_cells.
|
||||
*/
|
||||
int *sat_table_id;
|
||||
|
||||
/**
|
||||
* Well controls, one set of controls for each well.
|
||||
*/
|
||||
struct WellControls **ctrls;
|
||||
|
||||
/**
|
||||
* Well names. One string for each well.
|
||||
*/
|
||||
char **name;
|
||||
|
||||
/**
|
||||
* Array of flags indicating whether crossflow is allowed or not
|
||||
* if allow_cf[w] == 0 (false) then crossflow is not allowed in well w.
|
||||
*/
|
||||
int *allow_cf;
|
||||
|
||||
/**
|
||||
* Internal management structure.
|
||||
*/
|
||||
void *data;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Data structure aggregating dynamic information about all wells in a scenario.
|
||||
* All arrays in this structure contain data for each perforation, ordered the
|
||||
* same as Wells::well_cells and Wells:WI. The array sizes are, respectively,
|
||||
*
|
||||
* wdp NP
|
||||
* A n²*NP (matrix in column-major (i.e., Fortran) order).
|
||||
* phasemob n*NP
|
||||
*
|
||||
* in which "n" denotes the number of active fluid phases (and constituent
|
||||
* components) and "NP" is the total number of perforations,
|
||||
* <CODE>well_connpos[ number_of_wells ]</CODE>.
|
||||
*/
|
||||
struct CompletionData
|
||||
{
|
||||
/**
|
||||
* Gravity potentials.
|
||||
*/
|
||||
double *wdp;
|
||||
|
||||
/**
|
||||
* Volumes to surface-components matrix, A = RB^{-1}.
|
||||
*/
|
||||
double *A;
|
||||
|
||||
/**
|
||||
* Phase mobilities for all perforations, stored consecutively with the
|
||||
* phase index cycling the most rapidly.
|
||||
*/
|
||||
double *phasemob;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a Wells object initially capable of managing a given
|
||||
* number of wells and total number of well connections
|
||||
* (perforations).
|
||||
*
|
||||
* Function add_well() is used to populate the Wells object. No
|
||||
* reallocation occurs in function add_well() as long as the
|
||||
* initially indicated capacities are sufficient. Call function
|
||||
* destroy_wells() to dispose of the Wells object and its allocated
|
||||
* memory resources.
|
||||
*
|
||||
* \param[in] nphases Number of active phases in simulation scenario.
|
||||
*
|
||||
* \param[in] nwells Expected number of wells in simulation scenario.
|
||||
* Pass zero if the total number of wells is unknown.
|
||||
*
|
||||
* \param[in] nperf Expected total number of well connections
|
||||
* (perforations) for all wells in simulation
|
||||
* scenario. Pass zero if the total number of well
|
||||
* connections is unknown.
|
||||
*
|
||||
* \return A valid Wells object with no wells if successful, and NULL
|
||||
* otherwise.
|
||||
*/
|
||||
struct Wells *
|
||||
create_wells(int nphases, int nwells, int nperf);
|
||||
|
||||
|
||||
/**
|
||||
* Append a new well to an existing Wells object.
|
||||
*
|
||||
* Increments W->number_of_wells by one if successful. The new well
|
||||
* does not include operational constraints. Such information is
|
||||
* specified using function append_well_controls(). The current
|
||||
* control index is set to -1 (invalid).
|
||||
*
|
||||
* \param[in] type Type of well.
|
||||
* \param[in] depth_ref Reference depth for well's BHP.
|
||||
* \param[in] nperf Number of perforations.
|
||||
* \param[in] comp_frac Injection fraction array (size equal to W->number_of_phases) or NULL.
|
||||
* \param[in] cells Grid cells in which well is perforated. Should
|
||||
* ideally be track ordered.
|
||||
* \param[in] WI Well production index per perforation, or NULL.
|
||||
* \param[in] name Name of new well. NULL if no name.
|
||||
* \param[in] allow_cf Flag to determine whether crossflow is allowed or not.
|
||||
* \param[in,out] W Existing set of wells to which new well will
|
||||
* be added.
|
||||
*
|
||||
* \return Non-zero (true) if successful and zero otherwise.
|
||||
*/
|
||||
int
|
||||
add_well(enum WellType type ,
|
||||
double depth_ref,
|
||||
int nperf ,
|
||||
const double *comp_frac,
|
||||
const int *cells ,
|
||||
const double *WI ,
|
||||
const int *sat_table_id,
|
||||
const char *name ,
|
||||
int allow_cf ,
|
||||
struct Wells *W );
|
||||
|
||||
|
||||
/**
|
||||
* Append operational constraint to an existing well.
|
||||
*
|
||||
* Increments ctrl->num by one if successful. Introducing a new
|
||||
* operational constraint does not affect the well's notion of the
|
||||
* currently active constraint represented by ctrl->current.
|
||||
* Note that *_RATE controls now require a phase distribution array
|
||||
* to be associated with the control, see WellControls.
|
||||
*
|
||||
* \param[in] type Control type.
|
||||
* \param[in] target Target value for the control.
|
||||
* \param[in] alq Artificial lift quantity for control (for THP type only)
|
||||
* \param[in] vfp VFP table number for control (for THP type only)
|
||||
* \param[in] distr Array of size W->number_of_phases or NULL.
|
||||
* \param[in] well_index Index of well to receive additional control.
|
||||
* \param[in,out] W Existing set of well controls.
|
||||
* \return Non-zero (true) if successful and zero (false) otherwise.
|
||||
*/
|
||||
|
||||
|
||||
int
|
||||
append_well_controls(enum WellControlType type ,
|
||||
double target,
|
||||
double alq,
|
||||
int vfp,
|
||||
const double *distr,
|
||||
int well_index,
|
||||
struct Wells *W);
|
||||
|
||||
|
||||
/**
|
||||
* Set the current/active control for a single well.
|
||||
*
|
||||
* The new control ID must refer to a previously defined control mode.
|
||||
* Total number of defined control modes available through function
|
||||
* well_controls_get_num().
|
||||
*
|
||||
* \param[in] well_index
|
||||
* Identity of particular well. Must be in
|
||||
* \code [0 .. number_of_wells - 1] \endcode.
|
||||
*
|
||||
* \param[in] current_control
|
||||
* Index of new control mode.
|
||||
*
|
||||
* \param[in,out] W Existing set of wells.
|
||||
*/
|
||||
void
|
||||
set_current_control(int well_index, int current_control, struct Wells *W);
|
||||
|
||||
|
||||
/**
|
||||
* Clear all controls from a single well.
|
||||
*
|
||||
* Does not affect the control set capacity.
|
||||
*
|
||||
* \param[in] well_index
|
||||
* Identity of particular well. Must be in
|
||||
* \code [0 .. number_of_wells - 1] \endcode.
|
||||
*
|
||||
* \param[in,out] W Existing set of wells.
|
||||
*/
|
||||
void
|
||||
clear_well_controls(int well_index, struct Wells *W);
|
||||
|
||||
|
||||
/**
|
||||
* Wells object destructor.
|
||||
*
|
||||
* Disposes of all resources managed by the Wells object.
|
||||
*
|
||||
* The Wells object must be built using function create_wells() and
|
||||
* subsequently populated using function add_well().
|
||||
*/
|
||||
void
|
||||
destroy_wells(struct Wells *W);
|
||||
|
||||
|
||||
/**
|
||||
* Create a deep-copy (i.e., clone) of an existing Wells object, including its
|
||||
* controls.
|
||||
*
|
||||
* @param[in] W Existing Wells object.
|
||||
* @return Complete clone of the input object. Dispose of resources using
|
||||
* function destroy_wells() when no longer needed. Returns @c NULL in case of
|
||||
* allocation failure.
|
||||
*/
|
||||
struct Wells *
|
||||
clone_wells(const struct Wells *W);
|
||||
|
||||
|
||||
/**
|
||||
* Compare well structures for equality.
|
||||
*
|
||||
* Two sets of wells are equal if all of the following conditions hold
|
||||
* - They have the same number of wells
|
||||
*
|
||||
* - They have the same number of completions/connections
|
||||
*
|
||||
* - They specify the same number of phases
|
||||
*
|
||||
* - Individual wells with corresponding well IDs have the same names
|
||||
* (including both being \c NULL).
|
||||
*
|
||||
* - Individual wells with corresponding well IDs have the same
|
||||
* completions
|
||||
*
|
||||
* - Individual wells with corresponding well IDs have the same well
|
||||
* types
|
||||
*
|
||||
* - Individual wells with corresponding well IDs specify the same
|
||||
* reference depths
|
||||
*
|
||||
* - Individual wells with corresponding well IDs have the same set
|
||||
* of defined and active operational constraints as determined by
|
||||
* function well_controls_equal()
|
||||
*
|
||||
* \param[in] W1 Existing set of wells.
|
||||
* \param[in] W2 Existing set of wells.
|
||||
*
|
||||
* \param[in] verbose Flag for whether or not to report which
|
||||
* conditions do not hold. Use \code verbose =
|
||||
* true \endcode to print transcript to \c stdout.
|
||||
*
|
||||
* \return Whether or not well structures \c W1 and \c W2 represent
|
||||
* the same set of wells.
|
||||
*/
|
||||
bool
|
||||
wells_equal(const struct Wells *W1, const struct Wells *W2 , bool verbose);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* OPM_WELLS_H_INCLUDED */
|
@ -1,82 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 Sintef.
|
||||
Copyright 2016 Statoil.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/wells/InjectionSpecification.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
InjectionSpecification::InjectionSpecification()
|
||||
: injector_type_(WATER),
|
||||
control_mode_(NONE),
|
||||
surface_flow_max_rate_(-1e100),
|
||||
reservoir_flow_max_rate_(-1e100),
|
||||
BHP_limit_(-1e100),
|
||||
reinjection_fraction_target_(1),
|
||||
voidage_replacment_fraction_(1),
|
||||
guide_rate_(-1.0),
|
||||
guide_rate_type_(NONE_GRT)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string
|
||||
InjectionSpecification::toString(const ControlMode& mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case ControlMode::NONE: return "NONE";
|
||||
case ControlMode::RATE: return "RATE";
|
||||
case ControlMode::RESV: return "RESV";
|
||||
case ControlMode::BHP : return "BHP" ;
|
||||
case ControlMode::THP : return "THP" ;
|
||||
case ControlMode::REIN: return "REIN";
|
||||
case ControlMode::VREP: return "VREP";
|
||||
case ControlMode::GRUP: return "GRUP";
|
||||
case ControlMode::FLD : return "FLD" ;
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown control mode " << mode << " encountered in injection specification");
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
InjectionSpecification::toString(const InjectorType& type)
|
||||
{
|
||||
switch(type) {
|
||||
case InjectorType::WATER: return "WATER";
|
||||
case InjectorType::OIL : return "OIL" ;
|
||||
case InjectorType::GAS : return "GAS" ;
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown injector type " << type << " encountered in injection specification");
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
InjectionSpecification::toString(const GuideRateType& type)
|
||||
{
|
||||
switch(type) {
|
||||
case GuideRateType::RAT : return "RAT" ;
|
||||
case GuideRateType::NONE_GRT: return "NONE_GRT";
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown guide rate type " << type << " encountered in injection specification");
|
||||
}
|
||||
} // namespace Opm
|
@ -1,46 +0,0 @@
|
||||
#ifndef OPM_INJECTORSPECIFICATION_HPP
|
||||
#define OPM_INJECTORSPECIFICATION_HPP
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <string>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
struct InjectionSpecification
|
||||
{
|
||||
|
||||
enum ControlMode
|
||||
{
|
||||
NONE, RATE, RESV, BHP, THP, REIN, VREP, GRUP, FLD
|
||||
};
|
||||
|
||||
enum InjectorType
|
||||
{
|
||||
WATER, OIL, GAS
|
||||
};
|
||||
|
||||
enum GuideRateType
|
||||
{
|
||||
RAT, NONE_GRT
|
||||
};
|
||||
|
||||
InjectionSpecification();
|
||||
static std::string toString(const ControlMode& mode);
|
||||
static std::string toString(const InjectorType& type);
|
||||
static std::string toString(const GuideRateType& type);
|
||||
InjectorType injector_type_;
|
||||
ControlMode control_mode_;
|
||||
double surface_flow_max_rate_;
|
||||
double reservoir_flow_max_rate_;
|
||||
double BHP_limit_;
|
||||
double reinjection_fraction_target_;
|
||||
double voidage_replacment_fraction_;
|
||||
double guide_rate_;
|
||||
GuideRateType guide_rate_type_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* OPM_INJECTORSPECIFICATION_HPP */
|
||||
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 Sintef.
|
||||
Copyright 2016 Statoil.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <stdexcept>
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/wells/ProductionSpecification.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
ProductionSpecification::ProductionSpecification()
|
||||
:
|
||||
control_mode_(NONE),
|
||||
procedure_(NONE_P),
|
||||
oil_max_rate_(-1e100),
|
||||
water_max_rate_(-1e100),
|
||||
gas_max_rate_(-1e100),
|
||||
liquid_max_rate_(-1e100),
|
||||
reservoir_flow_max_rate_(-1e100),
|
||||
BHP_limit_(-1e100),
|
||||
guide_rate_(-1.0),
|
||||
guide_rate_type_(NONE_GRT)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
ProductionSpecification::toString(const ControlMode& mode)
|
||||
{
|
||||
switch(mode) {
|
||||
case ControlMode::NONE: return "NONE";
|
||||
case ControlMode::ORAT: return "ORAT";
|
||||
case ControlMode::WRAT: return "WRAT";
|
||||
case ControlMode::GRAT: return "GRAT";
|
||||
case ControlMode::LRAT: return "LRAT";
|
||||
case ControlMode::CRAT: return "CRAT";
|
||||
case ControlMode::RESV: return "RESV";
|
||||
case ControlMode::PRBL: return "RPBL";
|
||||
case ControlMode::BHP : return "BHP" ;
|
||||
case ControlMode::THP : return "THP" ;
|
||||
case ControlMode::GRUP: return "GRUP";
|
||||
case ControlMode::FLD : return "FLD" ;
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown control mode " << mode << " encountered in production specification");
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
ProductionSpecification::toString(const Procedure& type)
|
||||
{
|
||||
switch(type) {
|
||||
case Procedure::NONE_P: return "NONE_P";
|
||||
case Procedure::RATE : return "RATE" ;
|
||||
case Procedure::WELL : return "WELL" ;
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown procedure " << type << " encountered in production specification");
|
||||
}
|
||||
|
||||
|
||||
std::string
|
||||
ProductionSpecification::toString(const GuideRateType& type)
|
||||
{
|
||||
switch(type) {
|
||||
case GuideRateType::OIL : return "OIL" ;
|
||||
case GuideRateType::GAS : return "GAS" ;
|
||||
case GuideRateType::WATER : return "WATER" ;
|
||||
case GuideRateType::LIQ : return "LIQ" ;
|
||||
case GuideRateType::NONE_GRT: return "NONE_GRT";
|
||||
}
|
||||
OPM_THROW(std::domain_error, "Unknown guide rate type " << type << " encountered in production specification");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#ifndef OPM_PRODUCTIONSPECIFICATION_HPP
|
||||
#define OPM_PRODUCTIONSPECIFICATION_HPP
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <string>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
struct ProductionSpecification
|
||||
{
|
||||
|
||||
enum ControlMode
|
||||
{
|
||||
NONE = 0, ORAT = 1, WRAT=2, GRAT=3, LRAT=4, CRAT=5, RESV=6, PRBL=7, BHP=8, THP=9, GRUP=10, FLD=11
|
||||
};
|
||||
|
||||
enum Procedure
|
||||
{
|
||||
NONE_P, RATE, WELL
|
||||
};
|
||||
|
||||
enum GuideRateType
|
||||
{
|
||||
OIL, GAS, WATER, LIQ, NONE_GRT
|
||||
};
|
||||
|
||||
ProductionSpecification();
|
||||
static std::string toString(const ControlMode& mode);
|
||||
static std::string toString(const Procedure& type);
|
||||
static std::string toString(const GuideRateType& type);
|
||||
|
||||
ControlMode control_mode_;
|
||||
Procedure procedure_;
|
||||
|
||||
double oil_max_rate_;
|
||||
double water_max_rate_;
|
||||
double gas_max_rate_;
|
||||
double liquid_max_rate_;
|
||||
double reservoir_flow_max_rate_;
|
||||
double BHP_limit_;
|
||||
double guide_rate_;
|
||||
GuideRateType guide_rate_type_;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* OPM_PRODUCTIONSPECIFICATION_HPP */
|
@ -1,502 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
void WellCollection::addField(const Group& fieldGroup, const SummaryState& summaryState, const PhaseUsage& phaseUsage) {
|
||||
WellsGroupInterface* fieldNode = findNode(fieldGroup.name());
|
||||
if (fieldNode) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add FIELD node, but this already exists. Can only have one FIELD node.");
|
||||
}
|
||||
|
||||
roots_.push_back(createGroupWellsGroup(fieldGroup, summaryState, phaseUsage));
|
||||
}
|
||||
|
||||
void WellCollection::addGroup(const Group& groupChild, std::string parent_name,
|
||||
const SummaryState& summaryState, const PhaseUsage& phaseUsage) {
|
||||
WellsGroupInterface* parent = findNode(parent_name);
|
||||
if (!parent) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add child group to group named " << parent_name << ", but this does not exist in the WellCollection.");
|
||||
}
|
||||
|
||||
if (findNode(groupChild.name())) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add child group named " << groupChild.name() << ", but this group is already in the WellCollection.");
|
||||
|
||||
}
|
||||
|
||||
if (groupChild.isProductionGroup() || groupChild.isInjectionGroup()) {
|
||||
group_control_active_ = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<WellsGroupInterface> child = createGroupWellsGroup(groupChild, summaryState, phaseUsage);
|
||||
if (child->injSpec().control_mode_ == InjectionSpecification::VREP) {
|
||||
having_vrep_groups_ = true;
|
||||
}
|
||||
|
||||
WellsGroup* parent_as_group = static_cast<WellsGroup*> (parent);
|
||||
if (!parent_as_group) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add child group to group named " << parent->name() << ", but it's not a group.");
|
||||
}
|
||||
parent_as_group->addChild(child);
|
||||
child->setParent(parent);
|
||||
}
|
||||
|
||||
void WellCollection::addWell(const Well& wellChild, const SummaryState& summaryState, const PhaseUsage& phaseUsage) {
|
||||
if (wellChild.getStatus() == Well::Status::SHUT) {
|
||||
//SHUT wells are not added to the well collection
|
||||
return;
|
||||
}
|
||||
|
||||
WellsGroupInterface* parent = findNode(wellChild.groupName());
|
||||
if (!parent) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add well " << wellChild.name() << " to group named " << wellChild.groupName() << ", but this group does not exist in the WellCollection.");
|
||||
}
|
||||
|
||||
std::shared_ptr<WellsGroupInterface> child = createWellWellsGroup(wellChild, summaryState, phaseUsage);
|
||||
|
||||
WellsGroup* parent_as_group = static_cast<WellsGroup*> (parent);
|
||||
if (!parent_as_group) {
|
||||
OPM_THROW(std::runtime_error, "Trying to add well to group named " << wellChild.groupName() << ", but it's not a group.");
|
||||
}
|
||||
parent_as_group->addChild(child);
|
||||
|
||||
leaf_nodes_.push_back(static_cast<WellNode*>(child.get()));
|
||||
|
||||
child->setParent(parent);
|
||||
}
|
||||
|
||||
const std::vector<WellNode*>& WellCollection::getLeafNodes() const {
|
||||
return leaf_nodes_;
|
||||
}
|
||||
|
||||
WellsGroupInterface* WellCollection::findNode(const std::string& name)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < roots_.size(); i++) {
|
||||
WellsGroupInterface* result = roots_[i]->findGroup(name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const WellsGroupInterface* WellCollection::findNode(const std::string& name) const
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < roots_.size(); i++) {
|
||||
WellsGroupInterface* result = roots_[i]->findGroup(name);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
WellNode& WellCollection::findWellNode(const std::string& name) const
|
||||
{
|
||||
auto well_node = std::find_if(leaf_nodes_.begin(), leaf_nodes_.end(),
|
||||
[&] ( WellNode* w) {
|
||||
return w->name() == name;
|
||||
});
|
||||
|
||||
// Does not find the well
|
||||
if (well_node == leaf_nodes_.end()) {
|
||||
OPM_THROW(std::runtime_error, "Could not find well " << name << " in the well collection!\n");
|
||||
}
|
||||
|
||||
return *(*well_node);
|
||||
}
|
||||
|
||||
/// Adds the child to the collection
|
||||
/// and appends it to parent's children.
|
||||
/// \param[in] child the child node
|
||||
/// \param[in] parent name of parent node
|
||||
|
||||
void WellCollection::addChild(std::shared_ptr<WellsGroupInterface>& child_node,
|
||||
const std::string& parent_name)
|
||||
{
|
||||
WellsGroupInterface* parent = findNode(parent_name);
|
||||
if (parent == NULL) {
|
||||
OPM_THROW(std::runtime_error, "Parent with name = " << parent_name << " not found.");
|
||||
}
|
||||
assert(!parent->isLeafNode());
|
||||
static_cast<WellsGroup*>(parent)->addChild(child_node);
|
||||
if (child_node->isLeafNode()) {
|
||||
leaf_nodes_.push_back(static_cast<WellNode*>(child_node.get()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Adds the node to the collection (as a root node)
|
||||
|
||||
void WellCollection::addChild(std::shared_ptr<WellsGroupInterface>& child_node)
|
||||
{
|
||||
roots_.push_back(child_node);
|
||||
if (child_node->isLeafNode()) {
|
||||
leaf_nodes_.push_back(static_cast<WellNode*> (child_node.get()));
|
||||
}
|
||||
}
|
||||
|
||||
bool WellCollection::conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase)
|
||||
{
|
||||
for (size_t i = 0; i < roots_.size(); i++) {
|
||||
WellPhasesSummed phases;
|
||||
if (!roots_[i]->conditionsMet(well_bhp,
|
||||
well_reservoirrates_phase,
|
||||
well_surfacerates_phase,
|
||||
phases)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WellCollection::setWellsPointer(Wells* wells) {
|
||||
for(size_t i = 0; i < leaf_nodes_.size(); i++) {
|
||||
leaf_nodes_[i]->setWellsPointer(wells, i);
|
||||
}
|
||||
}
|
||||
|
||||
void WellCollection::applyGroupControls()
|
||||
{
|
||||
for (size_t i = 0; i < roots_.size(); ++i) {
|
||||
roots_[i]->applyProdGroupControls();
|
||||
roots_[i]->applyInjGroupControls();
|
||||
}
|
||||
|
||||
group_control_applied_ = true;
|
||||
}
|
||||
|
||||
/// 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 WellCollection::applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase)
|
||||
{
|
||||
for (size_t i = 0; i < roots_.size(); ++i) {
|
||||
roots_[i]->applyExplicitReinjectionControls(well_reservoirrates_phase, well_surfacerates_phase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WellCollection::applyVREPGroupControls(const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs)
|
||||
{
|
||||
for (size_t i = 0; i < roots_.size(); ++i) {
|
||||
roots_[i]->applyVREPGroupControls(well_voidage_rates, conversion_coeffs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: later, it should be extended to update group targets
|
||||
bool WellCollection::needUpdateWellTargets() const
|
||||
{
|
||||
return needUpdateInjectionTargets() || needUpdateProductionTargets();
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::needUpdateInjectionTargets() const
|
||||
{
|
||||
// TODO: it should based on individual group
|
||||
// With current approach, it will potentially result in more update,
|
||||
// thus more iterations, while it will not cause result wrong.
|
||||
// If the group control and individual control is mixed, then it need to
|
||||
// update the well targets
|
||||
bool any_group_control_node = false;
|
||||
bool any_individual_control_node = false;
|
||||
|
||||
for (size_t i = 0; i < leaf_nodes_.size(); ++i) {
|
||||
if (leaf_nodes_[i]->isInjector()) {
|
||||
if (leaf_nodes_[i]->individualControl()) {
|
||||
any_individual_control_node = true;
|
||||
} else {
|
||||
any_group_control_node = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (any_group_control_node && any_individual_control_node);
|
||||
}
|
||||
|
||||
|
||||
// These two functions should be made one
|
||||
bool WellCollection::needUpdateProductionTargets() const
|
||||
{
|
||||
// TODO: it should based on individual group
|
||||
// With current approach, it will potentially result in more update,
|
||||
// thus more iterations, while it will not cause result wrong.
|
||||
// If the group control and individual control is mixed, then it need to
|
||||
// update the well targets
|
||||
bool any_group_control_node = false;
|
||||
bool any_individual_control_node = false;
|
||||
|
||||
for (size_t i = 0; i < leaf_nodes_.size(); ++i) {
|
||||
if (leaf_nodes_[i]->isProducer()) {
|
||||
if (leaf_nodes_[i]->individualControl()) {
|
||||
any_individual_control_node = true;
|
||||
} else {
|
||||
any_group_control_node = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (any_group_control_node && any_individual_control_node);
|
||||
}
|
||||
|
||||
|
||||
void WellCollection::updateWellTargets(const std::vector<double>& well_rates)
|
||||
{
|
||||
|
||||
// TODO: if it gets converged, should we still update targets?
|
||||
|
||||
// set the target_updated to be false
|
||||
for (WellNode* well_node : leaf_nodes_) {
|
||||
well_node->setTargetUpdated(false);
|
||||
}
|
||||
|
||||
// TODO: currently, we only handle the level of the well groups for the moment, i.e. the level just above wells
|
||||
// We believe the relations between groups are similar to the relations between different wells inside the same group.
|
||||
// While there will be somre more complication invloved for sure.
|
||||
for (size_t i = 0; i < leaf_nodes_.size(); ++i) {
|
||||
// find a node needs to update targets, then update targets for all the wellls inside the group.
|
||||
if (!leaf_nodes_[i]->targetUpdated()) {
|
||||
WellsGroupInterface* parent_node = leaf_nodes_[i]->getParent();
|
||||
// update the target within this group.
|
||||
if (leaf_nodes_[i]->isProducer()) {
|
||||
if (parent_node->prodSpec().control_mode_ == ProductionSpecification::NONE) {
|
||||
continue;
|
||||
}
|
||||
parent_node->updateWellProductionTargets(well_rates);
|
||||
}
|
||||
|
||||
if (leaf_nodes_[i]->isInjector()) {
|
||||
if (parent_node->injSpec().control_mode_ == InjectionSpecification::NONE) {
|
||||
continue;
|
||||
}
|
||||
parent_node->updateWellInjectionTargets(well_rates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::havingVREPGroups() const
|
||||
{
|
||||
return having_vrep_groups_;
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::groupControlActive() const
|
||||
{
|
||||
return group_control_active_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool WellCollection::groupControlApplied() const
|
||||
{
|
||||
return group_control_applied_;
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::groupTargetConverged(const std::vector<double>& well_rates) const
|
||||
{
|
||||
// TODO: eventually, there should be only one root node
|
||||
// TODO: we also need to check the injection target, while we have not done that.
|
||||
for (const std::shared_ptr<WellsGroupInterface>& root_node : roots_) {
|
||||
if ( !root_node->groupProdTargetConverged(well_rates) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WellCollection::
|
||||
setGuideRatesWithPotentials(const Wells* wells,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& well_potentials) const
|
||||
{
|
||||
// TODO: assuming the order of well_potentials is the same with the order in wells struct
|
||||
// TODO: it will overwrite the well potentials from other means. It should be changed after
|
||||
// fixing the other part of the code. It makes the current flow only support guide rates based on
|
||||
// well potentials.
|
||||
const int np = wells->number_of_phases;
|
||||
const int nw = wells->number_of_wells;
|
||||
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const std::string well_name = wells->name[w];
|
||||
|
||||
WellNode& well_node = findWellNode(well_name);
|
||||
|
||||
const WellType well_type = wells->type[w];
|
||||
// TODO: eventually the following standard will be wrong, it will belong to FIELD group
|
||||
if (well_node.getParent() != nullptr) { // If it does not belong a group, will it belong to FIELD?
|
||||
const WellsGroupInterface* group = well_node.getParent();
|
||||
if (well_type == PRODUCER) {
|
||||
// The guide rates is calculated based on the group control
|
||||
// Currently only supporting WRAT, ORAT and GRAT.
|
||||
ProductionSpecification::ControlMode control_mode = group->prodSpec().control_mode_;
|
||||
if (control_mode == ProductionSpecification::FLD) {
|
||||
if (group->getParent() != nullptr) {
|
||||
// TODO: only handle one level FLD control
|
||||
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) {
|
||||
case ProductionSpecification::WRAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water rate controlled well.");
|
||||
}
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + water_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::WATER;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::ORAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil rate controlled well.");
|
||||
}
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + oil_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::GRAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas rate controlled well.");
|
||||
}
|
||||
const int gas_index = phase_usage.phase_pos[BlackoilPhases::Vapour];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + gas_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::GAS;
|
||||
break;
|
||||
}
|
||||
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 (phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
const double potential_oil = well_potentials[np * w + oil_index];
|
||||
guide_rate += potential_oil;
|
||||
}
|
||||
if (phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
const double potential_water = well_potentials[np * w + water_index];
|
||||
guide_rate += potential_water;
|
||||
}
|
||||
// not sure if no water and no oil, what will happen here, zero guide_rate?
|
||||
well_node.prodSpec().guide_rate_ = guide_rate;
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::LIQ;
|
||||
break;
|
||||
}
|
||||
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: " << ProductionSpecification::toString(group->prodSpec().control_mode_) );
|
||||
}
|
||||
|
||||
} else if (well_type == INJECTOR) {
|
||||
// The guide rates is calculated based on the group injector type
|
||||
switch (group->injSpec().injector_type_) {
|
||||
case InjectionSpecification::WATER: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water injecting well.");
|
||||
}
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + water_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e water
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::OIL: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil injecting well.");
|
||||
}
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + oil_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e. oil
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::GAS: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas injecting well.");
|
||||
}
|
||||
const int gas_index = phase_usage.phase_pos[BlackoilPhases::Vapour];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + gas_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e gas
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Not supported injector type for guide rate computed" <<
|
||||
" from well potentials: " << InjectionSpecification::toString(group->injSpec().injector_type_) );
|
||||
}
|
||||
} else { // neither injector nor producer
|
||||
OPM_THROW(std::logic_error, "Expected well type to be either INJECTOR or PRODUCER for well " << well_node.name() );
|
||||
|
||||
}
|
||||
} // end of if (well_node.getParent() != nullptr)
|
||||
} // end of for (int w = 0; w < nw; ++w)
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::requireWellPotentials() const
|
||||
{
|
||||
for (const auto& well_node : leaf_nodes_) {
|
||||
if (well_node->isGuideRateWellPotential()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
Copyright 2011 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
#ifndef OPM_WELLCOLLECTION_HPP
|
||||
#define OPM_WELLCOLLECTION_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <opm/core/wells/WellsGroup.hpp>
|
||||
#include <opm/grid/UnstructuredGrid.h>
|
||||
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
class SummaryState;
|
||||
class WellCollection
|
||||
{
|
||||
public:
|
||||
|
||||
void addField(const Group& fieldGroup, const SummaryState& summaryState, const PhaseUsage& phaseUsage);
|
||||
|
||||
void addWell(const Well& wellChild, const SummaryState& summaryState, const PhaseUsage& phaseUsage);
|
||||
|
||||
void addGroup(const Group& groupChild, std::string parent_name,
|
||||
const SummaryState& summaryState, const PhaseUsage& phaseUsage);
|
||||
|
||||
/// Adds the child to the collection
|
||||
/// and appends it to parent's children.
|
||||
/// \param[in] child the child node
|
||||
/// \param[in] parent name of parent node
|
||||
void addChild(std::shared_ptr<WellsGroupInterface>& child_node,
|
||||
const std::string& parent);
|
||||
|
||||
/// Adds the node to the collection (as a root node)
|
||||
void addChild(std::shared_ptr<WellsGroupInterface>& child_node);
|
||||
|
||||
/// Checks if each condition is met, applies well controls where needed
|
||||
/// (that is, it either changes the active control of violating wells, or shuts
|
||||
/// down wells). Only one change is applied per invocation. Typical use will be
|
||||
/// \code
|
||||
/// solve_pressure();
|
||||
/// while(!collection.conditionsMet(well_bhp, well_rate, summed_phases)) {
|
||||
/// solve_pressure();
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// \note It's highly recommended to use the conditionsMet found in WellsManager.
|
||||
/// \param[in] well_bhp A vector containing the bhp for each well. Is assumed
|
||||
/// to be ordered the same way as the related Wells-struct.
|
||||
/// \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.
|
||||
/// \return true if no violations were found, false otherwise (false also implies a change).
|
||||
bool conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
/// Adds the well pointer to each leaf node (does not take ownership).
|
||||
void setWellsPointer(Wells* wells);
|
||||
|
||||
/// \return A set of pointers to every well in the collection
|
||||
const std::vector<WellNode*>& getLeafNodes() const;
|
||||
|
||||
/// Finds the group with the given name.
|
||||
/// \param[in] the name of the group
|
||||
/// \return the pointer to the group if found, NULL otherwise
|
||||
WellsGroupInterface* findNode(const std::string& name);
|
||||
|
||||
/// Finds the group with the given name.
|
||||
/// \param[in] the name of the group
|
||||
/// \return the pointer to the group if found, NULL otherwise
|
||||
const WellsGroupInterface* findNode(const std::string& name) const;
|
||||
|
||||
|
||||
WellNode& findWellNode(const std::string& name) const;
|
||||
|
||||
|
||||
/// Applies all group controls (injection and production)
|
||||
void applyGroupControls();
|
||||
|
||||
/// 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 applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
|
||||
/// applying VREP group control based on calculated voidage rates
|
||||
void applyVREPGroupControls(const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs);
|
||||
|
||||
/// Checking whether need to update the targets of the wells / or the groups later
|
||||
/// True need to update well targets within this iteration, no switching control within this iteration.
|
||||
/// False no need to update well targets within this iteration, continuing as usual.
|
||||
bool needUpdateWellTargets() const;
|
||||
|
||||
/// Checking whether need to update the targets for the injection wells.
|
||||
bool needUpdateInjectionTargets() const;
|
||||
|
||||
/// Checking whehter need to update the targets for the production wells.
|
||||
bool needUpdateProductionTargets() const;
|
||||
|
||||
/// Updating the well targets based on the well rates.
|
||||
void updateWellTargets(const std::vector<double>& well_rates);
|
||||
|
||||
/// When we have VREP group, we need to update the targets based on the updated production voidage rates for each iteration.
|
||||
bool havingVREPGroups() const;
|
||||
|
||||
/// Whether we have active group control
|
||||
bool groupControlActive() const;
|
||||
|
||||
/// Whether we have applied the group control
|
||||
bool groupControlApplied() const;
|
||||
|
||||
/// Whether the group target is converged
|
||||
// It is considered converged if eitehr the group targets are matched or the group targets are not matched while the wells are
|
||||
// running under their own limits so that they can not produce more
|
||||
// It is considered not converged if the group targets are not matched while some of the wells are still running under group control
|
||||
// The strategy may need to be adjusted when more complicated multi-layered group control situation applied, not sure about thatyet.
|
||||
bool groupTargetConverged(const std::vector<double>& well_rates) const;
|
||||
|
||||
|
||||
/// Setting the guide rates with well potentials
|
||||
void setGuideRatesWithPotentials(const Wells* wells,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& well_potentials) const;
|
||||
|
||||
|
||||
bool requireWellPotentials() const;
|
||||
|
||||
private:
|
||||
// To account for the possibility of a forest
|
||||
std::vector<std::shared_ptr<WellsGroupInterface> > roots_;
|
||||
|
||||
// This will be used to traverse the bottom nodes.
|
||||
std::vector<WellNode*> leaf_nodes_;
|
||||
|
||||
bool having_vrep_groups_ = false;
|
||||
|
||||
bool group_control_active_ = false;
|
||||
|
||||
// This is used to mark whether apply or update the group control
|
||||
bool group_control_applied_ = false;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif /* OPM_WELLCOLLECTION_HPP */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,558 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELLSGROUP_HPP
|
||||
#define OPM_WELLSGROUP_HPP
|
||||
|
||||
#include <opm/core/wells/InjectionSpecification.hpp>
|
||||
#include <opm/core/wells/ProductionSpecification.hpp>
|
||||
#include <opm/grid/UnstructuredGrid.h>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
// Need to forward declare this one, some of the methods in the base
|
||||
// class returns pointers to it.
|
||||
class WellNode;
|
||||
|
||||
/// Basic information needed for group control (each group should typically
|
||||
/// not exceed the sum of its leaf nodes)
|
||||
struct WellPhasesSummed
|
||||
{
|
||||
WellPhasesSummed();
|
||||
double res_inj_rates[3];
|
||||
double res_prod_rates[3];
|
||||
double surf_inj_rates[3];
|
||||
double surf_prod_rates[3];
|
||||
|
||||
/// Sums each component
|
||||
void operator+=(const WellPhasesSummed& other);
|
||||
};
|
||||
|
||||
class WellsGroupInterface
|
||||
{
|
||||
public:
|
||||
WellsGroupInterface(const std::string& name,
|
||||
const double efficiency_factor,
|
||||
const ProductionSpecification& prod_spec,
|
||||
const InjectionSpecification& inj_spec,
|
||||
const PhaseUsage& phase_usage);
|
||||
virtual ~WellsGroupInterface();
|
||||
|
||||
/// The unique identifier for the well or well group.
|
||||
const std::string& name() const;
|
||||
|
||||
/// Production specifications for the well or well group.
|
||||
const ProductionSpecification& prodSpec() const;
|
||||
|
||||
/// Injection specifications for the well or well group.
|
||||
const InjectionSpecification& injSpec() const;
|
||||
|
||||
/// Production specifications for the well or well group.
|
||||
ProductionSpecification& prodSpec();
|
||||
|
||||
/// Injection specifications for the well or well group.
|
||||
InjectionSpecification& injSpec();
|
||||
|
||||
/// Phase usage information.
|
||||
const PhaseUsage& phaseUsage() const;
|
||||
|
||||
/// \returns true if the object is a leaf node (WellNode), false otherwise.
|
||||
virtual bool isLeafNode() const;
|
||||
|
||||
/// \returns the pointer to the WellsGroupInterface with the given name. NULL if
|
||||
/// the name is not found.a
|
||||
virtual WellsGroupInterface* findGroup(const std::string& name_of_node) = 0;
|
||||
|
||||
/// Sets the parent
|
||||
/// \param[in] parent the pointer to the parent
|
||||
void setParent(WellsGroupInterface* parent);
|
||||
|
||||
/// Gets the parent of the group, NULL if no parent.
|
||||
const WellsGroupInterface* getParent() const;
|
||||
|
||||
WellsGroupInterface* getParent();
|
||||
|
||||
/// Calculates the number of leaf nodes in the given group.
|
||||
/// A leaf node is defined to have one leaf node in its group.
|
||||
virtual int numberOfLeafNodes() = 0;
|
||||
|
||||
/// Checks if each condition is met, applies well controls where needed
|
||||
/// (that is, it either changes the active control of violating wells, or shuts
|
||||
/// down wells). Only one change is applied per invocation. Typical use will be
|
||||
/// \code
|
||||
/// solve_pressure();
|
||||
/// while(!group.conditionsMet(...)) {
|
||||
/// solve_pressure();
|
||||
/// }
|
||||
/// \endcode
|
||||
///
|
||||
/// \note It's highly recommended to use the conditionsMet found in WellsManager.
|
||||
/// \param[in] well_bhp A vector containing the bhp for each well. Is assumed
|
||||
/// to be ordered the same way as the related Wells-struct.
|
||||
/// \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.
|
||||
/// \param[out] summed_phases Will at end of invocation contain the summed phase rates
|
||||
/// (rate ,etc.) for the group.
|
||||
/// \return true if no violations were found, false otherwise (false also implies a change).
|
||||
virtual bool conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
WellPhasesSummed& summed_phases) = 0;
|
||||
|
||||
/// Sets the current active control to the provided one for all injectors within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
// otherwise, all children will be set under group control
|
||||
virtual void applyInjGroupControl(const InjectionSpecification::ControlMode control_mode,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const double target,
|
||||
const bool only_group) = 0;
|
||||
/// Sets the current active control to the provided one for all producers within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
// otherwise, all children will be set under group control
|
||||
virtual void applyProdGroupControl(const ProductionSpecification::ControlMode control_mode,
|
||||
const double target,
|
||||
const bool only_group) = 0;
|
||||
|
||||
/// Gets the worst offending well based on the input
|
||||
/// \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.
|
||||
/// \param[in] mode
|
||||
/// The relevant control mode to find the maximum over.
|
||||
/// \return first will be a pointer to the worst offending well, second will be the obtained value at that well.
|
||||
virtual std::pair<WellNode*, double> getWorstOffending(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
ProductionSpecification::ControlMode mode) = 0;
|
||||
|
||||
/// Gets the target rate for the given mode.
|
||||
double getTarget(ProductionSpecification::ControlMode mode) const;
|
||||
|
||||
/// Gets the target rate for the given mode.
|
||||
double getTarget(InjectionSpecification::ControlMode mode) const;
|
||||
|
||||
/// Applies any production group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyProdGroupControls() = 0;
|
||||
|
||||
/// Applies any injection group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyInjGroupControls() = 0;
|
||||
|
||||
/// Calculates the production guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double productionGuideRate(bool only_group) = 0;
|
||||
|
||||
/// Calculates the injection guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double injectionGuideRate(bool only_group) = 0;
|
||||
|
||||
/// Gets the total production flow of the given phase.
|
||||
/// \param[in] phase_flows A vector containing 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] phase The phase for which to sum up.
|
||||
virtual double getTotalProductionFlow(const std::vector<double>& phase_flows,
|
||||
const BlackoilPhases::PhaseIndex phase) const = 0;
|
||||
|
||||
/// 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.
|
||||
virtual void applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase) = 0;
|
||||
|
||||
/// TODO: prototyping a VREP enforcement function.
|
||||
virtual void applyVREPGroupControls(const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs) = 0;
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group) = 0;
|
||||
|
||||
virtual double getTotalVoidageRate(const std::vector<double>& well_voidage_rates) = 0;
|
||||
|
||||
/// Return whether the well is running under group control target
|
||||
/// or under their own limit.
|
||||
/// True under their own limit.
|
||||
/// False running under group control target
|
||||
bool individualControl() const;
|
||||
|
||||
/// Update the status for individual contrl
|
||||
void setIndividualControl(const bool);
|
||||
|
||||
virtual double getProductionRate(const std::vector<double>& well_rates,
|
||||
const ProductionSpecification::ControlMode prod_mode) const = 0;
|
||||
|
||||
virtual void updateWellProductionTargets(const std::vector<double>& well_rates) = 0;
|
||||
|
||||
virtual void updateWellInjectionTargets(const std::vector<double>& well_rates) = 0;
|
||||
|
||||
virtual void setTargetUpdated(const bool flag) = 0;
|
||||
|
||||
// bascially, for the group or wells under group control
|
||||
// they have the potential to adjust their targets to produce more to match the higher level target
|
||||
virtual bool canProduceMore() const = 0;
|
||||
|
||||
// checking wether group production target converged
|
||||
// if the group is producing following the target, then it should be considered okay
|
||||
// if the group is not producing following the target, then we should check wether the group
|
||||
// should be able to produce more to match the target.
|
||||
// if the group can not produce more, we also consider the effort to match the group target is
|
||||
// also done and the group target converged while we should give a message
|
||||
virtual bool groupProdTargetConverged(const std::vector<double>& well_rates) const = 0;
|
||||
|
||||
double efficiencyFactor() const;
|
||||
|
||||
void setEfficiencyFactor(const double efficiency_factor);
|
||||
|
||||
protected:
|
||||
/// Calculates the correct rate for the given ProductionSpecification::ControlMode
|
||||
double rateByMode(const double* res_rates,
|
||||
const double* surf_rates,
|
||||
const ProductionSpecification::ControlMode mode);
|
||||
|
||||
/// Calculates the correct rate for the given InjectionSpecification::ControlMode
|
||||
double rateByMode(const double* res_rates,
|
||||
const double* surf_rates,
|
||||
const InjectionSpecification::ControlMode mode);
|
||||
|
||||
WellsGroupInterface* parent_;
|
||||
|
||||
// Whether well is running under the group control target.
|
||||
// Current only consider one level of control.
|
||||
// So not putting it in the WellsGroupInterface yet.
|
||||
bool individual_control_;
|
||||
|
||||
// Efficiency factor
|
||||
double efficiency_factor_;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
ProductionSpecification production_specification_;
|
||||
InjectionSpecification injection_specification_;
|
||||
PhaseUsage phase_usage_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class WellsGroup : public WellsGroupInterface
|
||||
{
|
||||
public:
|
||||
WellsGroup(const std::string& name,
|
||||
const double efficiency_factor,
|
||||
const ProductionSpecification& prod_spec,
|
||||
const InjectionSpecification& inj_spec,
|
||||
const PhaseUsage& phase_usage);
|
||||
|
||||
virtual WellsGroupInterface* findGroup(const std::string& name_of_node);
|
||||
|
||||
void addChild(std::shared_ptr<WellsGroupInterface> child);
|
||||
|
||||
virtual bool conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
WellPhasesSummed& summed_phases);
|
||||
|
||||
virtual int numberOfLeafNodes();
|
||||
virtual std::pair<WellNode*, double> getWorstOffending(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
ProductionSpecification::ControlMode mode);
|
||||
|
||||
/// Sets the current active control to the provided one for all injectors within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
// otherwise, all children will be set under group control
|
||||
virtual void applyInjGroupControl(const InjectionSpecification::ControlMode control_mode,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const double target,
|
||||
bool only_group);
|
||||
|
||||
/// Sets the current active control to the provided one for all producers within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
// otherwise, all children will be set under group control
|
||||
virtual void applyProdGroupControl(const ProductionSpecification::ControlMode control_mode,
|
||||
const double target,
|
||||
bool only_group);
|
||||
|
||||
/// Applies any production group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyProdGroupControls();
|
||||
|
||||
/// Applies any injection group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyInjGroupControls();
|
||||
|
||||
/// Calculates the production guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double productionGuideRate(bool only_group);
|
||||
|
||||
/// Calculates the injection guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double injectionGuideRate(bool only_group);
|
||||
|
||||
/// Gets the total production flow of the given phase.
|
||||
/// \param[in] phase_flows A vector containing 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] phase The phase for which to sum up.
|
||||
virtual double getTotalProductionFlow(const std::vector<double>& phase_flows,
|
||||
const BlackoilPhases::PhaseIndex phase) const;
|
||||
|
||||
/// 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.
|
||||
virtual void applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
/// TODO: prototyping a VREP enforcement function.
|
||||
virtual void applyVREPGroupControls(const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs);
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group);
|
||||
|
||||
virtual double getTotalVoidageRate(const std::vector<double>& well_voidage_rates);
|
||||
|
||||
virtual void updateWellProductionTargets(const std::vector<double>& well_rates);
|
||||
|
||||
virtual void updateWellInjectionTargets(const std::vector<double>& well_rates);
|
||||
|
||||
virtual void setTargetUpdated(const bool flag);
|
||||
|
||||
virtual double getProductionRate(const std::vector<double>& well_rates,
|
||||
const ProductionSpecification::ControlMode prod_mode) const;
|
||||
|
||||
virtual bool canProduceMore() const;
|
||||
|
||||
virtual bool groupProdTargetConverged(const std::vector<double>& well_rates) const;
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<WellsGroupInterface> > children_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class WellNode : public WellsGroupInterface
|
||||
{
|
||||
public:
|
||||
WellNode(const std::string& name,
|
||||
const double efficiency_factor,
|
||||
const ProductionSpecification& prod_spec,
|
||||
const InjectionSpecification& inj_spec,
|
||||
const PhaseUsage& phase_usage);
|
||||
|
||||
virtual WellsGroupInterface* findGroup(const std::string& name_of_node);
|
||||
virtual bool conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
WellPhasesSummed& summed_phases);
|
||||
|
||||
virtual bool isLeafNode() const;
|
||||
|
||||
void setWellsPointer(Wells* wells, int self_index);
|
||||
|
||||
virtual int numberOfLeafNodes();
|
||||
|
||||
// Shuts the well (in the well struct)
|
||||
void shutWell();
|
||||
|
||||
virtual std::pair<WellNode*, double> getWorstOffending(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase,
|
||||
ProductionSpecification::ControlMode mode);
|
||||
|
||||
/// Sets the current active control to the provided one for all injectors within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
/// otherwise, all children will be set under group control
|
||||
virtual void applyInjGroupControl(const InjectionSpecification::ControlMode control_mode,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const double target,
|
||||
bool only_group);
|
||||
|
||||
/// Sets the current active control to the provided one for all producers within the group.
|
||||
/// After this call, the combined rate (which rate depending on control_mode) of the group
|
||||
/// shall be equal to target.
|
||||
/// \param[in] only_group if true, only children that are under group control will be changed.
|
||||
/// otherwise, all children will be set under group control
|
||||
virtual void applyProdGroupControl(const ProductionSpecification::ControlMode control_mode,
|
||||
const double target,
|
||||
bool only_group);
|
||||
|
||||
/// Applies any production group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyProdGroupControls();
|
||||
|
||||
/// Applies any injection group control relevant to all children nodes.
|
||||
/// If no group control is set, this is called recursively to the children.
|
||||
virtual void applyInjGroupControls();
|
||||
|
||||
/// Calculates the production guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double productionGuideRate(bool only_group);
|
||||
|
||||
/// Calculates the injection guide rate for the group.
|
||||
/// \param[in] only_group If true, will only accumelate guide rates for
|
||||
/// wells under group control
|
||||
virtual double injectionGuideRate(bool only_group);
|
||||
|
||||
/// Gets the total production flow of the given phase.
|
||||
/// \param[in] phase_flows A vector containing 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] phase The phase for which to sum up.
|
||||
virtual double getTotalProductionFlow(const std::vector<double>& phase_flows,
|
||||
const BlackoilPhases::PhaseIndex phase) const;
|
||||
|
||||
/// Returns the type of the well.
|
||||
WellType type() const;
|
||||
|
||||
/// 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.
|
||||
virtual void applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
/// TODO: prototyping a VREP enforcement function.
|
||||
virtual void applyVREPGroupControls(const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs);
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group);
|
||||
|
||||
virtual double getTotalVoidageRate(const std::vector<double>& well_voidage_rates);
|
||||
|
||||
int groupControlIndex() const;
|
||||
|
||||
virtual double getProductionRate(const std::vector<double>& well_rates,
|
||||
const ProductionSpecification::ControlMode prod_mode) const;
|
||||
|
||||
virtual void updateWellProductionTargets(const std::vector<double>& well_rates);
|
||||
|
||||
virtual void updateWellInjectionTargets(const std::vector<double>& well_rates);
|
||||
|
||||
/// the efficiency factor for groups are muliplitive, this function return the resulted final efficiency factor
|
||||
/// to the well in a multi-layer group structure.
|
||||
double getAccumulativeEfficiencyFactor() const;
|
||||
|
||||
bool isProducer() const;
|
||||
|
||||
bool isInjector() const;
|
||||
|
||||
int selfIndex() const;
|
||||
|
||||
bool targetUpdated() const;
|
||||
|
||||
bool isGuideRateWellPotential() const;
|
||||
|
||||
void setIsGuideRateWellPotential(const bool flag);
|
||||
|
||||
virtual void setTargetUpdated(const bool flag);
|
||||
|
||||
virtual bool canProduceMore() const;
|
||||
|
||||
virtual bool groupProdTargetConverged(const std::vector<double>& well_rates) const;
|
||||
|
||||
private:
|
||||
Wells* wells_;
|
||||
int self_index_;
|
||||
int group_control_index_;
|
||||
bool shut_well_;
|
||||
// TODO: used when updating well targets
|
||||
bool target_updated_;
|
||||
// whether the guide rate is specified with well potential
|
||||
// TODO: we have never handle the guide rates for groups, maybe this
|
||||
// is something will go to WellsGroupInterface later
|
||||
bool is_guiderate_wellpotential_;
|
||||
};
|
||||
|
||||
/// Creates the WellsGroupInterface for the given well
|
||||
/// \param[in] well the Well to construct object for
|
||||
/// \param[in] timeStep the time step in question
|
||||
/// \param[in] the phase usage
|
||||
std::shared_ptr<WellsGroupInterface> createWellWellsGroup(const Well& well,
|
||||
const SummaryState& summaryState,
|
||||
const PhaseUsage& phase_usage );
|
||||
|
||||
/// Creates the WellsGroupInterface for the given Group
|
||||
/// \param[in] group the Group to construct object for
|
||||
/// \param[in] timeStep the time step in question
|
||||
/// \param[in] the phase usage
|
||||
std::shared_ptr<WellsGroupInterface> createGroupWellsGroup(const Group& group,
|
||||
const SummaryState& summaryState,
|
||||
const PhaseUsage& phase_usage );
|
||||
}
|
||||
|
||||
#endif /* OPM_WELLSGROUP_HPP */
|
||||
|
@ -1,639 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2016 IRIS AS
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/grid/UnstructuredGrid.h>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
#include <opm/core/wells/WellsGroup.hpp>
|
||||
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
static double invalid_alq = -1e100;
|
||||
static double invalid_vfp = -2147483647;
|
||||
} //Namespace
|
||||
|
||||
// Helper structs and functions for the implementation.
|
||||
namespace WellsManagerDetail
|
||||
{
|
||||
|
||||
|
||||
|
||||
namespace ProductionControl
|
||||
{
|
||||
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
|
||||
|
||||
Mode mode(const std::string& control)
|
||||
{
|
||||
static std::map<std::string, Mode>
|
||||
mode_map = Details::init_mode_map();
|
||||
|
||||
std::map<std::string, Mode>::iterator
|
||||
p = mode_map.find(control);
|
||||
|
||||
if (p != mode_map.end()) {
|
||||
return p->second;
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::runtime_error, "Unknown well control mode = "
|
||||
<< control << " in input file");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Mode mode(Opm::Well::ProducerCMode controlMode)
|
||||
{
|
||||
switch( controlMode ) {
|
||||
case Opm::Well::ProducerCMode::ORAT:
|
||||
return ORAT;
|
||||
case Opm::Well::ProducerCMode::WRAT:
|
||||
return WRAT;
|
||||
case Opm::Well::ProducerCMode::GRAT:
|
||||
return GRAT;
|
||||
case Opm::Well::ProducerCMode::LRAT:
|
||||
return LRAT;
|
||||
case Opm::Well::ProducerCMode::CRAT:
|
||||
return CRAT;
|
||||
case Opm::Well::ProducerCMode::RESV:
|
||||
return RESV;
|
||||
case Opm::Well::ProducerCMode::BHP:
|
||||
return BHP;
|
||||
case Opm::Well::ProducerCMode::THP:
|
||||
return THP;
|
||||
case Opm::Well::ProducerCMode::GRUP:
|
||||
return GRUP;
|
||||
default:
|
||||
throw std::invalid_argument("unhandled enum value");
|
||||
}
|
||||
}
|
||||
} // namespace ProductionControl
|
||||
|
||||
|
||||
namespace InjectionControl
|
||||
{
|
||||
|
||||
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
|
||||
|
||||
Mode mode(const std::string& control)
|
||||
{
|
||||
static std::map<std::string, Mode>
|
||||
mode_map = Details::init_mode_map();
|
||||
|
||||
std::map<std::string, Mode>::iterator
|
||||
p = mode_map.find(control);
|
||||
|
||||
if (p != mode_map.end()) {
|
||||
return p->second;
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::runtime_error, "Unknown well control mode = "
|
||||
<< control << " in input file");
|
||||
}
|
||||
}
|
||||
|
||||
Mode mode(Opm::Well::InjectorCMode controlMode)
|
||||
{
|
||||
switch ( controlMode ) {
|
||||
case Opm::Well::InjectorCMode::GRUP:
|
||||
return GRUP;
|
||||
case Opm::Well::InjectorCMode::RESV:
|
||||
return RESV;
|
||||
case Opm::Well::InjectorCMode::RATE:
|
||||
return RATE;
|
||||
case Opm::Well::InjectorCMode::THP:
|
||||
return THP;
|
||||
case Opm::Well::InjectorCMode::BHP:
|
||||
return BHP;
|
||||
default:
|
||||
throw std::invalid_argument("unhandled enum value");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace InjectionControl
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
/// Default constructor.
|
||||
WellsManager::WellsManager()
|
||||
: w_(create_wells(0,0,0)), is_parallel_run_(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct from existing wells object.
|
||||
WellsManager::WellsManager(struct Wells* W)
|
||||
: w_(clone_wells(W)), is_parallel_run_(false)
|
||||
{
|
||||
}
|
||||
|
||||
/// Construct wells from deck.
|
||||
WellsManager::WellsManager(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
const UnstructuredGrid& grid)
|
||||
: w_(create_wells(0,0,0)), is_parallel_run_(false)
|
||||
{
|
||||
init(eclipseState, schedule, summaryState, timeStep, UgGridHelpers::numCells(grid),
|
||||
UgGridHelpers::globalCell(grid), UgGridHelpers::cartDims(grid),
|
||||
UgGridHelpers::dimensions(grid),
|
||||
UgGridHelpers::cell2Faces(grid), UgGridHelpers::beginFaceCentroids(grid),
|
||||
std::unordered_set<std::string>());
|
||||
|
||||
}
|
||||
|
||||
/// Destructor.
|
||||
WellsManager::~WellsManager()
|
||||
{
|
||||
destroy_wells(w_);
|
||||
}
|
||||
|
||||
|
||||
/// Does the "deck" define any wells?
|
||||
bool WellsManager::empty() const
|
||||
{
|
||||
return (w_ == 0) || (w_->number_of_wells == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// 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
|
||||
{
|
||||
return w_;
|
||||
}
|
||||
|
||||
const WellCollection& WellsManager::wellCollection() const
|
||||
{
|
||||
return well_collection_;
|
||||
}
|
||||
|
||||
WellCollection& WellsManager::wellCollection() {
|
||||
return well_collection_;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool WellsManager::conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase)
|
||||
{
|
||||
return well_collection_.conditionsMet(well_bhp,
|
||||
well_reservoirrates_phase,
|
||||
well_surfacerates_phase);
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
|
||||
void WellsManager::setupCompressedToCartesian(const int* global_cell, int number_of_cells,
|
||||
std::map<int,int>& cartesian_to_compressed ) {
|
||||
// global_cell is a map from compressed cells to Cartesian grid cells.
|
||||
// We must make the inverse lookup.
|
||||
|
||||
if (global_cell) {
|
||||
for (int i = 0; i < number_of_cells; ++i) {
|
||||
cartesian_to_compressed.insert(std::make_pair(global_cell[i], i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < number_of_cells; ++i) {
|
||||
cartesian_to_compressed.insert(std::make_pair(i, i));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WellsManager::setupWellControls(const std::vector<Well>& wells,
|
||||
const SummaryState& summaryState,
|
||||
std::vector<std::string>& well_names,
|
||||
const PhaseUsage& phaseUsage,
|
||||
const std::vector<int>& wells_on_proc) {
|
||||
int well_index = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
const auto& well = (*wellIter);
|
||||
|
||||
if (well.getStatus() == Well::Status::SHUT) {
|
||||
//SHUT wells are not added to the well list
|
||||
continue;
|
||||
}
|
||||
|
||||
if (well.getStatus() == Well::Status::STOP) {
|
||||
// Stopped wells are kept in the well list but marked as stopped.
|
||||
well_controls_stop_well(w_->ctrls[well_index]);
|
||||
}
|
||||
|
||||
|
||||
if (well.isInjector()) {
|
||||
const auto controls = well.injectionControls(summaryState);
|
||||
int ok = 1;
|
||||
int control_pos[5] = { -1, -1, -1, -1, -1 };
|
||||
|
||||
clear_well_controls(well_index, w_);
|
||||
if (controls.hasControl(Well::InjectorCMode::RATE)) {
|
||||
control_pos[WellsManagerDetail::InjectionControl::RATE] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
auto injectorType = controls.injector_type;
|
||||
|
||||
if (injectorType == Well::InjectorType::WATER) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
||||
} else if (injectorType == Well::InjectorType::OIL) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
||||
} else if (injectorType == Well::InjectorType::GAS) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
||||
}
|
||||
|
||||
ok = append_well_controls(SURFACE_RATE,
|
||||
controls.surface_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::InjectorCMode::RESV)) {
|
||||
control_pos[WellsManagerDetail::InjectionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
auto injectorType = controls.injector_type;
|
||||
|
||||
if (injectorType == Well::InjectorType::WATER) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
||||
} else if (injectorType == Well::InjectorType::OIL) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
||||
} else if (injectorType == Well::InjectorType::GAS) {
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
||||
}
|
||||
|
||||
ok = append_well_controls(RESERVOIR_RATE,
|
||||
controls.reservoir_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::InjectorCMode::BHP)) {
|
||||
control_pos[WellsManagerDetail::InjectionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
ok = append_well_controls(BHP,
|
||||
controls.bhp_limit,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
NULL,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::InjectorCMode::THP)) {
|
||||
control_pos[WellsManagerDetail::InjectionControl::THP] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
const double thp_limit = controls.thp_limit;
|
||||
const int vfp_number = controls.vfp_table_number;
|
||||
ok = append_well_controls(THP,
|
||||
thp_limit,
|
||||
invalid_alq,
|
||||
vfp_number,
|
||||
NULL,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]);
|
||||
}
|
||||
|
||||
if (controls.cmode != Well::InjectorCMode::CMODE_UNDEFINED) {
|
||||
WellsManagerDetail::InjectionControl::Mode mode = WellsManagerDetail::InjectionControl::mode(controls.cmode);
|
||||
int cpos = control_pos[mode];
|
||||
if (cpos == -1 && mode != WellsManagerDetail::InjectionControl::GRUP) {
|
||||
OPM_THROW(std::runtime_error, "Control not specified in well " << well_names[well_index]);
|
||||
}
|
||||
|
||||
set_current_control(well_index, cpos, w_);
|
||||
}
|
||||
|
||||
// Set well component fraction.
|
||||
double cf[3] = { 0.0, 0.0, 0.0 };
|
||||
{
|
||||
auto injectorType = controls.injector_type;
|
||||
|
||||
if (injectorType == Well::InjectorType::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 == Well::InjectorType::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 == Well::InjectorType::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;
|
||||
}
|
||||
std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases);
|
||||
}
|
||||
}
|
||||
|
||||
if (well.isProducer( )) {
|
||||
// Add all controls that are present in well.
|
||||
// First we must clear existing controls, in case the
|
||||
// current WCONPROD line is modifying earlier controls.
|
||||
const auto controls = well.productionControls(summaryState);
|
||||
int control_pos[9] = { -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
int ok = 1;
|
||||
|
||||
clear_well_controls(well_index, w_);
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::ORAT)) {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not active and ORAT control specified.");
|
||||
}
|
||||
|
||||
control_pos[WellsManagerDetail::ProductionControl::ORAT] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Liquid]] = 1.0;
|
||||
ok = append_well_controls(SURFACE_RATE,
|
||||
-controls.oil_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::WRAT)) {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not active and WRAT control specified.");
|
||||
}
|
||||
control_pos[WellsManagerDetail::ProductionControl::WRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Aqua]] = 1.0;
|
||||
ok = append_well_controls(SURFACE_RATE,
|
||||
-controls.water_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::GRAT)) {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not active and GRAT control specified.");
|
||||
}
|
||||
control_pos[WellsManagerDetail::ProductionControl::GRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 0.0, 0.0, 0.0 };
|
||||
distr[phaseUsage.phase_pos[BlackoilPhases::Vapour]] = 1.0;
|
||||
ok = append_well_controls(SURFACE_RATE,
|
||||
-controls.gas_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::LRAT)) {
|
||||
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.");
|
||||
}
|
||||
control_pos[WellsManagerDetail::ProductionControl::LRAT] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
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,
|
||||
-controls.liquid_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::RESV)) {
|
||||
control_pos[WellsManagerDetail::ProductionControl::RESV] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
double distr[3] = { 1.0, 1.0, 1.0 };
|
||||
ok = append_well_controls(RESERVOIR_RATE,
|
||||
-controls.resv_rate,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
distr,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (ok && controls.hasControl(Well::ProducerCMode::THP)) {
|
||||
const double thp_limit = controls.thp_limit;
|
||||
const double alq_value = controls.alq_value;
|
||||
const int vfp_number = controls.vfp_table_number;
|
||||
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_);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
const double bhp_limit = controls.bhp_limit;
|
||||
control_pos[WellsManagerDetail::ProductionControl::BHP] = well_controls_get_num(w_->ctrls[well_index]);
|
||||
ok = append_well_controls(BHP,
|
||||
bhp_limit,
|
||||
invalid_alq,
|
||||
invalid_vfp,
|
||||
NULL,
|
||||
well_index,
|
||||
w_);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
OPM_THROW(std::runtime_error, "Failure occured appending controls for well " << well_names[well_index]);
|
||||
}
|
||||
|
||||
if (controls.cmode != Well::ProducerCMode::CMODE_UNDEFINED) {
|
||||
WellsManagerDetail::ProductionControl::Mode mode = WellsManagerDetail::ProductionControl::mode(controls.cmode);
|
||||
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_);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
break;
|
||||
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;
|
||||
break;
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Unknown preferred phase: " << well.getPreferredPhase());
|
||||
}
|
||||
std::copy(cf, cf + phaseUsage.num_phases, w_->comp_frac + well_index*phaseUsage.num_phases);
|
||||
}
|
||||
}
|
||||
well_index++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// only handle the guide rates from the keyword WGRUPCON
|
||||
void WellsManager::setupGuideRates(const std::vector<Well>& wells, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index)
|
||||
{
|
||||
for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) {
|
||||
const auto& well = *wellIter;
|
||||
|
||||
if (well.getStatus() == Well::Status::SHUT) {
|
||||
//SHUT wells does not need guide rates
|
||||
continue;
|
||||
}
|
||||
|
||||
const int wix = well_names_to_index[well.name()];
|
||||
WellNode& wellnode = *well_collection_.getLeafNodes()[wix];
|
||||
|
||||
// TODO: looks like only handling OIL phase guide rate for producers
|
||||
if (well.getGuideRatePhase() != Well::GuideRateTarget::UNDEFINED && well.getGuideRate() >= 0.) {
|
||||
if (well_data[wix].type == PRODUCER) {
|
||||
wellnode.prodSpec().guide_rate_ = well.getGuideRate();
|
||||
if (well.getGuideRatePhase() == Well::GuideRateTarget::OIL) {
|
||||
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Guide rate type " << Well::GuideRateTarget2String(well.getGuideRatePhase()) << " specified for producer "
|
||||
<< well.name() << " in WGRUPCON, cannot handle.");
|
||||
}
|
||||
} else if (well_data[wix].type == INJECTOR) {
|
||||
wellnode.injSpec().guide_rate_ = well.getGuideRate();
|
||||
if (well.getGuideRatePhase() == Well::GuideRateTarget::RAT) {
|
||||
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Guide rate type " << Well::GuideRateTarget2String(well.getGuideRatePhase()) << " 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());
|
||||
}
|
||||
} else {
|
||||
wellnode.setIsGuideRateWellPotential(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Opm
|
@ -1,201 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELLSMANAGER_HEADER_INCLUDED
|
||||
#define OPM_WELLSMANAGER_HEADER_INCLUDED
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
#include <opm/core/wells/WellsGroup.hpp>
|
||||
|
||||
struct Wells;
|
||||
struct UnstructuredGrid;
|
||||
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class Schedule;
|
||||
|
||||
struct WellData
|
||||
{
|
||||
WellType type;
|
||||
bool allowCrossFlow;
|
||||
// WellControlType control;
|
||||
// double target;
|
||||
double reference_bhp_depth;
|
||||
// Opm::InjectionSpecification::InjectorType injected_phase;
|
||||
int welspecsline;
|
||||
};
|
||||
|
||||
|
||||
struct PerfData
|
||||
{
|
||||
int cell;
|
||||
double well_index;
|
||||
int satnumid;
|
||||
};
|
||||
/// This class manages a Wells struct in the sense that it
|
||||
/// encapsulates creation and destruction of the wells
|
||||
/// data structure.
|
||||
/// The resulting Wells is available through the c_wells() method.
|
||||
class WellsManager
|
||||
{
|
||||
public:
|
||||
/// Default constructor -- no wells.
|
||||
WellsManager();
|
||||
|
||||
/// Construct from existing wells object.
|
||||
/// WellsManager is not properly initialised in the sense that the logic to
|
||||
/// manage control switching does not exist.
|
||||
///
|
||||
/// @param[in] W Existing wells object.
|
||||
explicit WellsManager(struct Wells* W);
|
||||
|
||||
/// Construct from input deck and grid.
|
||||
/// The permeability argument may be zero if the input contain
|
||||
/// well productivity indices, otherwise it must be given in
|
||||
/// order to approximate these by the Peaceman formula.
|
||||
///
|
||||
/// \param deactivated_wells A set of wells that should be treated
|
||||
/// like shut wells. E.g. in a a parallel run these would be
|
||||
/// the wells handeled by another process. Defaults to empty set.
|
||||
template<class F2C, class FC>
|
||||
WellsManager(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
int num_cells,
|
||||
const int* global_cell,
|
||||
const int* cart_dims,
|
||||
int dimensions,
|
||||
const F2C& f2c,
|
||||
FC begin_face_centroids,
|
||||
bool is_parallel_run=false,
|
||||
const std::unordered_set<std::string>& deactivated_wells = std::unordered_set<std::string> ());
|
||||
|
||||
WellsManager(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const Opm::SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
const UnstructuredGrid& grid);
|
||||
/// Destructor.
|
||||
~WellsManager();
|
||||
|
||||
/// Does the "deck" define any wells?
|
||||
bool empty() const;
|
||||
|
||||
/// 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* c_wells() const;
|
||||
|
||||
/// Access the well group hierarchy.
|
||||
const WellCollection& wellCollection() const;
|
||||
WellCollection& wellCollection();
|
||||
|
||||
/// Checks if each condition is met, applies well controls where needed
|
||||
/// (that is, it either changes the active control of violating wells, or shuts
|
||||
/// down wells). Only one change is applied per invocation. Typical use will be
|
||||
/// \code
|
||||
/// solve_pressure();
|
||||
/// while(!wells.conditionsMet(...)) {
|
||||
/// solve_pressure();
|
||||
/// }
|
||||
/// \endcode
|
||||
/// \param[in] well_bhp A vector containing the bhp for each well. Is assumed
|
||||
/// to be ordered the same way as the related Wells-struct.
|
||||
/// \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.
|
||||
/// \return true if no violations were found, false otherwise (false also implies a change).
|
||||
bool conditionsMet(const std::vector<double>& well_bhp,
|
||||
const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
/// 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 applyExplicitReinjectionControls(const std::vector<double>& well_reservoirrates_phase,
|
||||
const std::vector<double>& well_surfacerates_phase);
|
||||
|
||||
|
||||
private:
|
||||
template<class C2F, class FC>
|
||||
void init(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const Opm::SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
int num_cells,
|
||||
const int* global_cell,
|
||||
const int* cart_dims,
|
||||
int dimensions,
|
||||
const C2F& cell_to_faces,
|
||||
FC begin_face_centroids,
|
||||
const std::unordered_set<std::string>& deactivated_wells);
|
||||
// Disable copying and assignment.
|
||||
WellsManager(const WellsManager& other);
|
||||
WellsManager& operator=(const WellsManager& other);
|
||||
static void setupCompressedToCartesian(const int* global_cell, int number_of_cells, std::map<int,int>& cartesian_to_compressed );
|
||||
void setupWellControls(const std::vector<Well>& wells, const SummaryState& summaryState,
|
||||
std::vector<std::string>& well_names, const PhaseUsage& phaseUsage,
|
||||
const std::vector<int>& wells_on_proc);
|
||||
|
||||
template<class C2F, class FC>
|
||||
void createWellsFromSpecs( const std::vector<Well>& wells, size_t timeStep,
|
||||
const C2F& cell_to_faces,
|
||||
const int* cart_dims,
|
||||
FC begin_face_centroids,
|
||||
int dimensions,
|
||||
std::vector<double>& dz,
|
||||
std::vector<std::string>& well_names,
|
||||
std::vector<WellData>& well_data,
|
||||
std::map<std::string, int> & well_names_to_index,
|
||||
const PhaseUsage& phaseUsage,
|
||||
const std::map<int,int>& cartesian_to_compressed,
|
||||
std::vector<int>& wells_on_proc,
|
||||
const std::unordered_set<std::string>& deactivated_wells);
|
||||
|
||||
void setupGuideRates(const std::vector<Well>& wells, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index);
|
||||
|
||||
// Data
|
||||
Wells* w_;
|
||||
WellCollection well_collection_;
|
||||
// Whether this is a parallel simulation
|
||||
bool is_parallel_run_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "WellsManager_impl.hpp"
|
||||
#endif // OPM_WELLSMANAGER_HEADER_INCLUDED
|
@ -1,408 +0,0 @@
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
#include <opm/grid/GridHelpers.hpp>
|
||||
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
#include <opm/grid/utility/compressedToCartesian.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
|
||||
namespace WellsManagerDetail
|
||||
{
|
||||
|
||||
|
||||
namespace ProductionControl
|
||||
{
|
||||
enum Mode { ORAT, WRAT, GRAT,
|
||||
LRAT, CRAT, RESV,
|
||||
BHP , THP , GRUP };
|
||||
/*
|
||||
namespace Details {
|
||||
std::map<std::string, Mode>
|
||||
init_mode_map();
|
||||
} // namespace Details
|
||||
*/
|
||||
Mode mode(const std::string& control);
|
||||
|
||||
|
||||
Mode mode(Opm::Well::ProducerCMode controlMode);
|
||||
} // namespace ProductionControl
|
||||
|
||||
|
||||
namespace InjectionControl
|
||||
{
|
||||
enum Mode { RATE, RESV, BHP,
|
||||
THP, GRUP };
|
||||
/*
|
||||
namespace Details {
|
||||
std::map<std::string, Mode>
|
||||
init_mode_map();
|
||||
} // namespace Details
|
||||
*/
|
||||
Mode mode(const std::string& control);
|
||||
|
||||
Mode mode(Opm::Well::InjectorCMode controlMode);
|
||||
|
||||
} // namespace InjectionControl
|
||||
|
||||
|
||||
template <int dim, class C2F, class FC>
|
||||
std::array<double, dim>
|
||||
getCubeDim(const C2F& c2f,
|
||||
FC begin_face_centroids,
|
||||
int cell)
|
||||
{
|
||||
std::array< std::vector<double>, dim > X;
|
||||
{
|
||||
const std::vector<double>::size_type
|
||||
nf = std::distance(c2f[cell].begin(),
|
||||
c2f[cell].end ());
|
||||
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
X[d].reserve(nf);
|
||||
}
|
||||
}
|
||||
|
||||
typedef typename C2F::row_type::const_iterator FI;
|
||||
|
||||
for (FI f = c2f[cell].begin(), e = c2f[cell].end(); f != e; ++f) {
|
||||
using Opm::UgGridHelpers::increment;
|
||||
using Opm::UgGridHelpers::getCoordinate;
|
||||
|
||||
const FC& fc = increment(begin_face_centroids, *f, dim);
|
||||
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
X[d].push_back(getCoordinate(fc, d));
|
||||
}
|
||||
}
|
||||
|
||||
std::array<double, dim> cube;
|
||||
for (int d = 0; d < dim; ++d) {
|
||||
typedef std::vector<double>::iterator VI;
|
||||
typedef std::pair<VI,VI> PVI;
|
||||
|
||||
const PVI m = std::minmax_element(X[d].begin(), X[d].end());
|
||||
|
||||
cube[d] = *m.second - *m.first;
|
||||
}
|
||||
|
||||
return cube;
|
||||
}
|
||||
} // end namespace WellsManagerDetail
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class C2F, class FC>
|
||||
void WellsManager::createWellsFromSpecs(const std::vector<Well>& wells, size_t timeStep,
|
||||
const C2F& /* c2f */,
|
||||
const int* cart_dims,
|
||||
FC /* begin_face_centroids */,
|
||||
int dimensions,
|
||||
std::vector<double>& /* dz */,
|
||||
std::vector<std::string>& well_names,
|
||||
std::vector<WellData>& well_data,
|
||||
std::map<std::string, int>& well_names_to_index,
|
||||
const PhaseUsage& phaseUsage,
|
||||
const std::map<int,int>& cartesian_to_compressed,
|
||||
std::vector<int>& wells_on_proc,
|
||||
const std::unordered_set<std::string>& ignored_wells)
|
||||
{
|
||||
if (dimensions != 3) {
|
||||
OPM_THROW(std::domain_error,
|
||||
"WellsManager::createWellsFromSpecs() only "
|
||||
"supported in three space dimensions");
|
||||
}
|
||||
|
||||
std::vector<std::vector<PerfData> > wellperf_data;
|
||||
wellperf_data.resize(wells.size());
|
||||
wells_on_proc.resize(wells.size(), 1);
|
||||
|
||||
// The well index on the current process.
|
||||
// Note that some wells are deactivated as they live on the interior
|
||||
// domain of another proccess. Therefore this might different from
|
||||
// the index of the well according to the eclipse state
|
||||
int active_well_index = 0;
|
||||
for (auto wellIter= wells.begin(); wellIter != wells.end(); ++wellIter) {
|
||||
const auto& well = (*wellIter);
|
||||
|
||||
if (well.getStatus() == Well::Status::SHUT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ignored_wells.find(well.name()) != ignored_wells.end() ) {
|
||||
wells_on_proc[ wellIter - wells.begin() ] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
{ // COMPDAT handling
|
||||
// shut completions and open ones stored in this process will have 1 others 0.
|
||||
|
||||
for(const auto& completion : well.getConnections()) {
|
||||
if (completion.state() == Connection::State::OPEN) {
|
||||
const int i = completion.getI();
|
||||
const int j = completion.getJ();
|
||||
const int k = completion.getK();
|
||||
|
||||
const int* cpgdim = cart_dims;
|
||||
const int cart_grid_indx = i + cpgdim[0]*(j + cpgdim[1]*k);
|
||||
const std::map<int, int>::const_iterator cgit = cartesian_to_compressed.find(cart_grid_indx);
|
||||
if (cgit == cartesian_to_compressed.end()) {
|
||||
const std::string msg = ("Cell with i,j,k indices " + std::to_string(i) + " " + std::to_string(j)
|
||||
+ " " + std::to_string(k) + " not found in grid (well = " + well.name() + ").");
|
||||
OPM_THROW(std::runtime_error, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
PerfData pd;
|
||||
pd.cell = cgit->second;
|
||||
pd.well_index = completion.CF() * completion.wellPi();
|
||||
pd.satnumid = completion.satTableId();
|
||||
|
||||
wellperf_data[active_well_index].push_back(pd);
|
||||
}
|
||||
} else {
|
||||
if (completion.state() != Connection::State::SHUT) {
|
||||
OPM_THROW(std::runtime_error, "Completion state: " << Connection::State2String( completion.state() ) << " not handled");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (wellperf_data[active_well_index].empty()) {
|
||||
const std::string msg = " there is no perforations associated with the well "
|
||||
+ well.name() + ", the well is ignored for the report step "
|
||||
+ std::to_string(timeStep);
|
||||
OpmLog::warning(msg);
|
||||
wells_on_proc[wellIter - wells.begin()] = 0;
|
||||
continue;
|
||||
}
|
||||
{ // WELSPECS handling
|
||||
well_names_to_index[well.name()] = active_well_index;
|
||||
well_names.push_back(well.name());
|
||||
{
|
||||
WellData wd;
|
||||
wd.reference_bhp_depth = well.getRefDepth( );
|
||||
wd.welspecsline = -1;
|
||||
if (well.isInjector( ))
|
||||
wd.type = INJECTOR;
|
||||
else
|
||||
wd.type = PRODUCER;
|
||||
|
||||
wd.allowCrossFlow = well.getAllowCrossFlow();
|
||||
well_data.push_back(wd);
|
||||
}
|
||||
}
|
||||
|
||||
active_well_index++;
|
||||
}
|
||||
// Set up reference depths that were defaulted. Count perfs.
|
||||
|
||||
const int num_wells = well_data.size();
|
||||
|
||||
int num_perfs = 0;
|
||||
assert (dimensions == 3);
|
||||
for (int w = 0; w < num_wells; ++w) {
|
||||
num_perfs += wellperf_data[w].size();
|
||||
}
|
||||
// Create the well data structures.
|
||||
struct Wells* w = create_wells(phaseUsage.num_phases, num_wells, num_perfs);
|
||||
|
||||
if (!w) {
|
||||
OPM_THROW(std::runtime_error, "Failed creating Wells struct.");
|
||||
}
|
||||
|
||||
std::swap( w, w_ );
|
||||
destroy_wells( w );
|
||||
|
||||
// Add wells.
|
||||
for (int iw = 0; iw < num_wells; ++iw) {
|
||||
const int w_num_perf = wellperf_data[iw].size();
|
||||
std::vector<int> perf_cells (w_num_perf);
|
||||
std::vector<double> perf_prodind(w_num_perf);
|
||||
std::vector<int> perf_satnumid(w_num_perf);
|
||||
|
||||
for (int perf = 0; perf < w_num_perf; ++perf) {
|
||||
perf_cells [perf] = wellperf_data[iw][perf].cell;
|
||||
perf_prodind[perf] = wellperf_data[iw][perf].well_index;
|
||||
perf_satnumid[perf] = wellperf_data[iw][perf].satnumid;
|
||||
}
|
||||
|
||||
const double* comp_frac = NULL;
|
||||
|
||||
// We initialize all wells with a null component fraction,
|
||||
// and must (for injection wells) overwrite it later.
|
||||
const int ok =
|
||||
add_well(well_data[iw].type,
|
||||
well_data[iw].reference_bhp_depth,
|
||||
w_num_perf,
|
||||
comp_frac,
|
||||
perf_cells.data(),
|
||||
perf_prodind.data(),
|
||||
perf_satnumid.data(),
|
||||
well_names[iw].c_str(),
|
||||
well_data[iw].allowCrossFlow,
|
||||
w_);
|
||||
|
||||
if (!ok) {
|
||||
OPM_THROW(std::runtime_error,
|
||||
"Failed adding well "
|
||||
<< well_names[iw]
|
||||
<< " to Wells data structure.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class C2F, class FC>
|
||||
WellsManager::
|
||||
WellsManager(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const Opm::SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
int number_of_cells,
|
||||
const int* global_cell,
|
||||
const int* cart_dims,
|
||||
int dimensions,
|
||||
const C2F& cell_to_faces,
|
||||
FC begin_face_centroids,
|
||||
bool is_parallel_run,
|
||||
const std::unordered_set<std::string>& deactivated_wells)
|
||||
: w_(create_wells(0,0,0)), is_parallel_run_(is_parallel_run)
|
||||
{
|
||||
init(eclipseState, schedule, summaryState, timeStep, number_of_cells, global_cell,
|
||||
cart_dims, dimensions,
|
||||
cell_to_faces, begin_face_centroids, deactivated_wells);
|
||||
}
|
||||
|
||||
/// Construct wells from deck.
|
||||
template <class C2F, class FC>
|
||||
void
|
||||
WellsManager::init(const Opm::EclipseState& eclipseState,
|
||||
const Opm::Schedule& schedule,
|
||||
const Opm::SummaryState& summaryState,
|
||||
const size_t timeStep,
|
||||
int number_of_cells,
|
||||
const int* global_cell,
|
||||
const int* cart_dims,
|
||||
int dimensions,
|
||||
const C2F& cell_to_faces,
|
||||
FC begin_face_centroids,
|
||||
const std::unordered_set<std::string>& deactivated_wells)
|
||||
{
|
||||
if (dimensions != 3) {
|
||||
OPM_THROW(std::runtime_error,
|
||||
"We cannot initialize wells from a deck unless "
|
||||
"the corresponding grid is 3-dimensional.");
|
||||
}
|
||||
|
||||
if (schedule.numWells() == 0) {
|
||||
OPM_MESSAGE("No wells specified in Schedule section, "
|
||||
"initializing no wells");
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<int,int> cartesian_to_compressed;
|
||||
setupCompressedToCartesian(global_cell, number_of_cells,
|
||||
cartesian_to_compressed);
|
||||
|
||||
// Obtain phase usage data.
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
|
||||
// These data structures will be filled in this constructor,
|
||||
// then used to initialize the Wells struct.
|
||||
std::vector<std::string> well_names;
|
||||
std::vector<WellData> well_data;
|
||||
|
||||
|
||||
// For easy lookup:
|
||||
std::map<std::string, int> well_names_to_index;
|
||||
|
||||
const auto wells = schedule.getWells(timeStep);
|
||||
std::vector<int> wells_on_proc;
|
||||
|
||||
well_names.reserve(wells.size());
|
||||
well_data.reserve(wells.size());
|
||||
|
||||
const auto& eclGrid = eclipseState.getInputGrid();
|
||||
|
||||
// use cell thickness (dz) from eclGrid
|
||||
// dz overwrites values calculated by WellDetails::getCubeDim
|
||||
std::vector<double> dz(number_of_cells);
|
||||
{
|
||||
std::vector<int> gc = compressedToCartesian(number_of_cells, global_cell);
|
||||
for (int cell = 0; cell < number_of_cells; ++cell) {
|
||||
dz[cell] = eclGrid.getCellThickness(gc[cell]);
|
||||
}
|
||||
}
|
||||
|
||||
createWellsFromSpecs(wells, timeStep, cell_to_faces,
|
||||
cart_dims,
|
||||
begin_face_centroids,
|
||||
dimensions,
|
||||
dz,
|
||||
well_names, well_data, well_names_to_index,
|
||||
pu, cartesian_to_compressed,
|
||||
wells_on_proc, deactivated_wells);
|
||||
|
||||
setupWellControls(wells, summaryState, well_names, pu, wells_on_proc);
|
||||
|
||||
{
|
||||
const auto& fieldGroup = schedule.getGroup( "FIELD", timeStep);
|
||||
well_collection_.addField(fieldGroup, summaryState, pu);
|
||||
std::vector< std::string > group_stack = { "FIELD" };
|
||||
|
||||
do {
|
||||
const auto& parent = schedule.getGroup(group_stack.back(), timeStep);
|
||||
group_stack.pop_back();
|
||||
for (const auto& child: parent.groups()) {
|
||||
group_stack.push_back(child);
|
||||
well_collection_.addGroup( schedule.getGroup( child, timeStep ), parent.name(), summaryState, pu );
|
||||
}
|
||||
|
||||
} while( !group_stack.empty() );
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < wells_on_proc.size(); ++i) {
|
||||
// wells_on_proc is a vector of flag to indicate whether a well is on the process
|
||||
if (wells_on_proc[i]) {
|
||||
well_collection_.addWell(wells[i], summaryState, pu);
|
||||
}
|
||||
}
|
||||
|
||||
well_collection_.setWellsPointer(w_);
|
||||
|
||||
if (well_collection_.groupControlActive()) {
|
||||
// here does not consider the well potentials related guide rate setting
|
||||
setupGuideRates(wells, well_data, well_names_to_index);
|
||||
}
|
||||
|
||||
// Debug output.
|
||||
#define EXTRA_OUTPUT
|
||||
#ifdef EXTRA_OUTPUT
|
||||
/*
|
||||
std::cout << "\t WELL DATA" << std::endl;
|
||||
for(int i = 0; i< num_wells; ++i) {
|
||||
std::cout << i << ": " << well_data[i].type << " "
|
||||
<< well_data[i].control << " " << well_data[i].target
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
std::cout << "\n\t PERF DATA" << std::endl;
|
||||
for(int i=0; i< int(wellperf_data.size()); ++i) {
|
||||
for(int j=0; j< int(wellperf_data[i].size()); ++j) {
|
||||
std::cout << i << ": " << wellperf_data[i][j].cell << " "
|
||||
<< wellperf_data[i][j].well_index << std::endl;
|
||||
}
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace Opm
|
@ -1,459 +0,0 @@
|
||||
/*===========================================================================
|
||||
//
|
||||
// File: newwells.c
|
||||
//
|
||||
// Created: 2012-02-03 11:28:40+0100
|
||||
//
|
||||
// Authors: Knut-Andreas Lie <Knut-Andreas.Lie@sintef.no>
|
||||
// Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
// Halvor M. Nilsen <HalvorMoll.Nilsen@sintef.no>
|
||||
// Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
// Xavier Raynaud <Xavier.Raynaud@sintef.no>
|
||||
// Bård Skaflestad <Bard.Skaflestad@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* Controls for a single well.
|
||||
* Each control specifies a well rate or bottom-hole pressure. Only
|
||||
* one control can be active at a time, indicated by current. The
|
||||
* meaning of each control's target value depends on the control type:
|
||||
*
|
||||
* - BHP -> target bottom hole pressure in Pascal.
|
||||
* - THP -> target tubing head pressure in Pascal.
|
||||
* - RESERVOIR_RATE -> target reservoir volume rate in cubic(meter)/second
|
||||
* - SURFACE_RATE -> target surface volume rate in cubic(meter)/second
|
||||
*
|
||||
* The sign convention for RATE targets is as follows:
|
||||
*
|
||||
* - (+) Fluid flowing into reservoir, i.e. injecting.
|
||||
* - (-) Fluid flowing out of reservoir, i.e. producing.
|
||||
*
|
||||
* For *_RATE controls, the distribution of phases used for the control
|
||||
* is also needed. For example, a total rate control should have 1.0
|
||||
* for each phase, whereas a control on oil rate should have 1.0 for
|
||||
* the oil phase and 0.0 for the rest. For BHP controls, this is unused.
|
||||
* The active control acts as an equality constraint, whereas the
|
||||
* non-active controls should be interpreted as inequality
|
||||
* constraints (upper or lower bounds). For instance, a PRODUCER's
|
||||
* BHP constraint defines a minimum acceptable bottom-hole pressure
|
||||
* value for the well.
|
||||
*/
|
||||
|
||||
struct WellControls
|
||||
{
|
||||
/**
|
||||
* Number of controls.
|
||||
*/
|
||||
int num;
|
||||
|
||||
int number_of_phases;
|
||||
|
||||
/**
|
||||
* Array of control types.
|
||||
*/
|
||||
enum WellControlType *type;
|
||||
|
||||
/**
|
||||
* Array of control targets.
|
||||
*/
|
||||
double *target;
|
||||
|
||||
/**
|
||||
* Array of artificial lift quantities.
|
||||
*/
|
||||
double *alq;
|
||||
|
||||
/**
|
||||
* Array of VFP table numbers
|
||||
*/
|
||||
int *vfp;
|
||||
|
||||
/**
|
||||
* Array of rate control distributions,
|
||||
* <CODE>number_of_phases</CODE> numbers for each control
|
||||
*/
|
||||
double *distr;
|
||||
|
||||
/**
|
||||
* Index of current active control.
|
||||
*/
|
||||
int current;
|
||||
|
||||
bool well_is_open;
|
||||
|
||||
/*
|
||||
The capacity allocated.
|
||||
*/
|
||||
int cpty;
|
||||
};
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
well_controls_destroy(struct WellControls *ctrl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (ctrl != NULL) {
|
||||
free (ctrl->distr);
|
||||
free (ctrl->target);
|
||||
free (ctrl->type);
|
||||
free (ctrl->alq);
|
||||
free (ctrl->vfp);
|
||||
}
|
||||
|
||||
free(ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct WellControls *
|
||||
well_controls_create(void)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct WellControls *ctrl;
|
||||
|
||||
ctrl = static_cast<WellControls*>(malloc(1 * sizeof *ctrl));
|
||||
|
||||
if (ctrl != NULL) {
|
||||
/* Initialise empty control set; the well is created open. */
|
||||
ctrl->num = 0;
|
||||
ctrl->number_of_phases = 0;
|
||||
ctrl->type = NULL;
|
||||
ctrl->target = NULL;
|
||||
ctrl->alq = NULL;
|
||||
ctrl->vfp = NULL;
|
||||
ctrl->distr = NULL;
|
||||
ctrl->current = -1;
|
||||
ctrl->cpty = 0;
|
||||
ctrl->well_is_open = true;
|
||||
}
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
well_controls_reserve(int nctrl, struct WellControls *ctrl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
void *type, *target, *alq, *vfp, *distr;
|
||||
|
||||
type = realloc(ctrl->type , nctrl * 1 * sizeof *ctrl->type );
|
||||
target = realloc(ctrl->target, nctrl * 1 * sizeof *ctrl->target);
|
||||
alq = realloc(ctrl->alq , nctrl * 1 * sizeof *ctrl->alq );
|
||||
vfp = realloc(ctrl->vfp , nctrl * 1 * sizeof *ctrl->vfp );
|
||||
distr = realloc(ctrl->distr , nctrl * ctrl->number_of_phases * sizeof *ctrl->distr );
|
||||
|
||||
int ok = 0;
|
||||
if (type != NULL) { ctrl->type = static_cast<WellControlType*>(type) ; ok++; }
|
||||
if (target != NULL) { ctrl->target = static_cast<double*>(target); ok++; }
|
||||
if (alq != NULL) { ctrl->alq = static_cast<double*>(alq ); ok++; }
|
||||
if (vfp != NULL) { ctrl->vfp = static_cast<int*>(vfp); ok++; }
|
||||
if (distr != NULL) { ctrl->distr = static_cast<double*>(distr) ; ok++; }
|
||||
|
||||
if (ok == 5) {
|
||||
for (int c = ctrl->cpty; c < nctrl; c++) {
|
||||
ctrl->type [c] = BHP;
|
||||
ctrl->target[c] = -1.0;
|
||||
}
|
||||
|
||||
for (int p = ctrl->cpty * ctrl->number_of_phases; p < nctrl * ctrl->number_of_phases; ++p) {
|
||||
ctrl->distr[ p ] = 0.0;
|
||||
}
|
||||
|
||||
ctrl->cpty = nctrl;
|
||||
}
|
||||
|
||||
return ok == 5;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct WellControls *
|
||||
well_controls_clone(const struct WellControls *ctrl)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct WellControls* new_ctrls = well_controls_create();
|
||||
|
||||
if (new_ctrls != NULL) {
|
||||
/* Assign appropriate number of phases */
|
||||
well_controls_assert_number_of_phases(new_ctrls, ctrl->number_of_phases);
|
||||
|
||||
int n = well_controls_get_num(ctrl);
|
||||
int ok = well_controls_reserve(n, new_ctrls);
|
||||
|
||||
if (! ok) {
|
||||
well_controls_destroy(new_ctrls);
|
||||
new_ctrls= NULL;
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = 0; ok && (i < n); i++) {
|
||||
enum WellControlType type = well_controls_iget_type (ctrl, i);
|
||||
const double* distr = well_controls_iget_distr (ctrl, i);
|
||||
double target = well_controls_iget_target(ctrl, i);
|
||||
double alq = well_controls_iget_alq (ctrl, i);
|
||||
int vfp = well_controls_iget_vfp (ctrl, i);
|
||||
|
||||
ok = well_controls_add_new(type, target, alq, vfp, distr, new_ctrls);
|
||||
}
|
||||
|
||||
if (i < n) {
|
||||
assert (!ok);
|
||||
well_controls_destroy(new_ctrls);
|
||||
|
||||
new_ctrls = NULL;
|
||||
}
|
||||
else {
|
||||
i = well_controls_get_current(ctrl);
|
||||
well_controls_set_current(new_ctrls, i);
|
||||
|
||||
if (well_controls_well_is_open(ctrl)) {
|
||||
well_controls_open_well(new_ctrls);
|
||||
}
|
||||
else {
|
||||
well_controls_stop_well(new_ctrls);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert (well_controls_equal(ctrl, new_ctrls, true));
|
||||
|
||||
return new_ctrls;
|
||||
}
|
||||
|
||||
|
||||
int well_controls_get_num(const struct WellControls *ctrl) {
|
||||
return ctrl->num;
|
||||
}
|
||||
|
||||
|
||||
int well_controls_get_current( const struct WellControls * ctrl) {
|
||||
return ctrl->current;
|
||||
}
|
||||
|
||||
void
|
||||
well_controls_set_current( struct WellControls * ctrl, int current) {
|
||||
ctrl->current = current;
|
||||
}
|
||||
|
||||
bool well_controls_well_is_stopped(const struct WellControls * ctrl) {
|
||||
return !ctrl->well_is_open;
|
||||
}
|
||||
|
||||
bool well_controls_well_is_open(const struct WellControls * ctrl) {
|
||||
return ctrl->well_is_open;
|
||||
}
|
||||
|
||||
void well_controls_open_well( struct WellControls * ctrl) {
|
||||
ctrl->well_is_open = true;
|
||||
}
|
||||
|
||||
void well_controls_stop_well( struct WellControls * ctrl) {
|
||||
ctrl->well_is_open = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum WellControlType
|
||||
well_controls_iget_type(const struct WellControls * ctrl, int control_index) {
|
||||
return ctrl->type[control_index];
|
||||
}
|
||||
|
||||
|
||||
enum WellControlType
|
||||
well_controls_get_current_type(const struct WellControls * ctrl) {
|
||||
if (ctrl->current < 0)
|
||||
throw std::logic_error("Tried to use invalid current control < 0");
|
||||
return well_controls_iget_type( ctrl , ctrl->current);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
well_controls_iset_type( struct WellControls * ctrls , int control_index , enum WellControlType type) {
|
||||
ctrls->type[control_index] = type;
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
well_controls_iget_target(const struct WellControls * ctrl, int control_index) {
|
||||
return ctrl->target[control_index];
|
||||
}
|
||||
|
||||
double
|
||||
well_controls_get_current_target(const struct WellControls * ctrl) {
|
||||
if (ctrl->current < 0)
|
||||
throw std::logic_error("Tried to use invalid current control < 0");
|
||||
return ctrl->target[ctrl->current];
|
||||
}
|
||||
|
||||
void
|
||||
well_controls_iset_target(struct WellControls * ctrl, int control_index , double target) {
|
||||
ctrl->target[control_index] = target;
|
||||
}
|
||||
|
||||
double
|
||||
well_controls_iget_alq(const struct WellControls * ctrl, int control_index) {
|
||||
return ctrl->alq[control_index];
|
||||
}
|
||||
|
||||
void
|
||||
well_controls_iset_alq(struct WellControls * ctrl, int control_index , double alq) {
|
||||
ctrl->alq[control_index] = alq;
|
||||
}
|
||||
|
||||
int
|
||||
well_controls_iget_vfp(const struct WellControls * ctrl, int control_index) {
|
||||
return ctrl->vfp[control_index];
|
||||
}
|
||||
|
||||
void
|
||||
well_controls_iset_vfp(struct WellControls * ctrl, int control_index , int vfp) {
|
||||
ctrl->vfp[control_index] = vfp;
|
||||
}
|
||||
|
||||
|
||||
const double *
|
||||
well_controls_iget_distr(const struct WellControls * ctrl, int control_index) {
|
||||
int offset = control_index * ctrl->number_of_phases;
|
||||
return &ctrl->distr[offset];
|
||||
}
|
||||
|
||||
|
||||
const double *
|
||||
well_controls_get_current_distr(const struct WellControls * ctrl) {
|
||||
if (ctrl->current < 0)
|
||||
throw std::logic_error("Tried to use invalid current control < 0");
|
||||
return well_controls_iget_distr( ctrl , ctrl->current );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
well_controls_iset_distr(const struct WellControls * ctrl, int control_index, const double * distr) {
|
||||
int offset = control_index * ctrl->number_of_phases;
|
||||
int p;
|
||||
for (p=0; p < ctrl->number_of_phases; p++)
|
||||
ctrl->distr[offset + p] = distr[p];
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
well_controls_assert_number_of_phases(struct WellControls * ctrl , int number_of_phases) {
|
||||
if (ctrl->num == 0)
|
||||
ctrl->number_of_phases = number_of_phases;
|
||||
|
||||
assert( ctrl->number_of_phases == number_of_phases );
|
||||
}
|
||||
|
||||
void
|
||||
well_controls_clear(struct WellControls * ctrl) {
|
||||
ctrl->num = 0;
|
||||
ctrl->number_of_phases = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
well_controls_add_new(enum WellControlType type , double target , double alq , int vfp , const double * distr , struct WellControls * ctrl) {
|
||||
if (ctrl->num == ctrl->cpty) {
|
||||
int new_cpty = 2*ctrl->cpty;
|
||||
if (new_cpty == ctrl->num)
|
||||
new_cpty += 1;
|
||||
|
||||
if (!well_controls_reserve( new_cpty , ctrl))
|
||||
return 0;
|
||||
}
|
||||
|
||||
well_controls_iset_type( ctrl , ctrl->num , type);
|
||||
well_controls_iset_target( ctrl , ctrl->num , target);
|
||||
well_controls_iset_alq(ctrl , ctrl->num , alq);
|
||||
well_controls_iset_vfp(ctrl , ctrl->num , vfp);
|
||||
|
||||
if (distr != NULL)
|
||||
well_controls_iset_distr( ctrl , ctrl->num , distr);
|
||||
|
||||
ctrl->num += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
well_controls_equal(const struct WellControls *ctrls1, const struct WellControls *ctrls2 , bool verbose)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
bool are_equal = true;
|
||||
|
||||
if (!ctrls1 || !ctrls2) {
|
||||
if (verbose)
|
||||
printf("ctrls1 %p or cntrls2 %p is NULL\n",
|
||||
(void*) ctrls1, (void*) ctrls2);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctrls1->num != ctrls2->num) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("ctrls1->num:%d ctrls2->num:%d \n",ctrls1->num , ctrls2->num);
|
||||
}
|
||||
|
||||
if (ctrls1->number_of_phases != ctrls2->number_of_phases) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("ctrls1->number_of_phases:%d ctrls2->number_of_phases:%d \n",ctrls1->number_of_phases , ctrls2->number_of_phases);
|
||||
}
|
||||
|
||||
if (!are_equal) {
|
||||
return are_equal;
|
||||
}
|
||||
|
||||
if (memcmp(ctrls1->type, ctrls2->type, ctrls1->num * sizeof *ctrls1->type ) != 0) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("The ->type vectors are different \n");
|
||||
}
|
||||
|
||||
if (memcmp(ctrls1->target, ctrls2->target, ctrls1->num * sizeof *ctrls1->target ) != 0) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("The ->target vectors are different \n");
|
||||
}
|
||||
|
||||
return are_equal;
|
||||
}
|
||||
|
@ -1,632 +0,0 @@
|
||||
/*===========================================================================
|
||||
//
|
||||
// File: newwells.c
|
||||
//
|
||||
// Created: 2012-02-03 11:28:40+0100
|
||||
//
|
||||
// Authors: Knut-Andreas Lie <Knut-Andreas.Lie@sintef.no>
|
||||
// Jostein R. Natvig <Jostein.R.Natvig@sintef.no>
|
||||
// Halvor M. Nilsen <HalvorMoll.Nilsen@sintef.no>
|
||||
// Atgeirr F. Rasmussen <atgeirr@sintef.no>
|
||||
// Xavier Raynaud <Xavier.Raynaud@sintef.no>
|
||||
// Bård Skaflestad <Bard.Skaflestad@sintef.no>
|
||||
//
|
||||
//==========================================================================*/
|
||||
|
||||
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2012 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
struct WellMgmt {
|
||||
int well_cpty;
|
||||
int perf_cpty;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
destroy_well_mgmt(struct WellMgmt *m)
|
||||
{
|
||||
free(m);
|
||||
}
|
||||
|
||||
|
||||
static struct WellMgmt *
|
||||
create_well_mgmt(void)
|
||||
{
|
||||
struct WellMgmt *m;
|
||||
|
||||
m = malloc(1 * sizeof *m);
|
||||
|
||||
if (m != NULL) {
|
||||
m->well_cpty = 0;
|
||||
m->perf_cpty = 0;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
wells_allocate(int nwells, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ok, np;
|
||||
void *type, *depth_ref, *comp_frac;
|
||||
void *well_connpos;
|
||||
void *ctrls, *name;
|
||||
void *allow_cf;
|
||||
|
||||
np = W->number_of_phases;
|
||||
|
||||
type = realloc(W->type , 1 * nwells * sizeof *W->type);
|
||||
depth_ref = realloc(W->depth_ref, 1 * nwells * sizeof *W->depth_ref);
|
||||
comp_frac = realloc(W->comp_frac, np * nwells * sizeof *W->comp_frac);
|
||||
ctrls = realloc(W->ctrls , 1 * nwells * sizeof *W->ctrls);
|
||||
name = realloc(W->name , 1 * nwells * sizeof *W->name);
|
||||
allow_cf = realloc(W->allow_cf , 1 * nwells * sizeof *W->allow_cf);
|
||||
|
||||
well_connpos = realloc(W->well_connpos,
|
||||
(nwells + 1) * sizeof *W->well_connpos);
|
||||
|
||||
ok = 0;
|
||||
if (type != NULL) { W->type = type ; ok++; }
|
||||
if (depth_ref != NULL) { W->depth_ref = depth_ref ; ok++; }
|
||||
if (comp_frac != NULL) { W->comp_frac = comp_frac ; ok++; }
|
||||
if (well_connpos != NULL) { W->well_connpos = well_connpos; ok++; }
|
||||
if (ctrls != NULL) { W->ctrls = ctrls ; ok++; }
|
||||
if (name != NULL) { W->name = name ; ok++; }
|
||||
if (allow_cf != NULL) { W->allow_cf = allow_cf ; ok++; }
|
||||
|
||||
return ok == 7;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
perfs_allocate(int nperf, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ok;
|
||||
void *well_cells, *WI, *sat_table_id;
|
||||
|
||||
well_cells = realloc(W->well_cells, nperf * sizeof *W->well_cells);
|
||||
WI = realloc(W->WI , nperf * sizeof *W->WI );
|
||||
sat_table_id = realloc(W->sat_table_id , nperf * sizeof *W->sat_table_id );
|
||||
|
||||
ok = 0;
|
||||
if (well_cells != NULL) { W->well_cells = well_cells; ok++; }
|
||||
if (WI != NULL) { W->WI = WI ; ok++; }
|
||||
if (sat_table_id != NULL) { W->sat_table_id = sat_table_id ; ok++; }
|
||||
|
||||
return ok == 3;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
initialise_new_wells(int nwells, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ok, w, p;
|
||||
|
||||
struct WellMgmt *m;
|
||||
|
||||
m = W->data;
|
||||
|
||||
for (w = m->well_cpty; w < nwells; w++) {
|
||||
W->type [w] = PRODUCER;
|
||||
W->depth_ref[w] = -1.0;
|
||||
W->name [w] = NULL;
|
||||
W->allow_cf [w] = 1;
|
||||
|
||||
for (p = 0; p < W->number_of_phases; ++p) {
|
||||
W->comp_frac[W->number_of_phases*w + p] = 0.0;
|
||||
}
|
||||
|
||||
W->well_connpos[w + 1] = W->well_connpos[w];
|
||||
}
|
||||
|
||||
for (w = m->well_cpty, ok = 1; ok && (w < nwells); w++) {
|
||||
W->ctrls[w] = well_controls_create( );
|
||||
|
||||
ok = W->ctrls[w] != NULL;
|
||||
}
|
||||
|
||||
if (! ok) {
|
||||
for (; w < nwells; w++) {
|
||||
W->ctrls[w] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void
|
||||
initialise_new_perfs(int nperf, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int k;
|
||||
|
||||
struct WellMgmt *m;
|
||||
|
||||
m = W->data;
|
||||
|
||||
for (k = m->perf_cpty; k < nperf; k++) {
|
||||
W->well_cells[k] = -1 ;
|
||||
W->WI [k] = 0.0;
|
||||
W->sat_table_id[k] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
wells_reserve(int nwells, int nperf, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
int ok;
|
||||
|
||||
struct WellMgmt *m;
|
||||
|
||||
m = W->data;
|
||||
|
||||
assert (nwells >= m->well_cpty);
|
||||
assert (nperf >= m->perf_cpty);
|
||||
|
||||
ok = 1;
|
||||
|
||||
if (nwells > m->well_cpty) {
|
||||
ok = wells_allocate(nwells, W);
|
||||
|
||||
if (ok) {
|
||||
ok = initialise_new_wells(nwells, W);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
m->well_cpty = nwells;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok && (nperf > m->perf_cpty)) {
|
||||
ok = perfs_allocate(nperf, W);
|
||||
|
||||
if (ok) {
|
||||
initialise_new_perfs(nperf, W);
|
||||
m->perf_cpty = nperf;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static char *
|
||||
dup_string(const char *s)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
char *t;
|
||||
|
||||
assert (s != NULL);
|
||||
|
||||
t = malloc((strlen(s) + 1) * sizeof *t);
|
||||
|
||||
if (t != NULL) {
|
||||
strcpy(t, s);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Public entry points below separator.
|
||||
* ====================================================================== */
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct Wells *
|
||||
create_wells(int nphases, int nwells, int nperf)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct Wells* W = malloc(1 * sizeof *W);
|
||||
|
||||
if (W != NULL) {
|
||||
W->number_of_wells = 0;
|
||||
W->number_of_phases = nphases;
|
||||
|
||||
W->type = NULL;
|
||||
W->depth_ref = NULL;
|
||||
W->comp_frac = NULL;
|
||||
|
||||
W->well_connpos = malloc(1 * sizeof *W->well_connpos);
|
||||
W->well_cells = NULL;
|
||||
W->WI = NULL;
|
||||
W->sat_table_id = NULL;
|
||||
|
||||
W->ctrls = NULL;
|
||||
W->name = NULL;
|
||||
W->allow_cf = NULL;
|
||||
|
||||
W->data = create_well_mgmt();
|
||||
|
||||
int ok = (W->well_connpos != NULL) && (W->data != NULL);
|
||||
if (ok) {
|
||||
W->well_connpos[0] = 0;
|
||||
|
||||
if ((nwells > 0) || (nperf > 0)) {
|
||||
ok = wells_reserve(nwells, nperf, W);
|
||||
}
|
||||
}
|
||||
|
||||
if (! ok) {
|
||||
destroy_wells(W);
|
||||
W = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return W;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
destroy_wells(struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (W != NULL) {
|
||||
struct WellMgmt* m = W->data;
|
||||
|
||||
for (int w = 0; w < m->well_cpty; w++) {
|
||||
well_controls_destroy(W->ctrls[w]);
|
||||
}
|
||||
|
||||
for (int w = 0; w < m->well_cpty; w++) {
|
||||
free(W->name[w]);
|
||||
}
|
||||
|
||||
destroy_well_mgmt(m);
|
||||
|
||||
free(W->name);
|
||||
free(W->ctrls);
|
||||
free(W->WI);
|
||||
free(W->sat_table_id);
|
||||
free(W->well_cells);
|
||||
free(W->well_connpos);
|
||||
free(W->comp_frac);
|
||||
free(W->depth_ref);
|
||||
free(W->type);
|
||||
free(W->allow_cf);
|
||||
}
|
||||
|
||||
free(W);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static int
|
||||
alloc_size(int n, int a, int cpty)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
if (cpty < n + a) {
|
||||
cpty *= 2; /* log_2(n) allocations */
|
||||
|
||||
if (cpty < n + a) { /* Typically for the first few allocs */
|
||||
cpty = n + a;
|
||||
}
|
||||
}
|
||||
|
||||
return cpty;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
add_well(enum WellType type ,
|
||||
double depth_ref,
|
||||
int nperf ,
|
||||
const double *comp_frac, /* Injection fraction or NULL */
|
||||
const int *cells ,
|
||||
const double *WI , /* Well index per perf (or NULL) */
|
||||
const int *sat_table_id, /*Saturation table id per perf (or NULL) */
|
||||
const char *name , /* Well name (or NULL) */
|
||||
int allow_cf ,
|
||||
struct Wells *W )
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
assert (W != NULL);
|
||||
|
||||
int nw = W->number_of_wells;
|
||||
int nperf_tot = W->well_connpos[nw];
|
||||
|
||||
struct WellMgmt* m = W->data;
|
||||
|
||||
int ok = (nw < m->well_cpty) && (nperf_tot + nperf <= m->perf_cpty);
|
||||
|
||||
if (! ok) {
|
||||
int nwalloc = alloc_size(nw , 1 , m->well_cpty);
|
||||
int nperfalloc = alloc_size(nperf_tot, nperf, m->perf_cpty);
|
||||
|
||||
ok = wells_reserve(nwalloc, nperfalloc, W);
|
||||
}
|
||||
|
||||
int off = W->well_connpos[nw];
|
||||
|
||||
if (ok && (nperf > 0)) {
|
||||
assert (cells != NULL);
|
||||
if (cells != NULL && W->well_cells != NULL)
|
||||
memcpy(W->well_cells + off,
|
||||
cells, nperf * sizeof *W->well_cells);
|
||||
|
||||
if (W->WI != NULL && WI != NULL) {
|
||||
memcpy(W->WI + off, WI, nperf * sizeof *W->WI);
|
||||
}
|
||||
if (W->sat_table_id != NULL && sat_table_id != NULL) {
|
||||
memcpy(W->sat_table_id + off, sat_table_id, nperf * sizeof *W->sat_table_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
W->type [nw] = type ;
|
||||
W->depth_ref[nw] = depth_ref;
|
||||
W->allow_cf [nw] = allow_cf;
|
||||
|
||||
if (name != NULL) {
|
||||
/* May return NULL, but that's fine for the current
|
||||
* purpose. */
|
||||
W->name [nw] = dup_string(name);
|
||||
}
|
||||
|
||||
int np = W->number_of_phases;
|
||||
if (comp_frac != NULL) {
|
||||
memcpy(W->comp_frac + np*nw, comp_frac, np * sizeof *W->comp_frac);
|
||||
}
|
||||
|
||||
W->well_connpos[nw + 1] = off + nperf;
|
||||
W->number_of_wells += 1;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
int
|
||||
append_well_controls(enum WellControlType type,
|
||||
double target,
|
||||
double alq,
|
||||
int vfp,
|
||||
const double *distr,
|
||||
int well_index,
|
||||
struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct WellControls *ctrl;
|
||||
|
||||
assert (W != NULL);
|
||||
assert ((0 <= well_index) && (well_index < W->number_of_wells));
|
||||
|
||||
ctrl = W->ctrls[well_index];
|
||||
assert (ctrl != NULL);
|
||||
|
||||
well_controls_assert_number_of_phases( ctrl , W->number_of_phases);
|
||||
return well_controls_add_new(type , target , alq , vfp , distr , ctrl);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
set_current_control(int well_index, int current_control, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
assert (W != NULL);
|
||||
assert ((0 <= well_index) && (well_index < W->number_of_wells));
|
||||
assert (W->ctrls[well_index] != NULL);
|
||||
assert (current_control < well_controls_get_num(W->ctrls[well_index]));
|
||||
|
||||
well_controls_set_current(W->ctrls[well_index] , current_control);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
void
|
||||
clear_well_controls(int well_index, struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
assert (W != NULL);
|
||||
assert ((0 <= well_index) && (well_index < W->number_of_wells));
|
||||
|
||||
if (W->ctrls[well_index] != NULL)
|
||||
well_controls_clear( W->ctrls[well_index] );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
struct Wells *
|
||||
clone_wells(const struct Wells *W)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
struct WellControls *ctrl;
|
||||
struct Wells *newWells;
|
||||
|
||||
if (W == NULL) {
|
||||
newWells = NULL;
|
||||
}
|
||||
else {
|
||||
int np = W->number_of_phases;
|
||||
newWells = create_wells(W->number_of_phases, W->number_of_wells,
|
||||
W->well_connpos[ W->number_of_wells ]);
|
||||
|
||||
if (newWells != NULL) {
|
||||
int pos = W->well_connpos[ 0 ];
|
||||
int ok = 1;
|
||||
|
||||
for (int w = 0; ok && (w < W->number_of_wells); w++) {
|
||||
int nperf = W->well_connpos[w + 1] - pos;
|
||||
const int* cells = W->well_cells + pos;
|
||||
|
||||
const double* WI = W->WI != NULL ? W->WI + pos : NULL;
|
||||
const int* sat_table_id = W->sat_table_id != NULL ? W->sat_table_id + pos : NULL;
|
||||
const double* comp_frac = W->comp_frac != NULL ? W->comp_frac + w*np : NULL;
|
||||
|
||||
ok = add_well(W->type[ w ], W->depth_ref[ w ], nperf,
|
||||
comp_frac, cells, WI, sat_table_id, W->name[ w ], W->allow_cf[ w ], newWells);
|
||||
|
||||
if (ok) {
|
||||
ok = (ctrl = well_controls_clone(W->ctrls[w])) != NULL;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
/* Destroy control set implied by add_well() */
|
||||
well_controls_destroy(newWells->ctrls[w]);
|
||||
|
||||
/* Assign complete clone of w's control set */
|
||||
newWells->ctrls[w] = ctrl;
|
||||
}
|
||||
|
||||
pos = W->well_connpos[w + 1];
|
||||
}
|
||||
|
||||
if (! ok) {
|
||||
destroy_wells(newWells);
|
||||
newWells = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert (wells_equal(newWells, W, false));
|
||||
|
||||
return newWells;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
bool
|
||||
wells_equal(const struct Wells *W1, const struct Wells *W2 , bool verbose)
|
||||
/* ---------------------------------------------------------------------- */
|
||||
{
|
||||
// Cater the case where W1 and W2 are the same (null) pointers.
|
||||
if( W1 == W2 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if( W1 == NULL || W2 == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool are_equal = (W1->number_of_wells == W2->number_of_wells) &&
|
||||
(W1->number_of_phases == W2->number_of_phases);
|
||||
if (!are_equal) {
|
||||
return are_equal;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i=0; i<W1->number_of_wells; i++) {
|
||||
if (are_equal) {
|
||||
/*
|
||||
The name attribute can be NULL. The comparison is as
|
||||
follows:
|
||||
|
||||
1. If both names are different from NULL a normal
|
||||
strcmp() is performed.
|
||||
2. If both names are NULL they compare as equal.
|
||||
3. If one name is NULL and the other is not NULL
|
||||
they are regarded as different.
|
||||
*/
|
||||
if (W1->name[i] && W2->name[i])
|
||||
are_equal = are_equal && (strcmp(W1->name[i], W2->name[i]) == 0);
|
||||
else
|
||||
are_equal = are_equal && (W1->name[i] == W2->name[i]);
|
||||
|
||||
if (verbose && !are_equal)
|
||||
printf("Well name[%d] %s and %s are different \n", i , W1->name[i] , W2->name[i]);
|
||||
}
|
||||
if (W1->type[i] != W2->type[i]) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("Well->type[%d] different %d %d \n",i , W1->type[i] , W2->type[i] );
|
||||
}
|
||||
if (W1->depth_ref[i] != W2->depth_ref[i]) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("Well->depth_ref[%d] different %g %g \n",i , W1->depth_ref[i] , W2->depth_ref[i] );
|
||||
}
|
||||
if (!well_controls_equal(W1->ctrls[i], W2->ctrls[i],verbose)) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("Well controls are different for well[%d]:%s \n",i,W1->name[i]);
|
||||
}
|
||||
if (W1->allow_cf[i] != W2->allow_cf[i]) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("Well->allow_cf[%d] different %d %d \n",i , W1->type[i] , W2->type[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
struct WellMgmt* mgmt1 = W1->data;
|
||||
struct WellMgmt* mgmt2 = W2->data;
|
||||
are_equal = are_equal && (mgmt1->perf_cpty == mgmt2->perf_cpty);
|
||||
are_equal = are_equal && (mgmt1->well_cpty == mgmt2->well_cpty);
|
||||
}
|
||||
|
||||
if (memcmp(W1->comp_frac, W2->comp_frac, W1->number_of_wells * W1->number_of_phases * sizeof *W1->comp_frac ) != 0) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("Component fractions different \n");
|
||||
}
|
||||
|
||||
if (memcmp(W1->well_connpos, W2->well_connpos, (1 + W1->number_of_wells) * sizeof *W1->well_connpos ) != 0) {
|
||||
are_equal = false;
|
||||
if (verbose)
|
||||
printf("perforation position map difference \n");
|
||||
}
|
||||
|
||||
{
|
||||
int number_of_perforations = W1->well_connpos[W1->number_of_wells];
|
||||
|
||||
are_equal = are_equal && (memcmp(W1->well_cells, W2->well_cells, number_of_perforations * sizeof *W1->well_cells ) == 0);
|
||||
are_equal = are_equal && (memcmp(W1->WI, W2->WI, number_of_perforations * sizeof *W1->WI ) == 0);
|
||||
are_equal = are_equal && (memcmp(W1->sat_table_id, W2->sat_table_id, number_of_perforations * sizeof *W1->sat_table_id ) == 0);
|
||||
}
|
||||
|
||||
return are_equal;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
@ -1,115 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
#include <chrono>
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellCollectionTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/Deck/Deck.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(AddWellsAndGroupToCollection) {
|
||||
Parser parser;
|
||||
std::string scheduleFile("wells_group.data");
|
||||
Deck deck = parser.parseFile(scheduleFile);
|
||||
EclipseState eclipseState(deck);
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const TableManager table ( deck );
|
||||
const Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Runspec runspec(deck);
|
||||
const Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
WellCollection collection;
|
||||
|
||||
// Add groups to WellCollection
|
||||
const auto& fieldGroup = sched.getGroup("FIELD", 2);
|
||||
collection.addField(fieldGroup, summaryState, pu);
|
||||
|
||||
collection.addGroup( sched.getGroup( "G1", 2 ), fieldGroup.name(), summaryState, pu);
|
||||
collection.addGroup( sched.getGroup( "G2", 2 ), fieldGroup.name(), summaryState, pu);
|
||||
|
||||
BOOST_CHECK_EQUAL("FIELD", collection.findNode("FIELD")->name());
|
||||
BOOST_CHECK_EQUAL("FIELD", collection.findNode("G1")->getParent()->name());
|
||||
BOOST_CHECK_EQUAL("FIELD", collection.findNode("G2")->getParent()->name());
|
||||
|
||||
// Add wells to WellCollection
|
||||
WellCollection wellCollection;
|
||||
const auto wells = sched.getWellsatEnd();
|
||||
for (size_t i=0; i<wells.size(); i++) {
|
||||
collection.addWell(wells[i], summaryState, pu);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL("G1", collection.findNode("INJ1")->getParent()->name());
|
||||
BOOST_CHECK_EQUAL("G1", collection.findNode("INJ2")->getParent()->name());
|
||||
BOOST_CHECK_EQUAL("G2", collection.findNode("PROD1")->getParent()->name());
|
||||
BOOST_CHECK_EQUAL("G2", collection.findNode("PROD2")->getParent()->name());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(EfficiencyFactor) {
|
||||
Parser parser;
|
||||
std::string scheduleFile("wells_group.data");
|
||||
Deck deck = parser.parseFile(scheduleFile);
|
||||
EclipseState eclipseState(deck);
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const TableManager table ( deck );
|
||||
const Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Runspec runspec(deck);
|
||||
const Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
size_t timestep = 2;
|
||||
WellCollection collection;
|
||||
// Add groups to WellCollection
|
||||
const auto& fieldGroup = sched.getGroup("FIELD", timestep);
|
||||
collection.addField( fieldGroup, summaryState, pu);
|
||||
collection.addGroup( sched.getGroup( "G1", timestep ), fieldGroup.name(), summaryState, pu);
|
||||
collection.addGroup( sched.getGroup( "G2", timestep ), fieldGroup.name(), summaryState, pu);
|
||||
|
||||
BOOST_CHECK_EQUAL(1.0, collection.findNode("FIELD")->efficiencyFactor());
|
||||
BOOST_CHECK_EQUAL(1.0, collection.findNode("G1")->getParent()->efficiencyFactor());
|
||||
BOOST_CHECK_EQUAL(1.0, collection.findNode("G2")->getParent()->efficiencyFactor());
|
||||
|
||||
// Add wells to WellCollection
|
||||
const auto wells1 = sched.getWells(timestep);
|
||||
for (size_t i=0; i<wells1.size(); i++) {
|
||||
collection.addWell(wells1[i], summaryState, pu);
|
||||
}
|
||||
|
||||
// 0.5(inj1) * 0.8(G1)
|
||||
BOOST_CHECK_CLOSE(0.4, collection.findWellNode("INJ1").getAccumulativeEfficiencyFactor(), 1e-10);
|
||||
// 0.8(inj2) * 0.8(G1)
|
||||
BOOST_CHECK_CLOSE(0.64, collection.findWellNode("INJ2").getAccumulativeEfficiencyFactor(), 1e-10);
|
||||
// 0.5 (prod1) * 1.0 (G2)
|
||||
BOOST_CHECK_CLOSE(0.5, collection.findWellNode("PROD1").getAccumulativeEfficiencyFactor(), 1e-10);
|
||||
// 1.0 (prod2) * 1.0 (G2)
|
||||
BOOST_CHECK_CLOSE(1.0, collection.findWellNode("PROD2").getAccumulativeEfficiencyFactor(), 1e-10);
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Statoil.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellsModuleTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Construction)
|
||||
{
|
||||
struct WellControls * ctrls = well_controls_create();
|
||||
|
||||
well_controls_set_current( ctrls , 1 );
|
||||
BOOST_CHECK_EQUAL( 1 , well_controls_get_current( ctrls ));
|
||||
well_controls_set_current( ctrls , 2 );
|
||||
BOOST_CHECK_EQUAL( 2 , well_controls_get_current( ctrls ));
|
||||
|
||||
{
|
||||
enum WellControlType type1 = BHP;
|
||||
enum WellControlType type2 = SURFACE_RATE;
|
||||
int num_phases = 3;
|
||||
double dist1[3] = {0 , 1 , 2};
|
||||
double dist2[3] = {10, 11 , 12};
|
||||
double target = 77;
|
||||
double alq = 88;
|
||||
int vfp = 42;
|
||||
|
||||
well_controls_assert_number_of_phases( ctrls , num_phases );
|
||||
well_controls_add_new( type1 , target , alq , vfp , dist1 , ctrls );
|
||||
well_controls_add_new( type2 , 2*target , 2*alq , 2*vfp , dist2 , ctrls );
|
||||
|
||||
BOOST_CHECK_EQUAL( target , well_controls_iget_target(ctrls , 0 ));
|
||||
BOOST_CHECK_EQUAL( alq , well_controls_iget_alq(ctrls , 0 ));
|
||||
BOOST_CHECK_EQUAL( vfp , well_controls_iget_vfp(ctrls , 0 ));
|
||||
BOOST_CHECK_EQUAL( type1 , well_controls_iget_type(ctrls , 0 ));
|
||||
|
||||
BOOST_CHECK_EQUAL( 2*target , well_controls_iget_target(ctrls , 1 ));
|
||||
BOOST_CHECK_EQUAL( 2*alq , well_controls_iget_alq(ctrls , 1 ));
|
||||
BOOST_CHECK_EQUAL( 2*vfp , well_controls_iget_vfp(ctrls , 1 ));
|
||||
BOOST_CHECK_EQUAL( type2 , well_controls_iget_type(ctrls , 1 ));
|
||||
well_controls_set_current( ctrls , 1 );
|
||||
BOOST_CHECK_EQUAL( type2 , well_controls_get_current_type( ctrls ));
|
||||
|
||||
BOOST_CHECK_EQUAL( well_controls_iget_target( ctrls , 1 ) , well_controls_get_current_target( ctrls ));
|
||||
|
||||
{
|
||||
const double * d1 = well_controls_iget_distr( ctrls , 0 );
|
||||
const double * d2 = well_controls_iget_distr( ctrls , 1 );
|
||||
BOOST_CHECK( memcmp(d1 , dist1 , num_phases * sizeof * d1 ) == 0);
|
||||
BOOST_CHECK( memcmp(d2 , dist2 , num_phases * sizeof * d2 ) == 0);
|
||||
}
|
||||
}
|
||||
well_controls_iset_target( ctrls , 0 , 123);
|
||||
BOOST_CHECK_EQUAL( 123 , well_controls_iget_target( ctrls , 0 ));
|
||||
well_controls_iset_target( ctrls , 1 , 456);
|
||||
BOOST_CHECK_EQUAL( 456 , well_controls_iget_target( ctrls , 1 ));
|
||||
|
||||
well_controls_iset_alq( ctrls , 0 , 789);
|
||||
BOOST_CHECK_EQUAL( 789 , well_controls_iget_alq( ctrls , 0 ));
|
||||
well_controls_iset_alq( ctrls , 1 , 234);
|
||||
BOOST_CHECK_EQUAL( 234 , well_controls_iget_alq( ctrls , 1 ));
|
||||
|
||||
well_controls_iset_vfp( ctrls , 0 , 567);
|
||||
BOOST_CHECK_EQUAL( 567 , well_controls_iget_vfp( ctrls , 0 ));
|
||||
well_controls_iset_vfp( ctrls , 1 , 890);
|
||||
BOOST_CHECK_EQUAL( 890 , well_controls_iget_vfp( ctrls , 1 ));
|
||||
|
||||
well_controls_iset_type( ctrls , 0 , SURFACE_RATE);
|
||||
BOOST_CHECK_EQUAL( SURFACE_RATE , well_controls_iget_type( ctrls , 0 ));
|
||||
well_controls_iset_type( ctrls , 1 , BHP);
|
||||
BOOST_CHECK_EQUAL( BHP, well_controls_iget_type( ctrls , 1 ));
|
||||
|
||||
|
||||
{
|
||||
double newDist[3] = {77,78,79};
|
||||
const double * tmp;
|
||||
well_controls_iset_distr( ctrls , 0 , newDist );
|
||||
tmp = well_controls_iget_distr( ctrls , 0);
|
||||
BOOST_CHECK( memcmp(tmp , newDist , 3 * sizeof * tmp ) == 0);
|
||||
}
|
||||
|
||||
|
||||
well_controls_destroy( ctrls );
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(OpenClose)
|
||||
{
|
||||
struct WellControls * ctrls = well_controls_create();
|
||||
|
||||
BOOST_CHECK_EQUAL( true , well_controls_well_is_open(ctrls) );
|
||||
BOOST_CHECK_EQUAL( false , well_controls_well_is_stopped(ctrls) );
|
||||
|
||||
well_controls_open_well( ctrls );
|
||||
BOOST_CHECK_EQUAL( true , well_controls_well_is_open(ctrls) );
|
||||
BOOST_CHECK_EQUAL( false , well_controls_well_is_stopped(ctrls) );
|
||||
|
||||
well_controls_stop_well( ctrls );
|
||||
BOOST_CHECK_EQUAL( false , well_controls_well_is_open(ctrls) );
|
||||
BOOST_CHECK_EQUAL( true , well_controls_well_is_stopped(ctrls) );
|
||||
|
||||
well_controls_destroy( ctrls );
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Clone)
|
||||
{
|
||||
std::shared_ptr<WellControls>
|
||||
ctrls(well_controls_create(),
|
||||
& well_controls_destroy);
|
||||
|
||||
const WellControlType type1 = BHP;
|
||||
const WellControlType type2 = SURFACE_RATE;
|
||||
|
||||
const int num_phases = 3;
|
||||
const double dist1[] = { 0, 1, 2};
|
||||
const double dist2[] = {10, 11, 12};
|
||||
const double target = 77;
|
||||
const double alq = 88;
|
||||
const int vfp = 42;
|
||||
|
||||
well_controls_assert_number_of_phases(ctrls.get(), num_phases);
|
||||
well_controls_add_new(type1, target, alq, vfp, dist1, ctrls.get());
|
||||
well_controls_add_new(type2, 2*target, 2*alq, 2*vfp, dist2, ctrls.get());
|
||||
|
||||
std::shared_ptr<WellControls>
|
||||
c(well_controls_clone(ctrls.get()),
|
||||
& well_controls_destroy);
|
||||
|
||||
BOOST_CHECK(well_controls_equal(ctrls.get(), c.get(), false));
|
||||
}
|
@ -1,249 +0,0 @@
|
||||
/*
|
||||
Copyright 2012 SINTEF ICT, Applied Mathematics.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellsModuleTest
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace
|
||||
{
|
||||
static double invalid_alq = -1e100;
|
||||
static double invalid_vfp = -2147483647;
|
||||
} //Namespace
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Construction)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
std::shared_ptr<Wells> W(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
if (W) {
|
||||
int cells[] = { 0, 9 };
|
||||
double WI = 1.0;
|
||||
int sat_table_id = -1;
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
|
||||
const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0],
|
||||
&WI, &sat_table_id,"INJECTOR", true, W.get());
|
||||
|
||||
const double pfrac[] = { 0.0, 0.0 };
|
||||
const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1],
|
||||
&WI, &sat_table_id,"PRODUCER", true, W.get());
|
||||
|
||||
if (ok0 && ok1) {
|
||||
BOOST_CHECK_EQUAL(W->number_of_phases, nphases);
|
||||
BOOST_CHECK_EQUAL(W->number_of_wells , nwells );
|
||||
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[0], 0);
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[1], 1);
|
||||
BOOST_CHECK_EQUAL(W->well_connpos[W->number_of_wells], nperfs);
|
||||
|
||||
BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[0]], cells[0]);
|
||||
BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[1]], cells[1]);
|
||||
|
||||
BOOST_CHECK_EQUAL(W->WI[W->well_connpos[0]], WI);
|
||||
BOOST_CHECK_EQUAL(W->WI[W->well_connpos[1]], WI);
|
||||
|
||||
using std::string;
|
||||
BOOST_CHECK_EQUAL(string(W->name[0]), string("INJECTOR"));
|
||||
BOOST_CHECK_EQUAL(string(W->name[1]), string("PRODUCER"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Controls)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 1;
|
||||
const int nperfs = 2;
|
||||
|
||||
std::shared_ptr<Wells> W(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
if (W) {
|
||||
int cells[] = { 0 , 9 };
|
||||
double WI [] = { 1.0, 1.0 };
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
int sat_table_id = -1;
|
||||
|
||||
const bool ok = add_well(INJECTOR, 0.0, nperfs, &ifrac[0], &cells[0],
|
||||
&WI[0], &sat_table_id, "INJECTOR", true, W.get());
|
||||
|
||||
if (ok) {
|
||||
const double distr[] = { 1.0, 0.0 };
|
||||
const bool ok1 = append_well_controls(BHP, 1,
|
||||
invalid_alq, invalid_vfp,
|
||||
&distr[0],
|
||||
0, W.get());
|
||||
const bool ok2 = append_well_controls(SURFACE_RATE, 1,
|
||||
invalid_alq, invalid_vfp,
|
||||
&distr[0],
|
||||
0, W.get());
|
||||
|
||||
if (ok1 && ok2) {
|
||||
WellControls* ctrls = W->ctrls[0];
|
||||
|
||||
BOOST_CHECK_EQUAL(well_controls_get_num(ctrls) , 2);
|
||||
BOOST_CHECK_EQUAL(well_controls_get_current(ctrls), -1);
|
||||
|
||||
set_current_control(0, 0, W.get());
|
||||
BOOST_CHECK_EQUAL(well_controls_get_current(ctrls), 0);
|
||||
|
||||
set_current_control(0, 1, W.get());
|
||||
BOOST_CHECK_EQUAL(well_controls_get_current(ctrls), 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_type(ctrls , 0) , BHP);
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_type(ctrls , 1) , SURFACE_RATE);
|
||||
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_target(ctrls , 0), 1.0);
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_target(ctrls , 1), 1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Copy)
|
||||
{
|
||||
const int nphases = 2;
|
||||
const int nwells = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
std::shared_ptr<Wells> W1(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
std::shared_ptr<Wells> W2;
|
||||
|
||||
if (W1) {
|
||||
int cells[] = { 0, 9 };
|
||||
const double WI = 1.0;
|
||||
const double ifrac[] = { 1.0, 0.0 };
|
||||
int sat_table_id = -1;
|
||||
|
||||
|
||||
const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0],
|
||||
&WI, &sat_table_id, "INJECTOR", true, W1.get());
|
||||
|
||||
const double pfrac[] = { 0.0, 0.0 };
|
||||
const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1],
|
||||
&WI, &sat_table_id, "PRODUCER", true, W1.get());
|
||||
|
||||
bool ok = ok0 && ok1;
|
||||
for (int w = 0; ok && (w < W1->number_of_wells); ++w) {
|
||||
const double distr[] = { 1.0, 0.0 };
|
||||
const bool okc1 = append_well_controls(BHP, 1,
|
||||
invalid_alq, invalid_vfp,
|
||||
&distr[0], w,
|
||||
W1.get());
|
||||
const bool okc2 = append_well_controls(SURFACE_RATE, 1,
|
||||
invalid_alq, invalid_vfp,
|
||||
&distr[0], w,
|
||||
W1.get());
|
||||
|
||||
ok = okc1 && okc2;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
W2.reset(clone_wells(W1.get()), destroy_wells);
|
||||
}
|
||||
}
|
||||
|
||||
if (W2) {
|
||||
BOOST_CHECK_EQUAL(W2->number_of_phases, W1->number_of_phases);
|
||||
BOOST_CHECK_EQUAL(W2->number_of_wells , W1->number_of_wells );
|
||||
BOOST_CHECK_EQUAL(W2->well_connpos[0] , W1->well_connpos[0] );
|
||||
|
||||
for (int w = 0; w < W1->number_of_wells; ++w) {
|
||||
using std::string;
|
||||
BOOST_CHECK_EQUAL(string(W2->name[w]), string(W1->name[w]));
|
||||
BOOST_CHECK_EQUAL( W2->type[w] , W1->type[w] );
|
||||
|
||||
BOOST_CHECK_EQUAL(W2->well_connpos[w + 1],
|
||||
W1->well_connpos[w + 1]);
|
||||
|
||||
for (int j = W1->well_connpos[w];
|
||||
j < W1->well_connpos[w + 1]; ++j) {
|
||||
BOOST_CHECK_EQUAL(W2->well_cells[j], W1->well_cells[j]);
|
||||
BOOST_CHECK_EQUAL(W2->WI [j], W1->WI [j]);
|
||||
}
|
||||
|
||||
BOOST_CHECK(W1->ctrls[w] != 0);
|
||||
BOOST_CHECK(W2->ctrls[w] != 0);
|
||||
|
||||
WellControls* c1 = W1->ctrls[w];
|
||||
WellControls* c2 = W2->ctrls[w];
|
||||
|
||||
BOOST_CHECK_EQUAL(well_controls_get_num(c2) , well_controls_get_num(c1));
|
||||
BOOST_CHECK_EQUAL(well_controls_get_current(c2) , well_controls_get_current(c1));
|
||||
|
||||
for (int c = 0; c < well_controls_get_num(c1); ++c) {
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_type(c2, c) , well_controls_iget_type(c1 , c));
|
||||
BOOST_CHECK_EQUAL(well_controls_iget_target(c2, c) , well_controls_iget_target(c1 , c));
|
||||
|
||||
{
|
||||
const double * dist1 = well_controls_iget_distr(c1 , c );
|
||||
const double * dist2 = well_controls_iget_distr(c2 , c );
|
||||
|
||||
for (int p = 0; p < W1->number_of_phases; ++p)
|
||||
BOOST_CHECK_EQUAL( dist1[p] , dist2[p]);
|
||||
}
|
||||
}
|
||||
BOOST_CHECK( well_controls_equal( c1 , c2 , false) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Equals_WellsEqual_ReturnsTrue) {
|
||||
const int nphases = 2;
|
||||
const int nwells = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
std::shared_ptr<Wells> W1(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
std::shared_ptr<Wells> W2(create_wells(nphases, nwells, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
BOOST_CHECK(wells_equal(W1.get(), W2.get() , false));
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Equals_WellsDiffer_ReturnsFalse) {
|
||||
const int nphases = 2;
|
||||
const int nperfs = 2;
|
||||
|
||||
std::shared_ptr<Wells> W1(create_wells(nphases, 2, nperfs),
|
||||
destroy_wells);
|
||||
std::shared_ptr<Wells> W2(create_wells(nphases, 3, nperfs),
|
||||
destroy_wells);
|
||||
|
||||
BOOST_CHECK(!wells_equal(W1.get(), W2.get() , false ));
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 Statoil.
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellsGroupTest
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
||||
#include <opm/core/wells/WellsGroup.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
using namespace Opm;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ConstructGroupFromWell) {
|
||||
std::string scheduleFile("wells_group.data");
|
||||
Parser parser;
|
||||
Deck deck = parser.parseFile(scheduleFile);
|
||||
EclipseState eclipseState(deck);
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const TableManager table ( deck );
|
||||
const Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
|
||||
auto wells = sched.getWellsatEnd();
|
||||
|
||||
for (size_t i=0; i<wells.size(); i++) {
|
||||
const auto& well = wells[i];
|
||||
std::shared_ptr<WellsGroupInterface> wellsGroup = createWellWellsGroup(well, summaryState, pu);
|
||||
BOOST_CHECK_EQUAL(well.name(), wellsGroup->name());
|
||||
if (well.isInjector()) {
|
||||
const auto controls = well.injectionControls(summaryState);
|
||||
BOOST_CHECK_EQUAL(controls.surface_rate, wellsGroup->injSpec().surface_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(controls.bhp_limit, wellsGroup->injSpec().BHP_limit_);
|
||||
BOOST_CHECK_EQUAL(controls.reservoir_rate, wellsGroup->injSpec().reservoir_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(0.0, wellsGroup->prodSpec().guide_rate_);
|
||||
}
|
||||
if (well.isProducer()) {
|
||||
const auto controls = well.productionControls(summaryState);
|
||||
BOOST_CHECK_EQUAL(controls.resv_rate, wellsGroup->prodSpec().reservoir_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(controls.bhp_limit, wellsGroup->prodSpec().BHP_limit_);
|
||||
BOOST_CHECK_EQUAL(controls.oil_rate, wellsGroup->prodSpec().oil_max_rate_);
|
||||
BOOST_CHECK_EQUAL(controls.water_rate, wellsGroup->prodSpec().water_max_rate_);
|
||||
BOOST_CHECK_EQUAL(0.0, wellsGroup->injSpec().guide_rate_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ConstructGroupFromGroup) {
|
||||
Parser parser;
|
||||
std::string scheduleFile("wells_group.data");
|
||||
Deck deck = parser.parseFile(scheduleFile);
|
||||
EclipseState eclipseState(deck);
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const TableManager table ( deck );
|
||||
const Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
for( const auto& grp_name : sched.groupNames() ) {
|
||||
const auto& group = sched.getGroup(grp_name, 2);
|
||||
|
||||
std::shared_ptr<WellsGroupInterface> wellsGroup = createGroupWellsGroup(group, summaryState, pu);
|
||||
BOOST_CHECK_EQUAL(group.name(), wellsGroup->name());
|
||||
if (group.isInjectionGroup()) {
|
||||
const auto& injection = group.injectionControls(summaryState);
|
||||
|
||||
BOOST_CHECK_EQUAL(injection.surface_max_rate, wellsGroup->injSpec().surface_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(injection.resv_max_rate, wellsGroup->injSpec().reservoir_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(injection.target_reinj_fraction, wellsGroup->injSpec().reinjection_fraction_target_);
|
||||
BOOST_CHECK_EQUAL(injection.target_void_fraction, wellsGroup->injSpec().voidage_replacment_fraction_);
|
||||
}
|
||||
|
||||
if (group.isProductionGroup()) {
|
||||
const auto& production = group.productionControls(summaryState);
|
||||
BOOST_CHECK_EQUAL(production.resv_target, wellsGroup->prodSpec().reservoir_flow_max_rate_);
|
||||
BOOST_CHECK_EQUAL(production.gas_target, wellsGroup->prodSpec().gas_max_rate_);
|
||||
BOOST_CHECK_EQUAL(production.oil_target, wellsGroup->prodSpec().oil_max_rate_);
|
||||
BOOST_CHECK_EQUAL(production.water_target, wellsGroup->prodSpec().water_max_rate_);
|
||||
BOOST_CHECK_EQUAL(production.liquid_target, wellsGroup->prodSpec().liquid_max_rate_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(EfficiencyFactor) {
|
||||
Parser parser;
|
||||
std::string scheduleFile("wells_group.data");
|
||||
Deck deck = parser.parseFile(scheduleFile);
|
||||
EclipseState eclipseState(deck);
|
||||
PhaseUsage pu = phaseUsageFromDeck(eclipseState);
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const TableManager table ( deck );
|
||||
const Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
for( const auto& grp_name : sched.groupNames() ) {
|
||||
const auto& group = sched.getGroup(grp_name, 2);
|
||||
|
||||
std::shared_ptr<WellsGroupInterface> wellsGroup = createGroupWellsGroup(group, summaryState, pu);
|
||||
BOOST_CHECK_EQUAL(group.name(), wellsGroup->name());
|
||||
BOOST_CHECK_EQUAL(group.getGroupEfficiencyFactor(), wellsGroup->efficiencyFactor());
|
||||
BOOST_CHECK_EQUAL(group.getGroupEfficiencyFactor(), wellsGroup->efficiencyFactor());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,331 +0,0 @@
|
||||
/*
|
||||
Copyright 2013 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#define NVERBOSE // Suppress own messages when throw()ing
|
||||
|
||||
#define BOOST_TEST_MODULE WellsManagerTests
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/Parser/Parser.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Tables/TableManager.hpp>
|
||||
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
|
||||
#include <opm/grid/GridManager.hpp>
|
||||
|
||||
void wells_static_check(const Wells* wells) {
|
||||
BOOST_CHECK_EQUAL(2, wells->number_of_wells);
|
||||
BOOST_CHECK_EQUAL(3, wells->number_of_phases);
|
||||
|
||||
BOOST_CHECK_EQUAL("INJ1", wells->name[0]);
|
||||
BOOST_CHECK_EQUAL("PROD1", wells->name[1]);
|
||||
|
||||
/* The mapping from well number into the wells->WI and wells->well_cells arrays. */
|
||||
BOOST_CHECK_EQUAL(0, wells->well_connpos[0]);
|
||||
BOOST_CHECK_EQUAL(1, wells->well_connpos[1]);
|
||||
BOOST_CHECK_EQUAL(2, wells->well_connpos[2]);
|
||||
|
||||
/* Connection factor */
|
||||
BOOST_CHECK_CLOSE(1.2279166666666664e-12, wells->WI[0], 0.001);
|
||||
BOOST_CHECK_CLOSE(1.2279166666666664e-12, wells->WI[1], 0.001);
|
||||
|
||||
/* Completed cells */
|
||||
BOOST_CHECK_EQUAL(0, wells->well_cells[0]);
|
||||
BOOST_CHECK_EQUAL(9 + 2 * 10 + 2 * 10 * 10, wells->well_cells[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
The number of controls is determined by looking at which elements
|
||||
have been given explicit - non-default - values in the WCONxxxx
|
||||
keyword. Is that at all interesting?
|
||||
*/
|
||||
|
||||
void check_controls_epoch0(struct WellControls ** ctrls) {
|
||||
// The injector
|
||||
{
|
||||
const struct WellControls * ctrls0 = ctrls[0];
|
||||
BOOST_CHECK_EQUAL(3, well_controls_get_num(ctrls0)); // The number of controls for the injector == 3??
|
||||
|
||||
BOOST_CHECK_EQUAL(SURFACE_RATE, well_controls_iget_type(ctrls0, 0));
|
||||
BOOST_CHECK_EQUAL(RESERVOIR_RATE, well_controls_iget_type(ctrls0, 1));
|
||||
BOOST_CHECK_EQUAL(BHP, well_controls_iget_type(ctrls0, 2));
|
||||
|
||||
// The different targets
|
||||
BOOST_CHECK_EQUAL(100.0 / 86400, well_controls_iget_target(ctrls0, 0));
|
||||
BOOST_CHECK_EQUAL(200.0 / 86400, well_controls_iget_target(ctrls0, 1));
|
||||
BOOST_CHECK_EQUAL(400 * 100000, well_controls_iget_target(ctrls0, 2));
|
||||
|
||||
// Which control is active
|
||||
BOOST_CHECK_EQUAL(0, well_controls_get_current(ctrls0));
|
||||
|
||||
// The phase distribution in the active target
|
||||
{
|
||||
const double * distr = well_controls_iget_distr(ctrls0, 0);
|
||||
BOOST_CHECK_EQUAL(0, distr[0]); // Water
|
||||
BOOST_CHECK_EQUAL(0, distr[1]); // Oil
|
||||
BOOST_CHECK_EQUAL(1, distr[2]); // Gas
|
||||
}
|
||||
}
|
||||
|
||||
// The producer
|
||||
{
|
||||
const struct WellControls * ctrls1 = ctrls[1];
|
||||
BOOST_CHECK_EQUAL(2, well_controls_get_num(ctrls1)); // The number of controls for the producer == 2??
|
||||
BOOST_CHECK_EQUAL(SURFACE_RATE, well_controls_iget_type(ctrls1, 0));
|
||||
BOOST_CHECK_EQUAL(BHP, well_controls_iget_type(ctrls1, 1));
|
||||
|
||||
// The different targets
|
||||
BOOST_CHECK_EQUAL(-20000.0 / 86400, well_controls_iget_target(ctrls1, 0));
|
||||
BOOST_CHECK_EQUAL(1000 * 100000, well_controls_iget_target(ctrls1, 1));
|
||||
|
||||
// Which control is active
|
||||
BOOST_CHECK_EQUAL(0, well_controls_get_current(ctrls1));
|
||||
|
||||
// The phase distribution in the active target
|
||||
{
|
||||
const double * distr = well_controls_iget_distr(ctrls1, 0);
|
||||
BOOST_CHECK_EQUAL(0, distr[0]); // Water
|
||||
BOOST_CHECK_EQUAL(1, distr[1]); // Oil
|
||||
BOOST_CHECK_EQUAL(0, distr[2]); // Gas
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_controls_epoch1(struct WellControls ** ctrls) {
|
||||
// The injector
|
||||
{
|
||||
const struct WellControls * ctrls0 = ctrls[0];
|
||||
BOOST_CHECK_EQUAL(3, well_controls_get_num(ctrls0)); // The number of controls for the injector == 3??
|
||||
|
||||
BOOST_CHECK_EQUAL(SURFACE_RATE, well_controls_iget_type(ctrls0, 0));
|
||||
BOOST_CHECK_EQUAL(RESERVOIR_RATE, well_controls_iget_type(ctrls0, 1));
|
||||
BOOST_CHECK_EQUAL(BHP, well_controls_iget_type(ctrls0, 2));
|
||||
|
||||
// The different targets
|
||||
BOOST_CHECK_CLOSE(10.0 / 86400, well_controls_iget_target(ctrls0, 0), 0.001);
|
||||
BOOST_CHECK_CLOSE(20.0 / 86400, well_controls_iget_target(ctrls0, 1), 0.001);
|
||||
BOOST_CHECK_CLOSE(40 * 100000, well_controls_iget_target(ctrls0, 2), 0.001);
|
||||
|
||||
// Which control is active
|
||||
BOOST_CHECK_EQUAL(1, well_controls_get_current(ctrls0));
|
||||
|
||||
{
|
||||
const double * distr = well_controls_iget_distr(ctrls0, 1);
|
||||
BOOST_CHECK_EQUAL(1, distr[0]); // Water
|
||||
BOOST_CHECK_EQUAL(0, distr[1]); // Oil
|
||||
BOOST_CHECK_EQUAL(0, distr[2]); // Gas
|
||||
}
|
||||
}
|
||||
|
||||
// The producer
|
||||
{
|
||||
const struct WellControls * ctrls1 = ctrls[1];
|
||||
BOOST_CHECK_EQUAL(3, well_controls_get_num(ctrls1)); // The number of controls for the producer - now 3.
|
||||
BOOST_CHECK_EQUAL(SURFACE_RATE, well_controls_iget_type(ctrls1, 0));
|
||||
BOOST_CHECK_EQUAL(RESERVOIR_RATE, well_controls_iget_type(ctrls1, 1));
|
||||
BOOST_CHECK_EQUAL(BHP, well_controls_iget_type(ctrls1, 2));
|
||||
|
||||
// The different targets
|
||||
BOOST_CHECK_CLOSE(-999.0 / 86400, well_controls_iget_target(ctrls1, 0), 0.001);
|
||||
BOOST_CHECK_CLOSE(-123.0 / 86400, well_controls_iget_target(ctrls1, 1), 0.001);
|
||||
BOOST_CHECK_CLOSE(100 * 100000, well_controls_iget_target(ctrls1, 2), 0.001);
|
||||
|
||||
// Which control is active
|
||||
BOOST_CHECK_EQUAL(1, well_controls_get_current(ctrls1));
|
||||
|
||||
{
|
||||
const double * distr = well_controls_iget_distr(ctrls1, 1);
|
||||
BOOST_CHECK_EQUAL(1, distr[0]); // Water
|
||||
BOOST_CHECK_EQUAL(1, distr[1]); // Oil
|
||||
BOOST_CHECK_EQUAL(1, distr[2]); // Gas
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_controls_epoch3(struct WellControls ** ctrls) {
|
||||
// The new producer
|
||||
const struct WellControls * ctrls1 = ctrls[1];
|
||||
// Note: controls include default (1 atm) BHP control.
|
||||
// and also an ORAT control
|
||||
BOOST_CHECK_EQUAL(2, well_controls_get_num(ctrls1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(New_Constructor_Works) {
|
||||
|
||||
const std::string filename = "wells_manager_data.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck = parser.parseFile(filename);
|
||||
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager vanguard(eclipseState.getInputGrid());
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Opm::Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
{
|
||||
Opm::WellsManager wellsManager(eclipseState, sched, summaryState, 0, *vanguard.c_grid());
|
||||
wells_static_check(wellsManager.c_wells());
|
||||
check_controls_epoch0(wellsManager.c_wells()->ctrls);
|
||||
}
|
||||
|
||||
{
|
||||
Opm::WellsManager wellsManager(eclipseState, sched, summaryState, 1, *vanguard.c_grid());
|
||||
wells_static_check(wellsManager.c_wells());
|
||||
check_controls_epoch1(wellsManager.c_wells()->ctrls);
|
||||
}
|
||||
|
||||
{
|
||||
Opm::WellsManager wellsManager(eclipseState, sched, summaryState, 3, *vanguard.c_grid());
|
||||
const Wells* wells = wellsManager.c_wells();
|
||||
|
||||
// There is 3 wells in total in the deck at the 3rd schedule step.
|
||||
// PROD1 is shut and should therefore not be counted.
|
||||
// The new well is therefore the secound well.
|
||||
BOOST_CHECK_EQUAL(2, wells->number_of_wells);
|
||||
BOOST_CHECK_EQUAL(wells->name[0], "INJ1");
|
||||
BOOST_CHECK_EQUAL(wells->name[1], "NEW");
|
||||
|
||||
check_controls_epoch3(wellsManager.c_wells()->ctrls);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WellsEqual) {
|
||||
const std::string filename = "wells_manager_data.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck(parser.parseFile(filename));
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager vanguard(eclipseState.getInputGrid());
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Opm::Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
|
||||
Opm::WellsManager wellsManager0(eclipseState, sched, summaryState, 0, *vanguard.c_grid());
|
||||
Opm::WellsManager wellsManager1(eclipseState, sched, summaryState, 1, *vanguard.c_grid());
|
||||
|
||||
BOOST_CHECK(wells_equal( wellsManager0.c_wells() , wellsManager0.c_wells(),false));
|
||||
BOOST_CHECK(!wells_equal( wellsManager0.c_wells() , wellsManager1.c_wells(),false));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(ControlsEqual) {
|
||||
const std::string filename = "wells_manager_data.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck(parser.parseFile(filename));
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager vanguard(eclipseState.getInputGrid());
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Opm::Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
|
||||
Opm::WellsManager wellsManager0(eclipseState, sched, summaryState, 0, *vanguard.c_grid());
|
||||
Opm::WellsManager wellsManager1(eclipseState, sched, summaryState, 1, *vanguard.c_grid());
|
||||
|
||||
BOOST_CHECK(well_controls_equal( wellsManager0.c_wells()->ctrls[0] , wellsManager0.c_wells()->ctrls[0] , false));
|
||||
BOOST_CHECK(well_controls_equal( wellsManager0.c_wells()->ctrls[1] , wellsManager0.c_wells()->ctrls[1] , false));
|
||||
BOOST_CHECK(well_controls_equal( wellsManager1.c_wells()->ctrls[0] , wellsManager1.c_wells()->ctrls[0] , false));
|
||||
BOOST_CHECK(well_controls_equal( wellsManager1.c_wells()->ctrls[1] , wellsManager1.c_wells()->ctrls[1] , false));
|
||||
|
||||
BOOST_CHECK(!well_controls_equal( wellsManager0.c_wells()->ctrls[0] , wellsManager0.c_wells()->ctrls[1] , false));
|
||||
BOOST_CHECK(!well_controls_equal( wellsManager0.c_wells()->ctrls[1] , wellsManager0.c_wells()->ctrls[0] , false));
|
||||
BOOST_CHECK(!well_controls_equal( wellsManager1.c_wells()->ctrls[0] , wellsManager0.c_wells()->ctrls[0] , false));
|
||||
BOOST_CHECK(!well_controls_equal( wellsManager1.c_wells()->ctrls[1] , wellsManager0.c_wells()->ctrls[1] , false));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WellShutOK) {
|
||||
const std::string filename = "wells_manager_data.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck(parser.parseFile(filename));
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager vanguard(eclipseState.getInputGrid());
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Opm::Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
Opm::WellsManager wellsManager2(eclipseState, sched, summaryState, 2, *vanguard.c_grid());
|
||||
|
||||
// Shut wells are not added to the deck. i.e number of wells should be 2-1
|
||||
BOOST_CHECK(wellsManager2.c_wells()->number_of_wells == 1);
|
||||
|
||||
//BOOST_CHECK_NO_THROW( Opm::WellsManager wellsManager2(eclipseState , 2 , *vanguard.c_grid(), NULL));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(WellSTOPOK) {
|
||||
const std::string filename = "wells_manager_data_wellSTOP.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck(parser.parseFile(filename));
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager vanguard(eclipseState.getInputGrid());
|
||||
const auto& grid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, grid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
const Opm::Schedule sched(deck, grid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
|
||||
|
||||
Opm::WellsManager wellsManager(eclipseState, sched, summaryState, 0, *vanguard.c_grid());
|
||||
|
||||
const Wells* wells = wellsManager.c_wells();
|
||||
const struct WellControls* ctrls0 = wells->ctrls[0];
|
||||
const struct WellControls* ctrls1 = wells->ctrls[1];
|
||||
|
||||
BOOST_CHECK(well_controls_well_is_stopped(ctrls0)); // The first well is closed
|
||||
BOOST_CHECK(well_controls_well_is_open(ctrls1)); // The second well is open
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(removeWellWithNoPerforation) {
|
||||
const std::string filename = "wells_no_perforation.data";
|
||||
Opm::Parser parser;
|
||||
Opm::Deck deck(parser.parseFile(filename));
|
||||
Opm::EclipseState eclipseState(deck);
|
||||
Opm::GridManager gridManager(eclipseState.getInputGrid());
|
||||
const auto& inputGrid = eclipseState.getInputGrid();
|
||||
const Opm::TableManager table ( deck );
|
||||
const Opm::Eclipse3DProperties eclipseProperties ( deck , table, inputGrid);
|
||||
const Opm::Runspec runspec (deck);
|
||||
Opm::Schedule sched(deck, inputGrid, eclipseProperties, runspec);
|
||||
Opm::SummaryState summaryState(std::chrono::system_clock::from_time_t(sched.getStartTime()));
|
||||
const auto eclipseGrid = Opm::UgGridHelpers::createEclipseGrid(*gridManager.c_grid(), inputGrid);
|
||||
sched.filterConnections(eclipseGrid);
|
||||
|
||||
Opm::WellsManager wellsManager0(eclipseState, sched, summaryState, 0, *gridManager.c_grid());
|
||||
BOOST_CHECK_EQUAL( wellsManager0.c_wells()->number_of_wells, 1);
|
||||
|
||||
Opm::WellsManager wellsManager5(eclipseState, sched, summaryState, 5, *gridManager.c_grid());
|
||||
BOOST_CHECK_EQUAL( wellsManager5.c_wells()->number_of_wells, 1);
|
||||
}
|
Loading…
Reference in New Issue
Block a user