SimulatorReport
    NonlinearSolver<PhysicalModel>::
    step(const SimulatorTimerInterface& timer,
         const ReservoirState& initial_reservoir_state,
         const WellState& initial_well_state,
         ReservoirState& reservoir_state,
         WellState& well_state)
    {
        SimulatorReport iterReport;
        SimulatorReport report;
        failureReport_ = SimulatorReport();

        // Do model-specific once-per-step calculations.
        model_->prepareStep(timer, initial_reservoir_state, initial_well_state);

        int iteration = 0;

        // Let the model do one nonlinear iteration.

        // Set up for main solver loop.
        bool converged = false;

        // ----------  Main nonlinear solver loop  ----------
        do {
            try {
                // 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 minIter() count has not been reached yet.
                iterReport = model_->nonlinearIteration(iteration, timer, *this, reservoir_state, well_state);

                report += iterReport;
                report.converged = iterReport.converged;

                converged = report.converged;
                iteration += 1;
            }
            catch (...) {
                // if an iteration fails during a time step, all previous iterations
                // count as a failure as well
                failureReport_ += report;
                failureReport_ += model_->failureReport();
                throw;
            }
        } while ( (!converged && (iteration <= maxIter())) || (iteration <= minIter()));

        if (!converged) {
            failureReport_ += report;

            std::string msg = "Solver convergence failure - Failed to complete a time step within " + std::to_string(maxIter()) + " iterations.";
            OPM_THROW_NOLOG(Opm::TooManyIterations, msg);
        }

        // Do model-specific post-step actions.
        model_->afterStep(timer, reservoir_state, well_state);
        report.converged = true;

        return report;
    }
예제 #2
0
        void checkConvergence( const Dune::InverseOperatorResult& result ) const
        {
            // store number of iterations
            iterations_ = result.iterations;

            // Check for failure of linear solver.
            if (!parameters_.ignoreConvergenceFailure_ && !result.converged) {
                const std::string msg("Convergence failure for linear solver.");
                if (isIORank_) {
                    OpmLog::problem(msg);
                }
                OPM_THROW_NOLOG(LinearSolverProblem, msg);
            }
        }
예제 #3
0
    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;
    }