SimulatorReport nonlinearIteration(const int iteration, const SimulatorTimerInterface& timer, NonlinearSolverType& nonlinear_solver) { SimulatorReport report; failureReport_ = SimulatorReport(); Dune::Timer perfTimer; perfTimer.start(); 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. residual_norms_history_.clear(); current_relaxation_ = 1.0; dx_old_ = 0.0; convergence_reports_.push_back({timer.reportStepNum(), timer.currentStepNum(), {}}); convergence_reports_.back().report.reserve(11); } report.total_linearizations = 1; try { report += assembleReservoir(timer, iteration); report.assemble_time += perfTimer.stop(); } catch (...) { report.assemble_time += perfTimer.stop(); failureReport_ += report; // todo (?): make the report an attribute of the class throw; // continue throwing the stick } std::vector<double> residual_norms; perfTimer.reset(); perfTimer.start(); // the step is not considered converged until at least minIter iterations is done { auto convrep = getConvergence(timer, iteration,residual_norms); report.converged = convrep.converged() && iteration > nonlinear_solver.minIter();; ConvergenceReport::Severity severity = convrep.severityOfWorstFailure(); convergence_reports_.back().report.push_back(std::move(convrep)); // Throw if any NaN or too large residual found. if (severity == ConvergenceReport::Severity::NotANumber) { OPM_THROW(Opm::NumericalIssue, "NaN residual found!"); } else if (severity == ConvergenceReport::Severity::TooLarge) { OPM_THROW(Opm::NumericalIssue, "Too large residual found!"); } } // checking whether the group targets are converged if (wellModel().wellCollection().groupControlActive()) { report.converged = report.converged && wellModel().wellCollection().groupTargetConverged(wellModel().wellState().wellRates()); } report.update_time += perfTimer.stop(); residual_norms_history_.push_back(residual_norms); if (!report.converged) { perfTimer.reset(); perfTimer.start(); report.total_newton_iterations = 1; // enable single precision for solvers when dt is smaller then 20 days //residual_.singlePrecision = (unit::convert::to(dt, unit::day) < 20.) ; // Compute the nonlinear update. const int nc = UgGridHelpers::numCells(grid_); BVector x(nc); // apply the Schur compliment of the well model to the reservoir linearized // equations wellModel().linearize(ebosSimulator().model().linearizer().jacobian(), ebosSimulator().model().linearizer().residual()); // Solve the linear system. linear_solve_setup_time_ = 0.0; try { solveJacobianSystem(x); report.linear_solve_setup_time += linear_solve_setup_time_; report.linear_solve_time += perfTimer.stop(); report.total_linear_iterations += linearIterationsLastSolve(); } catch (...) { report.linear_solve_setup_time += linear_solve_setup_time_; report.linear_solve_time += perfTimer.stop(); report.total_linear_iterations += linearIterationsLastSolve(); failureReport_ += report; throw; // re-throw up } perfTimer.reset(); perfTimer.start(); // handling well state update before oscillation treatment is a decision based // on observation to avoid some big performance degeneration under some circumstances. // there is no theorectical explanation which way is better for sure. wellModel().postSolve(x); if (param_.use_update_stabilization_) { // Stabilize the nonlinear update. bool isOscillate = false; bool isStagnate = false; nonlinear_solver.detectOscillations(residual_norms_history_, iteration, isOscillate, isStagnate); if (isOscillate) { current_relaxation_ -= nonlinear_solver.relaxIncrement(); current_relaxation_ = std::max(current_relaxation_, nonlinear_solver.relaxMax()); if (terminalOutputEnabled()) { std::string msg = " Oscillating behavior detected: Relaxation set to " + std::to_string(current_relaxation_); OpmLog::info(msg); } } nonlinear_solver.stabilizeNonlinearUpdate(x, dx_old_, current_relaxation_); } // Apply the update, with considering model-dependent limitations and // chopping of the update. updateSolution(x); report.update_time += perfTimer.stop(); } return report; }
// ----------------- Main program ----------------- int main(int argc, char** argv) { Dune::Timer externalSetupTimer; externalSetupTimer.start(); detail::handleVersionCmdLine(argc, argv); // MPI setup. #if HAVE_DUNE_FEM Dune::Fem::MPIManager::initialize(argc, argv); int mpiRank = Dune::Fem::MPIManager::rank(); #else // the design of the plain dune MPIHelper class is quite flawed: there is no way to // get the instance without having the argc and argv parameters available and it is // not possible to determine the MPI rank and size without an instance. (IOW: the // rank() and size() methods are supposed to be static.) const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv); int mpiRank = mpiHelper.rank(); #endif // we always want to use the default locale, and thus spare us the trouble // with incorrect locale settings. Opm::resetLocale(); // this is a work-around for a catch 22: we do not know what code path to use without // parsing the deck, but we don't know the deck without having access to the // parameters and this requires to know the type tag to be used. To solve this, we // use a type tag just for parsing the parameters before we instantiate the actual // simulator object. (Which parses the parameters again, but since this is done in an // identical manner it does not matter.) typedef TTAG(FlowEarlyBird) PreTypeTag; typedef GET_PROP_TYPE(PreTypeTag, Problem) PreProblem; PreProblem::setBriefDescription("Flow, an advanced reservoir simulator for ECL-decks provided by the Open Porous Media project."); int status = Opm::FlowMainEbos<PreTypeTag>::setupParameters_(argc, argv); if (status != 0) // if setupParameters_ returns a value smaller than 0, there was no error, but // the program should abort. This is the case e.g. for the --help and the // --print-properties parameters. return (status >= 0)?status:0; bool outputCout = false; if (mpiRank == 0) outputCout = EWOMS_GET_PARAM(PreTypeTag, bool, EnableTerminalOutput); std::string deckFilename = EWOMS_GET_PARAM(PreTypeTag, std::string, EclDeckFileName); typedef typename GET_PROP_TYPE(PreTypeTag, Vanguard) PreVanguard; try { deckFilename = PreVanguard::canonicalDeckPath(deckFilename).string(); } catch (const std::exception& e) { if ( mpiRank == 0 ) std::cerr << "Exception received: " << e.what() << ". Try '--help' for a usage description.\n"; #if HAVE_MPI MPI_Finalize(); #endif return 1; } if (outputCout) { Opm::FlowMainEbos<PreTypeTag>::printBanner(); } // Create Deck and EclipseState. try { if (outputCout) { std::cout << "Reading deck file '" << deckFilename << "'\n"; std::cout.flush(); } std::shared_ptr<Opm::Deck> deck; std::shared_ptr<Opm::EclipseState> eclipseState; std::shared_ptr<Opm::Schedule> schedule; std::shared_ptr<Opm::SummaryConfig> summaryConfig; { Opm::Parser parser; Opm::ParseContext parseContext; Opm::ErrorGuard errorGuard; if (EWOMS_GET_PARAM(PreTypeTag, bool, EclStrictParsing)) parseContext.update( Opm::InputError::DELAYED_EXIT1); else { parseContext.update(Opm::ParseContext::PARSE_RANDOM_SLASH, Opm::InputError::IGNORE); parseContext.update(Opm::ParseContext::PARSE_MISSING_DIMS_KEYWORD, Opm::InputError::WARN); parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_WELL, Opm::InputError::WARN); parseContext.update(Opm::ParseContext::SUMMARY_UNKNOWN_GROUP, Opm::InputError::WARN); } deck.reset( new Opm::Deck( parser.parseFile(deckFilename , parseContext, errorGuard))); Opm::MissingFeatures::checkKeywords(*deck, parseContext, errorGuard); if ( outputCout ) Opm::checkDeck(*deck, parser, parseContext, errorGuard); eclipseState.reset( new Opm::EclipseState(*deck, parseContext, errorGuard )); schedule.reset(new Opm::Schedule(*deck, *eclipseState, parseContext, errorGuard)); summaryConfig.reset( new Opm::SummaryConfig(*deck, *schedule, eclipseState->getTableManager(), parseContext, errorGuard)); if (errorGuard) { errorGuard.dump(); errorGuard.clear(); throw std::runtime_error("Unrecoverable errors were encountered while loading input."); } } const auto& phases = Opm::Runspec(*deck).phases(); // run the actual simulator // // TODO: make sure that no illegal combinations like thermal and twophase are // requested. // Twophase cases if( phases.size() == 2 ) { // oil-gas if (phases.active( Opm::Phase::GAS )) { Opm::flowEbosGasOilSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosGasOilMain(argc, argv); } // oil-water else if ( phases.active( Opm::Phase::WATER ) ) { Opm::flowEbosOilWaterSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosOilWaterMain(argc, argv); } else { if (outputCout) std::cerr << "No suitable configuration found, valid are Twophase (oilwater and oilgas), polymer, solvent, or blackoil" << std::endl; return EXIT_FAILURE; } } // Polymer case else if ( phases.active( Opm::Phase::POLYMER ) ) { if ( !phases.active( Opm::Phase::WATER) ) { if (outputCout) std::cerr << "No valid configuration is found for polymer simulation, valid options include " << "oilwater + polymer and blackoil + polymer" << std::endl; return EXIT_FAILURE; } // Need to track the polymer molecular weight // for the injectivity study if ( phases.active( Opm::Phase::POLYMW ) ) { // only oil water two phase for now assert( phases.size() == 4); return Opm::flowEbosOilWaterPolymerInjectivityMain(argc, argv); } if ( phases.size() == 3 ) { // oil water polymer case Opm::flowEbosOilWaterPolymerSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosOilWaterPolymerMain(argc, argv); } else { Opm::flowEbosPolymerSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosPolymerMain(argc, argv); } } // Solvent case else if ( phases.active( Opm::Phase::SOLVENT ) ) { Opm::flowEbosSolventSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosSolventMain(argc, argv); } // Energy case else if (eclipseState->getSimulationConfig().isThermal()) { Opm::flowEbosEnergySetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosEnergyMain(argc, argv); } // Blackoil case else if( phases.size() == 3 ) { Opm::flowEbosBlackoilSetDeck(externalSetupTimer.elapsed(), *deck, *eclipseState, *schedule, *summaryConfig); return Opm::flowEbosBlackoilMain(argc, argv); } else { if (outputCout) std::cerr << "No suitable configuration found, valid are Twophase, polymer, solvent, energy, or blackoil" << std::endl; return EXIT_FAILURE; } }