/* Copyright 2013, 2015 SINTEF ICT, Applied Mathematics. Copyright 2015 Dr. Blatt - HPC-Simulation-Software & Services Copyright 2015 NTNU Copyright 2015 IRIS 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 . */ #ifndef OPM_NONLINEARSOLVER_IMPL_HEADER_INCLUDED #define OPM_NONLINEARSOLVER_IMPL_HEADER_INCLUDED #include #include #include namespace Opm { template NonlinearSolver::NonlinearSolver(const SolverParameters& param, std::unique_ptr model_arg) : param_(param), model_(std::move(model_arg)), linearizations_(0), nonlinearIterations_(0), linearIterations_(0), wellIterations_(0), nonlinearIterationsLast_(0), linearIterationsLast_(0), wellIterationsLast_(0) { if (!model_) { OPM_THROW(std::logic_error, "Must provide a non-null model argument for NonlinearSolver."); } } template int NonlinearSolver::linearizations() const { return linearizations_; } template int NonlinearSolver::nonlinearIterations() const { return nonlinearIterations_; } template int NonlinearSolver::linearIterations() const { return linearIterations_; } template int NonlinearSolver::wellIterations() const { return wellIterations_; } template const PhysicalModel& NonlinearSolver::model() const { return *model_; } template PhysicalModel& NonlinearSolver::model() { return *model_; } template int NonlinearSolver::nonlinearIterationsLastStep() const { return nonlinearIterationsLast_; } template int NonlinearSolver::linearIterationsLastStep() const { return linearIterationsLast_; } template int NonlinearSolver::wellIterationsLastStep() const { return wellIterationsLast_; } template SimulatorReport NonlinearSolver:: step(const SimulatorTimerInterface& timer, ReservoirState& reservoir_state, WellState& well_state) { return step(timer, reservoir_state, well_state, reservoir_state, well_state); } template SimulatorReport NonlinearSolver:: 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; } template void NonlinearSolver::SolverParameters:: reset() { // default values for the solver parameters relax_type_ = DAMPEN; relax_max_ = 0.5; relax_increment_ = 0.1; relax_rel_tol_ = 0.2; max_iter_ = 10; min_iter_ = 1; } template NonlinearSolver::SolverParameters:: SolverParameters() { // set default values reset(); } template NonlinearSolver::SolverParameters:: SolverParameters( const ParameterGroup& param ) { // set default values reset(); // overload with given parameters relax_max_ = param.getDefault("relax_max", relax_max_); max_iter_ = param.getDefault("max_iter", max_iter_); min_iter_ = param.getDefault("min_iter", min_iter_); std::string relaxation_type = param.getDefault("relax_type", std::string("dampen")); if (relaxation_type == "dampen") { relax_type_ = DAMPEN; } else if (relaxation_type == "sor") { relax_type_ = SOR; } else { OPM_THROW(std::runtime_error, "Unknown Relaxtion Type " << relaxation_type); } } template void NonlinearSolver::detectOscillations(const std::vector>& residual_history, const int it, bool& oscillate, bool& stagnate) const { // The detection of oscillation in two primary variable results in the report of the detection // of oscillation for the solver. // Only the saturations are used for oscillation detection for the black oil model. // Stagnate is not used for any treatment here. if ( it < 2 ) { oscillate = false; stagnate = false; return; } stagnate = true; int oscillatePhase = 0; const std::vector& F0 = residual_history[it]; const std::vector& F1 = residual_history[it - 1]; const std::vector& F2 = residual_history[it - 2]; for (int p= 0; p < model_->numPhases(); ++p){ const double d1 = std::abs((F0[p] - F2[p]) / F0[p]); const double d2 = std::abs((F0[p] - F1[p]) / F0[p]); oscillatePhase += (d1 < relaxRelTol()) && (relaxRelTol() < d2); // Process is 'stagnate' unless at least one phase // exhibits significant residual change. stagnate = (stagnate && !(std::abs((F1[p] - F2[p]) / F2[p]) > 1.0e-3)); } oscillate = (oscillatePhase > 1); } template template void NonlinearSolver::stabilizeNonlinearUpdate(BVector& dx, BVector& dxOld, const double omega) const { // The dxOld is updated with dx. // If omega is equal to 1., no relaxtion will be appiled. BVector tempDxOld = dxOld; dxOld = dx; switch (relaxType()) { case DAMPEN: { if (omega == 1.) { return; } auto i = dx.size(); for (i = 0; i < dx.size(); ++i) { dx[i] *= omega; } return; } case SOR: { if (omega == 1.) { return; } auto i = dx.size(); for (i = 0; i < dx.size(); ++i) { dx[i] *= omega; tempDxOld[i] *= (1.-omega); dx[i] += tempDxOld[i]; } return; } default: OPM_THROW(std::runtime_error, "Can only handle DAMPEN and SOR relaxation type."); } return; } } // namespace Opm #endif // OPM_FULLYIMPLICITSOLVER_IMPL_HEADER_INCLUDED