opm-simulators
AdaptiveTimeStepping_impl.hpp
1 /*
2  Copyright 2024 Equinor ASA.
3 
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #ifndef OPM_ADAPTIVE_TIME_STEPPING_IMPL_HPP
21 #define OPM_ADAPTIVE_TIME_STEPPING_IMPL_HPP
22 
23 // Improve IDE experience
24 #ifndef OPM_ADAPTIVE_TIME_STEPPING_HPP
25 #include <config.h>
26 #include <opm/simulators/timestepping/AdaptiveTimeStepping.hpp>
27 #include <opm/simulators/timestepping/AdaptiveSimulatorTimer.hpp>
28 #endif
29 
30 #include <dune/common/timer.hh>
31 #include <dune/istl/istlexception.hh>
32 
33 #include <opm/common/Exceptions.hpp>
34 #include <opm/common/ErrorMacros.hpp>
35 #include <opm/common/OpmLog/OpmLog.hpp>
36 #include <opm/common/TimingMacros.hpp>
37 
38 #include <opm/grid/utility/StopWatch.hpp>
39 
40 #include <opm/input/eclipse/Schedule/Tuning.hpp>
41 
42 #include <opm/input/eclipse/Units/Units.hpp>
43 #include <opm/input/eclipse/Units/UnitSystem.hpp>
44 
46 
47 #include <opm/simulators/timestepping/EclTimeSteppingParams.hpp>
48 
49 #include <algorithm>
50 #include <cassert>
51 #include <cmath>
52 #include <sstream>
53 #include <stdexcept>
54 
55 #include <boost/date_time/posix_time/posix_time.hpp>
56 #include <fmt/format.h>
57 #include <fmt/ranges.h>
58 namespace Opm {
59 /*********************************************
60  * Public methods of AdaptiveTimeStepping
61  * ******************************************/
62 
63 
65 template<class TypeTag>
66 AdaptiveTimeStepping<TypeTag>::
67 AdaptiveTimeStepping(const UnitSystem& unit_system,
68  const SimulatorReport& report,
69  const double max_next_tstep,
70  const bool terminal_output
71 )
72  : time_step_control_{}
73  , restart_factor_{Parameters::Get<Parameters::SolverRestartFactor<Scalar>>()} // 0.33
74  , growth_factor_{Parameters::Get<Parameters::SolverGrowthFactor<Scalar>>()} // 2.0
75  , max_growth_{Parameters::Get<Parameters::SolverMaxGrowth<Scalar>>()} // 3.0
76  , max_time_step_{
77  Parameters::Get<Parameters::SolverMaxTimeStepInDays<Scalar>>() * 24 * 60 * 60} // 365.25
78  , min_time_step_{
79  unit_system.to_si(UnitSystem::measure::time,
80  Parameters::Get<Parameters::SolverMinTimeStep<Scalar>>())} // 1e-12;
81  , ignore_convergence_failure_{
82  Parameters::Get<Parameters::SolverContinueOnConvergenceFailure>()} // false;
83  , solver_restart_max_{Parameters::Get<Parameters::SolverMaxRestarts>()} // 10
84  , solver_verbose_{Parameters::Get<Parameters::SolverVerbosity>() > 0 && terminal_output} // 2
85  , timestep_verbose_{Parameters::Get<Parameters::TimeStepVerbosity>() > 0 && terminal_output} // 2
86  , suggested_next_timestep_{
87  (max_next_tstep <= 0 ? Parameters::Get<Parameters::InitialTimeStepInDays>()
88  : max_next_tstep) * 24 * 60 * 60} // 1.0
89  , full_timestep_initially_{Parameters::Get<Parameters::FullTimeStepInitially>()} // false
90  , timestep_after_event_{
91  Parameters::Get<Parameters::TimeStepAfterEventInDays<Scalar>>() * 24 * 60 * 60} // 1e30
92  , use_newton_iteration_{false}
93  , min_time_step_before_shutting_problematic_wells_{
94  Parameters::Get<Parameters::MinTimeStepBeforeShuttingProblematicWellsInDays>() * unit::day}
95  , report_(report)
96 {
97  init_(unit_system);
98 }
99 
106 template<class TypeTag>
107 AdaptiveTimeStepping<TypeTag>::
108 AdaptiveTimeStepping(double max_next_tstep,
109  const Tuning& tuning,
110  const UnitSystem& unit_system,
111  const SimulatorReport& report,
112  const bool terminal_output
113 )
114  : time_step_control_{}
115  , restart_factor_{tuning.TSFCNV}
116  , growth_factor_{tuning.TFDIFF}
117  , max_growth_{tuning.TSFMAX}
118  , max_time_step_{tuning.TSMAXZ} // 365.25
119  , min_time_step_{tuning.TSMINZ} // 0.1;
120  , ignore_convergence_failure_{true}
121  , solver_restart_max_{Parameters::Get<Parameters::SolverMaxRestarts>()} // 10
122  , solver_verbose_{Parameters::Get<Parameters::SolverVerbosity>() > 0 && terminal_output} // 2
123  , timestep_verbose_{Parameters::Get<Parameters::TimeStepVerbosity>() > 0 && terminal_output} // 2
124  , suggested_next_timestep_{
125  max_next_tstep <= 0 ? Parameters::Get<Parameters::InitialTimeStepInDays>() * 24 * 60 * 60
126  : max_next_tstep} // 1.0
127  , full_timestep_initially_{Parameters::Get<Parameters::FullTimeStepInitially>()} // false
128  , timestep_after_event_{tuning.TMAXWC} // 1e30
129  , use_newton_iteration_{false}
130  , min_time_step_before_shutting_problematic_wells_{
131  Parameters::Get<Parameters::MinTimeStepBeforeShuttingProblematicWellsInDays>() * unit::day}
132  , report_(report)
133 {
134  init_(unit_system);
135 }
136 
137 template<class TypeTag>
138 bool
139 AdaptiveTimeStepping<TypeTag>::
140 operator==(const AdaptiveTimeStepping<TypeTag>& rhs) const
141 {
142  if (this->time_step_control_type_ != rhs.time_step_control_type_ ||
143  (this->time_step_control_ && !rhs.time_step_control_) ||
144  (!this->time_step_control_ && rhs.time_step_control_)) {
145  return false;
146  }
147 
148  bool result = false;
149  switch (this->time_step_control_type_) {
150  case TimeStepControlType::HardCodedTimeStep:
151  result = castAndComp<HardcodedTimeStepControl>(rhs);
152  break;
153  case TimeStepControlType::PIDAndIterationCount:
154  result = castAndComp<PIDAndIterationCountTimeStepControl>(rhs);
155  break;
156  case TimeStepControlType::SimpleIterationCount:
157  result = castAndComp<SimpleIterationCountTimeStepControl>(rhs);
158  break;
159  case TimeStepControlType::PID:
160  result = castAndComp<PIDTimeStepControl>(rhs);
161  break;
162  case TimeStepControlType::General3rdOrder:
163  result = castAndComp<General3rdOrderController>(rhs);
164  break;
165  }
166 
167  return result &&
168  this->restart_factor_ == rhs.restart_factor_ &&
169  this->growth_factor_ == rhs.growth_factor_ &&
170  this->max_growth_ == rhs.max_growth_ &&
171  this->max_time_step_ == rhs.max_time_step_ &&
172  this->min_time_step_ == rhs.min_time_step_ &&
173  this->ignore_convergence_failure_ == rhs.ignore_convergence_failure_ &&
174  this->solver_restart_max_== rhs.solver_restart_max_ &&
175  this->solver_verbose_ == rhs.solver_verbose_ &&
176  this->full_timestep_initially_ == rhs.full_timestep_initially_ &&
177  this->timestep_after_event_ == rhs.timestep_after_event_ &&
178  this->use_newton_iteration_ == rhs.use_newton_iteration_ &&
179  this->min_time_step_before_shutting_problematic_wells_ ==
180  rhs.min_time_step_before_shutting_problematic_wells_;
181 }
182 
183 template<class TypeTag>
184 void
185 AdaptiveTimeStepping<TypeTag>::
186 registerParameters()
187 {
188  registerEclTimeSteppingParameters<Scalar>();
189  detail::registerAdaptiveParameters();
190 }
191 
192 // See Doxygen comment on the declaration in AdaptiveTimeStepping.hpp.
193 template<class TypeTag>
194 template <class Solver>
195 SimulatorReport
197 step(const SimulatorTimer& simulator_timer,
198  Solver& solver,
199  const bool is_event,
200  const TuningUpdateCallback& tuning_updater)
201 {
202  SubStepper<Solver> sub_stepper{
203  *this, simulator_timer, solver, is_event, tuning_updater,
204  };
205  return sub_stepper.run();
206 }
207 
208 template<class TypeTag>
209 template<class Serializer>
210 void
212 serializeOp(Serializer& serializer)
213 {
214  serializer(this->time_step_control_type_);
215  switch (this->time_step_control_type_) {
216  case TimeStepControlType::HardCodedTimeStep:
217  allocAndSerialize<HardcodedTimeStepControl>(serializer);
218  break;
219  case TimeStepControlType::PIDAndIterationCount:
220  allocAndSerialize<PIDAndIterationCountTimeStepControl>(serializer);
221  break;
222  case TimeStepControlType::SimpleIterationCount:
223  allocAndSerialize<SimpleIterationCountTimeStepControl>(serializer);
224  break;
225  case TimeStepControlType::PID:
226  allocAndSerialize<PIDTimeStepControl>(serializer);
227  break;
228  case TimeStepControlType::General3rdOrder:
229  allocAndSerialize<General3rdOrderController>(serializer);
230  break;
231  }
232  serializer(this->restart_factor_);
233  serializer(this->growth_factor_);
234  serializer(this->max_growth_);
235  serializer(this->max_time_step_);
236  serializer(this->min_time_step_);
237  serializer(this->ignore_convergence_failure_);
238  serializer(this->solver_restart_max_);
239  serializer(this->solver_verbose_);
240  serializer(this->timestep_verbose_);
241  serializer(this->suggested_next_timestep_);
242  serializer(this->full_timestep_initially_);
243  serializer(this->timestep_after_event_);
244  serializer(this->use_newton_iteration_);
245  serializer(this->min_time_step_before_shutting_problematic_wells_);
246 }
247 
248 template<class TypeTag>
249 SimulatorReport&
250 AdaptiveTimeStepping<TypeTag>::
251 report()
252 {
253  return report_;
254 }
255 
256 template<class TypeTag>
257 AdaptiveTimeStepping<TypeTag>
258 AdaptiveTimeStepping<TypeTag>::
259 serializationTestObjectHardcoded()
260 {
261  return serializationTestObject_<HardcodedTimeStepControl>();
262 }
263 
264 template<class TypeTag>
265 AdaptiveTimeStepping<TypeTag>
266 AdaptiveTimeStepping<TypeTag>::
267 serializationTestObjectPID()
268 {
269  return serializationTestObject_<PIDTimeStepControl>();
270 }
271 
272 template<class TypeTag>
273 AdaptiveTimeStepping<TypeTag>
274 AdaptiveTimeStepping<TypeTag>::
275 serializationTestObjectPIDIt()
276 {
277  return serializationTestObject_<PIDAndIterationCountTimeStepControl>();
278 }
279 
280 template<class TypeTag>
281 AdaptiveTimeStepping<TypeTag>
282 AdaptiveTimeStepping<TypeTag>::
283 serializationTestObjectSimple()
284 {
285  return serializationTestObject_<SimpleIterationCountTimeStepControl>();
286 }
287 
288 template<class TypeTag>
289 AdaptiveTimeStepping<TypeTag>
290 AdaptiveTimeStepping<TypeTag>::
291 serializationTestObject3rdOrder()
292 {
293  return serializationTestObject_<General3rdOrderController>();
294 }
295 
296 
297 template<class TypeTag>
298 void
300 setSuggestedNextStep(const double x)
301 {
302  this->suggested_next_timestep_ = x;
303 }
304 
305 template<class TypeTag>
306 double
309 {
310  return this->suggested_next_timestep_;
311 }
312 
313 template<class TypeTag>
316 timeStepControl() const
317 {
318  return *this->time_step_control_;
319 }
320 
321 
322 template<class TypeTag>
323 void
325 updateNEXTSTEP(double max_next_tstep)
326 {
327  // \Note Only update next suggested step if TSINIT was explicitly
328  // set in TUNING or NEXTSTEP is active.
329  if (max_next_tstep > 0) {
330  this->suggested_next_timestep_ = max_next_tstep;
331  }
332 }
333 
334 template<class TypeTag>
335 void
337 updateTUNING(double max_next_tstep, const Tuning& tuning)
338 {
339  this->restart_factor_ = tuning.TSFCNV;
340  this->growth_factor_ = tuning.TFDIFF;
341  this->max_growth_ = tuning.TSFMAX;
342  this->max_time_step_ = tuning.TSMAXZ;
343  updateNEXTSTEP(max_next_tstep);
344  this->timestep_after_event_ = tuning.TMAXWC;
345 }
346 
347 /*********************************************
348  * Private methods of AdaptiveTimeStepping
349  * ******************************************/
350 
351 template<class TypeTag>
352 template<class T, class Serializer>
353 void
355 allocAndSerialize(Serializer& serializer)
356 {
357  if (!serializer.isSerializing()) {
358  this->time_step_control_ = std::make_unique<T>();
359  }
360  serializer(*static_cast<T*>(this->time_step_control_.get()));
361 }
362 
363 template<class TypeTag>
364 template<class T>
365 bool
366 AdaptiveTimeStepping<TypeTag>::
367 castAndComp(const AdaptiveTimeStepping<TypeTag>& Rhs) const
368 {
369  const T* lhs = static_cast<const T*>(this->time_step_control_.get());
370  const T* rhs = static_cast<const T*>(Rhs.time_step_control_.get());
371  return *lhs == *rhs;
372 }
373 
374 template<class TypeTag>
375 void
376 AdaptiveTimeStepping<TypeTag>::
377 maybeModifySuggestedTimeStepAtBeginningOfReportStep_(const double original_time_step,
378  bool is_event)
379 {
380  // init last time step as a fraction of the given time step
381  if (this->suggested_next_timestep_ < 0) {
382  this->suggested_next_timestep_ = this->restart_factor_ * original_time_step;
383  }
384 
385  if (this->full_timestep_initially_) {
386  this->suggested_next_timestep_ = original_time_step;
387  }
388 
389  // use seperate time step after event
390  if (is_event && this->timestep_after_event_ > 0) {
391  this->suggested_next_timestep_ = this->timestep_after_event_;
392  }
393 }
394 
395 template<class TypeTag>
396 template<class Controller>
397 AdaptiveTimeStepping<TypeTag>
398 AdaptiveTimeStepping<TypeTag>::
399 serializationTestObject_()
400 {
401  AdaptiveTimeStepping<TypeTag> result;
402 
403  result.restart_factor_ = 1.0;
404  result.growth_factor_ = 2.0;
405  result.max_growth_ = 3.0;
406  result.max_time_step_ = 4.0;
407  result.min_time_step_ = 5.0;
408  result.ignore_convergence_failure_ = true;
409  result.solver_restart_max_ = 6;
410  result.solver_verbose_ = true;
411  result.timestep_verbose_ = true;
412  result.suggested_next_timestep_ = 7.0;
413  result.full_timestep_initially_ = true;
414  result.use_newton_iteration_ = true;
415  result.min_time_step_before_shutting_problematic_wells_ = 9.0;
416  result.time_step_control_type_ = Controller::Type;
417  result.time_step_control_ =
418  std::make_unique<Controller>(Controller::serializationTestObject());
419 
420  return result;
421 }
422 
423 /*********************************************
424  * Protected methods of AdaptiveTimeStepping
425  * ******************************************/
426 
427 template<class TypeTag>
428 void AdaptiveTimeStepping<TypeTag>::
429 init_(const UnitSystem& unitSystem)
430 {
431  std::tie(time_step_control_type_,
432  time_step_control_,
433  use_newton_iteration_) = detail::createController(unitSystem);
434  // make sure growth factor is something reasonable
435  if (this->growth_factor_ < 1.0) {
436  OPM_THROW(std::runtime_error,
437  "Growth factor cannot be less than 1.");
438  }
439 }
440 
441 
442 
443 /************************************************
444  * Private class SubStepper public methods
445  ************************************************/
446 
447 template<class TypeTag>
448 template<class Solver>
449 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
450 SubStepper(AdaptiveTimeStepping<TypeTag>& adaptive_time_stepping,
451  const SimulatorTimer& simulator_timer,
452  Solver& solver,
453  const bool is_event,
454  const TuningUpdateCallback& tuning_updater)
455  : adaptive_time_stepping_{adaptive_time_stepping}
456  , simulator_timer_{simulator_timer}
457  , solver_{solver}
458  , is_event_{is_event}
459  , tuning_updater_{tuning_updater}
460 {
461 }
462 
463 template<class TypeTag>
464 template<class Solver>
465 AdaptiveTimeStepping<TypeTag>&
466 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
467 getAdaptiveTimerStepper()
468 {
469  return adaptive_time_stepping_;
470 }
471 
472 template<class TypeTag>
473 template<class Solver>
474 SimulatorReport
475 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
476 run()
477 {
478 #ifdef RESERVOIR_COUPLING_ENABLED
479  if (isReservoirCouplingSlave_() && reservoirCouplingSlave_().activated()) {
480  return runStepReservoirCouplingSlave_();
481  }
482  else if (isReservoirCouplingMaster_() && reservoirCouplingMaster_().activated()) {
483  return runStepReservoirCouplingMaster_();
484  }
485  else {
486  return runStepOriginal_();
487  }
488 #else
489  return runStepOriginal_();
490 #endif
491 }
492 
493 /************************************************
494  * Private class SubStepper private methods
495  ************************************************/
496 
497 #ifdef RESERVOIR_COUPLING_ENABLED
498 template<class TypeTag>
499 template<class Solver>
500 bool
501 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
502 isReservoirCouplingMaster_() const
503 {
504  return this->solver_.model().simulator().reservoirCouplingMaster() != nullptr;
505 }
506 
507 template<class TypeTag>
508 template<class Solver>
509 bool
510 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
511 isReservoirCouplingSlave_() const
512 {
513  return this->solver_.model().simulator().reservoirCouplingSlave() != nullptr;
514 }
515 #endif
516 
517 template<class TypeTag>
518 template<class Solver>
519 void
520 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
521 maybeModifySuggestedTimeStepAtBeginningOfReportStep_(const double original_time_step)
522 {
523  this->adaptive_time_stepping_.maybeModifySuggestedTimeStepAtBeginningOfReportStep_(
524  original_time_step, this->is_event_
525  );
526 
527  if (this->adaptive_time_stepping_.time_step_control_type_ != TimeStepControlType::HardCodedTimeStep) {
528  return;
529  }
530 
531  struct ZeroRelativeChange final : RelativeChangeInterface {
532  double relativeChange() const override { return 0.0; }
533  } zero_relative_change;
534 
535  const auto* hardcoded_control = static_cast<const HardcodedTimeStepControl*>(
536  this->adaptive_time_stepping_.time_step_control_.get());
537  AdaptiveSimulatorTimer report_step_timer{
538  this->simulator_timer_.startDateTime(),
539  original_time_step,
540  this->simulator_timer_.simulationTimeElapsed(),
541  original_time_step,
542  this->simulator_timer_.reportStepNum(),
543  maxTimeStep_()
544  };
545 
546  const double hardcoded_initial_step = hardcoded_control->computeTimeStepSize(
547  original_time_step,
548  0,
549  zero_relative_change,
550  report_step_timer);
551  if (std::isfinite(hardcoded_initial_step) && hardcoded_initial_step > 0.0) {
552  this->adaptive_time_stepping_.setSuggestedNextStep(hardcoded_initial_step);
553  }
554 }
555 
556 // The maybeUpdateTuning_() lambda callback is defined in SimulatorFullyImplicit::runStep()
557 // It has to be called for each substep since TUNING might have been changed for next sub step due
558 // to ACTIONX (via NEXTSTEP) or WCYCLE keywords.
559 template<class TypeTag>
560 template<class Solver>
561 bool
562 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
563 maybeUpdateTuning_(double elapsed, double substep_length, int sub_step_number) const
564 {
565  return this->tuning_updater_(elapsed, substep_length, sub_step_number);
566 }
567 
568 template<class TypeTag>
569 template<class Solver>
570 double
571 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
572 maxTimeStep_() const
573 {
574  return this->adaptive_time_stepping_.max_time_step_;
575 }
576 
577 template <class TypeTag>
578 template <class Solver>
579 SimulatorReport
580 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
581 runStepOriginal_()
582 {
583  const auto elapsed = this->simulator_timer_.simulationTimeElapsed();
584  const auto original_time_step = this->simulator_timer_.currentStepLength();
585  const auto report_step = this->simulator_timer_.reportStepNum();
586  maybeUpdateTuning_(elapsed, suggestedNextTimestep_(), /*substep=*/0);
587  maybeModifySuggestedTimeStepAtBeginningOfReportStep_(original_time_step);
588 
589  AdaptiveSimulatorTimer substep_timer{
590  this->simulator_timer_.startDateTime(),
591  original_time_step,
592  elapsed,
593  suggestedNextTimestep_(),
594  report_step,
595  maxTimeStep_()
596  };
597  SubStepIteration<Solver> substepIteration{*this, substep_timer, original_time_step, /*final_step=*/true};
598  return substepIteration.run();
599 }
600 
601 template <class TypeTag>
602 template <class Solver>
603 double
604 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
605 suggestedNextTimestep_() const
606 {
607  return this->adaptive_time_stepping_.suggestedNextStep();
608 }
609 
610 
611 #ifdef RESERVOIR_COUPLING_ENABLED
612 // Throw if the slave has already been terminated by the master. A terminated slave has
613 // disconnected its intercommunicator, so running another coupled substep loop would issue
614 // an MPI_Recv on a null communicator and abort the job. The run loop in
615 // SimulatorFullyImplicit::runStep() stops before this can happen, so this guards
616 // against future regressions of that logic.
617 template <class TypeTag>
618 template <class Solver>
619 void
620 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
621 checkIfSlaveIsTerminated_()
622 {
623  if (reservoirCouplingSlave_().terminated()) {
624  OPM_THROW(ReservoirCouplingError,
625  "Internal error: attempt to run a coupled substep loop after the slave "
626  "has been terminated by the master process");
627  }
628 }
629 
630 // Pick the master's sync-step length for the next outer-loop iteration of
631 // `runStepReservoirCouplingMaster_()`. Includes the chop against slave-
632 // report dates and emits the user-visible log line. See the block comment
633 // above `runStepReservoirCouplingMaster_()` for TSYNC/RSYNC mode definitions.
634 //
635 // TSYNC (default):
636 // Each iteration is one master time step. Start from the master's
637 // adaptive suggestion, capped at the max time step and at the time
638 // remaining until the report-step end, then chop.
639 //
640 // RSYNC:
641 // The outer loop iterates per chunk between slave-report boundaries.
642 // Re-use the previous iteration's chopped value as the candidate for
643 // this iteration (the caller seeds the first iteration with
644 // `original_time_step`), then chop.
645 template <class TypeTag>
646 template <class Solver>
647 double
648 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
649 getRcMasterSyncStepLength_(double prev_step,
650  double current_time,
651  double step_end_time)
652 {
653  const bool sync_at_report_steps = reservoirCouplingMaster_().syncAtReportSteps();
654  double current_step_length;
655  if (sync_at_report_steps) {
656  current_step_length = prev_step;
657  } else {
658  const double remaining = step_end_time - current_time;
659  current_step_length = std::min({suggestedNextTimestep_(), maxTimeStep_(), remaining});
660  // NOTE: The substep timer is later constructed (in the caller) with
661  // current_step_length as its span, and after construction,
662  // substep_timer.currentStepLength() should return the same as
663  // current_step_length. The timer's constructor calls
664  // provideTimeStepEstimate() with suggestedNextTimestep_() as the dt_
665  // estimate and current_step_length as the span. Since we took
666  // current_step_length = min(suggestedNextTimestep_(), maxTimeStep_(), remaining)
667  // here (and only shrink it further via maybeChopSubStep below), the
668  // estimate is always >= the span, so the timer's snap branch (see
669  // AdaptiveSimulatorTimer::provideTimeStepEstimate) fires and clamps
670  // dt_ to current_step_length.
671  }
672  current_step_length = reservoirCouplingMaster_().maybeChopSubStep(current_step_length, current_time);
673  auto num_active = reservoirCouplingMaster_().numActivatedSlaves();
674  OpmLog::info(fmt::format(
675  "\nChoosing next sync time{} between master and {} active slave {}: {:.2f} days",
676  sync_at_report_steps ? " (RSYNC)" : "",
677  num_active, (num_active == 1 ? "process" : "processes"),
678  current_step_length / unit::day
679  ));
680  return current_step_length;
681 }
682 
683 template <class TypeTag>
684 template <class Solver>
685 ReservoirCouplingMaster<typename AdaptiveTimeStepping<TypeTag>::Scalar>&
686 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
687 reservoirCouplingMaster_()
688 {
689  return *(this->solver_.model().simulator().reservoirCouplingMaster());
690 }
691 
692 template <class TypeTag>
693 template <class Solver>
694 ReservoirCouplingSlave<typename AdaptiveTimeStepping<TypeTag>::Scalar>&
695 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
696 reservoirCouplingSlave_()
697 {
698  return *(this->solver_.model().simulator().reservoirCouplingSlave());
699 }
700 
701 // Description of the reservoir coupling master and slave substep loop
702 // -------------------------------------------------------------------
703 // The master and slave processes attempt to reach the end of the report step using a series of substeps
704 // (also called timesteps). Each substep has an upper limit roughly determined by a combination of the
705 // keywords TUNING (through TSINIT, TSMAXZ), NEXSTEP, WCYCLE, and the start of the next report step
706 // (the last substep has to coincide with this time). Note that NEXTSTEP can be updated from an
707 // ACTIONX keyword. Although this comment focuses on the maximum substep limit, there is also a lower
708 // limit on the substep length, and the substep sizes are adjusted automatically (or retried) based on
709 // the convergence behavior of the solver and other criteria.
710 //
711 // The master chooses a "sync step" — the span between successive master<->slave exchanges — via one
712 // of two modes, selected by the `--rescoup-sync-at-report-steps` CLI flag:
713 //
714 // TSYNC (default, flag=false)
715 // Each master time step is a sync step. Every outer iteration of the master substep
716 // loop redistributes group-rate targets to the slaves. This gives fine sync granularity and
717 // matches the reference simulator's coupling semantics.
718 //
719 // RSYNC (flag=true)
720 // Each chunk between slave-report-step boundaries is one sync step. The master's own adaptive
721 // timestepping runs inside the chunk, but slaves only hear back from the master once per chunk.
722 // Provided as a developer escape hatch.
723 //
724 // In both modes the sync step is also limited so as not to overshoot the next slave report date
725 // (`maybeChopSubStep`). A second, drift-based limit (GRUPMAST item 4 / RCMASTS) is parsed but not
726 // yet honored — tracked as a follow-up PR.
727 //
728 // Per sync step:
729 // - Master receives each activated slave's next report date (`receiveNextReportDateFromSlaves`).
730 // - Master picks the sync-step length via `getRcMasterSyncStepLength_()` (mode-dependent) and
731 // chops it so as not to overshoot any slave report date.
732 // - Master sends the chosen sync-step length to each slave (`sendNextTimeStepToSlaves`).
733 //
734 // The slaves use the sync-step end as a fixed point — a "mini report step" — that they reach via one
735 // or more of their own substeps. If the slave's current report step extends beyond the received
736 // sync step, the slave loops waiting for the master to send further sync steps; the loop ends when
737 // the received sync step coincides with the end of the slave's current report step.
738 
739 // Master-side rescoup outer loop. See the block comment above for TSYNC
740 // and RSYNC mode definitions.
741 //
742 // TSYNC (default):
743 // Each outer iteration is one master time step. The sync-step
744 // length is chosen fresh each iteration by `getRcMasterSyncStepLength_()`
745 // from the master's adaptive suggestion (capped at the max time step and
746 // at the remaining report-step time), then chopped against slave-report
747 // dates. `SubStepIteration::run()` is still used but normally handles
748 // only solver-driven chops within a single master time step.
749 //
750 // RSYNC:
751 // The outer loop iterates once per chunk between slave-report boundaries.
752 // The sync-step length starts at the full report step and only shrinks
753 // via `maybeChopSubStep`; the master's adaptive timestepping runs inside
754 // `SubStepIteration` as solver-driven substeps within each chunk.
755 template <class TypeTag>
756 template <class Solver>
757 SimulatorReport
758 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
759 runStepReservoirCouplingMaster_()
760 {
761  int iteration = 0;
762  const double original_time_step = this->simulator_timer_.currentStepLength();
763  double current_time{this->simulator_timer_.simulationTimeElapsed()};
764  double step_end_time = current_time + original_time_step;
765  const double report_step_start_time = current_time;
766  int report_step_substep_offset = 0;
767  // In RSYNC mode this variable persists across outer iterations and
768  // carries the previously-chopped sync span into the next iteration. In
769  // TSYNC mode it is overwritten each iteration by
770  // `getRcMasterSyncStepLength_()`; the initial value is only consumed by
771  // the first helper call in RSYNC mode (as the candidate span for that
772  // iteration).
773  auto current_step_length = original_time_step;
774  auto report_step_idx = this->simulator_timer_.currentStepNum();
775  if (report_step_idx == 0 && iteration == 0) {
776  reservoirCouplingMaster_().initTimeStepping();
777  }
778  SimulatorReport report;
779  // The master needs to know which slaves have activated before it can start the substep loop
780  reservoirCouplingMaster_().maybeReceiveActivationHandshakeFromSlaves(current_time);
781  while (true) {
782  reservoirCouplingMaster_().sendDontTerminateSignalToSlaves(); // Tell the slaves to keep running.
783  reservoirCouplingMaster_().receiveNextReportDateFromSlaves();
784  const bool start_of_report_step = (iteration == 0);
785  if (start_of_report_step) {
786  reservoirCouplingMaster_().initStartOfReportStep(report_step_idx);
787  maybeUpdateTuning_(current_time, suggestedNextTimestep_(), /*substep=*/0);
788  maybeModifySuggestedTimeStepAtBeginningOfReportStep_(original_time_step);
789  }
790  current_step_length = getRcMasterSyncStepLength_(
791  current_step_length, current_time, step_end_time);
792  reservoirCouplingMaster_().sendNextTimeStepToSlaves(current_step_length);
793  AdaptiveSimulatorTimer substep_timer{
794  this->simulator_timer_.startDateTime(),
795  /*stepLength=*/current_step_length,
796  /*elapsedTime=*/current_time,
797  /*timeStepEstimate=*/suggestedNextTimestep_(),
798  /*reportStep=*/this->simulator_timer_.reportStepNum(),
799  maxTimeStep_()
800  };
801  // Make the per-chunk timer log the enclosing report step's span and a
802  // cumulative substep counter (see AdaptiveSimulatorTimer "report step
803  // view"). Without this, log lines would show one sync chunk only.
804  substep_timer.setReportStepStartTime(report_step_start_time);
805  substep_timer.setReportStepTotalTime(step_end_time);
806  substep_timer.setReportStepSubstepOffset(report_step_substep_offset);
807  const bool final_step = ReservoirCoupling::Seconds::compare_gt_or_eq(
808  current_time + current_step_length, step_end_time
809  );
810  // Mark this as the first substep of the "sync" timestep. This flag controls
811  // whether master-slave data exchange should occur in beginTimeStep() in the well model.
812  // It will be cleared after the first runSubStep_() call.
813  reservoirCouplingMaster_().setFirstSubstepOfSyncTimestep(true);
814  // After the first master substep completes, timeStepSucceeded() will
815  // block until slaves finish the sync step and send production data.
816  // This ensures correct summary output for all subsequent substeps.
817  reservoirCouplingMaster_().setNeedsSlaveDataReceive(true);
818  SubStepIteration<Solver> substepIteration{*this, substep_timer, current_step_length, final_step};
819  const auto sub_steps_report = substepIteration.run();
820  report += sub_steps_report;
821  report_step_substep_offset += substep_timer.currentStepNum();
822  current_time += current_step_length;
823  if (final_step) {
824  break;
825  }
826  iteration++;
827  }
828  return report;
829 }
830 
831 template <class TypeTag>
832 template <class Solver>
833 SimulatorReport
834 AdaptiveTimeStepping<TypeTag>::SubStepper<Solver>::
835 runStepReservoirCouplingSlave_()
836 {
837  checkIfSlaveIsTerminated_();
838  int iteration = 0;
839  const double original_time_step = this->simulator_timer_.currentStepLength();
840  double current_time{this->simulator_timer_.simulationTimeElapsed()};
841  double step_end_time = current_time + original_time_step;
842  const double report_step_start_time = current_time;
843  int report_step_substep_offset = 0;
844  SimulatorReport report;
845  auto report_step_idx = this->simulator_timer_.currentStepNum();
846  if (report_step_idx == 0 && iteration == 0) {
847  reservoirCouplingSlave_().initTimeStepping();
848  }
849  while (true) {
850  bool start_of_report_step = (iteration == 0);
851  if (reservoirCouplingSlave_().maybeReceiveTerminateSignalFromMaster()) {
852  // Call MPI_Comm_disconnect() to terminate the MPI communicator, etc..
853  break;
854  }
855  reservoirCouplingSlave_().sendNextReportDateToMasterProcess();
856  const auto timestep = reservoirCouplingSlave_().receiveNextTimeStepFromMaster();
857  if (start_of_report_step) {
858  maybeUpdateTuning_(current_time, suggestedNextTimestep_(), /*substep=*/0);
859  maybeModifySuggestedTimeStepAtBeginningOfReportStep_(timestep);
860  }
861  AdaptiveSimulatorTimer substep_timer{
862  this->simulator_timer_.startDateTime(),
863  /*step_length=*/timestep,
864  /*elapsed_time=*/current_time,
865  /*time_step_estimate=*/suggestedNextTimestep_(),
866  this->simulator_timer_.reportStepNum(),
867  maxTimeStep_()
868  };
869  // Make the per-chunk timer log the enclosing report step's span and a
870  // cumulative substep counter (see AdaptiveSimulatorTimer "report step
871  // view"). Without this, log lines would show one sync chunk only.
872  substep_timer.setReportStepStartTime(report_step_start_time);
873  substep_timer.setReportStepTotalTime(step_end_time);
874  substep_timer.setReportStepSubstepOffset(report_step_substep_offset);
875  const bool final_step = ReservoirCoupling::Seconds::compare_gt_or_eq(
876  current_time + timestep, step_end_time
877  );
878  // Mark this as the first substep of the "sync" timestep. This flag controls
879  // whether master-slave data exchange should occur in beginTimeStep() in the well model.
880  // It will be cleared after the first runSubStep_() call.
881  reservoirCouplingSlave_().setFirstSubstepOfSyncTimestep(true);
882  SubStepIteration<Solver> substepIteration{*this, substep_timer, timestep, final_step};
883  const auto sub_steps_report = substepIteration.run();
884  report += sub_steps_report;
885  report_step_substep_offset += substep_timer.currentStepNum();
886  current_time += timestep;
887  if (final_step) {
888  break;
889  }
890  iteration++;
891  }
892  return report;
893 }
894 #endif // RESERVOIR_COUPLING_ENABLED
895 
896 
897 /************************************************
898  * Private class SubStepIteration public methods
899  ************************************************/
900 
901 template<class TypeTag>
902 template<class Solver>
903 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
904 SubStepIteration(SubStepper<Solver>& substepper,
905  AdaptiveSimulatorTimer& substep_timer,
906  const double original_time_step,
907  bool final_step)
908  : substepper_{substepper}
909  , substep_timer_{substep_timer}
910  , original_time_step_{original_time_step}
911  , final_step_{final_step}
912  , adaptive_time_stepping_{substepper.getAdaptiveTimerStepper()}
913 {
914 }
915 
916 template <class TypeTag>
917 template <class Solver>
918 SimulatorReport
919 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
920 run()
921 {
922  auto& simulator = solver_().model().simulator();
923  auto& problem = simulator.problem();
924  // counter for solver restarts
925  int restarts = 0;
926  SimulatorReport report;
927 
928  // sub step time loop
929  while (!this->substep_timer_.done()) {
930  // if we just chopped the timestep due to convergence i.e. restarts>0
931  // we dont want to update the next timestep based on Tuning
932  if (restarts == 0) {
933  maybeUpdateTuningAndTimeStep_();
934  }
935  const double dt = this->substep_timer_.currentStepLength();
936  if (timeStepVerbose_()) {
937  detail::logTimer(this->substep_timer_);
938  }
939 
940  maybeUpdateLastSubstepOfSyncTimestep_(dt); // Needed for reservoir coupling
941  auto substep_report = runSubStep_();
942  markFirstSubStepAsFinished_(); // Needed for reservoir coupling
943 
944  if (substep_report.converged || checkContinueOnUnconvergedSolution_(dt)) {
945  Dune::Timer perfTimer;
946  perfTimer.start();
947  // Pass substep to eclwriter for summary output
948  problem.setSubStepReport(substep_report);
949  auto& full_report = adaptive_time_stepping_.report();
950  full_report += substep_report;
951  problem.setSimulationReport(full_report);
952  problem.endTimeStep();
953  substep_report.pre_post_time += perfTimer.stop();
954 
955  report += substep_report;
956 
957  OPM_TIMEBLOCK(convergenceSucceeded);
958  ++this->substep_timer_; // advance by current dt
959 
960  const int iterations = getNumIterations_(substep_report);
961  auto dt_estimate = timeStepControlComputeEstimate_(
962  dt, iterations, this->substep_timer_);
963 
964  assert(dt_estimate > 0);
965  dt_estimate = maybeRestrictTimeStepGrowth_(dt, dt_estimate, restarts);
966  restarts = 0; // solver converged, reset restarts counter
967 
968  maybeReportSubStep_(substep_report);
969  if (this->final_step_ && this->substep_timer_.done()) {
970  // if the time step is done we do not need to write it as this will be done
971  // by the simulator anyway.
972  }
973  else {
974  report.success.output_write_time += writeOutput_();
975  }
976 
977  // set new time step length
978  setTimeStep_(dt_estimate);
979 
980  report.success.converged = this->substep_timer_.done();
981  this->substep_timer_.setLastStepFailed(false);
982  }
983  else { // in case of no convergence or time step tolerance test failure
984  OPM_TIMEBLOCK(convergenceFailed);
985  report += substep_report;
986  this->substep_timer_.setLastStepFailed(true);
987  checkTimeStepMaxRestartLimit_(restarts);
988 
989  double new_time_step = restartFactor_() * dt;
990  if (substep_report.time_step_rejected) {
991  const double tol = Parameters::Get<Parameters::TimeStepControlTolerance>();
992  const double safetyFactor = Parameters::Get<Parameters::TimeStepControlSafetyFactor>();
993  const double temp_time_step = std::sqrt(safetyFactor * tol / solver_().model().relativeChange()) * dt;
994  if (temp_time_step < dt) { // added in case suggested time step is not a reduction
995  new_time_step = temp_time_step;
996  }
997  }
998  checkTimeStepMinLimit_(new_time_step);
999  bool wells_shut = false;
1000  if (new_time_step > minTimeStepBeforeClosingWells_()) {
1001  chopTimeStep_(new_time_step);
1002  } else {
1003  wells_shut = chopTimeStepOrCloseFailingWells_(new_time_step);
1004  }
1005  if (wells_shut) {
1006  setTimeStep_(dt); // retry the old timestep
1007  }
1008  else {
1009  restarts++; // only increase if no wells were shut
1010  }
1011  }
1012  problem.setNextTimeStepSize(this->substep_timer_.currentStepLength());
1013  }
1014  updateSuggestedNextStep_();
1015  return report;
1016 }
1017 
1018 
1019 /************************************************
1020  * Private class SubStepIteration private methods
1021  ************************************************/
1022 
1023 
1024 template<class TypeTag>
1025 template<class Solver>
1026 bool
1027 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1028 checkContinueOnUnconvergedSolution_(double dt) const
1029 {
1030  const bool continue_on_uncoverged_solution = ignoreConvergenceFailure_() && dt <= minTimeStep_();
1031  if (continue_on_uncoverged_solution && solverVerbose_()) {
1032  // NOTE: This method is only called if the solver failed to converge.
1033  const auto msg = fmt::format(
1034  "Solver failed to converge but timestep {} is smaller or equal to {}\n"
1035  "which is the minimum threshold given by option --solver-min-time-step\n",
1036  dt, minTimeStep_()
1037  );
1038  OpmLog::problem(msg);
1039  }
1040  return continue_on_uncoverged_solution;
1041 }
1042 
1043 template<class TypeTag>
1044 template<class Solver>
1045 void
1046 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1047 checkTimeStepMaxRestartLimit_(const int restarts) const
1048 {
1049  // If we have restarted (i.e. cut the timestep) too
1050  // many times, we have failed and throw an exception.
1051  if (restarts >= solverRestartMax_()) {
1052  const auto msg = fmt::format(
1053  fmt::runtime("Solver failed to converge after cutting timestep {} times."), restarts
1054  );
1055  if (solverVerbose_()) {
1056  OpmLog::error(msg);
1057  }
1058  // Use throw directly to prevent file and line
1059  throw TimeSteppingBreakdown{msg};
1060  }
1061 }
1062 
1063 template<class TypeTag>
1064 template<class Solver>
1065 void
1066 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1067 checkTimeStepMinLimit_(const double new_time_step) const
1068 {
1069  using Meas = UnitSystem::measure;
1070  // If we have restarted (i.e. cut the timestep) too
1071  // much, we have failed and throw an exception.
1072  if (new_time_step < minTimeStep_()) {
1073  std::string msg = "Solver failed to converge after cutting timestep to ";
1074  if (Parameters::Get<Parameters::EnableTuning>()) {
1075  const UnitSystem& unit_system = solver_().model().simulator().vanguard().eclState().getDeckUnitSystem();
1076  msg += fmt::format(
1077  "{:.3E} {}\nwhich is the minimum threshold given by the TUNING keyword\n",
1078  unit_system.from_si(Meas::time, minTimeStep_()),
1079  unit_system.name(Meas::time)
1080  );
1081  }
1082  else {
1083  msg += fmt::format(
1084  "{:.3E} DAYS\nwhich is the minimum threshold given by option --solver-min-time-step\n",
1085  minTimeStep_() / 86400.0
1086  );
1087  }
1088  if (solverVerbose_()) {
1089  OpmLog::error(msg);
1090  }
1091  // Use throw directly to prevent file and line
1092  throw TimeSteppingBreakdown{msg};
1093  }
1094 }
1095 
1096 template<class TypeTag>
1097 template<class Solver>
1098 void
1099 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1100 chopTimeStep_(const double new_time_step)
1101 {
1102  setTimeStep_(new_time_step);
1103  if (solverVerbose_()) {
1104  const auto msg = fmt::format(fmt::runtime("{}\nTimestep chopped to {} days\n"),
1105  this->cause_of_failure_,
1106  unit::convert::to(this->substep_timer_.currentStepLength(), unit::day));
1107  OpmLog::problem(msg);
1108  }
1109 }
1110 
1111 template<class TypeTag>
1112 template<class Solver>
1113 bool
1114 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1115 chopTimeStepOrCloseFailingWells_(const double new_time_step)
1116 {
1117  bool wells_shut = false;
1118  // We are below the threshold, and will check if there are any
1119  // wells that fails repeatedly (that means that it fails in the last three steps)
1120  // we should close rather than chopping again.
1121  // If we already have chopped the timestep two times that is
1122  // new_time_step < minTimeStepBeforeClosingWells_()*restartFactor_()*restartFactor_()
1123  // We also shut wells that fails only on this step.
1124  const bool requireRepeatedFailures =
1125  new_time_step > (minTimeStepBeforeClosingWells_() * restartFactor_() * restartFactor_());
1126  const std::set<std::string> failing_wells =
1127  detail::consistentlyFailingWells(solver_().model().stepReports(), requireRepeatedFailures);
1128 
1129  if (failing_wells.empty()) {
1130  // Found no wells to close, chop the timestep
1131  chopTimeStep_(new_time_step);
1132  } else {
1133  // Close all consistently failing wells that are not under group control
1134  std::vector<std::string> shut_wells;
1135  for (const auto& well : failing_wells) {
1136  const bool was_shut =
1137  solver_().model().wellModel().forceShutWellByName(well,
1138  this->substep_timer_.simulationTimeElapsed(),
1139  /*dont_shut_grup_wells =*/ true);
1140  if (was_shut) {
1141  shut_wells.push_back(well);
1142  }
1143  }
1144  // If no wells are closed we also try to shut wells under group control
1145  if (shut_wells.empty()) {
1146  for (const auto& well : failing_wells) {
1147  const bool was_shut =
1148  solver_().model().wellModel().forceShutWellByName(well,
1149  this->substep_timer_.simulationTimeElapsed(),
1150  /*dont_shut_grup_wells =*/ false);
1151  if (was_shut) {
1152  shut_wells.push_back(well);
1153  }
1154  }
1155  }
1156  // If still no wells are closed we must fall back to chopping again
1157  if (shut_wells.empty()) {
1158  chopTimeStep_(new_time_step);
1159  } else {
1160  wells_shut = true;
1161  if (solverVerbose_()) {
1162  const std::string msg =
1163  fmt::format(fmt::runtime("\nProblematic well(s) were shut: {}"
1164  "(retrying timestep)\n"),
1165  fmt::join(shut_wells, " "));
1166  OpmLog::problem(msg);
1167  }
1168  }
1169  }
1170  return wells_shut;
1171 }
1172 
1173 template<class TypeTag>
1174 template<class Solver>
1175 boost::posix_time::ptime
1176 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1177 currentDateTime_() const
1178 {
1179  return simulatorTimer_().currentDateTime();
1180 }
1181 
1182 template<class TypeTag>
1183 template<class Solver>
1184 int
1185 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1186 getNumIterations_(const SimulatorReportSingle &substep_report) const
1187 {
1188  if (useNewtonIteration_()) {
1189  return substep_report.total_newton_iterations;
1190  }
1191  else {
1192  return substep_report.total_linear_iterations;
1193  }
1194 }
1195 
1196 template<class TypeTag>
1197 template<class Solver>
1198 double
1199 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1200 growthFactor_() const
1201 {
1202  return this->adaptive_time_stepping_.growth_factor_;
1203 }
1204 
1205 template<class TypeTag>
1206 template<class Solver>
1207 bool
1208 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1209 ignoreConvergenceFailure_() const
1210 {
1211  return adaptive_time_stepping_.ignore_convergence_failure_;
1212 }
1213 
1214 template<class TypeTag>
1215 template<class Solver>
1216 bool
1217 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1218 isReservoirCouplingMaster_() const
1219 {
1220  return this->substepper_.isReservoirCouplingMaster_();
1221 }
1222 
1223 template<class TypeTag>
1224 template<class Solver>
1225 bool
1226 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1227 isReservoirCouplingSlave_() const
1228 {
1229  return this->substepper_.isReservoirCouplingSlave_();
1230 }
1231 
1232 template<class TypeTag>
1233 template<class Solver>
1234 void
1235 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1236 markFirstSubStepAsFinished_() const
1237 {
1238 #ifdef RESERVOIR_COUPLING_ENABLED
1239  // Clear the first-substep flag after the first runSubStep_() call.
1240  // This ensures that master-slave synchronization only happens once per sync timestep,
1241  // not on retry attempts after convergence-driven timestep chops.
1242  if (isReservoirCouplingMaster_()) {
1243  reservoirCouplingMaster_().setFirstSubstepOfSyncTimestep(false);
1244  }
1245  else if (isReservoirCouplingSlave_()) {
1246  reservoirCouplingSlave_().setFirstSubstepOfSyncTimestep(false);
1247  }
1248 #endif
1249  return;
1250 }
1251 
1252 template<class TypeTag>
1253 template<class Solver>
1254 double
1255 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1256 maxGrowth_() const
1257 {
1258  return this->adaptive_time_stepping_.max_growth_;
1259 }
1260 
1261 template<class TypeTag>
1262 template<class Solver>
1263 void
1264 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1265 maybeReportSubStep_(SimulatorReportSingle substep_report) const
1266 {
1267  if (timeStepVerbose_()) {
1268  std::ostringstream ss;
1269  substep_report.reportStep(ss);
1270  OpmLog::info(ss.str());
1271  }
1272 }
1273 
1274 template<class TypeTag>
1275 template<class Solver>
1276 double
1277 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1278 maybeRestrictTimeStepGrowth_(const double dt, double dt_estimate, const int restarts) const
1279 {
1280  // limit the growth of the timestep size by the growth factor
1281  dt_estimate = std::min(dt_estimate, double(maxGrowth_() * dt));
1282  assert(dt_estimate > 0);
1283  // further restrict time step size growth after convergence problems
1284  if (restarts > 0) {
1285  dt_estimate = std::min(growthFactor_() * dt, dt_estimate);
1286  }
1287 
1288  return dt_estimate;
1289 }
1290 
1291 
1292 template<class TypeTag>
1293 template<class Solver>
1294 void
1295 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1296 maybeUpdateLastSubstepOfSyncTimestep_([[maybe_unused]] const double dt)
1297 {
1298 #ifdef RESERVOIR_COUPLING_ENABLED
1299  // For reservoir coupling slaves: predict if this substep will complete
1300  // the sync timestep. If so, timeStepSucceeded() will send production
1301  // data to the master (which is blocking on receive after its first substep).
1302  // This is used for summary data synchronization between slaves and master.
1303  if (isReservoirCouplingSlave_()) {
1304  const bool is_last = ReservoirCoupling::Seconds::compare_gt_or_eq(
1305  this->substep_timer_.simulationTimeElapsed() + dt,
1306  this->substep_timer_.totalTime()
1307  );
1308  reservoirCouplingSlave_().setLastSubstepOfSyncTimestep(is_last);
1309  }
1310 #endif
1311 }
1312 
1313 // The maybeUpdateTuning_() lambda callback is defined in SimulatorFullyImplicit::runStep()
1314 // It has to be called for each substep since TUNING might have been changed for next sub step due
1315 // to ACTIONX (via NEXTSTEP) or WCYCLE keywords.
1316 template<class TypeTag>
1317 template<class Solver>
1318 void
1319 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1320 maybeUpdateTuningAndTimeStep_()
1321 {
1322  // TODO: This function is currently only called if NEXTSTEP is activated from ACTIONX or
1323  // if the WCYCLE keyword needs to modify the current timestep. So this method should rather
1324  // be named maybeUpdateTimeStep_() or similar, since it should not update the tuning. However,
1325  // the current definition of the maybeUpdateTuning_() callback is actually calling
1326  // adaptiveTimeStepping_->updateTUNING(max_next_tstep, tuning) which is updating the tuning
1327  // see SimulatorFullyImplicit::runStep() for more details.
1328  const auto old_value = suggestedNextTimestep_();
1329  if (this->substepper_.maybeUpdateTuning_(this->substep_timer_.simulationTimeElapsed(),
1330  this->substep_timer_.currentStepLength(),
1331  this->substep_timer_.currentStepNum()))
1332  {
1333  // Either NEXTSTEP and WCYCLE wants to change the current time step, but they cannot
1334  // change the current time step directly. Instead, they change the suggested next time step
1335  // by calling updateNEXTSTEP() via the maybeUpdateTuning() callback. We now need to update
1336  // the current time step to the new suggested time step and reset the suggested time step
1337  // to the old value.
1338  setTimeStep_(suggestedNextTimestep_());
1339  setSuggestedNextStep_(old_value);
1340  }
1341 }
1342 
1343 template<class TypeTag>
1344 template<class Solver>
1345 double
1346 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1347 minTimeStepBeforeClosingWells_() const
1348 {
1349  return this->adaptive_time_stepping_.min_time_step_before_shutting_problematic_wells_;
1350 }
1351 
1352 template<class TypeTag>
1353 template<class Solver>
1354 double
1355 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1356 minTimeStep_() const
1357 {
1358  return this->adaptive_time_stepping_.min_time_step_;
1359 }
1360 
1361 #ifdef RESERVOIR_COUPLING_ENABLED
1362 template<class TypeTag>
1363 template<class Solver>
1364 ReservoirCouplingMaster<typename AdaptiveTimeStepping<TypeTag>::Scalar>&
1365 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1366 reservoirCouplingMaster_() const
1367 {
1368  return this->substepper_.reservoirCouplingMaster_();
1369 }
1370 
1371 template<class TypeTag>
1372 template<class Solver>
1373 ReservoirCouplingSlave<typename AdaptiveTimeStepping<TypeTag>::Scalar>&
1374 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1375 reservoirCouplingSlave_() const
1376 {
1377  return this->substepper_.reservoirCouplingSlave_();
1378 }
1379 #endif
1380 
1381 template<class TypeTag>
1382 template<class Solver>
1383 double
1384 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1385 restartFactor_() const
1386 {
1387  return this->adaptive_time_stepping_.restart_factor_;
1388 }
1389 
1390 template<class TypeTag>
1391 template<class Solver>
1392 SimulatorReportSingle
1393 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1394 runSubStep_()
1395 {
1396  OPM_TIMEFUNCTION();
1397  SimulatorReportSingle substep_report;
1398 
1399  auto handleFailure = [this, &substep_report]
1400  (const std::string& failure_reason, const std::exception& e, bool log_exception = true)
1401  {
1402  substep_report = solver_().failureReport();
1403  this->cause_of_failure_ = failure_reason;
1404  if (log_exception && solverVerbose_()) {
1405  OpmLog::debug(std::string("Caught Exception: ") + e.what());
1406  }
1407  };
1408 
1409  try {
1410  substep_report = solver_().step(this->substep_timer_, &this->adaptive_time_stepping_.timeStepControl());
1411  if (solverVerbose_()) {
1412  // report number of linear iterations
1413  OpmLog::debug("Overall linear iterations used: "
1414  + std::to_string(substep_report.total_linear_iterations));
1415  }
1416  }
1417  catch (const TooManyIterations& e) {
1418  handleFailure("Solver convergence failure - Iteration limit reached", e);
1419  }
1420  catch (const TimeSteppingBreakdown& e) {
1421  handleFailure(e.what(), e);
1422  }
1423  catch (const ConvergenceMonitorFailure& e) {
1424  handleFailure("Convergence monitor failure", e, /*log_exception=*/false);
1425  }
1426  catch (const LinearSolverProblem& e) {
1427  handleFailure("Linear solver convergence failure", e);
1428  }
1429  catch (const NumericalProblem& e) {
1430  handleFailure("Solver convergence failure - Numerical problem encountered", e);
1431  }
1432  catch (const std::runtime_error& e) {
1433  handleFailure("Runtime error encountered", e);
1434  }
1435  catch (const Dune::ISTLError& e) {
1436  handleFailure("ISTL error - Time step too large", e);
1437  }
1438  catch (const Dune::MatrixBlockError& e) {
1439  handleFailure("Matrix block error", e);
1440  }
1441 
1442  return substep_report;
1443 }
1444 
1445 template<class TypeTag>
1446 template<class Solver>
1447 void
1448 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1449 setTimeStep_(double dt_estimate)
1450 {
1451  this->substep_timer_.provideTimeStepEstimate(dt_estimate);
1452 }
1453 
1454 template<class TypeTag>
1455 template<class Solver>
1456 Solver&
1457 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1458 solver_() const
1459 {
1460  return this->substepper_.solver_;
1461 }
1462 
1463 
1464 template<class TypeTag>
1465 template<class Solver>
1466 int
1467 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1468 solverRestartMax_() const
1469 {
1470  return this->adaptive_time_stepping_.solver_restart_max_;
1471 }
1472 
1473 template<class TypeTag>
1474 template<class Solver>
1475 void
1476 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1477 setSuggestedNextStep_(double step)
1478 {
1479  this->adaptive_time_stepping_.setSuggestedNextStep(step);
1480 }
1481 
1482 template <class TypeTag>
1483 template <class Solver>
1484 const SimulatorTimer&
1485 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1486 simulatorTimer_() const
1487 {
1488  return this->substepper_.simulator_timer_;
1489 }
1490 
1491 template <class TypeTag>
1492 template <class Solver>
1493 bool
1494 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1495 solverVerbose_() const
1496 {
1497  return this->adaptive_time_stepping_.solver_verbose_;
1498 }
1499 
1500 template<class TypeTag>
1501 template<class Solver>
1502 boost::posix_time::ptime
1503 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1504 startDateTime_() const
1505 {
1506  return simulatorTimer_().startDateTime();
1507 }
1508 
1509 template <class TypeTag>
1510 template <class Solver>
1511 double
1512 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1513 suggestedNextTimestep_() const
1514 {
1515  return this->adaptive_time_stepping_.suggestedNextStep();
1516 }
1517 
1518 template <class TypeTag>
1519 template <class Solver>
1520 double
1521 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1522 timeStepControlComputeEstimate_(const double dt, const int iterations,
1523  const AdaptiveSimulatorTimer& substepTimer) const
1524 {
1525  // create object to compute the time error, simply forwards the call to the model
1526  const SolutionTimeErrorSolverWrapper<Solver> relative_change{solver_()};
1527  return this->adaptive_time_stepping_.time_step_control_->computeTimeStepSize(
1528  dt, iterations, relative_change, substepTimer);
1529 }
1530 
1531 template <class TypeTag>
1532 template <class Solver>
1533 bool
1534 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1535 timeStepVerbose_() const
1536 {
1537  return this->adaptive_time_stepping_.timestep_verbose_;
1538 }
1539 
1540 // The suggested time step is the stepsize that will be used as a first try for
1541 // the next sub step. It is updated at the end of each substep. It can also be
1542 // updated by the TUNING or NEXTSTEP keywords at the beginning of each report step or
1543 // at the beginning of each substep by the ACTIONX keyword (via NEXTSTEP), this is
1544 // done by the maybeUpdateTuning_() method which is called at the beginning of each substep
1545 // (and the begginning of each report step). Note that the WCYCLE keyword can also update the
1546 // suggested time step via the maybeUpdateTuning_() method.
1547 template <class TypeTag>
1548 template <class Solver>
1549 void
1550 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1551 updateSuggestedNextStep_()
1552 {
1553  auto suggested_next_step = this->substep_timer_.currentStepLength();
1554  if (! std::isfinite(suggested_next_step)) { // check for NaN
1555  suggested_next_step = this->original_time_step_;
1556  }
1557  if (timeStepVerbose_()) {
1558  std::ostringstream ss;
1559  this->substep_timer_.report(ss);
1560  ss << "Suggested next step size = "
1561  << unit::convert::to(suggested_next_step, unit::day) << " (days)" << std::endl;
1562  OpmLog::debug(ss.str());
1563  }
1564  setSuggestedNextStep_(suggested_next_step);
1565 }
1566 
1567 template <class TypeTag>
1568 template <class Solver>
1569 bool
1570 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1571 useNewtonIteration_() const
1572 {
1573  return this->adaptive_time_stepping_.use_newton_iteration_;
1574 }
1575 
1576 template <class TypeTag>
1577 template <class Solver>
1578 double
1579 AdaptiveTimeStepping<TypeTag>::SubStepIteration<Solver>::
1580 writeOutput_() const
1581 {
1582  time::StopWatch perf_timer;
1583  perf_timer.start();
1584  auto& problem = solver_().model().simulator().problem();
1585  problem.writeOutput(true);
1586  return perf_timer.secsSinceStart();
1587 }
1588 
1589 /************************************************
1590  * Private class SolutionTimeErrorSolverWrapper
1591  * **********************************************/
1592 
1593 template<class TypeTag>
1594 template<class Solver>
1595 AdaptiveTimeStepping<TypeTag>::
1596 SolutionTimeErrorSolverWrapper<Solver>::
1597 SolutionTimeErrorSolverWrapper(const Solver& solver)
1598  : solver_{solver}
1599 {}
1600 
1601 template<class TypeTag>
1602 template<class Solver>
1603 double AdaptiveTimeStepping<TypeTag>::SolutionTimeErrorSolverWrapper<Solver>::relativeChange() const
1604 {
1605  // returns: || u^n+1 - u^n || / || u^n+1 ||
1606  return solver_.model().relativeChange();
1607 }
1608 
1609 } // namespace Opm
1610 
1611 #endif // OPM_ADAPTIVE_TIME_STEPPING_IMPL_HPP
This file provides the infrastructure to retrieve run-time parameters.
void setSuggestedNextStep(const double x)
Set the suggested length for the next substep [s].
Definition: AdaptiveTimeStepping_impl.hpp:300
Adaptive time-stepping coordinator for the black-oil simulator.
Definition: AdaptiveTimeStepping.hpp:92
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
void updateNEXTSTEP(double max_next_tstep)
Set suggested_next_timestep_ to max_next_tstep iff max_next_tstep > 0.
Definition: AdaptiveTimeStepping_impl.hpp:325
SimulatorReport step(const SimulatorTimer &simulator_timer, Solver &solver, const bool is_event, const TuningUpdateCallback &tuning_updater)
Run one report step by orchestrating adaptive substepping.
Definition: AdaptiveTimeStepping_impl.hpp:197
void updateTUNING(double max_next_tstep, const Tuning &tuning)
Apply TUNING keyword parameters.
Definition: AdaptiveTimeStepping_impl.hpp:337
TimeStepControlInterface.
Definition: TimeStepControlInterface.hpp:50
double suggestedNextStep() const
Current suggested length for the next substep [s].
Definition: AdaptiveTimeStepping_impl.hpp:308
Definition: SimulatorReport.hpp:121
Definition: SimulatorTimer.hpp:38
std::function< bool(double elapsed, double substep_length, int sub_step_number)> TuningUpdateCallback
Callback invoked at the start of each substep to apply TUNING, NEXTSTEP (via ACTIONX), and WCYCLE updates.
Definition: AdaptiveTimeStepping.hpp:119
static bool compare_gt_or_eq(double a, double b)
Determines if a is greater than b within the specified tolerance.
Definition: ReservoirCoupling.cpp:183