mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #3335 from joakim-hove/segment-state2
Segment state2
This commit is contained in:
@@ -60,6 +60,7 @@ list (APPEND MAIN_SOURCE_FILES
|
||||
opm/simulators/wells/GlobalWellInfo.cpp
|
||||
opm/simulators/wells/GroupState.cpp
|
||||
opm/simulators/wells/ParallelWellInfo.cpp
|
||||
opm/simulators/wells/SegmentState.cpp
|
||||
opm/simulators/wells/TargetCalculator.cpp
|
||||
opm/simulators/wells/VFPHelpers.cpp
|
||||
opm/simulators/wells/VFPProdProperties.cpp
|
||||
@@ -205,6 +206,7 @@ list (APPEND PUBLIC_HEADER_FILES
|
||||
opm/core/props/phaseUsageFromDeck.hpp
|
||||
opm/core/props/satfunc/RelpermDiagnostics.hpp
|
||||
opm/simulators/timestepping/SimulatorReport.hpp
|
||||
opm/simulators/wells/SegmentState.hpp
|
||||
opm/simulators/wells/WellContainer.hpp
|
||||
opm/simulators/aquifers/AquiferInterface.hpp
|
||||
opm/simulators/aquifers/AquiferCarterTracy.hpp
|
||||
|
||||
@@ -2021,8 +2021,9 @@ namespace Opm {
|
||||
// \Note: eventually we need to hanlde the situations that some segments are shut
|
||||
assert(0u + segment_set.size() == rst_segments.size());
|
||||
|
||||
auto segment_pressure = well_state.segPress(well_index);
|
||||
auto segment_rates = well_state.segRates(well_index);
|
||||
auto& segments = well_state.segments(well_index);
|
||||
auto& segment_pressure = segments.pressure;
|
||||
auto& segment_rates = segments.rates;
|
||||
for (const auto& rst_segment : rst_segments) {
|
||||
const int segment_index = segment_set.segmentNumberToIndex(rst_segment.first);
|
||||
|
||||
|
||||
@@ -265,8 +265,8 @@ namespace Opm
|
||||
Base::updateWellStateWithTarget(ebos_simulator, well_state, deferred_logger);
|
||||
// scale segment rates based on the wellRates
|
||||
// and segment pressure based on bhp
|
||||
scaleSegmentPressuresWithBhp(well_state);
|
||||
scaleSegmentRatesWithWellRates(well_state);
|
||||
scaleSegmentPressuresWithBhp(well_state);
|
||||
}
|
||||
|
||||
|
||||
@@ -278,7 +278,8 @@ namespace Opm
|
||||
MultisegmentWell<TypeTag>::
|
||||
scaleSegmentRatesWithWellRates(WellState& well_state) const
|
||||
{
|
||||
auto segment_rates = well_state.segRates(index_of_well_);
|
||||
auto& segments = well_state.segments(this->index_of_well_);
|
||||
auto& segment_rates = segments.rates;
|
||||
for (int phase = 0; phase < number_of_phases_; ++phase) {
|
||||
const double unscaled_top_seg_rate = segment_rates[phase];
|
||||
const double well_phase_rate = well_state.wellRates(index_of_well_)[phase];
|
||||
@@ -302,9 +303,8 @@ namespace Opm
|
||||
}
|
||||
|
||||
std::vector<double> rates;
|
||||
WellState::calculateSegmentRates(segment_inlets_, segment_perforations_, perforation_rates, number_of_phases_,
|
||||
0, rates);
|
||||
std::copy(rates.begin(), rates.end(), segment_rates);
|
||||
WellState::calculateSegmentRates(segment_inlets_, segment_perforations_, perforation_rates, number_of_phases_, 0, rates);
|
||||
std::copy(rates.begin(), rates.end(), segment_rates.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,13 +314,9 @@ namespace Opm
|
||||
MultisegmentWell<TypeTag>::
|
||||
scaleSegmentPressuresWithBhp(WellState& well_state) const
|
||||
{
|
||||
//scale segment pressures
|
||||
const double bhp = well_state.bhp(index_of_well_);
|
||||
auto segment_pressure = well_state.segPress(index_of_well_);
|
||||
const double unscaled_top_seg_pressure = segment_pressure[0];
|
||||
for (int seg = 0; seg < numberOfSegments(); ++seg) {
|
||||
segment_pressure[seg] *= bhp/unscaled_top_seg_pressure;
|
||||
}
|
||||
auto& segments = well_state.segments(this->index_of_well_);
|
||||
auto bhp = well_state.bhp(this->index_of_well_);
|
||||
segments.scale_pressure(bhp);
|
||||
}
|
||||
|
||||
|
||||
@@ -719,8 +715,9 @@ namespace Opm
|
||||
const Well& well = Base::wellEcl();
|
||||
|
||||
// the index of the top segment in the WellState
|
||||
const auto segment_rates = well_state.segRates(index_of_well_);
|
||||
const auto segment_pressure = well_state.segPress(index_of_well_);
|
||||
const auto& segments = well_state.segments(this->index_of_well_);
|
||||
const auto& segment_rates = segments.rates;
|
||||
const auto& segment_pressure = segments.pressure;
|
||||
const PhaseUsage& pu = phaseUsage();
|
||||
|
||||
for (int seg = 0; seg < numberOfSegments(); ++seg) {
|
||||
@@ -1923,13 +1920,14 @@ namespace Opm
|
||||
// TODO: we might be able to add member variables to store these values, then we update well state
|
||||
// after converged
|
||||
const auto hydro_pressure_drop = getHydroPressureLoss(seg);
|
||||
well_state.segPressDropHydroStatic(index_of_well_)[seg] = hydro_pressure_drop.value();
|
||||
auto& segments = well_state.segments(this->index_of_well_);
|
||||
segments.pressure_drop_hydrostatic[seg] = hydro_pressure_drop.value();
|
||||
pressure_equation -= hydro_pressure_drop;
|
||||
|
||||
if (frictionalPressureLossConsidered()) {
|
||||
const auto friction_pressure_drop = getFrictionPressureLoss(seg);
|
||||
pressure_equation -= friction_pressure_drop;
|
||||
well_state.segPressDropFriction(index_of_well_)[seg] = friction_pressure_drop.value();
|
||||
segments.pressure_drop_friction[seg] = friction_pressure_drop.value();
|
||||
}
|
||||
|
||||
resWell_[seg][SPres] = pressure_equation.value();
|
||||
@@ -2042,7 +2040,7 @@ namespace Opm
|
||||
const double sign = mass_rate < 0. ? 1.0 : - 1.0;
|
||||
accelerationPressureLoss *= sign;
|
||||
|
||||
well_state.segPressDropAcceleration(index_of_well_)[seg] = accelerationPressureLoss.value();
|
||||
well_state.segments(this->index_of_well_).pressure_drop_accel[seg] = accelerationPressureLoss.value();
|
||||
|
||||
resWell_[seg][SPres] -= accelerationPressureLoss.value();
|
||||
duneD_[seg][seg][SPres][SPres] -= accelerationPressureLoss.derivative(SPres + numEq);
|
||||
@@ -2336,8 +2334,9 @@ namespace Opm
|
||||
assert( FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx) );
|
||||
const int oil_pos = pu.phase_pos[Oil];
|
||||
|
||||
auto segment_rates = well_state.segRates(this->index_of_well_);
|
||||
auto segment_pressure = well_state.segPress(this->index_of_well_);
|
||||
auto& segments = well_state.segments(this->index_of_well_);
|
||||
auto& segment_rates = segments.rates;
|
||||
auto& segment_pressure = segments.pressure;
|
||||
for (int seg = 0; seg < numberOfSegments(); ++seg) {
|
||||
std::vector<double> fractions(number_of_phases_, 0.0);
|
||||
fractions[oil_pos] = 1.0;
|
||||
@@ -3254,7 +3253,7 @@ namespace Opm
|
||||
}
|
||||
}
|
||||
pressure_equation = pressure_equation - icd_pressure_drop;
|
||||
well_state.segPressDropFriction(index_of_well_)[seg] = icd_pressure_drop.value();
|
||||
well_state.segments(this->index_of_well_).pressure_drop_friction[seg] = icd_pressure_drop.value();
|
||||
|
||||
const int seg_upwind = upwinding_segments_[seg];
|
||||
resWell_[seg][SPres] = pressure_equation.value();
|
||||
|
||||
78
opm/simulators/wells/SegmentState.cpp
Normal file
78
opm/simulators/wells/SegmentState.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright Equinor ASA 2021
|
||||
|
||||
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 <algorithm>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <opm/simulators/wells/SegmentState.hpp>
|
||||
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
|
||||
#include <opm/parser/eclipse/EclipseState/Schedule/MSW/WellSegments.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
namespace {
|
||||
std::vector<int> make_segment_number( const WellSegments& segments ) {
|
||||
std::vector<int> segment_number;
|
||||
std::transform(segments.begin(), segments.end(), std::back_insert_iterator(segment_number), [](const Segment& segment) { return segment.segmentNumber(); });
|
||||
return segment_number;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SegmentState::SegmentState(int num_phases, const WellSegments& segments) :
|
||||
rates(segments.size() * num_phases),
|
||||
pressure(segments.size()),
|
||||
pressure_drop_friction(segments.size()),
|
||||
pressure_drop_hydrostatic(segments.size()),
|
||||
pressure_drop_accel(segments.size()),
|
||||
m_segment_number(make_segment_number(segments))
|
||||
{
|
||||
}
|
||||
|
||||
double SegmentState::pressure_drop(std::size_t index) const {
|
||||
return this->pressure_drop_friction[index] + this->pressure_drop_hydrostatic[index] + this->pressure_drop_accel[index];
|
||||
}
|
||||
|
||||
|
||||
bool SegmentState::empty() const {
|
||||
return this->rates.empty();
|
||||
}
|
||||
|
||||
std::size_t SegmentState::size() const {
|
||||
return this->pressure.size();
|
||||
}
|
||||
|
||||
|
||||
void SegmentState::scale_pressure(double bhp) {
|
||||
if (this->empty())
|
||||
throw std::logic_error("Tried to pressure scale empty SegmentState");
|
||||
|
||||
auto scale_factor = bhp / this->pressure[0];
|
||||
std::transform(this->pressure.begin(),
|
||||
this->pressure.end(),
|
||||
this->pressure.begin(),
|
||||
[scale_factor] (const double& p) { return p*scale_factor;});
|
||||
}
|
||||
|
||||
const std::vector<int>& SegmentState::segment_number() const {
|
||||
return this->m_segment_number;
|
||||
}
|
||||
|
||||
}
|
||||
54
opm/simulators/wells/SegmentState.hpp
Normal file
54
opm/simulators/wells/SegmentState.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright Equinor ASA 2021
|
||||
|
||||
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_SEGMENTSTATE_HEADER_INCLUDED
|
||||
#define OPM_SEGMENTSTATE_HEADER_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
class WellSegments;
|
||||
class WellConnections;
|
||||
|
||||
|
||||
class SegmentState
|
||||
{
|
||||
public:
|
||||
SegmentState() = default;
|
||||
SegmentState(int num_phases, const WellSegments& segments);
|
||||
double pressure_drop(std::size_t index) const;
|
||||
bool empty() const;
|
||||
void scale_pressure(double bhp);
|
||||
const std::vector<int>& segment_number() const;
|
||||
std::size_t size() const;
|
||||
|
||||
std::vector<double> rates;
|
||||
std::vector<double> pressure;
|
||||
std::vector<double> pressure_drop_friction;
|
||||
std::vector<double> pressure_drop_hydrostatic;
|
||||
std::vector<double> pressure_drop_accel;
|
||||
private:
|
||||
std::vector<int> m_segment_number;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -49,6 +49,7 @@ void WellState::base_init(const std::vector<double>& cellPressures,
|
||||
this->bhp_.clear();
|
||||
this->thp_.clear();
|
||||
this->temperature_.clear();
|
||||
this->segment_state.clear();
|
||||
{
|
||||
// const int nw = wells->number_of_wells;
|
||||
const int nw = wells_ecl.size();
|
||||
@@ -97,6 +98,7 @@ void WellState::initSingleWell(const std::vector<double>& cellPressures,
|
||||
this->wellrates_.add(well.name(), std::vector<double>(np, 0));
|
||||
|
||||
const int num_perf_this_well = well_info->communication().sum(well_perf_data_[w].size());
|
||||
this->segment_state.add(well.name(), SegmentState{});
|
||||
this->perfpress_.add(well.name(), std::vector<double>(num_perf_this_well, -1e100));
|
||||
this->perfrates_.add(well.name(), std::vector<double>(num_perf_this_well, 0));
|
||||
this->bhp_.add(well.name(), 0.0);
|
||||
@@ -519,24 +521,6 @@ void WellState::init(const std::vector<double>& cellPressures,
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// we need to create a trival segment related values to avoid there will be some
|
||||
// multi-segment wells added later.
|
||||
nseg_ = nw;
|
||||
top_segment_index_.resize(nw);
|
||||
seg_number_.resize(nw);
|
||||
seg_press_.resize(nw);
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
top_segment_index_[w] = w;
|
||||
seg_number_[w] = 1; // Top segment is segment #1
|
||||
this->seg_press_[w] = this->bhp(w);
|
||||
}
|
||||
//seg_rates_ = wellRates();
|
||||
seg_rates_.assign(nw*np, 0);
|
||||
seg_pressdrop_hydorstatic_.assign(nw, 0.);
|
||||
seg_pressdrop_friction_.assign(nw, 0.);
|
||||
seg_pressdrop_acceleration_.assign(nw, 0.);
|
||||
}
|
||||
|
||||
updateWellsDefaultALQ(wells_ecl);
|
||||
do_glift_optimization_ = true;
|
||||
@@ -822,12 +806,6 @@ void WellState::initWellStateMSWell(const std::vector<Well>& wells_ecl,
|
||||
const auto& pu = this->phaseUsage();
|
||||
const int np = pu.num_phases;
|
||||
|
||||
top_segment_index_.clear();
|
||||
seg_press_.clear();
|
||||
seg_rates_.clear();
|
||||
seg_number_.clear();
|
||||
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) {
|
||||
@@ -837,26 +815,14 @@ void WellState::initWellStateMSWell(const std::vector<Well>& wells_ecl,
|
||||
const int connpos = well_info[1];
|
||||
const int num_perf_this_well = well_info[2];
|
||||
|
||||
const auto& rates = this->wellRates(w);
|
||||
top_segment_index_.push_back(nseg_);
|
||||
if ( !well_ecl.isMultiSegment() ) { // not multi-segment well
|
||||
nseg_ += 1;
|
||||
seg_number_.push_back(1); // Assign single segment (top) as number 1.
|
||||
seg_press_.push_back(bhp(w));
|
||||
for (int p = 0; p < np; ++p) {
|
||||
seg_rates_.push_back(rates[p]);
|
||||
}
|
||||
} else { // it is a multi-segment well
|
||||
if ( well_ecl.isMultiSegment() ) {
|
||||
const WellSegments& segment_set = well_ecl.getSegments();
|
||||
// assuming the order of the perforations in well_ecl is the same with Wells
|
||||
const WellConnections& completion_set = well_ecl.getConnections();
|
||||
// number of segment for this single well
|
||||
this->segment_state.update(w, SegmentState{np, segment_set});
|
||||
const int well_nseg = segment_set.size();
|
||||
int n_activeperf = 0;
|
||||
nseg_ += well_nseg;
|
||||
for (auto segID = 0*well_nseg; segID < well_nseg; ++segID) {
|
||||
this->seg_number_.push_back(segment_set[segID].segmentNumber());
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -905,12 +871,10 @@ void WellState::initWellStateMSWell(const std::vector<Well>& wells_ecl,
|
||||
|
||||
const auto * perf_rates = &perfPhaseRates()[np*start_perf];
|
||||
std::vector<double> perforation_rates(perf_rates, perf_rates + num_perf_this_well*np);
|
||||
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(seg_rates_));
|
||||
auto& segments = this->segments(w);
|
||||
calculateSegmentRates(segment_inlets, segment_perforations, perforation_rates, np, 0 /* top segment */, segments.rates);
|
||||
}
|
||||
|
||||
// for the segment pressure, the segment pressure is the same with the first perforation belongs to the segment
|
||||
// if there is no perforation associated with this segment, it uses the pressure from the outlet segment
|
||||
// which requres the ordering is successful
|
||||
@@ -918,70 +882,44 @@ void WellState::initWellStateMSWell(const std::vector<Well>& wells_ecl,
|
||||
// improved during the solveWellEq process
|
||||
{
|
||||
// top segment is always the first one, and its pressure is the well bhp
|
||||
seg_press_.push_back(bhp(w));
|
||||
const int top_segment = top_segment_index_[w];
|
||||
auto& segment_pressure = this->segments(w).pressure;
|
||||
segment_pressure[0] = bhp(w);
|
||||
const auto& perf_press = this->perfPress(w);
|
||||
for (int seg = 1; seg < well_nseg; ++seg) {
|
||||
if ( !segment_perforations[seg].empty() ) {
|
||||
const int first_perf = segment_perforations[seg][0];
|
||||
seg_press_.push_back(perf_press[first_perf]);
|
||||
segment_pressure[seg] = perf_press[first_perf];
|
||||
} else {
|
||||
// seg_press_.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();
|
||||
seg_press_.push_back(
|
||||
seg_press_[top_segment + segment_set.segmentNumberToIndex(outlet_seg)]);
|
||||
segment_pressure[seg] = segment_pressure[segment_set.segmentNumberToIndex(outlet_seg)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(int(seg_press_.size()) == nseg_);
|
||||
assert(int(seg_rates_.size()) == nseg_ * numPhases() );
|
||||
|
||||
seg_pressdrop_hydorstatic_.assign(nseg_, 0.);
|
||||
seg_pressdrop_friction_.assign(nseg_, 0.);
|
||||
seg_pressdrop_acceleration_.assign(nseg_, 0.);
|
||||
|
||||
if (prev_well_state && !prev_well_state->wellMap().empty()) {
|
||||
const auto& end = prev_well_state->wellMap().end();
|
||||
if (prev_well_state) {
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const Well& well = wells_ecl[w];
|
||||
if (well.getStatus() == Well::Status::SHUT)
|
||||
continue;
|
||||
|
||||
const auto& it = prev_well_state->wellMap().find( wells_ecl[w].name() );
|
||||
if (it != end) { // the well is found in the prev_well_state
|
||||
// TODO: the well with same name can change a lot, like they might not have same number of segments
|
||||
// we need to handle that later.
|
||||
// for now, we just copy them.
|
||||
const int old_index_well = (*it).second[0];
|
||||
const int new_index_well = w;
|
||||
if (prev_well_state->status_[old_index_well] == Well::Status::SHUT) {
|
||||
if ( !well.isMultiSegment() )
|
||||
continue;
|
||||
|
||||
const auto& wname = well.name();
|
||||
if (prev_well_state->segment_state.has(wname)) {
|
||||
if (prev_well_state->status_[wname] == Well::Status::SHUT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int new_top_segment_index = topSegmentIndex(new_index_well);
|
||||
int number_of_segment = 0;
|
||||
// if it is the last well in list
|
||||
if (new_index_well == int(top_segment_index_.size()) - 1) {
|
||||
number_of_segment = nseg_ - new_top_segment_index;
|
||||
} else {
|
||||
number_of_segment = topSegmentIndex(new_index_well + 1) - new_top_segment_index;
|
||||
}
|
||||
|
||||
auto segment_rates = this->segRates(w);
|
||||
auto segment_pressure = this->segPress(w);
|
||||
|
||||
const auto prev_segment_rates = prev_well_state->segRates(old_index_well);
|
||||
const auto prev_segment_pressure = prev_well_state->segPress(old_index_well);
|
||||
|
||||
for (int seg=0; seg < number_of_segment; ++seg) {
|
||||
for (int p = 0; p < np; ++p)
|
||||
segment_rates[seg*np + p] = prev_segment_rates[seg*np + p];
|
||||
|
||||
segment_pressure[seg] = prev_segment_pressure[seg];
|
||||
}
|
||||
// TODO: the well with same name can change a lot, like they might not have same number of segments
|
||||
// we need to handle that later.
|
||||
// for now, we just copy them.
|
||||
this->segment_state.copy_welldata(prev_well_state->segment_state, wname);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1033,12 +971,6 @@ double WellState::brineWellRate(const int w) const
|
||||
return parallel_well_info_[w]->sumPerfValues(perf_rates_brine, perf_rates_brine + this->numPerf(w));
|
||||
}
|
||||
|
||||
int WellState::topSegmentIndex(const int w) const
|
||||
{
|
||||
assert(w < int(top_segment_index_.size()) );
|
||||
|
||||
return top_segment_index_[w];
|
||||
}
|
||||
|
||||
void WellState::stopWell(int well_index)
|
||||
{
|
||||
@@ -1146,19 +1078,22 @@ WellState::reportSegmentResults(const PhaseUsage& pu,
|
||||
const int seg_ix,
|
||||
const int seg_no) const
|
||||
{
|
||||
const auto& segments = this->segments(well_id);
|
||||
if (segments.empty())
|
||||
return {};
|
||||
|
||||
auto seg_res = data::Segment{};
|
||||
{
|
||||
using Value = data::SegmentPressures::Value;
|
||||
auto& segpress = seg_res.pressures;
|
||||
segpress[Value::Pressure] = this->segPress(well_id)[seg_ix];
|
||||
segpress[Value::PDrop] = this->segPressDrop(well_id, seg_ix);
|
||||
segpress[Value::PDropHydrostatic] = this->segPressDropHydroStatic(well_id)[seg_ix];
|
||||
segpress[Value::PDropFriction] = this->segPressDropFriction(well_id)[seg_ix];
|
||||
segpress[Value::PDropAccel] = this->segPressDropAcceleration(well_id)[seg_ix];
|
||||
segpress[Value::Pressure] = segments.pressure[seg_ix];
|
||||
segpress[Value::PDrop] = segments.pressure_drop(seg_ix);
|
||||
segpress[Value::PDropHydrostatic] = segments.pressure_drop_hydrostatic[seg_ix];
|
||||
segpress[Value::PDropFriction] = segments.pressure_drop_friction[seg_ix];
|
||||
segpress[Value::PDropAccel] = segments.pressure_drop_accel[seg_ix];
|
||||
}
|
||||
|
||||
const auto segment_rates = this->segRates(well_id);
|
||||
const auto rate = &segment_rates[seg_ix * this->numPhases()];
|
||||
const auto rate = &segments.rates[seg_ix * pu.num_phases];
|
||||
if (pu.phase_used[Water]) {
|
||||
seg_res.rates.set(data::Rates::opt::wat,
|
||||
rate[pu.phase_pos[Water]]);
|
||||
@@ -1200,18 +1135,14 @@ bool WellState::wellIsOwned(const std::string& wellName) const
|
||||
|
||||
int WellState::numSegments(const int well_id) const
|
||||
{
|
||||
const auto topseg = this->topSegmentIndex(well_id);
|
||||
|
||||
return (well_id + 1 == this->numWells()) // Last well?
|
||||
? (this->numSegment() - topseg)
|
||||
: (this->topSegmentIndex(well_id + 1) - topseg);
|
||||
const auto& segments = this->segments(well_id);
|
||||
return segments.size();
|
||||
}
|
||||
|
||||
int WellState::segmentNumber(const int well_id, const int seg_id) const
|
||||
{
|
||||
const auto top_offset = this->topSegmentIndex(well_id);
|
||||
|
||||
return this->seg_number_[top_offset + seg_id];
|
||||
const auto& segments = this->segment_state[well_id];
|
||||
return segments.segment_number()[seg_id];
|
||||
}
|
||||
|
||||
void WellState::updateWellsDefaultALQ( const std::vector<Well>& wells_ecl )
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <opm/simulators/wells/ALQState.hpp>
|
||||
#include <opm/simulators/wells/GlobalWellInfo.hpp>
|
||||
#include <opm/simulators/wells/SegmentState.hpp>
|
||||
#include <opm/simulators/wells/WellContainer.hpp>
|
||||
#include <opm/core/props/BlackoilPhases.hpp>
|
||||
#include <opm/simulators/wells/PerforationData.hpp>
|
||||
@@ -195,81 +196,24 @@ public:
|
||||
}
|
||||
|
||||
|
||||
const double * segRates(std::size_t well_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &this->seg_rates_[top_segment_index * this->phase_usage_.num_phases];
|
||||
|
||||
|
||||
const SegmentState& segments(const std::size_t well_index) const {
|
||||
return this->segment_state[well_index];
|
||||
}
|
||||
|
||||
double * segRates(std::size_t well_index)
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &this->seg_rates_[top_segment_index * this->phase_usage_.num_phases];
|
||||
SegmentState& segments(const std::size_t well_index) {
|
||||
return this->segment_state[well_index];
|
||||
}
|
||||
|
||||
double * segPress(std::size_t well_index)
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_press_[top_segment_index];
|
||||
const SegmentState& segments(const std::string& wname) const {
|
||||
return this->segment_state[wname];
|
||||
}
|
||||
|
||||
const double * segPress(std::size_t well_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_press_[top_segment_index];
|
||||
SegmentState& segments(const std::string& wname) {
|
||||
return this->segment_state[wname];
|
||||
}
|
||||
|
||||
double segPressDrop(std::size_t well_index, std::size_t segment_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return this->seg_pressdrop_friction_[top_segment_index + segment_index] +
|
||||
this->seg_pressdrop_hydorstatic_[top_segment_index + segment_index] +
|
||||
this->seg_pressdrop_acceleration_[top_segment_index + segment_index];
|
||||
}
|
||||
|
||||
double* segPressDropFriction(std::size_t well_index)
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_friction_[top_segment_index];
|
||||
}
|
||||
|
||||
const double* segPressDropFriction(std::size_t well_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_friction_[top_segment_index];
|
||||
}
|
||||
|
||||
double* segPressDropHydroStatic(std::size_t well_index)
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_hydorstatic_[top_segment_index];
|
||||
}
|
||||
|
||||
const double* segPressDropHydroStatic(std::size_t well_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_hydorstatic_[top_segment_index];
|
||||
}
|
||||
|
||||
double * segPressDropAcceleration(std::size_t well_index)
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_acceleration_[top_segment_index];
|
||||
}
|
||||
|
||||
const double* segPressDropAcceleration(std::size_t well_index) const
|
||||
{
|
||||
const int top_segment_index = this->top_segment_index_[well_index];
|
||||
return &seg_pressdrop_acceleration_[top_segment_index];
|
||||
}
|
||||
|
||||
int numSegment() const
|
||||
{
|
||||
return nseg_;
|
||||
}
|
||||
|
||||
int topSegmentIndex(const int w) const;
|
||||
|
||||
std::vector<double>& productivityIndex() {
|
||||
return productivity_index_;
|
||||
}
|
||||
@@ -524,22 +468,7 @@ private:
|
||||
// \Note: for now, only WCON* keywords, and well status change is considered
|
||||
WellContainer<Events> events_;
|
||||
|
||||
// MS well related
|
||||
// for StandardWell, the number of segments will be one
|
||||
std::vector<double> seg_rates_;
|
||||
std::vector<double> seg_press_;
|
||||
// the index of the top segments, which is used to locate the
|
||||
// multisegment well related information in WellState
|
||||
std::vector<int> top_segment_index_;
|
||||
int nseg_; // total number of the segments
|
||||
|
||||
// The following data are only recorded for output
|
||||
// frictional pressure drop
|
||||
std::vector<double> seg_pressdrop_friction_;
|
||||
// hydrostatic pressure drop
|
||||
std::vector<double> seg_pressdrop_hydorstatic_;
|
||||
// accelerational pressure drop
|
||||
std::vector<double> seg_pressdrop_acceleration_;
|
||||
WellContainer<SegmentState> segment_state;
|
||||
|
||||
// Productivity Index
|
||||
std::vector<double> productivity_index_;
|
||||
@@ -550,14 +479,6 @@ private:
|
||||
// Well potentials
|
||||
std::vector<double> well_potentials_;
|
||||
|
||||
/// Map segment index to segment number, mostly for MS wells.
|
||||
///
|
||||
/// Segment number (one-based) of j-th segment in i-th well is
|
||||
/// \code
|
||||
/// const auto top = topSegmentIndex(i);
|
||||
/// const auto seg_No = seg_number_[top + j];
|
||||
/// \end
|
||||
std::vector<int> seg_number_;
|
||||
|
||||
data::Segment
|
||||
reportSegmentResults(const PhaseUsage& pu,
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <opm/simulators/wells/GlobalWellInfo.hpp>
|
||||
#include <opm/simulators/wells/ParallelWellInfo.hpp>
|
||||
#include <opm/simulators/wells/WellState.hpp>
|
||||
#include <opm/simulators/wells/SegmentState.hpp>
|
||||
#include <opm/simulators/wells/WellContainer.hpp>
|
||||
#include <opm/parser/eclipse/Python/Python.hpp>
|
||||
|
||||
@@ -167,23 +168,21 @@ namespace {
|
||||
|
||||
for (auto wellID = 0*nWell; wellID < nWell; ++wellID) {
|
||||
const auto& well = wells[wellID];
|
||||
const auto pressTop = 100.0 * wellID;
|
||||
|
||||
auto* press = wstate.segPress(wellID);
|
||||
|
||||
press[0] = pressTop;
|
||||
|
||||
if (! well.isMultiSegment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto pressTop = 100.0 * wellID;
|
||||
auto& segments = wstate.segments(wellID);
|
||||
segments.pressure[0] = pressTop;
|
||||
|
||||
const auto& segSet = well.getSegments();
|
||||
const auto nSeg = segSet.size();
|
||||
|
||||
for (auto segID = 0*nSeg + 1; segID < nSeg; ++segID) {
|
||||
// One-based numbering scheme for segments.
|
||||
const auto segNo = segSet[segID].segmentNumber();
|
||||
press[segNo - 1] = pressTop + 1.0*(segNo - 1);
|
||||
segments.pressure[segNo - 1] = pressTop + 1.0*(segNo - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,17 +208,18 @@ namespace {
|
||||
|
||||
for (auto wellID = 0*nWell; wellID < nWell; ++wellID) {
|
||||
const auto& well = wells[wellID];
|
||||
if (! well.isMultiSegment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto rateTop = 1000.0 * wellID;
|
||||
auto segRates = wstate.segRates(wellID);
|
||||
auto& segments = wstate.segments(wellID);
|
||||
auto& segRates = segments.rates;
|
||||
|
||||
if (wat) { segRates[iw] = rateTop; }
|
||||
if (oil) { segRates[io] = rateTop; }
|
||||
if (gas) { segRates[ig] = rateTop; }
|
||||
|
||||
if (! well.isMultiSegment()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& segSet = well.getSegments();
|
||||
const auto nSeg = segSet.size();
|
||||
|
||||
@@ -249,16 +249,10 @@ BOOST_AUTO_TEST_CASE(Linearisation)
|
||||
std::vector<Opm::ParallelWellInfo> pinfos;
|
||||
const auto wstate = buildWellState(setup, tstep, pinfos);
|
||||
|
||||
BOOST_CHECK_EQUAL(wstate.numSegment(), 6 + 1);
|
||||
BOOST_CHECK_EQUAL(wstate.segments("PROD01").size(), 6);
|
||||
|
||||
const auto& wells = setup.sched.getWellsatEnd();
|
||||
BOOST_CHECK_EQUAL(wells.size(), 2);
|
||||
|
||||
const auto prod01_first = wells[0].name() == "PROD01";
|
||||
|
||||
BOOST_CHECK_EQUAL(wstate.topSegmentIndex(0), 0);
|
||||
BOOST_CHECK_EQUAL(wstate.topSegmentIndex(1),
|
||||
prod01_first ? 6 : 1);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -278,18 +272,6 @@ BOOST_AUTO_TEST_CASE(Pressure)
|
||||
|
||||
const auto rpt = wstate.report(setup.grid.c_grid()->global_cell, [](const int){return false;});
|
||||
|
||||
{
|
||||
const auto& xw = rpt.at("INJE01");
|
||||
|
||||
BOOST_CHECK_EQUAL(xw.segments.size(), 1); // Top Segment
|
||||
|
||||
const auto& xseg = xw.segments.at(1);
|
||||
|
||||
BOOST_CHECK_EQUAL(xseg.segNumber, 1);
|
||||
const auto pres_idx = Opm::data::SegmentPressures::Value::Pressure;
|
||||
BOOST_CHECK_CLOSE(xseg.pressures[pres_idx], prod01_first ? 100.0 : 0.0, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expect_nSeg = 6;
|
||||
const auto& xw = rpt.at("PROD01");
|
||||
@@ -333,26 +315,6 @@ BOOST_AUTO_TEST_CASE(Rates)
|
||||
|
||||
BOOST_CHECK(wat && oil && gas);
|
||||
|
||||
{
|
||||
const auto rateTop = prod01_first ? 1000.0 : 0.0;
|
||||
|
||||
const auto& xw = rpt.at("INJE01");
|
||||
|
||||
BOOST_CHECK_EQUAL(xw.segments.size(), 1); // Top Segment
|
||||
|
||||
const auto& xseg = xw.segments.at(1);
|
||||
|
||||
BOOST_CHECK_EQUAL(xseg.segNumber, 1);
|
||||
BOOST_CHECK_CLOSE(xseg.rates.get(Opm::data::Rates::opt::wat),
|
||||
rateTop, 1.0e-10);
|
||||
|
||||
BOOST_CHECK_CLOSE(xseg.rates.get(Opm::data::Rates::opt::oil),
|
||||
rateTop, 1.0e-10);
|
||||
|
||||
BOOST_CHECK_CLOSE(xseg.rates.get(Opm::data::Rates::opt::gas),
|
||||
rateTop, 1.0e-10);
|
||||
}
|
||||
|
||||
{
|
||||
const auto expect_nSeg = 6;
|
||||
const auto& xw = rpt.at("PROD01");
|
||||
@@ -522,5 +484,50 @@ BOOST_AUTO_TEST_CASE(TESTWellContainer) {
|
||||
BOOST_CHECK(!wx.has_value());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TESTSegmentState) {
|
||||
const Setup setup{ "msw.data" };
|
||||
const auto& well = setup.sched.getWell("PROD01", 0);
|
||||
const auto& segments = well.getSegments();
|
||||
Opm::SegmentState ss1(3, segments);
|
||||
Opm::SegmentState ss2;
|
||||
|
||||
|
||||
ss1.pressure_drop_hydrostatic[0] = 1;
|
||||
ss1.pressure_drop_friction[0] = 2;
|
||||
ss1.pressure_drop_accel[0] = 3;
|
||||
|
||||
BOOST_CHECK_EQUAL(ss1.pressure_drop(0), 6);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(TESTSegmentState2) {
|
||||
const Setup setup{ "msw.data" };
|
||||
std::vector<Opm::ParallelWellInfo> pinfo;
|
||||
const auto wstate = buildWellState(setup, 0, pinfo);
|
||||
const auto& well = setup.sched.getWell("PROD01", 0);
|
||||
|
||||
BOOST_CHECK_THROW(wstate.segments(100), std::exception);
|
||||
auto segments = wstate.segments(1); // PROD01 is well 1
|
||||
BOOST_CHECK_EQUAL(segments.pressure.size(), well.getSegments().size());
|
||||
|
||||
segments.pressure_drop_friction[0] = 1;
|
||||
segments.pressure_drop_accel[0] = 2;
|
||||
segments.pressure_drop_hydrostatic[0] = 4;
|
||||
|
||||
BOOST_CHECK_EQUAL(segments.pressure_drop(0), 7);
|
||||
|
||||
|
||||
for (std::size_t i=0; i < segments.pressure.size(); i++)
|
||||
segments.pressure[i] = (i + 1);
|
||||
|
||||
const double bhp = 2.0;
|
||||
segments.scale_pressure(bhp);
|
||||
|
||||
for (std::size_t i=0; i < segments.pressure.size(); i++)
|
||||
BOOST_CHECK_EQUAL(segments.pressure[i], 2*(i+1));
|
||||
|
||||
BOOST_CHECK_EQUAL( segments.size(), well.getSegments().size() );
|
||||
}
|
||||
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
Reference in New Issue
Block a user