Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions opm/simulators/utils/PartiallySupportedFlowKeywords.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -673,9 +673,7 @@ partiallySupported()
{
{11,{true, allow_values<double> {}, "WECON(WCUT2): Feature not supported and should be defaulted"}}, // WELOPEN
{13,{true, allow_values<double> {}, "WECON(GLR): Feature not supported and should be defaulted"}}, // GLR
{14,{true, allow_values<double> {}, "WECON(LRAT): Feature not supported and should be defaulted"}}, // LRAT
{15,{true, allow_values<double> {}, "WECON(TEMP): Feature not supported and should be defaulted"}}, // TEMP
{16,{true, allow_values<double> {}, "WECON(RESV): Feature not supported and should be defaulted"}}, // RESV
},
},
{
Expand Down
14 changes: 14 additions & 0 deletions opm/simulators/wells/EconomicLimitsMessage.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

#include <opm/common/utility/TimeService.hpp>

#include <opm/input/eclipse/Units/UnitSystem.hpp>

#include <fmt/chrono.h>
#include <fmt/format.h>

Expand All @@ -42,6 +44,18 @@ inline std::string economicLimitDateString(const std::time_t start_time, const d
return fmt::format("{:%d-%b-%Y}", fmt::gmtime(cur_time));
}

//! \brief The "at time ... (date = ...)" clause shared by the well (WECON) and
//! connection (CECON) economic-limit messages.
inline std::string economicLimitWhenString(const UnitSystem& unit_system,
const std::time_t start_time,
const double sim_time)
{
return fmt::format("at time {:.2f} {} (date = {})",
unit_system.from_si(UnitSystem::measure::time, sim_time),
unit_system.name(UnitSystem::measure::time),
economicLimitDateString(start_time, sim_time));
}

//! \brief Separator line used to frame economic-limit workover messages.
//! Built once and returned by reference (all callers use the same line).
inline const std::string& economicLimitMessageSeparator()
Expand Down
17 changes: 17 additions & 0 deletions opm/simulators/wells/WellInterfaceFluidSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include <opm/simulators/wells/GroupStateHelper.hpp>
#include <opm/simulators/wells/WellState.hpp>

#include <numeric>

namespace Opm
{

Expand Down Expand Up @@ -151,6 +153,21 @@ calculateReservoirRates(const bool use_well_bhp_temperature, SingleWellState<Sca
}
}

template<typename FluidSystem>
typename FluidSystem::Scalar
WellInterfaceFluidSystem<FluidSystem>::
totalReservoirVoidageRate(const std::vector<Scalar>& surface_rates) const
{
std::vector<Scalar> voidage_rates(surface_rates.size(), 0.0);
this->rateConverter_
.calcReservoirVoidageRates(/*fipreg*/ 0,
this->pvtRegionIdx_,
surface_rates,
voidage_rates);

return std::accumulate(voidage_rates.begin(), voidage_rates.end(), Scalar{0.0});
}

template<typename FluidSystem>
bool
WellInterfaceFluidSystem<FluidSystem>::
Expand Down
2 changes: 2 additions & 0 deletions opm/simulators/wells/WellInterfaceFluidSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class WellInterfaceFluidSystem : public WellInterfaceGeneric<typename FluidSyste
return rateConverter_;
}

Scalar totalReservoirVoidageRate(const std::vector<Scalar>& surface_rates) const override;

