2019-02-07 07:43:17 -06:00
|
|
|
/*
|
2021-03-01 17:14:34 -06:00
|
|
|
Copyright 2016 - 2019 SINTEF Digital, Mathematics & Cybernetics.
|
|
|
|
Copyright 2016 - 2018 Equinor ASA.
|
|
|
|
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
|
|
|
|
Copyright 2016 - 2018 Norce AS
|
2017-02-13 09:45:06 -06:00
|
|
|
|
2021-03-01 17:14:34 -06:00
|
|
|
This file is part of the Open Porous Media project (OPM).
|
2017-02-13 09:45:06 -06:00
|
|
|
|
2021-03-01 17:14:34 -06:00
|
|
|
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.
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2021-03-01 17:14:34 -06:00
|
|
|
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.
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2021-03-01 17:14:34 -06:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
2019-02-07 07:43:17 -06:00
|
|
|
*/
|
|
|
|
|
2024-02-23 01:45:25 -06:00
|
|
|
// Improve IDE experience
|
|
|
|
#ifndef OPM_BLACKOILWELLMODEL_HEADER_INCLUDED
|
|
|
|
#define OPM_BLACKOILWELLMODEL_IMPL_HEADER_INCLUDED
|
|
|
|
#include <config.h>
|
|
|
|
#include <opm/simulators/wells/BlackoilWellModel.hpp>
|
|
|
|
#endif
|
|
|
|
|
2019-05-07 06:06:02 -05:00
|
|
|
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
|
2019-11-25 03:34:50 -06:00
|
|
|
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
2022-07-08 03:16:53 -05:00
|
|
|
#include <opm/grid/utility/cartesianToCompressed.hpp>
|
2023-01-10 05:37:34 -06:00
|
|
|
|
2021-12-14 01:30:15 -06:00
|
|
|
#include <opm/input/eclipse/Units/UnitSystem.hpp>
|
2023-04-20 15:46:38 -05:00
|
|
|
#include <opm/input/eclipse/Schedule/Network/Balance.hpp>
|
|
|
|
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
|
2023-06-06 14:31:17 -05:00
|
|
|
#include <opm/input/eclipse/Schedule/Well/PAvgDynamicSourceData.hpp>
|
2024-02-07 08:11:59 -06:00
|
|
|
#include <opm/input/eclipse/Schedule/Well/WellTestConfig.hpp>
|
2022-10-24 02:36:05 -05:00
|
|
|
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
|
2023-06-06 14:31:17 -05:00
|
|
|
#include <opm/simulators/wells/ParallelPAvgDynamicSourceData.hpp>
|
|
|
|
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
|
2021-05-31 07:31:56 -05:00
|
|
|
#include <opm/simulators/wells/VFPProperties.hpp>
|
2022-09-07 04:21:35 -05:00
|
|
|
#include <opm/simulators/utils/MPIPacker.hpp>
|
2024-05-02 04:31:23 -05:00
|
|
|
|
|
|
|
#if COMPILE_BDA_BRIDGE
|
2022-10-11 03:42:08 -05:00
|
|
|
#include <opm/simulators/linalg/bda/WellContributions.hpp>
|
2024-05-02 04:31:23 -05:00
|
|
|
#endif
|
2022-10-11 03:42:08 -05:00
|
|
|
|
2022-09-13 08:07:15 -05:00
|
|
|
#if HAVE_MPI
|
2024-01-23 03:51:32 -06:00
|
|
|
#include <opm/simulators/utils/MPISerializer.hpp>
|
2022-09-13 08:07:15 -05:00
|
|
|
#endif
|
2021-05-31 07:31:56 -05:00
|
|
|
|
2020-09-22 07:12:15 -05:00
|
|
|
#include <algorithm>
|
2024-04-02 07:20:50 -05:00
|
|
|
#include <cassert>
|
2023-06-15 07:26:56 -05:00
|
|
|
#include <iomanip>
|
2020-10-29 17:16:31 -05:00
|
|
|
#include <utility>
|
2024-02-07 08:11:59 -06:00
|
|
|
#include <optional>
|
2021-03-01 17:14:34 -06:00
|
|
|
|
2020-10-01 11:27:57 -05:00
|
|
|
#include <fmt/format.h>
|
2020-07-20 14:38:30 -05:00
|
|
|
|
2017-02-13 09:45:06 -06:00
|
|
|
namespace Opm {
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2024-02-06 04:55:07 -06:00
|
|
|
BlackoilWellModel(Simulator& simulator, const PhaseUsage& phase_usage)
|
2024-02-17 11:13:46 -06:00
|
|
|
: BlackoilWellModelGeneric<Scalar>(simulator.vanguard().schedule(),
|
|
|
|
simulator.vanguard().summaryState(),
|
|
|
|
simulator.vanguard().eclState(),
|
|
|
|
phase_usage,
|
|
|
|
simulator.gridView().comm())
|
2024-02-06 04:55:07 -06:00
|
|
|
, simulator_(simulator)
|
2017-02-13 09:45:06 -06:00
|
|
|
{
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_ = ((simulator.gridView().comm().rank() == 0) &&
|
|
|
|
Parameters::get<TypeTag, Properties::EnableTerminalOutput>());
|
2019-10-23 02:09:45 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
local_num_cells_ = simulator_.gridView().size(0);
|
2021-03-01 17:14:34 -06:00
|
|
|
|
2020-10-05 05:43:55 -05:00
|
|
|
// Number of cells the global grid view
|
2024-02-06 04:55:07 -06:00
|
|
|
global_num_cells_ = simulator_.vanguard().globalNumCells();
|
2019-10-23 02:09:45 -05:00
|
|
|
|
2021-12-01 07:00:21 -06:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
auto& parallel_wells = simulator.vanguard().parallelWells();
|
2021-10-19 05:24:04 -05:00
|
|
|
|
2022-09-08 06:33:39 -05:00
|
|
|
this->parallel_well_info_.reserve(parallel_wells.size());
|
|
|
|
for( const auto& name_bool : parallel_wells) {
|
|
|
|
this->parallel_well_info_.emplace_back(name_bool, grid().comm());
|
|
|
|
}
|
2021-03-01 17:14:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
this->alternative_well_rate_init_ =
|
2024-04-05 05:53:20 -05:00
|
|
|
Parameters::get<TypeTag, Properties::AlternativeWellRateInit>();
|
2023-06-06 14:31:17 -05:00
|
|
|
|
2024-04-11 03:21:02 -05:00
|
|
|
using SourceDataSpan =
|
|
|
|
typename PAvgDynamicSourceData<Scalar>::template SourceDataSpan<Scalar>;
|
2023-06-06 14:31:17 -05:00
|
|
|
this->wbpCalculationService_
|
|
|
|
.localCellIndex([this](const std::size_t globalIndex)
|
|
|
|
{ return this->compressedIndexForInterior(globalIndex); })
|
2024-04-11 03:21:02 -05:00
|
|
|
.evalCellSource([this](const int localCell,
|
|
|
|
SourceDataSpan sourceTerms)
|
2023-06-06 14:31:17 -05:00
|
|
|
{
|
2024-04-11 03:21:02 -05:00
|
|
|
using Item = typename SourceDataSpan::Item;
|
2023-06-06 14:31:17 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto* intQuants = this->simulator_.model()
|
2023-06-06 14:31:17 -05:00
|
|
|
.cachedIntensiveQuantities(localCell, /*timeIndex = */0);
|
|
|
|
const auto& fs = intQuants->fluidState();
|
|
|
|
|
|
|
|
sourceTerms.set(Item::PoreVol, intQuants->porosity().value() *
|
2024-02-06 04:55:07 -06:00
|
|
|
this->simulator_.model().dofTotalVolume(localCell));
|
2023-06-06 14:31:17 -05:00
|
|
|
|
|
|
|
constexpr auto io = FluidSystem::oilPhaseIdx;
|
|
|
|
constexpr auto ig = FluidSystem::gasPhaseIdx;
|
|
|
|
constexpr auto iw = FluidSystem::waterPhaseIdx;
|
|
|
|
|
|
|
|
// Ideally, these would be 'constexpr'.
|
|
|
|
const auto haveOil = FluidSystem::phaseIsActive(io);
|
|
|
|
const auto haveGas = FluidSystem::phaseIsActive(ig);
|
|
|
|
const auto haveWat = FluidSystem::phaseIsActive(iw);
|
|
|
|
|
|
|
|
auto weightedPhaseDensity = [&fs](const auto ip)
|
|
|
|
{
|
|
|
|
return fs.saturation(ip).value() * fs.density(ip).value();
|
|
|
|
};
|
|
|
|
|
|
|
|
if (haveOil) { sourceTerms.set(Item::Pressure, fs.pressure(io).value()); }
|
|
|
|
else if (haveGas) { sourceTerms.set(Item::Pressure, fs.pressure(ig).value()); }
|
|
|
|
else { sourceTerms.set(Item::Pressure, fs.pressure(iw).value()); }
|
|
|
|
|
|
|
|
// Strictly speaking, assumes SUM(s[p]) == 1.
|
|
|
|
auto rho = 0.0;
|
|
|
|
if (haveOil) { rho += weightedPhaseDensity(io); }
|
|
|
|
if (haveGas) { rho += weightedPhaseDensity(ig); }
|
|
|
|
if (haveWat) { rho += weightedPhaseDensity(iw); }
|
|
|
|
|
|
|
|
sourceTerms.set(Item::MixtureDensity, rho);
|
|
|
|
});
|
2018-08-16 04:51:36 -05:00
|
|
|
}
|
|
|
|
|
2021-04-25 13:47:31 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2024-02-06 04:55:07 -06:00
|
|
|
BlackoilWellModel(Simulator& simulator) :
|
|
|
|
BlackoilWellModel(simulator, phaseUsageFromDeck(simulator.vanguard().eclState()))
|
2021-04-25 13:47:31 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2019-04-03 10:26:57 -05:00
|
|
|
init()
|
2018-08-16 04:51:36 -05:00
|
|
|
{
|
|
|
|
extractLegacyCellPvtRegionIndex_();
|
|
|
|
extractLegacyDepth_();
|
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
gravity_ = simulator_.problem().gravity()[2];
|
2017-02-13 09:45:06 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
this->initial_step_ = true;
|
2018-08-16 04:51:36 -05:00
|
|
|
|
|
|
|
// add the eWoms auxiliary module for the wells to the list
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.model().addAuxiliaryModule(this);
|
2018-11-15 07:37:01 -06:00
|
|
|
|
2020-10-05 13:02:13 -05:00
|
|
|
is_cell_perforated_.resize(local_num_cells_, false);
|
2018-08-16 04:51:36 -05:00
|
|
|
}
|
|
|
|
|
2021-06-07 07:35:34 -05:00
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2022-04-12 01:44:52 -05:00
|
|
|
initWellContainer(const int reportStepIdx)
|
2021-06-07 07:35:34 -05:00
|
|
|
{
|
2022-04-12 01:44:52 -05:00
|
|
|
const uint64_t effective_events_mask = ScheduleEvents::WELL_STATUS_CHANGE
|
|
|
|
+ ScheduleEvents::NEW_WELL;
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& events = this->schedule()[reportStepIdx].wellgroup_events();
|
2021-06-07 07:35:34 -05:00
|
|
|
for (auto& wellPtr : this->well_container_) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const bool well_opened_this_step = this->report_step_starts_ && events.hasEvent(wellPtr->name(), effective_events_mask);
|
2021-06-07 07:35:34 -05:00
|
|
|
wellPtr->init(&this->phase_usage_, this->depth_, this->gravity_,
|
2022-04-12 01:44:52 -05:00
|
|
|
this->local_num_cells_, this->B_avg_, well_opened_this_step);
|
2021-06-07 07:35:34 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
addNeighbors(std::vector<NeighborSet>& neighbors) const
|
|
|
|
{
|
|
|
|
if (!param_.matrix_add_well_contributions_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create cartesian to compressed mapping
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& schedule_wells = this->schedule().getWellsatEnd();
|
2018-08-16 04:51:36 -05:00
|
|
|
|
|
|
|
// initialize the additional cell connections introduced by wells.
|
2019-05-02 05:51:25 -05:00
|
|
|
for (const auto& well : schedule_wells)
|
2018-08-16 04:51:36 -05:00
|
|
|
{
|
2023-01-12 05:21:07 -06:00
|
|
|
std::vector<int> wellCells = this->getCellsForConnections(well);
|
2018-08-16 04:51:36 -05:00
|
|
|
for (int cellIdx : wellCells) {
|
|
|
|
neighbors[cellIdx].insert(wellCells.begin(),
|
|
|
|
wellCells.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2019-10-02 05:48:29 -05:00
|
|
|
linearize(SparseMatrixAdapter& jacobian, GlobalEqVector& res)
|
2018-08-16 04:51:36 -05:00
|
|
|
{
|
2023-06-15 07:26:56 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
for (const auto& well: well_container_) {
|
|
|
|
// Modifiy the Jacobian with explicit Schur complement
|
|
|
|
// contributions if requested.
|
|
|
|
if (param_.matrix_add_well_contributions_) {
|
|
|
|
well->addWellContributions(jacobian);
|
2019-02-25 03:51:30 -06:00
|
|
|
}
|
2023-06-15 07:26:56 -05:00
|
|
|
// Apply as Schur complement the well residual to reservoir residuals:
|
|
|
|
// r = r - duneC_^T * invDuneD_ * resWell_
|
|
|
|
well->apply(res);
|
2019-02-25 03:51:30 -06:00
|
|
|
}
|
2023-06-15 07:26:56 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::linearize failed: ",
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.gridView().comm());
|
2023-06-15 07:26:56 -05:00
|
|
|
}
|
2019-02-25 03:51:30 -06:00
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
linearizeDomain(const Domain& domain, SparseMatrixAdapter& jacobian, GlobalEqVector& res)
|
|
|
|
{
|
|
|
|
// Note: no point in trying to do a parallel gathering
|
|
|
|
// try/catch here, as this function is not called in
|
|
|
|
// parallel but for each individual domain of each rank.
|
|
|
|
for (const auto& well: well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
// Modifiy the Jacobian with explicit Schur complement
|
|
|
|
// contributions if requested.
|
|
|
|
if (param_.matrix_add_well_contributions_) {
|
|
|
|
well->addWellContributions(jacobian);
|
|
|
|
}
|
|
|
|
// Apply as Schur complement the well residual to reservoir residuals:
|
|
|
|
// r = r - duneC_^T * invDuneD_ * resWell_
|
|
|
|
well->apply(res);
|
|
|
|
}
|
2018-08-16 04:51:36 -05:00
|
|
|
}
|
2017-11-08 06:57:36 -06:00
|
|
|
}
|
2017-02-13 09:45:06 -06:00
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 09:45:06 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-11-08 06:57:36 -06:00
|
|
|
beginReportStep(const int timeStepIdx)
|
2017-02-13 09:45:06 -06:00
|
|
|
{
|
2023-07-12 08:04:21 -05:00
|
|
|
DeferredLogger local_deferredLogger{};
|
2021-09-15 13:06:26 -05:00
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
this->report_step_starts_ = true;
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2024-04-02 09:42:40 -05:00
|
|
|
|
|
|
|
this->rateConverter_ = std::make_unique<RateConverterType>
|
|
|
|
(this->phase_usage_, std::vector<int>(this->local_num_cells_, 0));
|
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
{
|
|
|
|
// WELPI scaling runs at start of report step.
|
|
|
|
const auto enableWellPIScaling = true;
|
|
|
|
this->initializeLocalWellStructure(timeStepIdx, enableWellPIScaling);
|
|
|
|
}
|
2020-10-09 06:38:33 -05:00
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
this->initializeGroupStructure(timeStepIdx);
|
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& comm = this->simulator_.vanguard().grid().comm();
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
2021-09-15 13:06:26 -05:00
|
|
|
{
|
2023-07-12 08:04:21 -05:00
|
|
|
// Create facility for calculating reservoir voidage volumes for
|
|
|
|
// purpose of RESV controls.
|
2024-02-06 04:55:07 -06:00
|
|
|
this->rateConverter_->template defineState<ElementContext>(this->simulator_);
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
// Update VFP properties.
|
|
|
|
{
|
|
|
|
const auto& sched_state = this->schedule()[timeStepIdx];
|
2017-11-08 06:57:36 -06:00
|
|
|
|
2024-02-19 07:00:51 -06:00
|
|
|
this->vfp_properties_ = std::make_unique<VFPProperties<Scalar>>
|
2023-07-12 08:04:21 -05:00
|
|
|
(sched_state.vfpinj(), sched_state.vfpprod(), this->wellState());
|
2021-09-15 13:06:26 -05:00
|
|
|
}
|
2023-07-12 08:04:21 -05:00
|
|
|
}
|
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
|
|
|
"beginReportStep() failed: ",
|
|
|
|
this->terminal_output_, comm)
|
|
|
|
|
|
|
|
// Store the current well and group states in order to recover in
|
|
|
|
// the case of failed iterations
|
|
|
|
this->commitWGState();
|
2023-07-13 10:44:28 -05:00
|
|
|
|
|
|
|
this->wellStructureChangedDynamically_ = false;
|
2023-07-12 08:04:21 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
initializeLocalWellStructure(const int reportStepIdx,
|
|
|
|
const bool enableWellPIScaling)
|
|
|
|
{
|
|
|
|
DeferredLogger local_deferredLogger{};
|
2019-08-07 07:13:11 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& comm = this->simulator_.vanguard().grid().comm();
|
2017-11-08 06:57:36 -06:00
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
// Wells_ecl_ holds this rank's wells, both open and stopped/shut.
|
|
|
|
this->wells_ecl_ = this->getLocalWells(reportStepIdx);
|
|
|
|
this->local_parallel_well_info_ =
|
|
|
|
this->createLocalParallelWellInfo(this->wells_ecl_);
|
2021-09-01 08:04:54 -05:00
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
// At least initializeWellState() might be throw an exception in
|
|
|
|
// UniformTabulated2DFunction. Playing it safe by extending the
|
|
|
|
// scope a bit.
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
|
|
|
{
|
|
|
|
this->initializeWellPerfData();
|
|
|
|
this->initializeWellState(reportStepIdx);
|
|
|
|
this->initializeWBPCalculationService();
|
|
|
|
|
|
|
|
if (this->param_.use_multisegment_well_ && this->anyMSWellOpenLocal()) {
|
|
|
|
this->wellState().initWellStateMSWell(this->wells_ecl_, &this->prevWellState());
|
2021-01-14 12:22:34 -06:00
|
|
|
}
|
2020-10-29 17:30:09 -05:00
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
this->initializeWellProdIndCalculators();
|
|
|
|
|
|
|
|
if (enableWellPIScaling && this->schedule()[reportStepIdx].events()
|
|
|
|
.hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX))
|
2021-09-15 13:06:26 -05:00
|
|
|
{
|
2023-07-12 08:04:21 -05:00
|
|
|
this->runWellPIScaling(reportStepIdx, local_deferredLogger);
|
2023-06-06 14:31:17 -05:00
|
|
|
}
|
2023-07-12 08:04:21 -05:00
|
|
|
}
|
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
|
|
|
"Failed to initialize local well structure: ",
|
|
|
|
this->terminal_output_, comm)
|
|
|
|
}
|
2023-06-06 14:31:17 -05:00
|
|
|
|
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
initializeGroupStructure(const int reportStepIdx)
|
|
|
|
{
|
|
|
|
DeferredLogger local_deferredLogger{};
|
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& comm = this->simulator_.vanguard().grid().comm();
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
|
|
|
{
|
|
|
|
const auto& fieldGroup =
|
|
|
|
this->schedule().getGroup("FIELD", reportStepIdx);
|
|
|
|
|
2024-02-19 06:47:25 -06:00
|
|
|
WellGroupHelpers<Scalar>::setCmodeGroup(fieldGroup,
|
|
|
|
this->schedule(),
|
|
|
|
this->summaryState(),
|
|
|
|
reportStepIdx,
|
|
|
|
this->groupState());
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
// Define per region average pressure calculators for use by
|
|
|
|
// pressure maintenance groups (GPMAINT keyword).
|
|
|
|
if (this->schedule()[reportStepIdx].has_gpmaint()) {
|
2024-02-19 06:47:25 -06:00
|
|
|
WellGroupHelpers<Scalar>::setRegionAveragePressureCalculator
|
2023-07-12 08:04:21 -05:00
|
|
|
(fieldGroup,
|
|
|
|
this->schedule(),
|
|
|
|
reportStepIdx,
|
|
|
|
this->eclState_.fieldProps(),
|
|
|
|
this->phase_usage_,
|
|
|
|
this->regionalAveragePressureCalculator_);
|
2021-09-15 13:06:26 -05:00
|
|
|
}
|
|
|
|
}
|
2023-07-12 08:04:21 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
|
|
|
"Failed to initialize group structure: ",
|
|
|
|
this->terminal_output_, comm)
|
2018-06-06 08:17:59 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-12 08:04:21 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-06-06 08:17:59 -05:00
|
|
|
// called at the beginning of a time step
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-03-01 08:17:52 -06:00
|
|
|
beginTimeStep()
|
|
|
|
{
|
2023-03-21 16:44:14 -05:00
|
|
|
OPM_TIMEBLOCK(beginTimeStep);
|
2023-07-13 10:44:28 -05:00
|
|
|
|
|
|
|
this->updateAverageFormationFactor();
|
|
|
|
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2023-07-13 10:44:28 -05:00
|
|
|
|
|
|
|
this->switched_prod_groups_.clear();
|
|
|
|
this->switched_inj_groups_.clear();
|
|
|
|
|
|
|
|
if (this->wellStructureChangedDynamically_) {
|
|
|
|
// Something altered the well structure/topology. Possibly
|
|
|
|
// WELSPECS/COMPDAT and/or WELOPEN run from an ACTIONX block.
|
|
|
|
// Reconstruct the local wells to account for the new well
|
|
|
|
// structure.
|
|
|
|
const auto reportStepIdx =
|
2024-02-06 04:55:07 -06:00
|
|
|
this->simulator_.episodeIndex();
|
2023-07-13 10:44:28 -05:00
|
|
|
|
|
|
|
// Disable WELPI scaling when well structure is updated in the
|
|
|
|
// middle of a report step.
|
|
|
|
const auto enableWellPIScaling = false;
|
|
|
|
|
|
|
|
this->initializeLocalWellStructure(reportStepIdx, enableWellPIScaling);
|
|
|
|
this->initializeGroupStructure(reportStepIdx);
|
|
|
|
|
|
|
|
this->commitWGState();
|
|
|
|
|
|
|
|
// Reset topology flag to signal that we've handled this
|
|
|
|
// structure change. That way we don't end up here in
|
|
|
|
// subsequent calls to beginTimeStep() unless there's a new
|
|
|
|
// dynamic change to the well structure during a report step.
|
|
|
|
this->wellStructureChangedDynamically_ = false;
|
|
|
|
}
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2021-04-22 10:31:21 -05:00
|
|
|
this->resetWGState();
|
2023-07-13 10:44:28 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const int reportStepIdx = simulator_.episodeIndex();
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateAndCommunicateGroupData(reportStepIdx,
|
|
|
|
simulator_.model().newtonMethod().numIterations());
|
2023-07-13 10:44:28 -05:00
|
|
|
|
2023-12-01 12:44:09 -06:00
|
|
|
this->wellState().updateWellsDefaultALQ(this->wells_ecl_, this->summaryState());
|
2021-05-19 07:51:14 -05:00
|
|
|
this->wellState().gliftTimeStepInit();
|
2023-07-13 10:44:28 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const double simulationTime = simulator_.time();
|
2021-09-20 03:56:11 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
{
|
2019-02-07 07:43:17 -06:00
|
|
|
// test wells
|
|
|
|
wellTesting(reportStepIdx, simulationTime, local_deferredLogger);
|
2018-06-06 08:17:59 -05:00
|
|
|
|
2019-02-07 07:43:17 -06:00
|
|
|
// create the well container
|
2021-06-07 07:49:41 -05:00
|
|
|
createWellContainer(reportStepIdx);
|
2017-11-08 06:57:36 -06:00
|
|
|
|
2022-09-13 03:11:10 -05:00
|
|
|
// Wells are active if they are active wells on at least one process.
|
2024-02-06 04:55:07 -06:00
|
|
|
const Grid& grid = simulator_.vanguard().grid();
|
2024-02-17 11:13:46 -06:00
|
|
|
this->wells_active_ = grid.comm().max(!this->well_container_.empty());
|
2022-09-13 03:11:10 -05:00
|
|
|
|
2019-02-07 07:43:17 -06:00
|
|
|
// do the initialization for all the wells
|
|
|
|
// TODO: to see whether we can postpone of the intialization of the well containers to
|
|
|
|
// optimize the usage of the following several member variables
|
2022-04-12 01:44:52 -05:00
|
|
|
this->initWellContainer(reportStepIdx);
|
2017-08-07 04:35:59 -05:00
|
|
|
|
2019-02-07 07:43:17 -06:00
|
|
|
// update the updated cell flag
|
|
|
|
std::fill(is_cell_perforated_.begin(), is_cell_perforated_.end(), false);
|
|
|
|
for (auto& well : well_container_) {
|
|
|
|
well->updatePerforatedCell(is_cell_perforated_);
|
|
|
|
}
|
2018-11-15 07:37:01 -06:00
|
|
|
|
2019-02-07 07:43:17 -06:00
|
|
|
// calculate the efficiency factors for each well
|
2024-02-17 11:13:46 -06:00
|
|
|
this->calculateEfficiencyFactors(reportStepIdx);
|
2017-08-07 04:35:59 -05:00
|
|
|
|
2021-01-28 07:33:21 -06:00
|
|
|
if constexpr (has_polymer_)
|
2019-02-07 07:43:17 -06:00
|
|
|
{
|
2020-08-27 02:13:30 -05:00
|
|
|
if (PolymerModule::hasPlyshlog() || getPropValue<TypeTag, Properties::EnablePolymerMW>() ) {
|
2024-02-17 11:13:46 -06:00
|
|
|
this->setRepRadiusPerfLength();
|
2019-02-07 07:43:17 -06:00
|
|
|
}
|
2017-11-08 06:57:36 -06:00
|
|
|
}
|
2021-05-25 05:57:11 -05:00
|
|
|
|
2021-03-18 02:29:23 -05:00
|
|
|
}
|
2021-09-20 03:56:11 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, "beginTimeStep() failed: ",
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_, simulator_.vanguard().grid().comm());
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2018-04-07 14:41:34 -05:00
|
|
|
for (auto& well : well_container_) {
|
2024-02-17 11:13:46 -06:00
|
|
|
well->setVFPProperties(this->vfp_properties_.get());
|
|
|
|
well->setGuideRate(&this->guideRate_);
|
2018-04-07 14:41:34 -05:00
|
|
|
}
|
2017-08-07 04:35:59 -05:00
|
|
|
|
2023-07-07 04:20:08 -05:00
|
|
|
this->updateInjFCMult(local_deferredLogger);
|
2023-01-01 16:51:41 -06:00
|
|
|
|
2023-06-21 04:48:57 -05:00
|
|
|
// Close completions due to economic reasons
|
2018-06-06 08:17:59 -05:00
|
|
|
for (auto& well : well_container_) {
|
2024-02-17 11:13:46 -06:00
|
|
|
well->closeCompletions(this->wellTestState());
|
2018-06-06 08:17:59 -05:00
|
|
|
}
|
2019-09-30 05:49:36 -05:00
|
|
|
|
2023-06-26 04:04:37 -05:00
|
|
|
// we need the inj_multiplier from the previous time step
|
|
|
|
this->initInjMult();
|
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summaryState = simulator_.vanguard().summaryState();
|
2020-10-15 10:56:11 -05:00
|
|
|
if (alternative_well_rate_init_) {
|
2020-12-07 03:05:46 -06:00
|
|
|
// Update the well rates of well_state_, if only single-phase rates, to
|
|
|
|
// have proper multi-phase rates proportional to rates at bhp zero.
|
|
|
|
// This is done only for producers, as injectors will only have a single
|
|
|
|
// nonzero phase anyway.
|
2020-10-15 10:56:11 -05:00
|
|
|
for (auto& well : well_container_) {
|
2023-08-27 05:24:48 -05:00
|
|
|
const bool zero_target = well->stopppedOrZeroRateTarget(summaryState, this->wellState());
|
|
|
|
if (well->isProducer() && !zero_target) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateWellStateRates(simulator_, this->wellState(), local_deferredLogger);
|
2020-12-07 03:05:46 -06:00
|
|
|
}
|
2020-10-15 10:56:11 -05:00
|
|
|
}
|
2020-05-15 04:21:32 -05:00
|
|
|
}
|
|
|
|
|
2023-05-21 12:53:47 -05:00
|
|
|
for (auto& well : well_container_) {
|
|
|
|
if (well->isVFPActive(local_deferredLogger)){
|
2023-05-23 07:12:55 -05:00
|
|
|
well->setPrevSurfaceRates(this->wellState(), this->prevWellState());
|
2023-05-21 12:53:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 06:29:40 -05:00
|
|
|
// calculate the well potentials
|
|
|
|
try {
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateWellPotentials(reportStepIdx,
|
|
|
|
/*onlyAfterEvent*/true,
|
|
|
|
simulator_.vanguard().summaryConfig(),
|
|
|
|
local_deferredLogger);
|
2022-03-23 06:29:40 -05:00
|
|
|
} catch ( std::runtime_error& e ) {
|
|
|
|
const std::string msg = "A zero well potential is returned for output purposes. ";
|
|
|
|
local_deferredLogger.warning("WELL_POTENTIAL_CALCULATION_FAILED", msg);
|
|
|
|
}
|
|
|
|
|
2021-03-19 05:09:14 -05:00
|
|
|
//update guide rates
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& comm = simulator_.vanguard().grid().comm();
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> pot(this->numPhases(), 0.0);
|
2024-02-19 06:47:25 -06:00
|
|
|
const Group& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx);
|
2024-02-20 15:22:41 -06:00
|
|
|
WellGroupHelpers<Scalar>::updateGuideRates(fieldGroup,
|
2024-02-19 06:47:25 -06:00
|
|
|
this->schedule(),
|
|
|
|
summaryState,
|
|
|
|
this->phase_usage_,
|
|
|
|
reportStepIdx,
|
|
|
|
simulationTime,
|
|
|
|
this->wellState(),
|
|
|
|
this->groupState(),
|
|
|
|
comm,
|
|
|
|
&this->guideRate_,
|
|
|
|
pot,
|
|
|
|
local_deferredLogger);
|
2021-09-20 03:56:11 -05:00
|
|
|
std::string exc_msg;
|
|
|
|
auto exc_type = ExceptionType::NONE;
|
2021-09-21 07:03:35 -05:00
|
|
|
// update gpmaint targets
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->schedule_[reportStepIdx].has_gpmaint()) {
|
2023-01-11 03:49:51 -06:00
|
|
|
for (auto& calculator : regionalAveragePressureCalculator_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
calculator.second->template defineState<ElementContext>(simulator_);
|
2023-01-11 03:49:51 -06:00
|
|
|
}
|
2024-02-06 04:55:07 -06:00
|
|
|
const double dt = simulator_.timeStepSize();
|
2024-02-20 15:22:41 -06:00
|
|
|
WellGroupHelpers<Scalar>::updateGpMaintTargetForGroups(fieldGroup,
|
2024-02-19 06:47:25 -06:00
|
|
|
this->schedule_,
|
|
|
|
regionalAveragePressureCalculator_,
|
|
|
|
reportStepIdx,
|
|
|
|
dt,
|
|
|
|
this->wellState(),
|
|
|
|
this->groupState());
|
2021-09-21 07:03:35 -05:00
|
|
|
}
|
2021-01-19 07:43:32 -06:00
|
|
|
try {
|
2021-03-09 06:37:03 -06:00
|
|
|
// Compute initial well solution for new wells and injectors that change injection type i.e. WAG.
|
2021-01-19 07:43:32 -06:00
|
|
|
for (auto& well : well_container_) {
|
|
|
|
const uint64_t effective_events_mask = ScheduleEvents::WELL_STATUS_CHANGE
|
2021-03-09 06:37:03 -06:00
|
|
|
+ ScheduleEvents::INJECTION_TYPE_CHANGED
|
2021-04-16 06:38:56 -05:00
|
|
|
+ ScheduleEvents::WELL_SWITCHED_INJECTOR_PRODUCER
|
2021-03-09 06:37:03 -06:00
|
|
|
+ ScheduleEvents::NEW_WELL;
|
2021-01-19 07:43:32 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& events = this->schedule()[reportStepIdx].wellgroup_events();
|
|
|
|
const bool event = this->report_step_starts_ && events.hasEvent(well->name(), effective_events_mask);
|
2021-09-21 03:32:56 -05:00
|
|
|
const bool dyn_status_change = this->wellState().well(well->name()).status
|
|
|
|
!= this->prevWellState().well(well->name()).status;
|
|
|
|
|
|
|
|
if (event || dyn_status_change) {
|
2021-01-19 07:43:32 -06:00
|
|
|
try {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateWellStateWithTarget(simulator_, this->groupState(), this->wellState(), local_deferredLogger);
|
|
|
|
well->calculateExplicitQuantities(simulator_, this->wellState(), local_deferredLogger);
|
|
|
|
well->solveWellEquation(simulator_, this->wellState(), this->groupState(), local_deferredLogger);
|
2021-03-18 02:29:23 -05:00
|
|
|
} catch (const std::exception& e) {
|
2021-01-19 07:43:32 -06:00
|
|
|
const std::string msg = "Compute initial well solution for new well " + well->name() + " failed. Continue with zero initial rates";
|
|
|
|
local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
|
|
|
|
}
|
|
|
|
}
|
2021-01-04 07:00:59 -06:00
|
|
|
}
|
2021-03-18 02:29:23 -05:00
|
|
|
}
|
2021-09-20 03:56:11 -05:00
|
|
|
// Catch clauses for all errors setting exc_type and exc_msg
|
|
|
|
OPM_PARALLEL_CATCH_CLAUSE(exc_type, exc_msg);
|
2021-03-18 02:29:23 -05:00
|
|
|
|
|
|
|
if (exc_type != ExceptionType::NONE) {
|
2021-01-19 07:43:32 -06:00
|
|
|
const std::string msg = "Compute initial well solution for new wells failed. Continue with zero initial rates";
|
|
|
|
local_deferredLogger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
|
2021-01-04 07:00:59 -06:00
|
|
|
}
|
|
|
|
|
2020-09-30 03:04:39 -05:00
|
|
|
logAndCheckForExceptionsAndThrow(local_deferredLogger,
|
2024-02-17 11:13:46 -06:00
|
|
|
exc_type, "beginTimeStep() failed: " + exc_msg, this->terminal_output_, comm);
|
2020-09-30 03:04:39 -05:00
|
|
|
|
2017-08-10 08:27:05 -05:00
|
|
|
}
|
|
|
|
|
2017-11-08 08:48:30 -06:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
2021-03-01 08:17:52 -06:00
|
|
|
BlackoilWellModel<TypeTag>::wellTesting(const int timeStepIdx,
|
|
|
|
const double simulationTime,
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger& deferred_logger)
|
2021-03-01 08:17:52 -06:00
|
|
|
{
|
2023-01-17 00:22:17 -06:00
|
|
|
for (const std::string& well_name : this->getWellsForTesting(timeStepIdx, simulationTime)) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const Well& wellEcl = this->schedule().getWell(well_name, timeStepIdx);
|
2023-01-17 00:22:17 -06:00
|
|
|
if (wellEcl.getStatus() == Well::Status::SHUT)
|
|
|
|
continue;
|
2018-06-06 08:17:59 -05:00
|
|
|
|
2023-01-17 00:22:17 -06:00
|
|
|
WellInterfacePtr well = createWellForWellTest(well_name, timeStepIdx, deferred_logger);
|
|
|
|
// some preparation before the well can be used
|
2024-02-17 11:13:46 -06:00
|
|
|
well->init(&this->phase_usage_, depth_, gravity_, local_num_cells_, B_avg_, true);
|
2021-11-26 03:39:07 -06:00
|
|
|
|
2024-02-20 15:22:41 -06:00
|
|
|
Scalar well_efficiency_factor = wellEcl.getEfficiencyFactor();
|
|
|
|
WellGroupHelpers<Scalar>::accumulateGroupEfficiencyFactor(this->schedule().getGroup(wellEcl.groupName(),
|
2024-02-19 06:47:25 -06:00
|
|
|
timeStepIdx),
|
|
|
|
this->schedule(),
|
|
|
|
timeStepIdx,
|
|
|
|
well_efficiency_factor);
|
2021-03-01 08:17:52 -06:00
|
|
|
|
2023-01-17 00:22:17 -06:00
|
|
|
well->setWellEfficiencyFactor(well_efficiency_factor);
|
2024-02-17 11:13:46 -06:00
|
|
|
well->setVFPProperties(this->vfp_properties_.get());
|
|
|
|
well->setGuideRate(&this->guideRate_);
|
2017-11-08 08:48:30 -06:00
|
|
|
|
2023-10-26 06:50:42 -05:00
|
|
|
// initialize rates/previous rates to prevent zero fractions in vfp-interpolation
|
|
|
|
if (well->isProducer()) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateWellStateRates(simulator_, this->wellState(), deferred_logger);
|
2023-10-26 06:50:42 -05:00
|
|
|
}
|
|
|
|
if (well->isVFPActive(deferred_logger)) {
|
|
|
|
well->setPrevSurfaceRates(this->wellState(), this->prevWellState());
|
|
|
|
}
|
|
|
|
|
2023-11-29 02:17:53 -06:00
|
|
|
try {
|
2024-02-17 11:13:46 -06:00
|
|
|
well->wellTesting(simulator_, simulationTime, this->wellState(),
|
|
|
|
this->groupState(), this->wellTestState(), deferred_logger);
|
2023-12-01 02:34:42 -06:00
|
|
|
} catch (const std::exception& e) {
|
2023-11-29 02:17:53 -06:00
|
|
|
const std::string msg = fmt::format("Exception during testing of well: {}. The well will not open.\n Exception message: {}", wellEcl.name(), e.what());
|
|
|
|
deferred_logger.warning("WELL_TESTING_FAILED", msg);
|
|
|
|
}
|
2019-01-18 07:04:30 -06:00
|
|
|
}
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
|
|
|
|
2021-03-01 08:17:52 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-11-08 08:48:30 -06:00
|
|
|
// called at the end of a report step
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-03-01 08:17:52 -06:00
|
|
|
endReportStep()
|
|
|
|
{
|
2020-11-27 11:26:59 -06:00
|
|
|
// Clear the communication data structures for above values.
|
2021-09-27 05:00:39 -05:00
|
|
|
for (auto&& pinfo : this->local_parallel_well_info_)
|
2020-11-27 11:26:59 -06:00
|
|
|
{
|
2021-09-27 05:00:39 -05:00
|
|
|
pinfo.get().clear();
|
2020-11-27 11:26:59 -06:00
|
|
|
}
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
|
|
|
|
2021-03-01 08:17:52 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-11-08 08:48:30 -06:00
|
|
|
// called at the end of a report step
|
|
|
|
template<typename TypeTag>
|
2020-05-07 09:13:39 -05:00
|
|
|
const SimulatorReportSingle&
|
2017-11-08 08:48:30 -06:00
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
lastReport() const {return last_report_; }
|
|
|
|
|
2021-03-01 08:17:52 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-11-08 08:48:30 -06:00
|
|
|
// called at the end of a time step
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-07-12 08:04:21 -05:00
|
|
|
timeStepSucceeded(const double simulationTime, const double dt)
|
2021-03-01 08:17:52 -06:00
|
|
|
{
|
2021-03-01 08:43:43 -06:00
|
|
|
this->closed_this_step_.clear();
|
2019-02-03 01:13:11 -06:00
|
|
|
|
2020-01-29 01:41:41 -06:00
|
|
|
// time step is finished and we are not any more at the beginning of an report step
|
2024-02-17 11:13:46 -06:00
|
|
|
this->report_step_starts_ = false;
|
2024-02-06 04:55:07 -06:00
|
|
|
const int reportStepIdx = simulator_.episodeIndex();
|
2020-01-29 01:41:41 -06:00
|
|
|
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2018-02-14 06:34:35 -06:00
|
|
|
for (const auto& well : well_container_) {
|
2020-08-27 02:13:30 -05:00
|
|
|
if (getPropValue<TypeTag, Properties::EnablePolymerMW>() && well->isInjector()) {
|
2021-03-26 16:10:16 -05:00
|
|
|
well->updateWaterThroughput(dt, this->wellState());
|
2018-11-30 05:15:51 -06:00
|
|
|
}
|
2023-06-30 07:53:19 -05:00
|
|
|
}
|
2023-08-23 01:35:26 -05:00
|
|
|
// update connection transmissibility factor and d factor (if applicable) in the wellstate
|
|
|
|
for (const auto& well : well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateConnectionTransmissibilityFactor(simulator_, this->wellState().well(well->indexOfWell()));
|
|
|
|
well->updateConnectionDFactor(simulator_, this->wellState().well(well->indexOfWell()));
|
2023-08-23 01:35:26 -05:00
|
|
|
}
|
2022-12-14 07:56:24 -06:00
|
|
|
|
2023-06-30 07:53:19 -05:00
|
|
|
if (Indices::waterEnabled) {
|
|
|
|
this->updateFiltrationParticleVolume(dt, FluidSystem::waterPhaseIdx);
|
2018-02-14 06:34:35 -06:00
|
|
|
}
|
2023-06-26 04:04:37 -05:00
|
|
|
|
|
|
|
// at the end of the time step, updating the inj_multiplier saved in WellState for later use
|
|
|
|
this->updateInjMult(local_deferredLogger);
|
|
|
|
|
2021-10-08 02:47:22 -05:00
|
|
|
// report well switching
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
well->reportWellSwitching(this->wellState().well(well->indexOfWell()), local_deferredLogger);
|
|
|
|
}
|
2022-08-26 06:38:55 -05:00
|
|
|
// report group switching
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2022-08-26 06:38:55 -05:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
for (const auto& [name, to] : this->switched_prod_groups_) {
|
2022-08-26 06:38:55 -05:00
|
|
|
const Group::ProductionCMode& oldControl = this->prevWGState().group_state.production_control(name);
|
|
|
|
std::string from = Group::ProductionCMode2String(oldControl);
|
|
|
|
if (to != from) {
|
2022-09-13 01:33:51 -05:00
|
|
|
std::string msg = " Production Group " + name
|
|
|
|
+ " control mode changed from ";
|
|
|
|
msg += from;
|
|
|
|
msg += " to " + to;
|
2022-08-26 06:38:55 -05:00
|
|
|
local_deferredLogger.info(msg);
|
|
|
|
}
|
|
|
|
}
|
2024-02-17 11:13:46 -06:00
|
|
|
for (const auto& [key, to] : this->switched_inj_groups_) {
|
2022-08-26 06:38:55 -05:00
|
|
|
const std::string& name = key.first;
|
|
|
|
const Opm::Phase& phase = key.second;
|
2022-09-13 01:33:51 -05:00
|
|
|
|
2022-08-26 06:38:55 -05:00
|
|
|
const Group::InjectionCMode& oldControl = this->prevWGState().group_state.injection_control(name, phase);
|
|
|
|
std::string from = Group::InjectionCMode2String(oldControl);
|
|
|
|
if (to != from) {
|
2022-09-13 01:33:51 -05:00
|
|
|
std::string msg = " Injection Group " + name
|
|
|
|
+ " control mode changed from ";
|
|
|
|
msg += from;
|
|
|
|
msg += " to " + to;
|
2022-08-26 06:38:55 -05:00
|
|
|
local_deferredLogger.info(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-11-07 07:53:43 -06:00
|
|
|
|
2019-08-07 07:13:11 -05:00
|
|
|
// update the rate converter with current averages pressures etc in
|
2024-02-06 04:55:07 -06:00
|
|
|
rateConverter_->template defineState<ElementContext>(simulator_);
|
2019-08-07 07:13:11 -05:00
|
|
|
|
|
|
|
// calculate the well potentials
|
2019-02-07 07:43:17 -06:00
|
|
|
try {
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateWellPotentials(reportStepIdx,
|
|
|
|
/*onlyAfterEvent*/false,
|
|
|
|
simulator_.vanguard().summaryConfig(),
|
|
|
|
local_deferredLogger);
|
2019-02-07 07:43:17 -06:00
|
|
|
} catch ( std::runtime_error& e ) {
|
2018-11-07 07:53:43 -06:00
|
|
|
const std::string msg = "A zero well potential is returned for output purposes. ";
|
2019-02-03 01:13:11 -06:00
|
|
|
local_deferredLogger.warning("WELL_POTENTIAL_CALCULATION_FAILED", msg);
|
2018-11-07 07:53:43 -06:00
|
|
|
}
|
2020-05-04 08:56:34 -05:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
updateWellTestState(simulationTime, this->wellTestState());
|
2021-08-09 06:21:10 -05:00
|
|
|
|
2020-05-04 08:56:34 -05:00
|
|
|
// check group sales limits at the end of the timestep
|
2024-02-17 11:13:46 -06:00
|
|
|
const Group& fieldGroup = this->schedule_.getGroup("FIELD", reportStepIdx);
|
|
|
|
this->checkGEconLimits(fieldGroup, simulationTime,
|
|
|
|
simulator_.episodeIndex(), local_deferredLogger);
|
|
|
|
this->checkGconsaleLimits(fieldGroup, this->wellState(),
|
|
|
|
simulator_.episodeIndex(), local_deferredLogger);
|
2020-05-04 08:56:34 -05:00
|
|
|
|
2020-10-09 06:38:33 -05:00
|
|
|
this->calculateProductivityIndexValues(local_deferredLogger);
|
|
|
|
|
2021-04-22 10:31:21 -05:00
|
|
|
this->commitWGState();
|
2023-10-09 04:14:48 -05:00
|
|
|
|
2021-05-25 05:57:11 -05:00
|
|
|
const Opm::Parallel::Communication& comm = grid().comm();
|
|
|
|
DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2019-02-03 01:13:11 -06:00
|
|
|
global_deferredLogger.logMessages();
|
|
|
|
}
|
2021-03-08 08:11:50 -06:00
|
|
|
|
|
|
|
//reporting output temperatures
|
|
|
|
this->computeWellTemperature();
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
2017-08-10 08:27:05 -05:00
|
|
|
|
2022-08-10 03:01:54 -05:00
|
|
|
|
2022-06-17 04:15:15 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
computeTotalRatesForDof(RateVector& rate,
|
2022-08-10 03:01:54 -05:00
|
|
|
unsigned elemIdx) const
|
2022-06-17 04:15:15 -05:00
|
|
|
{
|
|
|
|
rate = 0;
|
2022-08-10 03:01:54 -05:00
|
|
|
|
2022-06-17 04:15:15 -05:00
|
|
|
if (!is_cell_perforated_[elemIdx])
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (const auto& well : well_container_)
|
|
|
|
well->addCellRates(rate, elemIdx);
|
|
|
|
}
|
2018-08-16 04:51:36 -05:00
|
|
|
|
2022-08-10 03:01:54 -05:00
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
template <class Context>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
computeTotalRatesForDof(RateVector& rate,
|
|
|
|
const Context& context,
|
|
|
|
unsigned spaceIdx,
|
|
|
|
unsigned timeIdx) const
|
|
|
|
{
|
|
|
|
rate = 0;
|
|
|
|
int elemIdx = context.globalSpaceIndex(spaceIdx, timeIdx);
|
2018-11-15 07:37:01 -06:00
|
|
|
|
|
|
|
if (!is_cell_perforated_[elemIdx])
|
|
|
|
return;
|
|
|
|
|
2018-08-16 04:51:36 -05:00
|
|
|
for (const auto& well : well_container_)
|
|
|
|
well->addCellRates(rate, elemIdx);
|
|
|
|
}
|
|
|
|
|
2018-11-14 06:18:48 -06:00
|
|
|
|
|
|
|
|
2020-10-19 17:16:52 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-07-12 08:04:21 -05:00
|
|
|
initializeWellState(const int timeStepIdx)
|
2020-10-19 17:16:52 -05:00
|
|
|
{
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> cellPressures(this->local_num_cells_, 0.0);
|
2024-02-06 04:55:07 -06:00
|
|
|
ElementContext elemCtx(simulator_);
|
2020-10-19 17:16:52 -05:00
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& gridView = simulator_.vanguard().gridView();
|
2021-09-20 04:12:27 -05:00
|
|
|
|
2022-10-12 07:27:20 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
|
|
|
|
elemCtx.updatePrimaryStencil(elem);
|
2020-10-19 17:16:52 -05:00
|
|
|
elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
|
|
|
|
|
|
|
|
const auto& fs = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0).fluidState();
|
|
|
|
// copy of get perfpressure in Standard well except for value
|
2024-02-20 15:22:41 -06:00
|
|
|
Scalar& perf_pressure = cellPressures[elemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0)];
|
2020-10-19 17:16:52 -05:00
|
|
|
if (Indices::oilEnabled) {
|
|
|
|
perf_pressure = fs.pressure(FluidSystem::oilPhaseIdx).value();
|
|
|
|
} else if (Indices::waterEnabled) {
|
|
|
|
perf_pressure = fs.pressure(FluidSystem::waterPhaseIdx).value();
|
|
|
|
} else {
|
|
|
|
perf_pressure = fs.pressure(FluidSystem::gasPhaseIdx).value();
|
|
|
|
}
|
|
|
|
}
|
2024-02-06 04:55:07 -06:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::initializeWellState() failed: ", simulator_.vanguard().grid().comm());
|
2020-10-19 17:16:52 -05:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
this->wellState().init(cellPressures, this->schedule(), this->wells_ecl_,
|
|
|
|
this->local_parallel_well_info_, timeStepIdx,
|
|
|
|
&this->prevWellState(), this->well_perf_data_,
|
2023-07-12 08:04:21 -05:00
|
|
|
this->summaryState());
|
2020-10-19 17:16:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-06-15 10:19:49 -05:00
|
|
|
template<typename TypeTag>
|
2021-06-07 07:49:41 -05:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-10-11 04:33:02 -05:00
|
|
|
createWellContainer(const int report_step)
|
2017-06-15 10:19:49 -05:00
|
|
|
{
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2019-12-13 04:08:36 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
const int nw = this->numLocalWells();
|
2017-06-15 10:19:49 -05:00
|
|
|
|
2021-06-07 07:49:41 -05:00
|
|
|
well_container_.clear();
|
|
|
|
|
2017-08-21 03:23:42 -05:00
|
|
|
if (nw > 0) {
|
2021-06-07 07:49:41 -05:00
|
|
|
well_container_.reserve(nw);
|
2021-03-01 08:17:52 -06:00
|
|
|
|
2017-06-15 10:19:49 -05:00
|
|
|
for (int w = 0; w < nw; ++w) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const Well& well_ecl = this->wells_ecl_[w];
|
2021-06-02 04:02:46 -05:00
|
|
|
|
2023-01-12 05:20:09 -06:00
|
|
|
if (!well_ecl.hasConnections()) {
|
2021-06-02 04:02:46 -05:00
|
|
|
// No connections in this well. Nothing to do.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-10-23 02:09:45 -05:00
|
|
|
const std::string& well_name = well_ecl.name();
|
2021-03-01 08:43:43 -06:00
|
|
|
const auto well_status = this->schedule()
|
2023-10-11 04:33:02 -05:00
|
|
|
.getWell(well_name, report_step).getStatus();
|
2021-03-01 08:43:43 -06:00
|
|
|
|
|
|
|
if ((well_ecl.getStatus() == Well::Status::SHUT) ||
|
|
|
|
(well_status == Well::Status::SHUT))
|
|
|
|
{
|
|
|
|
// Due to ACTIONX the well might have been closed behind our back.
|
|
|
|
if (well_ecl.getStatus() != Well::Status::SHUT) {
|
|
|
|
this->closed_this_step_.insert(well_name);
|
2021-03-26 16:10:16 -05:00
|
|
|
this->wellState().shutWell(w);
|
2021-03-01 08:43:43 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2017-06-15 10:19:49 -05:00
|
|
|
|
2019-07-31 09:15:41 -05:00
|
|
|
// A new WCON keywords can re-open a well that was closed/shut due to Physical limit
|
2021-10-07 08:18:53 -05:00
|
|
|
if (this->wellTestState().well_is_closed(well_name)) {
|
2023-11-17 10:29:30 -06:00
|
|
|
// The well was shut this timestep, we are most likely retrying
|
|
|
|
// a timestep without the well in question, after it caused
|
|
|
|
// repeated timestep cuts. It should therefore not be opened,
|
|
|
|
// even if it was new or received new targets this report step.
|
2024-02-17 11:13:46 -06:00
|
|
|
const bool closed_this_step = (this->wellTestState().lastTestTime(well_name) == simulator_.time());
|
2019-07-31 09:15:41 -05:00
|
|
|
// TODO: more checking here, to make sure this standard more specific and complete
|
|
|
|
// maybe there is some WCON keywords will not open the well
|
2021-08-04 03:08:31 -05:00
|
|
|
auto& events = this->wellState().well(w).events;
|
2023-10-11 06:15:57 -05:00
|
|
|
if (events.hasEvent(ScheduleEvents::REQUEST_OPEN_WELL)) {
|
2023-11-17 10:29:30 -06:00
|
|
|
if (!closed_this_step) {
|
2024-02-17 11:13:46 -06:00
|
|
|
this->wellTestState().open_well(well_name);
|
|
|
|
this->wellTestState().open_completions(well_name);
|
2018-12-14 03:04:59 -06:00
|
|
|
}
|
2023-10-11 06:15:57 -05:00
|
|
|
events.clearEvent(ScheduleEvents::REQUEST_OPEN_WELL);
|
2018-11-17 16:36:31 -06:00
|
|
|
}
|
2019-07-31 09:15:41 -05:00
|
|
|
}
|
2018-11-17 16:30:27 -06:00
|
|
|
|
2019-07-31 09:15:41 -05:00
|
|
|
// TODO: should we do this for all kinds of closing reasons?
|
2021-09-21 02:30:02 -05:00
|
|
|
// something like wellTestState().hasWell(well_name)?
|
2019-08-07 07:13:11 -05:00
|
|
|
bool wellIsStopped = false;
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->wellTestState().well_is_closed(well_name))
|
2021-03-01 08:17:52 -06:00
|
|
|
{
|
|
|
|
if (well_ecl.getAutomaticShutIn()) {
|
2019-07-31 09:15:41 -05:00
|
|
|
// shut wells are not added to the well container
|
2021-03-26 16:10:16 -05:00
|
|
|
this->wellState().shutWell(w);
|
2019-07-31 09:15:41 -05:00
|
|
|
continue;
|
|
|
|
} else {
|
2022-06-07 03:01:02 -05:00
|
|
|
if (!well_ecl.getAllowCrossFlow()) {
|
|
|
|
// stopped wells where cross flow is not allowed
|
|
|
|
// are not added to the well container
|
|
|
|
this->wellState().shutWell(w);
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-07 07:13:11 -05:00
|
|
|
// stopped wells are added to the container but marked as stopped
|
2021-03-26 16:10:16 -05:00
|
|
|
this->wellState().stopWell(w);
|
2019-08-07 07:13:11 -05:00
|
|
|
wellIsStopped = true;
|
2018-06-06 08:17:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-30 09:19:07 -06:00
|
|
|
// shut wells with zero rante constraints and disallowing
|
|
|
|
if (!well_ecl.getAllowCrossFlow()) {
|
|
|
|
const bool any_zero_rate_constraint = well_ecl.isProducer()
|
2024-02-17 11:13:46 -06:00
|
|
|
? well_ecl.productionControls(this->summaryState_).anyZeroRateConstraint()
|
|
|
|
: well_ecl.injectionControls(this->summaryState_).anyZeroRateConstraint();
|
2024-01-30 09:19:07 -06:00
|
|
|
if (any_zero_rate_constraint) {
|
2019-12-13 04:08:36 -06:00
|
|
|
// Treat as shut, do not add to container.
|
2024-01-30 09:19:07 -06:00
|
|
|
local_deferredLogger.debug(fmt::format(" Well {} gets shut due to having zero rate constraint and disallowing crossflow ", well_ecl.name()) );
|
2021-03-26 16:10:16 -05:00
|
|
|
this->wellState().shutWell(w);
|
2019-12-13 04:08:36 -06:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-06 06:12:36 -06:00
|
|
|
if (well_status == Well::Status::STOP) {
|
2021-03-26 16:10:16 -05:00
|
|
|
this->wellState().stopWell(w);
|
2019-12-06 06:12:36 -06:00
|
|
|
wellIsStopped = true;
|
|
|
|
}
|
|
|
|
|
2023-10-11 04:33:02 -05:00
|
|
|
well_container_.emplace_back(this->createWellPointer(w, report_step));
|
2020-12-01 11:04:46 -06:00
|
|
|
|
2019-08-07 07:13:11 -05:00
|
|
|
if (wellIsStopped)
|
2021-06-07 07:49:41 -05:00
|
|
|
well_container_.back()->stopWell();
|
2017-06-15 10:19:49 -05:00
|
|
|
}
|
|
|
|
}
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2019-12-13 04:08:36 -06:00
|
|
|
// Collect log messages and print.
|
2023-10-09 04:14:48 -05:00
|
|
|
|
2021-05-25 05:57:11 -05:00
|
|
|
const Opm::Parallel::Communication& comm = grid().comm();
|
|
|
|
DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2019-12-13 04:08:36 -06:00
|
|
|
global_deferredLogger.logMessages();
|
|
|
|
}
|
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
this->well_container_generic_.clear();
|
2021-06-07 07:49:41 -05:00
|
|
|
for (auto& w : well_container_)
|
2024-02-17 11:13:46 -06:00
|
|
|
this->well_container_generic_.push_back(w.get());
|
2023-04-20 15:46:38 -05:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& network = this->schedule()[report_step].network();
|
2023-04-20 15:46:38 -05:00
|
|
|
if (network.active() && !this->node_pressures_.empty()) {
|
2024-02-17 11:13:46 -06:00
|
|
|
for (auto& well: this->well_container_generic_) {
|
2023-04-20 15:46:38 -05:00
|
|
|
// Producers only, since we so far only support the
|
|
|
|
// "extended" network model (properties defined by
|
|
|
|
// BRANPROP and NODEPROP) which only applies to producers.
|
|
|
|
if (well->isProducer()) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto it = this->node_pressures_.find(well->wellEcl().groupName());
|
|
|
|
if (it != this->node_pressures_.end()) {
|
2023-04-20 15:46:38 -05:00
|
|
|
// The well belongs to a group which has a network nodal pressure,
|
|
|
|
// set the dynamic THP constraint based on the network nodal pressure
|
2024-02-20 15:22:41 -06:00
|
|
|
const Scalar nodal_pressure = it->second;
|
2023-04-20 15:46:38 -05:00
|
|
|
well->setDynamicThpLimit(nodal_pressure);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-06-06 14:31:17 -05:00
|
|
|
|
|
|
|
this->registerOpenWellsForWBPCalculation();
|
2021-03-01 18:11:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-12-01 11:04:46 -06:00
|
|
|
template <typename TypeTag>
|
|
|
|
typename BlackoilWellModel<TypeTag>::WellInterfacePtr
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-10-11 04:33:02 -05:00
|
|
|
createWellPointer(const int wellID, const int report_step) const
|
2020-12-01 11:04:46 -06:00
|
|
|
{
|
|
|
|
const auto is_multiseg = this->wells_ecl_[wellID].isMultiSegment();
|
|
|
|
|
|
|
|
if (! (this->param_.use_multisegment_well_ && is_multiseg)) {
|
2023-10-11 04:33:02 -05:00
|
|
|
return this->template createTypedWellPointer<StandardWell<TypeTag>>(wellID, report_step);
|
2020-12-01 11:04:46 -06:00
|
|
|
}
|
|
|
|
else {
|
2023-10-11 04:33:02 -05:00
|
|
|
return this->template createTypedWellPointer<MultisegmentWell<TypeTag>>(wellID, report_step);
|
2020-12-01 11:04:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
template <typename WellType>
|
|
|
|
std::unique_ptr<WellType>
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
createTypedWellPointer(const int wellID, const int time_step) const
|
|
|
|
{
|
|
|
|
// Use the pvtRegionIdx from the top cell
|
|
|
|
const auto& perf_data = this->well_perf_data_[wellID];
|
|
|
|
|
2020-11-27 11:22:23 -06:00
|
|
|
// Cater for case where local part might have no perforations.
|
2021-03-01 08:17:52 -06:00
|
|
|
const auto pvtreg = perf_data.empty()
|
2024-02-17 11:13:46 -06:00
|
|
|
? 0 : this->pvt_region_idx_[perf_data.front().cell_index];
|
2021-03-01 08:17:52 -06:00
|
|
|
|
2021-09-27 05:00:39 -05:00
|
|
|
const auto& parallel_well_info = this->local_parallel_well_info_[wellID].get();
|
2021-03-01 08:17:52 -06:00
|
|
|
const auto global_pvtreg = parallel_well_info.broadcastFirstPerforationValue(pvtreg);
|
2020-11-27 11:22:23 -06:00
|
|
|
|
2020-12-01 11:04:46 -06:00
|
|
|
return std::make_unique<WellType>(this->wells_ecl_[wellID],
|
2020-11-27 11:22:23 -06:00
|
|
|
parallel_well_info,
|
2020-12-01 11:04:46 -06:00
|
|
|
time_step,
|
|
|
|
this->param_,
|
|
|
|
*this->rateConverter_,
|
2020-11-27 11:22:23 -06:00
|
|
|
global_pvtreg,
|
2020-12-01 11:04:46 -06:00
|
|
|
this->numComponents(),
|
|
|
|
this->numPhases(),
|
|
|
|
wellID,
|
|
|
|
perf_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-31 08:56:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
typename BlackoilWellModel<TypeTag>::WellInterfacePtr
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
createWellForWellTest(const std::string& well_name,
|
2019-02-07 07:43:17 -06:00
|
|
|
const int report_step,
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger& deferred_logger) const
|
2018-10-31 08:56:56 -05:00
|
|
|
{
|
|
|
|
// Finding the location of the well in wells_ecl
|
2024-02-17 11:13:46 -06:00
|
|
|
const int nw_wells_ecl = this->wells_ecl_.size();
|
2018-10-31 08:56:56 -05:00
|
|
|
int index_well_ecl = 0;
|
|
|
|
for (; index_well_ecl < nw_wells_ecl; ++index_well_ecl) {
|
2024-02-17 11:13:46 -06:00
|
|
|
if (well_name == this->wells_ecl_[index_well_ecl].name()) {
|
2018-10-31 08:56:56 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// It should be able to find in wells_ecl.
|
|
|
|
if (index_well_ecl == nw_wells_ecl) {
|
2022-12-20 05:36:43 -06:00
|
|
|
OPM_DEFLOG_THROW(std::logic_error,
|
|
|
|
fmt::format("Could not find well {} in wells_ecl ", well_name),
|
|
|
|
deferred_logger);
|
2018-10-31 08:56:56 -05:00
|
|
|
}
|
|
|
|
|
2020-12-01 11:04:46 -06:00
|
|
|
return this->createWellPointer(index_well_ecl, report_step);
|
2018-10-31 08:56:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-04-20 17:04:28 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-09-22 13:10:52 -05:00
|
|
|
doPreStepNetworkRebalance(DeferredLogger& deferred_logger) {
|
2024-02-06 04:55:07 -06:00
|
|
|
const double dt = this->simulator_.timeStepSize();
|
2023-04-20 17:04:28 -05:00
|
|
|
// TODO: should we also have the group and network backed-up here in case the solution did not get converged?
|
|
|
|
auto& well_state = this->wellState();
|
2023-10-06 08:14:59 -05:00
|
|
|
const std::size_t max_iter = param_.network_max_iterations_;
|
2023-04-20 17:04:28 -05:00
|
|
|
bool converged = false;
|
2023-08-15 02:32:10 -05:00
|
|
|
std::size_t iter = 0;
|
2023-04-20 17:04:28 -05:00
|
|
|
bool changed_well_group = false;
|
|
|
|
do {
|
2023-04-21 06:37:28 -05:00
|
|
|
changed_well_group = updateWellControlsAndNetwork(true, dt, deferred_logger);
|
2023-04-20 17:04:28 -05:00
|
|
|
assembleWellEqWithoutIteration(dt, deferred_logger);
|
|
|
|
converged = this->getWellConvergence(this->B_avg_, true).converged() && !changed_well_group;
|
|
|
|
if (converged) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++iter;
|
|
|
|
for (auto& well : this->well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = this->simulator_.vanguard().summaryState();
|
2023-04-21 03:17:03 -05:00
|
|
|
well->solveEqAndUpdateWellState(summary_state, well_state, deferred_logger);
|
2023-04-20 17:04:28 -05:00
|
|
|
}
|
|
|
|
this->initPrimaryVariablesEvaluation();
|
|
|
|
} while (iter < max_iter);
|
|
|
|
|
|
|
|
if (!converged) {
|
2023-10-06 08:14:59 -05:00
|
|
|
const std::string msg = fmt::format("Initial (pre-step) network balance did not get converged with {} iterations, "
|
|
|
|
"unconverged network balance result will be used", max_iter);
|
2023-04-20 17:04:28 -05:00
|
|
|
deferred_logger.warning(msg);
|
|
|
|
} else {
|
2023-10-06 08:14:59 -05:00
|
|
|
const std::string msg = fmt::format("Initial (pre-step) network balance converged with {} iterations", iter);
|
2023-04-20 17:04:28 -05:00
|
|
|
deferred_logger.debug(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-10-31 08:56:56 -05:00
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-11-08 06:57:36 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-11-08 06:57:36 -06:00
|
|
|
assemble(const int iterationIdx,
|
2018-06-29 02:12:14 -05:00
|
|
|
const double dt)
|
2017-02-13 09:45:06 -06:00
|
|
|
{
|
2017-11-08 06:57:36 -06:00
|
|
|
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2020-09-30 03:04:39 -05:00
|
|
|
if (this->glift_debug) {
|
2020-10-01 11:27:57 -05:00
|
|
|
const std::string msg = fmt::format(
|
|
|
|
"assemble() : iteration {}" , iterationIdx);
|
2024-02-17 11:13:46 -06:00
|
|
|
this->gliftDebug(msg, local_deferredLogger);
|
2020-09-30 03:04:39 -05:00
|
|
|
}
|
2020-05-07 09:13:39 -05:00
|
|
|
last_report_ = SimulatorReportSingle();
|
2020-09-07 08:05:02 -05:00
|
|
|
Dune::Timer perfTimer;
|
|
|
|
perfTimer.start();
|
2024-02-17 11:13:46 -06:00
|
|
|
this->closed_offending_wells_.clear();
|
2017-11-08 06:57:36 -06:00
|
|
|
|
2023-04-20 16:08:36 -05:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const int episodeIdx = simulator_.episodeIndex();
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& network = this->schedule()[episodeIdx].network();
|
|
|
|
if (!this->wellsActive() && !network.active()) {
|
2023-04-20 16:08:36 -05:00
|
|
|
return;
|
|
|
|
}
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
if (iterationIdx == 0 && this->wellsActive()) {
|
2021-09-23 13:15:19 -05:00
|
|
|
// try-catch is needed here as updateWellControls
|
|
|
|
// contains global communication and has either to
|
|
|
|
// be reached by all processes or all need to abort
|
|
|
|
// before.
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
{
|
2019-02-07 07:43:17 -06:00
|
|
|
calculateExplicitQuantities(local_deferredLogger);
|
2019-05-24 09:45:27 -05:00
|
|
|
prepareTimeStep(local_deferredLogger);
|
2019-02-07 07:43:17 -06:00
|
|
|
}
|
2022-12-20 05:36:43 -06:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
|
|
|
"assemble() failed (It=0): ",
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_, grid().comm());
|
2021-09-23 13:15:19 -05:00
|
|
|
}
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2023-04-21 06:37:28 -05:00
|
|
|
const bool well_group_control_changed = updateWellControlsAndNetwork(false, dt, local_deferredLogger);
|
2022-10-20 04:03:29 -05:00
|
|
|
|
2023-05-08 06:55:30 -05:00
|
|
|
// even when there is no wells active, the network nodal pressure still need to be updated through updateWellControlsAndNetwork()
|
|
|
|
// but there is no need to assemble the well equations
|
2024-02-17 11:13:46 -06:00
|
|
|
if ( ! this->wellsActive() ) {
|
2023-04-20 16:08:36 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
assembleWellEqWithoutIteration(dt, local_deferredLogger);
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2022-10-17 08:22:04 -05:00
|
|
|
// if group or well control changes we don't consider the
|
|
|
|
// case converged
|
|
|
|
last_report_.well_group_control_changed = well_group_control_changed;
|
2022-04-05 06:35:02 -05:00
|
|
|
last_report_.assemble_time_well += perfTimer.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
2022-10-17 08:22:04 -05:00
|
|
|
bool
|
2022-04-05 06:35:02 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-04-21 06:37:28 -05:00
|
|
|
updateWellControlsAndNetwork(const bool mandatory_network_balance, const double dt, DeferredLogger& local_deferredLogger)
|
2022-04-05 06:35:02 -05:00
|
|
|
{
|
2022-10-20 04:03:29 -05:00
|
|
|
// not necessarily that we always need to update once of the network solutions
|
|
|
|
bool do_network_update = true;
|
|
|
|
bool well_group_control_changed = false;
|
2023-04-25 04:56:46 -05:00
|
|
|
// after certain number of the iterations, we use relaxed tolerance for the network update
|
2023-08-15 02:32:10 -05:00
|
|
|
const std::size_t iteration_to_relax = param_.network_max_strict_iterations_;
|
2023-04-25 04:56:46 -05:00
|
|
|
// after certain number of the iterations, we terminate
|
2023-08-15 02:32:10 -05:00
|
|
|
const std::size_t max_iteration = param_.network_max_iterations_;
|
2022-10-20 04:03:29 -05:00
|
|
|
std::size_t network_update_iteration = 0;
|
|
|
|
while (do_network_update) {
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_ && (network_update_iteration == iteration_to_relax) ) {
|
2023-04-25 04:56:46 -05:00
|
|
|
local_deferredLogger.info(" we begin using relaxed tolerance for network update now after " + std::to_string(iteration_to_relax) + " iterations ");
|
|
|
|
}
|
|
|
|
const bool relax_network_balance = network_update_iteration >= iteration_to_relax;
|
2022-10-20 04:03:29 -05:00
|
|
|
std::tie(do_network_update, well_group_control_changed) =
|
2023-04-25 04:56:46 -05:00
|
|
|
updateWellControlsAndNetworkIteration(mandatory_network_balance, relax_network_balance, dt,local_deferredLogger);
|
2022-10-20 04:03:29 -05:00
|
|
|
++network_update_iteration;
|
2023-10-04 05:45:23 -05:00
|
|
|
|
|
|
|
if (network_update_iteration >= max_iteration ) {
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2023-10-04 06:24:01 -05:00
|
|
|
local_deferredLogger.info("maximum of " + std::to_string(max_iteration) + " iterations has been used, we stop the network update now. "
|
|
|
|
"The simulation will continue with unconverged network results");
|
2023-10-04 05:45:23 -05:00
|
|
|
}
|
2023-04-25 04:56:46 -05:00
|
|
|
break;
|
|
|
|
}
|
2022-10-20 04:03:29 -05:00
|
|
|
}
|
|
|
|
return well_group_control_changed;
|
|
|
|
}
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
std::pair<bool, bool>
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-04-21 06:37:28 -05:00
|
|
|
updateWellControlsAndNetworkIteration(const bool mandatory_network_balance,
|
2023-04-25 04:56:46 -05:00
|
|
|
const bool relax_network_tolerance,
|
2023-04-21 06:37:28 -05:00
|
|
|
const double dt,
|
2022-10-20 04:03:29 -05:00
|
|
|
DeferredLogger& local_deferredLogger)
|
|
|
|
{
|
2023-04-21 06:37:28 -05:00
|
|
|
auto [well_group_control_changed, more_network_update] =
|
2023-04-25 04:56:46 -05:00
|
|
|
updateWellControls(mandatory_network_balance, local_deferredLogger, relax_network_tolerance);
|
2019-11-06 09:16:19 -06:00
|
|
|
|
2022-02-08 08:46:08 -06:00
|
|
|
bool alq_updated = false;
|
2021-09-23 13:15:19 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
{
|
2019-02-07 07:43:17 -06:00
|
|
|
// Set the well primary variables based on the value of well solutions
|
|
|
|
initPrimaryVariablesEvaluation();
|
2017-02-13 09:45:06 -06:00
|
|
|
|
2022-02-08 08:46:08 -06:00
|
|
|
alq_updated = maybeDoGasLiftOptimize(local_deferredLogger);
|
2022-10-20 04:03:29 -05:00
|
|
|
|
|
|
|
prepareWellsBeforeAssembling(dt, local_deferredLogger);
|
2021-03-18 02:29:23 -05:00
|
|
|
}
|
2022-10-20 04:03:29 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger, "updateWellControlsAndNetworkIteration() failed: ",
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_, grid().comm());
|
2021-11-18 05:57:16 -06:00
|
|
|
|
|
|
|
//update guide rates
|
2024-02-06 04:55:07 -06:00
|
|
|
const int reportStepIdx = simulator_.episodeIndex();
|
2024-02-17 11:13:46 -06:00
|
|
|
if (alq_updated || BlackoilWellModelGuideRates(*this).
|
2024-02-17 11:13:46 -06:00
|
|
|
guideRateUpdateIsNeeded(reportStepIdx)) {
|
2024-02-06 04:55:07 -06:00
|
|
|
const double simulationTime = simulator_.time();
|
|
|
|
const auto& comm = simulator_.vanguard().grid().comm();
|
|
|
|
const auto& summaryState = simulator_.vanguard().summaryState();
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> pot(this->numPhases(), 0.0);
|
2024-02-19 06:47:25 -06:00
|
|
|
const Group& fieldGroup = this->schedule().getGroup("FIELD", reportStepIdx);
|
2024-02-20 15:22:41 -06:00
|
|
|
WellGroupHelpers<Scalar>::updateGuideRates(fieldGroup,
|
2024-02-19 06:47:25 -06:00
|
|
|
this->schedule(),
|
|
|
|
summaryState,
|
|
|
|
this->phase_usage_,
|
|
|
|
reportStepIdx,
|
|
|
|
simulationTime,
|
|
|
|
this->wellState(),
|
|
|
|
this->groupState(),
|
|
|
|
comm,
|
|
|
|
&this->guideRate_,
|
|
|
|
pot,
|
|
|
|
local_deferredLogger);
|
2021-11-18 05:57:16 -06:00
|
|
|
}
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
return {more_network_update, well_group_control_changed};
|
|
|
|
}
|
2022-04-05 06:35:02 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-06-20 15:52:51 -05:00
|
|
|
assembleDomain([[maybe_unused]] const int iterationIdx,
|
2023-06-15 07:26:56 -05:00
|
|
|
const double dt,
|
|
|
|
const Domain& domain)
|
|
|
|
{
|
|
|
|
last_report_ = SimulatorReportSingle();
|
|
|
|
Dune::Timer perfTimer;
|
|
|
|
perfTimer.start();
|
|
|
|
|
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const int episodeIdx = simulator_.episodeIndex();
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& network = this->schedule()[episodeIdx].network();
|
|
|
|
if (!this->wellsActive() && !network.active()) {
|
2023-06-15 07:26:56 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// We assume that calculateExplicitQuantities() and
|
|
|
|
// prepareTimeStep() have been called already for the entire
|
|
|
|
// well model, so we do not need to do it here (when
|
|
|
|
// iterationIdx is 0).
|
|
|
|
|
|
|
|
DeferredLogger local_deferredLogger;
|
|
|
|
updateWellControlsDomain(local_deferredLogger, domain);
|
|
|
|
initPrimaryVariablesEvaluationDomain(domain);
|
|
|
|
assembleWellEqDomain(dt, domain, local_deferredLogger);
|
|
|
|
|
|
|
|
// TODO: errors here must be caught higher up, as this method is not called in parallel.
|
|
|
|
// We will log errors on rank 0, but not other ranks for now.
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2023-06-15 07:26:56 -05:00
|
|
|
local_deferredLogger.logMessages();
|
|
|
|
}
|
|
|
|
|
|
|
|
last_report_.converged = true;
|
|
|
|
last_report_.assemble_time_well += perfTimer.stop();
|
|
|
|
}
|
|
|
|
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2021-03-25 16:40:12 -05:00
|
|
|
template<typename TypeTag>
|
2022-02-08 08:46:08 -06:00
|
|
|
bool
|
2021-03-25 16:40:12 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-05 04:22:44 -05:00
|
|
|
maybeDoGasLiftOptimize(DeferredLogger& deferred_logger)
|
2021-03-03 06:59:26 -06:00
|
|
|
{
|
2022-01-19 03:14:47 -06:00
|
|
|
bool do_glift_optimization = false;
|
2022-02-08 08:46:08 -06:00
|
|
|
int num_wells_changed = 0;
|
2024-02-06 04:55:07 -06:00
|
|
|
const double simulation_time = simulator_.time();
|
2024-02-20 15:22:41 -06:00
|
|
|
const Scalar min_wait = simulator_.vanguard().schedule().glo(simulator_.episodeIndex()).min_wait();
|
2022-01-19 03:14:47 -06:00
|
|
|
// We only optimize if a min_wait time has past.
|
|
|
|
// If all_newton is true we still want to optimize several times pr timestep
|
|
|
|
// i.e. we also optimize if check simulation_time == last_glift_opt_time_
|
|
|
|
// that is when the last_glift_opt_time is already updated with the current time step
|
2024-02-17 11:13:46 -06:00
|
|
|
if ( simulation_time == this->last_glift_opt_time_ || simulation_time >= (this->last_glift_opt_time_ + min_wait)) {
|
2022-01-19 03:14:47 -06:00
|
|
|
do_glift_optimization = true;
|
2024-02-17 11:13:46 -06:00
|
|
|
this->last_glift_opt_time_ = simulation_time;
|
2022-01-19 03:14:47 -06:00
|
|
|
}
|
|
|
|
|
2021-09-27 07:04:50 -05:00
|
|
|
if (do_glift_optimization) {
|
2021-05-27 01:31:49 -05:00
|
|
|
GLiftOptWells glift_wells;
|
|
|
|
GLiftProdWells prod_wells;
|
|
|
|
GLiftWellStateMap state_map;
|
|
|
|
// NOTE: To make GasLiftGroupInfo (see below) independent of the TypeTag
|
|
|
|
// associated with *this (i.e. BlackoilWellModel<TypeTag>) we observe
|
|
|
|
// that GasLiftGroupInfo's only dependence on *this is that it needs to
|
|
|
|
// access the eclipse Wells in the well container (the eclipse Wells
|
|
|
|
// themselves are independent of the TypeTag).
|
|
|
|
// Hence, we extract them from the well container such that we can pass
|
|
|
|
// them to the GasLiftGroupInfo constructor.
|
|
|
|
GLiftEclWells ecl_well_map;
|
|
|
|
initGliftEclWellMap(ecl_well_map);
|
|
|
|
GasLiftGroupInfo group_info {
|
|
|
|
ecl_well_map,
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.vanguard().schedule(),
|
|
|
|
simulator_.vanguard().summaryState(),
|
|
|
|
simulator_.episodeIndex(),
|
|
|
|
simulator_.model().newtonMethod().numIterations(),
|
2024-02-17 11:13:46 -06:00
|
|
|
this->phase_usage_,
|
2021-05-27 01:31:49 -05:00
|
|
|
deferred_logger,
|
2021-06-22 02:52:22 -05:00
|
|
|
this->wellState(),
|
2022-10-20 05:58:42 -05:00
|
|
|
this->groupState(),
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.vanguard().grid().comm(),
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
this->glift_debug
|
2021-05-27 01:31:49 -05:00
|
|
|
};
|
2021-06-22 02:52:22 -05:00
|
|
|
group_info.initialize();
|
2024-02-17 11:13:46 -06:00
|
|
|
gasLiftOptimizationStage1(deferred_logger, prod_wells, glift_wells,
|
|
|
|
group_info, state_map);
|
|
|
|
this->gasLiftOptimizationStage2(deferred_logger, prod_wells, glift_wells,
|
|
|
|
group_info, state_map, simulator_.episodeIndex());
|
|
|
|
if (this->glift_debug) {
|
|
|
|
this->gliftDebugShowALQ(deferred_logger);
|
|
|
|
}
|
2022-02-08 08:46:08 -06:00
|
|
|
num_wells_changed = glift_wells.size();
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
2022-03-24 06:42:46 -05:00
|
|
|
num_wells_changed = this->comm_.sum(num_wells_changed);
|
2022-02-08 08:46:08 -06:00
|
|
|
return num_wells_changed > 0;
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
gasLiftOptimizationStage1(DeferredLogger& deferred_logger,
|
2024-02-19 08:46:29 -06:00
|
|
|
GLiftProdWells& prod_wells,
|
|
|
|
GLiftOptWells &glift_wells,
|
|
|
|
GasLiftGroupInfo<Scalar>& group_info,
|
|
|
|
GLiftWellStateMap& state_map)
|
2021-05-27 01:31:49 -05:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
auto comm = simulator_.vanguard().grid().comm();
|
2021-06-17 03:44:32 -05:00
|
|
|
int num_procs = comm.size();
|
2021-05-27 01:31:49 -05:00
|
|
|
// NOTE: Gas lift optimization stage 1 seems to be difficult
|
|
|
|
// to do in parallel since the wells are optimized on different
|
|
|
|
// processes and each process needs to know the current ALQ allocated
|
|
|
|
// to each group it is a memeber of in order to check group limits and avoid
|
|
|
|
// allocating more ALQ than necessary. (Surplus ALQ is removed in
|
|
|
|
// stage 2). In stage1, as each well is adding ALQ, the current group ALQ needs
|
|
|
|
// to be communicated to the other processes. But there is no common
|
|
|
|
// synchronization point that all process will reach in the
|
|
|
|
// runOptimizeLoop_() in GasLiftSingleWell.cpp.
|
|
|
|
//
|
|
|
|
// TODO: Maybe a better solution could be invented by distributing
|
|
|
|
// wells according to certain parent groups. Then updated group rates
|
|
|
|
// might not have to be communicated to the other processors.
|
|
|
|
|
|
|
|
// Currently, the best option seems to be to run this part sequentially
|
|
|
|
// (not in parallel).
|
|
|
|
//
|
|
|
|
// TODO: The simplest approach seems to be if a) one process could take
|
|
|
|
// ownership of all the wells (the union of all the wells in the
|
|
|
|
// well_container_ of each process) then this process could do the
|
|
|
|
// optimization, while the other processes could wait for it to
|
|
|
|
// finish (e.g. comm.barrier()), or alternatively, b) if all
|
|
|
|
// processes could take ownership of all the wells. Then there
|
|
|
|
// would be no need for synchronization here..
|
|
|
|
//
|
2021-06-17 03:44:32 -05:00
|
|
|
for (int i = 0; i< num_procs; i++) {
|
2021-05-27 01:31:49 -05:00
|
|
|
int num_rates_to_sync = 0; // communication variable
|
|
|
|
GLiftSyncGroups groups_to_sync;
|
2021-06-17 03:44:32 -05:00
|
|
|
if (comm.rank() == i) {
|
2021-05-27 01:31:49 -05:00
|
|
|
// Run stage1: Optimize single wells while also checking group limits
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
// NOTE: Only the wells in "group_info" needs to be optimized
|
|
|
|
if (group_info.hasWell(well->name())) {
|
2022-02-07 04:28:35 -06:00
|
|
|
gasLiftOptimizationStage1SingleWell(
|
|
|
|
well.get(), deferred_logger, prod_wells, glift_wells,
|
|
|
|
group_info, state_map, groups_to_sync
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
num_rates_to_sync = groups_to_sync.size();
|
|
|
|
}
|
|
|
|
num_rates_to_sync = comm.sum(num_rates_to_sync);
|
|
|
|
if (num_rates_to_sync > 0) {
|
|
|
|
std::vector<int> group_indexes;
|
|
|
|
group_indexes.reserve(num_rates_to_sync);
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> group_alq_rates;
|
2021-05-27 01:31:49 -05:00
|
|
|
group_alq_rates.reserve(num_rates_to_sync);
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> group_oil_rates;
|
2021-05-27 01:31:49 -05:00
|
|
|
group_oil_rates.reserve(num_rates_to_sync);
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> group_gas_rates;
|
2021-05-27 01:31:49 -05:00
|
|
|
group_gas_rates.reserve(num_rates_to_sync);
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> group_water_rates;
|
2021-11-04 07:12:05 -05:00
|
|
|
group_water_rates.reserve(num_rates_to_sync);
|
2021-05-27 01:31:49 -05:00
|
|
|
if (comm.rank() == i) {
|
|
|
|
for (auto idx : groups_to_sync) {
|
2021-11-04 07:12:05 -05:00
|
|
|
auto [oil_rate, gas_rate, water_rate, alq] = group_info.getRates(idx);
|
2021-05-27 01:31:49 -05:00
|
|
|
group_indexes.push_back(idx);
|
|
|
|
group_oil_rates.push_back(oil_rate);
|
|
|
|
group_gas_rates.push_back(gas_rate);
|
2021-11-04 07:12:05 -05:00
|
|
|
group_water_rates.push_back(water_rate);
|
2021-05-27 01:31:49 -05:00
|
|
|
group_alq_rates.push_back(alq);
|
|
|
|
}
|
2021-11-09 05:48:48 -06:00
|
|
|
} else {
|
|
|
|
group_indexes.resize(num_rates_to_sync);
|
|
|
|
group_oil_rates.resize(num_rates_to_sync);
|
|
|
|
group_gas_rates.resize(num_rates_to_sync);
|
|
|
|
group_water_rates.resize(num_rates_to_sync);
|
|
|
|
group_alq_rates.resize(num_rates_to_sync);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
2022-09-13 08:07:15 -05:00
|
|
|
#if HAVE_MPI
|
2024-01-23 03:51:32 -06:00
|
|
|
Parallel::MpiSerializer ser(comm);
|
2022-09-07 06:43:23 -05:00
|
|
|
ser.broadcast(i, group_indexes, group_oil_rates,
|
|
|
|
group_gas_rates, group_water_rates, group_alq_rates);
|
2022-09-13 08:07:15 -05:00
|
|
|
#endif
|
2021-05-27 01:31:49 -05:00
|
|
|
if (comm.rank() != i) {
|
|
|
|
for (int j=0; j<num_rates_to_sync; j++) {
|
|
|
|
group_info.updateRate(group_indexes[j],
|
2021-11-04 07:12:05 -05:00
|
|
|
group_oil_rates[j], group_gas_rates[j], group_water_rates[j], group_alq_rates[j]);
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
}
|
Improve debugging tools in gaslift code.
Introduces a gaslift debugging variable in ALQState in WellState. This
variable will persist between timesteps in contrast to when debugging
variables are defined in GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2.
Currently only an integer variable debug_counter is added to ALQState,
which can be used as follows: First debugging is switched on globally
for BlackOilWellModel, GasLiftSingleWell, GasLiftGroupState, and
GasLiftStage2 by setting glift_debug to a true value in BlackOilWellModelGeneric.
Then, the following debugging code can be added to e.g. one of
GasLiftSingleWell, GasLiftGroupState, or GasLiftStage2 :
auto count = debugUpdateGlobalCounter_();
if (count == some_integer) {
displayDebugMessage_("stop here");
}
Here, the integer "some_integer" is determined typically by looking at
the debugging output of a previous run. This can be done since the
call to debugUpdateGlobalCounter_() will print out the current value
of the counter and then increment the counter by one. And it will be
easy to recognize these values in the debug ouput. If you find a place
in the output that looks suspect, just take a note of the counter
value in the output around that point and insert the value for
"some_integer", then after recompiling the code with the desired value
for "some_integer", it is now easy to set a breakpoint in GDB at the
line
displayDebugMessage_("stop here").
shown in the above snippet. This should improve the ability to quickly
to set a breakpoint in GDB around at a given time and point in the simulation.
2022-01-23 13:37:26 -06:00
|
|
|
if (this->glift_debug) {
|
|
|
|
int counter = 0;
|
|
|
|
if (comm.rank() == i) {
|
|
|
|
counter = this->wellState().gliftGetDebugCounter();
|
|
|
|
}
|
|
|
|
counter = comm.sum(counter);
|
|
|
|
if (comm.rank() != i) {
|
|
|
|
this->wellState().gliftSetDebugCounter(counter);
|
|
|
|
}
|
|
|
|
}
|
2021-05-27 01:31:49 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 04:28:35 -06:00
|
|
|
// NOTE: this method cannot be const since it passes this->wellState()
|
|
|
|
// (see below) to the GasLiftSingleWell constructor which accepts WellState
|
|
|
|
// as a non-const reference..
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2024-02-19 08:46:29 -06:00
|
|
|
gasLiftOptimizationStage1SingleWell(WellInterface<TypeTag>* well,
|
|
|
|
DeferredLogger& deferred_logger,
|
|
|
|
GLiftProdWells& prod_wells,
|
|
|
|
GLiftOptWells& glift_wells,
|
|
|
|
GasLiftGroupInfo<Scalar>& group_info,
|
|
|
|
GLiftWellStateMap& state_map,
|
|
|
|
GLiftSyncGroups& sync_groups)
|
2022-02-07 04:28:35 -06:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
2022-02-07 04:28:35 -06:00
|
|
|
std::unique_ptr<GasLiftSingleWell> glift
|
|
|
|
= std::make_unique<GasLiftSingleWell>(
|
2024-02-06 04:55:07 -06:00
|
|
|
*well, simulator_, summary_state,
|
2022-02-07 04:28:35 -06:00
|
|
|
deferred_logger, this->wellState(), this->groupState(),
|
2022-03-24 06:42:46 -05:00
|
|
|
group_info, sync_groups, this->comm_, this->glift_debug);
|
2022-02-07 04:28:35 -06:00
|
|
|
auto state = glift->runOptimize(
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.model().newtonMethod().numIterations());
|
2022-02-07 04:28:35 -06:00
|
|
|
if (state) {
|
|
|
|
state_map.insert({well->name(), std::move(state)});
|
|
|
|
glift_wells.insert({well->name(), std::move(glift)});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
prod_wells.insert({well->name(), well});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-27 01:31:49 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
initGliftEclWellMap(GLiftEclWells &ecl_well_map)
|
|
|
|
{
|
|
|
|
for ( const auto& well: well_container_ ) {
|
|
|
|
ecl_well_map.try_emplace(
|
|
|
|
well->name(), &(well->wellEcl()), well->indexOfWell());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-03 06:59:26 -06:00
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 09:45:06 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-05 04:22:44 -05:00
|
|
|
assembleWellEq(const double dt, DeferredLogger& deferred_logger)
|
2017-02-13 09:45:06 -06:00
|
|
|
{
|
2018-06-06 08:17:59 -05:00
|
|
|
for (auto& well : well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->assembleWellEq(simulator_, dt, this->wellState(), this->groupState(), deferred_logger);
|
2017-07-21 07:21:17 -05:00
|
|
|
}
|
2017-02-13 10:07:34 -06:00
|
|
|
}
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
assembleWellEqDomain(const double dt, const Domain& domain, DeferredLogger& deferred_logger)
|
|
|
|
{
|
|
|
|
for (auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->assembleWellEq(simulator_, dt, this->wellState(), this->groupState(), deferred_logger);
|
2023-06-15 07:26:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
prepareWellsBeforeAssembling(const double dt, DeferredLogger& deferred_logger)
|
|
|
|
{
|
|
|
|
for (auto& well : well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->prepareWellBeforeAssembling(simulator_, dt, this->wellState(), this->groupState(), deferred_logger);
|
2022-10-20 04:03:29 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
assembleWellEqWithoutIteration(const double dt, DeferredLogger& deferred_logger)
|
|
|
|
{
|
2023-12-07 08:41:35 -06:00
|
|
|
// We make sure that all processes throw in case there is an exception
|
|
|
|
// on one of them (WetGasPvt::saturationPressure might throw if not converged)
|
2023-12-07 09:41:16 -06:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
2023-12-07 08:41:35 -06:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
for (auto& well: well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->assembleWellEqWithoutIteration(simulator_, dt, this->wellState(), this->groupState(),
|
2022-10-20 04:03:29 -05:00
|
|
|
deferred_logger);
|
|
|
|
}
|
2023-12-07 08:41:35 -06:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(deferred_logger, "BlackoilWellModel::assembleWellEqWithoutIteration failed: ",
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_, grid().comm());
|
2023-12-07 08:41:35 -06:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-05 08:54:48 -06:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-05-22 03:01:33 -05:00
|
|
|
apply(BVector& r) const
|
2018-11-05 08:54:48 -06:00
|
|
|
{
|
|
|
|
for (auto& well : well_container_) {
|
|
|
|
well->apply(r);
|
|
|
|
}
|
|
|
|
}
|
2017-02-13 10:07:34 -06:00
|
|
|
|
|
|
|
|
2017-07-21 07:21:17 -05:00
|
|
|
// Ax = A x - C D^-1 B x
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 10:07:34 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-06-07 07:23:43 -05:00
|
|
|
apply(const BVector& x, BVector& Ax) const
|
2017-02-13 10:07:34 -06:00
|
|
|
{
|
2017-07-21 07:21:17 -05:00
|
|
|
for (auto& well : well_container_) {
|
2018-03-02 13:47:04 -06:00
|
|
|
well->apply(x, Ax);
|
2017-07-21 07:21:17 -05:00
|
|
|
}
|
2017-02-13 10:07:34 -06:00
|
|
|
}
|
|
|
|
|
2024-05-02 04:31:23 -05:00
|
|
|
#if COMPILE_BDA_BRIDGE
|
2020-03-13 08:21:59 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
getWellContributions(WellContributions& wellContribs) const
|
|
|
|
{
|
2020-05-15 09:00:09 -05:00
|
|
|
// prepare for StandardWells
|
2021-09-06 05:47:21 -05:00
|
|
|
wellContribs.setBlockSize(StandardWell<TypeTag>::Indices::numEq, StandardWell<TypeTag>::numStaticWellEq);
|
2020-09-24 14:34:46 -05:00
|
|
|
|
2020-03-13 08:21:59 -05:00
|
|
|
for(unsigned int i = 0; i < well_container_.size(); i++){
|
|
|
|
auto& well = well_container_[i];
|
|
|
|
std::shared_ptr<StandardWell<TypeTag> > derived = std::dynamic_pointer_cast<StandardWell<TypeTag> >(well);
|
2020-05-15 09:00:09 -05:00
|
|
|
if (derived) {
|
2022-11-11 14:41:24 -06:00
|
|
|
wellContribs.addNumBlocks(derived->linSys().getNumBlocks());
|
2020-05-15 09:00:09 -05:00
|
|
|
}
|
2020-03-13 08:21:59 -05:00
|
|
|
}
|
2020-05-15 09:00:09 -05:00
|
|
|
|
|
|
|
// allocate memory for data from StandardWells
|
2020-03-18 09:08:48 -05:00
|
|
|
wellContribs.alloc();
|
2020-05-15 09:00:09 -05:00
|
|
|
|
2020-03-13 08:21:59 -05:00
|
|
|
for(unsigned int i = 0; i < well_container_.size(); i++){
|
|
|
|
auto& well = well_container_[i];
|
2020-05-15 09:00:09 -05:00
|
|
|
// maybe WellInterface could implement addWellContribution()
|
2022-11-11 14:41:24 -06:00
|
|
|
auto derived_std = std::dynamic_pointer_cast<StandardWell<TypeTag>>(well);
|
2020-05-15 09:00:09 -05:00
|
|
|
if (derived_std) {
|
2022-11-11 14:41:24 -06:00
|
|
|
derived_std->linSys().extract(derived_std->numStaticWellEq, wellContribs);
|
2020-03-19 10:06:49 -05:00
|
|
|
} else {
|
2020-05-15 09:00:09 -05:00
|
|
|
auto derived_ms = std::dynamic_pointer_cast<MultisegmentWell<TypeTag> >(well);
|
|
|
|
if (derived_ms) {
|
2022-11-11 14:41:24 -06:00
|
|
|
derived_ms->linSys().extract(wellContribs);
|
2020-05-15 09:00:09 -05:00
|
|
|
} else {
|
|
|
|
OpmLog::warning("Warning unknown type of well");
|
|
|
|
}
|
2020-03-19 10:06:49 -05:00
|
|
|
}
|
2020-03-13 08:21:59 -05:00
|
|
|
}
|
|
|
|
}
|
2024-05-02 04:31:23 -05:00
|
|
|
#endif
|
2017-02-13 10:07:34 -06:00
|
|
|
|
2017-07-21 07:21:17 -05:00
|
|
|
// Ax = Ax - alpha * C D^-1 B x
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 10:07:34 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-06-07 07:23:43 -05:00
|
|
|
applyScaleAdd(const Scalar alpha, const BVector& x, BVector& Ax) const
|
2017-02-13 10:07:34 -06:00
|
|
|
{
|
2022-09-14 04:27:22 -05:00
|
|
|
if (this->well_container_.empty()) {
|
2017-02-13 10:07:34 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( scaleAddRes_.size() != Ax.size() ) {
|
|
|
|
scaleAddRes_.resize( Ax.size() );
|
|
|
|
}
|
|
|
|
|
|
|
|
scaleAddRes_ = 0.0;
|
2017-07-21 07:21:17 -05:00
|
|
|
// scaleAddRes_ = - C D^-1 B x
|
2017-02-13 10:07:34 -06:00
|
|
|
apply( x, scaleAddRes_ );
|
2017-07-21 07:21:17 -05:00
|
|
|
// Ax = Ax + alpha * scaleAddRes_
|
2017-02-13 10:07:34 -06:00
|
|
|
Ax.axpy( alpha, scaleAddRes_ );
|
|
|
|
}
|
|
|
|
|
2022-05-05 07:21:28 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
addWellContributions(SparseMatrixAdapter& jacobian) const
|
|
|
|
{
|
|
|
|
for ( const auto& well: well_container_ ) {
|
|
|
|
well->addWellContributions(jacobian);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
addWellPressureEquations(PressureMatrix& jacobian, const BVector& weights,const bool use_well_weights) const
|
|
|
|
{
|
|
|
|
int nw = this->numLocalWellsEnd();
|
|
|
|
int rdofs = local_num_cells_;
|
2022-06-02 06:33:23 -05:00
|
|
|
for ( int i = 0; i < nw; i++ ){
|
2023-10-09 04:14:48 -05:00
|
|
|
int wdof = rdofs + i;
|
2022-05-05 07:21:28 -05:00
|
|
|
jacobian[wdof][wdof] = 1.0;// better scaling ?
|
|
|
|
}
|
2022-06-10 04:08:24 -05:00
|
|
|
|
2022-06-02 06:33:23 -05:00
|
|
|
for ( const auto& well : well_container_ ) {
|
2022-05-05 07:21:28 -05:00
|
|
|
well->addWellPressureEquations(jacobian, weights, pressureVarIndex, use_well_weights, this->wellState());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-11 09:10:00 -05:00
|
|
|
template <typename TypeTag>
|
|
|
|
void BlackoilWellModel<TypeTag>::
|
2023-01-03 03:48:19 -06:00
|
|
|
addReservoirSourceTerms(GlobalEqVector& residual,
|
|
|
|
std::vector<typename SparseMatrixAdapter::MatrixBlock*>& diagMatAddress) const
|
2022-10-11 09:10:00 -05:00
|
|
|
{
|
|
|
|
// NB this loop may write multiple times to the same element
|
|
|
|
// if a cell is perforated by more than one well, so it should
|
|
|
|
// not be OpenMP-parallelized.
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
if (!well->isOperableAndSolvable() && !well->wellIsStopped()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const auto& cells = well->cells();
|
|
|
|
const auto& rates = well->connectionRates();
|
|
|
|
for (unsigned perfIdx = 0; perfIdx < rates.size(); ++perfIdx) {
|
|
|
|
unsigned cellIdx = cells[perfIdx];
|
|
|
|
auto rate = rates[perfIdx];
|
|
|
|
rate *= -1.0;
|
|
|
|
VectorBlockType res(0.0);
|
2023-01-03 03:48:19 -06:00
|
|
|
using MatrixBlockType = typename SparseMatrixAdapter::MatrixBlock;
|
2022-10-11 09:10:00 -05:00
|
|
|
MatrixBlockType bMat(0.0);
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.model().linearizer().setResAndJacobi(res, bMat, rate);
|
2022-10-11 09:10:00 -05:00
|
|
|
residual[cellIdx] += res;
|
2023-01-03 03:48:19 -06:00
|
|
|
*diagMatAddress[cellIdx] += bMat;
|
2022-10-11 09:10:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-05-05 07:21:28 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
addWellPressureEquationsStruct(PressureMatrix& jacobian) const
|
|
|
|
{
|
|
|
|
int nw = this->numLocalWellsEnd();
|
|
|
|
int rdofs = local_num_cells_;
|
|
|
|
for(int i=0; i < nw; i++){
|
2023-10-09 04:14:48 -05:00
|
|
|
int wdof = rdofs + i;
|
2022-05-05 07:21:28 -05:00
|
|
|
jacobian.entry(wdof,wdof) = 1.0;// better scaling ?
|
|
|
|
}
|
2024-02-17 11:13:46 -06:00
|
|
|
std::vector<std::vector<int>> wellconnections = this->getMaxWellConnections();
|
2022-05-05 07:21:28 -05:00
|
|
|
for(int i=0; i < nw; i++){
|
|
|
|
const auto& perfcells = wellconnections[i];
|
|
|
|
for(int perfcell : perfcells){
|
2023-10-09 04:14:48 -05:00
|
|
|
int wdof = rdofs + i;
|
2022-05-05 07:21:28 -05:00
|
|
|
jacobian.entry(wdof,perfcell) = 0.0;
|
|
|
|
jacobian.entry(perfcell, wdof) = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 10:07:34 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-11-08 06:57:36 -06:00
|
|
|
recoverWellSolutionAndUpdateWellState(const BVector& x)
|
2017-02-13 10:07:34 -06:00
|
|
|
{
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2021-09-20 03:56:11 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
2022-09-14 04:27:22 -05:00
|
|
|
for (auto& well : well_container_) {
|
2023-03-22 09:10:00 -05:00
|
|
|
well->recoverWellSolutionAndUpdateWellState(summary_state, x, this->wellState(), local_deferredLogger);
|
2019-02-07 07:43:17 -06:00
|
|
|
}
|
2021-03-18 02:29:23 -05:00
|
|
|
}
|
2021-09-20 03:56:11 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
|
|
|
"recoverWellSolutionAndUpdateWellState() failed: ",
|
2024-02-17 11:13:46 -06:00
|
|
|
this->terminal_output_, simulator_.vanguard().grid().comm());
|
2017-02-13 10:07:34 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
recoverWellSolutionAndUpdateWellStateDomain(const BVector& x, const Domain& domain)
|
|
|
|
{
|
|
|
|
// Note: no point in trying to do a parallel gathering
|
|
|
|
// try/catch here, as this function is not called in
|
|
|
|
// parallel but for each individual domain of each rank.
|
|
|
|
DeferredLogger local_deferredLogger;
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = this->simulator_.vanguard().summaryState();
|
2023-06-15 07:26:56 -05:00
|
|
|
for (auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
well->recoverWellSolutionAndUpdateWellState(summary_state, x,
|
|
|
|
this->wellState(),
|
|
|
|
local_deferredLogger);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO: avoid losing the logging information that could
|
|
|
|
// be stored in the local_deferredlogger in a parallel case.
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2023-06-15 07:26:56 -05:00
|
|
|
local_deferredLogger.logMessages();
|
|
|
|
}
|
|
|
|
}
|
2017-02-13 10:07:34 -06:00
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-13 10:07:34 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2017-08-21 08:41:25 -05:00
|
|
|
initPrimaryVariablesEvaluation() const
|
2017-02-13 10:07:34 -06:00
|
|
|
{
|
2017-07-25 03:15:27 -05:00
|
|
|
for (auto& well : well_container_) {
|
2017-08-21 08:41:25 -05:00
|
|
|
well->initPrimaryVariablesEvaluation();
|
2017-06-19 05:43:08 -05:00
|
|
|
}
|
2017-02-13 10:07:34 -06:00
|
|
|
}
|
|
|
|
|
2017-02-14 08:06:57 -06:00
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
initPrimaryVariablesEvaluationDomain(const Domain& domain) const
|
|
|
|
{
|
|
|
|
for (auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
well->initPrimaryVariablesEvaluation();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
ConvergenceReport
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
getDomainWellConvergence(const Domain& domain,
|
2024-01-12 08:56:00 -06:00
|
|
|
const std::vector<Scalar>& B_avg,
|
|
|
|
DeferredLogger& local_deferredLogger) const
|
2023-06-15 07:26:56 -05:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
|
|
|
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
2023-06-15 07:26:56 -05:00
|
|
|
const bool relax_tolerance = iterationIdx > param_.strict_outer_iter_wells_;
|
|
|
|
|
2024-01-12 08:56:00 -06:00
|
|
|
ConvergenceReport report;
|
2023-06-15 07:26:56 -05:00
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
if ((well_domain_.at(well->name()) == domain.index)) {
|
|
|
|
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
|
2024-01-12 08:56:00 -06:00
|
|
|
report += well->getWellConvergence(summary_state,
|
|
|
|
this->wellState(),
|
|
|
|
B_avg,
|
|
|
|
local_deferredLogger,
|
|
|
|
relax_tolerance);
|
2023-06-15 07:26:56 -05:00
|
|
|
} else {
|
2024-01-12 08:56:00 -06:00
|
|
|
ConvergenceReport xreport;
|
2023-06-15 07:26:56 -05:00
|
|
|
using CR = ConvergenceReport;
|
2024-01-12 08:56:00 -06:00
|
|
|
xreport.setWellFailed({CR::WellFailure::Type::Unsolvable, CR::Severity::Normal, -1, well->name()});
|
|
|
|
report += xreport;
|
2023-06-15 07:26:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log debug messages for NaN or too large residuals.
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2023-06-15 07:26:56 -05:00
|
|
|
for (const auto& f : report.wellFailures()) {
|
|
|
|
if (f.severity() == ConvergenceReport::Severity::NotANumber) {
|
2024-01-12 08:56:00 -06:00
|
|
|
local_deferredLogger.debug("NaN residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
2023-06-15 07:26:56 -05:00
|
|
|
} else if (f.severity() == ConvergenceReport::Severity::TooLarge) {
|
2024-01-12 08:56:00 -06:00
|
|
|
local_deferredLogger.debug("Too large residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
2023-06-15 07:26:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return report;
|
|
|
|
}
|
|
|
|
|
2017-02-14 08:06:57 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-14 04:34:03 -06:00
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2018-11-13 07:02:55 -06:00
|
|
|
ConvergenceReport
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2022-10-17 08:22:04 -05:00
|
|
|
getWellConvergence(const std::vector<Scalar>& B_avg, bool checkWellGroupControls) const
|
2017-02-14 04:34:03 -06:00
|
|
|
{
|
2019-02-07 07:43:17 -06:00
|
|
|
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2018-11-13 07:02:55 -06:00
|
|
|
// Get global (from all processes) convergence report.
|
|
|
|
ConvergenceReport local_report;
|
2024-02-06 04:55:07 -06:00
|
|
|
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
2017-06-28 04:15:04 -05:00
|
|
|
for (const auto& well : well_container_) {
|
2022-04-19 07:13:48 -05:00
|
|
|
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
2023-03-30 08:49:27 -05:00
|
|
|
local_report += well->getWellConvergence(
|
|
|
|
summary_state, this->wellState(), B_avg, local_deferredLogger,
|
|
|
|
iterationIdx > param_.strict_outer_iter_wells_);
|
2022-04-05 07:42:27 -05:00
|
|
|
} else {
|
|
|
|
ConvergenceReport report;
|
|
|
|
using CR = ConvergenceReport;
|
|
|
|
report.setWellFailed({CR::WellFailure::Type::Unsolvable, CR::Severity::Normal, -1, well->name()});
|
|
|
|
local_report += report;
|
2018-11-17 16:14:51 -06:00
|
|
|
}
|
2017-08-22 07:49:30 -05:00
|
|
|
}
|
2022-09-09 07:23:20 -05:00
|
|
|
|
2021-05-25 05:57:11 -05:00
|
|
|
const Opm::Parallel::Communication comm = grid().comm();
|
|
|
|
DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
|
|
|
|
ConvergenceReport report = gatherConvergenceReport(local_report, comm);
|
2017-08-22 07:49:30 -05:00
|
|
|
|
2022-10-17 08:22:04 -05:00
|
|
|
// the well_group_control_changed info is already communicated
|
|
|
|
if (checkWellGroupControls) {
|
|
|
|
report.setWellGroupTargetsViolated(this->lastReport().well_group_control_changed);
|
2022-09-19 01:33:57 -05:00
|
|
|
}
|
2022-10-17 08:22:04 -05:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2022-09-19 01:33:57 -05:00
|
|
|
global_deferredLogger.logMessages();
|
2022-09-09 07:23:20 -05:00
|
|
|
}
|
2018-11-13 07:02:55 -06:00
|
|
|
// Log debug messages for NaN or too large residuals.
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2019-02-03 01:13:11 -06:00
|
|
|
for (const auto& f : report.wellFailures()) {
|
|
|
|
if (f.severity() == ConvergenceReport::Severity::NotANumber) {
|
|
|
|
OpmLog::debug("NaN residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
|
|
|
} else if (f.severity() == ConvergenceReport::Severity::TooLarge) {
|
|
|
|
OpmLog::debug("Too large residual found with phase " + std::to_string(f.phase()) + " for well " + f.wellName());
|
|
|
|
}
|
2017-03-24 06:12:06 -05:00
|
|
|
}
|
|
|
|
}
|
2018-11-13 07:02:55 -06:00
|
|
|
return report;
|
2017-02-14 04:34:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-14 04:34:03 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-05 04:22:44 -05:00
|
|
|
calculateExplicitQuantities(DeferredLogger& deferred_logger) const
|
2017-02-14 04:34:03 -06:00
|
|
|
{
|
2021-09-29 09:01:16 -05:00
|
|
|
// TODO: checking isOperableAndSolvable() ?
|
2018-11-17 16:14:51 -06:00
|
|
|
for (auto& well : well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->calculateExplicitQuantities(simulator_, this->wellState(), deferred_logger);
|
2018-11-17 16:14:51 -06:00
|
|
|
}
|
2017-02-14 04:34:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-09-28 09:35:48 -05:00
|
|
|
template<typename TypeTag>
|
2022-10-20 04:03:29 -05:00
|
|
|
std::pair<bool, bool>
|
2022-09-28 09:35:48 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2023-04-25 04:56:46 -05:00
|
|
|
updateWellControls(const bool mandatory_network_balance, DeferredLogger& deferred_logger, const bool relax_network_tolerance)
|
2017-02-14 04:34:03 -06:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const int episodeIdx = simulator_.episodeIndex();
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& network = this->schedule()[episodeIdx].network();
|
|
|
|
if (!this->wellsActive() && !network.active()) {
|
2023-04-20 16:08:36 -05:00
|
|
|
return {false, false};
|
|
|
|
}
|
|
|
|
|
2024-02-06 04:55:07 -06:00
|
|
|
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
|
|
|
const auto& comm = simulator_.vanguard().grid().comm();
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateAndCommunicateGroupData(episodeIdx, iterationIdx);
|
2020-02-10 08:16:09 -06:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
// network related
|
|
|
|
bool more_network_update = false;
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->shouldBalanceNetwork(episodeIdx, iterationIdx) || mandatory_network_balance) {
|
|
|
|
const auto local_network_imbalance = this->updateNetworkPressures(episodeIdx);
|
2024-02-20 15:22:41 -06:00
|
|
|
const Scalar network_imbalance = comm.max(local_network_imbalance);
|
2024-02-17 11:13:46 -06:00
|
|
|
const auto& balance = this->schedule()[episodeIdx].network_balance();
|
2024-02-20 15:22:41 -06:00
|
|
|
constexpr Scalar relaxation_factor = 10.0;
|
|
|
|
const Scalar tolerance = relax_network_tolerance ? relaxation_factor * balance.pressure_tolerance() : balance.pressure_tolerance();
|
2023-09-21 01:31:53 -05:00
|
|
|
more_network_update = this->networkActive() && network_imbalance > tolerance;
|
2022-10-20 04:03:29 -05:00
|
|
|
}
|
2020-05-15 04:21:32 -05:00
|
|
|
|
2022-10-04 01:45:21 -05:00
|
|
|
bool changed_well_group = false;
|
2022-09-09 07:23:20 -05:00
|
|
|
// Check group individual constraints.
|
2024-02-17 11:13:46 -06:00
|
|
|
const int nupcol = this->schedule()[episodeIdx].nupcol();
|
2022-10-04 01:45:21 -05:00
|
|
|
// don't switch group control when iterationIdx > nupcol
|
|
|
|
// to avoid oscilations between group controls
|
|
|
|
if (iterationIdx <= nupcol) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const Group& fieldGroup = this->schedule().getGroup("FIELD", episodeIdx);
|
2022-10-04 01:45:21 -05:00
|
|
|
changed_well_group = updateGroupControls(fieldGroup, deferred_logger, episodeIdx, iterationIdx);
|
2022-09-09 07:23:20 -05:00
|
|
|
}
|
|
|
|
// Check wells' group constraints and communicate.
|
2022-10-04 01:45:21 -05:00
|
|
|
bool changed_well_to_group = false;
|
2023-11-25 12:32:44 -06:00
|
|
|
{
|
|
|
|
// For MS Wells a linear solve is performed below and the matrix might be singular.
|
|
|
|
// We need to communicate the exception thrown to the others and rethrow.
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
const auto mode = WellInterface<TypeTag>::IndividualOrGroup::Group;
|
2024-02-06 04:55:07 -06:00
|
|
|
const bool changed_well = well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger);
|
2023-11-25 12:32:44 -06:00
|
|
|
if (changed_well) {
|
|
|
|
changed_well_to_group = changed_well || changed_well_to_group;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel: updating well controls failed: ",
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.gridView().comm());
|
2022-09-09 07:23:20 -05:00
|
|
|
}
|
2022-10-04 01:45:21 -05:00
|
|
|
|
2023-11-22 09:50:57 -06:00
|
|
|
changed_well_to_group = comm.sum(static_cast<int>(changed_well_to_group));
|
2022-10-04 01:45:21 -05:00
|
|
|
if (changed_well_to_group) {
|
2022-09-09 07:23:20 -05:00
|
|
|
updateAndCommunicate(episodeIdx, iterationIdx, deferred_logger);
|
2022-10-04 01:45:21 -05:00
|
|
|
changed_well_group = true;
|
2019-08-07 07:13:11 -05:00
|
|
|
}
|
|
|
|
|
2020-02-10 08:16:09 -06:00
|
|
|
// Check individual well constraints and communicate.
|
2021-08-27 00:57:16 -05:00
|
|
|
bool changed_well_individual = false;
|
2023-11-25 12:32:44 -06:00
|
|
|
{
|
|
|
|
// For MS Wells a linear solve is performed below and the matrix might be singular.
|
|
|
|
// We need to communicate the exception thrown to the others and rethrow.
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
const auto mode = WellInterface<TypeTag>::IndividualOrGroup::Individual;
|
2024-02-06 04:55:07 -06:00
|
|
|
const bool changed_well = well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger);
|
2023-11-25 12:32:44 -06:00
|
|
|
if (changed_well) {
|
|
|
|
changed_well_individual = changed_well || changed_well_individual;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel: updating well controls failed: ",
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.gridView().comm());
|
2019-01-18 07:04:30 -06:00
|
|
|
}
|
2023-11-25 11:22:28 -06:00
|
|
|
|
2023-11-22 09:50:57 -06:00
|
|
|
changed_well_individual = comm.sum(static_cast<int>(changed_well_individual));
|
2022-09-09 07:23:20 -05:00
|
|
|
if (changed_well_individual) {
|
2021-08-27 00:57:16 -05:00
|
|
|
updateAndCommunicate(episodeIdx, iterationIdx, deferred_logger);
|
2022-10-04 01:45:21 -05:00
|
|
|
changed_well_group = true;
|
2022-09-09 07:23:20 -05:00
|
|
|
}
|
|
|
|
|
2021-09-24 05:09:02 -05:00
|
|
|
// update wsolvent fraction for REIN wells
|
2024-02-17 11:13:46 -06:00
|
|
|
const Group& fieldGroup = this->schedule().getGroup("FIELD", episodeIdx);
|
|
|
|
this->updateWsolvent(fieldGroup, episodeIdx, this->nupcolWellState());
|
2022-04-05 06:35:02 -05:00
|
|
|
|
2022-10-20 04:03:29 -05:00
|
|
|
return { changed_well_group, more_network_update };
|
2017-02-14 04:34:03 -06:00
|
|
|
}
|
|
|
|
|
2023-06-15 07:26:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
updateWellControlsDomain(DeferredLogger& deferred_logger, const Domain& domain)
|
|
|
|
{
|
2024-02-17 11:13:46 -06:00
|
|
|
if ( !this->wellsActive() ) return ;
|
2023-06-15 07:26:56 -05:00
|
|
|
|
|
|
|
// TODO: decide on and implement an approach to handling of
|
|
|
|
// group controls, network and similar for domain solves.
|
|
|
|
|
|
|
|
// Check only individual well constraints and communicate.
|
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
const auto mode = WellInterface<TypeTag>::IndividualOrGroup::Individual;
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateWellControl(simulator_, mode, this->wellState(), this->groupState(), deferred_logger);
|
2023-06-15 07:26:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-14 04:34:03 -06:00
|
|
|
|
2023-06-06 14:31:17 -05:00
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
initializeWBPCalculationService()
|
|
|
|
{
|
|
|
|
this->wbpCalcMap_.clear();
|
|
|
|
this->wbpCalcMap_.resize(this->wells_ecl_.size());
|
|
|
|
|
|
|
|
this->registerOpenWellsForWBPCalculation();
|
|
|
|
|
|
|
|
auto wellID = std::size_t{0};
|
|
|
|
for (const auto& well : this->wells_ecl_) {
|
|
|
|
this->wbpCalcMap_[wellID].wbpCalcIdx_ = this->wbpCalculationService_
|
|
|
|
.createCalculator(well,
|
|
|
|
this->local_parallel_well_info_[wellID],
|
|
|
|
this->conn_idx_map_[wellID].local(),
|
|
|
|
this->makeWellSourceEvaluatorFactory(wellID));
|
|
|
|
|
|
|
|
++wellID;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->wbpCalculationService_.defineCommunication();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
data::WellBlockAveragePressures
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
computeWellBlockAveragePressures() const
|
|
|
|
{
|
|
|
|
auto wbpResult = data::WellBlockAveragePressures{};
|
|
|
|
|
2024-04-11 03:21:02 -05:00
|
|
|
using Calculated = typename PAvgCalculator<Scalar>::Result::WBPMode;
|
2023-06-06 14:31:17 -05:00
|
|
|
using Output = data::WellBlockAvgPress::Quantity;
|
|
|
|
|
|
|
|
this->wbpCalculationService_.collectDynamicValues();
|
|
|
|
|
|
|
|
const auto numWells = this->wells_ecl_.size();
|
|
|
|
for (auto wellID = 0*numWells; wellID < numWells; ++wellID) {
|
|
|
|
const auto calcIdx = this->wbpCalcMap_[wellID].wbpCalcIdx_;
|
|
|
|
const auto& well = this->wells_ecl_[wellID];
|
|
|
|
|
|
|
|
if (! well.hasRefDepth()) {
|
|
|
|
// Can't perform depth correction without at least a
|
|
|
|
// fall-back datum depth.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->wbpCalculationService_
|
|
|
|
.inferBlockAveragePressures(calcIdx, well.pavg(),
|
|
|
|
this->gravity_,
|
|
|
|
well.getWPaveRefDepth());
|
|
|
|
|
|
|
|
const auto& result = this->wbpCalculationService_
|
|
|
|
.averagePressures(calcIdx);
|
|
|
|
|
|
|
|
auto& reported = wbpResult.values[well.name()];
|
|
|
|
|
|
|
|
reported[Output::WBP] = result.value(Calculated::WBP);
|
|
|
|
reported[Output::WBP4] = result.value(Calculated::WBP4);
|
|
|
|
reported[Output::WBP5] = result.value(Calculated::WBP5);
|
|
|
|
reported[Output::WBP9] = result.value(Calculated::WBP9);
|
|
|
|
}
|
|
|
|
|
|
|
|
return wbpResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
2024-02-20 13:27:55 -06:00
|
|
|
typename ParallelWBPCalculation<typename BlackoilWellModel<TypeTag>::Scalar>::EvaluatorFactory
|
2023-06-06 14:31:17 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
makeWellSourceEvaluatorFactory(const std::vector<Well>::size_type wellIdx) const
|
|
|
|
{
|
2024-04-11 03:21:02 -05:00
|
|
|
using Span = typename PAvgDynamicSourceData<Scalar>::template SourceDataSpan<Scalar>;
|
2023-06-06 14:31:17 -05:00
|
|
|
using Item = typename Span::Item;
|
|
|
|
|
2024-02-20 13:27:55 -06:00
|
|
|
return [wellIdx, this]() -> typename ParallelWBPCalculation<Scalar>::Evaluator
|
2023-06-06 14:31:17 -05:00
|
|
|
{
|
|
|
|
if (! this->wbpCalcMap_[wellIdx].openWellIdx_.has_value()) {
|
|
|
|
// Well is stopped/shut. Return evaluator for stopped wells.
|
|
|
|
return []([[maybe_unused]] const int connIdx, Span sourceTerm)
|
|
|
|
{
|
|
|
|
// Well/connection is stopped/shut. Set all items to
|
|
|
|
// zero.
|
|
|
|
|
|
|
|
sourceTerm
|
|
|
|
.set(Item::Pressure , 0.0)
|
|
|
|
.set(Item::PoreVol , 0.0)
|
|
|
|
.set(Item::MixtureDensity, 0.0);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Well is open. Return an evaluator for open wells/open connections.
|
|
|
|
return [this, wellPtr = this->well_container_[*this->wbpCalcMap_[wellIdx].openWellIdx_].get()]
|
|
|
|
(const int connIdx, Span sourceTerm)
|
|
|
|
{
|
|
|
|
// Note: The only item which actually matters for the WBP
|
|
|
|
// calculation at the well reservoir connection level is the
|
|
|
|
// mixture density. Set other items to zero.
|
|
|
|
|
|
|
|
const auto& connIdxMap =
|
|
|
|
this->conn_idx_map_[wellPtr->indexOfWell()];
|
|
|
|
|
|
|
|
const auto rho = wellPtr->
|
|
|
|
connectionDensity(connIdxMap.global(connIdx),
|
|
|
|
connIdxMap.open(connIdx));
|
|
|
|
|
|
|
|
sourceTerm
|
|
|
|
.set(Item::Pressure , 0.0)
|
|
|
|
.set(Item::PoreVol , 0.0)
|
|
|
|
.set(Item::MixtureDensity, rho);
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
registerOpenWellsForWBPCalculation()
|
|
|
|
{
|
|
|
|
assert (this->wbpCalcMap_.size() == this->wells_ecl_.size());
|
|
|
|
|
|
|
|
for (auto& wbpCalc : this->wbpCalcMap_) {
|
|
|
|
wbpCalc.openWellIdx_.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto openWellIdx = typename std::vector<WellInterfacePtr>::size_type{0};
|
|
|
|
for (const auto* openWell : this->well_container_generic_) {
|
|
|
|
this->wbpCalcMap_[openWell->indexOfWell()].openWellIdx_ = openWellIdx++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-08-25 07:06:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
updateAndCommunicate(const int reportStepIdx,
|
|
|
|
const int iterationIdx,
|
|
|
|
DeferredLogger& deferred_logger)
|
|
|
|
{
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateAndCommunicateGroupData(reportStepIdx, iterationIdx);
|
2023-12-04 09:49:39 -06:00
|
|
|
|
|
|
|
// updateWellStateWithTarget might throw for multisegment wells hence we
|
|
|
|
// have a parallel try catch here to thrown on all processes.
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH()
|
2021-08-25 07:06:56 -05:00
|
|
|
// if a well or group change control it affects all wells that are under the same group
|
|
|
|
for (const auto& well : well_container_) {
|
2024-02-17 11:13:46 -06:00
|
|
|
well->updateWellStateWithTarget(simulator_, this->groupState(),
|
|
|
|
this->wellState(), deferred_logger);
|
2021-08-25 07:06:56 -05:00
|
|
|
}
|
2023-12-04 09:49:39 -06:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAndCommunicate failed: ",
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.gridView().comm())
|
2024-02-17 11:13:46 -06:00
|
|
|
this->updateAndCommunicateGroupData(reportStepIdx, iterationIdx);
|
2021-08-25 07:06:56 -05:00
|
|
|
}
|
2017-02-14 06:39:53 -06:00
|
|
|
|
2022-09-29 02:22:56 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
bool
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
updateGroupControls(const Group& group,
|
|
|
|
DeferredLogger& deferred_logger,
|
|
|
|
const int reportStepIdx,
|
|
|
|
const int iterationIdx)
|
|
|
|
{
|
|
|
|
bool changed = false;
|
2024-02-17 11:13:46 -06:00
|
|
|
bool changed_hc = this->checkGroupHigherConstraints( group, deferred_logger, reportStepIdx);
|
2022-09-29 02:22:56 -05:00
|
|
|
if (changed_hc) {
|
|
|
|
changed = true;
|
|
|
|
updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger);
|
|
|
|
}
|
2024-02-07 08:11:59 -06:00
|
|
|
|
2022-10-24 02:36:05 -05:00
|
|
|
bool changed_individual =
|
2024-02-17 11:13:46 -06:00
|
|
|
BlackoilWellModelConstraints(*this).
|
2022-10-24 02:36:05 -05:00
|
|
|
updateGroupIndividualControl(group,
|
|
|
|
reportStepIdx,
|
|
|
|
this->switched_inj_groups_,
|
|
|
|
this->switched_prod_groups_,
|
2024-02-07 08:11:59 -06:00
|
|
|
this->closed_offending_wells_,
|
2022-10-24 02:36:05 -05:00
|
|
|
this->groupState(),
|
|
|
|
this->wellState(),
|
|
|
|
deferred_logger);
|
2024-02-07 08:11:59 -06:00
|
|
|
|
2022-09-29 02:22:56 -05:00
|
|
|
if (changed_individual) {
|
|
|
|
changed = true;
|
|
|
|
updateAndCommunicate(reportStepIdx, iterationIdx, deferred_logger);
|
|
|
|
}
|
|
|
|
// call recursively down the group hierarchy
|
|
|
|
for (const std::string& groupName : group.groups()) {
|
2024-02-17 11:13:46 -06:00
|
|
|
bool changed_this = updateGroupControls( this->schedule().getGroup(groupName, reportStepIdx), deferred_logger, reportStepIdx,iterationIdx);
|
2022-09-29 02:22:56 -05:00
|
|
|
changed = changed || changed_this;
|
|
|
|
}
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-14 06:39:53 -06:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2018-06-28 06:47:10 -05:00
|
|
|
updateWellTestState(const double& simulationTime, WellTestState& wellTestState) const
|
2017-02-14 06:39:53 -06:00
|
|
|
{
|
2021-05-05 04:22:44 -05:00
|
|
|
DeferredLogger local_deferredLogger;
|
2017-07-26 04:01:26 -05:00
|
|
|
for (const auto& well : well_container_) {
|
2021-09-20 04:16:32 -05:00
|
|
|
const auto& wname = well->name();
|
2021-10-07 08:18:53 -05:00
|
|
|
const auto wasClosed = wellTestState.well_is_closed(wname);
|
2024-02-06 04:55:07 -06:00
|
|
|
well->checkWellOperability(simulator_, this->wellState(), local_deferredLogger);
|
2021-09-20 04:16:32 -05:00
|
|
|
well->updateWellTestState(this->wellState().well(wname), simulationTime, /*writeMessageToOPMLog=*/ true, wellTestState, local_deferredLogger);
|
2021-03-01 08:43:43 -06:00
|
|
|
|
2021-10-07 08:18:53 -05:00
|
|
|
if (!wasClosed && wellTestState.well_is_closed(wname)) {
|
2021-09-20 04:16:32 -05:00
|
|
|
this->closed_this_step_.insert(wname);
|
2021-03-01 08:43:43 -06:00
|
|
|
}
|
2019-02-03 01:13:11 -06:00
|
|
|
}
|
2023-10-09 04:14:48 -05:00
|
|
|
|
2021-05-25 05:57:11 -05:00
|
|
|
const Opm::Parallel::Communication comm = grid().comm();
|
|
|
|
DeferredLogger global_deferredLogger = gatherDeferredLogger(local_deferredLogger, comm);
|
2024-02-07 08:11:59 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
for (const auto& [group_name, to] : this->closed_offending_wells_) {
|
2024-02-07 08:11:59 -06:00
|
|
|
if (!this->wasDynamicallyShutThisTimeStep(to.second)) {
|
2024-02-17 11:13:46 -06:00
|
|
|
wellTestState.close_well(to.second, WellTestConfig::Reason::GROUP, simulationTime);
|
2024-02-07 08:11:59 -06:00
|
|
|
this->updateClosedWellsThisStep(to.second);
|
2024-02-17 11:13:46 -06:00
|
|
|
const std::string msg =
|
|
|
|
fmt::format("Procedure on exceeding {} limit is WELL for group {}. Well {} is {}.",
|
|
|
|
to.first,
|
|
|
|
group_name,
|
|
|
|
to.second,
|
|
|
|
"shut");
|
2024-02-07 08:11:59 -06:00
|
|
|
global_deferredLogger.info(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2019-02-03 01:13:11 -06:00
|
|
|
global_deferredLogger.logMessages();
|
2017-07-26 04:01:26 -05:00
|
|
|
}
|
2017-02-14 06:39:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-02-14 06:39:53 -06:00
|
|
|
void
|
2021-06-07 07:09:36 -05:00
|
|
|
BlackoilWellModel<TypeTag>::computePotentials(const std::size_t widx,
|
2024-02-17 11:13:46 -06:00
|
|
|
const WellState<Scalar>& well_state_copy,
|
2021-06-07 07:09:36 -05:00
|
|
|
std::string& exc_msg,
|
|
|
|
ExceptionType::ExcEnum& exc_type,
|
|
|
|
DeferredLogger& deferred_logger)
|
2017-02-14 06:39:53 -06:00
|
|
|
{
|
2024-02-17 11:13:46 -06:00
|
|
|
const int np = this->numPhases();
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> potentials;
|
2024-02-07 06:48:18 -06:00
|
|
|
const auto& well = well_container_[widx];
|
|
|
|
std::string cur_exc_msg;
|
|
|
|
auto cur_exc_type = ExceptionType::NONE;
|
2021-06-07 07:09:36 -05:00
|
|
|
try {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->computeWellPotentials(simulator_, well_state_copy, potentials, deferred_logger);
|
2021-06-07 07:09:36 -05:00
|
|
|
}
|
2021-09-20 03:56:11 -05:00
|
|
|
// catch all possible exception and store type and message.
|
2024-02-07 06:48:18 -06:00
|
|
|
OPM_PARALLEL_CATCH_CLAUSE(cur_exc_type, cur_exc_msg);
|
|
|
|
if (cur_exc_type != ExceptionType::NONE) {
|
|
|
|
exc_msg += fmt::format("\nFor well {}: {}", well->name(), cur_exc_msg);
|
|
|
|
}
|
|
|
|
exc_type = std::max(exc_type, cur_exc_type);
|
2021-06-07 07:09:36 -05:00
|
|
|
// Store it in the well state
|
|
|
|
// potentials is resized and set to zero in the beginning of well->ComputeWellPotentials
|
|
|
|
// and updated only if sucessfull. i.e. the potentials are zero for exceptions
|
2021-08-05 03:57:15 -05:00
|
|
|
auto& ws = this->wellState().well(well->indexOfWell());
|
2021-06-07 07:09:36 -05:00
|
|
|
for (int p = 0; p < np; ++p) {
|
2021-11-18 05:57:16 -06:00
|
|
|
// make sure the potentials are positive
|
2024-04-12 10:06:27 -05:00
|
|
|
ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
|
2019-02-07 07:43:17 -06:00
|
|
|
}
|
2017-02-14 08:06:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-01 18:11:19 -06:00
|
|
|
template <typename TypeTag>
|
2020-10-09 06:38:33 -05:00
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
calculateProductivityIndexValues(DeferredLogger& deferred_logger)
|
|
|
|
{
|
2021-03-01 18:11:19 -06:00
|
|
|
for (const auto& wellPtr : this->well_container_) {
|
|
|
|
this->calculateProductivityIndexValues(wellPtr.get(), deferred_logger);
|
2020-10-09 06:38:33 -05:00
|
|
|
}
|
2021-03-01 18:11:19 -06:00
|
|
|
}
|
2020-10-09 06:38:33 -05:00
|
|
|
|
2021-03-01 18:11:19 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
calculateProductivityIndexValuesShutWells(const int reportStepIdx,
|
|
|
|
DeferredLogger& deferred_logger)
|
|
|
|
{
|
|
|
|
// For the purpose of computing PI/II values, it is sufficient to
|
|
|
|
// construct StandardWell instances only. We don't need to form
|
|
|
|
// well objects that honour the 'isMultisegment()' flag of the
|
|
|
|
// corresponding "this->wells_ecl_[shutWell]".
|
|
|
|
|
|
|
|
for (const auto& shutWell : this->local_shut_wells_) {
|
2023-01-12 05:20:09 -06:00
|
|
|
if (!this->wells_ecl_[shutWell].hasConnections()) {
|
2021-06-02 04:02:46 -05:00
|
|
|
// No connections in this well. Nothing to do.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-03-01 18:11:19 -06:00
|
|
|
auto wellPtr = this->template createTypedWellPointer
|
|
|
|
<StandardWell<TypeTag>>(shutWell, reportStepIdx);
|
|
|
|
|
|
|
|
wellPtr->init(&this->phase_usage_, this->depth_, this->gravity_,
|
2022-04-12 01:44:52 -05:00
|
|
|
this->local_num_cells_, this->B_avg_, true);
|
2021-03-01 18:11:19 -06:00
|
|
|
|
|
|
|
this->calculateProductivityIndexValues(wellPtr.get(), deferred_logger);
|
2020-10-09 06:38:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-03-01 18:11:19 -06:00
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
calculateProductivityIndexValues(const WellInterface<TypeTag>* wellPtr,
|
|
|
|
DeferredLogger& deferred_logger)
|
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
wellPtr->updateProductivityIndex(this->simulator_,
|
2021-03-01 18:11:19 -06:00
|
|
|
this->prod_index_calc_[wellPtr->indexOfWell()],
|
|
|
|
this->wellState(),
|
|
|
|
deferred_logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-05-03 06:34:15 -05:00
|
|
|
template<typename TypeTag>
|
2017-03-16 10:39:05 -05:00
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-05 04:22:44 -05:00
|
|
|
prepareTimeStep(DeferredLogger& deferred_logger)
|
2017-03-16 10:39:05 -05:00
|
|
|
{
|
2023-09-22 13:10:52 -05:00
|
|
|
// Check if there is a network with active prediction wells at this time step.
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto episodeIdx = simulator_.episodeIndex();
|
2023-09-22 13:10:52 -05:00
|
|
|
this->updateNetworkActiveState(episodeIdx);
|
|
|
|
|
|
|
|
// Rebalance the network initially if any wells in the network have status changes
|
|
|
|
// (Need to check this before clearing events)
|
2023-10-06 08:14:59 -05:00
|
|
|
const bool do_prestep_network_rebalance = this->needPreStepNetworkRebalance(episodeIdx);
|
2023-04-20 17:04:28 -05:00
|
|
|
|
2021-09-23 08:22:48 -05:00
|
|
|
for (const auto& well : well_container_) {
|
2021-09-24 02:46:22 -05:00
|
|
|
auto& events = this->wellState().well(well->indexOfWell()).events;
|
2024-02-17 11:13:46 -06:00
|
|
|
if (events.hasEvent(WellState<Scalar>::event_mask)) {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->updateWellStateWithTarget(simulator_, this->groupState(), this->wellState(), deferred_logger);
|
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
2023-03-22 09:10:00 -05:00
|
|
|
well->updatePrimaryVariables(summary_state, this->wellState(), deferred_logger);
|
2022-04-27 06:36:52 -05:00
|
|
|
well->initPrimaryVariablesEvaluation();
|
2021-09-24 02:46:22 -05:00
|
|
|
// There is no new well control change input within a report step,
|
|
|
|
// so next time step, the well does not consider to have effective events anymore.
|
2024-02-17 11:13:46 -06:00
|
|
|
events.clearEvent(WellState<Scalar>::event_mask);
|
2021-09-23 08:22:48 -05:00
|
|
|
}
|
2023-10-11 06:15:57 -05:00
|
|
|
// these events only work for the first time step within the report step
|
|
|
|
if (events.hasEvent(ScheduleEvents::REQUEST_OPEN_WELL)) {
|
|
|
|
events.clearEvent(ScheduleEvents::REQUEST_OPEN_WELL);
|
|
|
|
}
|
2021-09-24 02:46:22 -05:00
|
|
|
// solve the well equation initially to improve the initial solution of the well model
|
2021-11-18 05:57:16 -06:00
|
|
|
if (param_.solve_welleq_initially_ && well->isOperableAndSolvable()) {
|
|
|
|
try {
|
2024-02-06 04:55:07 -06:00
|
|
|
well->solveWellEquation(simulator_, this->wellState(), this->groupState(), deferred_logger);
|
2021-11-18 05:57:16 -06:00
|
|
|
} catch (const std::exception& e) {
|
2023-09-22 13:10:52 -05:00
|
|
|
const std::string msg = "Compute initial well solution for " + well->name() + " initially failed. Continue with the previous rates";
|
2021-11-18 05:57:16 -06:00
|
|
|
deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
|
|
|
|
}
|
2021-09-24 02:46:22 -05:00
|
|
|
}
|
2023-08-16 05:50:06 -05:00
|
|
|
// If we're using local well solves that include control switches, they also update
|
|
|
|
// operability, so reset before main iterations begin
|
|
|
|
well->resetWellOperability();
|
2021-09-24 02:46:22 -05:00
|
|
|
}
|
2021-09-23 08:22:48 -05:00
|
|
|
updatePrimaryVariables(deferred_logger);
|
2023-04-20 17:04:28 -05:00
|
|
|
|
2023-09-22 13:10:52 -05:00
|
|
|
// Actually do the pre-step network rebalance, using the updated well states and initial solutions
|
|
|
|
if (do_prestep_network_rebalance) doPreStepNetworkRebalance(deferred_logger);
|
2017-09-04 06:54:41 -05:00
|
|
|
}
|
|
|
|
|
2017-06-07 02:29:31 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-03-18 08:49:52 -05:00
|
|
|
updateAverageFormationFactor()
|
2017-06-23 05:24:50 -05:00
|
|
|
{
|
2021-03-18 08:49:52 -05:00
|
|
|
std::vector< Scalar > B_avg(numComponents(), Scalar() );
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& grid = simulator_.vanguard().grid();
|
2017-06-23 05:24:50 -05:00
|
|
|
const auto& gridView = grid.leafGridView();
|
2024-02-06 04:55:07 -06:00
|
|
|
ElementContext elemCtx(simulator_);
|
2017-06-23 05:24:50 -05:00
|
|
|
|
2022-10-12 07:27:20 -05:00
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
|
|
|
for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
|
|
|
|
elemCtx.updatePrimaryStencil(elem);
|
2017-06-23 05:24:50 -05:00
|
|
|
elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
|
|
|
|
|
|
|
|
const auto& intQuants = elemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
|
|
|
|
const auto& fs = intQuants.fluidState();
|
|
|
|
|
2017-12-04 02:14:08 -06:00
|
|
|
for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
|
2017-06-23 05:24:50 -05:00
|
|
|
{
|
2017-12-04 02:14:08 -06:00
|
|
|
if (!FluidSystem::phaseIsActive(phaseIdx)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const unsigned compIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
|
|
|
|
auto& B = B_avg[ compIdx ];
|
2017-06-23 05:24:50 -05:00
|
|
|
|
2017-12-04 02:14:08 -06:00
|
|
|
B += 1 / fs.invB(phaseIdx).value();
|
2017-06-23 05:24:50 -05:00
|
|
|
}
|
2021-01-28 07:33:21 -06:00
|
|
|
if constexpr (has_solvent_) {
|
2017-06-27 08:16:22 -05:00
|
|
|
auto& B = B_avg[solventSaturationIdx];
|
2017-06-23 05:24:50 -05:00
|
|
|
B += 1 / intQuants.solventInverseFormationVolumeFactor().value();
|
|
|
|
}
|
|
|
|
}
|
2021-05-25 05:57:11 -05:00
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAverageFormationFactor() failed: ", grid.comm())
|
2017-06-23 05:24:50 -05:00
|
|
|
|
|
|
|
// compute global average
|
|
|
|
grid.comm().sum(B_avg.data(), B_avg.size());
|
2022-10-12 07:27:20 -05:00
|
|
|
for (auto& bval : B_avg)
|
2017-06-23 05:24:50 -05:00
|
|
|
{
|
2022-10-12 07:27:20 -05:00
|
|
|
bval /= global_num_cells_;
|
2017-06-23 05:24:50 -05:00
|
|
|
}
|
2021-03-18 08:49:52 -05:00
|
|
|
B_avg_ = B_avg;
|
2017-06-23 05:24:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-08 03:44:10 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
2017-09-26 03:52:05 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-05 04:22:44 -05:00
|
|
|
updatePrimaryVariables(DeferredLogger& deferred_logger)
|
2017-08-08 03:44:10 -05:00
|
|
|
{
|
|
|
|
for (const auto& well : well_container_) {
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& summary_state = simulator_.vanguard().summaryState();
|
2023-03-22 09:10:00 -05:00
|
|
|
well->updatePrimaryVariables(summary_state, this->wellState(), deferred_logger);
|
2017-11-08 06:57:36 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 08:48:30 -06:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::extractLegacyCellPvtRegionIndex_()
|
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& grid = simulator_.vanguard().grid();
|
|
|
|
const auto& eclProblem = simulator_.problem();
|
2017-11-08 08:48:30 -06:00
|
|
|
const unsigned numCells = grid.size(/*codim=*/0);
|
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
this->pvt_region_idx_.resize(numCells);
|
2017-11-08 08:48:30 -06:00
|
|
|
for (unsigned cellIdx = 0; cellIdx < numCells; ++cellIdx) {
|
2024-02-17 11:13:46 -06:00
|
|
|
this->pvt_region_idx_[cellIdx] =
|
2017-11-08 08:48:30 -06:00
|
|
|
eclProblem.pvtRegionIndex(cellIdx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The number of components in the model.
|
|
|
|
template<typename TypeTag>
|
|
|
|
int
|
|
|
|
BlackoilWellModel<TypeTag>::numComponents() const
|
|
|
|
{
|
2022-09-13 07:00:43 -05:00
|
|
|
// The numComponents here does not reflect the actual number of the components in the system.
|
|
|
|
// It more or less reflects the number of mass conservation equations for the well equations.
|
|
|
|
// For example, in the current formulation, we do not have the polymer conservation equation
|
|
|
|
// in the well equations. As a result, for an oil-water-polymer system, this function will return 2.
|
|
|
|
// In some way, it makes this function appear to be confusing from its name, and we need
|
|
|
|
// to revisit/revise this function again when extending the variants of system that flow can simulate.
|
2024-02-17 11:13:46 -06:00
|
|
|
int numComp = this->numPhases() < 3 ? this->numPhases() : FluidSystem::numComponents;
|
2021-01-28 07:33:21 -06:00
|
|
|
if constexpr (has_solvent_) {
|
2023-03-29 01:21:54 -05:00
|
|
|
numComp++;
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
|
|
|
return numComp;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::extractLegacyDepth_()
|
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& eclProblem = simulator_.problem();
|
2021-12-01 07:00:21 -06:00
|
|
|
depth_.resize(local_num_cells_);
|
|
|
|
for (unsigned cellIdx = 0; cellIdx < local_num_cells_; ++cellIdx) {
|
|
|
|
depth_[cellIdx] = eclProblem.dofCenterDepth(cellIdx);
|
2017-11-08 08:48:30 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-23 08:15:55 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
typename BlackoilWellModel<TypeTag>::WellInterfacePtr
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
getWell(const std::string& well_name) const
|
|
|
|
{
|
|
|
|
// finding the iterator of the well in wells_ecl
|
|
|
|
auto well = std::find_if(well_container_.begin(),
|
|
|
|
well_container_.end(),
|
|
|
|
[&well_name](const WellInterfacePtr& elem)->bool {
|
|
|
|
return elem->name() == well_name;
|
|
|
|
});
|
|
|
|
|
|
|
|
assert(well != well_container_.end());
|
|
|
|
|
|
|
|
return *well;
|
|
|
|
}
|
|
|
|
|
2021-08-19 05:15:35 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
bool
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
hasWell(const std::string& well_name) const
|
|
|
|
{
|
|
|
|
return std::any_of(well_container_.begin(), well_container_.end(),
|
|
|
|
[&well_name](const WellInterfacePtr& elem) -> bool
|
|
|
|
{
|
|
|
|
return elem->name() == well_name;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2019-09-23 08:15:55 -05:00
|
|
|
|
2020-02-10 08:16:09 -06:00
|
|
|
|
|
|
|
|
2021-05-19 07:51:14 -05:00
|
|
|
template <typename TypeTag>
|
|
|
|
int
|
2020-02-10 08:16:09 -06:00
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-19 07:51:14 -05:00
|
|
|
reportStepIndex() const
|
2020-02-10 08:16:09 -06:00
|
|
|
{
|
2024-02-06 04:55:07 -06:00
|
|
|
return std::max(this->simulator_.episodeIndex(), 0);
|
2020-03-24 03:24:45 -05:00
|
|
|
}
|
2020-04-08 03:41:20 -05:00
|
|
|
|
|
|
|
|
2019-08-07 07:13:11 -05:00
|
|
|
|
2020-03-24 03:24:45 -05:00
|
|
|
|
2019-08-07 07:13:11 -05:00
|
|
|
|
2020-04-24 08:25:38 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2021-05-19 07:51:14 -05:00
|
|
|
calcRates(const int fipnum,
|
|
|
|
const int pvtreg,
|
2024-02-20 15:22:41 -06:00
|
|
|
const std::vector<Scalar>& production_rates,
|
|
|
|
std::vector<Scalar>& resv_coeff)
|
2020-04-24 08:25:38 -05:00
|
|
|
{
|
2023-03-24 08:59:52 -05:00
|
|
|
rateConverter_->calcCoeff(fipnum, pvtreg, production_rates, resv_coeff);
|
2020-03-24 03:24:45 -05:00
|
|
|
}
|
2020-05-04 08:56:34 -05:00
|
|
|
|
2021-06-08 08:29:15 -05:00
|
|
|
template<typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
calcInjRates(const int fipnum,
|
2024-02-20 15:22:41 -06:00
|
|
|
const int pvtreg,
|
|
|
|
std::vector<Scalar>& resv_coeff)
|
2021-06-08 08:29:15 -05:00
|
|
|
{
|
|
|
|
rateConverter_->calcInjCoeff(fipnum, pvtreg, resv_coeff);
|
|
|
|
}
|
|
|
|
|
2021-03-08 08:11:50 -06:00
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
computeWellTemperature()
|
|
|
|
{
|
|
|
|
if (!has_energy_)
|
|
|
|
return;
|
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
int np = this->numPhases();
|
2024-02-20 15:22:41 -06:00
|
|
|
Scalar cellInternalEnergy;
|
|
|
|
Scalar cellBinv;
|
|
|
|
Scalar cellDensity;
|
|
|
|
Scalar perfPhaseRate;
|
2024-02-17 11:13:46 -06:00
|
|
|
const int nw = this->numLocalWells();
|
2021-03-08 08:11:50 -06:00
|
|
|
for (auto wellID = 0*nw; wellID < nw; ++wellID) {
|
2024-02-17 11:13:46 -06:00
|
|
|
const Well& well = this->wells_ecl_[wellID];
|
2021-03-08 08:11:50 -06:00
|
|
|
if (well.isInjector())
|
|
|
|
continue;
|
|
|
|
|
2024-02-20 15:22:41 -06:00
|
|
|
std::array<Scalar,2> weighted{0.0,0.0};
|
2022-08-29 06:41:32 -05:00
|
|
|
auto& [weighted_temperature, total_weight] = weighted;
|
2021-03-08 08:11:50 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
auto& well_info = this->local_parallel_well_info_[wellID].get();
|
2021-08-06 06:14:00 -05:00
|
|
|
auto& ws = this->wellState().well(wellID);
|
|
|
|
auto& perf_data = ws.perf_data;
|
2021-06-06 10:09:44 -05:00
|
|
|
auto& perf_phase_rate = perf_data.phase_rates;
|
2021-03-08 08:11:50 -06:00
|
|
|
|
2024-02-17 11:13:46 -06:00
|
|
|
using int_type = decltype(this->well_perf_data_[wellID].size());
|
|
|
|
for (int_type perf = 0, end_perf = this->well_perf_data_[wellID].size(); perf < end_perf; ++perf) {
|
|
|
|
const int cell_idx = this->well_perf_data_[wellID][perf].cell_index;
|
2024-02-06 04:55:07 -06:00
|
|
|
const auto& intQuants = simulator_.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
|
2021-03-08 08:11:50 -06:00
|
|
|
const auto& fs = intQuants.fluidState();
|
|
|
|
|
2021-12-06 03:26:54 -06:00
|
|
|
// we on only have one temperature pr cell any phaseIdx will do
|
2024-02-20 15:22:41 -06:00
|
|
|
Scalar cellTemperatures = fs.temperature(/*phaseIdx*/0).value();
|
2021-12-06 03:26:54 -06:00
|
|
|
|
2024-02-20 15:22:41 -06:00
|
|
|
Scalar weight_factor = 0.0;
|
2021-12-06 03:26:54 -06:00
|
|
|
for (unsigned phaseIdx = 0; phaseIdx < FluidSystem::numPhases; ++phaseIdx)
|
|
|
|
{
|
|
|
|
if (!FluidSystem::phaseIsActive(phaseIdx)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-03-08 08:11:50 -06:00
|
|
|
cellInternalEnergy = fs.enthalpy(phaseIdx).value() - fs.pressure(phaseIdx).value() / fs.density(phaseIdx).value();
|
|
|
|
cellBinv = fs.invB(phaseIdx).value();
|
|
|
|
cellDensity = fs.density(phaseIdx).value();
|
2021-05-20 09:16:12 -05:00
|
|
|
perfPhaseRate = perf_phase_rate[ perf*np + phaseIdx ];
|
2021-03-08 08:11:50 -06:00
|
|
|
weight_factor += cellDensity * perfPhaseRate/cellBinv * cellInternalEnergy/cellTemperatures;
|
|
|
|
}
|
|
|
|
total_weight += weight_factor;
|
|
|
|
weighted_temperature += weight_factor * cellTemperatures;
|
|
|
|
}
|
2022-08-29 06:41:32 -05:00
|
|
|
well_info.communication().sum(weighted.data(), 2);
|
2021-08-03 14:25:03 -05:00
|
|
|
this->wellState().well(wellID).temperature = weighted_temperature/total_weight;
|
2021-03-08 08:11:50 -06:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 05:58:39 -05:00
|
|
|
|
|
|
|
|
2023-06-13 05:58:07 -05:00
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
logPrimaryVars() const
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
for (const auto& w : well_container_) {
|
|
|
|
os << w->name() << ":";
|
|
|
|
auto pv = w->getPrimaryVars();
|
2024-02-20 15:22:41 -06:00
|
|
|
for (const Scalar v : pv) {
|
2023-06-13 05:58:07 -05:00
|
|
|
os << ' ' << v;
|
|
|
|
}
|
|
|
|
os << '\n';
|
|
|
|
}
|
|
|
|
OpmLog::debug(os.str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<typename BlackoilWellModel<TypeTag>::Scalar>
|
2023-06-13 05:58:07 -05:00
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
getPrimaryVarsDomain(const Domain& domain) const
|
|
|
|
{
|
2024-02-20 15:22:41 -06:00
|
|
|
std::vector<Scalar> ret;
|
2023-06-13 05:58:07 -05:00
|
|
|
for (const auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
const auto& pv = well->getPrimaryVars();
|
|
|
|
ret.insert(ret.end(), pv.begin(), pv.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
2024-02-20 15:22:41 -06:00
|
|
|
setPrimaryVarsDomain(const Domain& domain, const std::vector<Scalar>& vars)
|
2023-06-13 05:58:07 -05:00
|
|
|
{
|
|
|
|
std::size_t offset = 0;
|
|
|
|
for (auto& well : well_container_) {
|
|
|
|
if (well_domain_.at(well->name()) == domain.index) {
|
|
|
|
int num_pri_vars = well->setPrimaryVars(vars.begin() + offset);
|
|
|
|
offset += num_pri_vars;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(offset == vars.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-03 05:58:39 -05:00
|
|
|
|
2023-06-13 05:58:07 -05:00
|
|
|
template <typename TypeTag>
|
|
|
|
void
|
|
|
|
BlackoilWellModel<TypeTag>::
|
|
|
|
setupDomains(const std::vector<Domain>& domains)
|
|
|
|
{
|
|
|
|
OPM_BEGIN_PARALLEL_TRY_CATCH();
|
2023-09-12 05:41:19 -05:00
|
|
|
// TODO: This loop nest may be slow for very large numbers of
|
|
|
|
// domains and wells, but that has not been observed on tests so
|
|
|
|
// far. Using the partition vector instead would be faster if we
|
|
|
|
// need to change.
|
2023-06-13 05:58:07 -05:00
|
|
|
for (const auto& wellPtr : this->well_container_) {
|
2023-09-12 05:41:19 -05:00
|
|
|
const int first_well_cell = wellPtr->cells().front();
|
2023-06-13 05:58:07 -05:00
|
|
|
for (const auto& domain : domains) {
|
2023-09-12 05:41:19 -05:00
|
|
|
auto cell_present = [&domain](const auto cell)
|
|
|
|
{
|
|
|
|
return std::binary_search(domain.cells.begin(),
|
|
|
|
domain.cells.end(), cell);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (cell_present(first_well_cell)) {
|
2023-06-13 05:58:07 -05:00
|
|
|
// Assuming that if the first well cell is found in a domain,
|
|
|
|
// then all of that well's cells are in that same domain.
|
|
|
|
well_domain_[wellPtr->name()] = domain.index;
|
2023-09-12 05:41:19 -05:00
|
|
|
|
2023-06-13 05:58:07 -05:00
|
|
|
// Verify that all of that well's cells are in that same domain.
|
|
|
|
for (int well_cell : wellPtr->cells()) {
|
2023-09-12 05:41:19 -05:00
|
|
|
if (! cell_present(well_cell)) {
|
|
|
|
OPM_THROW(std::runtime_error,
|
|
|
|
fmt::format("Well '{}' found on multiple domains.",
|
|
|
|
wellPtr->name()));
|
2023-06-13 05:58:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::setupDomains(): well found on multiple domains.",
|
2024-02-06 04:55:07 -06:00
|
|
|
simulator_.gridView().comm());
|
2021-06-03 05:58:39 -05:00
|
|
|
|
2023-06-13 05:58:07 -05:00
|
|
|
// Write well/domain info to the DBG file.
|
2024-01-19 09:15:53 -06:00
|
|
|
const Opm::Parallel::Communication& comm = grid().comm();
|
|
|
|
const int rank = comm.rank();
|
|
|
|
DeferredLogger local_log;
|
2024-01-24 07:19:07 -06:00
|
|
|
if (!well_domain_.empty()) {
|
2023-06-13 05:58:07 -05:00
|
|
|
std::ostringstream os;
|
2024-01-19 09:15:53 -06:00
|
|
|
os << "Well name Rank Domain\n";
|
2023-06-13 05:58:07 -05:00
|
|
|
for (const auto& [wname, domain] : well_domain_) {
|
2024-01-19 09:15:53 -06:00
|
|
|
os << wname << std::setw(19 - wname.size()) << rank << std::setw(12) << domain << '\n';
|
2023-06-13 05:58:07 -05:00
|
|
|
}
|
2024-01-19 09:15:53 -06:00
|
|
|
local_log.debug(os.str());
|
|
|
|
}
|
|
|
|
auto global_log = gatherDeferredLogger(local_log, comm);
|
2024-02-17 11:13:46 -06:00
|
|
|
if (this->terminal_output_) {
|
2024-01-19 09:15:53 -06:00
|
|
|
global_log.logMessages();
|
2023-06-13 05:58:07 -05:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 05:58:39 -05:00
|
|
|
|
2017-02-13 09:45:06 -06:00
|
|
|
} // namespace Opm
|