mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-01-13 09:51:57 -06:00
adding initMSWell() to handle MS well related WellState.
This commit is contained in:
parent
315c4a1659
commit
d694a72b53
@ -252,7 +252,6 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/autodiff/StandardWell_impl.hpp
|
||||
opm/autodiff/MultisegmentWell.hpp
|
||||
opm/autodiff/MultisegmentWell_impl.hpp
|
||||
opm/autodiff/WellStateMSWell.hpp
|
||||
opm/autodiff/StandardWellsDense.hpp
|
||||
opm/autodiff/StandardWellsSolvent.hpp
|
||||
opm/autodiff/StandardWellsSolvent_impl.hpp
|
||||
|
@ -175,6 +175,14 @@ namespace Opm
|
||||
// when no perforation is related to the segment, use it outlet segment's cell.
|
||||
std::vector<int> segment_cell_;
|
||||
|
||||
// the completions that is related to each segment
|
||||
// the completions's ids are their location 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_;
|
||||
|
||||
// Things are easy to get from SegmentSet
|
||||
// segment_volume_, segment_cross_area_, segment_length_(total length), segment_depth_
|
||||
// segment_internal_diameter_, segment_roughness_
|
||||
|
@ -60,8 +60,7 @@ namespace Opm
|
||||
// it can also use the cell center, which is the same for StandardWell.
|
||||
// For the last case, should we update the depth with the depth_arg? For the
|
||||
// future, it can be a source of wrong result with Multisegment well.
|
||||
// More facility will be required from the opm-parser to make all the situations
|
||||
// make sense.
|
||||
// An indicator from the opm-parser should indicate what kind of depth we should use here.
|
||||
}
|
||||
|
||||
|
||||
|
@ -298,6 +298,13 @@ public:
|
||||
|
||||
const auto& wells_ecl = eclState().getSchedule().getWells(timer.currentStepNum());
|
||||
extractLegacyCellPvtRegionIndex_();
|
||||
// handling MS well related
|
||||
for (const auto& well : wells_ecl) {
|
||||
if (well->isMultiSegment(timer.currentStepNum())) { // there is one well is MS well
|
||||
well_state.initMSWell(wells, wells_ecl, timer.currentStepNum());
|
||||
break;
|
||||
}
|
||||
}
|
||||
WellModel well_model(wells, &(wells_manager.wellCollection()), wells_ecl, model_param_,
|
||||
rateConverter_, terminal_output_, timer.currentStepNum(), legacyCellPvtRegionIdx_);
|
||||
|
||||
|
@ -651,6 +651,8 @@ namespace Opm {
|
||||
// after restarting, the well_controls can be modified while
|
||||
// the well_state still uses the old control index
|
||||
// we need to synchronize these two.
|
||||
// keep in mind that we set the control index of well_state to be the same with
|
||||
// with the wellControls from the deck when we create well_state at the beginning of the report step
|
||||
resetWellControlFromState(well_state);
|
||||
|
||||
if (wellCollection()->groupControlActive()) {
|
||||
|
322
opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp
Normal file
322
opm/autodiff/WellStateFullyImplicitBlackoilDense.hpp
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
Copyright 2016 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_WELLSTATEFULLYIMPLICITBLACKOILDENSE_HEADER_INCLUDED
|
||||
#define OPM_WELLSTATEFULLYIMPLICITBLACKOILDENSE_HEADER_INCLUDED
|
||||
|
||||
|
||||
#include <opm/core/wells.h>
|
||||
#include <opm/core/well_controls.h>
|
||||
#include <opm/core/simulator/WellState.hpp>
|
||||
#include <opm/autodiff/BlackoilModelEnums.hpp>
|
||||
#include <opm/autodiff/WellStateFullyImplicitBlackoil.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/common/ErrorMacros.hpp>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
/// The state of a set of wells, tailored for use by the fully
|
||||
/// implicit blackoil simulator.
|
||||
class WellStateFullyImplicitBlackoilDense
|
||||
: public WellStateFullyImplicitBlackoil
|
||||
{
|
||||
typedef WellStateFullyImplicitBlackoil BaseType;
|
||||
public:
|
||||
typedef BaseType :: WellMapType WellMapType;
|
||||
|
||||
using BaseType :: wellRates;
|
||||
using BaseType :: bhp;
|
||||
using BaseType :: perfPress;
|
||||
using BaseType :: wellMap;
|
||||
using BaseType :: numWells;
|
||||
using BaseType :: numPhases;
|
||||
using BaseType :: perfPhaseRates;
|
||||
using BaseType :: currentControls;
|
||||
|
||||
/// Allocate and initialize if wells is non-null. Also tries
|
||||
/// to give useful initial values to the bhp(), wellRates()
|
||||
/// and perfPhaseRates() fields, depending on controls
|
||||
template <class PrevWellState>
|
||||
void init(const Wells* wells, const std::vector<double>& cellPressures, const PrevWellState& prevState, const PhaseUsage& pu)
|
||||
{
|
||||
|
||||
// call init on base class
|
||||
BaseType :: init(wells, cellPressures, prevState);
|
||||
|
||||
|
||||
const int nw = wells->number_of_wells;
|
||||
if (nw == 0) {
|
||||
return;
|
||||
}
|
||||
const int nperf = wells->well_connpos[nw];
|
||||
perfRateSolvent_.clear();
|
||||
perfRateSolvent_.resize(nperf, 0.0);
|
||||
|
||||
if (pu.has_solvent) {
|
||||
|
||||
// intialize wells that have been there before
|
||||
// order may change so the mapping is based on the well name
|
||||
if( ! prevState.wellMap().empty() )
|
||||
{
|
||||
typedef typename WellMapType :: const_iterator const_iterator;
|
||||
const_iterator end = prevState.wellMap().end();
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
std::string name( wells->name[ w ] );
|
||||
const_iterator it = prevState.wellMap().find( name );
|
||||
if( it != end )
|
||||
{
|
||||
const int newIndex = w;
|
||||
|
||||
// perfSolventRates
|
||||
int oldPerf_idx = (*it).second[ 1 ];
|
||||
const int num_perf_old_well = (*it).second[ 2 ];
|
||||
const int num_perf_this_well = wells->well_connpos[newIndex + 1] - wells->well_connpos[newIndex];
|
||||
if( num_perf_old_well == num_perf_this_well )
|
||||
{
|
||||
for (int perf = wells->well_connpos[ newIndex ];
|
||||
perf < wells->well_connpos[ newIndex + 1]; ++perf, ++oldPerf_idx )
|
||||
{
|
||||
perfRateSolvent()[ perf ] = prevState.perfRateSolvent()[ oldPerf_idx ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// init the MS well related.
|
||||
void initMSWell(const Wells* wells, const std::vector<const Well*>& wells_ecl, const int time_step)
|
||||
{
|
||||
// still using the order in wells
|
||||
const int nw = wells->number_of_wells;
|
||||
if (nw == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
nseg_ = 0.;
|
||||
// in the init function, the well rates and perforation rates have been initialized or copied from prevState
|
||||
// what we do here, is to set the segment rates and perforation rates
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const int nw_wells_ecl = wells_ecl.size();
|
||||
int index_well_ecl = 0;
|
||||
const std::string well_name(wells->name[w]);
|
||||
for (; index_well_ecl < nw_wells_ecl; ++index_well_ecl) {
|
||||
if (well_name == wells_ecl[index_well_ecl]->name()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// It should be able to find in wells_ecl.
|
||||
if (index_well_ecl == nw_wells_ecl) {
|
||||
OPM_THROW(std::logic_error, "Could not find well " << well_name << " in wells_ecl ");
|
||||
}
|
||||
|
||||
const Well* well_ecl = wells_ecl[index_well_ecl];
|
||||
top_segment_loc_.push_back(nseg_);
|
||||
if ( !well_ecl->isMultiSegment(time_step) ) { // not multi-segment well
|
||||
nseg_ += 1;
|
||||
segpress_.push_back(bhp()[w]);
|
||||
const int np = numPhases();
|
||||
for (int p = 0; p < np; ++p) {
|
||||
segrates_.push_back(wellRates()[np * w + p]);
|
||||
}
|
||||
} else { // it is a multi-segment well
|
||||
const SegmentSet& segment_set = well_ecl->getSegmentSet(time_step);
|
||||
// assuming the oder of the perforations in well_ecl is the same with Wells
|
||||
const CompletionSet& completion_set = well_ecl->getCompletions(time_step);
|
||||
const int nseg = segment_set.numberSegment();
|
||||
const int nperf = completion_set.size();
|
||||
nseg_ += nseg;
|
||||
// we need to know for each segment, how many perforation it has and how many segments using it as outlet_segment
|
||||
// that is why I think we should use a well model to initialize the WellState here
|
||||
std::vector<std::vector<int>> segment_perforations(nseg);
|
||||
for (int perf = 0; perf < nperf; ++perf) {
|
||||
const Completion& completion = completion_set.get(perf);
|
||||
const int segment_number = completion.getSegmentNumber();
|
||||
const int segment_location = segment_set.numberToLocation(segment_number);
|
||||
segment_perforations[segment_location].push_back(perf);
|
||||
}
|
||||
|
||||
std::vector<std::vector<int>> segment_inlets(nseg);
|
||||
for (int seg = 0; seg < nseg; ++seg) {
|
||||
const Segment& segment = segment_set[seg];
|
||||
const int segment_number = segment.segmentNumber();
|
||||
const int outlet_segment_number = segment.outletSegment();
|
||||
if (outlet_segment_number > 0) {
|
||||
const int segment_location = segment_set.numberToLocation(segment_number);
|
||||
const int outlet_segment_location = segment_set.numberToLocation(outlet_segment_number);
|
||||
segment_inlets[outlet_segment_location].push_back(segment_location);
|
||||
}
|
||||
}
|
||||
|
||||
// for the segrates_, now it becomes a recursive solution procedure.
|
||||
{
|
||||
const int np = numPhases();
|
||||
const int start_perf = wells->well_connpos[w];
|
||||
const int start_perf_next_well = wells->well_connpos[w + 1];
|
||||
assert(nperf == (start_perf_next_well - start_perf)); // make sure the information from wells_ecl consistent with wells
|
||||
const std::vector<double> perforation_rates(perfPhaseRates().begin() + np * start_perf,
|
||||
perfPhaseRates().begin() + np * start_perf_next_well); // the perforation rates for this well
|
||||
std::vector<double> segment_rates;
|
||||
calculateSegmentRates(segment_inlets, segment_perforations, perforation_rates, np, 0 /* top segment */, segment_rates);
|
||||
std::copy(segment_rates.begin(), segment_rates.end(), std::back_inserter(segrates_));
|
||||
}
|
||||
|
||||
// for the segment pressure, the segment pressure is the same with the first perforation
|
||||
// if there is no perforation associated with this segment, it uses the pressure from the outlet segment
|
||||
// which requres the ordering is successful
|
||||
// TODO: maybe also check the relation with the cell?
|
||||
// Not sure what is the best way to handle the initialization, hopefully, the bad initialization can be
|
||||
// improved during the solveWellEq process
|
||||
{
|
||||
// top segment is always the first one, and its pressure is the well bhp
|
||||
segpress_.push_back(bhp()[w]);
|
||||
const int top_segment = top_segment_loc_[w];
|
||||
const int start_perf = wells->well_connpos[w];
|
||||
for (int seg = 1; seg < nseg; ++seg) {
|
||||
if ( !segment_perforations[seg].empty() ) {
|
||||
const int first_perf = segment_perforations[seg][0];
|
||||
segpress_.push_back(perfPress()[start_perf + first_perf]);
|
||||
} else {
|
||||
// segpress_.push_back(bhp); // may not be a good decision
|
||||
// using the outlet segment pressure // it needs the ordering is correct
|
||||
const int outlet_seg = segment_set[seg].outletSegment();
|
||||
segpress_.push_back(segpress_[top_segment + segment_set.numberToLocation(outlet_seg)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(int(segpress_.size()) == nseg_);
|
||||
assert(int(segrates_.size()) == nseg_ * numPhases() );
|
||||
// maybe also check whether the segment rates of the top segments consistent with the well rates.
|
||||
}
|
||||
|
||||
static void calculateSegmentRates(const std::vector<std::vector<int>>& segment_inlets, const std::vector<std::vector<int>>&segment_perforations,
|
||||
const std::vector<double>& perforation_rates, const int np, const int segment, std::vector<double>& segment_rates)
|
||||
{
|
||||
// the rate of the segment equals to the sum of the contribution from the perforations and inlet segment rates.
|
||||
// the first segment is always the top segment, its rates should be equal to the well rates.
|
||||
assert(segment_inlets.size() == segment_perforations.size());
|
||||
const int nseg = segment_inlets.size();
|
||||
if (segment == 0) { // beginning the calculation
|
||||
segment_rates.resize(np * nseg, 0.0);
|
||||
}
|
||||
// contributions from the perforations belong to this segment
|
||||
for (const int& perf : segment_perforations[segment]) {
|
||||
for (int p = 0; p < np; ++p) {
|
||||
segment_rates[np * segment + p] += perforation_rates[np * perf + p];
|
||||
}
|
||||
}
|
||||
for (const int& inlet_seg : segment_inlets[segment]) {
|
||||
calculateSegmentRates(segment_inlets, segment_perforations, perforation_rates, np, inlet_seg, segment_rates);
|
||||
for (int p = 0; p < np; ++p) {
|
||||
segment_rates[np * segment + p] += segment_rates[np * inlet_seg + p];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class State>
|
||||
void resize(const Wells* wells, const State& state, const PhaseUsage& pu ) {
|
||||
const WellStateFullyImplicitBlackoilDense dummy_state{}; // Init with an empty previous state only resizes
|
||||
init(wells, state.pressure(), dummy_state, pu) ;
|
||||
}
|
||||
|
||||
/// One rate pr well connection.
|
||||
std::vector<double>& perfRateSolvent() { return perfRateSolvent_; }
|
||||
const std::vector<double>& perfRateSolvent() const { return perfRateSolvent_; }
|
||||
|
||||
/// One rate pr well
|
||||
double solventWellRate(const int w) const {
|
||||
double solvent_well_rate = 0.0;
|
||||
for (int perf = wells_->well_connpos[w]; perf < wells_->well_connpos[w+1]; ++perf ) {
|
||||
solvent_well_rate += perfRateSolvent_[perf];
|
||||
}
|
||||
return solvent_well_rate;
|
||||
}
|
||||
|
||||
|
||||
data::Wells report(const PhaseUsage& pu) const override {
|
||||
data::Wells res = BaseType::report(pu);
|
||||
const int nw = WellState::numWells();
|
||||
// If there are now wells numPhases throws a floating point
|
||||
// exception.
|
||||
if (nw == 0) {
|
||||
return res;
|
||||
}
|
||||
if (pu.has_solvent) {
|
||||
// add solvent component
|
||||
for( int w = 0; w < nw; ++w ) {
|
||||
using rt = data::Rates::opt;
|
||||
res.at( wells_->name[ w ]).rates.set( rt::solvent, solventWellRate(w) );
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<double>& segRates()
|
||||
{
|
||||
return segrates_;
|
||||
}
|
||||
|
||||
std::vector<double>& segPress()
|
||||
{
|
||||
return segpress_;
|
||||
}
|
||||
|
||||
int numSegment() const
|
||||
{
|
||||
return nseg_;
|
||||
}
|
||||
|
||||
int topSegment(const int w) const
|
||||
{
|
||||
assert(w < int(top_segment_loc_.size()) );
|
||||
return top_segment_loc_[w];
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::vector<double> perfRateSolvent_;
|
||||
|
||||
// MS well related
|
||||
// for StandardWell, the segment number will be one
|
||||
std::vector<double> segrates_;
|
||||
std::vector<double> segpress_;
|
||||
std::vector<int> top_segment_loc_; // the index of the top segments
|
||||
int nseg_; // number of the segments
|
||||
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
|
||||
#endif // OPM_WELLSTATEFULLYIMPLICITBLACKOILDENSE_HEADER_INCLUDED
|
Loading…
Reference in New Issue
Block a user