mirror of
https://github.com/OPM/opm-simulators.git
synced 2026-10-24 22:57:42 -05:00
Refactor logic based on iteration index
This commit is contained in:
1 parent
79eb2a0d8f
commit
61113726d8
32 files changed
+519
-271
No files matched your search
@@ -135,10 +135,9 @@ public:
|
||||
*/
|
||||
bool converged() const
|
||||
{
|
||||
if (errorPvFraction_ < relaxedMaxPvFraction_) {
|
||||
return (this->error_ < relaxedTolerance_ && errorSum_ < sumTolerance_) ;
|
||||
} else if (this->numIterations() > numStrictIterations_) {
|
||||
return (this->error_ < relaxedTolerance_ && errorSum_ < sumTolerance_) ;
|
||||
if (errorPvFraction_ < relaxedMaxPvFraction_
|
||||
|| this->problem().iterationContext().shouldRelax(numStrictIterations_)) {
|
||||
return (this->error_ < relaxedTolerance_ && errorSum_ < sumTolerance_);
|
||||
}
|
||||
|
||||
return this->error_ <= this->tolerance() && errorSum_ <= sumTolerance_;
|
||||
@@ -257,7 +256,7 @@ public:
|
||||
const SolutionVector& currentSolution)
|
||||
{
|
||||
ParentType::endIteration_(nextSolution, currentSolution);
|
||||
OpmLog::debug( "Newton iteration " + std::to_string(this->numIterations_) + ""
|
||||
OpmLog::debug( "Newton iteration " + std::to_string(this->problem().iterationContext().iteration()) + ""
|
||||
+ " error: " + std::to_string(double(this->error_))
|
||||
+ this->endIterMsg().str());
|
||||
this->endIterMsg().str("");
|
||||
|
||||
@@ -161,8 +161,7 @@ private:
|
||||
void initWellConnectionData();
|
||||
void initWellState();
|
||||
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt);
|
||||
void assemble(const double dt);
|
||||
|
||||
void calculateExplicitQuantities();
|
||||
};
|
||||
|
||||
@@ -232,15 +232,15 @@ beginIteration()
|
||||
elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
|
||||
}
|
||||
|
||||
assemble(simulator_.model().newtonMethod().numIterations(), simulator_.timeStepSize());
|
||||
assemble(simulator_.timeStepSize());
|
||||
}
|
||||
|
||||
template <typename TypeTag>
|
||||
void
|
||||
CompWellModel<TypeTag>::
|
||||
assemble(const int iterationIdx,
|
||||
const double dt)
|
||||
assemble(const double dt)
|
||||
{
|
||||
const int iterationIdx = simulator_.problem().iterationContext().iteration();
|
||||
|
||||
if (iterationIdx == 0) {
|
||||
this->calculateExplicitQuantities();
|
||||
|
||||
@@ -528,7 +528,7 @@ private:
|
||||
// before the first iteration of each time step, we need to update the
|
||||
// constraints. (i.e., we assume that constraints can be time dependent, but they
|
||||
// can't depend on the solution.)
|
||||
if (model_().newtonMethod().numIterations() == 0) {
|
||||
if (problem_().iterationContext().isFirstGlobalIteration()) {
|
||||
updateConstraintsMap_();
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ protected:
|
||||
if (elemCtx.enableStorageCache()) {
|
||||
const auto& model = elemCtx.model();
|
||||
const unsigned globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
|
||||
if (model.newtonMethod().numIterations() == 0 &&
|
||||
if (elemCtx.problem().iterationContext().isFirstGlobalIteration() &&
|
||||
!elemCtx.haveStashedIntensiveQuantities())
|
||||
{
|
||||
if (!elemCtx.problem().recycleFirstIterationStorage()) {
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <opm/models/discretization/common/fvbaseproperties.hh>
|
||||
#include <opm/models/discretization/common/restrictprolong.hh>
|
||||
|
||||
#include <opm/simulators/flow/NewtonIterationContext.hpp>
|
||||
|
||||
#include <opm/models/io/vtkmultiwriter.hh>
|
||||
#include <opm/models/io/restart.hpp>
|
||||
|
||||
@@ -816,8 +818,46 @@ public:
|
||||
VtkMultiWriter& defaultVtkWriter() const
|
||||
{ return *defaultVtkWriter_; }
|
||||
|
||||
/*!
|
||||
* \brief Returns the iteration context for iteration-dependent decisions.
|
||||
*/
|
||||
const NewtonIterationContext& iterationContext() const
|
||||
{ return iterationContext_; }
|
||||
|
||||
/*!
|
||||
* \brief Reset the iteration context for a new timestep.
|
||||
*/
|
||||
void resetIterationForNewTimestep()
|
||||
{ iterationContext_.resetForNewTimestep(); }
|
||||
|
||||
/*!
|
||||
* \brief Advance the iteration counter.
|
||||
*/
|
||||
void advanceIteration()
|
||||
{ iterationContext_.advanceIteration(); }
|
||||
|
||||
/*!
|
||||
* \brief Mark timestep initialization as complete.
|
||||
*/
|
||||
void markTimestepInitialized()
|
||||
{ iterationContext_.markTimestepInitialized(); }
|
||||
|
||||
private:
|
||||
// LocalContextGuard needs mutable access to save/restore the context
|
||||
// during NLDD domain-local solves.
|
||||
template<class P> friend class LocalContextGuard;
|
||||
|
||||
/*!
|
||||
* \brief Mutable access to the iteration context for LocalContextGuard.
|
||||
*/
|
||||
NewtonIterationContext& mutableIterationContext()
|
||||
{ return iterationContext_; }
|
||||
|
||||
public:
|
||||
|
||||
protected:
|
||||
Scalar nextTimeStepSize_;
|
||||
NewtonIterationContext iterationContext_;
|
||||
|
||||
bool enableVtkOutput_() const
|
||||
{ return Parameters::Get<Parameters::EnableVtkOutput>(); }
|
||||
|
||||
@@ -996,7 +996,7 @@ private:
|
||||
// but the starting state may not be identical to the start-of-step state.
|
||||
// Note that a full assembly must be done before local solves
|
||||
// otherwise this will be left un-updated.
|
||||
if (model_().newtonMethod().numIterations() == 0 && !isNlddLocalSolve) {
|
||||
if (problem_().iterationContext().isFirstGlobalIteration() && !isNlddLocalSolve) {
|
||||
// Need to update the storage cache.
|
||||
if (problem_().recycleFirstIterationStorage()) {
|
||||
// Assumes nothing have changed in the system which
|
||||
|
||||
@@ -192,7 +192,7 @@ protected:
|
||||
|
||||
// do not become grossly unphysical in a single iteration for the first few
|
||||
// iterations of a time step
|
||||
if (this->numIterations_ < 3) {
|
||||
if (this->problem().iterationContext().iteration() < 3) {
|
||||
// fugacities
|
||||
for (unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
|
||||
Scalar& val = nextValue[fugacity0Idx + compIdx];
|
||||
|
||||
@@ -122,7 +122,6 @@ public:
|
||||
, endIterMsgStream_(std::ostringstream::out)
|
||||
, error_(1e100)
|
||||
, lastError_(1e100)
|
||||
, numIterations_(0)
|
||||
, linearSolver_(simulator)
|
||||
, comm_(Dune::MPIHelper::getCommunicator())
|
||||
, convergenceWriter_(asImp_())
|
||||
@@ -179,23 +178,6 @@ public:
|
||||
const Model& model() const
|
||||
{ return simulator_.model(); }
|
||||
|
||||
/*!
|
||||
* \brief Returns the number of iterations done since the Newton method
|
||||
* was invoked.
|
||||
*/
|
||||
int numIterations() const
|
||||
{ return numIterations_; }
|
||||
|
||||
/*!
|
||||
* \brief Set the index of current iteration.
|
||||
*
|
||||
* Normally this does not need to be called, but if the non-linear solver is
|
||||
* implemented externally, it needs to be set in order for the model to do the Right
|
||||
* Thing (TM) while linearizing.
|
||||
*/
|
||||
void setIterationIndex(int value)
|
||||
{ numIterations_ = value; }
|
||||
|
||||
/*!
|
||||
* \brief Return the current tolerance at which the Newton method considers itself to
|
||||
* be converged.
|
||||
@@ -449,14 +431,16 @@ public:
|
||||
// conservative when increasing it. the rationale is
|
||||
// that we want to avoid failing in the next time
|
||||
// integration which would be quite expensive
|
||||
if (numIterations_ > params_.targetIterations_) {
|
||||
Scalar percent = Scalar(numIterations_ - params_.targetIterations_) / params_.targetIterations_;
|
||||
const int nit = problem().iterationContext().iteration();
|
||||
if (lastSolveFailed_ || nit > params_.targetIterations_) {
|
||||
const int overshoot = lastSolveFailed_ ? params_.targetIterations_ : nit - params_.targetIterations_;
|
||||
Scalar percent = Scalar(overshoot) / params_.targetIterations_;
|
||||
Scalar nextDt = std::max(problem().minTimeStepSize(),
|
||||
oldDt / (Scalar{1.0} + percent));
|
||||
return nextDt;
|
||||
}
|
||||
|
||||
Scalar percent = Scalar(params_.targetIterations_ - numIterations_) / params_.targetIterations_;
|
||||
Scalar percent = Scalar(params_.targetIterations_ - nit) / params_.targetIterations_;
|
||||
Scalar nextDt = std::max(problem().minTimeStepSize(),
|
||||
oldDt*(Scalar{1.0} + percent / Scalar{1.2}));
|
||||
return nextDt;
|
||||
@@ -515,7 +499,9 @@ protected:
|
||||
*/
|
||||
void begin_(const SolutionVector&)
|
||||
{
|
||||
numIterations_ = 0;
|
||||
lastSolveFailed_ = false;
|
||||
|
||||
problem().resetIterationForNewTimestep();
|
||||
|
||||
if (params_.writeConvergence_) {
|
||||
convergenceWriter_.beginTimeStep();
|
||||
@@ -755,7 +741,7 @@ protected:
|
||||
void endIteration_(const SolutionVector&,
|
||||
const SolutionVector&)
|
||||
{
|
||||
++numIterations_;
|
||||
problem().advanceIteration();
|
||||
|
||||
const auto& comm = simulator_.gridView().comm();
|
||||
bool succeeded = true;
|
||||
@@ -777,7 +763,7 @@ protected:
|
||||
}
|
||||
|
||||
if (asImp_().verbose_()) {
|
||||
std::cout << "Newton iteration " << numIterations_ << ""
|
||||
std::cout << "Newton iteration " << problem().iterationContext().iteration() << ""
|
||||
<< " error: " << error_
|
||||
<< endIterMsg().str() << "\n" << std::flush;
|
||||
}
|
||||
@@ -788,7 +774,7 @@ protected:
|
||||
*/
|
||||
bool proceed_() const
|
||||
{
|
||||
if (asImp_().numIterations() < 1) {
|
||||
if (problem().iterationContext().iteration() < 1) {
|
||||
return true; // we always do at least one full iteration
|
||||
}
|
||||
else if (asImp_().converged()) {
|
||||
@@ -796,7 +782,7 @@ protected:
|
||||
// do more iterations
|
||||
return false;
|
||||
}
|
||||
else if (asImp_().numIterations() >= params_.maxIterations_) {
|
||||
else if (problem().iterationContext().iteration() >= params_.maxIterations_) {
|
||||
// we have exceeded the allowed number of steps. If the
|
||||
// error was reduced by a factor of at least 4,
|
||||
// in the last iterations we proceed even if we are above
|
||||
@@ -824,7 +810,7 @@ protected:
|
||||
* This method is called _after_ end_()
|
||||
*/
|
||||
void failed_()
|
||||
{ numIterations_ = params_.targetIterations_ * 2; }
|
||||
{ lastSolveFailed_ = true; }
|
||||
|
||||
/*!
|
||||
* \brief Called if the Newton method was successful.
|
||||
@@ -850,9 +836,8 @@ protected:
|
||||
Scalar lastError_;
|
||||
NewtonMethodParams<Scalar> params_;
|
||||
|
||||
// actual number of iterations done so far
|
||||
int numIterations_;
|
||||
|
||||
// Flags that the last solve failed, triggering a timestep reduction.
|
||||
bool lastSolveFailed_ = false;
|
||||
// the linear solver
|
||||
LinearSolverBackend linearSolver_;
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ protected:
|
||||
nextValue -= update;
|
||||
|
||||
// do not clamp anything after 4 iterations
|
||||
if (this->numIterations_ > 4) {
|
||||
if (this->problem().iterationContext().iteration() > 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,6 @@ public:
|
||||
SimulatorReportSingle prepareStep(const SimulatorTimerInterface& timer);
|
||||
|
||||
void initialLinearization(SimulatorReportSingle& report,
|
||||
const int iteration,
|
||||
const int minIter,
|
||||
const int maxIter,
|
||||
const SimulatorTimerInterface& timer);
|
||||
@@ -164,22 +163,19 @@ public:
|
||||
/// This model will perform a Newton-Raphson update, changing reservoir_state
|
||||
/// and well_state. It will also use the nonlinear_solver to do relaxation of
|
||||
/// updates if necessary.
|
||||
/// \param[in] iteration should be 0 for the first call of a new timestep
|
||||
/// The method increases the counter in iterationContext at the end of the method.
|
||||
/// \param[in] timer simulation timer
|
||||
/// \param[in] nonlinear_solver nonlinear solver used (for oscillation/relaxation control)
|
||||
template <class NonlinearSolverType>
|
||||
SimulatorReportSingle nonlinearIteration(const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
SimulatorReportSingle nonlinearIteration(const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver);
|
||||
|
||||
template <class NonlinearSolverType>
|
||||
SimulatorReportSingle nonlinearIterationNewton(const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
SimulatorReportSingle nonlinearIterationNewton(const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver);
|
||||
|
||||
/// Assemble the residual and Jacobian of the nonlinear system.
|
||||
SimulatorReportSingle assembleReservoir(const SimulatorTimerInterface& /* timer */,
|
||||
const int iterationIdx);
|
||||
SimulatorReportSingle assembleReservoir(const SimulatorTimerInterface& timer);
|
||||
|
||||
// compute the "relative" change of the solution between time steps
|
||||
Scalar relativeChange() const;
|
||||
@@ -236,8 +232,7 @@ public:
|
||||
void convergencePerCell(const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
const double tol_cnv,
|
||||
const double tol_cnv_energy,
|
||||
const int iteration);
|
||||
const double tol_cnv_energy);
|
||||
|
||||
void updateTUNING(const Tuning& tuning);
|
||||
void updateTUNINGDP(const TuningDp& tuning_dp);
|
||||
@@ -245,7 +240,6 @@ public:
|
||||
ConvergenceReport
|
||||
getReservoirConvergence(const double reportTime,
|
||||
const double dt,
|
||||
const int iteration,
|
||||
const int maxIter,
|
||||
std::vector<Scalar>& B_avg,
|
||||
std::vector<Scalar>& residual_norms);
|
||||
@@ -253,12 +247,10 @@ public:
|
||||
/// Compute convergence based on total mass balance (tol_mb) and maximum
|
||||
/// residual mass balance (tol_cnv).
|
||||
/// \param[in] timer simulation timer
|
||||
/// \param[in] iteration current iteration number
|
||||
/// \param[in] maxIter maximum number of iterations
|
||||
/// \param[out] residual_norms CNV residuals by phase
|
||||
ConvergenceReport
|
||||
getConvergence(const SimulatorTimerInterface& timer,
|
||||
const int iteration,
|
||||
const int maxIter,
|
||||
std::vector<Scalar>& residual_norms);
|
||||
|
||||
@@ -298,6 +290,13 @@ public:
|
||||
const std::vector<StepReport>& stepReports() const
|
||||
{ return convergence_reports_; }
|
||||
|
||||
/// Remove the last convergence report entry and residual norms history entry.
|
||||
void popLastConvergenceReport()
|
||||
{
|
||||
convergence_reports_.back().report.pop_back();
|
||||
residual_norms_history_.pop_back();
|
||||
}
|
||||
|
||||
void writePartitions(const std::filesystem::path& odir) const;
|
||||
|
||||
/// return the StandardWells object
|
||||
|
||||
@@ -237,27 +237,33 @@ public:
|
||||
|
||||
//! \brief Do one non-linear NLDD iteration.
|
||||
template <class NonlinearSolverType>
|
||||
SimulatorReportSingle nonlinearIterationNldd(const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
SimulatorReportSingle nonlinearIterationNldd(const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver)
|
||||
{
|
||||
// ----------- Set up reports and timer -----------
|
||||
SimulatorReportSingle report;
|
||||
|
||||
if (iteration < model_.param().nldd_num_initial_newton_iter_) {
|
||||
report = model_.nonlinearIterationNewton(iteration,
|
||||
timer,
|
||||
nonlinear_solver);
|
||||
// When nldd_num_initial_newton_iter_ == 0, NLDD starts immediately but wells are still
|
||||
// properly initialized via NewtonIterationContext::needsTimestepInit() which
|
||||
// ensures prepareTimeStep() is called during the initial global linearization.
|
||||
// If nldd_num_initial_newton_iter_ > 0, run global Newton iterations first.
|
||||
// TODO: Investigate whether wells and other subsystems are correctly initialized
|
||||
// for the following NLDD iteration.
|
||||
if (model_.simulator().problem().iterationContext().iteration() < model_.param().nldd_num_initial_newton_iter_) {
|
||||
report = model_.nonlinearIterationNewton(timer, nonlinear_solver);
|
||||
return report;
|
||||
}
|
||||
|
||||
model_.initialLinearization(report, iteration, model_.param().newton_min_iter_,
|
||||
model_.initialLinearization(report, model_.param().newton_min_iter_,
|
||||
model_.param().newton_max_iter_, timer);
|
||||
|
||||
if (report.converged) {
|
||||
return report;
|
||||
}
|
||||
|
||||
// Remove the initial linearization entry; the final Newton step will
|
||||
// record its own, keeping one entry per outer iteration.
|
||||
model_.popLastConvergenceReport();
|
||||
|
||||
// ----------- If not converged, do an NLDD iteration -----------
|
||||
Dune::Timer localSolveTimer;
|
||||
Dune::Timer detailTimer;
|
||||
@@ -282,7 +288,7 @@ public:
|
||||
detailTimer.reset();
|
||||
detailTimer.start();
|
||||
|
||||
domain_needs_solving_[domain_index] = checkIfSubdomainNeedsSolving(domain, iteration);
|
||||
domain_needs_solving_[domain_index] = checkIfSubdomainNeedsSolving(domain);
|
||||
|
||||
updateMobilities(domain);
|
||||
|
||||
@@ -295,12 +301,12 @@ public:
|
||||
switch (model_.param().local_solve_approach_) {
|
||||
case DomainSolveApproach::Jacobi:
|
||||
solveDomainJacobi(solution, locally_solved, local_report, logger,
|
||||
iteration, timer, domain);
|
||||
timer, domain);
|
||||
break;
|
||||
default:
|
||||
case DomainSolveApproach::GaussSeidel:
|
||||
solveDomainGaussSeidel(solution, locally_solved, local_report, logger,
|
||||
iteration, timer, domain);
|
||||
timer, domain);
|
||||
break;
|
||||
}
|
||||
// This should have updated the global matrix to be
|
||||
@@ -408,12 +414,8 @@ public:
|
||||
local_reports_accumulated_.success.total_time += total_local_solve_time;
|
||||
local_reports_accumulated_.success.pre_post_time += detailTimer.stop();
|
||||
|
||||
// Finish with a Newton step.
|
||||
// Note that the "iteration + 100" is a simple way to avoid entering
|
||||
// "if (iteration == 0)" and similar blocks, and also makes it a little
|
||||
// easier to spot the iteration residuals in the DBG file. A more sophisticated
|
||||
// approach can be done later.
|
||||
auto rep = model_.nonlinearIterationNewton(iteration + 100, timer, nonlinear_solver);
|
||||
// Finish with a global Newton step.
|
||||
auto rep = model_.nonlinearIterationNewton(timer, nonlinear_solver);
|
||||
report += rep;
|
||||
if (rep.converged) {
|
||||
report.converged = true;
|
||||
@@ -483,26 +485,24 @@ private:
|
||||
const SimulatorTimerInterface& timer,
|
||||
SimulatorReportSingle& local_report,
|
||||
DeferredLogger& logger,
|
||||
[[maybe_unused]] const int global_iteration,
|
||||
const bool initial_assembly_required)
|
||||
{
|
||||
auto& modelSimulator = model_.simulator();
|
||||
Dune::Timer detailTimer;
|
||||
|
||||
modelSimulator.model().newtonMethod().setIterationIndex(0);
|
||||
// RAII guard: local iteration context for this domain, restored on scope exit.
|
||||
LocalContextGuard localCtxGuard(modelSimulator.problem());
|
||||
auto& localCtx = localCtxGuard.context();
|
||||
|
||||
// When called, if assembly has already been performed
|
||||
// with the initial values, we only need to check
|
||||
// for local convergence. Otherwise, we must do a local
|
||||
// assembly.
|
||||
int iter = 0;
|
||||
if (initial_assembly_required) {
|
||||
detailTimer.start();
|
||||
modelSimulator.model().newtonMethod().setIterationIndex(iter);
|
||||
// TODO: we should have a beginIterationLocal function()
|
||||
// only handling the well model for now
|
||||
wellModel_.assemble(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
wellModel_.assemble(modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
const double tt0 = detailTimer.stop();
|
||||
local_report.assemble_time += tt0;
|
||||
@@ -517,7 +517,7 @@ private:
|
||||
detailTimer.reset();
|
||||
detailTimer.start();
|
||||
std::vector<Scalar> resnorms;
|
||||
auto convreport = this->getDomainConvergence(domain, timer, 0, logger, resnorms);
|
||||
auto convreport = this->getDomainConvergence(domain, timer, logger, resnorms);
|
||||
local_report.update_time += detailTimer.stop();
|
||||
if (convreport.converged()) {
|
||||
// TODO: set more info, timing etc.
|
||||
@@ -565,8 +565,8 @@ private:
|
||||
local_report.total_linear_iterations = domain_linsolvers_[domain.index].iterations();
|
||||
modelSimulator.problem().endIteration();
|
||||
local_report.converged = false;
|
||||
local_report.total_newton_iterations = iter;
|
||||
local_report.total_linearizations += iter;
|
||||
local_report.total_newton_iterations = localCtx.iteration();
|
||||
local_report.total_linearizations += localCtx.iteration();
|
||||
return convreport;
|
||||
}
|
||||
model_.wellModel().postSolveDomain(x, domain);
|
||||
@@ -586,13 +586,11 @@ private:
|
||||
// Assemble well and reservoir.
|
||||
detailTimer.reset();
|
||||
detailTimer.start();
|
||||
++iter;
|
||||
modelSimulator.model().newtonMethod().setIterationIndex(iter);
|
||||
localCtx.advanceIteration();
|
||||
// TODO: we should have a beginIterationLocal function()
|
||||
// only handling the well model for now
|
||||
// Assemble reservoir locally.
|
||||
wellModel_.assemble(modelSimulator.model().newtonMethod().numIterations(),
|
||||
modelSimulator.timeStepSize(),
|
||||
wellModel_.assemble(modelSimulator.timeStepSize(),
|
||||
domain);
|
||||
const double tt3 = detailTimer.stop();
|
||||
local_report.assemble_time += tt3;
|
||||
@@ -606,7 +604,7 @@ private:
|
||||
detailTimer.reset();
|
||||
detailTimer.start();
|
||||
resnorms.clear();
|
||||
convreport = this->getDomainConvergence(domain, timer, iter, logger, resnorms);
|
||||
convreport = this->getDomainConvergence(domain, timer, logger, resnorms);
|
||||
convergence_history.push_back(resnorms);
|
||||
local_report.update_time += detailTimer.stop();
|
||||
|
||||
@@ -626,20 +624,20 @@ private:
|
||||
bool oscillate = false;
|
||||
bool stagnate = false;
|
||||
const auto num_residuals = convergence_history.front().size();
|
||||
detail::detectOscillations(convergence_history, iter, num_residuals,
|
||||
detail::detectOscillations(convergence_history, localCtx.iteration(), num_residuals,
|
||||
Scalar{0.2}, 1, oscillate, stagnate);
|
||||
if (oscillate) {
|
||||
damping_factor *= 0.85;
|
||||
logger.debug(fmt::format(fmt::runtime("| Damping factor is now {}"), damping_factor));
|
||||
}
|
||||
}
|
||||
} while (!convreport.converged() && iter <= max_iter);
|
||||
} while (!convreport.converged() && localCtx.iteration() <= max_iter);
|
||||
|
||||
modelSimulator.problem().endIteration();
|
||||
|
||||
local_report.converged = convreport.converged();
|
||||
local_report.total_newton_iterations = iter;
|
||||
local_report.total_linearizations += iter;
|
||||
local_report.total_newton_iterations = localCtx.iteration();
|
||||
local_report.total_linearizations += localCtx.iteration();
|
||||
// TODO: set more info, timing etc.
|
||||
return convreport;
|
||||
}
|
||||
@@ -766,7 +764,6 @@ private:
|
||||
|
||||
ConvergenceReport getDomainReservoirConvergence(const double reportTime,
|
||||
const double dt,
|
||||
const int iteration,
|
||||
const Domain& domain,
|
||||
DeferredLogger& logger,
|
||||
std::vector<Scalar>& B_avg,
|
||||
@@ -774,6 +771,8 @@ private:
|
||||
{
|
||||
using Vector = std::vector<Scalar>;
|
||||
|
||||
const auto& iterCtx = model_.simulator().problem().iterationContext();
|
||||
|
||||
const int numComp = numEq;
|
||||
Vector R_sum(numComp, 0.0 );
|
||||
Vector maxCoeff(numComp, std::numeric_limits<Scalar>::lowest() );
|
||||
@@ -794,11 +793,11 @@ private:
|
||||
const int maxLocalIter = model_.param().max_local_solve_iterations_;
|
||||
|
||||
const bool relax_final_iteration_mb =
|
||||
model_.param().min_strict_mb_iter_ < 0 && iteration == maxLocalIter;
|
||||
model_.param().min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxLocalIter;
|
||||
|
||||
const bool relax_iter_mb =
|
||||
model_.param().min_strict_mb_iter_ >= 0 &&
|
||||
iteration >= model_.param().min_strict_mb_iter_;
|
||||
iterCtx.shouldRelax(model_.param().min_strict_mb_iter_);
|
||||
|
||||
const bool use_relaxed_mb = relax_final_iteration_mb || relax_iter_mb;
|
||||
|
||||
@@ -810,11 +809,11 @@ private:
|
||||
// tolerance after the given number of iterations. We also use relaxed tolerances
|
||||
// for cells with total pore-volume less than relaxed_max_pv_fraction_.
|
||||
const bool relax_final_iteration_cnv =
|
||||
model_.param().min_strict_cnv_iter_ < 0 && iteration == maxLocalIter;
|
||||
model_.param().min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxLocalIter;
|
||||
|
||||
const bool relax_iter_cnv =
|
||||
model_.param().min_strict_cnv_iter_ >= 0 &&
|
||||
iteration >= model_.param().min_strict_cnv_iter_;
|
||||
iterCtx.shouldRelax(model_.param().min_strict_cnv_iter_);
|
||||
|
||||
const bool relax_pv_fraction_cnv =
|
||||
cnvErrorPvFraction < model_.param().relaxed_max_pv_fraction_;
|
||||
@@ -869,9 +868,9 @@ private:
|
||||
}
|
||||
|
||||
// Output of residuals. If converged at initial state, log nothing.
|
||||
const bool converged_at_initial_state = (report.converged() && iteration == 0);
|
||||
const bool converged_at_initial_state = (report.converged() && iterCtx.iteration() == 0);
|
||||
if (!converged_at_initial_state) {
|
||||
if (iteration == 0) {
|
||||
if (iterCtx.iteration() == 0) {
|
||||
// Log header.
|
||||
std::string msg = fmt::format("Domain {} on rank {}, size {}, containing cell {}\n| Iter",
|
||||
domain.index, this->rank_, domain.cells.size(), domain.cells[0]);
|
||||
@@ -892,7 +891,7 @@ private:
|
||||
ss << "| ";
|
||||
const std::streamsize oprec = ss.precision(3);
|
||||
const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
|
||||
ss << std::setw(4) << iteration;
|
||||
ss << std::setw(4) << iterCtx.iteration();
|
||||
for (int compIdx = 0; compIdx < numComp; ++compIdx) {
|
||||
ss << std::setw(11) << mass_balance_residual[compIdx];
|
||||
}
|
||||
@@ -909,7 +908,6 @@ private:
|
||||
|
||||
ConvergenceReport getDomainConvergence(const Domain& domain,
|
||||
const SimulatorTimerInterface& timer,
|
||||
const int iteration,
|
||||
DeferredLogger& logger,
|
||||
std::vector<Scalar>& residual_norms)
|
||||
{
|
||||
@@ -917,7 +915,6 @@ private:
|
||||
std::vector<Scalar> B_avg(numEq, 0.0);
|
||||
auto report = this->getDomainReservoirConvergence(timer.simulationTimeElapsed(),
|
||||
timer.currentStepLength(),
|
||||
iteration,
|
||||
domain,
|
||||
logger,
|
||||
B_avg,
|
||||
@@ -996,13 +993,12 @@ private:
|
||||
GlobalEqVector& locally_solved,
|
||||
SimulatorReportSingle& local_report,
|
||||
DeferredLogger& logger,
|
||||
const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
const Domain& domain)
|
||||
{
|
||||
auto initial_local_well_primary_vars = wellModel_.getPrimaryVarsDomain(domain.index);
|
||||
auto initial_local_solution = Details::extractVector(solution, domain.cells);
|
||||
auto convrep = solveDomain(domain, timer, local_report, logger, iteration, false);
|
||||
auto convrep = solveDomain(domain, timer, local_report, logger, false);
|
||||
if (local_report.converged) {
|
||||
auto local_solution = Details::extractVector(solution, domain.cells);
|
||||
Details::setGlobal(local_solution, domain.cells, locally_solved);
|
||||
@@ -1020,13 +1016,12 @@ private:
|
||||
GlobalEqVector& locally_solved,
|
||||
SimulatorReportSingle& local_report,
|
||||
DeferredLogger& logger,
|
||||
const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
const Domain& domain)
|
||||
{
|
||||
auto initial_local_well_primary_vars = wellModel_.getPrimaryVarsDomain(domain.index);
|
||||
auto initial_local_solution = Details::extractVector(solution, domain.cells);
|
||||
auto convrep = solveDomain(domain, timer, local_report, logger, iteration, true);
|
||||
auto convrep = solveDomain(domain, timer, local_report, logger, true);
|
||||
if (!local_report.converged) {
|
||||
// We look at the detailed convergence report to evaluate
|
||||
// if we should accept the unconverged solution.
|
||||
@@ -1169,7 +1164,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
bool checkIfSubdomainNeedsSolving(const Domain& domain, const int iteration)
|
||||
bool checkIfSubdomainNeedsSolving(const Domain& domain)
|
||||
{
|
||||
if (domain.skip) {
|
||||
return false;
|
||||
@@ -1186,8 +1181,8 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
// Skip mobility check on first iteration
|
||||
if (iteration == 0) {
|
||||
// Skip mobility check on first global iteration (no previous mobilities to compare)
|
||||
if (model_.simulator().problem().iterationContext().isFirstGlobalIteration()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,13 +101,14 @@ prepareStep(const SimulatorTimerInterface& timer)
|
||||
simulator_.model().advanceTimeLevel();
|
||||
}
|
||||
|
||||
// Set the timestep size, episode index, and non-linear iteration index
|
||||
// for the model explicitly. The model needs to know the report step/episode index
|
||||
// because of timing dependent data despite the fact that flow uses its
|
||||
// own time stepper. (The length of the episode does not matter, though.)
|
||||
// Set the timestep size and episode index for the model explicitly.
|
||||
// The model needs to know the report step/episode index because of
|
||||
// timing dependent data despite the fact that flow uses its own time stepper.
|
||||
// (The length of the episode does not matter, though.)
|
||||
simulator_.setTime(timer.simulationTimeElapsed());
|
||||
simulator_.setTimeStepSize(timer.currentStepLength());
|
||||
simulator_.model().newtonMethod().setIterationIndex(0);
|
||||
|
||||
simulator_.problem().resetIterationForNewTimestep();
|
||||
|
||||
simulator_.problem().beginTimeStep();
|
||||
|
||||
@@ -153,7 +154,6 @@ template <class TypeTag>
|
||||
void
|
||||
BlackoilModel<TypeTag>::
|
||||
initialLinearization(SimulatorReportSingle& report,
|
||||
const int iteration,
|
||||
const int minIter,
|
||||
const int maxIter,
|
||||
const SimulatorTimerInterface& timer)
|
||||
@@ -167,8 +167,11 @@ initialLinearization(SimulatorReportSingle& report,
|
||||
|
||||
// ----------- Assemble -----------
|
||||
try {
|
||||
report += assembleReservoir(timer, iteration);
|
||||
report += assembleReservoir(timer);
|
||||
report.assemble_time += perfTimer.stop();
|
||||
// Mark timestep initialized after assembleReservoir(), because the well model's
|
||||
// assemble() also uses needsTimestepInit() to trigger prepareTimeStep().
|
||||
simulator_.problem().markTimestepInitialized();
|
||||
}
|
||||
catch (...) {
|
||||
report.assemble_time += perfTimer.stop();
|
||||
@@ -182,8 +185,9 @@ initialLinearization(SimulatorReportSingle& report,
|
||||
perfTimer.start();
|
||||
// the step is not considered converged until at least minIter iterations is done
|
||||
{
|
||||
auto convrep = getConvergence(timer, iteration, maxIter, residual_norms);
|
||||
report.converged = convrep.converged() && iteration >= minIter;
|
||||
auto convrep = getConvergence(timer, maxIter, residual_norms);
|
||||
report.converged = convrep.converged() &&
|
||||
simulator_.problem().iterationContext().iteration() >= minIter;
|
||||
ConvergenceReport::Severity severity = convrep.severityOfWorstFailure();
|
||||
convergence_reports_.back().report.push_back(std::move(convrep));
|
||||
|
||||
@@ -211,13 +215,13 @@ template <class TypeTag>
|
||||
template <class NonlinearSolverType>
|
||||
SimulatorReportSingle
|
||||
BlackoilModel<TypeTag>::
|
||||
nonlinearIteration(const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
nonlinearIteration(const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver)
|
||||
{
|
||||
if (iteration == 0) {
|
||||
// For each iteration we store in a vector the norms of the residual of
|
||||
// the mass balance for each active phase, the well flux and the well equations.
|
||||
// Model-level timestep initialization (once per timestep).
|
||||
// markTimestepInitialized() is called later in initialLinearization(),
|
||||
// after assembleReservoir() has triggered the well model's prepareTimeStep().
|
||||
if (simulator_.problem().iterationContext().needsTimestepInit()) {
|
||||
residual_norms_history_.clear();
|
||||
conv_monitor_.reset();
|
||||
current_relaxation_ = 1.0;
|
||||
@@ -229,19 +233,16 @@ nonlinearIteration(const int iteration,
|
||||
SimulatorReportSingle result;
|
||||
if (this->param_.nonlinear_solver_ != "nldd")
|
||||
{
|
||||
result = this->nonlinearIterationNewton(iteration,
|
||||
timer,
|
||||
nonlinear_solver);
|
||||
result = this->nonlinearIterationNewton(timer, nonlinear_solver);
|
||||
}
|
||||
else {
|
||||
result = this->nlddSolver_->nonlinearIterationNldd(iteration,
|
||||
timer,
|
||||
nonlinear_solver);
|
||||
result = this->nlddSolver_->nonlinearIterationNldd(timer, nonlinear_solver);
|
||||
}
|
||||
|
||||
auto& rst_conv = simulator_.problem().eclWriter().mutableOutputModule().getConv();
|
||||
rst_conv.update(simulator_.model().linearizer().residual());
|
||||
|
||||
simulator_.problem().advanceIteration();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -249,8 +250,7 @@ template <class TypeTag>
|
||||
template <class NonlinearSolverType>
|
||||
SimulatorReportSingle
|
||||
BlackoilModel<TypeTag>::
|
||||
nonlinearIterationNewton(const int iteration,
|
||||
const SimulatorTimerInterface& timer,
|
||||
nonlinearIterationNewton(const SimulatorTimerInterface& timer,
|
||||
NonlinearSolverType& nonlinear_solver)
|
||||
{
|
||||
// ----------- Set up reports and timer -----------
|
||||
@@ -258,7 +258,6 @@ nonlinearIterationNewton(const int iteration,
|
||||
Dune::Timer perfTimer;
|
||||
|
||||
this->initialLinearization(report,
|
||||
iteration,
|
||||
this->param_.newton_min_iter_,
|
||||
this->param_.newton_max_iter_,
|
||||
timer);
|
||||
@@ -341,11 +340,9 @@ nonlinearIterationNewton(const int iteration,
|
||||
template <class TypeTag>
|
||||
SimulatorReportSingle
|
||||
BlackoilModel<TypeTag>::
|
||||
assembleReservoir(const SimulatorTimerInterface& /* timer */,
|
||||
const int iterationIdx)
|
||||
assembleReservoir(const SimulatorTimerInterface& /* timer */)
|
||||
{
|
||||
// -------- Mass balance equations --------
|
||||
simulator_.model().newtonMethod().setIterationIndex(iterationIdx);
|
||||
simulator_.problem().beginIteration();
|
||||
simulator_.model().linearizer().linearizeDomain();
|
||||
simulator_.problem().endIteration();
|
||||
@@ -850,7 +847,6 @@ ConvergenceReport
|
||||
BlackoilModel<TypeTag>::
|
||||
getReservoirConvergence(const double reportTime,
|
||||
const double dt,
|
||||
const int iteration,
|
||||
const int maxIter,
|
||||
std::vector<Scalar>& B_avg,
|
||||
std::vector<Scalar>& residual_norms)
|
||||
@@ -858,6 +854,8 @@ getReservoirConvergence(const double reportTime,
|
||||
OPM_TIMEBLOCK(getReservoirConvergence);
|
||||
using Vector = std::vector<Scalar>;
|
||||
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
|
||||
ConvergenceReport report{reportTime};
|
||||
|
||||
const int numComp = numEq;
|
||||
@@ -888,11 +886,10 @@ getReservoirConvergence(const double reportTime,
|
||||
// iterations. For positive values we use the relaxed tolerance
|
||||
// after the given number of iterations
|
||||
const bool relax_final_iteration_mb =
|
||||
this->param_.min_strict_mb_iter_ < 0 && iteration == maxIter;
|
||||
this->param_.min_strict_mb_iter_ < 0 && iterCtx.iteration() == maxIter;
|
||||
|
||||
const bool relax_iter_mb =
|
||||
this->param_.min_strict_mb_iter_ >= 0 &&
|
||||
iteration >= this->param_.min_strict_mb_iter_;
|
||||
const bool relax_iter_mb = this->param_.min_strict_mb_iter_ >= 0 &&
|
||||
iterCtx.shouldRelax(this->param_.min_strict_mb_iter_);
|
||||
|
||||
const bool use_relaxed_mb = relax_final_iteration_mb
|
||||
|| relax_iter_mb;
|
||||
@@ -904,11 +901,10 @@ getReservoirConvergence(const double reportTime,
|
||||
// pore-volume less than relaxed_max_pv_fraction_. Default
|
||||
// value of relaxed_max_pv_fraction_ is 0.03
|
||||
const bool relax_final_iteration_cnv =
|
||||
this->param_.min_strict_cnv_iter_ < 0 && iteration == maxIter;
|
||||
this->param_.min_strict_cnv_iter_ < 0 && iterCtx.iteration() == maxIter;
|
||||
|
||||
const bool relax_iter_cnv =
|
||||
this->param_.min_strict_cnv_iter_ >= 0 &&
|
||||
iteration >= this->param_.min_strict_cnv_iter_;
|
||||
const bool relax_iter_cnv = this->param_.min_strict_cnv_iter_ >= 0 &&
|
||||
iterCtx.shouldRelax(this->param_.min_strict_cnv_iter_);
|
||||
|
||||
// Note trailing parentheses here, just before the final
|
||||
// semicolon. This is an immediately invoked function
|
||||
@@ -935,7 +931,7 @@ getReservoirConvergence(const double reportTime,
|
||||
const bool use_drv_tol = this->param_.tolerance_max_drv_ > 0.0;
|
||||
const bool use_dsol_tol = use_dp_tol || use_ds_tol || use_drs_tol || use_drv_tol;
|
||||
bool relax_dsol_cnv = false;
|
||||
if (iteration > 0 && use_dsol_tol) {
|
||||
if (!iterCtx.isFirstGlobalIteration() && use_dsol_tol) {
|
||||
maxSolUpd = getMaxSolutionUpdate(cnvSplitData.ixCells);
|
||||
relax_dsol_cnv =
|
||||
(!use_dp_tol || (maxSolUpd.dPMax > 0.0 && maxSolUpd.dPMax < this->param_.tolerance_max_dp_)) &&
|
||||
@@ -1014,12 +1010,12 @@ getReservoirConvergence(const double reportTime,
|
||||
}
|
||||
|
||||
// Compute the Newton convergence per cell.
|
||||
this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy, iteration);
|
||||
this->convergencePerCell(B_avg, dt, tol_cnv, tol_cnv_energy);
|
||||
|
||||
// Output of residuals.
|
||||
if (this->terminal_output_) {
|
||||
// Only rank 0 does print to std::cout
|
||||
if (iteration == 0) {
|
||||
if (iterCtx.isFirstGlobalIteration()) {
|
||||
std::string msg = "Iter";
|
||||
for (int compIdx = 0; compIdx < numComp; ++compIdx) {
|
||||
msg += " MB(";
|
||||
@@ -1050,7 +1046,7 @@ getReservoirConvergence(const double reportTime,
|
||||
const std::streamsize oprec = ss.precision(3);
|
||||
const std::ios::fmtflags oflags = ss.setf(std::ios::scientific);
|
||||
|
||||
ss << std::setw(4) << iteration;
|
||||
ss << std::setw(4) << iterCtx.iteration();
|
||||
for (int compIdx = 0; compIdx < numComp; ++compIdx) {
|
||||
ss << std::setw(11) << mass_balance_residual[compIdx];
|
||||
}
|
||||
@@ -1065,7 +1061,7 @@ getReservoirConvergence(const double reportTime,
|
||||
if (!use_tol) {
|
||||
return;
|
||||
}
|
||||
if (iteration == 0 || dsol <= 0.0) {
|
||||
if (iterCtx.isFirstGlobalIteration() || dsol <= 0.0) {
|
||||
ss << std::string(5, ' ') << "-" << std::string(5, ' ');
|
||||
}
|
||||
else {
|
||||
@@ -1099,15 +1095,14 @@ BlackoilModel<TypeTag>::
|
||||
convergencePerCell(const std::vector<Scalar>& B_avg,
|
||||
const double dt,
|
||||
const double tol_cnv,
|
||||
const double tol_cnv_energy,
|
||||
const int iteration)
|
||||
const double tol_cnv_energy)
|
||||
{
|
||||
auto& rst_conv = simulator_.problem().eclWriter().mutableOutputModule().getConv();
|
||||
if (!rst_conv.hasConv()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (iteration == 0) {
|
||||
if (simulator_.problem().iterationContext().isFirstGlobalIteration()) {
|
||||
rst_conv.prepareConv();
|
||||
}
|
||||
|
||||
@@ -1144,7 +1139,6 @@ template <class TypeTag>
|
||||
ConvergenceReport
|
||||
BlackoilModel<TypeTag>::
|
||||
getConvergence(const SimulatorTimerInterface& timer,
|
||||
const int iteration,
|
||||
const int maxIter,
|
||||
std::vector<Scalar>& residual_norms)
|
||||
{
|
||||
@@ -1153,14 +1147,14 @@ getConvergence(const SimulatorTimerInterface& timer,
|
||||
std::vector<Scalar> B_avg(numEq, 0.0);
|
||||
auto report = getReservoirConvergence(timer.simulationTimeElapsed(),
|
||||
timer.currentStepLength(),
|
||||
iteration, maxIter, B_avg, residual_norms);
|
||||
maxIter, B_avg, residual_norms);
|
||||
{
|
||||
OPM_TIMEBLOCK(getWellConvergence);
|
||||
report += wellModel().getWellConvergence(B_avg,
|
||||
/*checkWellGroupControlsAndNetwork*/report.converged());
|
||||
}
|
||||
|
||||
conv_monitor_.checkPenaltyCard(report, iteration);
|
||||
conv_monitor_.checkPenaltyCard(report, simulator_.problem().iterationContext().iteration());
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2026 SINTEF AS.
|
||||
|
||||
This file is part of the Open Porous Media project (OPM).
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef OPM_NEWTON_ITERATION_CONTEXT_HPP
|
||||
#define OPM_NEWTON_ITERATION_CONTEXT_HPP
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
/// \brief Context for iteration-dependent decisions in the Newton solver.
|
||||
///
|
||||
/// Provides explicit state for iteration-dependent behavior:
|
||||
///
|
||||
/// - Global iteration: The top-level Newton iteration count (0-based).
|
||||
/// Used for NUPCOL checks, group controls, gas lift timing.
|
||||
///
|
||||
/// - Local iteration: Iteration count within a nested domain solve.
|
||||
/// Starts at 0 for each domain.
|
||||
///
|
||||
/// - Timestep initialization: One-time setup at the start of each timestep,
|
||||
/// independent of iteration number.
|
||||
///
|
||||
/// For domain-local solves, `forLocalSolve()` creates a context that
|
||||
/// preserves the global iteration but resets local state.
|
||||
///
|
||||
struct NewtonIterationContext {
|
||||
|
||||
NewtonIterationContext() = default;
|
||||
|
||||
/// Getters
|
||||
|
||||
/// Current iteration number (0-based).
|
||||
/// Returns the local iteration during domain-local solves,
|
||||
/// and the global iteration otherwise.
|
||||
int iteration() const
|
||||
{
|
||||
return inLocalSolve_ ? localIteration_ : globalIteration_;
|
||||
}
|
||||
|
||||
/// Whether we are inside a domain-local solve (NLDD).
|
||||
bool inLocalSolve() const { return inLocalSolve_; }
|
||||
|
||||
/// Whether timestep initialization has been performed.
|
||||
bool timestepInitialized() const { return timestepInitialized_; }
|
||||
|
||||
/// Semantic Queries
|
||||
|
||||
/// Should timestep initialization run?
|
||||
/// True only on first global iteration before initialization is done.
|
||||
bool needsTimestepInit() const
|
||||
{
|
||||
return !timestepInitialized_ && !inLocalSolve_;
|
||||
}
|
||||
|
||||
/// Is this the first iteration of the global solve (not a local solve)?
|
||||
/// Use for one-time-per-timestep logic
|
||||
bool isFirstGlobalIteration() const
|
||||
{
|
||||
return globalIteration_ == 0 && !inLocalSolve_;
|
||||
}
|
||||
|
||||
/// Are we within the NUPCOL iteration window?
|
||||
/// Always uses global iteration regardless of local solve state.
|
||||
bool withinNupcol(int nupcol) const
|
||||
{
|
||||
return globalIteration_ < nupcol;
|
||||
}
|
||||
|
||||
/// Should tolerances be relaxed based on iteration count?
|
||||
/// Uses local iteration for local solves, global otherwise.
|
||||
bool shouldRelax(int strictIterations) const
|
||||
{
|
||||
return iteration() >= strictIterations;
|
||||
}
|
||||
|
||||
/// Whether inner well iterations should run.
|
||||
/// Returns false during local solves, otherwise true while below maxIter.
|
||||
bool shouldRunInnerWellIterations(int maxIter) const
|
||||
{
|
||||
if (inLocalSolve_) return false;
|
||||
return globalIteration_ < maxIter;
|
||||
}
|
||||
|
||||
/// State Mutations
|
||||
|
||||
/// Mark timestep initialization as complete.
|
||||
void markTimestepInitialized()
|
||||
{
|
||||
timestepInitialized_ = true;
|
||||
}
|
||||
|
||||
/// Advance the current iteration counter.
|
||||
/// Advances the local iteration during domain-local solves,
|
||||
/// and the global iteration otherwise.
|
||||
void advanceIteration()
|
||||
{
|
||||
if (inLocalSolve_) {
|
||||
localIteration_++;
|
||||
} else {
|
||||
globalIteration_++;
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset all state for a new timestep.
|
||||
void resetForNewTimestep()
|
||||
{
|
||||
globalIteration_ = 0;
|
||||
localIteration_ = 0;
|
||||
timestepInitialized_ = false;
|
||||
inLocalSolve_ = false;
|
||||
}
|
||||
|
||||
/// Create a context for a domain-local solve.
|
||||
/// Preserves global iteration, resets local iteration to 0.
|
||||
NewtonIterationContext forLocalSolve() const
|
||||
{
|
||||
return {globalIteration_, 0, /*inLocalSolve=*/true, /*timestepInitialized=*/true};
|
||||
}
|
||||
|
||||
private:
|
||||
NewtonIterationContext(int globalIter, int localIter, bool localSolve, bool tsInit)
|
||||
: globalIteration_(globalIter), localIteration_(localIter)
|
||||
, inLocalSolve_(localSolve), timestepInitialized_(tsInit)
|
||||
{}
|
||||
int globalIteration_ = 0;
|
||||
int localIteration_ = 0;
|
||||
bool inLocalSolve_ = false;
|
||||
bool timestepInitialized_ = false;
|
||||
};
|
||||
|
||||
/// RAII guard for NLDD domain-local iteration context.
|
||||
/// Saves the current context on the problem, installs a local-solve
|
||||
/// context, and restores the original on destruction.
|
||||
template<class Problem>
|
||||
class LocalContextGuard {
|
||||
public:
|
||||
LocalContextGuard(Problem& problem)
|
||||
: problem_(problem)
|
||||
, saved_(problem.iterationContext())
|
||||
{
|
||||
problem_.mutableIterationContext() = saved_.forLocalSolve();
|
||||
}
|
||||
|
||||
~LocalContextGuard()
|
||||
{
|
||||
problem_.mutableIterationContext() = saved_;
|
||||
}
|
||||
|
||||
LocalContextGuard(const LocalContextGuard&) = delete;
|
||||
LocalContextGuard& operator=(const LocalContextGuard&) = delete;
|
||||
LocalContextGuard(LocalContextGuard&&) = delete;
|
||||
LocalContextGuard& operator=(LocalContextGuard&&) = delete;
|
||||
|
||||
NewtonIterationContext& context() { return problem_.mutableIterationContext(); }
|
||||
const NewtonIterationContext& context() const { return problem_.iterationContext(); }
|
||||
|
||||
private:
|
||||
Problem& problem_;
|
||||
NewtonIterationContext saved_;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
||||
#endif // OPM_NEWTON_ITERATION_CONTEXT_HPP
|
||||
@@ -135,10 +135,6 @@ struct NonlinearSolverParameters
|
||||
// Do model-specific once-per-step calculations.
|
||||
report += model_->prepareStep(timer);
|
||||
|
||||
int iteration = 0;
|
||||
|
||||
// Let the model do one nonlinear iteration.
|
||||
|
||||
// Set up for main solver loop.
|
||||
bool converged = false;
|
||||
|
||||
@@ -148,13 +144,12 @@ struct NonlinearSolverParameters
|
||||
// Do the nonlinear step. If we are in a converged state, the
|
||||
// model will usually do an early return without an expensive
|
||||
// solve, unless the newton_min_iter_ count has not been reached yet.
|
||||
auto iterReport = model_->nonlinearIteration(iteration, timer, *this);
|
||||
auto iterReport = model_->nonlinearIteration(timer, *this);
|
||||
iterReport.global_time = timer.simulationTimeElapsed();
|
||||
report += iterReport;
|
||||
report.converged = iterReport.converged;
|
||||
|
||||
converged = report.converged;
|
||||
iteration += 1;
|
||||
}
|
||||
catch (...) {
|
||||
// if an iteration fails during a time step, all previous iterations
|
||||
@@ -164,8 +159,8 @@ struct NonlinearSolverParameters
|
||||
throw;
|
||||
}
|
||||
}
|
||||
while ( (!converged && (iteration <= this->model().param().newton_max_iter_)) ||
|
||||
(iteration <= this->model().param().newton_min_iter_));
|
||||
while ( (!converged && (model_->simulator().problem().iterationContext().iteration() <= this->model().param().newton_max_iter_)) ||
|
||||
(model_->simulator().problem().iterationContext().iteration() <= this->model().param().newton_min_iter_));
|
||||
|
||||
if (!converged) {
|
||||
failureReport_ = report;
|
||||
|
||||
@@ -533,8 +533,7 @@ std::unique_ptr<Matrix> blockJacobiAdjacency(const Grid& grid,
|
||||
}
|
||||
if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 1) {
|
||||
// Recreate solver on the first iteration of every timestep.
|
||||
const int newton_iteration = this->simulator_.model().newtonMethod().numIterations();
|
||||
return newton_iteration == 0;
|
||||
return this->simulator_.problem().iterationContext().isFirstGlobalIteration();
|
||||
}
|
||||
if (this->parameters_[activeSolverNum_].cpr_reuse_setup_ == 2) {
|
||||
// Recreate solver if the last solve used more than 10 iterations.
|
||||
|
||||
@@ -73,10 +73,11 @@ namespace Opm::Helper {
|
||||
}
|
||||
|
||||
// Combine and return.
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
std::ostringstream oss;
|
||||
oss << "prob_" << simulator.episodeIndex()
|
||||
<< "_time_" << std::setprecision(15) << std::setw(12) << std::setfill('0') << simulator.time()
|
||||
<< "_nit_" << simulator.model().newtonMethod().numIterations()
|
||||
<< "_nit_" << iterCtx.iteration()
|
||||
<< '_' << objName << "_istl";
|
||||
|
||||
const auto filename = (output_dir / oss.str()).generic_string();
|
||||
|
||||
@@ -156,8 +156,7 @@ template<class Scalar> class WellContributions;
|
||||
void beginIteration()
|
||||
{
|
||||
OPM_TIMEBLOCK(beginIteration);
|
||||
assemble(simulator_.model().newtonMethod().numIterations(),
|
||||
simulator_.timeStepSize());
|
||||
assemble(simulator_.timeStepSize());
|
||||
}
|
||||
|
||||
void endIteration()
|
||||
@@ -263,26 +262,23 @@ template<class Scalar> class WellContributions;
|
||||
// called at the beginning of a report step
|
||||
void beginReportStep(const int time_step);
|
||||
|
||||
// it should be able to go to prepareTimeStep(), however, the updateWellControls()
|
||||
// makes it a little more difficult. unless we introduce if (iterationIdx != 0) to avoid doing the above function
|
||||
// twice at the beginning of the time step
|
||||
/// Calculating the explict quantities used in the well calculation. By explicit, we mean they are cacluated
|
||||
/// at the beginning of the time step and no derivatives are included in these quantities
|
||||
/// Calculating the explicit quantities used in the well calculation. By explicit, we mean they are calculated
|
||||
/// at the beginning of the time step and no derivatives are included in these quantities.
|
||||
/// Called from assemble() when needsTimestepInit() returns true.
|
||||
void calculateExplicitQuantities() const;
|
||||
// some preparation work, mostly related to group control and RESV,
|
||||
// at the beginning of each time step (Not report step)
|
||||
|
||||
/// One-time initialization at the start of each timestep.
|
||||
/// Called once per timestep from assemble() when needsTimestepInit() returns true.
|
||||
void prepareTimeStep(DeferredLogger& deferred_logger);
|
||||
|
||||
bool
|
||||
updateWellControls(DeferredLogger& deferred_logger);
|
||||
|
||||
void updateAndCommunicate(const int reportStepIdx,
|
||||
const int iterationIdx);
|
||||
void updateAndCommunicate(const int reportStepIdx);
|
||||
|
||||
bool updateGroupControls(const Group& group,
|
||||
DeferredLogger& deferred_logger,
|
||||
const int reportStepIdx,
|
||||
const int iterationIdx);
|
||||
const int reportStepIdx);
|
||||
|
||||
const WellInterface<TypeTag>& getWell(const std::string& well_name) const;
|
||||
|
||||
@@ -535,8 +531,7 @@ template<class Scalar> class WellContributions;
|
||||
|
||||
// compute the well fluxes and assemble them in to the reservoir equations as source terms
|
||||
// and in the well equations.
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt);
|
||||
void assemble(const double dt);
|
||||
|
||||
// well controls and network pressures affect each other and are solved in an iterative manner.
|
||||
// the function handles one iteration of updating well controls and network pressures.
|
||||
|
||||
@@ -86,12 +86,13 @@ maybeDoGasLiftOptimize(const Simulator& simulator,
|
||||
// them to the GasLiftGroupInfo constructor.
|
||||
GLiftEclWells ecl_well_map;
|
||||
initGliftEclWellMap(well_container, ecl_well_map);
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
GasLiftGroupInfo group_info {
|
||||
ecl_well_map,
|
||||
simulator.vanguard().schedule(),
|
||||
simulator.vanguard().summaryState(),
|
||||
simulator.episodeIndex(),
|
||||
simulator.model().newtonMethod().numIterations(),
|
||||
iterCtx,
|
||||
deferred_logger,
|
||||
wellState,
|
||||
groupState,
|
||||
@@ -280,7 +281,8 @@ gasLiftOptimizationStage1SingleWell(WellInterface<TypeTag>* well,
|
||||
sync_groups,
|
||||
simulator.vanguard().gridView().comm(),
|
||||
this->glift_debug);
|
||||
auto state = glift->runOptimize(simulator.model().newtonMethod().numIterations());
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
auto state = glift->runOptimize(iterCtx.iteration());
|
||||
if (state) {
|
||||
state_map.emplace(well->name(), std::move(state));
|
||||
glift_wells.emplace(well->name(), std::move(glift));
|
||||
|
||||
@@ -1206,7 +1206,7 @@ groupAndNetworkData(const int reportStepIdx) const
|
||||
template<typename Scalar, typename IndexTraits>
|
||||
void BlackoilWellModelGeneric<Scalar, IndexTraits>::
|
||||
updateAndCommunicateGroupData(const int reportStepIdx,
|
||||
const int iterationIdx,
|
||||
const NewtonIterationContext& iterCtx,
|
||||
const Scalar tol_nupcol,
|
||||
const bool update_wellgrouptarget)
|
||||
{
|
||||
@@ -1215,7 +1215,7 @@ updateAndCommunicateGroupData(const int reportStepIdx,
|
||||
const int nupcol = schedule()[reportStepIdx].nupcol();
|
||||
|
||||
// Update accumulated group consumption/import rates for current report step
|
||||
if(iterationIdx == 0) {
|
||||
if (iterCtx.isFirstGlobalIteration()) {
|
||||
this->groupState().update_gconsump(schedule(), reportStepIdx, this->summaryState_);
|
||||
}
|
||||
|
||||
@@ -1232,7 +1232,7 @@ updateAndCommunicateGroupData(const int reportStepIdx,
|
||||
this->wellState().updateGlobalIsGrup(comm_, well_status);
|
||||
|
||||
GroupStateHelperType &group_state_helper = this->groupStateHelper();
|
||||
if (iterationIdx < nupcol) {
|
||||
if (iterCtx.withinNupcol(nupcol)) {
|
||||
OPM_TIMEBLOCK(updateNupcol);
|
||||
this->updateNupcolWGState();
|
||||
} else {
|
||||
@@ -1282,7 +1282,7 @@ updateAndCommunicateGroupData(const int reportStepIdx,
|
||||
const std::string control_str = is_vrep? "VREP" : "REIN";
|
||||
const std::string msg = fmt::format("Group prodution relative change {} larger than tolerance {} "
|
||||
"at iteration {}. Update {} for Group {} even if iteration is larger than {} given by NUPCOL." ,
|
||||
rel_change, tol_nupcol, iterationIdx, control_str, gr_name, nupcol);
|
||||
rel_change, tol_nupcol, iterCtx.iteration(), control_str, gr_name, nupcol);
|
||||
group_state_helper.deferredLogger().debug(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <opm/input/eclipse/Schedule/Well/PAvgCalculatorCollection.hpp>
|
||||
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
|
||||
|
||||
#include <opm/simulators/flow/NewtonIterationContext.hpp>
|
||||
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
|
||||
#include <opm/simulators/wells/BlackoilWellModelWBP.hpp>
|
||||
#include <opm/simulators/wells/ConnectionIndexMap.hpp>
|
||||
@@ -304,7 +305,7 @@ public:
|
||||
}
|
||||
|
||||
void updateAndCommunicateGroupData(const int reportStepIdx,
|
||||
const int iterationIdx,
|
||||
const NewtonIterationContext& iterCtx,
|
||||
const Scalar tol_nupcol,
|
||||
// we only want to update the wellgroup target
|
||||
// after the groups have found their controls
|
||||
|
||||
@@ -105,7 +105,7 @@ needPreStepRebalance(const int report_step) const
|
||||
|
||||
template<typename Scalar, typename IndexTraits>
|
||||
bool BlackoilWellModelNetworkGeneric<Scalar, IndexTraits>::
|
||||
shouldBalance(const int reportStepIdx, const int iterationIdx) const
|
||||
shouldBalance(const int reportStepIdx, const NewtonIterationContext& iterCtx) const
|
||||
{
|
||||
// if network is not active, we do not need to balance the network
|
||||
const auto& network = well_model_.schedule()[reportStepIdx].network();
|
||||
@@ -115,10 +115,10 @@ shouldBalance(const int reportStepIdx, const int iterationIdx) const
|
||||
|
||||
const auto& balance = well_model_.schedule()[reportStepIdx].network_balance();
|
||||
if (balance.mode() == Network::Balance::CalcMode::TimeStepStart) {
|
||||
return iterationIdx == 0;
|
||||
return iterCtx.isFirstGlobalIteration();
|
||||
} else if (balance.mode() == Network::Balance::CalcMode::NUPCOL) {
|
||||
const int nupcol = well_model_.schedule()[reportStepIdx].nupcol();
|
||||
return iterationIdx < nupcol;
|
||||
return iterCtx.withinNupcol(nupcol);
|
||||
} else {
|
||||
// We do not support any other rebalancing modes,
|
||||
// i.e. TimeInterval based rebalancing is not available.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <opm/output/data/Groups.hpp>
|
||||
|
||||
#include <opm/simulators/flow/NewtonIterationContext.hpp>
|
||||
#include <opm/simulators/utils/ParallelCommunication.hpp>
|
||||
|
||||
#include <map>
|
||||
@@ -80,7 +81,7 @@ public:
|
||||
bool needPreStepRebalance(const int report_step) const;
|
||||
|
||||
bool shouldBalance(const int reportStepIndex,
|
||||
const int iterationIdx) const;
|
||||
const NewtonIterationContext& iterCtx) const;
|
||||
|
||||
Scalar updatePressures(const int reportStepIdx,
|
||||
const Scalar damping_factor,
|
||||
|
||||
@@ -96,13 +96,13 @@ update(const bool mandatory_network_balance,
|
||||
return {false, 0.0};
|
||||
}
|
||||
|
||||
const int iterationIdx = well_model_.simulator().model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = well_model_.simulator().problem().iterationContext();
|
||||
const auto& comm = well_model_.simulator().vanguard().grid().comm();
|
||||
|
||||
// network related
|
||||
Scalar network_imbalance = 0.0;
|
||||
bool more_network_update = false;
|
||||
if (this->shouldBalance(episodeIdx, iterationIdx) || mandatory_network_balance) {
|
||||
if (this->shouldBalance(episodeIdx, iterCtx) || mandatory_network_balance) {
|
||||
OPM_TIMEBLOCK(BalanceNetwork);
|
||||
const double dt = well_model_.simulator().timeStepSize();
|
||||
// Calculate common THP for subsea manifold well group (item 3 of NODEPROP set to YES)
|
||||
@@ -144,7 +144,7 @@ update(const bool mandatory_network_balance,
|
||||
}
|
||||
}
|
||||
well_model_.updateAndCommunicateGroupData(episodeIdx,
|
||||
iterationIdx,
|
||||
iterCtx,
|
||||
well_model_.param().nupcol_group_rate_tolerance_,
|
||||
/*update_wellgrouptarget*/ true);
|
||||
}
|
||||
|
||||
@@ -102,8 +102,7 @@ public:
|
||||
|
||||
// prototype for assemble function for ASPIN solveLocal()
|
||||
// will try to merge back to assemble() when done prototyping
|
||||
void assemble(const int iterationIdx,
|
||||
const double dt,
|
||||
void assemble(const double dt,
|
||||
const Domain& domain);
|
||||
|
||||
void updateWellControls(const Domain& domain);
|
||||
|
||||
@@ -36,15 +36,55 @@ namespace Opm {
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModelNldd<TypeTag>::
|
||||
assemble(const int /*iterationIdx*/,
|
||||
const double dt,
|
||||
assemble(const double dt,
|
||||
const Domain& domain)
|
||||
{
|
||||
OPM_TIMEBLOCK(assemble);
|
||||
// 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).
|
||||
// NLDD domain well assembly for local solves.
|
||||
//
|
||||
// Call chain: BlackoilWellModelNldd::assemble()
|
||||
// -> BlackoilWellModelNldd::assembleWellEq(dt, domain)
|
||||
// -> WellInterface::assembleWellEq()
|
||||
// -> WellInterface::prepareWellBeforeAssembling()
|
||||
// -> WellInterface::assembleWellEqWithoutIteration()
|
||||
//
|
||||
// Key assumptions and design decisions:
|
||||
//
|
||||
// 1. Global well initialization (BlackoilWellModel::prepareTimeStep, including the
|
||||
// optional initial well solve) has already been called via
|
||||
// BlackoilWellModel::assemble() during the initial global linearization.
|
||||
// This is ensured by NewtonIterationContext::needsTimestepInit().
|
||||
//
|
||||
// 2. Inner well iterations (BlackoilWellModel::iterateWellEquations) are skipped
|
||||
// during local solves via NewtonIterationContext::shouldRunInnerWellIterations()
|
||||
// (inLocalSolve=true).
|
||||
// The NLDD local Newton loop already iterates wells to convergence.
|
||||
//
|
||||
// 3. No MPI collective operations (do_mpi_gather=false).
|
||||
//
|
||||
// 4. Only individual well constraints are checked (see
|
||||
// BlackoilWellModelNldd::updateWellControls).
|
||||
// Group controls, network balancing, gas lift, and NUPCOL/VREP updates
|
||||
// are NOT performed during domain solves. This means:
|
||||
// - Wells can switch between individual controls (BHP, THP, rate limits)
|
||||
// to respect physical safety constraints.
|
||||
// - Wells cannot be switched to/from GRUP control. Individual constraint
|
||||
// checks (WellInterface::checkIndividualConstraints) never produce GRUP
|
||||
// as a result; only WellInterface::checkGroupConstraints can do that,
|
||||
// and it is not called here.
|
||||
// - Group production targets and VREP/REIN injection targets use stale
|
||||
// group state from the last global assembly.
|
||||
//
|
||||
// The final Newton step after NLDD domain solves re-synchronizes everything
|
||||
// via a global BlackoilWellModel::assemble() which performs the full control
|
||||
// update pipeline: BlackoilWellModelGeneric::updateAndCommunicateGroupData,
|
||||
// BlackoilWellModelGeneric::updateGroupControls,
|
||||
// group+individual well constraint checks, network balancing, and gas lift.
|
||||
// Importantly, the global step also checks individual constraints (after
|
||||
// group constraints), so if a domain solve switched a well to e.g. BHP
|
||||
// because of a genuine BHP limit violation, the global step will reach
|
||||
// the same conclusion -- the two levels are consistent for individual
|
||||
// constraints.
|
||||
|
||||
// Use do_mpi_gather=false to avoid MPI collective operations in domain solves.
|
||||
auto loggerGuard = wellModel_.groupStateHelper().pushLogger(/*do_mpi_gather=*/false);
|
||||
@@ -132,8 +172,8 @@ getWellConvergence(const Domain& domain,
|
||||
const std::vector<Scalar>& B_avg,
|
||||
DeferredLogger& local_deferredLogger) const
|
||||
{
|
||||
const int iterationIdx = wellModel_.simulator().model().newtonMethod().numIterations();
|
||||
const bool relax_tolerance = iterationIdx > wellModel_.numStrictIterations();
|
||||
const auto& iterCtx = wellModel_.simulator().problem().iterationContext();
|
||||
const bool relax_tolerance = iterCtx.shouldRelax(wellModel_.numStrictIterations());
|
||||
|
||||
ConvergenceReport report;
|
||||
{
|
||||
@@ -184,10 +224,15 @@ updateWellControls(const Domain& domain)
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Only individual well constraints (BHP, THP, rate limits) are checked.
|
||||
// Group constraints, network balancing, and NUPCOL/VREP updates require
|
||||
// MPI collectives and consistent global group state, which are not
|
||||
// available during domain-local solves. The final global Newton step
|
||||
// handles all group-level control decisions.
|
||||
//
|
||||
// Individual constraints are physical safety limits that are essential
|
||||
// for well convergence (e.g., BHP minimum prevents infeasible operating
|
||||
// points). Skipping them would cause domain convergence failures.
|
||||
for (const auto& well : wellModel_.localNonshutWells()) {
|
||||
if (this->well_domain().at(well->name()) == domain.index) {
|
||||
constexpr auto mode = WellInterface<TypeTag>::IndividualOrGroup::Individual;
|
||||
|
||||
@@ -388,8 +388,9 @@ namespace Opm {
|
||||
|
||||
// we need to update the group data after the well is created
|
||||
// to make sure we get the correct mapping.
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
this->updateAndCommunicateGroupData(reportStepIdx,
|
||||
simulator_.model().newtonMethod().numIterations(),
|
||||
iterCtx,
|
||||
param_.nupcol_group_rate_tolerance_, /*update_wellgrouptarget*/ false);
|
||||
|
||||
// Wells are active if they are active wells on at least one process.
|
||||
@@ -492,10 +493,13 @@ namespace Opm {
|
||||
OPM_PARALLEL_CATCH_CLAUSE(exc_type, exc_msg);
|
||||
}
|
||||
|
||||
this->updateAndCommunicateGroupData(reportStepIdx,
|
||||
simulator_.model().newtonMethod().numIterations(),
|
||||
param_.nupcol_group_rate_tolerance_,
|
||||
/*update_wellgrouptarget*/ true);
|
||||
{
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
this->updateAndCommunicateGroupData(reportStepIdx,
|
||||
iterCtx,
|
||||
param_.nupcol_group_rate_tolerance_,
|
||||
/*update_wellgrouptarget*/ true);
|
||||
}
|
||||
try {
|
||||
// Compute initial well solution for new wells and injectors that change injection type i.e. WAG.
|
||||
for (auto& well : well_container_) {
|
||||
@@ -1155,17 +1159,18 @@ namespace Opm {
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
assemble(const int iterationIdx,
|
||||
const double dt)
|
||||
assemble(const double dt)
|
||||
{
|
||||
OPM_TIMEFUNCTION();
|
||||
auto logger_guard = this->groupStateHelper().pushLogger();
|
||||
auto& local_deferredLogger = this->groupStateHelper().deferredLogger();
|
||||
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
|
||||
if constexpr (BlackoilWellModelGasLift<TypeTag>::glift_debug) {
|
||||
if (gaslift_.terminalOutput()) {
|
||||
const std::string msg =
|
||||
fmt::format(fmt::runtime("assemble() : iteration {}"), iterationIdx);
|
||||
fmt::format(fmt::runtime("assemble() : iteration {}"), iterCtx.iteration());
|
||||
gaslift_.gliftDebug(msg, local_deferredLogger);
|
||||
}
|
||||
}
|
||||
@@ -1182,8 +1187,9 @@ namespace Opm {
|
||||
}
|
||||
}
|
||||
|
||||
if (iterationIdx == 0 && this->wellsActive()) {
|
||||
OPM_TIMEBLOCK(firstIterationAssmble);
|
||||
// Timestep initialization: should run once at the start of each timestep.
|
||||
if (iterCtx.needsTimestepInit() && this->wellsActive()) {
|
||||
OPM_TIMEBLOCK(firstIterationAssemble);
|
||||
// try-catch is needed here as updateWellControls
|
||||
// contains global communication and has either to
|
||||
// be reached by all processes or all need to abort
|
||||
@@ -1194,7 +1200,7 @@ namespace Opm {
|
||||
prepareTimeStep(local_deferredLogger);
|
||||
}
|
||||
OPM_END_PARALLEL_TRY_CATCH_LOG(local_deferredLogger,
|
||||
"assemble() failed (It=0): ",
|
||||
"assemble() failed during well initialization: ",
|
||||
this->terminal_output_, grid().comm());
|
||||
}
|
||||
|
||||
@@ -1241,8 +1247,8 @@ namespace Opm {
|
||||
if (network_update_iteration >= max_iteration ) {
|
||||
// only output to terminal if we at the last newton iterations where we try to balance the network.
|
||||
const int episodeIdx = simulator_.episodeIndex();
|
||||
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
||||
if (this->network_.shouldBalance(episodeIdx, iterationIdx + 1)) {
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
if (this->network_.shouldBalance(episodeIdx, iterCtx)) {
|
||||
if (this->terminal_output_) {
|
||||
const std::string msg = fmt::format("Maximum of {:d} network iterations has been used and we stop the update, \n"
|
||||
"and try again after the next Newton iteration (imbalance = {:.2e} bar)",
|
||||
@@ -1288,9 +1294,9 @@ namespace Opm {
|
||||
DeferredLogger& local_deferredLogger)
|
||||
{
|
||||
OPM_TIMEFUNCTION();
|
||||
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
const int reportStepIdx = simulator_.episodeIndex();
|
||||
this->updateAndCommunicateGroupData(reportStepIdx, iterationIdx,
|
||||
this->updateAndCommunicateGroupData(reportStepIdx, iterCtx,
|
||||
param_.nupcol_group_rate_tolerance_, /*update_wellgrouptarget*/ true);
|
||||
// We need to call updateWellControls before we update the network as
|
||||
// network updates are only done on thp controlled wells.
|
||||
@@ -1308,7 +1314,7 @@ namespace Opm {
|
||||
if (optimize_gas_lift) {
|
||||
// we need to update the potentials if the thp limit as been modified by
|
||||
// the network balancing
|
||||
const bool updatePotentials = (this->network_.shouldBalance(reportStepIdx, iterationIdx) ||
|
||||
const bool updatePotentials = (this->network_.shouldBalance(reportStepIdx, iterCtx) ||
|
||||
mandatory_network_balance);
|
||||
alq_updated = gaslift_.maybeDoGasLiftOptimize(simulator_,
|
||||
well_container_,
|
||||
@@ -1337,7 +1343,7 @@ namespace Opm {
|
||||
}
|
||||
// we need to re-iterate the network when the well group controls changed or gaslift/alq is changed or
|
||||
// the inner iterations are did not converge
|
||||
const bool more_network_update = this->network_.shouldBalance(reportStepIdx, iterationIdx) &&
|
||||
const bool more_network_update = this->network_.shouldBalance(reportStepIdx, iterCtx) &&
|
||||
(more_inner_network_update || alq_updated);
|
||||
return {well_group_control_changed, more_network_update, network_imbalance};
|
||||
}
|
||||
@@ -1582,14 +1588,15 @@ namespace Opm {
|
||||
{
|
||||
// Get global (from all processes) convergence report.
|
||||
ConvergenceReport local_report;
|
||||
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
const bool relaxTolerance = iterCtx.shouldRelax(param_.strict_outer_iter_wells_);
|
||||
{
|
||||
auto logger_guard = this->groupStateHelper().pushLogger();
|
||||
for (const auto& well : well_container_) {
|
||||
if (well->isOperableAndSolvable() || well->wellIsStopped()) {
|
||||
local_report += well->getWellConvergence(
|
||||
this->groupStateHelper(), B_avg,
|
||||
iterationIdx > param_.strict_outer_iter_wells_);
|
||||
relaxTolerance);
|
||||
} else {
|
||||
ConvergenceReport report;
|
||||
using CR = ConvergenceReport;
|
||||
@@ -1650,7 +1657,6 @@ namespace Opm {
|
||||
return false;
|
||||
}
|
||||
const int episodeIdx = simulator_.episodeIndex();
|
||||
const int iterationIdx = simulator_.model().newtonMethod().numIterations();
|
||||
const auto& comm = simulator_.vanguard().grid().comm();
|
||||
size_t iter = 0;
|
||||
bool changed_well_group = false;
|
||||
@@ -1659,7 +1665,7 @@ namespace Opm {
|
||||
// iterate a few times to make sure all constraints are honored
|
||||
const std::size_t max_iter = param_.well_group_constraints_max_iterations_;
|
||||
while(!changed_well_group && iter < max_iter) {
|
||||
changed_well_group = updateGroupControls(fieldGroup, deferred_logger, episodeIdx, iterationIdx);
|
||||
changed_well_group = updateGroupControls(fieldGroup, deferred_logger, episodeIdx);
|
||||
|
||||
// Check wells' group constraints and communicate.
|
||||
bool changed_well_to_group = false;
|
||||
@@ -1683,7 +1689,7 @@ namespace Opm {
|
||||
|
||||
changed_well_to_group = comm.sum(static_cast<int>(changed_well_to_group));
|
||||
if (changed_well_to_group) {
|
||||
updateAndCommunicate(episodeIdx, iterationIdx);
|
||||
updateAndCommunicate(episodeIdx);
|
||||
changed_well_group = true;
|
||||
}
|
||||
|
||||
@@ -1708,7 +1714,7 @@ namespace Opm {
|
||||
|
||||
changed_well_individual = comm.sum(static_cast<int>(changed_well_individual));
|
||||
if (changed_well_individual) {
|
||||
updateAndCommunicate(episodeIdx, iterationIdx);
|
||||
updateAndCommunicate(episodeIdx);
|
||||
changed_well_group = true;
|
||||
}
|
||||
iter++;
|
||||
@@ -1724,11 +1730,11 @@ namespace Opm {
|
||||
template<typename TypeTag>
|
||||
void
|
||||
BlackoilWellModel<TypeTag>::
|
||||
updateAndCommunicate(const int reportStepIdx,
|
||||
const int iterationIdx)
|
||||
updateAndCommunicate(const int reportStepIdx)
|
||||
{
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
this->updateAndCommunicateGroupData(reportStepIdx,
|
||||
iterationIdx,
|
||||
iterCtx,
|
||||
param_.nupcol_group_rate_tolerance_,
|
||||
/*update_wellgrouptarget*/ true);
|
||||
|
||||
@@ -1750,7 +1756,7 @@ namespace Opm {
|
||||
OPM_END_PARALLEL_TRY_CATCH("BlackoilWellModel::updateAndCommunicate failed: ",
|
||||
simulator_.gridView().comm())
|
||||
this->updateAndCommunicateGroupData(reportStepIdx,
|
||||
iterationIdx,
|
||||
iterCtx,
|
||||
param_.nupcol_group_rate_tolerance_,
|
||||
/*update_wellgrouptarget*/ true);
|
||||
}
|
||||
@@ -1760,19 +1766,19 @@ namespace Opm {
|
||||
BlackoilWellModel<TypeTag>::
|
||||
updateGroupControls(const Group& group,
|
||||
DeferredLogger& deferred_logger,
|
||||
const int reportStepIdx,
|
||||
const int iterationIdx)
|
||||
const int reportStepIdx)
|
||||
{
|
||||
OPM_TIMEFUNCTION();
|
||||
const auto& iterCtx = simulator_.problem().iterationContext();
|
||||
bool changed = false;
|
||||
// restrict the number of group switches but only after nupcol iterations.
|
||||
const int nupcol = this->schedule()[reportStepIdx].nupcol();
|
||||
const int max_number_of_group_switches = param_.max_number_of_group_switches_;
|
||||
const bool update_group_switching_log = iterationIdx >= nupcol;
|
||||
const bool update_group_switching_log = !iterCtx.withinNupcol(nupcol);
|
||||
const bool changed_hc = this->checkGroupHigherConstraints(group, deferred_logger, reportStepIdx, max_number_of_group_switches, update_group_switching_log);
|
||||
if (changed_hc) {
|
||||
changed = true;
|
||||
updateAndCommunicate(reportStepIdx, iterationIdx);
|
||||
updateAndCommunicate(reportStepIdx);
|
||||
}
|
||||
|
||||
bool changed_individual =
|
||||
@@ -1790,11 +1796,11 @@ namespace Opm {
|
||||
|
||||
if (changed_individual) {
|
||||
changed = true;
|
||||
updateAndCommunicate(reportStepIdx, iterationIdx);
|
||||
updateAndCommunicate(reportStepIdx);
|
||||
}
|
||||
// call recursively down the group hierarchy
|
||||
for (const std::string& groupName : group.groups()) {
|
||||
bool changed_this = updateGroupControls( this->schedule().getGroup(groupName, reportStepIdx), deferred_logger, reportStepIdx,iterationIdx);
|
||||
bool changed_this = updateGroupControls(this->schedule().getGroup(groupName, reportStepIdx), deferred_logger, reportStepIdx);
|
||||
changed = changed || changed_this;
|
||||
}
|
||||
return changed;
|
||||
|
||||
@@ -41,7 +41,7 @@ GasLiftGroupInfo(GLiftEclWells& ecl_wells,
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summary_state,
|
||||
const int report_step_idx,
|
||||
const int iteration_idx,
|
||||
const NewtonIterationContext& iterCtx,
|
||||
DeferredLogger& deferred_logger,
|
||||
WellState<Scalar, IndexTraits>& well_state,
|
||||
const GroupState<Scalar>& group_state,
|
||||
@@ -52,7 +52,7 @@ GasLiftGroupInfo(GLiftEclWells& ecl_wells,
|
||||
, schedule_{schedule}
|
||||
, summary_state_{summary_state}
|
||||
, report_step_idx_{report_step_idx}
|
||||
, iteration_idx_{iteration_idx}
|
||||
, iterCtx_{iterCtx}
|
||||
, phase_usage_{well_state.phaseUsageInfo()}
|
||||
, glo_{schedule_.glo(report_step_idx_)}
|
||||
{}
|
||||
@@ -426,25 +426,27 @@ checkNewtonIterationIdxOk_(const std::string& well_name)
|
||||
{
|
||||
if (this->glo_.all_newton()) {
|
||||
const int nupcol = this->schedule_[this->report_step_idx_].nupcol();
|
||||
const bool optimize = this->iterCtx_.withinNupcol(nupcol);
|
||||
if (this->debug) {
|
||||
const std::string msg = fmt::format(
|
||||
"LIFTOPT item4 == YES, it = {}, nupcol = {} --> GLIFT optimize = {}",
|
||||
this->iteration_idx_,
|
||||
this->iterCtx_.iteration(),
|
||||
nupcol,
|
||||
((this->iteration_idx_ < nupcol) ? "TRUE" : "FALSE"));
|
||||
(optimize ? "TRUE" : "FALSE"));
|
||||
displayDebugMessage_(msg, well_name);
|
||||
}
|
||||
return this->iteration_idx_ < nupcol;
|
||||
return optimize;
|
||||
}
|
||||
else {
|
||||
const bool optimize = this->iterCtx_.iteration() == 1;
|
||||
if (this->debug) {
|
||||
const std::string msg = fmt::format(
|
||||
"LIFTOPT item4 == NO, it = {} --> GLIFT optimize = {}",
|
||||
this->iteration_idx_,
|
||||
((this->iteration_idx_ == 1) ? "TRUE" : "FALSE"));
|
||||
this->iterCtx_.iteration(),
|
||||
(optimize ? "TRUE" : "FALSE"));
|
||||
displayDebugMessage_(msg, well_name);
|
||||
}
|
||||
return this->iteration_idx_ == 1;
|
||||
return optimize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#ifndef OPM_GASLIFT_GROUP_INFO_HEADER_INCLUDED
|
||||
#define OPM_GASLIFT_GROUP_INFO_HEADER_INCLUDED
|
||||
|
||||
#include <opm/simulators/flow/NewtonIterationContext.hpp>
|
||||
#include <opm/simulators/wells/GasLiftCommon.hpp>
|
||||
|
||||
#include <map>
|
||||
@@ -66,7 +67,7 @@ public:
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summary_state,
|
||||
const int report_step_idx,
|
||||
const int iteration_idx,
|
||||
const NewtonIterationContext& iterCtx,
|
||||
DeferredLogger& deferred_logger,
|
||||
WellState<Scalar, IndexTraits>& well_state,
|
||||
const GroupState<Scalar>& group_state,
|
||||
@@ -234,7 +235,7 @@ protected:
|
||||
const Schedule& schedule_;
|
||||
const SummaryState& summary_state_;
|
||||
const int report_step_idx_;
|
||||
const int iteration_idx_;
|
||||
const NewtonIterationContext& iterCtx_;
|
||||
const PhaseUsageInfo<IndexTraits>& phase_usage_;
|
||||
const GasLiftOpt& glo_;
|
||||
GroupRateMap group_rate_map_;
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace Opm
|
||||
}
|
||||
|
||||
const int episodeIdx = simulator.episodeIndex();
|
||||
const int iterationIdx = simulator.model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
const int nupcol = schedule[episodeIdx].nupcol();
|
||||
const bool oscillating =
|
||||
std::ranges::count(this->well_control_log_, from) >= this->param_.max_number_of_well_switches_;
|
||||
@@ -255,6 +255,9 @@ namespace Opm
|
||||
ss << " Switching control mode for well " << this->name()
|
||||
<< " from " << from
|
||||
<< " to " << to;
|
||||
if (iterCtx.inLocalSolve()) {
|
||||
ss << " (NLDD domain solve)";
|
||||
}
|
||||
if (cc.size() > 1) {
|
||||
ss << " on rank " << cc.rank();
|
||||
}
|
||||
@@ -263,7 +266,7 @@ namespace Opm
|
||||
// We always store the current control as it is used for output
|
||||
// and only after iteration >= nupcol
|
||||
// we log all switches to check if the well controls oscillates
|
||||
if (iterationIdx >= nupcol || this->well_control_log_.empty()) {
|
||||
if (!iterCtx.withinNupcol(nupcol) || this->well_control_log_.empty()) {
|
||||
this->well_control_log_.push_back(from);
|
||||
}
|
||||
updateWellStateWithTarget(simulator, groupStateHelper, well_state);
|
||||
@@ -572,13 +575,13 @@ namespace Opm
|
||||
const auto msg = fmt::format(" Well {} switched from {} to {} during local solve", this->name(), from, to);
|
||||
deferred_logger.debug(msg);
|
||||
const int episodeIdx = simulator.episodeIndex();
|
||||
const int iterationIdx = simulator.model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
const auto& schedule = simulator.vanguard().schedule();
|
||||
const int nupcol = schedule[episodeIdx].nupcol();
|
||||
// We always store the current control as it is used for output
|
||||
// and only after iteration >= nupcol
|
||||
// we log all switches to check if the well controls oscillates
|
||||
if (iterationIdx >= nupcol || this->well_control_log_.empty()) {
|
||||
if (!iterCtx.withinNupcol(nupcol) || this->well_control_log_.empty()) {
|
||||
this->well_control_log_.push_back(from);
|
||||
}
|
||||
}
|
||||
@@ -996,8 +999,8 @@ namespace Opm
|
||||
checkWellOperability(simulator, well_state, groupStateHelper);
|
||||
|
||||
// only use inner well iterations for the first newton iterations.
|
||||
const int iteration_idx = simulator.model().newtonMethod().numIterations();
|
||||
if (iteration_idx < this->param_.max_niter_inner_well_iter_) {
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
if (iterCtx.shouldRunInnerWellIterations(this->param_.max_niter_inner_well_iter_)) {
|
||||
const auto& ws = well_state.well(this->indexOfWell());
|
||||
const bool nonzero_rate_original =
|
||||
std::any_of(ws.surface_rates.begin(),
|
||||
@@ -1089,13 +1092,15 @@ namespace Opm
|
||||
}
|
||||
}
|
||||
if (old_well_operable) {
|
||||
deferred_logger.debug(" well " + this->name() + " gets STOPPED during iteration ");
|
||||
const std::string ctx = iterCtx.inLocalSolve() ? " (NLDD domain solve)" : "";
|
||||
deferred_logger.debug(" well " + this->name() + " gets STOPPED during iteration" + ctx);
|
||||
changed_to_stopped_this_step_ = true;
|
||||
}
|
||||
} else if (well_state.isOpen(this->name())) {
|
||||
this->openWell();
|
||||
if (!old_well_operable) {
|
||||
deferred_logger.debug(" well " + this->name() + " gets REVIVED during iteration ");
|
||||
const std::string ctx = iterCtx.inLocalSolve() ? " (NLDD domain solve)" : "";
|
||||
deferred_logger.debug(" well " + this->name() + " gets REVIVED during iteration" + ctx);
|
||||
this->changed_to_open_this_step_ = true;
|
||||
}
|
||||
}
|
||||
@@ -2289,12 +2294,13 @@ namespace Opm
|
||||
// Instantiate group info object (without initialization) since it is needed in GasLiftSingleWell
|
||||
auto& comm = simulator.vanguard().grid().comm();
|
||||
ecl_well_map.try_emplace(this->name(), &(this->wellEcl()), this->indexOfWell());
|
||||
const auto& iterCtx = simulator.problem().iterationContext();
|
||||
GasLiftGroupInfo<Scalar, IndexTraits> group_info {
|
||||
ecl_well_map,
|
||||
simulator.vanguard().schedule(),
|
||||
simulator.vanguard().summaryState(),
|
||||
simulator.episodeIndex(),
|
||||
simulator.model().newtonMethod().numIterations(),
|
||||
iterCtx,
|
||||
deferred_logger,
|
||||
well_state,
|
||||
group_state,
|
||||
|
||||
+10
-8
@@ -78,7 +78,8 @@ BOOST_AUTO_TEST_CASE(G1)
|
||||
simulator->setEpisodeLength(0.0);
|
||||
simulator->startNextEpisode(/*episodeStartTime=*/0.0, /*episodeLength=*/1e30);
|
||||
simulator->setTimeStepSize(43200); // 12 hours
|
||||
simulator->model().newtonMethod().setIterationIndex(0);
|
||||
// Reset iteration context so code using problem().iterationContext() sees first iteration
|
||||
simulator->problem().resetIterationForNewTimestep();
|
||||
WellModel& well_model = simulator->problem().wellModel();
|
||||
|
||||
// we tests 4 different setups given in the .DATA file
|
||||
@@ -117,14 +118,14 @@ BOOST_AUTO_TEST_CASE(G1)
|
||||
GLiftEclWells ecl_well_map;
|
||||
Opm::BlackoilWellModelGasLift<TypeTag>::
|
||||
initGliftEclWellMap(well_model.localNonshutWells(), ecl_well_map);
|
||||
const int iteration_idx = simulator->model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator->problem().iterationContext();
|
||||
const auto& comm = simulator->vanguard().grid().comm();
|
||||
GasLiftGroupInfo group_info {
|
||||
ecl_well_map,
|
||||
schedule,
|
||||
summary_state,
|
||||
simulator->episodeIndex(),
|
||||
iteration_idx,
|
||||
iterCtx,
|
||||
deferred_logger,
|
||||
well_state,
|
||||
group_state,
|
||||
@@ -139,7 +140,7 @@ BOOST_AUTO_TEST_CASE(G1)
|
||||
};
|
||||
group_info.initialize();
|
||||
const auto& controls = well.productionControls(summary_state);
|
||||
auto state = glift.runOptimize(iteration_idx);
|
||||
auto state = glift.runOptimize(iterCtx.iteration());
|
||||
auto pot = well_state[well.name()].well_potentials;
|
||||
|
||||
const auto& glo = schedule.glo(report_step_idx);
|
||||
@@ -230,7 +231,8 @@ BOOST_AUTO_TEST_CASE(G2)
|
||||
simulator->setEpisodeLength(0.0);
|
||||
simulator->startNextEpisode(/*episodeStartTime=*/0.0, /*episodeLength=*/1e30);
|
||||
simulator->setTimeStepSize(43200); // 12 hours
|
||||
simulator->model().newtonMethod().setIterationIndex(0);
|
||||
// Reset iteration context so code using problem().iterationContext() sees first iteration
|
||||
simulator->problem().resetIterationForNewTimestep();
|
||||
WellModel& well_model = simulator->problem().wellModel();
|
||||
|
||||
const auto& comm = simulator->vanguard().grid().comm();
|
||||
@@ -257,7 +259,7 @@ BOOST_AUTO_TEST_CASE(G2)
|
||||
GLiftEclWells ecl_well_map;
|
||||
Opm::BlackoilWellModelGasLift<TypeTag>::
|
||||
initGliftEclWellMap(well_model.localNonshutWells(), ecl_well_map);
|
||||
const int iteration_idx = simulator->model().newtonMethod().numIterations();
|
||||
const auto& iterCtx = simulator->problem().iterationContext();
|
||||
WellState& well_state = well_model.wellState();
|
||||
const auto& group_state = well_model.groupState();
|
||||
GasLiftGroupInfo group_info {
|
||||
@@ -265,7 +267,7 @@ BOOST_AUTO_TEST_CASE(G2)
|
||||
schedule,
|
||||
summary_state,
|
||||
report_step_idx,
|
||||
iteration_idx,
|
||||
iterCtx,
|
||||
deferred_logger,
|
||||
well_state,
|
||||
group_state,
|
||||
@@ -280,7 +282,7 @@ BOOST_AUTO_TEST_CASE(G2)
|
||||
deferred_logger, well_state, group_state, group_info, sync_groups,
|
||||
comm, /*glift_debug=*/false
|
||||
);
|
||||
auto state = glift->runOptimize(iteration_idx);
|
||||
auto state = glift->runOptimize(iterCtx.iteration());
|
||||
if (state) {
|
||||
state_map.emplace(well->name(), std::move(state));
|
||||
glift_wells.emplace(well->name(), std::move(glift));
|
||||
|
||||
@@ -249,7 +249,8 @@ private:
|
||||
simulator_->setEpisodeLength(0.0);
|
||||
simulator_->startNextEpisode(/*episodeStartTime=*/0.0, /*episodeLength=*/1e30);
|
||||
simulator_->setTimeStepSize(Opm::unit::day);
|
||||
simulator_->model().newtonMethod().setIterationIndex(0);
|
||||
// Reset iteration context so code using problem().iterationContext() sees first iteration
|
||||
simulator_->problem().resetIterationForNewTimestep();
|
||||
|
||||
wellModel().beginReportStep(report_step_idx_);
|
||||
|
||||
|
||||
Reference in new issue
Block a user