protected:
WellInterfaceFluidSystem(const Well& well,
const ParallelWellInfo<Scalar>& parallel_well_info,
Expand Down
6 changes: 4 additions & 2 deletions opm/simulators/wells/WellInterfaceGeneric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,14 +346,16 @@ updateWellTestState(const SingleWellState<Scalar, IndexTraits>& ws,
WellTestState& wellTestState,
const UnitSystem& unit_system,
const std::time_t start_time,
DeferredLogger& deferred_logger) const
DeferredLogger& deferred_logger,
std::string* closure_reason) const
{
const WellTest<Scalar, IndexTraits> well_test(*this);
// updating well test state based on Economic limits for operable wells
if (this->isOperableAndSolvable()) {
well_test.updateWellTestStateEconomic(ws, simulationTime, writeMessageToOPMLog,
during_well_test, wellTestState,
zero_group_target, unit_system, start_time, deferred_logger);
zero_group_target, unit_system, start_time,
deferred_logger, closure_reason);
well_test.updateWellTestStateCECON(ws, simulationTime, writeMessageToOPMLog, wellTestState,
unit_system, start_time, deferred_logger);
} else {
Expand Down
11 changes: 10 additions & 1 deletion opm/simulators/wells/WellInterfaceGeneric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ class WellInterfaceGeneric {
//! which re-solves the well after every completion closure; false for
//! the regular timestep update. See
//! WellTest::updateWellTestStateEconomic().
//! \param closure_reason when non-null, receives the reason a limit closed
//! the well instead of it being logged here. Used by the WTEST re-open
//! testing, where no shut-in actually happens.
void updateWellTestState(const SingleWellState<Scalar, IndexTraits>& ws,
const double& simulationTime,
const bool& writeMessageToOPMLog,
Expand All @@ -197,7 +200,8 @@ class WellInterfaceGeneric {
WellTestState& wellTestState,
const UnitSystem& unit_system,
const std::time_t start_time,
DeferredLogger& deferred_logger) const;
DeferredLogger& deferred_logger,
std::string* closure_reason = nullptr) const;

bool isPressureControlled(const WellStateType& well_state) const;

Expand All @@ -224,6 +228,11 @@ class WellInterfaceGeneric {
virtual Scalar connectionDensity(const int globalConnIdx,
const int openConnIdx) const = 0;

//! \brief The total reservoir voidage rate for the given surface phase rates
//! (or potentials), using the region average PVT properties that also
//! produce the voidage rates in the well state.
virtual Scalar totalReservoirVoidageRate(const std::vector<Scalar>& surface_rates) const = 0;

void addPerforations(const std::vector<RuntimePerforation>& perfs);

protected:
Expand Down
39 changes: 28 additions & 11 deletions opm/simulators/wells/WellInterface_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ namespace Opm
unit_system.name(UnitSystem::measure::time),
economicLimitDateString(start_time, simulation_time));

deferred_logger.info(fmt::format(" well {} is being tested {}", this->name(), when));
deferred_logger.info(fmt::format("Well {} is being tested {}.", this->name(), when));

GroupStateHelperType groupStateHelper_copy = groupStateHelper;
WellStateType well_state_copy = well_state;
Expand Down Expand Up @@ -464,6 +464,21 @@ namespace Opm
}

WellTestState welltest_state_temp;
// Why a limit closed the well again. Empty when there is no reason to give.
std::string closure_reason;

// The test can fail to re-open the well at several points below, and they
// all report it through here. The outcome goes to the PRT; the reason only
// to the debug log, because a shut well is re-tested at every interval and
// this message recurs for as long as it stays shut.
auto notReopened = [this, &when, &deferred_logger](const std::string_view reason)
{
deferred_logger.info(
fmt::format("Well {} is not re-opened {}.", this->name(), when));
if (!reason.empty()) {
deferred_logger.debug(fmt::format("Because {}.", reason));
}
};

bool testWell = true;
// if a well is closed because all completions are closed, we need to check each completion
Expand All @@ -473,26 +488,21 @@ namespace Opm
const std::size_t original_number_closed_completions = welltest_state_temp.num_closed_completions();
bool converged = solveWellForTesting(simulator, groupStateHelper_copy, well_state_copy);
if (!converged) {
const auto msg = fmt::format("WTEST: Well {} is not solvable (physical)", this->name());
deferred_logger.debug(msg);
notReopened("the well equations could not be solved");
return;
}


updateWellOperability(simulator, well_state_copy, groupStateHelper_copy);
if ( !this->isOperableAndSolvable() ) {
const auto msg = fmt::format("WTEST: Well {} is not operable (physical)", this->name());
deferred_logger.debug(msg);
notReopened("the well is not operable");
return;
}
std::vector<Scalar> potentials;
try {
computeWellPotentials(simulator, well_state_copy, groupStateHelper_copy, potentials);
} catch (const std::exception& e) {
const std::string msg = fmt::format("well {}: computeWellPotentials() "
"failed during testing for re-opening: ",
this->name(), e.what());
deferred_logger.info(msg);
notReopened(fmt::format("computing the well potentials failed: {}", e.what()));
return;
}
const int np = well_state_copy.numPhases();
Expand All @@ -508,7 +518,8 @@ namespace Opm
welltest_state_temp,
simulator.vanguard().eclState().getUnits(),
simulator.vanguard().schedule().getStartTime(),
deferred_logger);
deferred_logger,
&closure_reason);
this->closeCompletions(welltest_state_temp);

// Stop testing if the well is closed or shut due to all completions shut
Expand All @@ -525,8 +536,10 @@ namespace Opm
if (!welltest_state_temp.well_is_closed(this->name())) {
well_test_state.open_well(this->name());

const std::string& sep = economicLimitMessageSeparator();
deferred_logger.info(
fmt::format("well {} is re-opened {}", this->name(), when));
fmt::format("{}\nWell {} is re-opened {}.\n{}",
sep, this->name(), when, sep));

// also reopen completions
for (const auto& completion : this->well_ecl_.getCompletions()) {
Expand All @@ -536,6 +549,10 @@ namespace Opm
well_state = well_state_copy;
open_times.try_emplace(this->name(), well_test_state.lastTestTime(this->name()));
}
else {
// A limit closed the well again: discard the tested state, stay shut.
notReopened(closure_reason);
}
}


Expand Down
Loading