From 3aef6578f97944cdf7de74f45879ca175ce47e1a Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Tue, 11 Mar 2014 15:06:07 +0100 Subject: [PATCH 1/2] SimulatorTimer: rename currentTime() to simulationTimeElapsed() because the name "currentTime()" can be mistaken for the point in real-life time at which the simulation is run (e.g. March 11, 2014, 15:07:45.123), the _point_ in time which the simulator timer currently represents (e.g. Jun 5, 1985, 02:33:12.345) instead of the simulator time in seconds which elapsed since the START date (e.g. 52633.345 s). this rename may lead to some fallout in other modules. I'll fix them after this PR has been merged... --- opm/core/simulator/SimulatorCompressibleTwophase.cpp | 4 ++-- opm/core/simulator/SimulatorIncompTwophase.cpp | 4 ++-- opm/core/simulator/SimulatorOutput.cpp | 2 +- opm/core/simulator/SimulatorTimer.cpp | 12 +++++++++--- opm/core/simulator/SimulatorTimer.hpp | 9 +++++++-- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/opm/core/simulator/SimulatorCompressibleTwophase.cpp b/opm/core/simulator/SimulatorCompressibleTwophase.cpp index a5760716..7284d8d4 100644 --- a/opm/core/simulator/SimulatorCompressibleTwophase.cpp +++ b/opm/core/simulator/SimulatorCompressibleTwophase.cpp @@ -498,13 +498,13 @@ namespace Opm << std::endl; std::cout.precision(8); - watercut.push(timer.currentTime() + timer.currentStepLength(), + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { wellreport.push(props_, *wells_, state.pressure(), state.surfacevol(), state.saturation(), - timer.currentTime() + timer.currentStepLength(), + timer.simulationTimeElapsed() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } sreport.total_time = step_timer.secsSinceStart(); diff --git a/opm/core/simulator/SimulatorIncompTwophase.cpp b/opm/core/simulator/SimulatorIncompTwophase.cpp index 12871c76..271cc8fe 100644 --- a/opm/core/simulator/SimulatorIncompTwophase.cpp +++ b/opm/core/simulator/SimulatorIncompTwophase.cpp @@ -578,12 +578,12 @@ namespace Opm dynamic_cast(*tsolver_) .solveGravity(&initial_porevol[0], stepsize, state); } - watercut.push(timer.currentTime() + timer.currentStepLength(), + watercut.push(timer.simulationTimeElapsed() + timer.currentStepLength(), produced[0]/(produced[0] + produced[1]), tot_produced[0]/tot_porevol_init); if (wells_) { wellreport.push(props_, *wells_, state.saturation(), - timer.currentTime() + timer.currentStepLength(), + timer.simulationTimeElapsed() + timer.currentStepLength(), well_state.bhp(), well_state.perfRates()); } } diff --git a/opm/core/simulator/SimulatorOutput.cpp b/opm/core/simulator/SimulatorOutput.cpp index fd7bf85b..ddb375af 100644 --- a/opm/core/simulator/SimulatorOutput.cpp +++ b/opm/core/simulator/SimulatorOutput.cpp @@ -72,7 +72,7 @@ SimulatorOutputBase::operator std::function () { void SimulatorOutputBase::writeOutput () { - const int this_time = timer_->currentTime (); + const int this_time = timer_->simulationTimeElapsed (); // if the simulator signals for timesteps that aren't reporting // times, then ignore them diff --git a/opm/core/simulator/SimulatorTimer.cpp b/opm/core/simulator/SimulatorTimer.cpp index fddc3aa8..b672b613 100644 --- a/opm/core/simulator/SimulatorTimer.cpp +++ b/opm/core/simulator/SimulatorTimer.cpp @@ -113,8 +113,8 @@ namespace Opm return timesteps_[current_step_ - 1]; } - /// Current time. - double SimulatorTimer::currentTime() const + /// time elapsed since the start of the simulation [s]. + double SimulatorTimer::simulationTimeElapsed() const { if (timeMap_) return timeMap_->getTimePassedUntil(current_step_); @@ -122,6 +122,12 @@ namespace Opm return current_time_; } + /// time elapsed since the start of the POSIX epoch (Jan 1st, 1970) [s]. + time_t SimulatorTimer::currentPosixTime() const + { + tm t = boost::posix_time::to_tm(currentDateTime()); + return std::mktime(&t); + } boost::posix_time::ptime SimulatorTimer::currentDateTime() const { @@ -161,7 +167,7 @@ namespace Opm void SimulatorTimer::report(std::ostream& os) const { os << "\n\n--------------- Simulation step number " << currentStepNum() << " ---------------" - << "\n Current time (days) " << Opm::unit::convert::to(currentTime(), Opm::unit::day) + << "\n Current time (days) " << Opm::unit::convert::to(simulationTimeElapsed(), Opm::unit::day) << "\n Current stepsize (days) " << Opm::unit::convert::to(currentStepLength(), Opm::unit::day) << "\n Total time (days) " << Opm::unit::convert::to(totalTime(), Opm::unit::day) << "\n" << std::endl; diff --git a/opm/core/simulator/SimulatorTimer.hpp b/opm/core/simulator/SimulatorTimer.hpp index 949859e5..ad49282f 100644 --- a/opm/core/simulator/SimulatorTimer.hpp +++ b/opm/core/simulator/SimulatorTimer.hpp @@ -79,8 +79,13 @@ namespace Opm /// it is an error to call stepLengthTaken(). double stepLengthTaken () const; - /// Current time. - double currentTime() const; + /// Time elapsed since the start of the POSIX epoch (Jan 1st, + /// 1970) until the current time step begins [s]. + time_t currentPosixTime() const; + + /// Time elapsed since the start of the simulation until the + /// beginning of the current time step [s]. + double simulationTimeElapsed() const; /// Return the current time as a posix time object. boost::posix_time::ptime currentDateTime() const; From 314b3a32d87af4de808247b4cfaf533f2af5a221 Mon Sep 17 00:00:00 2001 From: Andreas Lauser Date: Fri, 14 Mar 2014 12:54:10 +0100 Subject: [PATCH 2/2] some cleanups for EclipseWriter - the output=true|false parameter gets respected now - if output is "true", it is checked that the value of the "output_dir" parameter is a directory (although I did not find an easy way to check whether it is writable short of writing a file to it.) - the "output_interval" parameter gets respected as well - the index of a written timestep is now independent of the time step index of the simulation: the initial solution is always 0, the first result written to disk is always 1 and so on... (i.e., it does not matter anymore how many steps the simulator needed between two writes) these changes have been proposed by @atgeirr. Thanks! --- opm/core/io/eclipse/EclipseWriter.cpp | 168 +++++++++++++++++--------- opm/core/io/eclipse/EclipseWriter.hpp | 8 +- 2 files changed, 116 insertions(+), 60 deletions(-) diff --git a/opm/core/io/eclipse/EclipseWriter.cpp b/opm/core/io/eclipse/EclipseWriter.cpp index 2ac553d5..c13aef46 100644 --- a/opm/core/io/eclipse/EclipseWriter.cpp +++ b/opm/core/io/eclipse/EclipseWriter.cpp @@ -1,5 +1,5 @@ - /* - Copyright (c) 2013 Andreas Lauser +/* + Copyright (c) 2013-2014 Andreas Lauser Copyright (c) 2013 SINTEF ICT, Applied Mathematics. Copyright (c) 2013 Uni Research AS @@ -18,9 +18,7 @@ You should have received a copy of the GNU General Public License along with OPM. If not, see . */ -#if HAVE_CONFIG_H #include "config.h" -#endif // HAVE_CONFIG_H #include "EclipseWriter.hpp" @@ -259,14 +257,6 @@ EclipseKeyword ::EclipseKeyword ( copyData (data, transf, offset, stride); } -/** - * Extract the current time from a timer object into the C type used by ERT. - */ -static time_t current (const SimulatorTimer& timer) { - tm t = boost::posix_time::to_tm (timer.currentDateTime()); - return std::mktime(&t); -} - /** * Pointer to memory that holds the name to an Eclipse output file. */ @@ -274,7 +264,7 @@ struct EclipseFileName : public EclipseHandle { EclipseFileName (const std::string& outputDir, const std::string& baseName, ecl_file_enum type, - const SimulatorTimer& timer) + int outputStepIdx) // filename formatting function returns a pointer to allocated // memory that must be released with the free() function @@ -283,7 +273,7 @@ struct EclipseFileName : public EclipseHandle { baseName.c_str(), type, false, // formatted? - timer.currentStepNum ()), + outputStepIdx), freestr) { } private: /// Facade which allows us to free a const char* @@ -325,7 +315,8 @@ static int phaseMask (const PhaseUsage uses) { struct EclipseRestart : public EclipseHandle { EclipseRestart (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) + const SimulatorTimer& timer, + int outputStepIdx) // notice the poor man's polymorphism of the allocation function : EclipseHandle ( (timer.currentStepNum () > 0 ? ecl_rst_file_open_append @@ -333,18 +324,19 @@ struct EclipseRestart : public EclipseHandle { EclipseFileName (outputDir, baseName, ECL_UNIFIED_RESTART_FILE, - timer)), + outputStepIdx)), ecl_rst_file_close) { } void writeHeader (const SimulatorTimer& timer, + int outputStepIdx, const PhaseUsage uses, const EclipseGridParser parser, const int num_active_cells) { const std::vector dim = parserDim (parser); ecl_rst_file_fwrite_header (*this, - timer.currentStepNum (), - current (timer), - Opm::unit::convert::to (timer.currentTime (), + outputStepIdx, + timer.currentPosixTime(), + Opm::unit::convert::to (timer.simulationTimeElapsed (), Opm::unit::day), dim[0], dim[1], @@ -458,12 +450,12 @@ struct EclipseGrid : public EclipseHandle { */ void write (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) { + int outputStepIdx) { ecl_grid_fwrite_EGRID (*this, EclipseFileName (outputDir, baseName, ECL_EGRID_FILE, - timer)); + outputStepIdx)); } // GCC 4.4 doesn't generate these constructors for us; provide the @@ -500,7 +492,7 @@ private: EclipseGrid (const int dims[], const EclipseKeyword& zcorn, const EclipseKeyword& coord, - const EclipseKeyword& actnum, + const EclipseKeyword& actnum, const EclipseKeyword& mapaxes) : EclipseHandle ( ecl_grid_alloc_GRDECL_kw(dims[0], @@ -524,11 +516,11 @@ struct EclipseInit : public EclipseHandle { // constructor, so we'll have to do with a static wrapper) static EclipseInit make (const std::string& outputDir, const std::string& baseName, - const SimulatorTimer& timer) { + int outputStepIdx) { EclipseFileName initFileName (outputDir, baseName, ECL_INIT_FILE, - timer); + outputStepIdx); bool fmt_file; if (!ecl_util_fmt_file(initFileName, &fmt_file)) { OPM_THROW(std::runtime_error, @@ -538,15 +530,15 @@ struct EclipseInit : public EclipseHandle { } void writeHeader (const EclipseGrid& grid, - const SimulatorTimer& timer, - const EclipseGridParser& parser, - const PhaseUsage uses) { + const SimulatorTimer& timer, + const EclipseGridParser& parser, + const PhaseUsage uses) { EclipseKeyword poro (PORO_KW, parser); ecl_init_file_fwrite_header (*this, grid, poro, phaseMask (uses), - current (timer)); + timer.currentPosixTime()); } void writeKeyword (const std::string& keyword, @@ -639,8 +631,7 @@ private: EclipseTimeStep* tstep = new EclipseTimeStep ( ecl_sum_add_tstep (*this, timer.currentStepNum (), - // currentTime is always relative to start - Opm::unit::convert::to (timer.currentTime (), + Opm::unit::convert::to (timer.simulationTimeElapsed (), Opm::unit::day))); return std::unique_ptr (tstep); } @@ -660,7 +651,7 @@ private: false, /* formatted */ true, /* unified */ ":", /* join string */ - current (timer), + timer.simulationTimeElapsed (), dim[0], dim[1], dim[2]); @@ -857,7 +848,8 @@ struct EclipseWellBhp : public EclipseWellReport { inline void EclipseSummary::writeTimeStep (const SimulatorTimer& timer, - const WellState& wellState) { + const WellState& wellState) +{ // internal view; do not move this code out of EclipseSummary! std::unique_ptr tstep = makeTimeStep (timer); // write all the variables @@ -873,7 +865,8 @@ static WellType WELL_TYPES[] = { INJECTOR, PRODUCER }; inline void EclipseSummary::addWells (const EclipseGridParser& parser, - const PhaseUsage& uses) { + const PhaseUsage& uses) +{ // TODO: Only create report variables that are requested with keywords // (e.g. "WOPR") in the input files, and only for those wells that are // mentioned in those keywords @@ -957,12 +950,19 @@ namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer &timer, const SimulatorState& reservoirState, - const WellState& wellState) { + const WellState& wellState) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + /* Grid files */ EclipseGrid ecl_grid = EclipseGrid::make (*parser_, *grid_); - ecl_grid.write (outputDir_, baseName_, timer); + ecl_grid.write (outputDir_, baseName_, /*stepIdx=*/0); - EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, timer); + EclipseInit fortio = EclipseInit::make (outputDir_, baseName_, /*stepIdx=*/0); fortio.writeHeader (ecl_grid, timer, *parser_, @@ -973,7 +973,7 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, fortio.writeKeyword ("PERMZ", *parser_, &toMilliDarcy); /* Initial solution (pressure and saturation) */ - writeSolution (timer, reservoirState, wellState); + writeSolution_(timer, reservoirState); /* Create summary object (could not do it at construction time, since it requires knowledge of the start time). */ @@ -981,14 +981,16 @@ void EclipseWriter::writeInit(const SimulatorTimer &timer, summary_->addWells (*parser_, uses_); } -void EclipseWriter::writeSolution (const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& /*wellState*/) { +void EclipseWriter::writeSolution_(const SimulatorTimer& timer, + const SimulatorState& reservoirState) +{ // start writing to files EclipseRestart rst (outputDir_, baseName_, - timer); + timer, + outputTimeStepIdx_); rst.writeHeader (timer, + outputTimeStepIdx_, uses_, *parser_, reservoirState.pressure ().size ()); @@ -997,9 +999,7 @@ void EclipseWriter::writeSolution (const SimulatorTimer& timer, // write pressure and saturation fields (same as DataMap holds) // convert the pressures from Pascals to bar because Eclipse // seems to write bars - sol.add (EclipseKeyword (reservoirState.pressure (), - "PRESSURE", - &toBar)); + sol.add(EclipseKeyword(reservoirState.pressure(), "PRESSURE", &toBar)); for (int phase = 0; phase != BlackoilPhases::MaxNumPhases; ++phase) { // Eclipse never writes the oil saturation, so all post-processors @@ -1015,13 +1015,27 @@ void EclipseWriter::writeSolution (const SimulatorTimer& timer, uses_.num_phases)); } } + + ++outputTimeStepIdx_; } void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, const SimulatorState& reservoirState, - const WellState& wellState) { + const WellState& wellState) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + + // respected the output_interval parameter + if (timer.currentStepNum() % outputInterval_ != 0) { + return; + } + /* Field variables (pressure, saturation) */ - writeSolution (timer, reservoirState, wellState); + writeSolution_(timer, reservoirState); /* Summary variables (well reporting) */ // TODO: instead of writing the header (smspec) every time, it should @@ -1037,15 +1051,23 @@ void EclipseWriter::writeTimeStep(const SimulatorTimer& timer, // called. This has been changed so that the final summary file // will contain data from the whole simulation, instead of just // the last step. - summary_->writeTimeStep (timer, wellState); + summary_->writeTimeStep(timer, wellState); } +} // namespace Opm #else namespace Opm { void EclipseWriter::writeInit(const SimulatorTimer&, const SimulatorState&, - const WellState&) { + const WellState&) + { + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + OPM_THROW(std::runtime_error, "The ERT libraries are required to write ECLIPSE output files."); } @@ -1053,20 +1075,35 @@ void EclipseWriter::writeInit(const SimulatorTimer&, void EclipseWriter::writeTimeStep( const SimulatorTimer&, const SimulatorState&, - const WellState&) { + const WellState&) +{ + // if we don't want to write anything, this method becomes a + // no-op... + if (!enableOutput_) { + return; + } + OPM_THROW(std::runtime_error, "The ERT libraries are required to write ECLIPSE output files."); } +} // namespace Opm + #endif // HAVE_ERT +namespace Opm { + EclipseWriter::EclipseWriter ( const ParameterGroup& params, std::shared_ptr parser, std::shared_ptr grid) : parser_ (parser) , grid_ (grid) - , uses_ (phaseUsageFromDeck (*parser)) { + , uses_ (phaseUsageFromDeck (*parser)) +{ + // set the index of the first time step written to 0... + outputTimeStepIdx_ = 0; + // get the base name from the name of the deck using boost::filesystem::path; @@ -1082,14 +1119,31 @@ EclipseWriter::EclipseWriter ( // of some of the files (.SMSPEC, .UNSMRY) and not others baseName_ = boost::to_upper_copy (baseName_); + // retrieve the value of the "output" parameter + enableOutput_ = params.getDefault("output", /*defaultValue=*/true); + + // retrieve the interval at which something should get written to + // disk (once every N timesteps) + outputInterval_ = params.getDefault("output_interval", /*defaultValue=*/1); + // store in current directory if not explicitly set - if (params.has ("output_dir")) { - outputDir_ = params.get ("output_dir"); - } - else { - // this is needed to prevent file names like "/FOO.INIT" which - // lead to segfaults - outputDir_ = "."; + outputDir_ = params.getDefault("output_dir", "."); + + // set the index of the first time step written to 0... + outputTimeStepIdx_ = 0; + + if (enableOutput_) { + // make sure that the output directory exists, if not try to create it + if (!boost::filesystem::exists(outputDir_)) { + std::cout << "Trying to create directory \"" << outputDir_ << "\" for the simulation output\n"; + boost::filesystem::create_directories(outputDir_); + } + + if (!boost::filesystem::is_directory(outputDir_)) { + OPM_THROW(std::runtime_error, + "The path specified as output directory '" << outputDir_ + << "' is not a directory"); + } } } diff --git a/opm/core/io/eclipse/EclipseWriter.hpp b/opm/core/io/eclipse/EclipseWriter.hpp index 5634a347..a3451ac3 100644 --- a/opm/core/io/eclipse/EclipseWriter.hpp +++ b/opm/core/io/eclipse/EclipseWriter.hpp @@ -88,15 +88,17 @@ public: private: std::shared_ptr parser_; std::shared_ptr grid_; + bool enableOutput_; + int outputInterval_; + int outputTimeStepIdx_; std::string outputDir_; std::string baseName_; PhaseUsage uses_; // active phases in the input deck std::shared_ptr summary_; /// Write solution field variables (pressure and saturation) - void writeSolution (const SimulatorTimer& timer, - const SimulatorState& reservoirState, - const WellState& wellState); + void writeSolution_(const SimulatorTimer& timer, + const SimulatorState& reservoirState); }; } // namespace Opm