diff --git a/ebos/eclbasevanguard.hh b/ebos/eclbasevanguard.hh index a71fd6412..4e96cc437 100644 --- a/ebos/eclbasevanguard.hh +++ b/ebos/eclbasevanguard.hh @@ -438,9 +438,11 @@ public: * computed, ready to use summary values. The values will typically be used by * the UDQ, WTEST and ACTIONX calculations. */ - Opm::SummaryState& summaryState() + Opm::SummaryState& summaryState() { return summaryState_; } + const Opm::SummaryState& summaryState() const + { return summaryState_; } /*! * \brief Returns the name of the case. * diff --git a/ebos/eclproblem.hh b/ebos/eclproblem.hh index ae851049f..90735c1e9 100644 --- a/ebos/eclproblem.hh +++ b/ebos/eclproblem.hh @@ -1009,6 +1009,7 @@ public: bool isSubStep = !EWOMS_GET_PARAM(TypeTag, bool, EnableWriteAllSolutions) && !this->simulator().episodeWillBeOver(); + eclWriter_->evalSummaryState(isSubStep); if (enableEclOutput_) eclWriter_->writeOutput(isSubStep); } diff --git a/ebos/eclwriter.hh b/ebos/eclwriter.hh index f1b67b9a5..123c9db07 100644 --- a/ebos/eclwriter.hh +++ b/ebos/eclwriter.hh @@ -54,6 +54,10 @@ #include #include +#ifdef HAVE_MPI +#include +#endif + BEGIN_PROPERTIES NEW_PROP_TAG(EnableEclOutput); @@ -207,13 +211,109 @@ public: /*! * \brief collect and pass data and pass it to eclIO writer */ - void writeOutput(bool isSubStep) + + void evalSummaryState(bool isSubStep) { + int reportStepNum = simulator_.episodeIndex() + 1; + /* + The summary data is not evaluated for timestep 0, that is + implemented with a: + + if (time_step == 0) + return; + + check somewhere in the summary code. When the summary code was + split in separate methods Summary::eval() and + Summary::add_timestep() it was necessary to pull this test out + here to ensure that the well and group related keywords in the + restart file, like XWEL and XGRP were "correct" also in the + initial report step. + + "Correct" in this context means unchanged behavior, might very + well be more correct to actually remove this if test. + */ + if (reportStepNum == 0) + return; + + const auto& summary = eclIO_->summary(); Scalar curTime = simulator_.time() + simulator_.timeStepSize(); Scalar totalCpuTime = simulator_.executionTimer().realTimeElapsed() + simulator_.setupTimer().realTimeElapsed() + simulator_.vanguard().externalSetupTime(); + + Opm::data::Wells localWellData = simulator_.problem().wellModel().wellData(); + + const auto& gridView = simulator_.vanguard().gridView(); + int numElements = gridView.size(/*codim=*/0); + bool log = collectToIORank_.isIORank(); + eclOutputModule_.allocBuffers(numElements, reportStepNum, isSubStep, log); + + ElementContext elemCtx(simulator_); + ElementIterator elemIt = gridView.template begin(); + const ElementIterator& elemEndIt = gridView.template end(); + for (; elemIt != elemEndIt; ++elemIt) { + const Element& elem = *elemIt; + elemCtx.updatePrimaryStencil(elem); + elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0); + eclOutputModule_.processElement(elemCtx); + } + + if (collectToIORank_.isParallel()) + collectToIORank_.collect({}, eclOutputModule_.getBlockData(), localWellData); + + std::map miscSummaryData; + std::map> regionData; + eclOutputModule_.outputFipLog(miscSummaryData, regionData, isSubStep); + + std::vector buffer; + if (collectToIORank_.isIORank()) { + const auto& eclState = simulator_.vanguard().eclState(); + + // Add TCPU + if (totalCpuTime != 0.0) + miscSummaryData["TCPU"] = totalCpuTime; + + const Opm::data::Wells& wellData = collectToIORank_.isParallel() ? collectToIORank_.globalWellData() : localWellData; + + const std::map, double>& blockData + = collectToIORank_.isParallel() + ? collectToIORank_.globalBlockData() + : eclOutputModule_.getBlockData(); + + summary.eval(summaryState(), + reportStepNum, + curTime, + eclState, + schedule(), + wellData, + miscSummaryData, + regionData, + blockData); + buffer = summaryState().serialize(); + } + + if (collectToIORank_.isParallel()) { + unsigned long buffer_size = buffer.size(); + +#ifdef HAVE_MPI + MPI_Bcast(&buffer_size, 1, MPI_UNSIGNED_LONG, collectToIORank_.ioRank, MPI_COMM_WORLD); + if (!collectToIORank_.isIORank()) + buffer.resize( buffer_size ); + + MPI_Bcast(buffer.data(), buffer_size, MPI_CHAR, collectToIORank_.ioRank, MPI_COMM_WORLD); + if (!collectToIORank_.isIORank()) { + Opm::SummaryState& st = summaryState(); + st.deserialize(buffer); + } +#endif + } + } + + + void writeOutput(bool isSubStep) + { + Scalar curTime = simulator_.time() + simulator_.timeStepSize(); Scalar nextStepSize = simulator_.problem().nextTimeStepSize(); // output using eclWriter if enabled @@ -248,48 +348,30 @@ public: if (collectToIORank_.isParallel()) collectToIORank_.collect(localCellData, eclOutputModule_.getBlockData(), localWellData); - std::map miscSummaryData; - std::map> regionData; - eclOutputModule_.outputFipLog(miscSummaryData, regionData, isSubStep); - // write output on I/O rank if (collectToIORank_.isIORank()) { const auto& eclState = simulator_.vanguard().eclState(); const auto& simConfig = eclState.getSimulationConfig(); - // Add TCPU - if (totalCpuTime != 0.0) - miscSummaryData["TCPU"] = totalCpuTime; - bool enableDoublePrecisionOutput = EWOMS_GET_PARAM(TypeTag, bool, EclOutputDoublePrecision); const Opm::data::Solution& cellData = collectToIORank_.isParallel() ? collectToIORank_.globalCellData() : localCellData; const Opm::data::Wells& wellData = collectToIORank_.isParallel() ? collectToIORank_.globalWellData() : localWellData; Opm::RestartValue restartValue(cellData, wellData); - const std::map, double>& blockData - = collectToIORank_.isParallel() - ? collectToIORank_.globalBlockData() - : eclOutputModule_.getBlockData(); + if (simConfig.useThresholdPressure()) + restartValue.addExtra("THRESHPR", Opm::UnitSystem::measure::pressure, simulator_.problem().thresholdPressure().data()); // Add suggested next timestep to extra data. if (!isSubStep) restartValue.addExtra("OPMEXTRA", std::vector(1, nextStepSize)); - if (simConfig.useThresholdPressure()) - restartValue.addExtra("THRESHPR", Opm::UnitSystem::measure::pressure, simulator_.problem().thresholdPressure().data()); - // first, create a tasklet to write the data for the current time step to disk auto eclWriteTasklet = std::make_shared(summaryState(), - eclState, - schedule(), *eclIO_, reportStepNum, isSubStep, curTime, restartValue, - miscSummaryData, - regionData, - blockData, enableDoublePrecisionOutput); // then, make sure that the previous I/O request has been completed and the @@ -575,79 +657,34 @@ private: struct EclWriteTasklet : public TaskletInterface { - Opm::SummaryState& summaryState; - const Opm::EclipseState& eclState; - const Opm::Schedule& schedule; + Opm::SummaryState summaryState_; Opm::EclipseIO& eclIO_; int reportStepNum_; bool isSubStep_; double secondsElapsed_; Opm::RestartValue restartValue_; - std::map singleSummaryValues_; - std::map> regionSummaryValues_; - std::map, double> blockSummaryValues_; bool writeDoublePrecision_; - explicit EclWriteTasklet(Opm::SummaryState& summaryState, - const Opm::EclipseState& eclState, - const Opm::Schedule& schedule, + explicit EclWriteTasklet(const Opm::SummaryState& summaryState, Opm::EclipseIO& eclIO, int reportStepNum, bool isSubStep, double secondsElapsed, Opm::RestartValue restartValue, - const std::map& singleSummaryValues, - const std::map>& regionSummaryValues, - const std::map, double>& blockSummaryValues, bool writeDoublePrecision) - : summaryState(summaryState) - , eclState(eclState) - , schedule(schedule) + : summaryState_(summaryState) , eclIO_(eclIO) , reportStepNum_(reportStepNum) , isSubStep_(isSubStep) , secondsElapsed_(secondsElapsed) , restartValue_(restartValue) - , singleSummaryValues_(singleSummaryValues) - , regionSummaryValues_(regionSummaryValues) - , blockSummaryValues_(blockSummaryValues) , writeDoublePrecision_(writeDoublePrecision) { } // callback to eclIO serial writeTimeStep method void run() { - const auto& summary = eclIO_.summary(); - - /* - The summary data is not evaluated for timestep 0, that is - implemented with a: - - if (time_step == 0) - return; - - check somewhere in the summary code. When the summary code was - split in separate methods Summary::eval() and - Summary::add_timestep() it was necessary to pull this test out - here to ensure that the well and group related keywords in the - restart file, like XWEL and XGRP were "correct" also in the - initial report step. - - "Correct" in this context means unchanged behavior, might very - well be more correct to actually remove this if test. - */ - if (reportStepNum_ > 0) - summary.eval(summaryState, - reportStepNum_, - secondsElapsed_, - eclState, - schedule, - restartValue_.wells, - singleSummaryValues_, - regionSummaryValues_, - blockSummaryValues_); - - eclIO_.writeTimeStep(summaryState, + eclIO_.writeTimeStep(summaryState_, reportStepNum_, isSubStep_, secondsElapsed_, diff --git a/tests/test_ecl_output.cc b/tests/test_ecl_output.cc index 7e8bb6b23..e5cbf1bf6 100644 --- a/tests/test_ecl_output.cc +++ b/tests/test_ecl_output.cc @@ -132,11 +132,17 @@ void test_summary() Opm::data::Wells dw; bool substep = false; simulator->startNextEpisode(0.0, 1e30); + simulator->setEpisodeIndex(0); + eclWriter->evalSummaryState(substep); eclWriter->writeOutput(substep); + simulator->setEpisodeIndex(1); + eclWriter->evalSummaryState(substep); eclWriter->writeOutput(substep); + simulator->setEpisodeIndex(2); + eclWriter->evalSummaryState(substep); eclWriter->writeOutput(substep); auto res = readsum( casename );