diff --git a/ebos/ebos_altidx.cc b/ebos/ebos_altidx.cc index 509cf7e16..f77ef2763 100644 --- a/ebos/ebos_altidx.cc +++ b/ebos/ebos_altidx.cc @@ -32,6 +32,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" namespace Opm { class EclAlternativeBlackOilIndexTraits @@ -67,5 +68,5 @@ END_PROPERTIES int main(int argc, char **argv) { typedef TTAG(EbosAltIdxTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } diff --git a/ebos/ebos_blackoil.cc b/ebos/ebos_blackoil.cc index 840643b6a..8ee9e5bd0 100644 --- a/ebos/ebos_blackoil.cc +++ b/ebos/ebos_blackoil.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" namespace Opm { @@ -105,7 +106,7 @@ void ebosBlackOilSetDeck(Opm::Deck* deck, int ebosBlackOilMain(int argc, char **argv) { typedef TTAG(EbosTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_foam.cc b/ebos/ebos_foam.cc index 41e344d4d..6f534f317 100644 --- a/ebos/ebos_foam.cc +++ b/ebos/ebos_foam.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -57,7 +58,7 @@ void ebosFoamSetDeck(Opm::Deck* deck, int ebosFoamMain(int argc, char **argv) { typedef TTAG(EbosFoamTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_gasoil.cc b/ebos/ebos_gasoil.cc index c05c9bcb7..6d09e5d74 100644 --- a/ebos/ebos_gasoil.cc +++ b/ebos/ebos_gasoil.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -72,7 +73,7 @@ void ebosGasOilSetDeck(Opm::Deck* deck, int ebosGasOilMain(int argc, char **argv) { typedef TTAG(EbosGasOilTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_oilwater.cc b/ebos/ebos_oilwater.cc index f5f877486..118a79e07 100644 --- a/ebos/ebos_oilwater.cc +++ b/ebos/ebos_oilwater.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -72,7 +73,7 @@ void ebosOilWaterSetDeck(Opm::Deck* deck, int ebosOilWaterMain(int argc, char **argv) { typedef TTAG(EbosOilWaterTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_polymer.cc b/ebos/ebos_polymer.cc index 75be77657..3786e3999 100644 --- a/ebos/ebos_polymer.cc +++ b/ebos/ebos_polymer.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -57,7 +58,7 @@ void ebosPolymerSetDeck(Opm::Deck* deck, int ebosPolymerMain(int argc, char **argv) { typedef TTAG(EbosPolymerTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_solvent.cc b/ebos/ebos_solvent.cc index cfc7c4d12..89ec59d66 100644 --- a/ebos/ebos_solvent.cc +++ b/ebos/ebos_solvent.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -57,7 +58,7 @@ void ebosSolventSetDeck(Opm::Deck* deck, int ebosSolventMain(int argc, char **argv) { typedef TTAG(EbosSolventTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/ebos_thermal.cc b/ebos/ebos_thermal.cc index add63d7c4..8f7f0411b 100644 --- a/ebos/ebos_thermal.cc +++ b/ebos/ebos_thermal.cc @@ -28,6 +28,7 @@ #include "config.h" #include "ebos.hh" +#include "startEbos.hh" BEGIN_PROPERTIES @@ -57,7 +58,7 @@ void ebosThermalSetDeck(Opm::Deck* deck, int ebosThermalMain(int argc, char **argv) { typedef TTAG(EbosThermalTypeTag) ProblemTypeTag; - return Opm::start(argc, argv); + return Opm::startEbos(argc, argv); } } diff --git a/ebos/eclnewtonmethod.hh b/ebos/eclnewtonmethod.hh index dba0102c2..eccff96a8 100644 --- a/ebos/eclnewtonmethod.hh +++ b/ebos/eclnewtonmethod.hh @@ -30,6 +30,8 @@ #include #include +#include + #include @@ -227,6 +229,16 @@ public: +std::to_string(double(newtonMaxError))); } + void endIteration_(SolutionVector& nextSolution, + const SolutionVector& currentSolution) + { + ParentType::endIteration_(nextSolution, currentSolution); + OpmLog::debug( "Newton iteration " + std::to_string(this->numIterations_) + "" + + " error: " + std::to_string(double(this->error_)) + + this->endIterMsg().str()); + this->endIterMsg().str(""); + } + private: Scalar errorPvFraction_; Scalar errorSum_; diff --git a/ebos/eclproblem.hh b/ebos/eclproblem.hh index 640668f30..c8231cac9 100644 --- a/ebos/eclproblem.hh +++ b/ebos/eclproblem.hh @@ -160,6 +160,7 @@ NEW_PROP_TAG(EclEnableAquifers); NEW_PROP_TAG(EclMaxTimeStepSizeAfterWellEvent); NEW_PROP_TAG(EclRestartShrinkFactor); NEW_PROP_TAG(EclEnableTuning); +NEW_PROP_TAG(OutputMode); // Set the problem property SET_TYPE_PROP(EclBaseProblem, Problem, Opm::EclProblem); @@ -370,6 +371,10 @@ SET_SCALAR_PROP(EclBaseProblem, EclMaxTimeStepSizeAfterWellEvent, 3600*24*365.25 SET_SCALAR_PROP(EclBaseProblem, EclRestartShrinkFactor, 3); SET_BOOL_PROP(EclBaseProblem, EclEnableTuning, false); +SET_STRING_PROP(EclBaseProblem, OutputMode, "all"); + + + END_PROPERTIES namespace Opm { @@ -492,6 +497,9 @@ public: "Factor by which the time step is reduced after convergence failure"); EWOMS_REGISTER_PARAM(TypeTag, bool, EclEnableTuning, "Honor some aspects of the TUNING keyword from the ECL deck."); + EWOMS_REGISTER_PARAM(TypeTag, std::string, OutputMode, + "Specify which messages are going to be printed. Valid values are: none, log, all (default)"); + } /*! @@ -636,6 +644,7 @@ public: // Set the start time of the simulation simulator.setStartTime(timeMap.getStartTime(/*reportStepIdx=*/0)); + simulator.setEndTime(timeMap.getTotalTime()); // We want the episode index to be the same as the report step index to make // things simpler, so we have to set the episode index to -1 because it is @@ -777,11 +786,11 @@ public: { // Proceed to the next report step auto& simulator = this->simulator(); + int episodeIdx = simulator.episodeIndex(); auto& eclState = simulator.vanguard().eclState(); const auto& schedule = simulator.vanguard().schedule(); const auto& events = schedule.getEvents(); const auto& timeMap = schedule.getTimeMap(); - int episodeIdx = simulator.episodeIndex(); if (episodeIdx >= 0 && events.hasEvent(Opm::ScheduleEvents::GEO_MODIFIER, episodeIdx)) { // bring the contents of the keywords to the current state of the SCHEDULE @@ -803,14 +812,18 @@ public: if (enableExperiments && this->gridView().comm().rank() == 0 && episodeIdx >= 0) { // print some useful information in experimental mode. (the production // simulator does this externally.) + std::ostringstream ss; + boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y"); boost::posix_time::ptime curDateTime = boost::posix_time::from_time_t(timeMap.getStartTime(episodeIdx)); - std::cout << "Report step " << episodeIdx + 1 + ss.imbue(std::locale(std::locale::classic(), facet)); + ss << "Report step " << episodeIdx + 1 << "/" << timeMap.numTimesteps() << " at day " << timeMap.getTimePassedUntil(episodeIdx)/(24*3600) << "/" << timeMap.getTotalTime()/(24*3600) << ", date = " << curDateTime.date() << "\n "; + OpmLog::info(ss.str()); } // react to TUNING changes @@ -859,9 +872,23 @@ public: void beginTimeStep() { const auto& simulator = this->simulator(); - int epsiodeIdx = simulator.episodeIndex(); + int episodeIdx = simulator.episodeIndex(); + + if (enableExperiments && this->gridView().comm().rank() == 0 && episodeIdx >= 0) { + std::ostringstream ss; + boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y"); + boost::posix_time::ptime date = boost::posix_time::from_time_t((this->simulator().startTime())) + boost::posix_time::milliseconds(static_cast(this->simulator().time() / Opm::prefix::milli)); + ss.imbue(std::locale(std::locale::classic(), facet)); + ss <<"\nTime step " << this->simulator().timeStepIndex() << ", stepsize " + << unit::convert::to(this->simulator().timeStepSize(), unit::day) << " days," + << " at day " << (double)unit::convert::to(this->simulator().time(), unit::day) + << "/" << (double)unit::convert::to(this->simulator().endTime(), unit::day) + << ", date = " << date; + OpmLog::info(ss.str()); + } + bool invalidateIntensiveQuantities = false; - const auto& oilVaporizationControl = simulator.vanguard().schedule().getOilVaporizationProperties(epsiodeIdx); + const auto& oilVaporizationControl = simulator.vanguard().schedule().getOilVaporizationProperties(episodeIdx); if (drsdtActive_()) // DRSDT is enabled for (size_t pvtRegionIdx = 0; pvtRegionIdx < maxDRs_.size(); ++pvtRegionIdx) diff --git a/ebos/startEbos.hh b/ebos/startEbos.hh new file mode 100644 index 000000000..cf7d65f1a --- /dev/null +++ b/ebos/startEbos.hh @@ -0,0 +1,211 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/* + 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 2 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 . + + Consult the COPYING file in the top-level source directory of this + module for the precise wording of the license and the list of + copyright holders. +*/ +/*! + * \file + * \brief Provides convenience routines to bring up the simulation at runtime. + */ +#ifndef EWOMS_STARTEBOS_HH +#define EWOMS_STARTEBOS_HH + +#include "ebos.hh" +#include + +#if HAVE_MPI +#include +#endif + +#if HAVE_ECL_INPUT +#include +#include +#include +#endif + +BEGIN_PROPERTIES + +// forward declaration of property tags +NEW_PROP_TAG(Scalar); +NEW_PROP_TAG(Simulator); +NEW_PROP_TAG(ThreadManager); +NEW_PROP_TAG(PrintProperties); +NEW_PROP_TAG(PrintParameters); +NEW_PROP_TAG(ParameterFile); +NEW_PROP_TAG(Problem); +END_PROPERTIES + +//! \cond SKIP_THIS + +namespace Opm { + +enum class FileOutputMode { + //! \brief No output to files. + OUTPUT_NONE = 0, + //! \brief Output only to log files, no eclipse output. + OUTPUT_LOG_ONLY = 1, + //! \brief Output to all files. + OUTPUT_ALL = 3 +}; + + +static void ensureOutputDirExists(const std::string& cmdline_output_dir) +{ + if (!boost::filesystem::is_directory(cmdline_output_dir)) { + try { + boost::filesystem::create_directories(cmdline_output_dir); + } + catch (...) { + throw std::runtime_error("Creation of output directory '" + cmdline_output_dir + "' failed\n"); + } + } +} + +// Setup the OpmLog backends +static FileOutputMode setupLogging(int mpi_rank_, const std::string& deck_filename, const std::string& cmdline_output_dir, const std::string& cmdline_output, bool output_cout_, const std::string& stdout_log_id) { + + if (!cmdline_output_dir.empty()) { + ensureOutputDirExists(cmdline_output_dir); + } + + // create logFile + using boost::filesystem::path; + path fpath(deck_filename); + std::string baseName; + std::ostringstream debugFileStream; + std::ostringstream logFileStream; + + // Strip extension "." or ".DATA" + std::string extension = boost::to_upper_copy(fpath.extension().string()); + if (extension == ".DATA" || extension == ".") { + baseName = boost::to_upper_copy(fpath.stem().string()); + } else { + baseName = boost::to_upper_copy(fpath.filename().string()); + } + + std::string output_dir = cmdline_output_dir; + if (output_dir.empty()) { + output_dir = absolute(path(baseName).parent_path()).string(); + } + + logFileStream << output_dir << "/" << baseName; + debugFileStream << output_dir << "/" << baseName; + + if (mpi_rank_ != 0) { + // Added rank to log file for non-zero ranks. + // This prevents message loss. + debugFileStream << "." << mpi_rank_; + // If the following file appears then there is a bug. + logFileStream << "." << mpi_rank_; + } + logFileStream << ".PRT"; + debugFileStream << ".DBG"; + + FileOutputMode output; + { + static std::map stringToOutputMode = + { {"none", FileOutputMode::OUTPUT_NONE }, + {"false", FileOutputMode::OUTPUT_LOG_ONLY }, + {"log", FileOutputMode::OUTPUT_LOG_ONLY }, + {"all" , FileOutputMode::OUTPUT_ALL }, + {"true" , FileOutputMode::OUTPUT_ALL }}; + auto outputModeIt = stringToOutputMode.find(cmdline_output); + if (outputModeIt != stringToOutputMode.end()) { + output = outputModeIt->second; + } + else { + output = FileOutputMode::OUTPUT_ALL; + std::cerr << "Value " << cmdline_output << + " is not a recognized output mode. Using \"all\" instead." + << std::endl; + } + } + + if (output > FileOutputMode::OUTPUT_NONE) { + std::shared_ptr prtLog = std::make_shared(logFileStream.str(), Opm::Log::NoDebugMessageTypes, false, output_cout_); + Opm::OpmLog::addBackend("ECLIPSEPRTLOG", prtLog); + prtLog->setMessageLimiter(std::make_shared()); + prtLog->setMessageFormatter(std::make_shared(false)); + } + + if (output >= FileOutputMode::OUTPUT_LOG_ONLY) { + std::string debugFile = debugFileStream.str(); + std::shared_ptr debugLog = std::make_shared(debugFileStream.str(), Opm::Log::DefaultMessageTypes, false, output_cout_); + Opm::OpmLog::addBackend("DEBUGLOG", debugLog); + } + + std::shared_ptr streamLog = std::make_shared(std::cout, Opm::Log::StdoutMessageTypes); + Opm::OpmLog::addBackend(stdout_log_id, streamLog); + streamLog->setMessageFormatter(std::make_shared(true)); + + return output; +} + +//! \endcond + +/*! + * \ingroup Common + * + * \brief Wrapper around the main function that set up the OPM + * logging (.PRT, .DBG) for ebos. + * + * \tparam TypeTag The type tag of the problem which needs to be solved + * + * \param argc The number of command line arguments + * \param argv The array of the command line arguments + */ +template +static inline int startEbos(int argc, char **argv) +{ + + int myRank = 0; +#if HAVE_DUNE_FEM + Dune::Fem::MPIManager::initialize(argc, argv); + myRank = Dune::Fem::MPIManager::rank(); +#else + myRank = Dune::MPIHelper::instance(argc, argv).rank(); +#endif + + int paramStatus = setupParameters_(argc, const_cast(argv), /*registerParams=*/true); + if (paramStatus == 1) + return 1; + if (paramStatus == 2) + return 0; + + bool outputCout = false; + if (myRank == 0) + outputCout = EWOMS_GET_PARAM(TypeTag, bool, EnableTerminalOutput); + + std::string deckFilename = EWOMS_GET_PARAM(TypeTag, std::string, EclDeckFileName); + setupLogging(myRank, + deckFilename, + EWOMS_GET_PARAM(TypeTag, std::string, OutputDir), + EWOMS_GET_PARAM(TypeTag, std::string, OutputMode), + outputCout, "STDOUT_LOGGER"); + + // Call the main function. Parameters are already registered + // They should not be registered again + return Opm::start(argc, argv, /*registerParams=*/false); + +} + +} // namespace Opm + +#endif diff --git a/opm/simulators/flow/FlowMainEbos.hpp b/opm/simulators/flow/FlowMainEbos.hpp index 8cb27c5dc..0acb23170 100644 --- a/opm/simulators/flow/FlowMainEbos.hpp +++ b/opm/simulators/flow/FlowMainEbos.hpp @@ -50,14 +50,11 @@ BEGIN_PROPERTIES -NEW_PROP_TAG(OutputMode); NEW_PROP_TAG(EnableDryRun); NEW_PROP_TAG(OutputInterval); NEW_PROP_TAG(UseAmg); NEW_PROP_TAG(EnableLoggingFalloutWarning); -SET_STRING_PROP(EclFlowProblem, OutputMode, "all"); - // TODO: enumeration parameters. we use strings for now. SET_STRING_PROP(EclFlowProblem, EnableDryRun, "auto"); // Do not merge parallel output files or warn about them @@ -87,8 +84,6 @@ namespace Opm static int setupParameters_(int argc, char** argv) { // register the flow specific parameters - EWOMS_REGISTER_PARAM(TypeTag, std::string, OutputMode, - "Specify which messages are going to be printed. Valid values are: none, log, all (default)"); EWOMS_REGISTER_PARAM(TypeTag, std::string, EnableDryRun, "Specify if the simulation ought to be actually run, or just pretended to be"); EWOMS_REGISTER_PARAM(TypeTag, int, OutputInterval, @@ -472,7 +467,6 @@ namespace Opm SimulatorReport successReport = simulator_->run(simtimer); SimulatorReport failureReport = simulator_->failureReport(); - if (output_cout) { std::ostringstream ss; ss << "\n\n================ End of simulation ===============\n\n";