mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
ebos: implement partial support for the TUNING keyword
this only implements the parameters that are currently supported by `flow`.
This commit is contained in:
parent
0b600cb824
commit
a34a8d6b9e
@ -139,6 +139,11 @@ NEW_PROP_TAG(EnableThermalFluxBoundaries);
|
|||||||
// The class which deals with ECL aquifers
|
// The class which deals with ECL aquifers
|
||||||
NEW_PROP_TAG(EclAquiferModel);
|
NEW_PROP_TAG(EclAquiferModel);
|
||||||
|
|
||||||
|
NEW_PROP_TAG(MaxTimeStepSizeAfterWellEvent);
|
||||||
|
NEW_PROP_TAG(RestartShrinkFactor);
|
||||||
|
NEW_PROP_TAG(MaxFails);
|
||||||
|
NEW_PROP_TAG(EnableEclTuning);
|
||||||
|
|
||||||
// Set the problem property
|
// Set the problem property
|
||||||
SET_TYPE_PROP(EclBaseProblem, Problem, Ewoms::EclProblem<TypeTag>);
|
SET_TYPE_PROP(EclBaseProblem, Problem, Ewoms::EclProblem<TypeTag>);
|
||||||
|
|
||||||
@ -231,7 +236,7 @@ SET_SCALAR_PROP(EclBaseProblem, EndTime, 1e100);
|
|||||||
// The chosen value means that the size of the first time step is the
|
// The chosen value means that the size of the first time step is the
|
||||||
// one of the initial episode (if the length of the initial episode is
|
// one of the initial episode (if the length of the initial episode is
|
||||||
// not millions of trillions of years, that is...)
|
// not millions of trillions of years, that is...)
|
||||||
SET_SCALAR_PROP(EclBaseProblem, InitialTimeStepSize, 1e100);
|
SET_SCALAR_PROP(EclBaseProblem, InitialTimeStepSize, 3600*24);
|
||||||
|
|
||||||
// the default for the allowed volumetric error for oil per second
|
// the default for the allowed volumetric error for oil per second
|
||||||
SET_SCALAR_PROP(EclBaseProblem, NewtonRawTolerance, 1e-2);
|
SET_SCALAR_PROP(EclBaseProblem, NewtonRawTolerance, 1e-2);
|
||||||
@ -329,6 +334,12 @@ SET_BOOL_PROP(EclBaseProblem, EnableTracerModel, false);
|
|||||||
// i.e., experimental features must be explicitly enabled at compile time
|
// i.e., experimental features must be explicitly enabled at compile time
|
||||||
SET_BOOL_PROP(EclBaseProblem, EnableExperiments, false);
|
SET_BOOL_PROP(EclBaseProblem, EnableExperiments, false);
|
||||||
|
|
||||||
|
// set defaults for the time stepping parameters
|
||||||
|
SET_SCALAR_PROP(EclBaseProblem, MaxTimeStepSizeAfterWellEvent, 3600*24*365.25);
|
||||||
|
SET_SCALAR_PROP(EclBaseProblem, RestartShrinkFactor, 3);
|
||||||
|
SET_INT_PROP(EclBaseProblem, MaxFails, 10);
|
||||||
|
SET_BOOL_PROP(EclBaseProblem, EnableEclTuning, false);
|
||||||
|
|
||||||
END_PROPERTIES
|
END_PROPERTIES
|
||||||
|
|
||||||
namespace Ewoms {
|
namespace Ewoms {
|
||||||
@ -430,6 +441,14 @@ public:
|
|||||||
"The frequencies of which time steps are serialized to disk");
|
"The frequencies of which time steps are serialized to disk");
|
||||||
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableTracerModel,
|
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableTracerModel,
|
||||||
"Transport tracers found in the deck.");
|
"Transport tracers found in the deck.");
|
||||||
|
EWOMS_REGISTER_PARAM(TypeTag, Scalar, MaxTimeStepSizeAfterWellEvent,
|
||||||
|
"Maximum time step size after an well event");
|
||||||
|
EWOMS_REGISTER_PARAM(TypeTag, Scalar, RestartShrinkFactor,
|
||||||
|
"Factor by which the time step is reduced after convergence failure");
|
||||||
|
EWOMS_REGISTER_PARAM(TypeTag, int, MaxFails,
|
||||||
|
"Maximum consecutive convergence failures before termination");
|
||||||
|
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableEclTuning,
|
||||||
|
"Honor some aspects of the TUNING keyword from the ECL deck.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -540,6 +559,14 @@ public:
|
|||||||
if (EWOMS_GET_PARAM(TypeTag, bool, EnableEclOutput))
|
if (EWOMS_GET_PARAM(TypeTag, bool, EnableEclOutput))
|
||||||
// create the ECL writer
|
// create the ECL writer
|
||||||
eclWriter_.reset(new EclWriterType(simulator));
|
eclWriter_.reset(new EclWriterType(simulator));
|
||||||
|
|
||||||
|
enableEclTuning_ = EWOMS_GET_PARAM(TypeTag, bool, EnableEclTuning);
|
||||||
|
initialTimeStepSize_ = EWOMS_GET_PARAM(TypeTag, Scalar, InitialTimeStepSize);
|
||||||
|
maxTimeStepSize_ = EWOMS_GET_PARAM(TypeTag, Scalar, MaxTimeStepSize);
|
||||||
|
maxTimeStepAfterWellEvent_ = EWOMS_GET_PARAM(TypeTag, Scalar, MaxTimeStepSizeAfterWellEvent);
|
||||||
|
restartShrinkFactor_ = EWOMS_GET_PARAM(TypeTag, Scalar, RestartShrinkFactor);
|
||||||
|
maxFails_ = EWOMS_GET_PARAM(TypeTag, int, MaxFails);
|
||||||
|
minTimeStepSize_ = EWOMS_GET_PARAM(TypeTag, Scalar, MinTimeStepSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -551,13 +578,27 @@ public:
|
|||||||
|
|
||||||
auto& simulator = this->simulator();
|
auto& simulator = this->simulator();
|
||||||
|
|
||||||
// set the value of the gravity constant to the one used by the FLOW simulator
|
// the "NOGRAV" keyword from Frontsim or setting the EnableGravity to false
|
||||||
|
// disables gravity, else the standard value of the gravity constant at sea level
|
||||||
|
// on earth is used
|
||||||
this->gravity_ = 0.0;
|
this->gravity_ = 0.0;
|
||||||
|
|
||||||
// the "NOGRAV" keyword from Frontsim disables gravity...
|
|
||||||
const auto& deck = simulator.vanguard().deck();
|
const auto& deck = simulator.vanguard().deck();
|
||||||
if (!deck.hasKeyword("NOGRAV") && EWOMS_GET_PARAM(TypeTag, bool, EnableGravity))
|
if (EWOMS_GET_PARAM(TypeTag, bool, EnableGravity))
|
||||||
this->gravity_[dim - 1] = 9.80665;
|
this->gravity_[dim - 1] = 9.80665;
|
||||||
|
if (deck.hasKeyword("NOGRAV"))
|
||||||
|
this->gravity_[dim - 1] = 0.0;
|
||||||
|
|
||||||
|
if (enableEclTuning_) {
|
||||||
|
// if support for the TUNING keyword is enabled, we get the initial time
|
||||||
|
// steping parameters from it instead of from command line parameters
|
||||||
|
const auto& schedule = simulator.vanguard().schedule();
|
||||||
|
const auto& tuning = schedule.getTuning();
|
||||||
|
initialTimeStepSize_ = tuning.getTSINIT(0);
|
||||||
|
maxTimeStepAfterWellEvent_ = tuning.getTMAXWC(0);
|
||||||
|
maxTimeStepSize_ = tuning.getTSMAXZ(0);
|
||||||
|
restartShrinkFactor_ = tuning.getTSFCNV(0);
|
||||||
|
minTimeStepSize_ = tuning.getTSMINZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
// deal with DRSDT
|
// deal with DRSDT
|
||||||
const auto& eclState = simulator.vanguard().eclState();
|
const auto& eclState = simulator.vanguard().eclState();
|
||||||
@ -683,6 +724,19 @@ public:
|
|||||||
updatePffDofData_();
|
updatePffDofData_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// react to TUNING changes
|
||||||
|
if (nextEpisodeIdx > 0
|
||||||
|
&& enableEclTuning_
|
||||||
|
&& events.hasEvent(Opm::ScheduleEvents::TUNING_CHANGE, nextEpisodeIdx))
|
||||||
|
{
|
||||||
|
const auto& tuning = schedule.getTuning();
|
||||||
|
initialTimeStepSize_ = tuning.getTSINIT(nextEpisodeIdx);
|
||||||
|
maxTimeStepAfterWellEvent_ = tuning.getTMAXWC(nextEpisodeIdx);
|
||||||
|
maxTimeStepSize_ = tuning.getTSMAXZ(nextEpisodeIdx);
|
||||||
|
restartShrinkFactor_ = tuning.getTSFCNV(nextEpisodeIdx);
|
||||||
|
minTimeStepSize_ = tuning.getTSMINZ(nextEpisodeIdx);
|
||||||
|
}
|
||||||
|
|
||||||
// Opm::TimeMap deals with points in time, so the number of time intervals (i.e.,
|
// Opm::TimeMap deals with points in time, so the number of time intervals (i.e.,
|
||||||
// report steps) is one less!
|
// report steps) is one less!
|
||||||
int numReportSteps = timeMap.size() - 1;
|
int numReportSteps = timeMap.size() - 1;
|
||||||
@ -696,17 +750,14 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scalar episodeLength = timeMap.getTimeStepLength(nextEpisodeIdx);
|
Scalar episodeLength = timeMap.getTimeStepLength(nextEpisodeIdx);
|
||||||
Scalar dt = episodeLength;
|
simulator.startNextEpisode(episodeLength);
|
||||||
if (nextEpisodeIdx == 0) {
|
|
||||||
// allow the size of the initial time step to be set via an external parameter
|
|
||||||
Scalar initialDt = EWOMS_GET_PARAM(TypeTag, Scalar, InitialTimeStepSize);
|
|
||||||
dt = std::min(dt, initialDt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextEpisodeIdx < numReportSteps) {
|
Scalar dt = limitNextTimeStepSize_(episodeLength);
|
||||||
simulator.startNextEpisode(episodeLength);
|
if (nextEpisodeIdx == 0)
|
||||||
simulator.setTimeStepSize(dt);
|
// allow the size of the initial time step to be set via an external parameter
|
||||||
}
|
dt = std::min(dt, initialTimeStepSize_);
|
||||||
|
|
||||||
|
simulator.setTimeStepSize(dt);
|
||||||
|
|
||||||
const bool invalidateFromHyst = updateHysteresis_();
|
const bool invalidateFromHyst = updateHysteresis_();
|
||||||
const bool invalidateFromMaxOilSat = updateMaxOilSaturation_();
|
const bool invalidateFromMaxOilSat = updateMaxOilSaturation_();
|
||||||
@ -1486,6 +1537,68 @@ public:
|
|||||||
bool hasFreeBoundaryConditions() const
|
bool hasFreeBoundaryConditions() const
|
||||||
{ return hasFreeBoundaryConditions_; }
|
{ return hasFreeBoundaryConditions_; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Propose the size of the next time step to the simulator.
|
||||||
|
*
|
||||||
|
* This method is only called if the Newton solver does converge, the simulator
|
||||||
|
* automatically cuts the time step in half without consultating this method again.
|
||||||
|
*/
|
||||||
|
Scalar nextTimeStepSize() const
|
||||||
|
{
|
||||||
|
int episodeIdx = this->simulator().episodeIndex();
|
||||||
|
|
||||||
|
// for the initial episode, we use a fixed time step size
|
||||||
|
if (episodeIdx < 0)
|
||||||
|
return initialTimeStepSize_;
|
||||||
|
|
||||||
|
// ask the newton method for a suggestion. This suggestion will be based on how
|
||||||
|
// well the previous time step converged. After that, apply the runtime time
|
||||||
|
// stepping constraints.
|
||||||
|
const auto& newtonMethod = this->model().newtonMethod();
|
||||||
|
const auto& simulator = this->simulator();
|
||||||
|
return limitNextTimeStepSize_(newtonMethod.suggestTimeStepSize(simulator.timeStepSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Called by Ewoms::Simulator in order to do a time integration on the model.
|
||||||
|
*/
|
||||||
|
void timeIntegration()
|
||||||
|
{
|
||||||
|
Simulator& simulator = this->simulator();
|
||||||
|
// if the time step size of the simulator is smaller than
|
||||||
|
// the specified minimum size and we're not going to finish
|
||||||
|
// the simulation or an episode, try with the minimum size.
|
||||||
|
if (simulator.timeStepSize() < minTimeStepSize_ &&
|
||||||
|
!simulator.episodeWillBeOver() &&
|
||||||
|
!simulator.willBeFinished())
|
||||||
|
{
|
||||||
|
simulator.setTimeStepSize(minTimeStepSize_);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < maxFails_; ++i) {
|
||||||
|
bool converged = this->model().update();
|
||||||
|
if (converged)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Scalar dt = simulator.timeStepSize();
|
||||||
|
Scalar nextDt = dt / restartShrinkFactor_;
|
||||||
|
if (nextDt < minTimeStepSize_)
|
||||||
|
break; // give up: we can't make the time step smaller anymore!
|
||||||
|
simulator.setTimeStepSize(nextDt);
|
||||||
|
|
||||||
|
// update failed
|
||||||
|
if (this->gridView().comm().rank() == 0)
|
||||||
|
std::cout << "Newton solver did not converge with "
|
||||||
|
<< "dt=" << dt << " seconds. Retrying with time step of "
|
||||||
|
<< nextDt << " seconds\n" << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Newton solver didn't converge after "
|
||||||
|
+std::to_string(maxFails_)+" time-step divisions. dt="
|
||||||
|
+std::to_string(double(simulator.timeStepSize())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool drsdtActive_() const
|
bool drsdtActive_() const
|
||||||
{
|
{
|
||||||
@ -2409,6 +2522,56 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this method applies the runtime constraints specified via the deck and/or command
|
||||||
|
// line parameters for the size of the next time step size.
|
||||||
|
Scalar limitNextTimeStepSize_(Scalar dtNext) const
|
||||||
|
{
|
||||||
|
const auto& events = this->simulator().vanguard().schedule().getEvents();
|
||||||
|
int episodeIdx = this->simulator().episodeIndex();
|
||||||
|
|
||||||
|
// first thing in the morning, limit the time step size to the maximum size
|
||||||
|
dtNext = std::min(dtNext, maxTimeStepSize_);
|
||||||
|
|
||||||
|
// if TUNING is enabled, limit the time step size after a tuning event to TSINIT
|
||||||
|
if (enableEclTuning_ && events.hasEvent(Opm::ScheduleEvents::TUNING_CHANGE, episodeIdx)) {
|
||||||
|
const auto& tuning = this->simulator().vanguard().schedule().getTuning();
|
||||||
|
return std::min(dtNext, tuning.getTSINIT(episodeIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// use at least slightly more than half of the maximum time step size by default
|
||||||
|
if (dtNext < maxTimeStepSize_ && maxTimeStepSize_ < dtNext*2)
|
||||||
|
dtNext = 1.01 * maxTimeStepSize_/2.0;
|
||||||
|
|
||||||
|
Scalar remainingEpisodeTime =
|
||||||
|
this->simulator().episodeStartTime()
|
||||||
|
+ this->simulator().episodeLength()
|
||||||
|
- this->simulator().time()
|
||||||
|
- this->simulator().timeStepSize();
|
||||||
|
|
||||||
|
// limit the time step size to the remaining time of the episode
|
||||||
|
dtNext = std::min(dtNext, remainingEpisodeTime);
|
||||||
|
|
||||||
|
// if we would have a small amount of time left over in the current episode, make
|
||||||
|
// two equal time steps instead of a big and a small one
|
||||||
|
if (dtNext < remainingEpisodeTime*(1.0 - 1e-5) && dtNext*1.25 > remainingEpisodeTime)
|
||||||
|
// note: limiting to the maximum time step size here is probably not strictly
|
||||||
|
// necessary, but it should not hurt and is more fool-proof
|
||||||
|
dtNext = std::min(maxTimeStepSize_, remainingEpisodeTime/2.0);
|
||||||
|
|
||||||
|
// if a well event occured, respect the limit for the maximum time step after
|
||||||
|
// that, too
|
||||||
|
int reportStepIdx = std::max(episodeIdx, 0);
|
||||||
|
bool wellEventOccured =
|
||||||
|
events.hasEvent(Opm::ScheduleEvents::NEW_WELL, reportStepIdx)
|
||||||
|
|| events.hasEvent(Opm::ScheduleEvents::PRODUCTION_UPDATE, reportStepIdx)
|
||||||
|
|| events.hasEvent(Opm::ScheduleEvents::INJECTION_UPDATE, reportStepIdx)
|
||||||
|
|| events.hasEvent(Opm::ScheduleEvents::WELL_STATUS_CHANGE, reportStepIdx);
|
||||||
|
if (episodeIdx >= 0 && wellEventOccured)
|
||||||
|
dtNext = std::min(dtNext, maxTimeStepAfterWellEvent_);
|
||||||
|
|
||||||
|
return dtNext;
|
||||||
|
}
|
||||||
|
|
||||||
static std::string briefDescription_;
|
static std::string briefDescription_;
|
||||||
|
|
||||||
std::vector<Scalar> porosity_;
|
std::vector<Scalar> porosity_;
|
||||||
@ -2460,6 +2623,7 @@ private:
|
|||||||
std::vector<bool> freebcYMinus_;
|
std::vector<bool> freebcYMinus_;
|
||||||
std::vector<bool> freebcZ_;
|
std::vector<bool> freebcZ_;
|
||||||
std::vector<bool> freebcZMinus_;
|
std::vector<bool> freebcZMinus_;
|
||||||
|
|
||||||
std::vector<RateVector> massratebcX_;
|
std::vector<RateVector> massratebcX_;
|
||||||
std::vector<RateVector> massratebcXMinus_;
|
std::vector<RateVector> massratebcXMinus_;
|
||||||
std::vector<RateVector> massratebcY_;
|
std::vector<RateVector> massratebcY_;
|
||||||
@ -2467,6 +2631,14 @@ private:
|
|||||||
std::vector<RateVector> massratebcZ_;
|
std::vector<RateVector> massratebcZ_;
|
||||||
std::vector<RateVector> massratebcZMinus_;
|
std::vector<RateVector> massratebcZMinus_;
|
||||||
|
|
||||||
|
// time stepping parameters
|
||||||
|
bool enableEclTuning_;
|
||||||
|
Scalar initialTimeStepSize_;
|
||||||
|
Scalar maxTimeStepAfterWellEvent_;
|
||||||
|
Scalar maxTimeStepSize_;
|
||||||
|
Scalar restartShrinkFactor_;
|
||||||
|
unsigned maxFails_;
|
||||||
|
Scalar minTimeStepSize_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class TypeTag>
|
template <class TypeTag>
|
||||||
|
Loading…
Reference in New Issue
Block a user