SimulatorReport AdaptiveTimeStepping:: stepImpl( const SimulatorTimer& simulatorTimer, Solver& solver, State& state, WState& well_state, Output* outputWriter ) { SimulatorReport report; const double timestep = simulatorTimer.currentStepLength(); // init last time step as a fraction of the given time step if( suggested_next_timestep_ < 0 ) { suggested_next_timestep_ = restart_factor_ * timestep; } if (full_timestep_initially_) { suggested_next_timestep_ = timestep; } // TODO // take change in well state into account // create adaptive step timer with previously used sub step size AdaptiveSimulatorTimer substepTimer( simulatorTimer, suggested_next_timestep_, max_time_step_ ); // copy states in case solver has to be restarted (to be revised) State last_state( state ); WState last_well_state( well_state ); // counter for solver restarts int restarts = 0; // sub step time loop while( ! substepTimer.done() ) { // get current delta t const double dt = substepTimer.currentStepLength() ; if( timestep_verbose_ ) { std::ostringstream ss; ss <<" Substep " << substepTimer.currentStepNum() << ", stepsize " << unit::convert::to(substepTimer.currentStepLength(), unit::day) << " days."; OpmLog::info(ss.str()); } SimulatorReport substepReport; try { substepReport = solver.step( substepTimer, state, well_state); report += substepReport; if( solver_verbose_ ) { // report number of linear iterations OpmLog::note("Overall linear iterations used: " + std::to_string(substepReport.total_linear_iterations)); } } catch (const Opm::NumericalProblem& e) { detail::logException(e, solver_verbose_); // since linearIterations is < 0 this will restart the solver } catch (const std::runtime_error& e) { detail::logException(e, solver_verbose_); // also catch linear solver not converged } catch (const Dune::ISTLError& e) { detail::logException(e, solver_verbose_); // also catch errors in ISTL AMG that occur when time step is too large } catch (const Dune::MatrixBlockError& e) { detail::logException(e, solver_verbose_); // this can be thrown by ISTL's ILU0 in block mode, yet is not an ISTLError } if( substepReport.converged ) { // advance by current dt ++substepTimer; // create object to compute the time error, simply forwards the call to the model detail::SolutionTimeErrorSolverWrapper< Solver, State > relativeChange( solver, last_state, state ); // compute new time step estimate double dtEstimate = timeStepControl_->computeTimeStepSize( dt, substepReport.total_linear_iterations, relativeChange, substepTimer.simulationTimeElapsed()); // limit the growth of the timestep size by the growth factor dtEstimate = std::min( dtEstimate, double(max_growth_ * dt) ); // further restrict time step size growth after convergence problems if( restarts > 0 ) { dtEstimate = std::min( growth_factor_ * dt, dtEstimate ); // solver converged, reset restarts counter restarts = 0; } if( timestep_verbose_ ) { std::ostringstream ss; ss << " Substep summary: "; if (report.total_well_iterations != 0) { ss << "well iterations = " << report.total_well_iterations << ", "; } ss << "newton iterations = " << report.total_newton_iterations << ", " << "linearizations = " << report.total_linearizations << " (" << report.assemble_time << " sec), " << "linear iterations = " << report.total_linear_iterations << " (" << report.linear_solve_time << " sec)"; OpmLog::info(ss.str()); } // write data if outputWriter was provided // if the time step is done we do not need // to write it as this will be done by the simulator // anyway. if( outputWriter && !substepTimer.done() ) { Opm::time::StopWatch perfTimer; perfTimer.start(); bool substep = true; const auto& physicalModel = solver.model(); outputWriter->writeTimeStep( substepTimer, state, well_state, physicalModel, substep); report.output_write_time += perfTimer.secsSinceStart(); } // set new time step length substepTimer.provideTimeStepEstimate( dtEstimate ); // update states last_state = state ; last_well_state = well_state; report.converged = substepTimer.done(); } else // in case of no convergence (linearIterations < 0) { report.converged = false; // increase restart counter if( restarts >= solver_restart_max_ ) { const auto msg = std::string("Solver failed to converge after ") + std::to_string(restarts) + " restarts."; if (solver_verbose_) { OpmLog::error(msg); } OPM_THROW_NOLOG(Opm::NumericalProblem, msg); } const double newTimeStep = restart_factor_ * dt; // we need to revise this substepTimer.provideTimeStepEstimate( newTimeStep ); if( solver_verbose_ ) { std::string msg; msg = "Solver convergence failed, restarting solver with new time step (" + std::to_string(unit::convert::to( newTimeStep, unit::day )) + " days).\n"; OpmLog::problem(msg); } // reset states state = last_state; well_state = last_well_state; ++restarts; } } // store estimated time step for next reportStep suggested_next_timestep_ = substepTimer.currentStepLength(); if( timestep_verbose_ ) { std::ostringstream ss; substepTimer.report(ss); ss << "Suggested next step size = " << unit::convert::to( suggested_next_timestep_, unit::day ) << " (days)" << std::endl; OpmLog::note(ss.str()); } if( ! std::isfinite( suggested_next_timestep_ ) ) { // check for NaN suggested_next_timestep_ = timestep; } return report; }
void AdaptiveTimeStepping:: stepImpl( const SimulatorTimer& simulatorTimer, Solver& solver, State& state, WState& well_state, OutputWriter* outputWriter ) { const double timestep = simulatorTimer.currentStepLength(); // init last time step as a fraction of the given time step if( last_timestep_ < 0 ) { last_timestep_ = restart_factor_ * timestep; } // TODO // take change in well state into account // create adaptive step timer with previously used sub step size AdaptiveSimulatorTimer substepTimer( simulatorTimer, last_timestep_, max_time_step_ ); // copy states in case solver has to be restarted (to be revised) State last_state( state ); WState last_well_state( well_state ); // counter for solver restarts int restarts = 0; // sub step time loop while( ! substepTimer.done() ) { // get current delta t const double dt = substepTimer.currentStepLength() ; // initialize time step control in case current state is needed later timeStepControl_->initialize( state ); if( timestep_verbose_ ) { std::cout <<"Substep( " << substepTimer.currentStepNum() << " ), try with stepsize " << unit::convert::to(substepTimer.currentStepLength(), unit::day) << " (days)." << std::endl; } int linearIterations = -1; try { // (linearIterations < 0 means on convergence in solver) linearIterations = solver.step( dt, state, well_state); if( solver_verbose_ ) { // report number of linear iterations std::cout << "Overall linear iterations used: " << linearIterations << std::endl; } } catch (const Opm::NumericalProblem& e) { std::cerr << e.what() << std::endl; // since linearIterations is < 0 this will restart the solver } catch (const std::runtime_error& e) { std::cerr << e.what() << std::endl; // also catch linear solver not converged } // (linearIterations < 0 means no convergence in solver) if( linearIterations >= 0 ) { // advance by current dt ++substepTimer; // compute new time step estimate double dtEstimate = timeStepControl_->computeTimeStepSize( dt, linearIterations, state ); // avoid time step size growth if( restarts > 0 ) { dtEstimate = std::min( growth_factor_ * dt, dtEstimate ); // solver converged, reset restarts counter restarts = 0; } if( timestep_verbose_ ) { std::cout << "Substep( " << substepTimer.currentStepNum()-1 // it was already advanced by ++ << " ) finished at time " << unit::convert::to(substepTimer.simulationTimeElapsed(),unit::day) << " (days)." << std::endl << std::endl; } // write data if outputWriter was provided if( outputWriter ) { outputWriter->writeTimeStep( substepTimer, state, well_state ); } // set new time step length substepTimer.provideTimeStepEstimate( dtEstimate ); // update states last_state = state ; last_well_state = well_state; } else // in case of no convergence (linearIterations < 0) { // increase restart counter if( restarts >= solver_restart_max_ ) { OPM_THROW(Opm::NumericalProblem,"Solver failed to converge after " << restarts << " restarts."); } const double newTimeStep = restart_factor_ * dt; // we need to revise this substepTimer.provideTimeStepEstimate( newTimeStep ); if( solver_verbose_ ) std::cerr << "Solver convergence failed, restarting solver with new time step (" << unit::convert::to( newTimeStep, unit::day ) <<" days)." << std::endl; // reset states state = last_state; well_state = last_well_state; ++restarts; } } // store max of the small time step for next reportStep last_timestep_ = substepTimer.averageStepLength(); if( timestep_verbose_ ) { substepTimer.report( std::cout ); std::cout << "Suggested next step size = " << unit::convert::to( last_timestep_, unit::day ) << " (days)" << std::endl; } if( ! std::isfinite( last_timestep_ ) ) { // check for NaN last_timestep_ = timestep; } }