diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake
index a34a1e9b5..e760baf44 100644
--- a/CMakeLists_files.cmake
+++ b/CMakeLists_files.cmake
@@ -59,6 +59,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/simulators/wells/WellGroupHelpers.cpp
opm/simulators/wells/WellProdIndexCalculator.cpp
opm/simulators/wells/WellState.cpp
+ opm/simulators/wells/WellStateFullyImplicitBlackoil.cpp
opm/simulators/wells/WGState.cpp
)
diff --git a/opm/simulators/wells/WellStateFullyImplicitBlackoil.cpp b/opm/simulators/wells/WellStateFullyImplicitBlackoil.cpp
new file mode 100644
index 000000000..f0ad4cadf
--- /dev/null
+++ b/opm/simulators/wells/WellStateFullyImplicitBlackoil.cpp
@@ -0,0 +1,928 @@
+/*
+ Copyright 2014 SINTEF ICT, Applied Mathematics.
+ Copyright 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 .
+*/
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+namespace Opm
+{
+
+void WellStateFullyImplicitBlackoil::init(const std::vector& cellPressures,
+ const Schedule& schedule,
+ const std::vector& wells_ecl,
+ const std::vector& parallel_well_info,
+ const int report_step,
+ const WellStateFullyImplicitBlackoil* prevState,
+ const std::vector>& well_perf_data,
+ const SummaryState& summary_state)
+{
+ // call init on base class
+ BaseType :: init(cellPressures, wells_ecl, parallel_well_info, well_perf_data, summary_state);
+ this->global_well_info = std::make_optional( schedule, report_step, wells_ecl );
+ for (const auto& winfo: parallel_well_info)
+ {
+ well_rates.insert({winfo->name(), std::make_pair(winfo->isOwner(), std::vector())});
+ }
+
+ const int nw = wells_ecl.size();
+
+ if( nw == 0 ) return ;
+
+ // Initialize perfphaserates_, which must be done here.
+ const auto& pu = this->phaseUsage();
+ const int np = pu.num_phases;
+
+ int nperf = 0;
+ for (const auto& wpd : well_perf_data) {
+ nperf += wpd.size();
+ }
+
+ well_reservoir_rates_.resize(nw * this->numPhases(), 0.0);
+ well_dissolved_gas_rates_.resize(nw, 0.0);
+ well_vaporized_oil_rates_.resize(nw, 0.0);
+
+ this->events_.clear();
+ {
+ const auto& wg_events = schedule[report_step].wellgroup_events();
+ for (const auto& ecl_well : wells_ecl) {
+ const auto& wname = ecl_well.name();
+ if (wg_events.has(wname))
+ this->events_.add( wname, wg_events.at(wname) );
+ else
+ this->events_.add( wname, Events() );
+ }
+ }
+ // Ensure that we start out with zero rates by default.
+ perfphaserates_.clear();
+ perfphaserates_.resize(nperf * this->numPhases(), 0.0);
+
+ // these are only used to monitor the injectivity
+ perf_water_throughput_.clear();
+ perf_water_throughput_.resize(nperf, 0.0);
+ perf_water_velocity_.clear();
+ perf_water_velocity_.resize(nperf, 0.0);
+ perf_skin_pressure_.clear();
+ perf_skin_pressure_.resize(nperf, 0.0);
+
+ num_perf_.resize(nw, 0);
+ first_perf_index_.resize(nw, 0);
+ first_perf_index_[0] = 0;
+ for (int w = 0; w < nw; ++w) {
+ // Initialize perfphaserates_ to well
+ // rates divided by the number of perforations.
+ const auto& wname = wells_ecl[w].name();
+ const auto& well_info = this->wellMap().at(wname);
+ const int connpos = well_info[1];
+ const int num_perf_this_well = well_info[2];
+ const int global_num_perf_this_well = parallel_well_info[w]->communication().sum(num_perf_this_well);
+ auto * perf_press = &this->perfPress()[connpos];
+ auto * phase_rates = &this->mutable_perfPhaseRates()[connpos * this->numPhases()];
+
+ for (int perf = 0; perf < num_perf_this_well; ++perf) {
+ if (wells_ecl[w].getStatus() == Well::Status::OPEN) {
+ for (int p = 0; p < this->numPhases(); ++p) {
+ phase_rates[this->numPhases()*perf + p] = wellRates()[this->numPhases()*w + p] / double(global_num_perf_this_well);
+ }
+ }
+ perf_press[perf] = cellPressures[well_perf_data[w][perf].cell_index];
+ }
+ num_perf_[w] = num_perf_this_well;
+ first_perf_index_[w] = connpos;
+ }
+
+ is_producer_.resize(nw, false);
+ for (int w = 0; w < nw; ++w) {
+ is_producer_[w] = wells_ecl[w].isProducer();
+ }
+
+ current_injection_controls_.resize(nw, Well::InjectorCMode::CMODE_UNDEFINED);
+ current_production_controls_.resize(nw, Well::ProducerCMode::CMODE_UNDEFINED);
+ for (int w = 0; w < nw; ++w) {
+ if (wells_ecl[w].isProducer()) {
+ const auto controls = wells_ecl[w].productionControls(summary_state);
+ currentProductionControls()[w] = controls.cmode;
+ }
+ else {
+ const auto controls = wells_ecl[w].injectionControls(summary_state);
+ currentInjectionControls()[w] = controls.cmode;
+ }
+ }
+
+ perfRateSolvent_.clear();
+ perfRateSolvent_.resize(nperf, 0.0);
+ productivity_index_.resize(nw * this->numPhases(), 0.0);
+ conn_productivity_index_.resize(nperf * this->numPhases(), 0.0);
+ well_potentials_.resize(nw * this->numPhases(), 0.0);
+
+ perfRatePolymer_.clear();
+ perfRatePolymer_.resize(nperf, 0.0);
+
+ perfRateBrine_.clear();
+ perfRateBrine_.resize(nperf, 0.0);
+
+ for (int w = 0; w < nw; ++w) {
+ switch (wells_ecl[w].getStatus()) {
+ case Well::Status::SHUT:
+ this->shutWell(w);
+ break;
+
+ case Well::Status::STOP:
+ this->stopWell(w);
+ break;
+
+ default:
+ this->openWell(w);
+ break;
+ }
+ }
+
+ // intialize wells that have been there before
+ // order may change so the mapping is based on the well name
+ if (prevState && !prevState->wellMap().empty()) {
+ auto end = prevState->wellMap().end();
+ for (int w = 0; w < nw; ++w) {
+ const Well& well = wells_ecl[w];
+ if (well.getStatus() == Well::Status::SHUT) {
+ continue;
+ }
+
+ auto it = prevState->wellMap().find(well.name());
+ if (it != end)
+ {
+ const int newIndex = w;
+ const int oldIndex = it->second[ 0 ];
+ if (prevState->status_[oldIndex] == Well::Status::SHUT) {
+ // Well was shut in previous state, do not use its values.
+ continue;
+ }
+
+ if (is_producer_[newIndex] != prevState->is_producer_[oldIndex]) {
+ // Well changed to/from injector from/to producer, do not use its privious values.
+ continue;
+ }
+
+ // bhp
+ bhp()[ newIndex ] = prevState->bhp()[ oldIndex ];
+
+ // thp
+ thp()[ newIndex ] = prevState->thp()[ oldIndex ];
+
+ // If new target is set using WCONPROD, WCONINJE etc. we use the new control
+ if (!this->events_[w].hasEvent(WellStateFullyImplicitBlackoil::event_mask)) {
+ current_injection_controls_[ newIndex ] = prevState->currentInjectionControls()[ oldIndex ];
+ current_production_controls_[ newIndex ] = prevState->currentProductionControls()[ oldIndex ];
+ }
+
+ // wellrates
+ for( int i=0, idx=newIndex*np, oldidx=oldIndex*np; iwellRates()[ oldidx ];
+ }
+
+ // wellResrates
+ for( int i=0, idx=newIndex*np, oldidx=oldIndex*np; iwellReservoirRates()[ oldidx ];
+ }
+
+ // Well potentials
+ for( int i=0, idx=newIndex*np, oldidx=oldIndex*np; iwellPotentials()[ oldidx ];
+ }
+
+ // perfPhaseRates
+ const int oldPerf_idx_beg = (*it).second[ 1 ];
+ const int num_perf_old_well = (*it).second[ 2 ];
+ const auto new_iter = this->wellMap().find(well.name());
+ if (new_iter == this->wellMap().end()) {
+ throw std::logic_error {
+ well.name() + " is not in internal well map - "
+ "Bug in WellStateFullyImplicitBlackoil"
+ };
+ }
+
+ const int connpos = new_iter->second[1];
+ const int num_perf_this_well = new_iter->second[2];
+
+ const int num_perf_changed = parallel_well_info[w]->communication()
+ .sum(static_cast(num_perf_old_well != num_perf_this_well));
+ const bool global_num_perf_same = num_perf_changed == 0;
+
+
+ // copy perforation rates when the number of
+ // perforations is equal, otherwise initialize
+ // perfphaserates to well rates divided by the
+ // number of perforations.
+ if (global_num_perf_same)
+ {
+ const auto * src_rates = &prevState->perfPhaseRates()[oldPerf_idx_beg* np];
+ auto * target_rates = &this->mutable_perfPhaseRates()[connpos*np];
+ for (int perf_index = 0; perf_index < num_perf_this_well; perf_index++) {
+ for (int p = 0; p < np; p++) {
+ target_rates[perf_index*np + p] = src_rates[perf_index*np + p];
+ }
+ }
+ } else {
+ const int global_num_perf_this_well = parallel_well_info[w]->communication().sum(num_perf_this_well);
+ auto * target_rates = &this->mutable_perfPhaseRates()[connpos*np];
+ for (int perf_index = 0; perf_index < num_perf_this_well; perf_index++) {
+ for (int p = 0; p < np; ++p) {
+ target_rates[perf_index*np + p] = wellRates()[np*newIndex + p] / double(global_num_perf_this_well);
+ }
+ }
+ }
+
+ // perfPressures
+ if (global_num_perf_same)
+ {
+ auto * target_press = &perfPress()[connpos];
+ const auto * src_press = &prevState->perfPress()[oldPerf_idx_beg];
+ for (int perf = 0; perf < num_perf_this_well; ++perf)
+ {
+ target_press[perf] = src_press[perf];
+ }
+ }
+
+ // perfSolventRates
+ if (pu.has_solvent) {
+ if (global_num_perf_same)
+ {
+ int oldPerf_idx = oldPerf_idx_beg;
+ for (int perf = connpos; perf < connpos + num_perf_this_well; ++perf, ++oldPerf_idx )
+ {
+ perfRateSolvent()[ perf ] = prevState->perfRateSolvent()[ oldPerf_idx ];
+ }
+ }
+ }
+
+ // polymer injectivity related
+ //
+ // here we did not consider the case that we close
+ // some perforation during the running and also,
+ // wells can be shut and re-opened
+ if (pu.has_polymermw) {
+ if (global_num_perf_same)
+ {
+ int oldPerf_idx = oldPerf_idx_beg;
+ for (int perf = connpos; perf < connpos + num_perf_this_well; ++perf, ++oldPerf_idx )
+ {
+ perf_water_throughput_[ perf ] = prevState->perfThroughput()[ oldPerf_idx ];
+ perf_skin_pressure_[ perf ] = prevState->perfSkinPressure()[ oldPerf_idx ];
+ perf_water_velocity_[ perf ] = prevState->perfWaterVelocity()[ oldPerf_idx ];
+ }
+ }
+ }
+
+ // Productivity index.
+ {
+ auto* thisWellPI = &this ->productivityIndex()[newIndex*np + 0];
+ const auto* thatWellPI = &prevState->productivityIndex()[oldIndex*np + 0];
+
+ for (int p = 0; p < np; ++p) {
+ thisWellPI[p] = thatWellPI[p];
+ }
+ }
+ }
+
+ // If in the new step, there is no THP related
+ // target/limit anymore, its thp value should be set to
+ // zero.
+ const bool has_thp = well.isInjector()
+ ? well.injectionControls (summary_state).hasControl(Well::InjectorCMode::THP)
+ : well.productionControls(summary_state).hasControl(Well::ProducerCMode::THP);
+
+ if (!has_thp) {
+ thp()[w] = 0.0;
+ }
+ }
+ }
+
+ {
+ // 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);
+ for (int w = 0; w < nw; ++w) {
+ top_segment_index_[w] = w;
+ seg_number_[w] = 1; // Top segment is segment #1
+ }
+ seg_press_ = bhp();
+ seg_rates_ = wellRates();
+
+ seg_pressdrop_.assign(nw, 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;
+}
+
+void WellStateFullyImplicitBlackoil::resize(const std::vector& wells_ecl,
+ const std::vector& parallel_well_info,
+ const Schedule& schedule,
+ const bool handle_ms_well,
+ const size_t numCells,
+ const std::vector>& well_perf_data,
+ const SummaryState& summary_state)
+{
+ const std::vector tmp(numCells, 0.0); // <- UGLY HACK to pass the size
+ init(tmp, schedule, wells_ecl, parallel_well_info, 0, nullptr, well_perf_data, summary_state);
+
+ if (handle_ms_well) {
+ initWellStateMSWell(wells_ecl, nullptr);
+ }
+}
+
+const std::vector&
+WellStateFullyImplicitBlackoil::currentWellRates(const std::string& wellName) const
+{
+ auto it = well_rates.find(wellName);
+
+ if (it == well_rates.end())
+ OPM_THROW(std::logic_error, "Could not find any rates for well " << wellName);
+
+ return it->second.second;
+}
+
+data::Wells
+WellStateFullyImplicitBlackoil::report(const int* globalCellIdxMap,
+ const std::function& wasDynamicallyClosed) const
+{
+ data::Wells res =
+ WellState::report(globalCellIdxMap, wasDynamicallyClosed);
+
+ const int nw = this->numWells();
+ if (nw == 0) {
+ return res;
+ }
+
+ const auto& pu = this->phaseUsage();
+ const int np = pu.num_phases;
+
+ using rt = data::Rates::opt;
+ std::vector