110class EclWriter :
public EclGenericWriter<GetPropType<TypeTag, Properties::Grid>,
111 GetPropType<TypeTag, Properties::EquilGrid>,
112 GetPropType<TypeTag, Properties::GridView>,
113 GetPropType<TypeTag, Properties::ElementMapper>,
114 GetPropType<TypeTag, Properties::Scalar>>
124 using Element =
typename GridView::template Codim<0>::Entity;
126 using ElementIterator =
typename GridView::template Codim<0>::Iterator;
127 using BaseType = EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>;
129 typedef Dune::MultipleCodimMultipleGeomTypeMapper< GridView > VertexMapper;
139 static void registerParameters()
141 OutputModule::registerParameters();
144 (
"Write the ECL-formated results in a non-blocking way "
145 "(i.e., using a separate thread).");
147 (
"Write ESMRY file for fast loading of summary data.");
153 explicit EclWriter(Simulator& simulator)
154 : BaseType(simulator.vanguard().schedule(),
155 simulator.vanguard().eclState(),
156 simulator.vanguard().summaryConfig(),
157 simulator.vanguard().grid(),
158 ((simulator.vanguard().grid().comm().rank() == 0)
159 ? &simulator.vanguard().equilGrid()
161 simulator.vanguard().gridView(),
162 simulator.vanguard().cartesianIndexMapper(),
163 ((simulator.vanguard().grid().comm().rank() == 0)
164 ? &simulator.vanguard().equilCartesianIndexMapper()
168 , simulator_(simulator)
171 if (this->simulator_.vanguard().grid().comm().size() > 1) {
172 auto smryCfg = (this->simulator_.vanguard().grid().comm().rank() == 0)
173 ? this->eclIO_->finalSummaryConfig()
176 eclBroadcast(this->simulator_.vanguard().grid().comm(), smryCfg);
178 this->outputModule_ = std::make_unique<OutputModule>
179 (simulator, smryCfg, this->collectOnIORank_);
184 this->outputModule_ = std::make_unique<OutputModule>
185 (simulator, this->eclIO_->finalSummaryConfig(), this->collectOnIORank_);
188 this->rank_ = this->simulator_.vanguard().grid().comm().rank();
190 this->simulator_.vanguard().eclState().computeFipRegionStatistics();
196 const EquilGrid& globalGrid()
const
198 return simulator_.vanguard().equilGrid();
207 const int reportStepNum = simulator_.episodeIndex() + 1;
227 if (reportStepNum == 0)
230 const Scalar curTime = simulator_.time() + simulator_.timeStepSize();
231 const Scalar totalCpuTime =
232 simulator_.executionTimer().realTimeElapsed() +
233 simulator_.setupTimer().realTimeElapsed() +
234 simulator_.vanguard().setupTime();
236 const auto localWellData = simulator_.problem().wellModel().wellData();
237 const auto localWBP = simulator_.problem().wellModel().wellBlockAveragePressures();
238 const auto localGroupAndNetworkData = simulator_.problem().wellModel()
239 .groupAndNetworkData(reportStepNum);
241 const auto localAquiferData = simulator_.problem().aquiferModel().aquiferData();
242 const auto localWellTestState = simulator_.problem().wellModel().wellTestState();
243 this->prepareLocalCellData(isSubStep, reportStepNum);
245 if (this->outputModule_->needInterfaceFluxes(isSubStep)) {
246 this->captureLocalFluxData();
249 if (this->collectOnIORank_.isParallel()) {
250 OPM_BEGIN_PARALLEL_TRY_CATCH()
252 std::map<std::pair<std::string,int>,
double> dummy;
253 this->collectOnIORank_.collect({},
254 outputModule_->getBlockData(),
258 localGroupAndNetworkData,
261 this->outputModule_->getInterRegFlows(),
265 if (this->collectOnIORank_.isIORank()) {
266 auto& iregFlows = this->collectOnIORank_.globalInterRegFlows();
268 if (! iregFlows.readIsConsistent()) {
269 throw std::runtime_error {
270 "Inconsistent inter-region flow "
271 "region set names in parallel"
275 iregFlows.compress();
278 OPM_END_PARALLEL_TRY_CATCH(
"Collect to I/O rank: ",
279 this->simulator_.vanguard().grid().comm());
283 std::map<std::string, double> miscSummaryData;
284 std::map<std::string, std::vector<double>> regionData;
288 OPM_TIMEBLOCK(outputFipLogAndFipresvLog);
290 inplace = outputModule_->calc_inplace(miscSummaryData, regionData, simulator_.gridView().comm());
292 if (this->collectOnIORank_.isIORank()){
298 if (totalCpuTime != 0.0) {
299 miscSummaryData[
"TCPU"] = totalCpuTime;
301 if (this->sub_step_report_.total_newton_iterations != 0) {
302 miscSummaryData[
"NEWTON"] = this->sub_step_report_.total_newton_iterations;
304 if (this->sub_step_report_.total_linear_iterations != 0) {
305 miscSummaryData[
"MLINEARS"] = this->sub_step_report_.total_linear_iterations;
307 if (this->sub_step_report_.total_newton_iterations != 0) {
308 miscSummaryData[
"NLINEARS"] =
static_cast<float>(this->sub_step_report_.total_linear_iterations) / this->sub_step_report_.total_newton_iterations;
310 if (this->sub_step_report_.min_linear_iterations != std::numeric_limits<unsigned int>::max()) {
311 miscSummaryData[
"NLINSMIN"] = this->sub_step_report_.min_linear_iterations;
313 if (this->sub_step_report_.max_linear_iterations != 0) {
314 miscSummaryData[
"NLINSMAX"] = this->sub_step_report_.max_linear_iterations;
316 if (this->simulation_report_.success.total_linear_iterations != 0) {
317 miscSummaryData[
"MSUMLINS"] = this->simulation_report_.success.total_linear_iterations;
319 if (this->simulation_report_.success.total_newton_iterations != 0) {
320 miscSummaryData[
"MSUMNEWT"] = this->simulation_report_.success.total_newton_iterations;
324 OPM_TIMEBLOCK(evalSummary);
326 const auto& blockData = this->collectOnIORank_.isParallel()
327 ? this->collectOnIORank_.globalBlockData()
328 : this->outputModule_->getBlockData();
330 const auto& interRegFlows = this->collectOnIORank_.isParallel()
331 ? this->collectOnIORank_.globalInterRegFlows()
332 : this->outputModule_->getInterRegFlows();
334 this->evalSummary(reportStepNum,
338 localGroupAndNetworkData,
344 this->outputModule_->initialInplace(),
346 this->summaryState(),
354 const auto& gridView = simulator_.vanguard().gridView();
355 const int num_interior = detail::
356 countLocalInteriorCellsGridView(gridView);
358 this->outputModule_->
359 allocBuffers(num_interior, 0,
false,
false,
false);
362#pragma omp parallel for
364 for (
int dofIdx = 0; dofIdx < num_interior; ++dofIdx) {
365 const auto& intQuants = *simulator_.model().cachedIntensiveQuantities(dofIdx, 0);
366 const auto totVolume = simulator_.model().dofTotalVolume(dofIdx);
368 this->outputModule_->updateFluidInPlace(dofIdx, intQuants, totVolume);
373 outputModule_->calc_initial_inplace(simulator_.gridView().comm());
376 const auto& fip = simulator_.vanguard().eclState().getEclipseConfig().fip();
377 if (fip.output(FIPConfig::OutputField::FIELD) ||
378 fip.output(FIPConfig::OutputField::RESV))
380 OPM_TIMEBLOCK(outputFipLogAndFipresvLog);
382 const auto start_time = boost::posix_time::
383 from_time_t(simulator_.vanguard().schedule().getStartTime());
385 if (this->collectOnIORank_.isIORank()) {
386 this->inplace_ = *this->outputModule_->initialInplace();
388 this->outputModule_->
389 outputFipAndResvLog(this->inplace_, 0, 0.0, start_time,
390 false, simulator_.gridView().comm());
394 outputModule_->outputFipAndResvLogToCSV(0,
false, simulator_.gridView().comm());
399 if (! this->collectOnIORank_.isIORank()) {
411 const auto firstStep = this->initialStep();
415 const auto& rpt = this->schedule_[simStep].rpt_config();
417 if (rpt.contains(
"WELSPECS") && (rpt.at(
"WELSPECS") > 0)) {
420 this->writeWellspecReport(timer);
428 if (rpt.contains(
"WELLS") && rpt.at(
"WELLS") > 0) {
429 this->writeWellflowReport(timer, simStep, rpt.at(
"WELLS"));
432 this->outputModule_->outputFipAndResvLog(this->inplace_,
437 simulator_.gridView().comm());
442 void writeOutput(data::Solution&& localCellData,
const bool isSubStep,
const bool isForcedFinalOutput)
444 OPM_TIMEBLOCK(writeOutput);
446 const int reportStepNum = simulator_.episodeIndex() + 1;
447 this->prepareLocalCellData(isSubStep, reportStepNum);
448 this->outputModule_->outputErrorLog(simulator_.gridView().comm());
451 auto localWellData = simulator_.problem().wellModel().wellData();
452 auto localGroupAndNetworkData = simulator_.problem().wellModel()
453 .groupAndNetworkData(reportStepNum);
455 auto localAquiferData = simulator_.problem().aquiferModel().aquiferData();
456 auto localWellTestState = simulator_.problem().wellModel().wellTestState();
458 const bool isFlowsn = this->outputModule_->getFlows().hasFlowsn();
459 auto flowsn = this->outputModule_->getFlows().getFlowsn();
461 const bool isFloresn = this->outputModule_->getFlows().hasFloresn();
462 auto floresn = this->outputModule_->getFlows().getFloresn();
464 if (! isSubStep || Parameters::Get<Parameters::EnableWriteAllSolutions>()) {
466 if (localCellData.empty()) {
467 this->outputModule_->assignToSolution(localCellData);
471 this->outputModule_->addRftDataToWells(localWellData,
473 simulator_.gridView().comm());
476 if (this->collectOnIORank_.isParallel() ||
477 this->collectOnIORank_.doesNeedReordering())
484 this->collectOnIORank_.collect(localCellData,
485 this->outputModule_->getBlockData(),
486 this->outputModule_->getExtraBlockData(),
489 localGroupAndNetworkData,
495 if (this->collectOnIORank_.isIORank()) {
496 this->outputModule_->assignGlobalFieldsToSolution(this->collectOnIORank_.globalCellData());
499 this->outputModule_->assignGlobalFieldsToSolution(localCellData);
502 if (this->collectOnIORank_.isIORank()) {
503 const Scalar curTime = simulator_.time() + simulator_.timeStepSize();
504 const Scalar nextStepSize = simulator_.problem().nextTimeStepSize();
505 std::optional<int> timeStepIdx;
506 if (Parameters::Get<Parameters::EnableWriteAllSolutions>()) {
507 timeStepIdx = simulator_.timeStepIndex();
509 this->doWriteOutput(reportStepNum, timeStepIdx, isSubStep,
511 std::move(localCellData),
512 std::move(localWellData),
513 std::move(localGroupAndNetworkData),
514 std::move(localAquiferData),
515 std::move(localWellTestState),
518 this->summaryState(),
519 this->simulator_.problem().thresholdPressure().getRestartVector(),
520 curTime, nextStepSize,
521 Parameters::Get<Parameters::EclOutputDoublePrecision>(),
522 isFlowsn, std::move(flowsn),
523 isFloresn, std::move(floresn));
529 const auto enablePCHysteresis = simulator_.problem().materialLawManager()->enablePCHysteresis();
530 const auto enableNonWettingHysteresis = simulator_.problem().materialLawManager()->enableNonWettingHysteresis();
531 const auto enableWettingHysteresis = simulator_.problem().materialLawManager()->enableWettingHysteresis();
532 const auto oilActive = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
533 const auto gasActive = FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
534 const auto waterActive = FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
535 const auto enableSwatinit = simulator_.vanguard().eclState().fieldProps().has_double(
"SWATINIT");
537 std::vector<RestartKey> solutionKeys {
538 {
"PRESSURE", UnitSystem::measure::pressure},
539 {
"SWAT", UnitSystem::measure::identity, waterActive},
540 {
"SGAS", UnitSystem::measure::identity, gasActive},
541 {
"TEMP", UnitSystem::measure::temperature, enableEnergy},
542 {
"SSOLVENT", UnitSystem::measure::identity, enableSolvent},
544 {
"RS", UnitSystem::measure::gas_oil_ratio, FluidSystem::enableDissolvedGas()},
545 {
"RV", UnitSystem::measure::oil_gas_ratio, FluidSystem::enableVaporizedOil()},
546 {
"RVW", UnitSystem::measure::oil_gas_ratio, FluidSystem::enableVaporizedWater()},
547 {
"RSW", UnitSystem::measure::gas_oil_ratio, FluidSystem::enableDissolvedGasInWater()},
549 {
"SGMAX", UnitSystem::measure::identity, enableNonWettingHysteresis && oilActive && gasActive},
550 {
"SHMAX", UnitSystem::measure::identity, enableWettingHysteresis && oilActive && gasActive},
552 {
"SOMAX", UnitSystem::measure::identity,
553 (enableNonWettingHysteresis && oilActive && waterActive)
554 || simulator_.problem().vapparsActive(simulator_.episodeIndex())},
556 {
"SOMIN", UnitSystem::measure::identity, enablePCHysteresis && oilActive && gasActive},
557 {
"SWHY1", UnitSystem::measure::identity, enablePCHysteresis && oilActive && waterActive},
558 {
"SWMAX", UnitSystem::measure::identity, enableWettingHysteresis && oilActive && waterActive},
560 {
"PPCW", UnitSystem::measure::pressure, enableSwatinit},
564 const auto& tracers = simulator_.vanguard().eclState().tracer();
566 for (
const auto& tracer : tracers) {
567 const auto enableSolTracer =
568 ((tracer.phase == Phase::GAS) && FluidSystem::enableDissolvedGas()) ||
569 ((tracer.phase == Phase::OIL) && FluidSystem::enableVaporizedOil());
571 solutionKeys.emplace_back(tracer.fname(), UnitSystem::measure::identity,
true);
572 solutionKeys.emplace_back(tracer.sname(), UnitSystem::measure::identity, enableSolTracer);
576 const auto& inputThpres = eclState().getSimulationConfig().getThresholdPressure();
577 const std::vector<RestartKey> extraKeys {
578 {
"OPMEXTRA", UnitSystem::measure::identity,
false},
579 {
"THRESHPR", UnitSystem::measure::pressure, inputThpres.active()},
582 const auto& gridView = this->simulator_.vanguard().gridView();
583 const auto numElements = gridView.size(0);
587 this->outputModule_->allocBuffers(numElements,
593 const auto restartSolution =
594 loadParallelRestartSolution(this->eclIO_.get(),
595 solutionKeys, gridView.comm(), 0);
597 if (!restartSolution.empty()) {
598 for (
auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
599 const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
600 this->outputModule_->setRestart(restartSolution, elemIdx, globalIdx);
603 this->simulator_.problem().readSolutionFromOutputModule(0,
true);
604 this->simulator_.problem().temperatureModel().init();
605 ElementContext elemCtx(this->simulator_);
606 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
607 elemCtx.updatePrimaryStencil(elem);
608 elemCtx.updatePrimaryIntensiveQuantities(0);
610 this->outputModule_->updateFluidInPlace(elemCtx);
613 this->outputModule_->calc_initial_inplace(this->simulator_.gridView().comm());
621 const auto restartStepIdx = this->simulator_.vanguard()
622 .eclState().getInitConfig().getRestartStep();
624 this->outputModule_->allocBuffers(numElements,
632 const auto restartValues =
633 loadParallelRestart(this->eclIO_.get(),
635 this->summaryState(),
636 solutionKeys, extraKeys, gridView.comm());
638 for (
auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
639 const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
640 this->outputModule_->setRestart(restartValues.solution, elemIdx, globalIdx);
643 auto& tracer_model = simulator_.problem().tracerModel();
644 for (
int tracer_index = 0; tracer_index < tracer_model.numTracers(); ++tracer_index) {
647 const auto& free_tracer_name = tracer_model.fname(tracer_index);
648 const auto& free_tracer_solution = restartValues.solution
649 .template data<double>(free_tracer_name);
651 for (
auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
652 const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
653 tracer_model.setFreeTracerConcentration
654 (tracer_index, elemIdx, free_tracer_solution[globalIdx]);
659 if ((tracer_model.phase(tracer_index) == Phase::GAS && FluidSystem::enableDissolvedGas()) ||
660 (tracer_model.phase(tracer_index) == Phase::OIL && FluidSystem::enableVaporizedOil()))
662 tracer_model.setEnableSolTracers(tracer_index,
true);
664 const auto& sol_tracer_name = tracer_model.sname(tracer_index);
665 const auto& sol_tracer_solution = restartValues.solution
666 .template data<double>(sol_tracer_name);
668 for (
auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
669 const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
670 tracer_model.setSolTracerConcentration
671 (tracer_index, elemIdx, sol_tracer_solution[globalIdx]);
675 tracer_model.setEnableSolTracers(tracer_index,
false);
677 for (
auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
678 tracer_model.setSolTracerConcentration(tracer_index, elemIdx, 0.0);
683 if (inputThpres.active()) {
684 const_cast<Simulator&
>(this->simulator_)
685 .problem().thresholdPressure()
686 .setFromRestart(restartValues.getExtra(
"THRESHPR"));
689 restartTimeStepSize_ = restartValues.getExtra(
"OPMEXTRA")[0];
690 if (restartTimeStepSize_ <= 0) {
691 restartTimeStepSize_ = std::numeric_limits<double>::max();
695 this->simulator_.problem().wellModel()
696 .initFromRestartFile(restartValues);
698 if (!restartValues.aquifer.empty()) {
699 this->simulator_.problem().mutableAquiferModel()
700 .initFromRestart(restartValues.aquifer);
710 this->outputModule_->calc_initial_inplace(this->simulator_.gridView().comm());
712 if (this->collectOnIORank_.isIORank()) {
713 if (
const auto* iip = this->outputModule_->initialInplace(); iip !=
nullptr) {
714 this->inplace_ = *iip;
719 const OutputModule& outputModule()
const
720 {
return *outputModule_; }
722 OutputModule& mutableOutputModule()
const
723 {
return *outputModule_; }
725 Scalar restartTimeStepSize()
const
726 {
return restartTimeStepSize_; }
728 template <
class Serializer>
729 void serializeOp(Serializer& serializer)
731 serializer(*outputModule_);
735 static bool enableEclOutput_()
737 static bool enable = Parameters::Get<Parameters::EnableEclOutput>();
741 const EclipseState& eclState()
const
742 {
return simulator_.vanguard().eclState(); }
744 SummaryState& summaryState()
745 {
return simulator_.vanguard().summaryState(); }
747 Action::State& actionState()
748 {
return simulator_.vanguard().actionState(); }
751 {
return simulator_.vanguard().udqState(); }
753 const Schedule& schedule()
const
754 {
return simulator_.vanguard().schedule(); }
756 void prepareLocalCellData(
const bool isSubStep,
757 const int reportStepNum)
759 OPM_TIMEBLOCK(prepareLocalCellData);
761 if (this->outputModule_->localDataValid()) {
765 const auto& gridView = simulator_.vanguard().gridView();
766 const bool log = this->collectOnIORank_.isIORank();
768 const int num_interior = detail::
769 countLocalInteriorCellsGridView(gridView);
770 this->outputModule_->
771 allocBuffers(num_interior, reportStepNum,
772 isSubStep && !Parameters::Get<Parameters::EnableWriteAllSolutions>(),
775 ElementContext elemCtx(simulator_);
777 OPM_BEGIN_PARALLEL_TRY_CATCH();
780 OPM_TIMEBLOCK(prepareCellBasedData);
782 this->outputModule_->prepareDensityAccumulation();
783 this->outputModule_->setupExtractors(isSubStep, reportStepNum);
784 for (
const auto& elem : elements(gridView, Dune::Partitions::interior)) {
785 elemCtx.updatePrimaryStencil(elem);
786 elemCtx.updatePrimaryIntensiveQuantities(0);
788 this->outputModule_->processElement(elemCtx);
789 this->outputModule_->processElementBlockData(elemCtx);
791 this->outputModule_->clearExtractors();
793 this->outputModule_->accumulateDensityParallel();
797 OPM_TIMEBLOCK(prepareFluidInPlace);
800#pragma omp parallel for
802 for (
int dofIdx = 0; dofIdx < num_interior; ++dofIdx) {
803 const auto& intQuants = *simulator_.model().cachedIntensiveQuantities(dofIdx, 0);
804 const auto totVolume = simulator_.model().dofTotalVolume(dofIdx);
806 this->outputModule_->updateFluidInPlace(dofIdx, intQuants, totVolume);
810 this->outputModule_->validateLocalData();
812 OPM_END_PARALLEL_TRY_CATCH(
"EclWriter::prepareLocalCellData() failed: ",
813 this->simulator_.vanguard().grid().comm());
816 void captureLocalFluxData()
818 OPM_TIMEBLOCK(captureLocalData);
820 const auto& gridView = this->simulator_.vanguard().gridView();
821 const auto timeIdx = 0u;
823 auto elemCtx = ElementContext { this->simulator_ };
825 const auto elemMapper = ElementMapper { gridView, Dune::mcmgElementLayout() };
826 const auto activeIndex = [&elemMapper](
const Element& e)
828 return elemMapper.index(e);
831 const auto cartesianIndex = [
this](
const int elemIndex)
833 return this->cartMapper_.cartesianIndex(elemIndex);
836 this->outputModule_->initializeFluxData();
838 OPM_BEGIN_PARALLEL_TRY_CATCH();
840 for (
const auto& elem : elements(gridView, Dune::Partitions::interiorBorder)) {
841 elemCtx.updateStencil(elem);
842 elemCtx.updateIntensiveQuantities(timeIdx);
843 elemCtx.updateExtensiveQuantities(timeIdx);
845 this->outputModule_->processFluxes(elemCtx, activeIndex, cartesianIndex);
848 OPM_END_PARALLEL_TRY_CATCH(
"EclWriter::captureLocalFluxData() failed: ",
849 this->simulator_.vanguard().grid().comm())
851 this->outputModule_->finalizeFluxData();
854 void writeWellspecReport(const SimulatorTimer& timer)
const
856 const auto changedWells = this->schedule_
857 .changed_wells(timer.reportStepNum(), this->initialStep());
859 const auto changedWellLists = this->schedule_
860 .changedWellLists(timer.reportStepNum(), this->initialStep());
862 if (changedWells.empty() && !changedWellLists) {
866 this->outputModule_->outputWellspecReport(changedWells,
868 timer.reportStepNum(),
869 timer.simulationTimeElapsed(),
870 timer.currentDateTime());
873 void writeWellflowReport(
const SimulatorTimer& timer,
875 const int wellsRequest)
const
877 this->outputModule_->outputTimeStamp(
"WELLS",
878 timer.simulationTimeElapsed(),
879 timer.reportStepNum(),
880 timer.currentDateTime());
882 const auto wantConnData = wellsRequest > 1;
884 this->outputModule_->outputProdLog(simStep, wantConnData);
885 this->outputModule_->outputInjLog(simStep, wantConnData);
886 this->outputModule_->outputCumLog(simStep, wantConnData);
887 this->outputModule_->outputMSWLog(simStep);
890 int initialStep()
const
892 const auto& initConfig = this->eclState().cfg().init();
894 return initConfig.restartRequested()
895 ? initConfig.getRestartStep()
899 Simulator& simulator_;
900 std::unique_ptr<OutputModule> outputModule_;
901 Scalar restartTimeStepSize_;