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: 2 additions & 0 deletions opm/simulators/timestepping/ConvergenceReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ namespace Opm
return "MassBalance";
case T::Pressure:
return "Pressure";
case T::Energy:
return "Energy";
case T::ControlBHP:
return "ControlBHP";
case T::ControlTHP:
Expand Down
1 change: 1 addition & 0 deletions opm/simulators/timestepping/ConvergenceReport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ namespace Opm
Invalid,
MassBalance,
Pressure,
Energy,
ControlBHP,
ControlTHP,
ControlRate,
Expand Down
6 changes: 6 additions & 0 deletions opm/simulators/wells/BlackoilWellModel_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2159,6 +2159,12 @@ namespace Opm {
int
BlackoilWellModel<TypeTag>::numConservationQuantities() const
{
// TODO: when the energy equation joins, here should we start
// of the refactoring related to the the usage of the numConservationQuantities()
// since energy equation is also a conservation equation
// at the current stage, we only have energy equations for MSW, so we should not
// refactor at this stage.

// The numPhases() functions returns 1-3, depending on which
// of the (oil, water, gas) phases are active. For each of those phases,
// if the phase is active the corresponding component is present and
Expand Down
6 changes: 5 additions & 1 deletion opm/simulators/wells/MSWellHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,9 @@ using Mat = Dune::BCRSMatrix<Dune::FieldMatrix<Scalar,M,N>>;
INSTANTIATE_PARALLELLMSWELLB(T, 3, 4) \
INSTANTIATE_PARALLELLMSWELLB(T, 4, 3) \
INSTANTIATE_PARALLELLMSWELLB(T, 4, 4) \
INSTANTIATE_PARALLELLMSWELLB(T, 4, 5)
INSTANTIATE_PARALLELLMSWELLB(T, 4, 5) \
INSTANTIATE_PARALLELLMSWELLB(T, 5, 4) \
INSTANTIATE_PARALLELLMSWELLB(T, 5, 5)

#define INSTANTIATE_UMF(T,Dim) \
template Vec<T,Dim> applyUMFPack(Dune::UMFPack<Mat<T,Dim>>&, \
Expand Down Expand Up @@ -414,13 +416,15 @@ using Mat = Dune::BCRSMatrix<Dune::FieldMatrix<Scalar,M,N>>;
INSTANTIATE_UMF(T,2) \
INSTANTIATE_UMF(T,3) \
INSTANTIATE_UMF(T,4) \
INSTANTIATE_UMF(T,5) \
INSTANTIATE_EVAL(T,3) \
INSTANTIATE_EVAL(T,4) \
INSTANTIATE_EVAL(T,5) \
INSTANTIATE_EVAL(T,6) \
INSTANTIATE_EVAL(T,7) \
INSTANTIATE_EVAL(T,8) \
INSTANTIATE_EVAL(T,9) \
INSTANTIATE_EVAL(T,10) \
INSTANTIATE_ALL_PARALLELLMSWELLB(T)

INSTANTIATE_TYPE(double)
Expand Down
83 changes: 77 additions & 6 deletions opm/simulators/wells/MultisegmentWell.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include <opm/simulators/wells/WellInterface.hpp>
#include <opm/simulators/wells/MultisegmentWellEval.hpp>

#include <string_view>

namespace Opm {

class DeferredLogger;
Expand Down Expand Up @@ -56,6 +58,8 @@ namespace Opm {

using Base::has_solvent;
using Base::has_polymer;
using Base::has_energy;
using Base::has_brine;
using Base::Water;
using Base::Oil;
using Base::Gas;
Expand All @@ -71,8 +75,16 @@ namespace Opm {
using typename MSWEval::BVectorWell;
using MSWEval::SPres;
using typename Base::PressureMatrix;
// it contains the temperature and salt concentration of the first perforation
// TODO: kind of temporary measure, will evolve or be replaced based on future development and understanding
using FSInfo = std::tuple<Scalar, Scalar>;

// a fluid state to calculate the properties inside the wellbore for each segment
// it will be probably used for more things, but at the moment, it is for the enthalpy
// calculation in the wellbore.
template <typename ValueType>
using SegmentFluidState = Base::template BlackOilFluidStateType<ValueType>;

MultisegmentWell(const Well& well,
const ParallelWellInfo<Scalar>& pw_info,
const int time_step,
Expand Down Expand Up @@ -173,6 +185,16 @@ namespace Opm {

// the intial amount of fluids in each segment under surface condition
std::vector<std::vector<Scalar> > segment_fluid_initial_;
// total energy inside the segments at the beginning of the time step
std::vector<Scalar> segment_initial_energy_;

// segment fluid state
std::vector<SegmentFluidState<EvalWell>> segment_fluid_state_;

// fluid state under the wellhead condition, it is used to calculate the enthalpy
// under operation condition for energy injection
// because BHP will be involved, we use EvalWell type here
SegmentFluidState<EvalWell> wellhead_fluid_state_;

mutable int debug_cost_counter_ = 0;

Expand All @@ -184,7 +206,7 @@ namespace Opm {
const Scalar relaxation_factor = 1.0);

// computing the accumulation term for later use in well mass equations
void computeInitialSegmentFluids(const Simulator& simulator, DeferredLogger& deferred_logger);
void computeInitialSegmentFluids(DeferredLogger& deferred_logger);

// compute the pressure difference between the perforation and cell center
void computePerfCellPressDiffs(const Simulator& simulator);
Expand Down Expand Up @@ -289,10 +311,6 @@ namespace Opm {

void updateWaterThroughput(const double dt, WellStateType& well_state) const override;

EvalWell getSegmentSurfaceVolume(const Simulator& simulator,
const int seg_idx,
DeferredLogger& deferred_logger) const;

// turn on crossflow to avoid singular well equations
// when the well is banned from cross-flow and the BHP is not properly initialized,
// we turn on crossflow to avoid singular well equations. It can result in wrong-signed
Expand Down Expand Up @@ -332,9 +350,62 @@ namespace Opm {
DeferredLogger& deferred_logger) const override;

FSInfo getFirstPerforationFluidStateInfo(const Simulator& simulator) const;

// this function can potentially be shared between multisegment wells and standard wells
// TODO: this function largely overlaps with calculatePhaseProperties(), some refactoring/unification should be done
template <typename ValueType = EvalWell>
SegmentFluidState<ValueType>
createFluidState(const std::vector<ValueType>& fluid_composition,
const ValueType& pressure,
const ValueType& temperature,
const ValueType& saltConcentration,
ValueType& volume_ratio,
DeferredLogger& deferred_logger) const;

SegmentFluidState<EvalWell>
createSegmentFluidState(int seg, const FSInfo& info, DeferredLogger& deferred_logger);

void computeInitialSegmentEnergy();

// assemble the energy equation contribution for a single perforation/connection
void assemblePerforationEnergyEq(const IntensiveQuantities& int_quants,
const std::vector<EvalWell>& cq_s,
const int seg,
const int local_perf_index,
DeferredLogger& deferred_logger);

void updateWellHeadCondition(const Simulator& simulator, const FSInfo& info, DeferredLogger& deferred_logger);

void updateSegmentFluidState(const FSInfo& info, DeferredLogger& deferred_logger);

template <typename ValueType = EvalWell>
ValueType computeSegmentEnergy(int seg) const;

// Convert per-component surface volumetric rates to a phase reservoir
// volumetric rate using the upwind segment fluid state. Handles the
// dissolved-gas/vaporized-oil coupling (rs/rv). When the determinant
// (1 - rs*rv) is non-positive, falls back to the no-dissolution case
// and logs a debug message tagged with @p context.
// @return the reservoir volumetric rate of @p phaseIdx.
EvalWell surfaceToReservoirRate(unsigned phaseIdx,
const SegmentFluidState<EvalWell>& segment_fs,
const std::vector<EvalWell>& surface_rates,
int seg,
std::string_view context,
DeferredLogger& deferred_logger) const;

// Compute the energy flux carried by fluid flowing from segment
// @p seg toward its outlet, using @p upwind_fs as the upwind
// fluid state. @p context is used in diagnostic messages.
// @return the energy flux as sum_phases(reservoir_rate * enthalpy * density).
EvalWell computeSegmentEnergyRate(int seg,
int upwind_seg,
const SegmentFluidState<EvalWell>& upwind_fs,
std::string_view context,
DeferredLogger& deferred_logger) const;
};

}
} // namespace Opm

#include "MultisegmentWell_impl.hpp"

Expand Down
12 changes: 12 additions & 0 deletions opm/simulators/wells/MultisegmentWellAssemble.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,12 @@ assembleOutflowTerm(const int seg,
eqns.D()[seg][seg_upwind][comp_idx][GFrac] -= segment_rate.derivative(GFrac + Indices::numEq);
}
// pressure derivative should be zero

if constexpr (enable_energy) {
// energy flux depends on the pressure
eqns.D()[seg][seg_upwind][comp_idx][SPres] -= segment_rate.derivative(SPres + Indices::numEq);
eqns.D()[seg][seg_upwind][comp_idx][Temperature] -= segment_rate.derivative(Temperature + Indices::numEq);
}
}

template<class FluidSystem, class Indices>
Expand All @@ -377,6 +383,12 @@ assembleInflowTerm(const int seg,
eqns.D()[seg][inlet_upwind][comp_idx][GFrac] += inlet_rate.derivative(GFrac + Indices::numEq);
}
// pressure derivative should be zero

if constexpr (enable_energy) {
// energy flux depends on the pressure
eqns.D()[seg][inlet_upwind][comp_idx][SPres] += inlet_rate.derivative(SPres + Indices::numEq);
eqns.D()[seg][inlet_upwind][comp_idx][Temperature] += inlet_rate.derivative(Temperature + Indices::numEq);
}
}

template<class FluidSystem, class Indices>
Expand Down
4 changes: 3 additions & 1 deletion opm/simulators/wells/MultisegmentWellAssemble.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@ class MultisegmentWellAssemble
static constexpr int WQTotal = PrimaryVariables::WQTotal;
static constexpr bool has_wfrac_variable = PrimaryVariables::has_wfrac_variable;
static constexpr bool has_gfrac_variable = PrimaryVariables::has_gfrac_variable;
static constexpr bool enable_energy = PrimaryVariables::enable_energy;
static constexpr int WFrac = PrimaryVariables::WFrac;
static constexpr int GFrac = PrimaryVariables::GFrac;
static constexpr int Temperature = PrimaryVariables::Temperature;
static constexpr int SPres = PrimaryVariables::SPres;

public:
static constexpr int numWellEq = Indices::numPhases+1;
static constexpr int numWellEq = Indices::numPhases + 1 + enable_energy;
using Scalar = typename FluidSystem::Scalar;
using IndexTraits = typename FluidSystem::IndexTraitsType;
using Equations = MultisegmentWellEquations<Scalar, IndexTraits, numWellEq,Indices::numEq>;
Expand Down
4 changes: 3 additions & 1 deletion opm/simulators/wells/MultisegmentWellEquations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,9 @@ sumDistributed(Parallel::Communication comm)
INSTANTIATE(T,3,4) \
INSTANTIATE(T,4,3) \
INSTANTIATE(T,4,4) \
INSTANTIATE(T,4,5)
INSTANTIATE(T,4,5) \
INSTANTIATE(T,5,4) \
INSTANTIATE(T,5,5)

INSTANTIATE_TYPE(double)

Expand Down
40 changes: 35 additions & 5 deletions opm/simulators/wells/MultisegmentWellEval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ getWellConvergence(const WellState<Scalar, IndexTraits>& well_state,
const bool relax_tolerance,
const bool well_is_stopped) const
{
assert(int(B_avg.size()) == baseif_.numConservationQuantities());
// TODO: numConservationQuantities() should be refactored to incorporate the energy equation
// TODO: when enable_energy is true, at the current stage, B_avg can be baseif_.numConservationQuantities() if calculated in BlackoilWellModel
// or baseif_.numConservationQuantities() + 1 if calculated in getReservoirConvergence() in BlackoilModel
// we are only accessing the first baseif_.numConservationQuantities() elements with current form of the code
assert(int(B_avg.size()) == baseif_.numConservationQuantities() || enable_energy);

// checking if any residual is NaN or too large. The two large one is only handled for the well flux
std::vector<std::vector<Scalar>> abs_residual(this->numberOfSegments(),
Expand All @@ -103,14 +107,19 @@ getWellConvergence(const WellState<Scalar, IndexTraits>& well_state,
std::vector<Scalar> maximum_residual(numWellEq, 0.0);

ConvergenceReport report;
// TODO: the following is a little complicated, maybe can be simplified in some way?
// TODO: the energy equation should not have a B_avg here to use, maybe we can use 1 for it
for (int eq_idx = 0; eq_idx < numWellEq; ++eq_idx) {
for (int seg = 0; seg < this->numberOfSegments(); ++seg) {
if (eq_idx < baseif_.numConservationQuantities()) { // phase or component mass equations
const Scalar flux_residual = B_avg[eq_idx] * abs_residual[seg][eq_idx];
if (flux_residual > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = flux_residual;
}
} else if (enable_energy && eq_idx == Temperature) { // energy equation
const Scalar energy_residual = abs_residual[seg][eq_idx];
if (energy_residual > maximum_residual[eq_idx]) {
maximum_residual[eq_idx] = energy_residual;
}
} else { // pressure or control equation
// for the top segment (seg == 0), it is control equation, will be checked later separately
if (seg > 0) {
Expand Down Expand Up @@ -139,7 +148,22 @@ getWellConvergence(const WellState<Scalar, IndexTraits>& well_state,
} else if (flux_residual > relaxed_inner_tolerance_flow_ms_well) {
report.setWellFailed({CR::WellFailure::Type::MassBalance, CR::Severity::Normal, eq_idx, baseif_.name()});
}
} else { // pressure equation
} else if (enable_energy && eq_idx == Temperature) {
const Scalar energy_residual = maximum_residual[eq_idx];
// TODO: possibly the dummy_phase should be something else, while requires extension to WellConvergenceMetric
constexpr int dummy_phase = -1;
constexpr Scalar tolerance_energy_wells = 1.e-5; // TODO: need to determine a reasonable value for this
constexpr Scalar relaxed_tolerance_energy_wells = 1.e-4; // TODO: need to determine a reasonable value for this
if (std::isnan(energy_residual)) {
report.setWellFailed({CR::WellFailure::Type::Energy, CR::Severity::NotANumber, dummy_phase, baseif_.name()});
} else if (std::isinf(energy_residual)) {
report.setWellFailed({CR::WellFailure::Type::Energy, CR::Severity::TooLarge, dummy_phase, baseif_.name()});
} else if (!relax_tolerance && energy_residual > tolerance_energy_wells) {
report.setWellFailed({CR::WellFailure::Type::Energy, CR::Severity::Normal, dummy_phase, baseif_.name()});
} else if (energy_residual > relaxed_tolerance_energy_wells) {
report.setWellFailed({CR::WellFailure::Type::Energy, CR::Severity::Normal, dummy_phase, baseif_.name()});
}
} else if (eq_idx == SPres) { // pressure equation
const Scalar pressure_residual = maximum_residual[eq_idx];
const int dummy_component = -1;
if (std::isnan(pressure_residual)) {
Expand Down Expand Up @@ -437,7 +461,10 @@ getFiniteWellResiduals(const std::vector<Scalar>& B_avg,
Scalar residual = 0.;
if (eq_idx < baseif_.numConservationQuantities()) {
residual = std::abs(linSys_.residual()[seg][eq_idx]) * B_avg[eq_idx];
} else {
} else if (eq_idx == Temperature) {
residual = std::abs(linSys_.residual()[seg][eq_idx]);
} else if (eq_idx == SPres) {
// skip seg 0 because it holds the control equation
if (seg > 0) {
residual = std::abs(linSys_.residual()[seg][eq_idx]);
}
Expand Down Expand Up @@ -548,7 +575,10 @@ getResidualMeasureValue(const WellState<Scalar, IndexTraits>& well_state,
const Scalar rate_tolerance = tolerance_wells;
[[maybe_unused]] int count = 0;
Scalar sum = 0;
for (int eq_idx = 0; eq_idx < numWellEq - 1; ++eq_idx) {
// Loop over mass balance equations
// TODO: we should probably also do something related to the energy equation also
// in the residual measures
for (int eq_idx = 0; eq_idx < baseif_.numConservationQuantities(); ++eq_idx) {
if (residuals[eq_idx] > rate_tolerance) {
sum += residuals[eq_idx] / rate_tolerance;
++count;
Expand Down
3 changes: 3 additions & 0 deletions opm/simulators/wells/MultisegmentWellEval.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class MultisegmentWellEval : public MultisegmentWellGeneric<typename FluidSystem
static constexpr int SPres = PrimaryVariables::SPres;
static constexpr int WQTotal = PrimaryVariables::WQTotal;

static constexpr bool enable_energy = PrimaryVariables::enable_energy;
static constexpr int Temperature = PrimaryVariables::Temperature;

using Equations = MultisegmentWellEquations<Scalar, IndexTraits, numWellEq, Indices::numEq>;
using MSWSegments = MultisegmentWellSegments<FluidSystem,Indices>;

Expand Down
3 changes: 3 additions & 0 deletions opm/simulators/wells/MultisegmentWellGeneric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class MultisegmentWellGeneric
const std::vector<Scalar>& seg_dp) const;

const WellInterfaceGeneric<Scalar, IndexTraits>& baseif_;

// surface densities of the fluid (it is better in WellInterfaceGeneric, w)
std::vector<Scalar> surface_densities_;
};

}
Expand Down
Loading