Merge pull request #681 from plgbrts/saltp

Enable salt precipitation/dissolution
This commit is contained in:
Tor Harald Sandve 2022-01-18 12:02:18 +01:00 committed by GitHub
commit c464bceb3b
4 changed files with 213 additions and 13 deletions

View File

@ -38,6 +38,10 @@
#include <opm/input/eclipse/Deck/Deck.hpp>
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
#include <opm/input/eclipse/EclipseState/Tables/PvtwsaltTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/PermfactTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SaltSolubilityTable.hpp>
#include <opm/input/eclipse/EclipseState/Tables/TableManager.hpp>
#include <opm/input/eclipse/EclipseState/Tables/SimpleTable.hpp>
#endif
#include <opm/material/common/Valgrind.hpp>
@ -78,8 +82,10 @@ class BlackOilBrineModule
static constexpr unsigned saltConcentrationIdx = Indices::saltConcentrationIdx;
static constexpr unsigned contiBrineEqIdx = Indices::contiBrineEqIdx;
static constexpr unsigned waterPhaseIdx = FluidSystem::waterPhaseIdx;
static const bool gasEnabled = Indices::gasEnabled;
static const bool oilEnabled = Indices::oilEnabled;
static constexpr unsigned enableBrine = enableBrineV;
static constexpr unsigned enableSaltPrecipitation = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>();
static constexpr unsigned numEq = getPropValue<TypeTag, Properties::NumEq>();
static constexpr unsigned numPhases = FluidSystem::numPhases;
@ -103,7 +109,6 @@ public:
"contains the BRINE keyword");
}
if (!eclState.runspec().phases().active(Phase::BRINE))
return; // brine treatment is supposed to be disabled
@ -126,6 +131,25 @@ public:
bdensityTable_[pvtRegionIdx].setXYContainers(c, bdensityTable);
}
}
if (enableSaltPrecipitation) {
const TableContainer& permfactTables = tableManager.getPermfactTables();
permfactTable_.resize(numPvtRegions);
for (size_t i = 0; i < permfactTables.size(); ++i) {
const PermfactTable& permfactTable = permfactTables.getTable<PermfactTable>(i);
permfactTable_[i].setXYContainers(permfactTable.getPorosityChangeColumn(), permfactTable.getPermeabilityMultiplierColumn());
}
const TableContainer& saltsolTables = tableManager.getSaltsolTables();
if (!saltsolTables.empty()) {
saltsolTable_.resize(numPvtRegions);
assert(numPvtRegions == saltsolTables.size());
for (unsigned pvtRegionIdx = 0; pvtRegionIdx < numPvtRegions; ++ pvtRegionIdx) {
const SaltsolTable& saltsolTable = saltsolTables.getTable<SaltsolTable>(pvtRegionIdx );
saltsolTable_[pvtRegionIdx] = saltsolTable.getSaltsolColumn().front();
}
}
}
}
#endif
@ -139,7 +163,6 @@ public:
return;
}
static bool primaryVarApplies(unsigned pvIdx)
{
if (!enableBrine)
@ -222,7 +245,16 @@ public:
const LhsEval massBrine = surfaceVolumeWater
* Toolbox::template decay<LhsEval>(fs.saltConcentration());
storage[contiBrineEqIdx] += massBrine;
if (enableSaltPrecipitation){
double saltDensity = 2170; // Solid salt density kg/m3
const LhsEval solidSalt =
Toolbox::template decay<LhsEval>(intQuants.porosity())
* saltDensity
* Toolbox::template decay<LhsEval>(intQuants.saltSaturation());
storage[contiBrineEqIdx] += massBrine + solidSalt;
}
else { storage[contiBrineEqIdx] += massBrine;}
}
static void computeFlux(RateVector& flux,
@ -234,7 +266,12 @@ public:
if (!enableBrine)
return;
static unsigned contiGasEqIdx, contiOilEqIdx;
if(gasEnabled) { contiGasEqIdx = Indices::conti0EqIdx + Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx); }
if(oilEnabled) { contiOilEqIdx = Indices::conti0EqIdx + Indices::canonicalToActiveComponentIndex(FluidSystem::oilCompIdx); }
const auto& extQuants = elemCtx.extensiveQuantities(scvfIdx, timeIdx);
const auto& intQuants = elemCtx.intensiveQuantities(scvfIdx, timeIdx);
const unsigned upIdx = extQuants.upstreamIndex(FluidSystem::waterPhaseIdx);
const unsigned inIdx = extQuants.interiorIndex();
@ -245,12 +282,24 @@ public:
extQuants.volumeFlux(waterPhaseIdx)
*up.fluidState().invB(waterPhaseIdx)
*up.fluidState().saltConcentration();
if (enableSaltPrecipitation) {
// modify gas and oil flux for mobility change
if(gasEnabled) { flux[contiGasEqIdx] *= intQuants.permFactor(); }
if(oilEnabled) { flux[contiOilEqIdx] *= intQuants.permFactor(); }
}
}
else {
flux[contiBrineEqIdx] =
extQuants.volumeFlux(waterPhaseIdx)
*decay<Scalar>(up.fluidState().invB(waterPhaseIdx))
*decay<Scalar>(up.fluidState().saltConcentration());
if (enableSaltPrecipitation) {
// modify gas and oil flux for mobility change
if(gasEnabled) { flux[contiGasEqIdx] *= decay<Scalar>(intQuants.permFactor()); }
if(oilEnabled) { flux[contiOilEqIdx] *= decay<Scalar>(intQuants.permFactor()); }
}
}
}
@ -310,13 +359,40 @@ public:
return bdensityTable_[pvtnumRegionIdx];
}
static const TabulatedFunction& permfactTable(const ElementContext& elemCtx,
unsigned scvIdx,
unsigned timeIdx)
{
unsigned pvtnumRegionIdx = elemCtx.problem().pvtRegionIndex(elemCtx, scvIdx, timeIdx);
return permfactTable_[pvtnumRegionIdx];
}
static const Scalar saltsolTable(const ElementContext& elemCtx,
unsigned scvIdx,
unsigned timeIdx)
{
unsigned pvtnumRegionIdx = elemCtx.problem().pvtRegionIndex(elemCtx, scvIdx, timeIdx);
return saltsolTable_[pvtnumRegionIdx];
}
static bool hasBDensityTables()
{
return !bdensityTable_.empty();
}
static bool hasSaltsolTables()
{
return !saltsolTable_.empty();
}
static Scalar saltSol(unsigned regionIdx) {
return saltsolTable_[regionIdx];
}
private:
static std::vector<TabulatedFunction> bdensityTable_;
static std::vector<TabulatedFunction> permfactTable_;
static std::vector<Scalar> saltsolTable_;
static std::vector<Scalar> referencePressure_;
};
@ -329,6 +405,14 @@ template <class TypeTag, bool enableBrineV>
std::vector<typename BlackOilBrineModule<TypeTag, enableBrineV>::Scalar>
BlackOilBrineModule<TypeTag, enableBrineV>::referencePressure_;
template <class TypeTag, bool enableBrineV>
std::vector<typename BlackOilBrineModule<TypeTag, enableBrineV>::Scalar>
BlackOilBrineModule<TypeTag, enableBrineV>::saltsolTable_;
template <class TypeTag, bool enableBrineV>
std::vector<typename BlackOilBrineModule<TypeTag, enableBrineV>::TabulatedFunction>
BlackOilBrineModule<TypeTag, enableBrineV>::permfactTable_;
/*!
* \ingroup BlackOil
* \class Ewoms::BlackOilBrineIntensiveQuantities
@ -354,8 +438,10 @@ class BlackOilBrineIntensiveQuantities
enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
static constexpr int saltConcentrationIdx = Indices::saltConcentrationIdx;
static constexpr int waterPhaseIdx = FluidSystem::waterPhaseIdx;
static constexpr int gasPhaseIdx = FluidSystem::gasPhaseIdx;
static constexpr int oilPhaseIdx = FluidSystem::oilPhaseIdx;
static constexpr unsigned enableBrine = enableBrineV;
static constexpr unsigned enableSaltPrecipitation = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>();
static constexpr int contiBrineEqIdx = Indices::contiBrineEqIdx;
public:
@ -372,9 +458,42 @@ public:
const PrimaryVariables& priVars = elemCtx.primaryVars(dofIdx, timeIdx);
auto& fs = asImp_().fluidState_;
// set saltconcentration
fs.setSaltConcentration(priVars.makeEvaluation(saltConcentrationIdx, timeIdx));
if (enableSaltPrecipitation) {
const auto& saltsolTable = BrineModule::saltsolTable(elemCtx, dofIdx, timeIdx);
saltSolubility_ = saltsolTable;
if (priVars.primaryVarsMeaningBrine() == PrimaryVariables::Sp) {
saltSaturation_ = priVars.makeEvaluation(saltConcentrationIdx, timeIdx);
fs.setSaltConcentration(saltSolubility_);
}
else {
saltConcentration_ = priVars.makeEvaluation(saltConcentrationIdx, timeIdx);
fs.setSaltConcentration(saltConcentration_);
saltSaturation_ = 0.0;
}
}
else {
saltConcentration_ = priVars.makeEvaluation(saltConcentrationIdx, timeIdx);
fs.setSaltConcentration(saltConcentration_);
}
}
void saltPropertiesUpdate_(const ElementContext& elemCtx,
unsigned dofIdx,
unsigned timeIdx)
{
if (enableSaltPrecipitation) {
const Evaluation porosityFactor = 1.0 - saltSaturation(); //phi/phi_0
const auto& permfactTable = BrineModule::permfactTable(elemCtx, dofIdx, timeIdx);
permFactor_ = permfactTable.eval(scalarValue(porosityFactor));
if (permFactor_ < 1 ) {
// adjust mobility for changing permeability
asImp_().mobility_[waterPhaseIdx] *= permFactor_;
}
}
}
const Evaluation& saltConcentration() const
@ -383,12 +502,24 @@ public:
const Evaluation& brineRefDensity() const
{ return refDensity_; }
const Evaluation& saltSaturation() const
{ return saltSaturation_; }
Scalar saltSolubility() const
{ return saltSolubility_; }
const Evaluation& permFactor() const
{ return permFactor_; }
protected:
Implementation& asImp_()
{ return *static_cast<Implementation*>(this); }
Evaluation saltConcentration_;
Evaluation refDensity_;
Evaluation saltSaturation_;
Evaluation permFactor_;
Scalar saltSolubility_;
};
@ -405,18 +536,28 @@ public:
unsigned)
{ }
void saltPropertiesUpdate_(const ElementContext&,
unsigned,
unsigned)
{ }
const Evaluation& saltConcentration() const
{ throw std::runtime_error("saltConcentration() called but brine are disabled"); }
const Evaluation& brineRefDensity() const
{ throw std::runtime_error("brineRefDensity() called but brine are disabled"); }
const Evaluation& saltSaturation() const
{ throw std::logic_error("saltSaturation() called but salt precipitation is disabled"); }
const Scalar saltSolubility() const
{ throw std::logic_error("saltSolubility() called but salt precipitation is disabled"); }
const Evaluation& permFactor() const
{ throw std::logic_error("permFactor() called but salt precipitation is disabled"); }
};
} // namespace Ewoms
#endif

View File

@ -403,6 +403,7 @@ public:
asImp_().updateEnergyQuantities_(elemCtx, dofIdx, timeIdx, paramCache);
asImp_().foamPropertiesUpdate_(elemCtx, dofIdx, timeIdx);
asImp_().MICPPropertiesUpdate_(elemCtx, dofIdx, timeIdx);
asImp_().saltPropertiesUpdate_(elemCtx, dofIdx, timeIdx);
// update the quantities which are required by the chosen
// velocity model

View File

@ -118,6 +118,7 @@ class BlackOilNewtonMethod : public GetPropType<TypeTag, Properties::DiscNewtonM
using MICPModule = BlackOilMICPModule<TypeTag>;
static const unsigned numEq = getPropValue<TypeTag, Properties::NumEq>();
static constexpr bool enableSaltPrecipitation = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>();
public:
BlackOilNewtonMethod(Simulator& simulator) : ParentType(simulator)
@ -369,8 +370,10 @@ protected:
nextValue[pvIdx] = std::max(nextValue[pvIdx], 0.0);
// keep the salt concentration above 0
if (enableBrine && pvIdx == Indices::saltConcentrationIdx)
nextValue[pvIdx] = std::max(nextValue[pvIdx], 0.0);
if (enableBrine && pvIdx == Indices::saltConcentrationIdx) {
if (!enableSaltPrecipitation || (enableSaltPrecipitation && currentValue.primaryVarsMeaningBrine() == PrimaryVariables::Cs))
nextValue[pvIdx] = std::max(nextValue[pvIdx], 0.0);
}
// keep the temperature within given values
if (enableEnergy && pvIdx == Indices::temperatureIdx)

View File

@ -82,6 +82,7 @@ class BlackOilPrimaryVariables : public FvBasePrimaryVariables<TypeTag>
enum { waterSaturationIdx = Indices::waterSaturationIdx };
enum { pressureSwitchIdx = Indices::pressureSwitchIdx };
enum { compositionSwitchIdx = Indices::compositionSwitchIdx };
enum { saltConcentrationIdx = Indices::saltConcentrationIdx };
static const bool compositionSwitchEnabled = Indices::compositionSwitchIdx >= 0;
static const bool waterEnabled = Indices::waterEnabled;
@ -101,6 +102,7 @@ class BlackOilPrimaryVariables : public FvBasePrimaryVariables<TypeTag>
enum { enablePolymer = getPropValue<TypeTag, Properties::EnablePolymer>() };
enum { enableFoam = getPropValue<TypeTag, Properties::EnableFoam>() };
enum { enableBrine = getPropValue<TypeTag, Properties::EnableBrine>() };
enum { enableSaltPrecipitation = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>() };
enum { enableEnergy = getPropValue<TypeTag, Properties::EnableEnergy>() };
enum { enableMICP = getPropValue<TypeTag, Properties::EnableMICP>() };
enum { gasCompIdx = FluidSystem::gasCompIdx };
@ -128,6 +130,11 @@ public:
OnePhase_p, // onephase case
};
enum PrimaryVarsMeaningBrine {
Cs, // salt concentration
Sp, // (precipitated) salt saturation
};
BlackOilPrimaryVariables()
: ParentType()
{
@ -181,6 +188,17 @@ public:
void setPrimaryVarsMeaning(PrimaryVarsMeaning newMeaning)
{ primaryVarsMeaning_ = newMeaning; }
PrimaryVarsMeaningBrine primaryVarsMeaningBrine() const
{ return primaryVarsMeaningBrine_; }
/*!
* \brief Set the interpretation which should be applied to the switching primary
* variables.
*/
void setPrimaryVarsMeaningBrine(PrimaryVarsMeaningBrine newMeaning)
{ primaryVarsMeaningBrine_ = newMeaning; }
/*!
* \copydoc ImmisciblePrimaryVariables::assignMassConservative
*/
@ -273,6 +291,8 @@ public:
bool oilPresent = FluidSystem::phaseIsActive(oilPhaseIdx)?(fluidState.saturation(oilPhaseIdx) > 0.0):false;
static const Scalar thresholdWaterFilledCell = 1.0 - 1e-6;
bool onlyWater = FluidSystem::phaseIsActive(waterPhaseIdx)?(fluidState.saturation(waterPhaseIdx) > thresholdWaterFilledCell):false;
const auto& saltSaturation = BlackOil::getSaltSaturation_<FluidSystem, FluidState, Scalar>(fluidState, pvtRegionIdx_);
bool precipitatedSaltPresent = enableSaltPrecipitation?(saltSaturation > 0.0):false;
// deal with the primary variables for the energy extension
EnergyModule::assignPrimaryVars(*this, fluidState);
@ -304,6 +324,13 @@ public:
primaryVarsMeaning_ = Sw_po_Sg;
}
if (enableSaltPrecipitation){
if (precipitatedSaltPresent)
primaryVarsMeaningBrine_ = Sp;
else
primaryVarsMeaningBrine_ = Cs;
}
// assign the actual primary variables
if (primaryVarsMeaning() == OnePhase_p) {
if (waterEnabled) {
@ -311,7 +338,6 @@ public:
} else {
throw std::logic_error("For single-phase runs, only pure water is presently allowed.");
}
}
else if (primaryVarsMeaning() == Sw_po_Sg) {
if (waterEnabled)
@ -347,6 +373,16 @@ public:
(*this)[compositionSwitchIdx] = Rv;
}
if (enableSaltPrecipitation) {
if (primaryVarsMeaningBrine() == Sp) {
(*this)[saltConcentrationIdx] = FsToolbox::value(saltSaturation);
}
else {
const auto& saltConcentration = BlackOil::getSaltConcentration_<FluidSystem, FluidState, Scalar>(fluidState, pvtRegionIdx_);
(*this)[saltConcentrationIdx] = FsToolbox::value(saltConcentration);
}
}
checkDefined();
}
@ -379,6 +415,24 @@ public:
if (waterEnabled)
Sw = (*this)[Indices::waterSaturationIdx];
if (enableSaltPrecipitation) {
Scalar saltSolubility = BrineModule::saltSol(pvtRegionIndex());
if (primaryVarsMeaningBrine() == Sp) {
Scalar saltSat = (*this)[saltConcentrationIdx];
if (saltSat < -eps){ //precipitated salt dissappears
setPrimaryVarsMeaningBrine(Cs);
(*this)[saltConcentrationIdx] = saltSolubility; //set salt concentration to solubility limit
}
}
else if (primaryVarsMeaningBrine() == Cs) {
Scalar saltConc = (*this)[saltConcentrationIdx];
if (saltConc > saltSolubility + eps){ //salt concentration exceeds solubility limit
setPrimaryVarsMeaningBrine(Sp);
(*this)[saltConcentrationIdx] = 0.0;
}
}
}
if (primaryVarsMeaning() == Sw_po_Sg) {
// special case for cells with almost only water
@ -816,6 +870,7 @@ private:
}
PrimaryVarsMeaning primaryVarsMeaning_;
PrimaryVarsMeaningBrine primaryVarsMeaningBrine_;
unsigned short pvtRegionIdx_;
};