mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Moved files to opm/simulators/ subdirs.
This commit is contained in:
432
opm/simulators/wells/BlackoilWellModel.hpp
Normal file
432
opm/simulators/wells/BlackoilWellModel.hpp
Normal file
@@ -0,0 +1,432 @@
|
||||
/*
|
||||
Copyright 2016 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2016 - 2017 Statoil ASA.
|
||||
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2016 - 2018 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_BLACKOILWELLMODEL_HEADER_INCLUDED
|
||||
#define OPM_BLACKOILWELLMODEL_HEADER_INCLUDED
|
||||
|
||||
#include <ebos/eclproblem.hh>
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
|
||||
#include <opm/common/utility/platform_dependent/disable_warnings.h>
|
||||
#include <opm/common/utility/platform_dependent/reenable_warnings.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <tuple>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp>
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/wells/WellCollection.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
#include <opm/simulators/wells/VFPInjProperties.hpp>
|
||||
#include <opm/simulators/wells/VFPProdProperties.hpp>
|
||||
#include <opm/autodiff/BlackoilDetails.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/autodiff/RateConverter.hpp>
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
#include <opm/simulators/wells/StandardWell.hpp>
|
||||
#include <opm/simulators/wells/StandardWellV.hpp>
|
||||
#include <opm/simulators/wells/MultisegmentWell.hpp>
|
||||
#include <opm/simulators/timestepping/gatherConvergenceReport.hpp>
|
||||
#include <opm/autodiff/SimFIBODetails.hpp>
|
||||
#include <dune/common/fmatrix.hh>
|
||||
#include <dune/istl/bcrsmatrix.hh>
|
||||
#include <dune/istl/matrixmatrix.hh>
|
||||
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
|
||||
BEGIN_PROPERTIES
|
||||
|
||||
NEW_PROP_TAG(EnableTerminalOutput);
|
||||
|
||||
END_PROPERTIES
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// Class for handling the blackoil well model.
|
||||
template<typename TypeTag>
|
||||
class BlackoilWellModel : public Ewoms::BaseAuxiliaryModule<TypeTag>
|
||||
{
|
||||
public:
|
||||
// --------- Types ---------
|
||||
typedef WellStateFullyImplicitBlackoil WellState;
|
||||
typedef BlackoilModelParametersEbos<TypeTag> ModelParameters;
|
||||
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, ElementContext) ElementContext;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Scalar) Scalar;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, RateVector) RateVector;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, GlobalEqVector) GlobalEqVector;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, SparseMatrixAdapter) SparseMatrixAdapter;
|
||||
|
||||
typedef typename Ewoms::BaseAuxiliaryModule<TypeTag>::NeighborSet NeighborSet;
|
||||
|
||||
static const int numEq = Indices::numEq;
|
||||
static const int solventSaturationIdx = Indices::solventSaturationIdx;
|
||||
|
||||
// TODO: where we should put these types, WellInterface or Well Model?
|
||||
// or there is some other strategy, like TypeTag
|
||||
typedef Dune::FieldVector<Scalar, numEq > VectorBlockType;
|
||||
typedef Dune::BlockVector<VectorBlockType> BVector;
|
||||
|
||||
#if DUNE_VERSION_NEWER_REV(DUNE_ISTL, 2 , 5, 1)
|
||||
// 3x3 matrix block inversion was unstable from at least 2.3 until and
|
||||
// including 2.5.0
|
||||
typedef Dune::FieldMatrix<Scalar, numEq, numEq > MatrixBlockType;
|
||||
#else
|
||||
typedef Dune::FieldMatrix<Scalar, numEq, numEq > MatrixBlockType;
|
||||
#endif
|
||||
typedef typename SparseMatrixAdapter::IstlMatrix Mat;
|
||||
|
||||
typedef Ewoms::BlackOilPolymerModule<TypeTag> PolymerModule;
|
||||
|
||||
// For the conversion between the surface volume rate and resrevoir voidage rate
|
||||
using RateConverterType = RateConverter::
|
||||
SurfaceToReservoirVoidage<FluidSystem, std::vector<int> >;
|
||||
|
||||
BlackoilWellModel(Simulator& ebosSimulator);
|
||||
|
||||
void init();
|
||||
|
||||
/////////////
|
||||
// <eWoms auxiliary module stuff>
|
||||
/////////////
|
||||
unsigned numDofs() const
|
||||
// No extra dofs are inserted for wells. (we use a Schur complement.)
|
||||
{ return 0; }
|
||||
|
||||
void addNeighbors(std::vector<NeighborSet>& neighbors) const;
|
||||
|
||||
void applyInitial()
|
||||
{}
|
||||
|
||||
void linearize(SparseMatrixAdapter& mat , GlobalEqVector& res);
|
||||
|
||||
void postSolve(GlobalEqVector& deltaX)
|
||||
{
|
||||
recoverWellSolutionAndUpdateWellState(deltaX);
|
||||
}
|
||||
|
||||
/////////////
|
||||
// </ eWoms auxiliary module stuff>
|
||||
/////////////
|
||||
|
||||
template <class Restarter>
|
||||
void deserialize(Restarter& /* res */)
|
||||
{
|
||||
// TODO (?)
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief This method writes the complete state of the well
|
||||
* to the harddisk.
|
||||
*/
|
||||
template <class Restarter>
|
||||
void serialize(Restarter& /* res*/)
|
||||
{
|
||||
// TODO (?)
|
||||
}
|
||||
|
||||
void beginEpisode()
|
||||
{
|
||||
beginReportStep(ebosSimulator_.episodeIndex());
|
||||
}
|
||||
|
||||
void beginTimeStep();
|
||||
|
||||
void beginIteration()
|
||||
{
|
||||
assemble(ebosSimulator_.model().newtonMethod().numIterations(),
|
||||
ebosSimulator_.timeStepSize());
|
||||
}
|
||||
|
||||
void endIteration()
|
||||
{ }
|
||||
|
||||
void endTimeStep()
|
||||
{
|
||||
timeStepSucceeded(ebosSimulator_.time(), ebosSimulator_.timeStepSize());
|
||||
}
|
||||
|
||||
void endEpisode()
|
||||
{
|
||||
endReportStep();
|
||||
}
|
||||
|
||||
template <class Context>
|
||||
void computeTotalRatesForDof(RateVector& rate,
|
||||
const Context& context,
|
||||
unsigned spaceIdx,
|
||||
unsigned timeIdx) const;
|
||||
|
||||
|
||||
using WellInterfacePtr = std::shared_ptr<WellInterface<TypeTag> >;
|
||||
WellInterfacePtr well(const std::string& wellName) const;
|
||||
|
||||
void initFromRestartFile(const RestartValue& restartValues);
|
||||
|
||||
Opm::data::Wells wellData() const
|
||||
{ return well_state_.report(phase_usage_, Opm::UgGridHelpers::globalCell(grid())); }
|
||||
|
||||
// substract Binv(D)rw from r;
|
||||
void apply( BVector& r) const;
|
||||
|
||||
// subtract B*inv(D)*C * x from A*x
|
||||
void apply(const BVector& x, BVector& Ax) const;
|
||||
|
||||
// apply well model with scaling of alpha
|
||||
void applyScaleAdd(const Scalar alpha, const BVector& x, BVector& Ax) const;
|
||||
|
||||
// Check if well equations is converged.
|
||||
ConvergenceReport getWellConvergence(const std::vector<Scalar>& B_avg) const;
|
||||
|
||||
// return all the wells.
|
||||
const WellCollection& wellCollection() const;
|
||||
// return non const reference to all the wells.
|
||||
WellCollection& wellCollection();
|
||||
|
||||
// return the internal well state, ignore the passed one.
|
||||
// Used by the legacy code to make it compatible with the legacy well models.
|
||||
const WellState& wellState(const WellState& well_state OPM_UNUSED) const;
|
||||
|
||||
// return the internal well state
|
||||
const WellState& wellState() const;
|
||||
|
||||
const SimulatorReport& lastReport() const;
|
||||
|
||||
void addWellContributions(Mat& mat) const
|
||||
{
|
||||
for ( const auto& well: well_container_ ) {
|
||||
well->addWellContributions(mat);
|
||||
}
|
||||
}
|
||||
|
||||
// called at the beginning of a report step
|
||||
void beginReportStep(const int time_step);
|
||||
|
||||
/// Return true if any well has a THP constraint.
|
||||
bool hasTHPConstraints() const;
|
||||
|
||||
/// Shut down any single well, but only if it is in prediction mode.
|
||||
/// Returns true if the well was actually found and shut.
|
||||
bool forceShutWellByNameIfPredictionMode(const std::string& wellname, const double simulation_time);
|
||||
|
||||
protected:
|
||||
|
||||
void extractLegacyPressure_(std::vector<double>& cellPressure) const
|
||||
{
|
||||
size_t nc = number_of_cells_;
|
||||
std::vector<double> cellPressures(nc, 0.0);
|
||||
ElementContext elemCtx(ebosSimulator_);
|
||||
const auto& gridView = ebosSimulator_.vanguard().gridView();
|
||||
const auto& elemEndIt = gridView.template end</*codim=*/0>();
|
||||
for (auto elemIt = gridView.template begin</*codim=*/0>();
|
||||
elemIt != elemEndIt;
|
||||
++elemIt)
|
||||
{
|
||||
const auto& elem = *elemIt;
|
||||
if (elem.partitionType() != Dune::InteriorEntity) {
|
||||
continue;
|
||||
}
|
||||
elemCtx.updatePrimaryStencil(elem);
|
||||
elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
|
||||
|
||||
const unsigned cellIdx = elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
|
||||
const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
|
||||
const auto& fs = intQuants.fluidState();
|
||||
|
||||
const double p = fs.pressure(FluidSystem::oilPhaseIdx).value();
|
||||
cellPressures[cellIdx] = p;
|
||||
}
|
||||
}
|
||||
|
||||
Simulator& ebosSimulator_;
|
||||
std::unique_ptr<WellsManager> wells_manager_;
|
||||
std::vector< const Well* > wells_ecl_;
|
||||
|
||||
bool wells_active_;
|
||||
|
||||
// a vector of all the wells.
|
||||
std::vector<WellInterfacePtr > well_container_;
|
||||
|
||||
// map from logically cartesian cell indices to compressed ones
|
||||
std::vector<int> cartesian_to_compressed_;
|
||||
|
||||
std::vector<bool> is_cell_perforated_;
|
||||
|
||||
// create the well container
|
||||
std::vector<WellInterfacePtr > createWellContainer(const int time_step, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
WellInterfacePtr createWellForWellTest(const std::string& well_name, const int report_step, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
WellState well_state_;
|
||||
WellState previous_well_state_;
|
||||
|
||||
const ModelParameters param_;
|
||||
bool terminal_output_;
|
||||
bool has_solvent_;
|
||||
bool has_polymer_;
|
||||
std::vector<int> pvt_region_idx_;
|
||||
PhaseUsage phase_usage_;
|
||||
size_t global_nc_;
|
||||
// the number of the cells in the local grid
|
||||
size_t number_of_cells_;
|
||||
double gravity_;
|
||||
std::vector<double> depth_;
|
||||
bool initial_step_;
|
||||
|
||||
std::unique_ptr<RateConverterType> rateConverter_;
|
||||
std::unique_ptr<VFPProperties<VFPInjProperties,VFPProdProperties>> vfp_properties_;
|
||||
|
||||
SimulatorReport last_report_;
|
||||
|
||||
WellTestState wellTestState_;
|
||||
|
||||
// used to better efficiency of calcuation
|
||||
mutable BVector scaleAddRes_;
|
||||
|
||||
const Wells* wells() const { return wells_manager_->c_wells(); }
|
||||
|
||||
const Grid& grid() const
|
||||
{ return ebosSimulator_.vanguard().grid(); }
|
||||
|
||||
const EclipseState& eclState() const
|
||||
{ return ebosSimulator_.vanguard().eclState(); }
|
||||
|
||||
const Schedule& schedule() const
|
||||
{ return ebosSimulator_.vanguard().schedule(); }
|
||||
|
||||
// compute the well fluxes and assemble them in to the reservoir equations as source terms
|
||||
// and in the well equations.
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt);
|
||||
|
||||
// called at the end of a time step
|
||||
void timeStepSucceeded(const double& simulationTime, const double dt);
|
||||
|
||||
// called at the end of a report step
|
||||
void endReportStep();
|
||||
|
||||
// using the solution x to recover the solution xw for wells and applying
|
||||
// xw to update Well State
|
||||
void recoverWellSolutionAndUpdateWellState(const BVector& x);
|
||||
|
||||
void updateWellControls(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void updateGroupControls(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// setting the well_solutions_ based on well_state.
|
||||
void updatePrimaryVariables(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void setupCartesianToCompressed_(const int* global_cell, int number_of_cells);
|
||||
|
||||
void computeRepRadiusPerfLength(const Grid& grid, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
|
||||
void computeAverageFormationFactor(std::vector<Scalar>& B_avg) const;
|
||||
|
||||
void applyVREPGroupControl();
|
||||
|
||||
void computeWellVoidageRates(std::vector<double>& well_voidage_rates,
|
||||
std::vector<double>& voidage_conversion_coeffs) const;
|
||||
|
||||
// Calculating well potentials for each well
|
||||
void computeWellPotentials(std::vector<double>& well_potentials, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
const std::vector<double>& wellPerfEfficiencyFactors() const;
|
||||
|
||||
void calculateEfficiencyFactors();
|
||||
|
||||
// it should be able to go to prepareTimeStep(), however, the updateWellControls() and initPrimaryVariablesEvaluation()
|
||||
// makes it a little more difficult. unless we introduce if (iterationIdx != 0) to avoid doing the above functions
|
||||
// twice at the beginning of the time step
|
||||
/// Calculating the explict quantities used in the well calculation. By explicit, we mean they are cacluated
|
||||
/// at the beginning of the time step and no derivatives are included in these quantities
|
||||
void calculateExplicitQuantities(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
SimulatorReport solveWellEq(const std::vector<Scalar>& B_avg, const double dt, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void initPrimaryVariablesEvaluation() const;
|
||||
|
||||
// The number of components in the model.
|
||||
int numComponents() const;
|
||||
|
||||
int numWells() const;
|
||||
|
||||
int numPhases() const;
|
||||
|
||||
void resetWellControlFromState() const;
|
||||
|
||||
void assembleWellEq(const std::vector<Scalar>& B_avg, const double dt, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// some preparation work, mostly related to group control and RESV,
|
||||
// at the beginning of each time step (Not report step)
|
||||
void prepareTimeStep(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void prepareGroupControl(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void computeRESV(const std::size_t step, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void extractLegacyCellPvtRegionIndex_();
|
||||
|
||||
void extractLegacyDepth_();
|
||||
|
||||
/// return true if wells are available in the reservoir
|
||||
bool wellsActive() const;
|
||||
|
||||
void setWellsActive(const bool wells_active);
|
||||
|
||||
/// return true if wells are available on this process
|
||||
bool localWellsActive() const;
|
||||
|
||||
/// upate the wellTestState related to economic limits
|
||||
void updateWellTestState(const double& simulationTime, WellTestState& wellTestState) const;
|
||||
|
||||
void updatePerforationIntensiveQuantities();
|
||||
|
||||
void wellTesting(const int timeStepIdx, const double simulationTime, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// convert well data from opm-common to well state from opm-core
|
||||
void wellsToState( const data::Wells& wells,
|
||||
const PhaseUsage& phases,
|
||||
const bool handle_ms_well,
|
||||
const int report_step,
|
||||
WellStateFullyImplicitBlackoil& state ) const;
|
||||
|
||||
// whether there exists any multisegment well open on this process
|
||||
bool anyMSWellOpenLocal(const Wells* wells, const int report_step) const;
|
||||
|
||||
const Well* getWellEcl(const std::string& well_name) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#include "BlackoilWellModel_impl.hpp"
|
||||
#endif
|
||||
1805
opm/simulators/wells/BlackoilWellModel_impl.hpp
Normal file
1805
opm/simulators/wells/BlackoilWellModel_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
204
opm/simulators/wells/MSWellHelpers.hpp
Normal file
204
opm/simulators/wells/MSWellHelpers.hpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2017 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_MSWELLHELPERS_HEADER_INCLUDED
|
||||
#define OPM_MSWELLHELPERS_HEADER_INCLUDED
|
||||
|
||||
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <dune/istl/solvers.hh>
|
||||
#if HAVE_UMFPACK
|
||||
#include <dune/istl/umfpack.hh>
|
||||
#endif // HAVE_UMFPACK
|
||||
#include <cmath>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
namespace mswellhelpers
|
||||
{
|
||||
// obtain y = D^-1 * x with a direct solver
|
||||
template <typename MatrixType, typename VectorType>
|
||||
VectorType
|
||||
invDXDirect(const MatrixType& D, VectorType x)
|
||||
{
|
||||
#if HAVE_UMFPACK
|
||||
VectorType y(x.size());
|
||||
y = 0.;
|
||||
|
||||
Dune::UMFPack<MatrixType> linsolver(D, 0);
|
||||
|
||||
// Object storing some statistics about the solving process
|
||||
Dune::InverseOperatorResult res;
|
||||
|
||||
// Solve
|
||||
linsolver.apply(y, x, res);
|
||||
|
||||
// Checking if there is any inf or nan in y
|
||||
// it will be the solution before we find a way to catch the singularity of the matrix
|
||||
for (size_t i_block = 0; i_block < y.size(); ++i_block) {
|
||||
for (size_t i_elem = 0; i_elem < y[i_block].size(); ++i_elem) {
|
||||
if (std::isinf(y[i_block][i_elem]) || std::isnan(y[i_block][i_elem]) ) {
|
||||
OPM_THROW(Opm::NumericalIssue, "nan or inf value found in invDXDirect due to singular matrix");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return y;
|
||||
#else
|
||||
// this is not thread safe
|
||||
OPM_THROW(std::runtime_error, "Cannot use invDXDirect() without UMFPACK. "
|
||||
"Reconfigure opm-simulator with SuiteSparse/UMFPACK support and recompile.");
|
||||
#endif // HAVE_UMFPACK
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// obtain y = D^-1 * x with a BICSSTAB iterative solver
|
||||
template <typename MatrixType, typename VectorType>
|
||||
VectorType
|
||||
invDX(const MatrixType& D, VectorType x, Opm::DeferredLogger& deferred_logger)
|
||||
{
|
||||
// the function will change the value of x, so we should not use reference of x here.
|
||||
|
||||
// TODO: store some of the following information to avoid to call it again and again for
|
||||
// efficiency improvement.
|
||||
// Bassically, only the solve / apply step is different.
|
||||
|
||||
VectorType y(x.size());
|
||||
y = 0.;
|
||||
|
||||
Dune::MatrixAdapter<MatrixType, VectorType, VectorType> linearOperator(D);
|
||||
|
||||
// Sequential incomplete LU decomposition as the preconditioner
|
||||
Dune::SeqILU0<MatrixType, VectorType, VectorType> preconditioner(D, 1.0);
|
||||
// Dune::SeqILUn<MatrixType, VectorType, VectorType> preconditioner(D, 1, 0.92);
|
||||
// Dune::SeqGS<MatrixType, VectorType, VectorType> preconditioner(D, 1, 1);
|
||||
// Dune::SeqJac<MatrixType, VectorType, VectorType> preconditioner(D, 1, 1);
|
||||
|
||||
// Preconditioned BICGSTAB solver
|
||||
Dune::BiCGSTABSolver<VectorType> linsolver(linearOperator,
|
||||
preconditioner,
|
||||
1.e-8, // desired residual reduction factor
|
||||
250, // maximum number of iterations
|
||||
0); // verbosity of the solver */
|
||||
|
||||
// Object storing some statistics about the solving process
|
||||
Dune::InverseOperatorResult res;
|
||||
|
||||
// Solve
|
||||
linsolver.apply(y, x, res);
|
||||
|
||||
if ( !res.converged ) {
|
||||
OPM_DEFLOG_THROW(Opm::NumericalIssue, "the invDX does not get converged! ", deferred_logger);
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline double haalandFormular(const double re, const double diameter, const double roughness)
|
||||
{
|
||||
const double value = -3.6 * std::log10(6.9 / re + std::pow(roughness / (3.7 * diameter), 10. / 9.) );
|
||||
|
||||
// sqrt(1/f) should be non-positive
|
||||
assert(value >= 0.0);
|
||||
|
||||
return 1. / (value * value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline double calculateFrictionFactor(const double area, const double diameter,
|
||||
const double w, const double roughness, const double mu)
|
||||
{
|
||||
|
||||
double f = 0.;
|
||||
// Reynolds number
|
||||
const double re = std::abs(diameter * w / (area * mu));
|
||||
|
||||
if ( re == 0.0 ) {
|
||||
// make sure it is because the mass rate is zero
|
||||
assert(w == 0.);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
const double re_value1 = 200.;
|
||||
const double re_value2 = 4000.;
|
||||
|
||||
if (re < re_value1) {
|
||||
f = 16. / re;
|
||||
} else if (re > re_value2){
|
||||
f = haalandFormular(re, diameter, roughness);
|
||||
} else { // in between
|
||||
const double f1 = 16. / re_value1;
|
||||
const double f2 = haalandFormular(re_value2, diameter, roughness);
|
||||
|
||||
f = (f2 - f1) / (re_value2 - re_value1) * (re - re_value1) + f1;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// calculating the friction pressure loss
|
||||
// l is the segment length
|
||||
// area is the segment cross area
|
||||
// diameter is the segment inner diameter
|
||||
// w is mass flow rate through the segment
|
||||
// density is density
|
||||
// roughness is the absolute roughness
|
||||
// mu is the average phase viscosity
|
||||
template <typename ValueType>
|
||||
ValueType frictionPressureLoss(const double l, const double diameter, const double area, const double roughness,
|
||||
const ValueType& density, const ValueType& w, const ValueType& mu)
|
||||
{
|
||||
const double f = calculateFrictionFactor(area, diameter, w.value(), roughness, mu.value());
|
||||
// \Note: a factor of 2 needs to be here based on the dimensional analysis
|
||||
return 2. * f * l * w * w / (area * area * diameter * density);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename ValueType>
|
||||
ValueType velocityHead(const double area, const ValueType& mass_rate, const ValueType& density)
|
||||
{
|
||||
return (0.5 * mass_rate * mass_rate / (area * area * density));
|
||||
}
|
||||
|
||||
|
||||
} // namespace mswellhelpers
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
390
opm/simulators/wells/MultisegmentWell.hpp
Normal file
390
opm/simulators/wells/MultisegmentWell.hpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2017 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_MULTISEGMENTWELL_HEADER_INCLUDED
|
||||
#define OPM_MULTISEGMENTWELL_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
template<typename TypeTag>
|
||||
class MultisegmentWell: public WellInterface<TypeTag>
|
||||
{
|
||||
public:
|
||||
typedef WellInterface<TypeTag> Base;
|
||||
|
||||
using typename Base::WellState;
|
||||
using typename Base::Simulator;
|
||||
using typename Base::IntensiveQuantities;
|
||||
using typename Base::FluidSystem;
|
||||
using typename Base::ModelParameters;
|
||||
using typename Base::MaterialLaw;
|
||||
using typename Base::Indices;
|
||||
using typename Base::RateConverterType;
|
||||
|
||||
|
||||
/// the number of reservior equations
|
||||
using Base::numEq;
|
||||
|
||||
using Base::has_solvent;
|
||||
using Base::has_polymer;
|
||||
using Base::Water;
|
||||
using Base::Oil;
|
||||
using Base::Gas;
|
||||
|
||||
// TODO: for now, not considering the polymer, solvent and so on to simplify the development process.
|
||||
|
||||
// TODO: we need to have order for the primary variables and also the order for the well equations.
|
||||
// sometimes, they are similar, while sometimes, they can have very different forms.
|
||||
|
||||
// TODO: the following system looks not rather flexible. Looking into all kinds of possibilities
|
||||
// TODO: gas is always there? how about oil water case?
|
||||
// Is it gas oil two phase case?
|
||||
static const bool gasoil = numEq == 2 && (Indices::compositionSwitchIdx >= 0);
|
||||
static const int GTotal = 0;
|
||||
static const int WFrac = gasoil? -1000: 1;
|
||||
static const int GFrac = gasoil? 1 : 2;
|
||||
static const int SPres = gasoil? 2 : 3;
|
||||
|
||||
/// the number of well equations // TODO: it should have a more general strategy for it
|
||||
static const int numWellEq = GET_PROP_VALUE(TypeTag, EnablePolymer)? numEq : numEq + 1;
|
||||
|
||||
using typename Base::Scalar;
|
||||
|
||||
/// the matrix and vector types for the reservoir
|
||||
using typename Base::Mat;
|
||||
using typename Base::BVector;
|
||||
using typename Base::Eval;
|
||||
|
||||
// sparsity pattern for the matrices
|
||||
// [A C^T [x = [ res
|
||||
// B D ] x_well] res_well]
|
||||
|
||||
// the vector type for the res_well and x_well
|
||||
typedef Dune::FieldVector<Scalar, numWellEq> VectorBlockWellType;
|
||||
typedef Dune::BlockVector<VectorBlockWellType> BVectorWell;
|
||||
|
||||
// the matrix type for the diagonal matrix D
|
||||
typedef Dune::FieldMatrix<Scalar, numWellEq, numWellEq > DiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix <DiagMatrixBlockWellType> DiagMatWell;
|
||||
|
||||
// the matrix type for the non-diagonal matrix B and C^T
|
||||
typedef Dune::FieldMatrix<Scalar, numWellEq, numEq> OffDiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix<OffDiagMatrixBlockWellType> OffDiagMatWell;
|
||||
|
||||
// TODO: for more efficient implementation, we should have EvalReservoir, EvalWell, and EvalRerservoirAndWell
|
||||
// EvalR (Eval), EvalW, EvalRW
|
||||
// TODO: for now, we only use one type to save some implementation efforts, while improve later.
|
||||
typedef DenseAd::Evaluation<double, /*size=*/numEq + numWellEq> EvalWell;
|
||||
|
||||
MultisegmentWell(const Well* well, const int time_step, const Wells* wells,
|
||||
const ModelParameters& param,
|
||||
const RateConverterType& rate_converter,
|
||||
const int pvtRegionIdx,
|
||||
const int num_components);
|
||||
|
||||
virtual void init(const PhaseUsage* phase_usage_arg,
|
||||
const std::vector<double>& depth_arg,
|
||||
const double gravity_arg,
|
||||
const int num_cells) override;
|
||||
|
||||
|
||||
virtual void initPrimaryVariablesEvaluation() const override;
|
||||
|
||||
virtual void assembleWellEq(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
/// updating the well state based the current control mode
|
||||
virtual void updateWellStateWithTarget(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// check whether the well equations get converged for this well
|
||||
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// Ax = Ax - C D^-1 B x
|
||||
virtual void apply(const BVector& x, BVector& Ax) const override;
|
||||
/// r = r - C D^-1 Rw
|
||||
virtual void apply(BVector& r) const override;
|
||||
|
||||
/// using the solution x to recover the solution xw for wells and applying
|
||||
/// xw to update Well State
|
||||
virtual void recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// computing the well potentials for group control
|
||||
virtual void computeWellPotentials(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& well_potentials,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void updatePrimaryVariables(const WellState& well_state, Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) override; // const?
|
||||
|
||||
virtual void calculateExplicitQuantities(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override; // should be const?
|
||||
|
||||
/// number of segments for this well
|
||||
/// int number_of_segments_;
|
||||
int numberOfSegments() const;
|
||||
|
||||
int numberOfPerforations() const;
|
||||
|
||||
protected:
|
||||
int number_segments_;
|
||||
|
||||
// components of the pressure drop to be included
|
||||
WellSegment::CompPressureDropEnum compPressureDrop() const;
|
||||
// multi-phase flow model
|
||||
WellSegment::MultiPhaseModelEnum multiphaseModel() const;
|
||||
|
||||
// get the WellSegments from the well_ecl_
|
||||
const WellSegments& segmentSet() const;
|
||||
|
||||
// protected member variables from the Base class
|
||||
using Base::well_ecl_;
|
||||
using Base::number_of_perforations_; // TODO: can use well_ecl_?
|
||||
using Base::current_step_;
|
||||
using Base::index_of_well_;
|
||||
using Base::number_of_phases_;
|
||||
|
||||
// TODO: the current implementation really relies on the order of the
|
||||
// perforation does not change from the parser to Wells structure.
|
||||
using Base::well_cells_;
|
||||
using Base::param_;
|
||||
using Base::well_index_;
|
||||
using Base::well_type_;
|
||||
using Base::first_perf_;
|
||||
using Base::saturation_table_number_;
|
||||
using Base::well_efficiency_factor_;
|
||||
using Base::gravity_;
|
||||
using Base::well_controls_;
|
||||
using Base::perf_depth_;
|
||||
using Base::num_components_;
|
||||
using Base::connectionRates_;
|
||||
|
||||
// protected functions from the Base class
|
||||
using Base::phaseUsage;
|
||||
using Base::name;
|
||||
using Base::flowPhaseToEbosCompIdx;
|
||||
using Base::ebosCompIdxToFlowCompIdx;
|
||||
using Base::getAllowCrossFlow;
|
||||
using Base::scalingFactor;
|
||||
|
||||
// TODO: trying to use the information from the Well opm-parser as much
|
||||
// as possible, it will possibly be re-implemented later for efficiency reason.
|
||||
|
||||
// the completions that is related to each segment
|
||||
// the completions's ids are their index in the vector well_index_, well_cell_
|
||||
// This is also assuming the order of the completions in Well is the same with
|
||||
// the order of the completions in wells.
|
||||
// it is for convinience reason. we can just calcuate the inforation for segment once then using it for all the perofrations
|
||||
// belonging to this segment
|
||||
std::vector<std::vector<int> > segment_perforations_;
|
||||
|
||||
// the inlet segments for each segment. It is for convinience and efficiency reason
|
||||
std::vector<std::vector<int> > segment_inlets_;
|
||||
|
||||
// segment number is an ID of the segment, it is specified in the deck
|
||||
// get the loation of the segment with a segment number in the segmentSet
|
||||
int segmentNumberToIndex(const int segment_number) const;
|
||||
|
||||
// TODO, the following should go to a class for computing purpose
|
||||
// two off-diagonal matrices
|
||||
mutable OffDiagMatWell duneB_;
|
||||
mutable OffDiagMatWell duneC_;
|
||||
// diagonal matrix for the well
|
||||
mutable DiagMatWell duneD_;
|
||||
|
||||
// residuals of the well equations
|
||||
mutable BVectorWell resWell_;
|
||||
|
||||
// the values for the primary varibles
|
||||
// based on different solutioin strategies, the wells can have different primary variables
|
||||
mutable std::vector<std::array<double, numWellEq> > primary_variables_;
|
||||
|
||||
// the Evaluation for the well primary variables, which contain derivativles and are used in AD calculation
|
||||
mutable std::vector<std::array<EvalWell, numWellEq> > primary_variables_evaluation_;
|
||||
|
||||
// depth difference between perforations and the perforated grid cells
|
||||
std::vector<double> cell_perforation_depth_diffs_;
|
||||
// pressure correction due to the different depth of the perforation and
|
||||
// center depth of the grid block
|
||||
std::vector<double> cell_perforation_pressure_diffs_;
|
||||
|
||||
// depth difference between the segment and the peforation
|
||||
// or in another way, the depth difference between the perforation and
|
||||
// the segment the perforation belongs to
|
||||
std::vector<double> perforation_segment_depth_diffs_;
|
||||
|
||||
// the intial amount of fluids in each segment under surface condition
|
||||
std::vector<std::vector<double> > segment_fluid_initial_;
|
||||
|
||||
// the densities of segment fluids
|
||||
// we should not have this member variable
|
||||
std::vector<EvalWell> segment_densities_;
|
||||
|
||||
// the viscosity of the segments
|
||||
std::vector<EvalWell> segment_viscosities_;
|
||||
|
||||
// the mass rate of the segments
|
||||
std::vector<EvalWell> segment_mass_rates_;
|
||||
|
||||
std::vector<double> segment_depth_diffs_;
|
||||
|
||||
void initMatrixAndVectors(const int num_cells) const;
|
||||
|
||||
// protected functions
|
||||
// EvalWell getBhp(); this one should be something similar to getSegmentPressure();
|
||||
// EvalWell getQs(); this one should be something similar to getSegmentRates()
|
||||
// EValWell wellVolumeFractionScaled, wellVolumeFraction, wellSurfaceVolumeFraction ... these should have different names, and probably will be needed.
|
||||
// bool crossFlowAllowed(const Simulator& ebosSimulator) const; probably will be needed
|
||||
// xw = inv(D)*(rw - C*x)
|
||||
void recoverSolutionWell(const BVector& x, BVectorWell& xw) const;
|
||||
|
||||
// updating the well_state based on well solution dwells
|
||||
void updateWellState(const BVectorWell& dwells,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger,
|
||||
const double relaxation_factor=1.0) const;
|
||||
|
||||
|
||||
// initialize the segment rates with well rates
|
||||
// when there is no more accurate way to initialize the segment rates, we initialize
|
||||
// the segment rates based on well rates with a simple strategy
|
||||
void initSegmentRatesWithWellRates(WellState& well_state) const;
|
||||
|
||||
// computing the accumulation term for later use in well mass equations
|
||||
void computeInitialSegmentFluids(const Simulator& ebos_simulator);
|
||||
|
||||
// compute the pressure difference between the perforation and cell center
|
||||
void computePerfCellPressDiffs(const Simulator& ebosSimulator);
|
||||
|
||||
// fraction value of the primary variables
|
||||
// should we just use member variables to store them instead of calculating them again and again
|
||||
EvalWell volumeFraction(const int seg, const unsigned comp_idx) const;
|
||||
|
||||
// F_p / g_p, the basic usage of this value is because Q_p = G_t * F_p / G_p
|
||||
EvalWell volumeFractionScaled(const int seg, const int comp_idx) const;
|
||||
|
||||
// basically Q_p / \sigma_p Q_p
|
||||
EvalWell surfaceVolumeFraction(const int seg, const int comp_idx) const;
|
||||
|
||||
void computePerfRatePressure(const IntensiveQuantities& int_quants,
|
||||
const std::vector<EvalWell>& mob_perfcells,
|
||||
const int seg,
|
||||
const int perf,
|
||||
const EvalWell& segment_pressure,
|
||||
const bool& allow_cf,
|
||||
std::vector<EvalWell>& cq_s,
|
||||
EvalWell& perf_press,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// convert a Eval from reservoir to contain the derivative related to wells
|
||||
EvalWell extendEval(const Eval& in) const;
|
||||
|
||||
// compute the fluid properties, such as densities, viscosities, and so on, in the segments
|
||||
// They will be treated implicitly, so they need to be of Evaluation type
|
||||
void computeSegmentFluidProperties(const Simulator& ebosSimulator);
|
||||
|
||||
EvalWell getSegmentPressure(const int seg) const;
|
||||
|
||||
EvalWell getSegmentRate(const int seg, const int comp_idx) const;
|
||||
|
||||
EvalWell getSegmentRateUpwinding(const int seg, const int comp_idx, const bool upwinding, int& seg_upwind) const;
|
||||
|
||||
EvalWell getSegmentGTotal(const int seg) const;
|
||||
|
||||
// get the mobility for specific perforation
|
||||
void getMobility(const Simulator& ebosSimulator,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& mob) const;
|
||||
|
||||
void assembleControlEq(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void assemblePressureEq(const int seg) const;
|
||||
|
||||
// hytrostatic pressure loss
|
||||
EvalWell getHydroPressureLoss(const int seg) const;
|
||||
|
||||
// frictinal pressure loss
|
||||
EvalWell getFrictionPressureLoss(const int seg) const;
|
||||
|
||||
void handleAccelerationPressureLoss(const int seg) const;
|
||||
|
||||
// handling the overshooting and undershooting of the fractions
|
||||
void processFractions(const int seg) const;
|
||||
|
||||
// checking the operability of the well based on current reservoir condition
|
||||
// it is not implemented for multisegment well yet
|
||||
virtual void checkWellOperability(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
void updateWellStateFromPrimaryVariables(WellState& well_state) const;
|
||||
|
||||
bool frictionalPressureLossConsidered() const;
|
||||
|
||||
bool accelerationalPressureLossConsidered() const;
|
||||
|
||||
// TODO: try to make ebosSimulator const, as it should be
|
||||
void iterateWellEquations(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void assembleWellEqWithoutIteration(const Simulator& ebosSimulator,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
virtual void wellTestingPhysical(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
WellState& well_state, WellTestState& welltest_state, Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void updateWaterThroughput(const double dt, WellState& well_state) const override;
|
||||
|
||||
EvalWell getSegmentSurfaceVolume(const Simulator& ebos_simulator, const int seg_idx) const;
|
||||
|
||||
std::vector<Scalar> getWellResiduals(const std::vector<Scalar>& B_avg) const;
|
||||
|
||||
void detectOscillations(const std::vector<double>& measure_history,
|
||||
const int it, bool& oscillate, bool& stagnate) const;
|
||||
|
||||
double getResidualMeasureValue(const std::vector<double>& residuals) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "MultisegmentWell_impl.hpp"
|
||||
|
||||
#endif // OPM_MULTISEGMENTWELL_HEADER_INCLUDED
|
||||
2275
opm/simulators/wells/MultisegmentWell_impl.hpp
Normal file
2275
opm/simulators/wells/MultisegmentWell_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
448
opm/simulators/wells/StandardWell.hpp
Normal file
448
opm/simulators/wells/StandardWell.hpp
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2017 Statoil ASA.
|
||||
Copyright 2016 - 2017 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_STANDARDWELL_HEADER_INCLUDED
|
||||
#define OPM_STANDARDWELL_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
#include <opm/simulators/linalg/ISTLSolverEbos.hpp>
|
||||
#include <opm/autodiff/RateConverter.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
template<typename TypeTag>
|
||||
class StandardWell: public WellInterface<TypeTag>
|
||||
{
|
||||
|
||||
public:
|
||||
typedef WellInterface<TypeTag> Base;
|
||||
|
||||
// TODO: some functions working with AD variables handles only with values (double) without
|
||||
// dealing with derivatives. It can be beneficial to make functions can work with either AD or scalar value.
|
||||
// And also, it can also be beneficial to make these functions hanle different types of AD variables.
|
||||
using typename Base::Simulator;
|
||||
using typename Base::WellState;
|
||||
using typename Base::IntensiveQuantities;
|
||||
using typename Base::FluidSystem;
|
||||
using typename Base::MaterialLaw;
|
||||
using typename Base::ModelParameters;
|
||||
using typename Base::Indices;
|
||||
using typename Base::PolymerModule;
|
||||
using typename Base::RateConverterType;
|
||||
|
||||
using Base::numEq;
|
||||
|
||||
using Base::has_solvent;
|
||||
using Base::has_polymer;
|
||||
using Base::has_energy;
|
||||
|
||||
// polymer concentration and temperature are already known by the well, so
|
||||
// polymer and energy conservation do not need to be considered explicitly
|
||||
static const int numPolymerEq = Indices::numPolymers;
|
||||
static const int numEnergyEq = Indices::numEnergy;
|
||||
|
||||
// number of the conservation equations
|
||||
static const int numWellConservationEq = numEq - numPolymerEq - numEnergyEq;
|
||||
// number of the well control equations
|
||||
static const int numWellControlEq = 1;
|
||||
// number of the well equations that will always be used
|
||||
// based on the solution strategy, there might be other well equations be introduced
|
||||
static const int numStaticWellEq = numWellConservationEq + numWellControlEq;
|
||||
|
||||
// the positions of the primary variables for StandardWell
|
||||
// the first one is the weighted total rate (WQ_t), the second and the third ones are F_w and F_g,
|
||||
// which represent the fraction of Water and Gas based on the weighted total rate, the last one is BHP.
|
||||
// correspondingly, we have four well equations for blackoil model, the first three are mass
|
||||
// converstation equations, and the last one is the well control equation.
|
||||
// primary variables related to other components, will be before the Bhp and after F_g.
|
||||
// well control equation is always the last well equation.
|
||||
// TODO: in the current implementation, we use the well rate as the first primary variables for injectors,
|
||||
// instead of G_t.
|
||||
static const bool gasoil = numEq == 2 && (Indices::compositionSwitchIdx >= 0);
|
||||
static const int WQTotal = 0;
|
||||
static const int WFrac = gasoil? -1000: 1;
|
||||
static const int GFrac = gasoil? 1: 2;
|
||||
static const int SFrac = !has_solvent ? -1000 : 3;
|
||||
// the index for Bhp in primary variables and also the index of well control equation
|
||||
// they both will be the last one in their respective system.
|
||||
// TODO: we should have indices for the well equations and well primary variables separately
|
||||
static const int Bhp = numStaticWellEq - numWellControlEq;
|
||||
|
||||
// total number of the well equations and primary variables
|
||||
// for StandardWell, no extra well equations will be used.
|
||||
static const int numWellEq = numStaticWellEq;
|
||||
|
||||
using typename Base::Scalar;
|
||||
|
||||
|
||||
using Base::name;
|
||||
using Base::Water;
|
||||
using Base::Oil;
|
||||
using Base::Gas;
|
||||
|
||||
using typename Base::Mat;
|
||||
using typename Base::BVector;
|
||||
using typename Base::Eval;
|
||||
|
||||
// sparsity pattern for the matrices
|
||||
//[A C^T [x = [ res
|
||||
// B D ] x_well] res_well]
|
||||
|
||||
// the vector type for the res_well and x_well
|
||||
typedef Dune::FieldVector<Scalar, numWellEq> VectorBlockWellType;
|
||||
typedef Dune::BlockVector<VectorBlockWellType> BVectorWell;
|
||||
|
||||
// the matrix type for the diagonal matrix D
|
||||
typedef Dune::FieldMatrix<Scalar, numWellEq, numWellEq > DiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix <DiagMatrixBlockWellType> DiagMatWell;
|
||||
|
||||
// the matrix type for the non-diagonal matrix B and C^T
|
||||
typedef Dune::FieldMatrix<Scalar, numWellEq, numEq> OffDiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix<OffDiagMatrixBlockWellType> OffDiagMatWell;
|
||||
|
||||
typedef DenseAd::Evaluation<double, /*size=*/numEq + numWellEq> EvalWell;
|
||||
|
||||
using Base::contiSolventEqIdx;
|
||||
using Base::contiPolymerEqIdx;
|
||||
static const int contiEnergyEqIdx = Indices::contiEnergyEqIdx;
|
||||
|
||||
StandardWell(const Well* well, const int time_step, const Wells* wells,
|
||||
const ModelParameters& param,
|
||||
const RateConverterType& rate_converter,
|
||||
const int pvtRegionIdx,
|
||||
const int num_components);
|
||||
|
||||
virtual void init(const PhaseUsage* phase_usage_arg,
|
||||
const std::vector<double>& depth_arg,
|
||||
const double gravity_arg,
|
||||
const int num_cells) override;
|
||||
|
||||
|
||||
virtual void initPrimaryVariablesEvaluation() const override;
|
||||
|
||||
virtual void assembleWellEq(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void updateWellStateWithTarget(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// check whether the well equations get converged for this well
|
||||
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// Ax = Ax - C D^-1 B x
|
||||
virtual void apply(const BVector& x, BVector& Ax) const override;
|
||||
/// r = r - C D^-1 Rw
|
||||
virtual void apply(BVector& r) const override;
|
||||
|
||||
/// using the solution x to recover the solution xw for wells and applying
|
||||
/// xw to update Well State
|
||||
virtual void recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// computing the well potentials for group control
|
||||
virtual void computeWellPotentials(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& well_potentials,
|
||||
Opm::DeferredLogger& deferred_logger) /* const */ override;
|
||||
|
||||
virtual void updatePrimaryVariables(const WellState& well_state, Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void calculateExplicitQuantities(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override; // should be const?
|
||||
|
||||
virtual void addWellContributions(Mat& mat) const override;
|
||||
|
||||
/// \brief Wether the Jacobian will also have well contributions in it.
|
||||
virtual bool jacobianContainsWellContributions() const override
|
||||
{
|
||||
return param_.matrix_add_well_contributions_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// protected functions from the Base class
|
||||
using Base::getAllowCrossFlow;
|
||||
using Base::phaseUsage;
|
||||
using Base::flowPhaseToEbosCompIdx;
|
||||
using Base::ebosCompIdxToFlowCompIdx;
|
||||
using Base::wsolvent;
|
||||
using Base::wpolymer;
|
||||
using Base::wellHasTHPConstraints;
|
||||
using Base::mostStrictBhpFromBhpLimits;
|
||||
using Base::scalingFactor;
|
||||
using Base::scaleProductivityIndex;
|
||||
|
||||
// protected member variables from the Base class
|
||||
using Base::current_step_;
|
||||
using Base::well_ecl_;
|
||||
using Base::vfp_properties_;
|
||||
using Base::gravity_;
|
||||
using Base::param_;
|
||||
using Base::well_efficiency_factor_;
|
||||
using Base::first_perf_;
|
||||
using Base::ref_depth_;
|
||||
using Base::perf_depth_;
|
||||
using Base::well_cells_;
|
||||
using Base::number_of_perforations_;
|
||||
using Base::number_of_phases_;
|
||||
using Base::saturation_table_number_;
|
||||
using Base::comp_frac_;
|
||||
using Base::well_index_;
|
||||
using Base::index_of_well_;
|
||||
using Base::well_controls_;
|
||||
using Base::well_type_;
|
||||
using Base::num_components_;
|
||||
using Base::connectionRates_;
|
||||
|
||||
using Base::perf_rep_radius_;
|
||||
using Base::perf_length_;
|
||||
using Base::bore_diameters_;
|
||||
|
||||
// densities of the fluid in each perforation
|
||||
std::vector<double> perf_densities_;
|
||||
// pressure drop between different perforations
|
||||
std::vector<double> perf_pressure_diffs_;
|
||||
|
||||
// residuals of the well equations
|
||||
BVectorWell resWell_;
|
||||
|
||||
// two off-diagonal matrices
|
||||
OffDiagMatWell duneB_;
|
||||
OffDiagMatWell duneC_;
|
||||
// diagonal matrix for the well
|
||||
DiagMatWell invDuneD_;
|
||||
|
||||
// several vector used in the matrix calculation
|
||||
mutable BVectorWell Bx_;
|
||||
mutable BVectorWell invDrw_;
|
||||
|
||||
// the values for the primary varibles
|
||||
// based on different solutioin strategies, the wells can have different primary variables
|
||||
mutable std::vector<double> primary_variables_;
|
||||
|
||||
// the Evaluation for the well primary variables, which contain derivativles and are used in AD calculation
|
||||
mutable std::vector<EvalWell> primary_variables_evaluation_;
|
||||
|
||||
// the saturations in the well bore under surface conditions at the beginning of the time step
|
||||
std::vector<double> F0_;
|
||||
|
||||
// the vectors used to describe the inflow performance relationship (IPR)
|
||||
// Q = IPR_A - BHP * IPR_B
|
||||
// TODO: it minght need to go to WellInterface, let us implement it in StandardWell first
|
||||
// it is only updated and used for producers for now
|
||||
mutable std::vector<double> ipr_a_;
|
||||
mutable std::vector<double> ipr_b_;
|
||||
|
||||
const EvalWell& getBhp() const;
|
||||
|
||||
EvalWell getQs(const int comp_idx) const;
|
||||
|
||||
const EvalWell& getWQTotal() const;
|
||||
|
||||
EvalWell wellVolumeFractionScaled(const int phase) const;
|
||||
|
||||
EvalWell wellVolumeFraction(const unsigned compIdx) const;
|
||||
|
||||
EvalWell wellSurfaceVolumeFraction(const int phase) const;
|
||||
|
||||
EvalWell extendEval(const Eval& in) const;
|
||||
|
||||
// xw = inv(D)*(rw - C*x)
|
||||
void recoverSolutionWell(const BVector& x, BVectorWell& xw) const;
|
||||
|
||||
// updating the well_state based on well solution dwells
|
||||
void updateWellState(const BVectorWell& dwells,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// calculate the properties for the well connections
|
||||
// to calulate the pressure difference between well connections.
|
||||
void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& b_perf,
|
||||
std::vector<double>& rsmax_perf,
|
||||
std::vector<double>& rvmax_perf,
|
||||
std::vector<double>& surf_dens_perf) const;
|
||||
|
||||
// TODO: not total sure whether it is a good idea to put this function here
|
||||
// the major reason to put here is to avoid the usage of Wells struct
|
||||
void computeConnectionDensities(const std::vector<double>& perfComponentRates,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf);
|
||||
|
||||
void computeConnectionPressureDelta();
|
||||
|
||||
void computeWellConnectionDensitesPressures(const WellState& well_state,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf);
|
||||
|
||||
// computing the accumulation term for later use in well mass equations
|
||||
void computeAccumWell();
|
||||
|
||||
void computeWellConnectionPressures(const Simulator& ebosSimulator,
|
||||
const WellState& well_state);
|
||||
|
||||
void computePerfRate(const IntensiveQuantities& intQuants,
|
||||
const std::vector<EvalWell>& mob,
|
||||
const EvalWell& bhp,
|
||||
const double Tw,
|
||||
const int perf,
|
||||
const bool allow_cf,
|
||||
std::vector<EvalWell>& cq_s,
|
||||
double& perf_dis_gas_rate,
|
||||
double& perf_vap_oil_rate,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// TODO: maybe we should provide a light version of computePerfRate, which does not include the
|
||||
// calculation of the derivatives
|
||||
void computeWellRatesWithBhp(const Simulator& ebosSimulator,
|
||||
const EvalWell& bhp,
|
||||
std::vector<double>& well_flux,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
std::vector<double> computeWellPotentialWithTHP(const Simulator& ebosSimulator,
|
||||
const double initial_bhp, // bhp from BHP constraints
|
||||
const std::vector<double>& initial_potential,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
template <class ValueType>
|
||||
ValueType calculateBhpFromThp(const std::vector<ValueType>& rates, const int control_index, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
double calculateThpFromBhp(const std::vector<double>& rates, const double bhp, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// get the mobility for specific perforation
|
||||
void getMobility(const Simulator& ebosSimulator,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& mob,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateWaterMobilityWithPolymer(const Simulator& ebos_simulator,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& mob_water,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updatePrimaryVariablesNewton(const BVectorWell& dwells,
|
||||
const WellState& well_state) const;
|
||||
|
||||
void updateWellStateFromPrimaryVariables(WellState& well_state, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateThp(WellState& well_state, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void assembleControlEq(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// handle the non reasonable fractions due to numerical overshoot
|
||||
void processFractions() const;
|
||||
|
||||
// updating the inflow based on the current reservoir condition
|
||||
void updateIPR(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// update the operability status of the well is operable under the current reservoir condition
|
||||
// mostly related to BHP limit and THP limit
|
||||
virtual void checkWellOperability(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger
|
||||
) override;
|
||||
|
||||
// check whether the well is operable under the current reservoir condition
|
||||
// mostly related to BHP limit and THP limit
|
||||
void updateWellOperability(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger
|
||||
);
|
||||
|
||||
// check whether the well is operable under BHP limit with current reservoir condition
|
||||
void checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// check whether the well is operable under THP limit with current reservoir condition
|
||||
void checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// update WellState based on IPR and associated VFP table
|
||||
void updateWellStateWithTHPTargetIPR(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateWellStateWithTHPTargetIPRProducer(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// for a well, when all drawdown are in the wrong direction, then this well will not
|
||||
// be able to produce/inject .
|
||||
bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const;
|
||||
|
||||
// whether the well can produce / inject based on the current well state (bhp)
|
||||
bool canProduceInjectWithCurrentBhp(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// turn on crossflow to avoid singular well equations
|
||||
// when the well is banned from cross-flow and the BHP is not properly initialized,
|
||||
// we turn on crossflow to avoid singular well equations. It can result in wrong-signed
|
||||
// well rates, it can cause problem for THP calculation
|
||||
// TODO: looking for better alternative to avoid wrong-signed well rates
|
||||
bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const;
|
||||
|
||||
// calculate the BHP from THP target based on IPR
|
||||
// TODO: we need to check the operablility here first, if not operable, then maybe there is
|
||||
// no point to do this
|
||||
double calculateBHPWithTHPTargetIPR(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// relaxation factor considering only one fraction value
|
||||
static double relaxationFactorFraction(const double old_value,
|
||||
const double dx);
|
||||
|
||||
// calculate a relaxation factor to avoid overshoot of the fractions for producers
|
||||
// which might result in negative rates
|
||||
static double relaxationFactorFractionsProducer(const std::vector<double>& primary_variables,
|
||||
const BVectorWell& dwells);
|
||||
|
||||
// calculate a relaxation factor to avoid overshoot of total rates
|
||||
static double relaxationFactorRate(const std::vector<double>& primary_variables,
|
||||
const BVectorWell& dwells);
|
||||
|
||||
virtual void wellTestingPhysical(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
WellState& well_state, WellTestState& welltest_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void updateWaterThroughput(const double dt, WellState& well_state) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "StandardWell_impl.hpp"
|
||||
|
||||
#endif // OPM_STANDARDWELL_HEADER_INCLUDED
|
||||
474
opm/simulators/wells/StandardWellV.hpp
Normal file
474
opm/simulators/wells/StandardWellV.hpp
Normal file
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2017 Statoil ASA.
|
||||
Copyright 2016 - 2017 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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_STANDARDWELLV_HEADER_INCLUDED
|
||||
#define OPM_STANDARDWELLV_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/simulators/wells/WellInterface.hpp>
|
||||
#include <opm/simulators/linalg/ISTLSolverEbos.hpp>
|
||||
#include <opm/autodiff/RateConverter.hpp>
|
||||
|
||||
#include <opm/material/densead/DynamicEvaluation.hpp>
|
||||
|
||||
#include <dune/common/dynvector.hh>
|
||||
#include <dune/common/dynmatrix.hh>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
template<typename TypeTag>
|
||||
class StandardWellV: public WellInterface<TypeTag>
|
||||
{
|
||||
|
||||
public:
|
||||
typedef WellInterface<TypeTag> Base;
|
||||
|
||||
// TODO: some functions working with AD variables handles only with values (double) without
|
||||
// dealing with derivatives. It can be beneficial to make functions can work with either AD or scalar value.
|
||||
// And also, it can also be beneficial to make these functions hanle different types of AD variables.
|
||||
using typename Base::Simulator;
|
||||
using typename Base::WellState;
|
||||
using typename Base::IntensiveQuantities;
|
||||
using typename Base::FluidSystem;
|
||||
using typename Base::MaterialLaw;
|
||||
using typename Base::ModelParameters;
|
||||
using typename Base::Indices;
|
||||
using typename Base::PolymerModule;
|
||||
using typename Base::RateConverterType;
|
||||
|
||||
using Base::numEq;
|
||||
|
||||
using Base::has_solvent;
|
||||
using Base::has_polymer;
|
||||
using Base::has_energy;
|
||||
|
||||
// polymer concentration and temperature are already known by the well, so
|
||||
// polymer and energy conservation do not need to be considered explicitly
|
||||
static const int numPolymerEq = Indices::numPolymers;
|
||||
static const int numEnergyEq = Indices::numEnergy;
|
||||
|
||||
// number of the conservation equations
|
||||
static const int numWellConservationEq = numEq - numPolymerEq - numEnergyEq;
|
||||
// number of the well control equations
|
||||
static const int numWellControlEq = 1;
|
||||
// number of the well equations that will always be used
|
||||
// based on the solution strategy, there might be other well equations be introduced
|
||||
static const int numStaticWellEq = numWellConservationEq + numWellControlEq;
|
||||
|
||||
// the positions of the primary variables for StandardWell
|
||||
// the first one is the weighted total rate (WQ_t), the second and the third ones are F_w and F_g,
|
||||
// which represent the fraction of Water and Gas based on the weighted total rate, the last one is BHP.
|
||||
// correspondingly, we have four well equations for blackoil model, the first three are mass
|
||||
// converstation equations, and the last one is the well control equation.
|
||||
// primary variables related to other components, will be before the Bhp and after F_g.
|
||||
// well control equation is always the last well equation.
|
||||
// TODO: in the current implementation, we use the well rate as the first primary variables for injectors,
|
||||
// instead of G_t.
|
||||
static const bool gasoil = numEq == 2 && (Indices::compositionSwitchIdx >= 0);
|
||||
static const int WQTotal = 0;
|
||||
static const int WFrac = gasoil? -1000: 1;
|
||||
static const int GFrac = gasoil? 1: 2;
|
||||
static const int SFrac = !has_solvent ? -1000 : 3;
|
||||
// the index for Bhp in primary variables and also the index of well control equation
|
||||
// they both will be the last one in their respective system.
|
||||
// TODO: we should have indices for the well equations and well primary variables separately
|
||||
static const int Bhp = numStaticWellEq - numWellControlEq;
|
||||
|
||||
using typename Base::Scalar;
|
||||
|
||||
|
||||
using Base::name;
|
||||
using Base::Water;
|
||||
using Base::Oil;
|
||||
using Base::Gas;
|
||||
|
||||
using typename Base::Mat;
|
||||
using typename Base::BVector;
|
||||
using typename Base::Eval;
|
||||
|
||||
// sparsity pattern for the matrices
|
||||
//[A C^T [x = [ res
|
||||
// B D ] x_well] res_well]
|
||||
|
||||
// the vector type for the res_well and x_well
|
||||
typedef Dune::DynamicVector<Scalar> VectorBlockWellType;
|
||||
typedef Dune::BlockVector<VectorBlockWellType> BVectorWell;
|
||||
|
||||
// the matrix type for the diagonal matrix D
|
||||
typedef Dune::DynamicMatrix<Scalar> DiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix <DiagMatrixBlockWellType> DiagMatWell;
|
||||
|
||||
// the matrix type for the non-diagonal matrix B and C^T
|
||||
typedef Dune::DynamicMatrix<Scalar> OffDiagMatrixBlockWellType;
|
||||
typedef Dune::BCRSMatrix<OffDiagMatrixBlockWellType> OffDiagMatWell;
|
||||
|
||||
typedef DenseAd::DynamicEvaluation<Scalar> EvalWell;
|
||||
|
||||
using Base::contiSolventEqIdx;
|
||||
using Base::contiPolymerEqIdx;
|
||||
static const int contiEnergyEqIdx = Indices::contiEnergyEqIdx;
|
||||
|
||||
StandardWellV(const Well* well, const int time_step, const Wells* wells,
|
||||
const ModelParameters& param,
|
||||
const RateConverterType& rate_converter,
|
||||
const int pvtRegionIdx,
|
||||
const int num_components);
|
||||
|
||||
virtual void init(const PhaseUsage* phase_usage_arg,
|
||||
const std::vector<double>& depth_arg,
|
||||
const double gravity_arg,
|
||||
const int num_cells) override;
|
||||
|
||||
|
||||
virtual void initPrimaryVariablesEvaluation() const override;
|
||||
|
||||
virtual void assembleWellEq(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void updateWellStateWithTarget(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// check whether the well equations get converged for this well
|
||||
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// Ax = Ax - C D^-1 B x
|
||||
virtual void apply(const BVector& x, BVector& Ax) const override;
|
||||
/// r = r - C D^-1 Rw
|
||||
virtual void apply(BVector& r) const override;
|
||||
|
||||
/// using the solution x to recover the solution xw for wells and applying
|
||||
/// xw to update Well State
|
||||
virtual void recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
/// computing the well potentials for group control
|
||||
virtual void computeWellPotentials(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& well_potentials,
|
||||
Opm::DeferredLogger& deferred_logger) /* const */ override;
|
||||
|
||||
virtual void updatePrimaryVariables(const WellState& well_state, Opm::DeferredLogger& deferred_logger) const override;
|
||||
|
||||
virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
virtual void calculateExplicitQuantities(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override; // should be const?
|
||||
|
||||
virtual void addWellContributions(Mat& mat) const override;
|
||||
|
||||
/// \brief Wether the Jacobian will also have well contributions in it.
|
||||
virtual bool jacobianContainsWellContributions() const override
|
||||
{
|
||||
return param_.matrix_add_well_contributions_;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// protected functions from the Base class
|
||||
using Base::getAllowCrossFlow;
|
||||
using Base::phaseUsage;
|
||||
using Base::flowPhaseToEbosCompIdx;
|
||||
using Base::ebosCompIdxToFlowCompIdx;
|
||||
using Base::wsolvent;
|
||||
using Base::wpolymer;
|
||||
using Base::wellHasTHPConstraints;
|
||||
using Base::mostStrictBhpFromBhpLimits;
|
||||
using Base::scalingFactor;
|
||||
using Base::scaleProductivityIndex;
|
||||
|
||||
// protected member variables from the Base class
|
||||
using Base::current_step_;
|
||||
using Base::well_ecl_;
|
||||
using Base::vfp_properties_;
|
||||
using Base::gravity_;
|
||||
using Base::param_;
|
||||
using Base::well_efficiency_factor_;
|
||||
using Base::first_perf_;
|
||||
using Base::ref_depth_;
|
||||
using Base::perf_depth_;
|
||||
using Base::well_cells_;
|
||||
using Base::number_of_perforations_;
|
||||
using Base::number_of_phases_;
|
||||
using Base::saturation_table_number_;
|
||||
using Base::comp_frac_;
|
||||
using Base::well_index_;
|
||||
using Base::index_of_well_;
|
||||
using Base::well_controls_;
|
||||
using Base::well_type_;
|
||||
using Base::num_components_;
|
||||
using Base::connectionRates_;
|
||||
|
||||
using Base::perf_rep_radius_;
|
||||
using Base::perf_length_;
|
||||
using Base::bore_diameters_;
|
||||
|
||||
// total number of the well equations and primary variables
|
||||
// there might be extra equations be used, numWellEq will be updated during the initialization
|
||||
int numWellEq_ = numStaticWellEq;
|
||||
|
||||
// densities of the fluid in each perforation
|
||||
std::vector<double> perf_densities_;
|
||||
// pressure drop between different perforations
|
||||
std::vector<double> perf_pressure_diffs_;
|
||||
|
||||
// residuals of the well equations
|
||||
BVectorWell resWell_;
|
||||
|
||||
// two off-diagonal matrices
|
||||
OffDiagMatWell duneB_;
|
||||
OffDiagMatWell duneC_;
|
||||
// diagonal matrix for the well
|
||||
DiagMatWell invDuneD_;
|
||||
|
||||
// several vector used in the matrix calculation
|
||||
mutable BVectorWell Bx_;
|
||||
mutable BVectorWell invDrw_;
|
||||
|
||||
// the values for the primary varibles
|
||||
// based on different solutioin strategies, the wells can have different primary variables
|
||||
mutable std::vector<double> primary_variables_;
|
||||
|
||||
// the Evaluation for the well primary variables, which contain derivativles and are used in AD calculation
|
||||
mutable std::vector<EvalWell> primary_variables_evaluation_;
|
||||
|
||||
// the saturations in the well bore under surface conditions at the beginning of the time step
|
||||
std::vector<double> F0_;
|
||||
|
||||
// the vectors used to describe the inflow performance relationship (IPR)
|
||||
// Q = IPR_A - BHP * IPR_B
|
||||
// TODO: it minght need to go to WellInterface, let us implement it in StandardWell first
|
||||
// it is only updated and used for producers for now
|
||||
mutable std::vector<double> ipr_a_;
|
||||
mutable std::vector<double> ipr_b_;
|
||||
|
||||
const EvalWell& getBhp() const;
|
||||
|
||||
EvalWell getQs(const int comp_idx) const;
|
||||
|
||||
const EvalWell& getWQTotal() const;
|
||||
|
||||
EvalWell wellVolumeFractionScaled(const int phase) const;
|
||||
|
||||
EvalWell wellVolumeFraction(const unsigned compIdx) const;
|
||||
|
||||
EvalWell wellSurfaceVolumeFraction(const int phase) const;
|
||||
|
||||
EvalWell extendEval(const Eval& in) const;
|
||||
|
||||
// xw = inv(D)*(rw - C*x)
|
||||
void recoverSolutionWell(const BVector& x, BVectorWell& xw) const;
|
||||
|
||||
// updating the well_state based on well solution dwells
|
||||
void updateWellState(const BVectorWell& dwells,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// calculate the properties for the well connections
|
||||
// to calulate the pressure difference between well connections.
|
||||
void computePropertiesForWellConnectionPressures(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& b_perf,
|
||||
std::vector<double>& rsmax_perf,
|
||||
std::vector<double>& rvmax_perf,
|
||||
std::vector<double>& surf_dens_perf) const;
|
||||
|
||||
// TODO: not total sure whether it is a good idea to put this function here
|
||||
// the major reason to put here is to avoid the usage of Wells struct
|
||||
void computeConnectionDensities(const std::vector<double>& perfComponentRates,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf);
|
||||
|
||||
void computeConnectionPressureDelta();
|
||||
|
||||
void computeWellConnectionDensitesPressures(const WellState& well_state,
|
||||
const std::vector<double>& b_perf,
|
||||
const std::vector<double>& rsmax_perf,
|
||||
const std::vector<double>& rvmax_perf,
|
||||
const std::vector<double>& surf_dens_perf);
|
||||
|
||||
// computing the accumulation term for later use in well mass equations
|
||||
void computeAccumWell();
|
||||
|
||||
void computeWellConnectionPressures(const Simulator& ebosSimulator,
|
||||
const WellState& well_state);
|
||||
|
||||
void computePerfRate(const IntensiveQuantities& intQuants,
|
||||
const std::vector<EvalWell>& mob,
|
||||
const EvalWell& bhp,
|
||||
const int perf,
|
||||
const bool allow_cf,
|
||||
std::vector<EvalWell>& cq_s,
|
||||
double& perf_dis_gas_rate,
|
||||
double& perf_vap_oil_rate,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// TODO: maybe we should provide a light version of computePerfRate, which does not include the
|
||||
// calculation of the derivatives
|
||||
void computeWellRatesWithBhp(const Simulator& ebosSimulator,
|
||||
const EvalWell& bhp,
|
||||
std::vector<double>& well_flux,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
std::vector<double> computeWellPotentialWithTHP(const Simulator& ebosSimulator,
|
||||
const double initial_bhp, // bhp from BHP constraints
|
||||
const std::vector<double>& initial_potential,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
template <class ValueType>
|
||||
ValueType calculateBhpFromThp(const std::vector<ValueType>& rates, const int control_index, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
double calculateThpFromBhp(const std::vector<double>& rates, const double bhp, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// get the mobility for specific perforation
|
||||
void getMobility(const Simulator& ebosSimulator,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& mob,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateWaterMobilityWithPolymer(const Simulator& ebos_simulator,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& mob_water,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updatePrimaryVariablesNewton(const BVectorWell& dwells,
|
||||
const WellState& well_state) const;
|
||||
|
||||
void updateWellStateFromPrimaryVariables(WellState& well_state, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateThp(WellState& well_state, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void assembleControlEq(Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// handle the non reasonable fractions due to numerical overshoot
|
||||
void processFractions() const;
|
||||
|
||||
// updating the inflow based on the current reservoir condition
|
||||
void updateIPR(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// update the operability status of the well is operable under the current reservoir condition
|
||||
// mostly related to BHP limit and THP limit
|
||||
virtual void checkWellOperability(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
// check whether the well is operable under the current reservoir condition
|
||||
// mostly related to BHP limit and THP limit
|
||||
void updateWellOperability(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// check whether the well is operable under BHP limit with current reservoir condition
|
||||
void checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// check whether the well is operable under THP limit with current reservoir condition
|
||||
void checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// update WellState based on IPR and associated VFP table
|
||||
void updateWellStateWithTHPTargetIPR(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateWellStateWithTHPTargetIPRProducer(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// for a well, when all drawdown are in the wrong direction, then this well will not
|
||||
// be able to produce/inject .
|
||||
bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const;
|
||||
|
||||
// whether the well can produce / inject based on the current well state (bhp)
|
||||
bool canProduceInjectWithCurrentBhp(const Simulator& ebos_simulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// turn on crossflow to avoid singular well equations
|
||||
// when the well is banned from cross-flow and the BHP is not properly initialized,
|
||||
// we turn on crossflow to avoid singular well equations. It can result in wrong-signed
|
||||
// well rates, it can cause problem for THP calculation
|
||||
// TODO: looking for better alternative to avoid wrong-signed well rates
|
||||
bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const;
|
||||
|
||||
// calculate the BHP from THP target based on IPR
|
||||
// TODO: we need to check the operablility here first, if not operable, then maybe there is
|
||||
// no point to do this
|
||||
double calculateBHPWithTHPTargetIPR(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// relaxation factor considering only one fraction value
|
||||
static double relaxationFactorFraction(const double old_value,
|
||||
const double dx);
|
||||
|
||||
// calculate a relaxation factor to avoid overshoot of the fractions for producers
|
||||
// which might result in negative rates
|
||||
static double relaxationFactorFractionsProducer(const std::vector<double>& primary_variables,
|
||||
const BVectorWell& dwells);
|
||||
|
||||
// calculate a relaxation factor to avoid overshoot of total rates
|
||||
static double relaxationFactorRate(const std::vector<double>& primary_variables,
|
||||
const BVectorWell& dwells);
|
||||
|
||||
virtual void wellTestingPhysical(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
WellState& well_state, WellTestState& welltest_state, Opm::DeferredLogger& deferred_logger) override;
|
||||
|
||||
// calculate the skin pressure based on water velocity, throughput and polymer concentration.
|
||||
// throughput is used to describe the formation damage during water/polymer injection.
|
||||
// calculated skin pressure will be applied to the drawdown during perforation rate calculation
|
||||
// to handle the effect from formation damage.
|
||||
EvalWell pskin(const double throuhgput,
|
||||
const EvalWell& water_velocity,
|
||||
const EvalWell& poly_inj_conc,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// calculate the skin pressure based on water velocity, throughput during water injection.
|
||||
EvalWell pskinwater(const double throughput,
|
||||
const EvalWell& water_velocity,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// calculate the injecting polymer molecular weight based on the througput and water velocity
|
||||
EvalWell wpolymermw(const double throughput,
|
||||
const EvalWell& water_velocity,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// handle the extra equations for polymer injectivity study
|
||||
void handleInjectivityRateAndEquations(const IntensiveQuantities& int_quants,
|
||||
const WellState& well_state,
|
||||
const int perf,
|
||||
std::vector<EvalWell>& cq_s,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
virtual void updateWaterThroughput(const double dt, WellState& well_state) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "StandardWellV_impl.hpp"
|
||||
|
||||
#endif // OPM_STANDARDWELLV_HEADER_INCLUDED
|
||||
3115
opm/simulators/wells/StandardWellV_impl.hpp
Normal file
3115
opm/simulators/wells/StandardWellV_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
2856
opm/simulators/wells/StandardWell_impl.hpp
Normal file
2856
opm/simulators/wells/StandardWell_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
866
opm/simulators/wells/VFPHelpers.hpp
Normal file
866
opm/simulators/wells/VFPHelpers.hpp
Normal file
@@ -0,0 +1,866 @@
|
||||
/*
|
||||
Copyright 2015 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_AUTODIFF_VFPHELPERS_HPP_
|
||||
#define OPM_AUTODIFF_VFPHELPERS_HPP_
|
||||
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
|
||||
#include <cmath>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
#include <opm/material/densead/Evaluation.hpp>
|
||||
|
||||
/**
|
||||
* This file contains a set of helper functions used by VFPProd / VFPInj.
|
||||
*/
|
||||
namespace Opm {
|
||||
namespace detail {
|
||||
|
||||
|
||||
/**
|
||||
* Returns zero if input value is NaN of INF
|
||||
*/
|
||||
inline double zeroIfNanInf(const double& value) {
|
||||
const bool nan_or_inf = std::isnan(value) || std::isinf(value);
|
||||
|
||||
if (nan_or_inf) {
|
||||
OpmLog::warning("NAN_OR_INF_VFP", "NAN or INF value encountered during VFP calculation, the value is set to zero");
|
||||
}
|
||||
|
||||
return nan_or_inf ? 0.0 : value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns zero if input value is NaN or INF
|
||||
*/
|
||||
template <class EvalWell>
|
||||
inline EvalWell zeroIfNanInf(const EvalWell& value) {
|
||||
const bool nan_or_inf = std::isnan(value.value()) || std::isinf(value.value());
|
||||
|
||||
if (nan_or_inf) {
|
||||
OpmLog::warning("NAN_OR_INF_VFP_EVAL", "NAN or INF Evalution encountered during VFP calculation, the Evalution is set to zero");
|
||||
}
|
||||
|
||||
using Toolbox = MathToolbox<EvalWell>;
|
||||
|
||||
return nan_or_inf ? Toolbox::createBlank(value) : value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the flo parameter according to the flo_type_
|
||||
* for production tables
|
||||
* @return Production rate of oil, gas or liquid.
|
||||
*/
|
||||
template <typename T>
|
||||
static T getFlo(const T& aqua, const T& liquid, const T& vapour,
|
||||
const VFPProdTable::FLO_TYPE& type) {
|
||||
switch (type) {
|
||||
case VFPProdTable::FLO_OIL:
|
||||
//Oil = liquid phase
|
||||
return liquid;
|
||||
case VFPProdTable::FLO_LIQ:
|
||||
//Liquid = aqua + liquid phases
|
||||
return aqua + liquid;
|
||||
case VFPProdTable::FLO_GAS:
|
||||
//Gas = vapor phase
|
||||
return vapour;
|
||||
case VFPProdTable::FLO_INVALID: //Intentional fall-through
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Invalid FLO_TYPE: '" << type << "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Computes the flo parameter according to the flo_type_
|
||||
* for injection tables
|
||||
* @return Production rate of oil, gas or liquid.
|
||||
*/
|
||||
template <typename T>
|
||||
static T getFlo(const T& aqua, const T& liquid, const T& vapour,
|
||||
const VFPInjTable::FLO_TYPE& type) {
|
||||
switch (type) {
|
||||
case VFPInjTable::FLO_OIL:
|
||||
//Oil = liquid phase
|
||||
return liquid;
|
||||
case VFPInjTable::FLO_WAT:
|
||||
//Liquid = aqua phase
|
||||
return aqua;
|
||||
case VFPInjTable::FLO_GAS:
|
||||
//Gas = vapor phase
|
||||
return vapour;
|
||||
case VFPInjTable::FLO_INVALID: //Intentional fall-through
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Invalid FLO_TYPE: '" << type << "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Computes the wfr parameter according to the wfr_type_
|
||||
* @return Production rate of oil, gas or liquid.
|
||||
*/
|
||||
template <typename T>
|
||||
static T getWFR(const T& aqua, const T& liquid, const T& vapour,
|
||||
const VFPProdTable::WFR_TYPE& type) {
|
||||
switch(type) {
|
||||
case VFPProdTable::WFR_WOR: {
|
||||
//Water-oil ratio = water / oil
|
||||
T wor = aqua / liquid;
|
||||
return zeroIfNanInf(wor);
|
||||
}
|
||||
case VFPProdTable::WFR_WCT:
|
||||
//Water cut = water / (water + oil)
|
||||
return zeroIfNanInf(aqua / (aqua + liquid));
|
||||
case VFPProdTable::WFR_WGR:
|
||||
//Water-gas ratio = water / gas
|
||||
return zeroIfNanInf(aqua / vapour);
|
||||
case VFPProdTable::WFR_INVALID: //Intentional fall-through
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Invalid WFR_TYPE: '" << type << "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Computes the gfr parameter according to the gfr_type_
|
||||
* @return Production rate of oil, gas or liquid.
|
||||
*/
|
||||
template <typename T>
|
||||
static T getGFR(const T& aqua, const T& liquid, const T& vapour,
|
||||
const VFPProdTable::GFR_TYPE& type) {
|
||||
switch(type) {
|
||||
case VFPProdTable::GFR_GOR:
|
||||
// Gas-oil ratio = gas / oil
|
||||
return zeroIfNanInf(vapour / liquid);
|
||||
case VFPProdTable::GFR_GLR:
|
||||
// Gas-liquid ratio = gas / (oil + water)
|
||||
return zeroIfNanInf(vapour / (liquid + aqua));
|
||||
case VFPProdTable::GFR_OGR:
|
||||
// Oil-gas ratio = oil / gas
|
||||
return zeroIfNanInf(liquid / vapour);
|
||||
case VFPProdTable::GFR_INVALID: //Intentional fall-through
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Invalid GFR_TYPE: '" << type << "'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper struct for linear interpolation
|
||||
*/
|
||||
struct InterpData {
|
||||
InterpData() : ind_{0, 0}, inv_dist_(0.0), factor_(0.0) {}
|
||||
int ind_[2]; //[First element greater than or equal to value, Last element smaller than or equal to value]
|
||||
double inv_dist_; // 1 / distance between the two end points of the segment. Used to calculate derivatives and uses 1.0 / 0.0 = 0.0 as a convention
|
||||
double factor_; // Interpolation factor
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to find indices etc. for linear interpolation and extrapolation
|
||||
* @param value Value to find in values
|
||||
* @param values Sorted list of values to search for value in.
|
||||
* @return Data required to find the interpolated value
|
||||
*/
|
||||
inline InterpData findInterpData(const double& value, const std::vector<double>& values) {
|
||||
InterpData retval;
|
||||
|
||||
const int nvalues = values.size();
|
||||
|
||||
//If we only have one value in our vector, return that
|
||||
if (values.size() == 1) {
|
||||
retval.ind_[0] = 0;
|
||||
retval.ind_[1] = 0;
|
||||
retval.inv_dist_ = 0.0;
|
||||
retval.factor_ = 0.0;
|
||||
}
|
||||
// Else search in the vector
|
||||
else {
|
||||
//If value is less than all values, use first interval
|
||||
if (value < values.front()) {
|
||||
retval.ind_[0] = 0;
|
||||
retval.ind_[1] = 1;
|
||||
}
|
||||
//If value is greater than all values, use last interval
|
||||
else if (value >= values.back()) {
|
||||
retval.ind_[0] = nvalues-2;
|
||||
retval.ind_[1] = nvalues-1;
|
||||
}
|
||||
else {
|
||||
//Search internal intervals
|
||||
for (int i=1; i<nvalues; ++i) {
|
||||
if (values[i] >= value) {
|
||||
retval.ind_[0] = i-1;
|
||||
retval.ind_[1] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double start = values[retval.ind_[0]];
|
||||
const double end = values[retval.ind_[1]];
|
||||
|
||||
//Find interpolation ratio
|
||||
if (end > start) {
|
||||
//FIXME: Possible source for floating point error here if value and floor are large,
|
||||
//but very close to each other
|
||||
retval.inv_dist_ = 1.0 / (end-start);
|
||||
retval.factor_ = (value-start) * retval.inv_dist_;
|
||||
}
|
||||
else {
|
||||
retval.inv_dist_ = 0.0;
|
||||
retval.factor_ = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An "ADB-like" structure with a single value and a set of derivatives
|
||||
*/
|
||||
struct VFPEvaluation {
|
||||
VFPEvaluation() : value(0.0), dthp(0.0), dwfr(0.0), dgfr(0.0), dalq(0.0), dflo(0.0) {};
|
||||
double value;
|
||||
double dthp;
|
||||
double dwfr;
|
||||
double dgfr;
|
||||
double dalq;
|
||||
double dflo;
|
||||
};
|
||||
|
||||
inline VFPEvaluation operator+(
|
||||
VFPEvaluation lhs,
|
||||
const VFPEvaluation& rhs) {
|
||||
lhs.value += rhs.value;
|
||||
lhs.dthp += rhs.dthp;
|
||||
lhs.dwfr += rhs.dwfr;
|
||||
lhs.dgfr += rhs.dgfr;
|
||||
lhs.dalq += rhs.dalq;
|
||||
lhs.dflo += rhs.dflo;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline VFPEvaluation operator-(
|
||||
VFPEvaluation lhs,
|
||||
const VFPEvaluation& rhs) {
|
||||
lhs.value -= rhs.value;
|
||||
lhs.dthp -= rhs.dthp;
|
||||
lhs.dwfr -= rhs.dwfr;
|
||||
lhs.dgfr -= rhs.dgfr;
|
||||
lhs.dalq -= rhs.dalq;
|
||||
lhs.dflo -= rhs.dflo;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
inline VFPEvaluation operator*(
|
||||
double lhs,
|
||||
const VFPEvaluation& rhs) {
|
||||
VFPEvaluation retval;
|
||||
retval.value = rhs.value * lhs;
|
||||
retval.dthp = rhs.dthp * lhs;
|
||||
retval.dwfr = rhs.dwfr * lhs;
|
||||
retval.dgfr = rhs.dgfr * lhs;
|
||||
retval.dalq = rhs.dalq * lhs;
|
||||
retval.dflo = rhs.dflo * lhs;
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper function which interpolates data using the indices etc. given in the inputs.
|
||||
*/
|
||||
inline VFPEvaluation interpolate(
|
||||
const VFPProdTable::array_type& array,
|
||||
const InterpData& flo_i,
|
||||
const InterpData& thp_i,
|
||||
const InterpData& wfr_i,
|
||||
const InterpData& gfr_i,
|
||||
const InterpData& alq_i) {
|
||||
|
||||
//Values and derivatives in a 5D hypercube
|
||||
VFPEvaluation nn[2][2][2][2][2];
|
||||
|
||||
|
||||
//Pick out nearest neighbors (nn) to our evaluation point
|
||||
//This is not really required, but performance-wise it may pay off, since the 32-elements
|
||||
//we copy to (nn) will fit better in cache than the full original table for the
|
||||
//interpolation below.
|
||||
//The following ladder of for loops will presumably be unrolled by a reasonable compiler.
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int w=0; w<=1; ++w) {
|
||||
for (int g=0; g<=1; ++g) {
|
||||
for (int a=0; a<=1; ++a) {
|
||||
for (int f=0; f<=1; ++f) {
|
||||
//Shorthands for indexing
|
||||
const int ti = thp_i.ind_[t];
|
||||
const int wi = wfr_i.ind_[w];
|
||||
const int gi = gfr_i.ind_[g];
|
||||
const int ai = alq_i.ind_[a];
|
||||
const int fi = flo_i.ind_[f];
|
||||
|
||||
//Copy element
|
||||
nn[t][w][g][a][f].value = array[ti][wi][gi][ai][fi];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate derivatives
|
||||
//Note that the derivative of the two end points of a line aligned with the
|
||||
//"axis of the derivative" are equal
|
||||
for (int i=0; i<=1; ++i) {
|
||||
for (int j=0; j<=1; ++j) {
|
||||
for (int k=0; k<=1; ++k) {
|
||||
for (int l=0; l<=1; ++l) {
|
||||
nn[0][i][j][k][l].dthp = (nn[1][i][j][k][l].value - nn[0][i][j][k][l].value) * thp_i.inv_dist_;
|
||||
nn[i][0][j][k][l].dwfr = (nn[i][1][j][k][l].value - nn[i][0][j][k][l].value) * wfr_i.inv_dist_;
|
||||
nn[i][j][0][k][l].dgfr = (nn[i][j][1][k][l].value - nn[i][j][0][k][l].value) * gfr_i.inv_dist_;
|
||||
nn[i][j][k][0][l].dalq = (nn[i][j][k][1][l].value - nn[i][j][k][0][l].value) * alq_i.inv_dist_;
|
||||
nn[i][j][k][l][0].dflo = (nn[i][j][k][l][1].value - nn[i][j][k][l][0].value) * flo_i.inv_dist_;
|
||||
|
||||
nn[1][i][j][k][l].dthp = nn[0][i][j][k][l].dthp;
|
||||
nn[i][1][j][k][l].dwfr = nn[i][0][j][k][l].dwfr;
|
||||
nn[i][j][1][k][l].dgfr = nn[i][j][0][k][l].dgfr;
|
||||
nn[i][j][k][1][l].dalq = nn[i][j][k][0][l].dalq;
|
||||
nn[i][j][k][l][1].dflo = nn[i][j][k][l][0].dflo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double t1, t2; //interpolation variables, so that t1 = (1-t) and t2 = t.
|
||||
|
||||
// Remove dimensions one by one
|
||||
// Example: going from 3D to 2D to 1D, we start by interpolating along
|
||||
// the z axis first, leaving a 2D problem. Then interpolating along the y
|
||||
// axis, leaving a 1D, problem, etc.
|
||||
t2 = flo_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int w=0; w<=1; ++w) {
|
||||
for (int g=0; g<=1; ++g) {
|
||||
for (int a=0; a<=1; ++a) {
|
||||
nn[t][w][g][a][0] = t1*nn[t][w][g][a][0] + t2*nn[t][w][g][a][1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t2 = alq_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int w=0; w<=1; ++w) {
|
||||
for (int g=0; g<=1; ++g) {
|
||||
nn[t][w][g][0][0] = t1*nn[t][w][g][0][0] + t2*nn[t][w][g][1][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t2 = gfr_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int w=0; w<=1; ++w) {
|
||||
nn[t][w][0][0][0] = t1*nn[t][w][0][0][0] + t2*nn[t][w][1][0][0];
|
||||
}
|
||||
}
|
||||
|
||||
t2 = wfr_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
for (int t=0; t<=1; ++t) {
|
||||
nn[t][0][0][0][0] = t1*nn[t][0][0][0][0] + t2*nn[t][1][0][0][0];
|
||||
}
|
||||
|
||||
t2 = thp_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
nn[0][0][0][0][0] = t1*nn[0][0][0][0][0] + t2*nn[1][0][0][0][0];
|
||||
|
||||
return nn[0][0][0][0][0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This basically models interpolate(VFPProdTable::array_type, ...)
|
||||
* which performs 5D interpolation, but here for the 2D case only
|
||||
*/
|
||||
inline VFPEvaluation interpolate(
|
||||
const VFPInjTable::array_type& array,
|
||||
const InterpData& flo_i,
|
||||
const InterpData& thp_i) {
|
||||
|
||||
//Values and derivatives in a 2D plane
|
||||
VFPEvaluation nn[2][2];
|
||||
|
||||
|
||||
//Pick out nearest neighbors (nn) to our evaluation point
|
||||
//The following ladder of for loops will presumably be unrolled by a reasonable compiler.
|
||||
for (int t=0; t<=1; ++t) {
|
||||
for (int f=0; f<=1; ++f) {
|
||||
//Shorthands for indexing
|
||||
const int ti = thp_i.ind_[t];
|
||||
const int fi = flo_i.ind_[f];
|
||||
|
||||
//Copy element
|
||||
nn[t][f].value = array[ti][fi];
|
||||
}
|
||||
}
|
||||
|
||||
//Calculate derivatives
|
||||
//Note that the derivative of the two end points of a line aligned with the
|
||||
//"axis of the derivative" are equal
|
||||
for (int i=0; i<=1; ++i) {
|
||||
nn[0][i].dthp = (nn[1][i].value - nn[0][i].value) * thp_i.inv_dist_;
|
||||
nn[i][0].dwfr = -1e100;
|
||||
nn[i][0].dgfr = -1e100;
|
||||
nn[i][0].dalq = -1e100;
|
||||
nn[i][0].dflo = (nn[i][1].value - nn[i][0].value) * flo_i.inv_dist_;
|
||||
|
||||
nn[1][i].dthp = nn[0][i].dthp;
|
||||
nn[i][1].dwfr = nn[i][0].dwfr;
|
||||
nn[i][1].dgfr = nn[i][0].dgfr;
|
||||
nn[i][1].dalq = nn[i][0].dalq;
|
||||
nn[i][1].dflo = nn[i][0].dflo;
|
||||
}
|
||||
|
||||
double t1, t2; //interpolation variables, so that t1 = (1-t) and t2 = t.
|
||||
|
||||
// Go from 2D to 1D
|
||||
t2 = flo_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
nn[0][0] = t1*nn[0][0] + t2*nn[0][1];
|
||||
nn[1][0] = t1*nn[1][0] + t2*nn[1][1];
|
||||
|
||||
// Go from line to point on line
|
||||
t2 = thp_i.factor_;
|
||||
t1 = (1.0-t2);
|
||||
nn[0][0] = t1*nn[0][0] + t2*nn[1][0];
|
||||
|
||||
return nn[0][0];
|
||||
}
|
||||
|
||||
inline VFPEvaluation bhp(const VFPProdTable* table,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp,
|
||||
const double& alq) {
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
double wfr = detail::getWFR(aqua, liquid, vapour, table->getWFRType());
|
||||
double gfr = detail::getGFR(aqua, liquid, vapour, table->getGFRType());
|
||||
|
||||
//First, find the values to interpolate between
|
||||
//Recall that flo is negative in Opm, so switch sign.
|
||||
auto flo_i = detail::findInterpData(-flo, table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData( thp, table->getTHPAxis());
|
||||
auto wfr_i = detail::findInterpData( wfr, table->getWFRAxis());
|
||||
auto gfr_i = detail::findInterpData( gfr, table->getGFRAxis());
|
||||
auto alq_i = detail::findInterpData( alq, table->getALQAxis());
|
||||
|
||||
detail::VFPEvaluation retval = detail::interpolate(table->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
inline VFPEvaluation bhp(const VFPInjTable* table,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp) {
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
|
||||
//First, find the values to interpolate between
|
||||
auto flo_i = detail::findInterpData(flo, table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData(thp, table->getTHPAxis());
|
||||
|
||||
//Then perform the interpolation itself
|
||||
detail::VFPEvaluation retval = detail::interpolate(table->getTable(), flo_i, thp_i);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the table from the map if found, or throws an exception
|
||||
*/
|
||||
template <typename T>
|
||||
const T* getTable(const std::map<int, T*> tables, int table_id) {
|
||||
auto entry = tables.find(table_id);
|
||||
if (entry == tables.end()) {
|
||||
OPM_THROW(std::invalid_argument, "Nonexistent VFP table " << table_id << " referenced.");
|
||||
}
|
||||
else {
|
||||
return entry->second;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have a table with the table number
|
||||
*/
|
||||
template <typename T>
|
||||
bool hasTable(const std::map<int, T*> tables, int table_id) {
|
||||
const auto entry = tables.find(table_id);
|
||||
return (entry != tables.end() );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type variable for FLO/GFR/WFR for production tables
|
||||
*/
|
||||
template <typename TYPE, typename TABLE>
|
||||
TYPE getType(const TABLE* table);
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::FLO_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getFloType();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::WFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getWFRType();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline
|
||||
VFPProdTable::GFR_TYPE getType(const VFPProdTable* table) {
|
||||
return table->getGFRType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type variable for FLO for injection tables
|
||||
*/
|
||||
template <>
|
||||
inline
|
||||
VFPInjTable::FLO_TYPE getType(const VFPInjTable* table) {
|
||||
return table->getFloType();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function that finds x for a given value of y for a line
|
||||
* *NOTE ORDER OF ARGUMENTS*
|
||||
*/
|
||||
inline double findX(const double& x0,
|
||||
const double& x1,
|
||||
const double& y0,
|
||||
const double& y1,
|
||||
const double& y) {
|
||||
const double dx = x1 - x0;
|
||||
const double dy = y1 - y0;
|
||||
|
||||
/**
|
||||
* y = y0 + (dy / dx) * (x - x0)
|
||||
* => x = x0 + (y - y0) * (dx / dy)
|
||||
*
|
||||
* If dy is zero, use x1 as the value.
|
||||
*/
|
||||
|
||||
double x = 0.0;
|
||||
|
||||
if (dy != 0.0) {
|
||||
x = x0 + (y-y0) * (dx/dy);
|
||||
}
|
||||
else {
|
||||
x = x1;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This function finds the value of THP given a specific BHP.
|
||||
* Essentially:
|
||||
* Given the function f(thp_array(x)) = bhp_array(x), which is piecewise linear,
|
||||
* find thp so that f(thp) = bhp.
|
||||
*/
|
||||
inline double findTHP(
|
||||
const std::vector<double>& bhp_array,
|
||||
const std::vector<double>& thp_array,
|
||||
double bhp) {
|
||||
int nthp = thp_array.size();
|
||||
|
||||
double thp = -1e100;
|
||||
|
||||
//Check that our thp axis is sorted
|
||||
assert(std::is_sorted(thp_array.begin(), thp_array.end()));
|
||||
|
||||
/**
|
||||
* Our *interpolated* bhp_array will be montonic increasing for increasing
|
||||
* THP if our input BHP values are monotonic increasing for increasing
|
||||
* THP values. However, if we have to *extrapolate* along any of the other
|
||||
* axes, this guarantee holds no more, and bhp_array may be "random"
|
||||
*/
|
||||
if (std::is_sorted(bhp_array.begin(), bhp_array.end())) {
|
||||
//Target bhp less than all values in array, extrapolate
|
||||
if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp within table ranges, interpolate
|
||||
else {
|
||||
//Loop over the values and find min(bhp_array(thp)) == bhp
|
||||
//so that we maximize the rate.
|
||||
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Assuming a small number of values in bhp_array, this should be quite
|
||||
//efficient. Other strategies might be bisection, etc.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Canary in a coal mine: shouldn't really be required
|
||||
assert(found == true);
|
||||
static_cast<void>(found); //Silence compiler warning
|
||||
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
}
|
||||
//bhp_array not sorted, raw search.
|
||||
else {
|
||||
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
|
||||
//Since the BHP values might not be sorted, first search within
|
||||
//our interpolation values, and then try to extrapolate.
|
||||
int i=0;
|
||||
bool found = false;
|
||||
for (; i<nthp-1; ++i) {
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
|
||||
if (y0 < bhp && bhp <= y1) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
const double& x0 = thp_array[i ];
|
||||
const double& x1 = thp_array[i+1];
|
||||
const double& y0 = bhp_array[i ];
|
||||
const double& y1 = bhp_array[i+1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else if (bhp <= bhp_array[0]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[0];
|
||||
const double& x1 = thp_array[1];
|
||||
const double& y0 = bhp_array[0];
|
||||
const double& y1 = bhp_array[1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
//Target bhp greater than all values in array, extrapolate
|
||||
else if (bhp > bhp_array[nthp-1]) {
|
||||
//TODO: LOG extrapolation
|
||||
const double& x0 = thp_array[nthp-2];
|
||||
const double& x1 = thp_array[nthp-1];
|
||||
const double& y0 = bhp_array[nthp-2];
|
||||
const double& y1 = bhp_array[nthp-1];
|
||||
thp = detail::findX(x0, x1, y0, y1, bhp);
|
||||
}
|
||||
else {
|
||||
OPM_THROW(std::logic_error, "Programmer error: Unable to find THP in THP array");
|
||||
}
|
||||
}
|
||||
|
||||
return thp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// a data type use to do the intersection calculation to get the intial bhp under THP control
|
||||
struct RateBhpPair {
|
||||
double rate;
|
||||
double bhp;
|
||||
};
|
||||
|
||||
|
||||
// looking for a intersection point a line segment and a line, they are both defined with two points
|
||||
// it is copied from #include <opm/polymer/Point2D.hpp>, which should be removed since it is only required by the lagacy polymer
|
||||
inline bool findIntersection(const std::array<RateBhpPair, 2>& line_segment, const std::array<RateBhpPair, 2>& line, double& bhp) {
|
||||
const double x1 = line_segment[0].rate;
|
||||
const double y1 = line_segment[0].bhp;
|
||||
const double x2 = line_segment[1].rate;
|
||||
const double y2 = line_segment[1].bhp;
|
||||
|
||||
const double x3 = line[0].rate;
|
||||
const double y3 = line[0].bhp;
|
||||
const double x4 = line[1].rate;
|
||||
const double y4 = line[1].bhp;
|
||||
|
||||
const double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
|
||||
|
||||
if (d == 0.) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const double x = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
|
||||
const double y = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
|
||||
|
||||
if (x >= std::min(x1,x2) && x <= std::max(x1,x2)) {
|
||||
bhp = y;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// calculating the BHP from thp through the intersection of VFP curves and inflow performance relationship
|
||||
inline bool findIntersectionForBhp(const std::vector<RateBhpPair>& ratebhp_samples,
|
||||
const std::array<RateBhpPair, 2>& ratebhp_twopoints_ipr,
|
||||
double& obtained_bhp)
|
||||
{
|
||||
// there possibly two intersection point, then we choose the one corresponding with the bigger rate
|
||||
|
||||
const double bhp1 = ratebhp_twopoints_ipr[0].bhp;
|
||||
const double rate1 = ratebhp_twopoints_ipr[0].rate;
|
||||
|
||||
const double bhp2 = ratebhp_twopoints_ipr[1].bhp;
|
||||
const double rate2 = ratebhp_twopoints_ipr[1].rate;
|
||||
|
||||
assert(rate1 != rate2);
|
||||
|
||||
const double line_slope = (bhp2 - bhp1) / (rate2 - rate1);
|
||||
|
||||
// line equation will be
|
||||
// bhp - bhp1 - line_slope * (flo_rate - flo_rate1) = 0
|
||||
auto flambda = [&](const double flo_rate, const double bhp) {
|
||||
return bhp - bhp1 - line_slope * (flo_rate - rate1);
|
||||
};
|
||||
|
||||
int number_intersection_found = 0;
|
||||
int index_segment = 0; // the intersection segment that intersection happens
|
||||
const size_t num_samples = ratebhp_samples.size();
|
||||
for (size_t i = 0; i < num_samples - 1; ++i) {
|
||||
const double temp1 = flambda(ratebhp_samples[i].rate, ratebhp_samples[i].bhp);
|
||||
const double temp2 = flambda(ratebhp_samples[i+1].rate, ratebhp_samples[i+1].bhp);
|
||||
if (temp1 * temp2 <= 0.) { // intersection happens
|
||||
// in theory there should be maximum two intersection points
|
||||
// while considering the situation == 0. here, we might find more
|
||||
// we always use the last one, which is the one corresponds to the biggest rate,
|
||||
// which we assume is the more stable one
|
||||
++number_intersection_found;
|
||||
index_segment = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (number_intersection_found == 0) { // there is not intersection point
|
||||
return false;
|
||||
}
|
||||
|
||||
// then we pick the segment from the VFP curve to do the line intersection calculation
|
||||
const std::array<RateBhpPair, 2> line_segment{ ratebhp_samples[index_segment], ratebhp_samples[index_segment + 1] };
|
||||
|
||||
const bool intersection_found = findIntersection(line_segment, ratebhp_twopoints_ipr, obtained_bhp);
|
||||
|
||||
return intersection_found;
|
||||
}
|
||||
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* OPM_AUTODIFF_VFPHELPERS_HPP_ */
|
||||
102
opm/simulators/wells/VFPInjProperties.cpp
Normal file
102
opm/simulators/wells/VFPInjProperties.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2015 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/simulators/wells/VFPInjProperties.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
|
||||
#include <opm/simulators/wells/VFPHelpers.hpp>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties(const VFPInjTable* table){
|
||||
m_tables[table->getTableNum()] = table;
|
||||
}
|
||||
|
||||
|
||||
VFPInjProperties::VFPInjProperties(const VFPInjProperties::InjTable& tables) {
|
||||
for (const auto& table : tables) {
|
||||
m_tables[table.first] = table.second.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double VFPInjProperties::bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp_arg) const {
|
||||
const VFPInjTable* table = detail::getTable(m_tables, table_id);
|
||||
|
||||
detail::VFPEvaluation retval = detail::bhp(table, aqua, liquid, vapour, thp_arg);
|
||||
return retval.value;
|
||||
}
|
||||
|
||||
|
||||
double VFPInjProperties::thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp_arg) const {
|
||||
const VFPInjTable* table = detail::getTable(m_tables, table_id);
|
||||
const VFPInjTable::array_type& data = table->getTable();
|
||||
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
|
||||
const std::vector<double> thp_array = table->getTHPAxis();
|
||||
int nthp = thp_array.size();
|
||||
|
||||
/**
|
||||
* Find the function bhp_array(thp) by creating a 1D view of the data
|
||||
* by interpolating for every value of thp. This might be somewhat
|
||||
* expensive, but let us assome that nthp is small
|
||||
*/
|
||||
auto flo_i = detail::findInterpData(flo, table->getFloAxis());
|
||||
std::vector<double> bhp_array(nthp);
|
||||
for (int i=0; i<nthp; ++i) {
|
||||
auto thp_i = detail::findInterpData(thp_array[i], thp_array);
|
||||
bhp_array[i] = detail::interpolate(data, flo_i, thp_i).value;
|
||||
}
|
||||
|
||||
double retval = detail::findTHP(bhp_array, thp_array, bhp_arg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const VFPInjTable* VFPInjProperties::getTable(const int table_id) const {
|
||||
return detail::getTable(m_tables, table_id);
|
||||
}
|
||||
|
||||
bool VFPInjProperties::hasTable(const int table_id) const {
|
||||
return detail::hasTable(m_tables, table_id);
|
||||
}
|
||||
|
||||
} //Namespace Opm
|
||||
169
opm/simulators/wells/VFPInjProperties.hpp
Normal file
169
opm/simulators/wells/VFPInjProperties.hpp
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright 2015 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_AUTODIFF_VFPINJPROPERTIES_HPP_
|
||||
#define OPM_AUTODIFF_VFPINJPROPERTIES_HPP_
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
#include <opm/material/densead/Evaluation.hpp>
|
||||
#include <opm/simulators/wells/VFPHelpers.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
class VFPInjProperties {
|
||||
public:
|
||||
/**
|
||||
* Empty constructor
|
||||
*/
|
||||
VFPInjProperties();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_table A *single* VFPINJ table
|
||||
*/
|
||||
explicit VFPInjProperties(const VFPInjTable* inj_table);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_tables A map of different VFPINJ tables.
|
||||
*/
|
||||
using InjTable = std::map<int, std::shared_ptr<const VFPInjTable> >;
|
||||
explicit VFPInjProperties(const InjTable& inj_tables);
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters given as
|
||||
* Evaluation
|
||||
* Each entry corresponds typically to one well.
|
||||
* @param table_id Table number to use. A negative entry (e.g., -1)
|
||||
* will indicate that no table is used, and the corresponding
|
||||
* BHP will be calculated as a constant -1e100.
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table, for each entry in the
|
||||
* input ADB objects.
|
||||
*/
|
||||
template <class EvalWell>
|
||||
EvalWell bhp(const int table_id,
|
||||
const EvalWell& aqua,
|
||||
const EvalWell& liquid,
|
||||
const EvalWell& vapour,
|
||||
const double& thp) const {
|
||||
|
||||
//Get the table
|
||||
const VFPInjTable* table = detail::getTable(m_tables, table_id);
|
||||
EvalWell bhp = 0.0 * aqua;
|
||||
|
||||
//Find interpolation variables
|
||||
EvalWell flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
|
||||
//Compute the BHP for each well independently
|
||||
if (table != nullptr) {
|
||||
//First, find the values to interpolate between
|
||||
//Value of FLO is negative in OPM for producers, but positive in VFP table
|
||||
auto flo_i = detail::findInterpData(flo.value(), table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData( thp, table->getTHPAxis()); // assume constant
|
||||
|
||||
detail::VFPEvaluation bhp_val = detail::interpolate(table->getTable(), flo_i, thp_i);
|
||||
|
||||
bhp = bhp_val.dflo * flo;
|
||||
bhp.setValue(bhp_val.value); // thp is assumed constant i.e.
|
||||
}
|
||||
else {
|
||||
bhp.setValue(-1e100); //Signal that this value has not been calculated properly, due to "missing" table
|
||||
}
|
||||
return bhp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table associated with the ID, or throws an exception if
|
||||
* the table does not exist
|
||||
*/
|
||||
const VFPInjTable* getTable(const int table_id) const;
|
||||
|
||||
/**
|
||||
* Check whether there is table associated with ID
|
||||
*/
|
||||
bool hasTable(const int table_id) const;
|
||||
|
||||
/**
|
||||
* Returns true if no vfp tables are in the current map
|
||||
*/
|
||||
bool empty() const {
|
||||
return m_tables.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp) const;
|
||||
|
||||
/**
|
||||
* Linear interpolation of thp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param bhp Bottom hole pressure
|
||||
*
|
||||
* @return The tubing hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp) const;
|
||||
|
||||
protected:
|
||||
// Map which connects the table number with the table itself
|
||||
std::map<int, const VFPInjTable*> m_tables;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
|
||||
#endif /* OPM_AUTODIFF_VFPINJPROPERTIES_HPP_ */
|
||||
255
opm/simulators/wells/VFPProdProperties.cpp
Normal file
255
opm/simulators/wells/VFPProdProperties.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
Copyright 2015 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/simulators/wells/VFPProdProperties.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
#include <opm/material/densead/Evaluation.hpp>
|
||||
#include <opm/simulators/wells/VFPHelpers.hpp>
|
||||
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
|
||||
|
||||
VFPProdProperties::VFPProdProperties() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
VFPProdProperties::VFPProdProperties(const VFPProdTable* table){
|
||||
m_tables[table->getTableNum()] = table;
|
||||
}
|
||||
|
||||
|
||||
VFPProdProperties::VFPProdProperties(const VFPProdProperties::ProdTable& tables) {
|
||||
for (const auto& table : tables) {
|
||||
m_tables[table.first] = table.second.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double VFPProdProperties::thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp_arg,
|
||||
const double& alq) const {
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
const VFPProdTable::array_type& data = table->getTable();
|
||||
|
||||
//Find interpolation variables
|
||||
double flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
double wfr = detail::getWFR(aqua, liquid, vapour, table->getWFRType());
|
||||
double gfr = detail::getGFR(aqua, liquid, vapour, table->getGFRType());
|
||||
|
||||
const std::vector<double> thp_array = table->getTHPAxis();
|
||||
int nthp = thp_array.size();
|
||||
|
||||
/**
|
||||
* Find the function bhp_array(thp) by creating a 1D view of the data
|
||||
* by interpolating for every value of thp. This might be somewhat
|
||||
* expensive, but let us assome that nthp is small
|
||||
* Recall that flo is negative in Opm, so switch the sign
|
||||
*/
|
||||
auto flo_i = detail::findInterpData(-flo, table->getFloAxis());
|
||||
auto wfr_i = detail::findInterpData( wfr, table->getWFRAxis());
|
||||
auto gfr_i = detail::findInterpData( gfr, table->getGFRAxis());
|
||||
auto alq_i = detail::findInterpData( alq, table->getALQAxis());
|
||||
std::vector<double> bhp_array(nthp);
|
||||
for (int i=0; i<nthp; ++i) {
|
||||
auto thp_i = detail::findInterpData(thp_array[i], thp_array);
|
||||
bhp_array[i] = detail::interpolate(data, flo_i, thp_i, wfr_i, gfr_i, alq_i).value;
|
||||
}
|
||||
|
||||
double retval = detail::findTHP(bhp_array, thp_array, bhp_arg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
double VFPProdProperties::bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp_arg,
|
||||
const double& alq) const {
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
|
||||
detail::VFPEvaluation retval = detail::bhp(table, aqua, liquid, vapour, thp_arg, alq);
|
||||
return retval.value;
|
||||
}
|
||||
|
||||
|
||||
const VFPProdTable* VFPProdProperties::getTable(const int table_id) const {
|
||||
return detail::getTable(m_tables, table_id);
|
||||
}
|
||||
|
||||
bool VFPProdProperties::hasTable(const int table_id) const {
|
||||
return detail::hasTable(m_tables, table_id);
|
||||
}
|
||||
|
||||
|
||||
std::vector<double>
|
||||
VFPProdProperties::
|
||||
bhpwithflo(const std::vector<double>& flos,
|
||||
const int table_id,
|
||||
const double wfr,
|
||||
const double gfr,
|
||||
const double thp,
|
||||
const double alq,
|
||||
const double dp) const
|
||||
{
|
||||
// Get the table
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
const auto thp_i = detail::findInterpData( thp, table->getTHPAxis()); // assume constant
|
||||
const auto wfr_i = detail::findInterpData( wfr, table->getWFRAxis());
|
||||
const auto gfr_i = detail::findInterpData( gfr, table->getGFRAxis());
|
||||
const auto alq_i = detail::findInterpData( alq, table->getALQAxis()); //assume constant
|
||||
|
||||
std::vector<double> bhps(flos.size(), 0.);
|
||||
for (size_t i = 0; i < flos.size(); ++i) {
|
||||
// Value of FLO is negative in OPM for producers, but positive in VFP table
|
||||
const auto flo_i = detail::findInterpData(-flos[i], table->getFloAxis());
|
||||
const detail::VFPEvaluation bhp_val = detail::interpolate(table->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i);
|
||||
|
||||
// TODO: this kind of breaks the conventions for the functions here by putting dp within the function
|
||||
bhps[i] = bhp_val.value - dp;
|
||||
}
|
||||
|
||||
return bhps;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
double
|
||||
VFPProdProperties::
|
||||
calculateBhpWithTHPTarget(const std::vector<double>& ipr_a,
|
||||
const std::vector<double>& ipr_b,
|
||||
const double bhp_limit,
|
||||
const double thp_table_id,
|
||||
const double thp_limit,
|
||||
const double alq,
|
||||
const double dp) const
|
||||
{
|
||||
// For producers, bhp_safe_limit is the highest BHP value that can still produce based on IPR
|
||||
double bhp_safe_limit = 1.e100;
|
||||
for (size_t i = 0; i < ipr_a.size(); ++i) {
|
||||
if (ipr_b[i] == 0.) continue;
|
||||
|
||||
const double bhp = ipr_a[i] / ipr_b[i];
|
||||
if (bhp < bhp_safe_limit) {
|
||||
bhp_safe_limit = bhp;
|
||||
}
|
||||
}
|
||||
|
||||
// Here, we use the middle point between the bhp_limit and bhp_safe_limit to calculate the ratio of the flow
|
||||
// and the middle point serves one of the two points to describe inflow performance relationship line
|
||||
const double bhp_middle = (bhp_limit + bhp_safe_limit) / 2.0;
|
||||
|
||||
// FLO is the rate based on the type specified with the VFP table
|
||||
// The two points correspond to the bhp values of bhp_limit, and the middle of bhp_limit and bhp_safe_limit
|
||||
// for producers, the rates are negative
|
||||
std::vector<double> rates_bhp_limit(ipr_a.size());
|
||||
std::vector<double> rates_bhp_middle(ipr_a.size());
|
||||
for (size_t i = 0; i < rates_bhp_limit.size(); ++i) {
|
||||
rates_bhp_limit[i] = bhp_limit * ipr_b[i] - ipr_a[i];
|
||||
rates_bhp_middle[i] = bhp_middle * ipr_b[i] - ipr_a[i];
|
||||
}
|
||||
|
||||
// TODO: we need to be careful that there is nothings wrong related to the indices here
|
||||
const int Water = BlackoilPhases::Aqua;
|
||||
const int Oil = BlackoilPhases::Liquid;
|
||||
const int Gas = BlackoilPhases::Vapour;
|
||||
|
||||
const VFPProdTable* table = detail::getTable(m_tables, thp_table_id);
|
||||
const double aqua_bhp_limit = rates_bhp_limit[Water];
|
||||
const double liquid_bhp_limit = rates_bhp_limit[Oil];
|
||||
const double vapour_bhp_limit = rates_bhp_limit[Gas];
|
||||
const double flo_bhp_limit = detail::getFlo(aqua_bhp_limit, liquid_bhp_limit, vapour_bhp_limit, table->getFloType() );
|
||||
|
||||
const double aqua_bhp_middle = rates_bhp_middle[Water];
|
||||
const double liquid_bhp_middle = rates_bhp_middle[Oil];
|
||||
const double vapour_bhp_middle = rates_bhp_middle[Gas];
|
||||
const double flo_bhp_middle = detail::getFlo(aqua_bhp_middle, liquid_bhp_middle, vapour_bhp_middle, table->getFloType() );
|
||||
|
||||
// we use the ratios based on the middle value of bhp_limit and bhp_safe_limit
|
||||
const double wfr = detail::getWFR(aqua_bhp_middle, liquid_bhp_middle, vapour_bhp_middle, table->getWFRType());
|
||||
const double gfr = detail::getGFR(aqua_bhp_middle, liquid_bhp_middle, vapour_bhp_middle, table->getGFRType());
|
||||
|
||||
// we get the flo sampling points from the table,
|
||||
// then extend it with zero and rate under bhp_limit for extrapolation
|
||||
std::vector<double> flo_samples = table->getFloAxis();
|
||||
|
||||
if (flo_samples[0] > 0.) {
|
||||
flo_samples.insert(flo_samples.begin(), 0.);
|
||||
}
|
||||
|
||||
if (flo_samples.back() < std::abs(flo_bhp_limit)) {
|
||||
flo_samples.push_back(std::abs(flo_bhp_limit));
|
||||
}
|
||||
|
||||
// kind of unncessarily following the tradation that producers should have negative rates
|
||||
// the key is here that it should be consistent with the function bhpwithflo
|
||||
for (double& value : flo_samples) {
|
||||
value = -value;
|
||||
}
|
||||
|
||||
// get the bhp sampling values based on the flo sample values
|
||||
const std::vector<double> bhp_flo_samples = bhpwithflo(flo_samples, thp_table_id, wfr, gfr, thp_limit, alq, dp);
|
||||
|
||||
std::vector<detail::RateBhpPair> ratebhp_samples;
|
||||
for (size_t i = 0; i < flo_samples.size(); ++i) {
|
||||
ratebhp_samples.push_back( detail::RateBhpPair{flo_samples[i], bhp_flo_samples[i]} );
|
||||
}
|
||||
|
||||
const std::array<detail::RateBhpPair, 2> ratebhp_twopoints_ipr {detail::RateBhpPair{flo_bhp_middle, bhp_middle},
|
||||
detail::RateBhpPair{flo_bhp_limit, bhp_limit} };
|
||||
|
||||
double obtain_bhp = 0.;
|
||||
const bool can_obtain_bhp_with_thp_limit = detail::findIntersectionForBhp(ratebhp_samples, ratebhp_twopoints_ipr, obtain_bhp);
|
||||
|
||||
// \Note: assuming that negative BHP does not make sense
|
||||
if (can_obtain_bhp_with_thp_limit && obtain_bhp > 0.) {
|
||||
// getting too high bhp that might cause negative rates (rates in the undesired direction)
|
||||
if (obtain_bhp >= bhp_safe_limit) {
|
||||
const std::string msg (" We are getting a too high BHP value from the THP constraint, which may "
|
||||
" cause problems later ");
|
||||
OpmLog::info("TOO_HIGH_BHP_FOUND_THP_TARGET", msg);
|
||||
|
||||
const std::string debug_msg = " obtain_bhp " + std::to_string(obtain_bhp)
|
||||
+ " bhp_safe_limit " + std::to_string(bhp_safe_limit)
|
||||
+ " thp limit " + std::to_string(thp_limit);
|
||||
OpmLog::debug(debug_msg);
|
||||
}
|
||||
return obtain_bhp;
|
||||
} else {
|
||||
OpmLog::warning("NO_BHP_FOUND_THP_TARGET", " we could not find a bhp value with thp target.");
|
||||
return -100.;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
207
opm/simulators/wells/VFPProdProperties.hpp
Normal file
207
opm/simulators/wells/VFPProdProperties.hpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
Copyright 2015 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_AUTODIFF_VFPPRODPROPERTIES_HPP_
|
||||
#define OPM_AUTODIFF_VFPPRODPROPERTIES_HPP_
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
#include <opm/material/densead/Evaluation.hpp>
|
||||
#include <opm/simulators/wells/VFPHelpers.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/**
|
||||
* Class which linearly interpolates BHP as a function of rate, tubing head pressure,
|
||||
* water fraction, gas fraction, and artificial lift for production VFP tables, and similarly
|
||||
* the BHP as a function of the rate and tubing head pressure.
|
||||
*/
|
||||
class VFPProdProperties {
|
||||
public:
|
||||
/**
|
||||
* Empty constructor
|
||||
*/
|
||||
VFPProdProperties();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param prod_table A *single* VFPPROD table
|
||||
*/
|
||||
explicit VFPProdProperties(const VFPProdTable* prod_table);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param prod_tables A map of different VFPPROD tables.
|
||||
*/
|
||||
using ProdTable = std::map<int, std::shared_ptr<const VFPProdTable> >;
|
||||
explicit VFPProdProperties(const ProdTable& prod_tables);
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters given as
|
||||
* Evalutions
|
||||
* Each entry corresponds typically to one well.
|
||||
* @param table_id Table number to use. A negative entry (e.g., -1)
|
||||
* will indicate that no table is used, and the corresponding
|
||||
* BHP will be calculated as a constant -1e100.
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
* @param alq Artificial lift or other parameter
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table, for each entry in the
|
||||
* input ADB objects.
|
||||
*/
|
||||
template <class EvalWell>
|
||||
EvalWell bhp(const int table_id,
|
||||
const EvalWell& aqua,
|
||||
const EvalWell& liquid,
|
||||
const EvalWell& vapour,
|
||||
const double& thp,
|
||||
const double& alq) const {
|
||||
|
||||
//Get the table
|
||||
const VFPProdTable* table = detail::getTable(m_tables, table_id);
|
||||
EvalWell bhp = 0.0 * aqua;
|
||||
|
||||
//Find interpolation variables
|
||||
EvalWell flo = detail::getFlo(aqua, liquid, vapour, table->getFloType());
|
||||
EvalWell wfr = detail::getWFR(aqua, liquid, vapour, table->getWFRType());
|
||||
EvalWell gfr = detail::getGFR(aqua, liquid, vapour, table->getGFRType());
|
||||
|
||||
//Compute the BHP for each well independently
|
||||
if (table != nullptr) {
|
||||
//First, find the values to interpolate between
|
||||
//Value of FLO is negative in OPM for producers, but positive in VFP table
|
||||
auto flo_i = detail::findInterpData(-flo.value(), table->getFloAxis());
|
||||
auto thp_i = detail::findInterpData( thp, table->getTHPAxis()); // assume constant
|
||||
auto wfr_i = detail::findInterpData( wfr.value(), table->getWFRAxis());
|
||||
auto gfr_i = detail::findInterpData( gfr.value(), table->getGFRAxis());
|
||||
auto alq_i = detail::findInterpData( alq, table->getALQAxis()); //assume constant
|
||||
|
||||
detail::VFPEvaluation bhp_val = detail::interpolate(table->getTable(), flo_i, thp_i, wfr_i, gfr_i, alq_i);
|
||||
|
||||
bhp = (bhp_val.dwfr * wfr) + (bhp_val.dgfr * gfr) - (bhp_val.dflo * flo);
|
||||
bhp.setValue(bhp_val.value);
|
||||
}
|
||||
else {
|
||||
bhp.setValue(-1e100); //Signal that this value has not been calculated properly, due to "missing" table
|
||||
}
|
||||
return bhp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear interpolation of bhp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param thp Tubing head pressure
|
||||
* @param alq Artificial lift or other parameter
|
||||
*
|
||||
* @return The bottom hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double bhp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& thp,
|
||||
const double& alq) const;
|
||||
|
||||
/**
|
||||
* Linear interpolation of thp as a function of the input parameters
|
||||
* @param table_id Table number to use
|
||||
* @param aqua Water phase
|
||||
* @param liquid Oil phase
|
||||
* @param vapour Gas phase
|
||||
* @param bhp Bottom hole pressure
|
||||
* @param alq Artificial lift or other parameter
|
||||
*
|
||||
* @return The tubing hole pressure, interpolated/extrapolated linearly using
|
||||
* the above parameters from the values in the input table.
|
||||
*/
|
||||
double thp(int table_id,
|
||||
const double& aqua,
|
||||
const double& liquid,
|
||||
const double& vapour,
|
||||
const double& bhp,
|
||||
const double& alq) const;
|
||||
|
||||
/**
|
||||
* Returns the table associated with the ID, or throws an exception if
|
||||
* the table does not exist
|
||||
*/
|
||||
const VFPProdTable* getTable(const int table_id) const;
|
||||
|
||||
/**
|
||||
* Check whether there is table associated with ID
|
||||
*/
|
||||
bool hasTable(const int table_id) const;
|
||||
|
||||
/**
|
||||
* Returns true if no vfp tables are in the current map
|
||||
*/
|
||||
bool empty() const {
|
||||
return m_tables.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the Bhp value from the THP target/constraint value
|
||||
* based on inflow performance relationship and VFP curves
|
||||
*/
|
||||
double
|
||||
calculateBhpWithTHPTarget(const std::vector<double>& ipr_a,
|
||||
const std::vector<double>& ipr_b,
|
||||
const double bhp_limit,
|
||||
const double thp_table_id,
|
||||
const double thp_limit,
|
||||
const double alq,
|
||||
const double dp) const;
|
||||
|
||||
protected:
|
||||
// calculate a group bhp values with a group of flo rate values
|
||||
std::vector<double> bhpwithflo(const std::vector<double>& flos,
|
||||
const int table_id,
|
||||
const double wfr,
|
||||
const double gfr,
|
||||
const double thp,
|
||||
const double alq,
|
||||
const double dp) const;
|
||||
|
||||
// Map which connects the table number with the table itself
|
||||
std::map<int, const VFPProdTable*> m_tables;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
} //namespace
|
||||
|
||||
|
||||
#endif /* OPM_AUTODIFF_VFPPRODPROPERTIES_HPP_ */
|
||||
87
opm/simulators/wells/VFPProperties.hpp
Normal file
87
opm/simulators/wells/VFPProperties.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2015 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_AUTODIFF_VFPPROPERTIES_HPP_
|
||||
#define OPM_AUTODIFF_VFPPROPERTIES_HPP_
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPInjTable.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/VFPProdTable.hpp>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/**
|
||||
* A thin wrapper class that holds one VFPProdProperties and one
|
||||
* VFPInjProperties object.
|
||||
*/
|
||||
template<typename VFPInjProp, typename VFPProdProp>
|
||||
class VFPProperties {
|
||||
public:
|
||||
VFPProperties() {}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_table A *single* VFPINJ table or NULL (no table)
|
||||
* @param prod_table A *single* VFPPROD table or NULL (no table)
|
||||
*/
|
||||
explicit VFPProperties(const VFPInjTable* inj_table, const VFPProdTable* prod_table)
|
||||
{
|
||||
if (inj_table != nullptr) {
|
||||
m_inj.reset(new VFPInjProp(inj_table));
|
||||
}
|
||||
if (prod_table != nullptr) {
|
||||
m_prod.reset(new VFPProdProp(prod_table));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Takes *no* ownership of data.
|
||||
* @param inj_tables A map of different VFPINJ tables.
|
||||
* @param prod_tables A map of different VFPPROD tables.
|
||||
*/
|
||||
VFPProperties(const std::map<int, std::shared_ptr<const VFPInjTable> >& inj_tables,
|
||||
const std::map<int, std::shared_ptr<const VFPProdTable> >& prod_tables)
|
||||
: m_inj(new VFPInjProp(inj_tables)), m_prod(new VFPProdProp(prod_tables)) {}
|
||||
|
||||
/**
|
||||
* Returns the VFP properties for injection wells
|
||||
*/
|
||||
const VFPInjProp* getInj() const {
|
||||
return m_inj.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the VFP properties for production wells
|
||||
*/
|
||||
const VFPProdProp* getProd() const {
|
||||
return m_prod.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<VFPInjProp> m_inj;
|
||||
std::shared_ptr<VFPProdProp> m_prod;
|
||||
};
|
||||
|
||||
|
||||
} //Namespace
|
||||
|
||||
#endif /* OPM_AUTODIFF_VFPPROPERTIES_HPP_ */
|
||||
130
opm/simulators/wells/WellConnectionAuxiliaryModule.hpp
Normal file
130
opm/simulators/wells/WellConnectionAuxiliaryModule.hpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
||||
Copyright 2017 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/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_WELLCONNECTIONAUXILIARYMODULE_HEADER_INCLUDED
|
||||
#define OPM_WELLCONNECTIONAUXILIARYMODULE_HEADER_INCLUDED
|
||||
|
||||
#include <ewoms/disc/common/baseauxiliarymodule.hh>
|
||||
|
||||
#include <opm/grid/CpGrid.hpp>
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/EclipseState.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
template<class TypeTag>
|
||||
class WellConnectionAuxiliaryModule
|
||||
: public Ewoms::BaseAuxiliaryModule<TypeTag>
|
||||
{
|
||||
typedef typename GET_PROP_TYPE(TypeTag, GlobalEqVector) GlobalEqVector;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, SparseMatrixAdapter) SparseMatrixAdapter;
|
||||
|
||||
public:
|
||||
|
||||
using NeighborSet = typename
|
||||
Ewoms::BaseAuxiliaryModule<TypeTag>::NeighborSet;
|
||||
|
||||
WellConnectionAuxiliaryModule(const Schedule& schedule,
|
||||
const Dune::CpGrid& grid)
|
||||
{
|
||||
// Create cartesian to compressed mapping
|
||||
const auto& globalCell = grid.globalCell();
|
||||
const auto& cartesianSize = grid.logicalCartesianSize();
|
||||
|
||||
auto size = cartesianSize[0]*cartesianSize[1]*cartesianSize[2];
|
||||
|
||||
std::vector<int> cartesianToCompressed(size, -1);
|
||||
auto begin = globalCell.begin();
|
||||
|
||||
for ( auto cell = begin, end= globalCell.end(); cell != end; ++cell )
|
||||
{
|
||||
cartesianToCompressed[ *cell ] = cell - begin;
|
||||
}
|
||||
|
||||
int last_time_step = schedule.getTimeMap().size() - 1;
|
||||
const auto& schedule_wells = schedule.getWells();
|
||||
wells_.reserve(schedule_wells.size());
|
||||
|
||||
// initialize the additional cell connections introduced by wells.
|
||||
for ( const auto well : schedule_wells )
|
||||
{
|
||||
std::vector<int> compressed_well_perforations;
|
||||
// All possible completions of the well
|
||||
const auto& completionSet = well->getConnections(last_time_step);
|
||||
compressed_well_perforations.reserve(completionSet.size());
|
||||
|
||||
for ( size_t c=0; c < completionSet.size(); c++ )
|
||||
{
|
||||
const auto& completion = completionSet.get(c);
|
||||
int i = completion.getI();
|
||||
int j = completion.getJ();
|
||||
int k = completion.getK();
|
||||
int cart_grid_idx = i + cartesianSize[0]*(j + cartesianSize[1]*k);
|
||||
int compressed_idx = cartesianToCompressed[cart_grid_idx];
|
||||
|
||||
if ( compressed_idx >= 0 ) // Ignore completions in inactive/remote cells.
|
||||
{
|
||||
compressed_well_perforations.push_back(compressed_idx);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! compressed_well_perforations.empty() )
|
||||
{
|
||||
std::sort(compressed_well_perforations.begin(),
|
||||
compressed_well_perforations.end());
|
||||
|
||||
wells_.push_back(compressed_well_perforations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned numDofs() const
|
||||
{
|
||||
// No extra dofs are inserted for wells.
|
||||
return 0;
|
||||
}
|
||||
|
||||
void addNeighbors(std::vector<NeighborSet>& neighbors) const
|
||||
{
|
||||
for(const auto well_perforations : wells_)
|
||||
{
|
||||
for(const auto& perforation : well_perforations)
|
||||
neighbors[perforation].insert(well_perforations.begin(),
|
||||
well_perforations.end());
|
||||
}
|
||||
}
|
||||
|
||||
void applyInitial()
|
||||
{}
|
||||
|
||||
void linearize(SparseMatrixAdapter& , GlobalEqVector&)
|
||||
{
|
||||
// Linearization is done in StandardDenseWells
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::vector<std::vector<int> > wells_;
|
||||
};
|
||||
|
||||
} // end namespace OPM
|
||||
#endif
|
||||
198
opm/simulators/wells/WellHelpers.hpp
Normal file
198
opm/simulators/wells/WellHelpers.hpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
Copyright 2016 SINTEF ICT, Applied Mathematics.
|
||||
Copyright 2016 Statoil ASA.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
OPM is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OPM is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPM_WELLHELPERS_HEADER_INCLUDED
|
||||
#define OPM_WELLHELPERS_HEADER_INCLUDED
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
// #include <opm/autodiff/AutoDiffHelpers.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
|
||||
|
||||
|
||||
namespace wellhelpers
|
||||
{
|
||||
|
||||
|
||||
inline
|
||||
double rateToCompare(const std::vector<double>& well_phase_flow_rate,
|
||||
const int well,
|
||||
const int num_phases,
|
||||
const double* distr)
|
||||
{
|
||||
double rate = 0.0;
|
||||
for (int phase = 0; phase < num_phases; ++phase) {
|
||||
// Important: well_phase_flow_rate is ordered with all phase rates for first
|
||||
// well first, then all phase rates for second well etc.
|
||||
rate += well_phase_flow_rate[well*num_phases + phase] * distr[phase];
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
inline
|
||||
bool constraintBroken(const std::vector<double>& bhp,
|
||||
const std::vector<double>& thp,
|
||||
const std::vector<double>& well_phase_flow_rate,
|
||||
const int well,
|
||||
const int num_phases,
|
||||
const WellType& well_type,
|
||||
const WellControls* wc,
|
||||
const int ctrl_index)
|
||||
{
|
||||
const WellControlType ctrl_type = well_controls_iget_type(wc, ctrl_index);
|
||||
const double target = well_controls_iget_target(wc, ctrl_index);
|
||||
const double* distr = well_controls_iget_distr(wc, ctrl_index);
|
||||
|
||||
bool broken = false;
|
||||
|
||||
switch (well_type) {
|
||||
case INJECTOR:
|
||||
{
|
||||
switch (ctrl_type) {
|
||||
case BHP:
|
||||
broken = bhp[well] > target;
|
||||
break;
|
||||
|
||||
case THP:
|
||||
broken = thp[well] > target;
|
||||
break;
|
||||
|
||||
case RESERVOIR_RATE: // Intentional fall-through
|
||||
case SURFACE_RATE:
|
||||
broken = rateToCompare(well_phase_flow_rate,
|
||||
well, num_phases, distr) > target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PRODUCER:
|
||||
{
|
||||
switch (ctrl_type) {
|
||||
case BHP:
|
||||
broken = bhp[well] < target;
|
||||
break;
|
||||
|
||||
case THP:
|
||||
broken = thp[well] < target;
|
||||
break;
|
||||
|
||||
case RESERVOIR_RATE: // Intentional fall-through
|
||||
case SURFACE_RATE:
|
||||
// Note that the rates compared below are negative,
|
||||
// so breaking the constraints means: too high flow rate
|
||||
// (as for injection).
|
||||
broken = rateToCompare(well_phase_flow_rate,
|
||||
well, num_phases, distr) < target;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Can only handle INJECTOR and PRODUCER wells.");
|
||||
}
|
||||
|
||||
return broken;
|
||||
}
|
||||
|
||||
|
||||
// --------- Types ---------
|
||||
|
||||
/**
|
||||
* Simple hydrostatic correction for VFP table
|
||||
* @param wells - wells struct
|
||||
* @param w Well number
|
||||
* @param vfp_table VFP table
|
||||
* @param well_perforation_densities Densities at well perforations
|
||||
* @param gravity Gravitational constant (e.g., 9.81...)
|
||||
*/
|
||||
inline
|
||||
double computeHydrostaticCorrection(const Wells& wells, const int w, double vfp_ref_depth,
|
||||
const double& rho, const double gravity) {
|
||||
if ( wells.well_connpos[w] == wells.well_connpos[w+1] )
|
||||
{
|
||||
// This is a well with no perforations.
|
||||
// If this is the last well we would subscript over the
|
||||
// bounds below.
|
||||
// we assume well_perforation_densities to be 0
|
||||
return 0;
|
||||
}
|
||||
const double well_ref_depth = wells.depth_ref[w];
|
||||
const double dh = vfp_ref_depth - well_ref_depth;
|
||||
const double dp = rho*gravity*dh;
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
inline
|
||||
double computeHydrostaticCorrection(const double well_ref_depth, const double vfp_ref_depth,
|
||||
const double rho, const double gravity) {
|
||||
const double dh = vfp_ref_depth - well_ref_depth;
|
||||
const double dp = rho * gravity * dh;
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
template <class Vector>
|
||||
inline
|
||||
Vector computeHydrostaticCorrection(const Wells& wells, const Vector vfp_ref_depth,
|
||||
const Vector& well_perforation_densities, const double gravity) {
|
||||
const int nw = wells.number_of_wells;
|
||||
Vector retval = Vector::Zero(nw);
|
||||
|
||||
#if HAVE_OPENMP
|
||||
#pragma omp parallel for schedule(static)
|
||||
#endif // HAVE_OPENMP
|
||||
for (int i=0; i<nw; ++i) {
|
||||
const int perf = wells.well_connpos[i];
|
||||
retval[i] = computeHydrostaticCorrection(wells, i, vfp_ref_depth[i], well_perforation_densities[perf], gravity);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
inline
|
||||
std::vector<double> computeHydrostaticCorrection(const Wells& wells, const std::vector<double>& vfp_ref_depth,
|
||||
const std::vector<double>& well_perforation_densities, const double gravity) {
|
||||
const int nw = wells.number_of_wells;
|
||||
std::vector<double> retval(nw,0.0);
|
||||
|
||||
#if HAVE_OPENMP
|
||||
#pragma omp parallel for schedule(static)
|
||||
#endif // HAVE_OPENMP
|
||||
for (int i=0; i<nw; ++i) {
|
||||
const int perf = wells.well_connpos[i];
|
||||
retval[i] = computeHydrostaticCorrection(wells, i, vfp_ref_depth[i], well_perforation_densities[perf], gravity);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
} // namespace wellhelpers
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
476
opm/simulators/wells/WellInterface.hpp
Normal file
476
opm/simulators/wells/WellInterface.hpp
Normal file
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2017 Statoil ASA.
|
||||
Copyright 2017 IRIS
|
||||
|
||||
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_WELLINTERFACE_HEADER_INCLUDED
|
||||
#define OPM_WELLINTERFACE_HEADER_INCLUDED
|
||||
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <opm/common/Exceptions.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellTestState.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/ScheduleEnums.hpp>
|
||||
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/core/wells/WellsManager.hpp>
|
||||
#include <opm/core/simulator/SimulatorReport.hpp>
|
||||
|
||||
#include <opm/simulators/wells/VFPProperties.hpp>
|
||||
#include <opm/simulators/wells/WellHelpers.hpp>
|
||||
#include <opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/autodiff/BlackoilModelParametersEbos.hpp>
|
||||
#include <opm/autodiff/RateConverter.hpp>
|
||||
|
||||
#include <opm/simulators/timestepping/ConvergenceReport.hpp>
|
||||
#include <opm/simulators/utils/DeferredLogger.hpp>
|
||||
|
||||
#include <ewoms/models/blackoil/blackoilpolymermodules.hh>
|
||||
#include <ewoms/models/blackoil/blackoilsolventmodules.hh>
|
||||
|
||||
#include<dune/common/fmatrix.hh>
|
||||
#include<dune/istl/bcrsmatrix.hh>
|
||||
#include<dune/istl/matrixmatrix.hh>
|
||||
|
||||
#include <opm/material/densead/Math.hpp>
|
||||
#include <opm/material/densead/Evaluation.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
|
||||
template<typename TypeTag>
|
||||
class WellInterface
|
||||
{
|
||||
public:
|
||||
|
||||
using WellState = WellStateFullyImplicitBlackoil;
|
||||
|
||||
typedef BlackoilModelParametersEbos<TypeTag> ModelParameters;
|
||||
|
||||
static const int Water = BlackoilPhases::Aqua;
|
||||
static const int Oil = BlackoilPhases::Liquid;
|
||||
static const int Gas = BlackoilPhases::Vapour;
|
||||
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Grid) Grid;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Simulator) Simulator;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, FluidSystem) FluidSystem;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, Indices) Indices;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, IntensiveQuantities) IntensiveQuantities;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, MaterialLaw) MaterialLaw;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, SparseMatrixAdapter) SparseMatrixAdapter;
|
||||
typedef typename GET_PROP_TYPE(TypeTag, RateVector) RateVector;
|
||||
|
||||
static const int numEq = Indices::numEq;
|
||||
typedef double Scalar;
|
||||
|
||||
typedef Dune::FieldVector<Scalar, numEq > VectorBlockType;
|
||||
typedef Dune::FieldMatrix<Scalar, numEq, numEq > MatrixBlockType;
|
||||
typedef typename SparseMatrixAdapter::IstlMatrix Mat;
|
||||
typedef Dune::BlockVector<VectorBlockType> BVector;
|
||||
typedef DenseAd::Evaluation<double, /*size=*/numEq> Eval;
|
||||
|
||||
typedef Ewoms::BlackOilPolymerModule<TypeTag> PolymerModule;
|
||||
|
||||
static const bool has_solvent = GET_PROP_VALUE(TypeTag, EnableSolvent);
|
||||
static const bool has_polymer = GET_PROP_VALUE(TypeTag, EnablePolymer);
|
||||
static const bool has_energy = GET_PROP_VALUE(TypeTag, EnableEnergy);
|
||||
// flag for polymer molecular weight related
|
||||
static const bool has_polymermw = GET_PROP_VALUE(TypeTag, EnablePolymerMW);
|
||||
static const int contiSolventEqIdx = Indices::contiSolventEqIdx;
|
||||
static const int contiPolymerEqIdx = Indices::contiPolymerEqIdx;
|
||||
// index for the polymer molecular weight continuity equation
|
||||
static const int contiPolymerMWEqIdx = Indices::contiPolymerMWEqIdx;
|
||||
|
||||
// For the conversion between the surface volume rate and resrevoir voidage rate
|
||||
using RateConverterType = RateConverter::
|
||||
SurfaceToReservoirVoidage<FluidSystem, std::vector<int> >;
|
||||
|
||||
/// Constructor
|
||||
WellInterface(const Well* well, const int time_step, const Wells* wells,
|
||||
const ModelParameters& param,
|
||||
const RateConverterType& rate_converter,
|
||||
const int pvtRegionIdx,
|
||||
const int num_components);
|
||||
|
||||
/// Virutal destructor
|
||||
virtual ~WellInterface() {}
|
||||
|
||||
/// Well name.
|
||||
const std::string& name() const;
|
||||
|
||||
/// Index of well in the wells struct and wellState
|
||||
int indexOfWell() const;
|
||||
|
||||
/// Well cells.
|
||||
const std::vector<int>& cells() const {return well_cells_; }
|
||||
|
||||
/// Well type, INJECTOR or PRODUCER.
|
||||
WellType wellType() const;
|
||||
|
||||
/// Well controls
|
||||
WellControls* wellControls() const;
|
||||
|
||||
void setVFPProperties(const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_arg);
|
||||
|
||||
virtual void init(const PhaseUsage* phase_usage_arg,
|
||||
const std::vector<double>& depth_arg,
|
||||
const double gravity_arg,
|
||||
const int num_cells);
|
||||
|
||||
virtual void initPrimaryVariablesEvaluation() const = 0;
|
||||
|
||||
virtual ConvergenceReport getWellConvergence(const std::vector<double>& B_avg, Opm::DeferredLogger& deferred_logger) const = 0;
|
||||
|
||||
virtual void solveEqAndUpdateWellState(WellState& well_state, Opm::DeferredLogger& deferred_logger) = 0;
|
||||
|
||||
virtual void assembleWellEq(const Simulator& ebosSimulator,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger
|
||||
) = 0;
|
||||
|
||||
void updateWellTestState(const WellState& well_state,
|
||||
const double& simulationTime,
|
||||
const bool& writeMessageToOPMLog,
|
||||
WellTestState& wellTestState,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void setWellEfficiencyFactor(const double efficiency_factor);
|
||||
|
||||
void computeRepRadiusPerfLength(const Grid& grid, const std::vector<int>& cartesian_to_compressed, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
/// using the solution x to recover the solution xw for wells and applying
|
||||
/// xw to update Well State
|
||||
virtual void recoverWellSolutionAndUpdateWellState(const BVector& x,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const = 0;
|
||||
|
||||
/// Ax = Ax - C D^-1 B x
|
||||
virtual void apply(const BVector& x, BVector& Ax) const = 0;
|
||||
|
||||
/// r = r - C D^-1 Rw
|
||||
virtual void apply(BVector& r) const = 0;
|
||||
|
||||
// TODO: before we decide to put more information under mutable, this function is not const
|
||||
virtual void computeWellPotentials(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
std::vector<double>& well_potentials,
|
||||
Opm::DeferredLogger& deferred_logger) = 0;
|
||||
|
||||
virtual void updateWellStateWithTarget(const Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const = 0;
|
||||
|
||||
void updateWellControl(/* const */ Simulator& ebos_simulator,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) /* const */;
|
||||
|
||||
virtual void updatePrimaryVariables(const WellState& well_state, Opm::DeferredLogger& deferred_logger) const = 0;
|
||||
|
||||
virtual void calculateExplicitQuantities(const Simulator& ebosSimulator,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) = 0; // should be const?
|
||||
|
||||
/// \brief Wether the Jacobian will also have well contributions in it.
|
||||
virtual bool jacobianContainsWellContributions() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// updating the voidage rates in well_state when requested
|
||||
void calculateReservoirRates(WellState& well_state) const;
|
||||
|
||||
// Add well contributions to matrix
|
||||
virtual void addWellContributions(Mat&) const
|
||||
{}
|
||||
|
||||
void addCellRates(RateVector& rates, int cellIdx) const;
|
||||
|
||||
Scalar volumetricSurfaceRateForConnection(int cellIdx, int phaseIdx) const;
|
||||
|
||||
|
||||
template <class EvalWell>
|
||||
Eval restrictEval(const EvalWell& in) const
|
||||
{
|
||||
Eval out = 0.0;
|
||||
out.setValue(in.value());
|
||||
for(int eqIdx = 0; eqIdx < numEq;++eqIdx) {
|
||||
out.setDerivative(eqIdx, in.derivative(eqIdx));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void closeCompletions(WellTestState& wellTestState);
|
||||
|
||||
const Well* wellEcl() const;
|
||||
|
||||
// TODO: theoretically, it should be a const function
|
||||
// Simulator is not const is because that assembleWellEq is non-const Simulator
|
||||
void wellTesting(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
const WellTestConfig::Reason testing_reason,
|
||||
/* const */ WellState& well_state, WellTestState& welltest_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void updatePerforatedCell(std::vector<bool>& is_cell_perforated);
|
||||
|
||||
virtual void checkWellOperability(const Simulator& ebos_simulator, const WellState& well_state, Opm::DeferredLogger& deferred_logger) = 0;
|
||||
|
||||
// whether the well is operable
|
||||
bool isOperable() const;
|
||||
|
||||
/// Returns true if the well has one or more THP limits/constraints.
|
||||
bool wellHasTHPConstraints() const;
|
||||
|
||||
/// Returns true if the well is currently in prediction mode (i.e. not history mode).
|
||||
bool underPredictionMode(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// update perforation water throughput based on solved water rate
|
||||
virtual void updateWaterThroughput(const double dt, WellState& well_state) const = 0;
|
||||
|
||||
protected:
|
||||
|
||||
// to indicate a invalid completion
|
||||
static const int INVALIDCOMPLETION = INT_MAX;
|
||||
|
||||
const Well* well_ecl_;
|
||||
|
||||
const int current_step_;
|
||||
|
||||
// the index of well in Wells struct
|
||||
int index_of_well_;
|
||||
|
||||
// simulation parameters
|
||||
const ModelParameters& param_;
|
||||
|
||||
// well type
|
||||
// INJECTOR or PRODUCER
|
||||
enum WellType well_type_;
|
||||
|
||||
// number of phases
|
||||
int number_of_phases_;
|
||||
|
||||
// component fractions for each well
|
||||
// typically, it should apply to injection wells
|
||||
std::vector<double> comp_frac_;
|
||||
|
||||
// controls for this well
|
||||
struct WellControls* well_controls_;
|
||||
|
||||
// number of the perforations for this well
|
||||
int number_of_perforations_;
|
||||
|
||||
// record the index of the first perforation
|
||||
// of states of individual well.
|
||||
int first_perf_;
|
||||
|
||||
// well index for each perforation
|
||||
std::vector<double> well_index_;
|
||||
|
||||
// depth for each perforation
|
||||
std::vector<double> perf_depth_;
|
||||
|
||||
// reference depth for the BHP
|
||||
double ref_depth_;
|
||||
|
||||
double well_efficiency_factor_;
|
||||
|
||||
// cell index for each well perforation
|
||||
std::vector<int> well_cells_;
|
||||
|
||||
// saturation table nubmer for each well perforation
|
||||
std::vector<int> saturation_table_number_;
|
||||
|
||||
// representative radius of the perforations, used in shear calculation
|
||||
std::vector<double> perf_rep_radius_;
|
||||
|
||||
// length of the perforations, use in shear calculation
|
||||
std::vector<double> perf_length_;
|
||||
|
||||
// well bore diameter
|
||||
std::vector<double> bore_diameters_;
|
||||
|
||||
const PhaseUsage* phase_usage_;
|
||||
|
||||
bool getAllowCrossFlow() const;
|
||||
|
||||
const VFPProperties<VFPInjProperties,VFPProdProperties>* vfp_properties_;
|
||||
|
||||
double gravity_;
|
||||
|
||||
// For the conversion between the surface volume rate and resrevoir voidage rate
|
||||
const RateConverterType& rateConverter_;
|
||||
|
||||
// The pvt region of the well. We assume
|
||||
// We assume a well to not penetrate more than one pvt region.
|
||||
const int pvtRegionIdx_;
|
||||
|
||||
const int num_components_;
|
||||
|
||||
std::vector<RateVector> connectionRates_;
|
||||
|
||||
const PhaseUsage& phaseUsage() const;
|
||||
|
||||
int flowPhaseToEbosCompIdx( const int phaseIdx ) const;
|
||||
|
||||
int ebosCompIdxToFlowCompIdx( const unsigned compIdx ) const;
|
||||
|
||||
double wsolvent() const;
|
||||
|
||||
double wpolymer() const;
|
||||
|
||||
bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
double getTHPConstraint(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
int getTHPControlIndex() const;
|
||||
|
||||
// Component fractions for each phase for the well
|
||||
const std::vector<double>& compFrac() const;
|
||||
|
||||
double mostStrictBhpFromBhpLimits(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
// a tuple type for ratio limit check.
|
||||
// first value indicates whether ratio limit is violated, when the ratio limit is not violated, the following two
|
||||
// values should not be used.
|
||||
// second value indicates the index of the worst-offending completion.
|
||||
// the last value indicates the extent of the violation for the worst-offending completion, which is defined by
|
||||
// the ratio of the actual value to the value of the violated limit.
|
||||
using RatioCheckTuple = std::tuple<bool, int, double>;
|
||||
|
||||
RatioCheckTuple checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits,
|
||||
const WellState& well_state) const;
|
||||
|
||||
RatioCheckTuple checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits,
|
||||
const WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
double scalingFactor(const int comp_idx) const;
|
||||
|
||||
// whether a well is specified with a non-zero and valid VFP table number
|
||||
bool isVFPActive(Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
struct OperabilityStatus;
|
||||
|
||||
OperabilityStatus operability_status_;
|
||||
|
||||
void wellTestingEconomic(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
const WellState& well_state, WellTestState& welltest_state, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
virtual void wellTestingPhysical(Simulator& simulator, const std::vector<double>& B_avg,
|
||||
const double simulation_time, const int report_step,
|
||||
WellState& well_state, WellTestState& welltest_state, Opm::DeferredLogger& deferred_logger) = 0;
|
||||
|
||||
void updateWellTestStateEconomic(const WellState& well_state,
|
||||
const double simulation_time,
|
||||
const bool write_message_to_opmlog,
|
||||
WellTestState& well_test_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void updateWellTestStatePhysical(const WellState& well_state,
|
||||
const double simulation_time,
|
||||
const bool write_message_to_opmlog,
|
||||
WellTestState& well_test_state,
|
||||
Opm::DeferredLogger& deferred_logger) const;
|
||||
|
||||
void solveWellForTesting(Simulator& ebosSimulator, WellState& well_state,
|
||||
const std::vector<double>& B_avg,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
bool solveWellEqUntilConverged(Simulator& ebosSimulator,
|
||||
const std::vector<double>& B_avg,
|
||||
WellState& well_state,
|
||||
Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
void scaleProductivityIndex(const int perfIdx, double& productivity_index, const bool new_well, Opm::DeferredLogger& deferred_logger);
|
||||
|
||||
// count the number of times an output log message is created in the productivity
|
||||
// index calculations
|
||||
int well_productivity_index_logger_counter_;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// definition of the struct OperabilityStatus
|
||||
template<typename TypeTag>
|
||||
struct
|
||||
WellInterface<TypeTag>::
|
||||
OperabilityStatus {
|
||||
bool isOperable() const {
|
||||
if (!operable_under_only_bhp_limit) {
|
||||
return false;
|
||||
} else {
|
||||
return ( (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()) );
|
||||
}
|
||||
}
|
||||
|
||||
bool isOperableUnderBHPLimit() const {
|
||||
return operable_under_only_bhp_limit && obey_thp_limit_under_bhp_limit;
|
||||
}
|
||||
|
||||
bool isOperableUnderTHPLimit() const {
|
||||
return can_obtain_bhp_with_thp_limit && obey_bhp_limit_with_thp_limit;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
operable_under_only_bhp_limit = true;
|
||||
obey_thp_limit_under_bhp_limit = true;
|
||||
can_obtain_bhp_with_thp_limit = true;
|
||||
obey_bhp_limit_with_thp_limit = true;
|
||||
}
|
||||
|
||||
// whether the well can be operated under bhp limit
|
||||
// without considering other limits.
|
||||
// if it is false, then the well is not operable for sure.
|
||||
bool operable_under_only_bhp_limit = true;
|
||||
// if the well can be operated under bhp limit, will it obey(not violate)
|
||||
// the thp limit when operated under bhp limit
|
||||
bool obey_thp_limit_under_bhp_limit = true;
|
||||
// whether the well operate under the thp limit only
|
||||
bool can_obtain_bhp_with_thp_limit = true;
|
||||
// whether the well obey bhp limit when operated under thp limit
|
||||
bool obey_bhp_limit_with_thp_limit = true;
|
||||
|
||||
};
|
||||
const std::string modestring[4] = { "BHP", "THP", "RESERVOIR_RATE", "SURFACE_RATE" };
|
||||
|
||||
}
|
||||
|
||||
#include "WellInterface_impl.hpp"
|
||||
|
||||
#endif // OPM_WELLINTERFACE_HEADER_INCLUDED
|
||||
1337
opm/simulators/wells/WellInterface_impl.hpp
Normal file
1337
opm/simulators/wells/WellInterface_impl.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1033
opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp
Normal file
1033
opm/simulators/wells/WellStateFullyImplicitBlackoil.hpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user