opm-simulators
EclWriter.hpp
Go to the documentation of this file.
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 /*
4  This file is part of the Open Porous Media project (OPM).
5 
6  OPM is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 2 of the License, or
9  (at your option) any later version.
10 
11  OPM is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with OPM. If not, see <http://www.gnu.org/licenses/>.
18 
19  Consult the COPYING file in the top-level source directory of this
20  module for the precise wording of the license and the list of
21  copyright holders.
22 */
28 #ifndef OPM_ECL_WRITER_HPP
29 #define OPM_ECL_WRITER_HPP
30 
31 #include <dune/grid/common/partitionset.hh>
32 
33 #include <opm/common/TimingMacros.hpp> // OPM_TIMEBLOCK
34 #include <opm/common/OpmLog/OpmLog.hpp>
35 #include <opm/input/eclipse/Schedule/RPTConfig.hpp>
36 
37 #include <opm/input/eclipse/Units/UnitSystem.hpp>
38 #include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
39 
40 #include <opm/output/eclipse/Inplace.hpp>
41 #include <opm/output/eclipse/RestartValue.hpp>
42 
43 #include <opm/models/blackoil/blackoilproperties.hh> // Properties::EnableMech, EnableSolvent
44 #include <opm/models/common/multiphasebaseproperties.hh> // Properties::FluidSystem
45 
46 #include <opm/simulators/flow/CollectDataOnIORank.hpp>
47 #include <opm/simulators/flow/countGlobalCells.hpp>
50 #include <opm/simulators/timestepping/SimulatorTimer.hpp>
51 #include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
52 #include <opm/simulators/utils/ParallelRestart.hpp>
53 #include <opm/simulators/utils/ParallelSerialization.hpp>
54 
55 #include <opm/simulators/flow/rescoup/ReservoirCouplingEnabled.hpp>
56 #ifdef RESERVOIR_COUPLING_ENABLED
57 #include <opm/simulators/flow/rescoup/ReservoirCouplingMaster.hpp>
58 #endif
59 
60 #include <boost/date_time/posix_time/posix_time.hpp>
61 
62 #include <limits>
63 #include <map>
64 #include <memory>
65 #include <optional>
66 #include <stdexcept>
67 #include <string>
68 #include <utility>
69 #include <vector>
70 
71 namespace Opm::Parameters {
72 
73 // If available, write the ECL output in a non-blocking manner
74 struct EnableAsyncEclOutput { static constexpr bool value = true; };
75 
76 // By default, use single precision for the ECL formated results
77 struct EclOutputDoublePrecision { static constexpr bool value = false; };
78 
79 // Write all solutions for visualization, not just the ones for the
80 // report steps...
81 struct EnableWriteAllSolutions { static constexpr bool value = false; };
82 
83 // Write ESMRY file for fast loading of summary data
84 struct EnableEsmry { static constexpr bool value = true; };
85 
86 } // namespace Opm::Parameters
87 
88 namespace Opm::Action {
89  class State;
90 } // namespace Opm::Action
91 
92 namespace Opm {
93  class EclipseIO;
94  class UDQState;
95 } // namespace Opm
96 
97 namespace Opm {
113 template <class TypeTag, class OutputModule>
114 class EclWriter : public EclGenericWriter<GetPropType<TypeTag, Properties::Grid>,
115  GetPropType<TypeTag, Properties::EquilGrid>,
116  GetPropType<TypeTag, Properties::GridView>,
117  GetPropType<TypeTag, Properties::ElementMapper>,
118  GetPropType<TypeTag, Properties::Scalar>>
119 {
128  using Element = typename GridView::template Codim<0>::Entity;
130  using ElementIterator = typename GridView::template Codim<0>::Iterator;
132 
133  typedef Dune::MultipleCodimMultipleGeomTypeMapper< GridView > VertexMapper;
134 
135  static constexpr bool enableEnergy =
136  getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal ||
137  getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::SequentialImplicitThermal;
138  enum { enableMech = getPropValue<TypeTag, Properties::EnableMech>() };
139  static constexpr bool enableSolvent = getPropValue<TypeTag, Properties::EnableSolvent>();
140  enum { enableGeochemistry = getPropValue<TypeTag, Properties::EnableGeochemistry>() };
141 
142 public:
143 
144  static void registerParameters()
145  {
146  OutputModule::registerParameters();
147 
148  Parameters::Register<Parameters::EnableAsyncEclOutput>
149  ("Write the ECL-formated results in a non-blocking way "
150  "(i.e., using a separate thread).");
151  Parameters::Register<Parameters::EnableEsmry>
152  ("Write ESMRY file for fast loading of summary data.");
153  }
154 
155  // The Simulator object should preferably have been const - the
156  // only reason that is not the case is due to the SummaryState
157  // object owned deep down by the vanguard.
158  explicit EclWriter(Simulator& simulator)
159  : BaseType(simulator.vanguard().schedule(),
160  simulator.vanguard().eclState(),
161  simulator.vanguard().summaryConfig(),
162  simulator.vanguard().grid(),
163  ((simulator.vanguard().grid().comm().rank() == 0)
164  ? &simulator.vanguard().equilGrid()
165  : nullptr),
166  simulator.vanguard().gridView(),
167  simulator.vanguard().cartesianIndexMapper(),
168  ((simulator.vanguard().grid().comm().rank() == 0)
169  ? &simulator.vanguard().equilCartesianIndexMapper()
170  : nullptr),
171  Parameters::Get<Parameters::EnableAsyncEclOutput>(),
172  Parameters::Get<Parameters::EnableEsmry>())
173  , simulator_(simulator)
174  {
175 #if HAVE_MPI
176  if (this->simulator_.vanguard().grid().comm().size() > 1) {
177  auto smryCfg = (this->simulator_.vanguard().grid().comm().rank() == 0)
178  ? this->eclIO_->finalSummaryConfig()
179  : SummaryConfig{};
180 
181  eclBroadcast(this->simulator_.vanguard().grid().comm(), smryCfg);
182 
183  this->outputModule_ = std::make_unique<OutputModule>
184  (simulator, smryCfg, this->collectOnIORank_);
185  }
186  else
187 #endif
188  {
189  this->outputModule_ = std::make_unique<OutputModule>
190  (simulator, this->eclIO_->finalSummaryConfig(), this->collectOnIORank_);
191  }
192 
193  this->rank_ = this->simulator_.vanguard().grid().comm().rank();
194 
195  this->simulator_.vanguard().eclState().computeFipRegionStatistics();
196  }
197 
198  ~EclWriter()
199  {}
200 
201  const EquilGrid& globalGrid() const
202  {
203  return simulator_.vanguard().equilGrid();
204  }
205 
209  void evalSummaryState(bool isSubStep)
210  {
211  OPM_TIMEBLOCK(evalSummaryState);
212  const int reportStepNum = simulator_.episodeIndex() + 1;
213 
214  /*
215  The summary data is not evaluated for timestep 0, that is
216  implemented with a:
217 
218  if (time_step == 0)
219  return;
220 
221  check somewhere in the summary code. When the summary code was
222  split in separate methods Summary::eval() and
223  Summary::add_timestep() it was necessary to pull this test out
224  here to ensure that the well and group related keywords in the
225  restart file, like XWEL and XGRP were "correct" also in the
226  initial report step.
227 
228  "Correct" in this context means unchanged behavior, might very
229  well be more correct to actually remove this if test.
230  */
231 
232  if (reportStepNum == 0)
233  return;
234 
235  const Scalar curTime = simulator_.time() + simulator_.timeStepSize();
236  const Scalar totalCpuTime =
237  simulator_.executionTimer().realTimeElapsed() +
238  simulator_.setupTimer().realTimeElapsed() +
239  simulator_.vanguard().setupTime();
240 
241  const auto localWellData = simulator_.problem().wellModel().wellData();
242  const auto localWBP = simulator_.problem().wellModel().wellBlockAveragePressures();
243  const auto localGroupAndNetworkData = simulator_.problem().wellModel()
244  .groupAndNetworkData(reportStepNum);
245 
246  const auto localAquiferData = simulator_.problem().aquiferModel().aquiferData();
247  const auto localWellTestState = simulator_.problem().wellModel().wellTestState();
248  this->prepareLocalCellData(isSubStep, reportStepNum);
249 
250  if (this->outputModule_->needInterfaceFluxes(isSubStep)) {
251  this->captureLocalFluxData();
252  }
253 
254  if (this->collectOnIORank_.isParallel()) {
255  OPM_BEGIN_PARALLEL_TRY_CATCH()
256 
257  std::map<std::pair<std::string,int>,double> dummy;
258  this->collectOnIORank_.collect({},
259  outputModule_->getBlockData(),
260  dummy,
261  localWellData,
262  localWBP,
263  localGroupAndNetworkData,
264  localAquiferData,
265  localWellTestState,
266  this->outputModule_->getInterRegFlows(),
267  {},
268  {},
269  this->outputModule_->getLgrBlockData());
270 
271  if (this->collectOnIORank_.isIORank()) {
272  auto& iregFlows = this->collectOnIORank_.globalInterRegFlows();
273 
274  if (! iregFlows.readIsConsistent()) {
275  throw std::runtime_error {
276  "Inconsistent inter-region flow "
277  "region set names in parallel"
278  };
279  }
280 
281  iregFlows.compress();
282  }
283 
284  OPM_END_PARALLEL_TRY_CATCH("Collect to I/O rank: ",
285  this->simulator_.vanguard().grid().comm());
286  }
287 
288 
289  std::map<std::string, double> miscSummaryData;
290  std::map<std::string, std::vector<double>> regionData;
291  Inplace inplace;
292 
293  {
294  OPM_TIMEBLOCK(outputFipLogAndFipresvLog);
295 
296  inplace = outputModule_->calc_inplace(miscSummaryData, regionData, simulator_.gridView().comm());
297 
298  if (this->collectOnIORank_.isIORank()){
299  inplace_ = inplace;
300  }
301  }
302 
303  // Add TCPU
304  if (totalCpuTime != 0.0) {
305  miscSummaryData["TCPU"] = totalCpuTime;
306  }
307  if (this->sub_step_report_.total_newton_iterations != 0) {
308  miscSummaryData["NEWTON"] = this->sub_step_report_.total_newton_iterations;
309  }
310  if (this->sub_step_report_.total_linear_iterations != 0) {
311  miscSummaryData["MLINEARS"] = this->sub_step_report_.total_linear_iterations;
312  }
313  if (this->sub_step_report_.total_newton_iterations != 0) {
314  miscSummaryData["NLINEARS"] = static_cast<float>(this->sub_step_report_.total_linear_iterations) / this->sub_step_report_.total_newton_iterations;
315  }
316  if (this->sub_step_report_.min_linear_iterations != std::numeric_limits<unsigned int>::max()) {
317  miscSummaryData["NLINSMIN"] = this->sub_step_report_.min_linear_iterations;
318  }
319  if (this->sub_step_report_.max_linear_iterations != 0) {
320  miscSummaryData["NLINSMAX"] = this->sub_step_report_.max_linear_iterations;
321  }
322  if (this->simulation_report_.success.total_linear_iterations != 0) {
323  miscSummaryData["MSUMLINS"] = this->simulation_report_.success.total_linear_iterations;
324  }
325  if (this->simulation_report_.success.total_newton_iterations != 0) {
326  miscSummaryData["MSUMNEWT"] = this->simulation_report_.success.total_newton_iterations;
327  }
328 
329  // For reservoir coupling master: collect slave production/injection
330  // rates to pass through to Summary::eval() via DynamicSimulatorState.
331  const auto rcGroupRates = this->collectReservoirCouplingGroupRates_();
332 
333  {
334  OPM_TIMEBLOCK(evalSummary);
335 
336  const auto& blockData = this->collectOnIORank_.isParallel()
337  ? this->collectOnIORank_.globalBlockData()
338  : this->outputModule_->getBlockData();
339 
340  const auto& lgrBlockData = this->collectOnIORank_.isParallel()
341  ? this->collectOnIORank_.globalLgrBlockData()
342  : this->outputModule_->getLgrBlockData();
343 
344  const auto& interRegFlows = this->collectOnIORank_.isParallel()
345  ? this->collectOnIORank_.globalInterRegFlows()
346  : this->outputModule_->getInterRegFlows();
347 
348  this->evalSummary(reportStepNum,
349  curTime,
350  localWellData,
351  localWBP,
352  localGroupAndNetworkData,
353  localAquiferData,
354  blockData,
355  lgrBlockData,
356  miscSummaryData,
357  regionData,
358  inplace,
359  this->outputModule_->initialInplace(),
360  interRegFlows,
361  this->summaryState(),
362  this->udqState(),
363  rcGroupRates ? &(*rcGroupRates) : nullptr);
364  }
365  }
366 
369  {
370  const auto& gridView = simulator_.vanguard().gridView();
371  const int num_interior = detail::
372  countLocalInteriorCellsGridView(gridView);
373 
374  this->outputModule_->
375  allocBuffers(num_interior, 0, false, false, /*isRestart*/ false);
376 
377 #ifdef _OPENMP
378 #pragma omp parallel for
379 #endif
380  for (int dofIdx = 0; dofIdx < num_interior; ++dofIdx) {
381  const auto& intQuants = *simulator_.model().cachedIntensiveQuantities(dofIdx, /*timeIdx=*/0);
382  const auto totVolume = simulator_.model().dofTotalVolume(dofIdx);
383 
384  this->outputModule_->updateFluidInPlace(dofIdx, intQuants, totVolume);
385  }
386 
387  // We always calculate the initial fip values as it may be used by various
388  // keywords in the Schedule, e.g. FIP=2 in RPTSCHED but no FIP in RPTSOL
389  outputModule_->calc_initial_inplace(simulator_.gridView().comm());
390 
391  // check if RPTSOL entry has FIP output
392  const auto& fip = simulator_.vanguard().eclState().getEclipseConfig().fip();
393  if (fip.output(FIPConfig::OutputField::FIELD) ||
394  fip.output(FIPConfig::OutputField::RESV))
395  {
396  OPM_TIMEBLOCK(outputFipLogAndFipresvLog);
397 
398  const auto start_time = boost::posix_time::
399  from_time_t(simulator_.vanguard().schedule().getStartTime());
400 
401  if (this->collectOnIORank_.isIORank()) {
402  this->inplace_ = *this->outputModule_->initialInplace();
403 
404  this->outputModule_->
405  outputFipAndResvLog(this->inplace_, 0, 0.0, start_time,
406  false, simulator_.gridView().comm());
407  }
408  }
409 
410  outputModule_->outputFipAndResvLogToCSV(0, false, simulator_.gridView().comm());
411  }
412 
413  void writeReports(const SimulatorTimer& timer)
414  {
415  if (! this->collectOnIORank_.isIORank()) {
416  return;
417  }
418 
419  // SimulatorTimer::reportStepNum() is the simulator's zero-based
420  // "episode index". This is generally the index value needed to
421  // look up objects in the Schedule container. That said, function
422  // writeReports() is invoked at the *beginning* of a report
423  // step/episode which means we typically need the objects from the
424  // *previous* report step/episode. We therefore need special case
425  // handling for reportStepNum() == 0 in base runs and
426  // reportStepNum() <= restart step in restarted runs.
427  const auto firstStep = this->initialStep();
428  const auto simStep =
429  std::max(timer.reportStepNum() - 1, firstStep);
430 
431  const auto& rpt = this->schedule_[simStep].rpt_config();
432 
433  if (rpt.contains("WELSPECS") && (rpt.at("WELSPECS") > 0)) {
434  // Requesting a well specification report is valid at all times,
435  // including reportStepNum() == initialStep().
436  this->writeWellspecReport(timer);
437  }
438 
439  if (timer.reportStepNum() == firstStep) {
440  // No dynamic flows at the beginning of the initialStep().
441  return;
442  }
443 
444  if (rpt.contains("WELLS") && rpt.at("WELLS") > 0) {
445  this->writeWellflowReport(timer, simStep, rpt.at("WELLS"));
446  }
447 
448  this->outputModule_->outputFipAndResvLog(this->inplace_,
449  timer.reportStepNum(),
450  timer.simulationTimeElapsed(),
451  timer.currentDateTime(),
452  /* isSubstep = */ false,
453  simulator_.gridView().comm());
454 
455  OpmLog::note(""); // Blank line after all reports.
456  }
457 
458  void writeOutput(data::Solution&& localCellData, const bool isSubStep, const bool isForcedFinalOutput)
459  {
460  OPM_TIMEBLOCK(writeOutput);
461 
462  const int reportStepNum = simulator_.episodeIndex() + 1;
463  this->prepareLocalCellData(isSubStep, reportStepNum);
464  this->outputModule_->outputErrorLog(simulator_.gridView().comm());
465 
466  // output using eclWriter if enabled
467  auto localWellData = simulator_.problem().wellModel().wellData();
468  auto localGroupAndNetworkData = simulator_.problem().wellModel()
469  .groupAndNetworkData(reportStepNum);
470 
471  auto localAquiferData = simulator_.problem().aquiferModel().aquiferData();
472  auto localWellTestState = simulator_.problem().wellModel().wellTestState();
473 
474  const bool isFlowsn = this->outputModule_->getFlows().hasFlowsn();
475  auto flowsn = this->outputModule_->getFlows().getFlowsn();
476 
477  const bool isFloresn = this->outputModule_->getFlows().hasFloresn();
478  auto floresn = this->outputModule_->getFlows().getFloresn();
479 
480  if (! isSubStep || Parameters::Get<Parameters::EnableWriteAllSolutions>()) {
481 
482  if (localCellData.empty()) {
483  this->outputModule_->assignToSolution(localCellData);
484  }
485 
486  // Add cell data to perforations for RFT output
487  this->outputModule_->addRftDataToWells(localWellData,
488  reportStepNum,
489  simulator_.gridView().comm());
490  }
491 
492  if (this->collectOnIORank_.isParallel() ||
493  this->collectOnIORank_.doesNeedReordering())
494  {
495  // Note: We don't need WBP (well-block averaged pressures) or
496  // inter-region flow rate values in order to create restart file
497  // output. There's consequently no need to collect those
498  // properties on the I/O rank.
499 
500  this->collectOnIORank_.collect(localCellData,
501  this->outputModule_->getBlockData(),
502  this->outputModule_->getExtraBlockData(),
503  localWellData,
504  /* wbpData = */ {},
505  localGroupAndNetworkData,
506  localAquiferData,
507  localWellTestState,
508  /* interRegFlows = */ {},
509  flowsn,
510  floresn,
511  /* lgrBlockData = */ {});
512  if (this->collectOnIORank_.isIORank()) {
513  this->outputModule_->assignGlobalFieldsToSolution(this->collectOnIORank_.globalCellData());
514  }
515  } else {
516  this->outputModule_->assignGlobalFieldsToSolution(localCellData);
517  }
518 
519  if (this->collectOnIORank_.isIORank()) {
520  const Scalar curTime = simulator_.time() + simulator_.timeStepSize();
521  const Scalar nextStepSize = simulator_.problem().nextTimeStepSize();
522  std::optional<int> timeStepIdx;
523  if (Parameters::Get<Parameters::EnableWriteAllSolutions>()) {
524  timeStepIdx = simulator_.timeStepIndex();
525  }
526  this->doWriteOutput(reportStepNum, timeStepIdx, isSubStep,
527  isForcedFinalOutput,
528  std::move(localCellData),
529  std::move(localWellData),
530  std::move(localGroupAndNetworkData),
531  std::move(localAquiferData),
532  std::move(localWellTestState),
533  this->actionState(),
534  this->udqState(),
535  this->summaryState(),
536  this->simulator_.problem().thresholdPressure().getRestartVector(),
537  curTime, nextStepSize,
538  Parameters::Get<Parameters::EclOutputDoublePrecision>(),
539  isFlowsn, std::move(flowsn),
540  isFloresn, std::move(floresn));
541  }
542  }
543 
544  void beginRestart()
545  {
546  const auto enablePCHysteresis = simulator_.problem().materialLawManager()->enablePCHysteresis();
547  const auto enableNonWettingHysteresis = simulator_.problem().materialLawManager()->enableNonWettingHysteresis();
548  const auto enableWettingHysteresis = simulator_.problem().materialLawManager()->enableWettingHysteresis();
549  const auto oilActive = FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx);
550  const auto gasActive = FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx);
551  const auto waterActive = FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx);
552  const auto enableSwatinit = simulator_.vanguard().eclState().fieldProps().has_double("SWATINIT");
553 
554  std::vector<RestartKey> solutionKeys {
555  {"PRESSURE", UnitSystem::measure::pressure},
556  {"SWAT", UnitSystem::measure::identity, waterActive},
557  {"SGAS", UnitSystem::measure::identity, gasActive},
558  {"TEMP", UnitSystem::measure::temperature, enableEnergy},
559  {"SSOLVENT", UnitSystem::measure::identity, enableSolvent},
560 
561  {"RS", UnitSystem::measure::gas_oil_ratio, FluidSystem::enableDissolvedGas()},
562  {"RV", UnitSystem::measure::oil_gas_ratio, FluidSystem::enableVaporizedOil()},
563  {"RVW", UnitSystem::measure::oil_gas_ratio, FluidSystem::enableVaporizedWater()},
564  {"RSW", UnitSystem::measure::gas_oil_ratio, FluidSystem::enableDissolvedGasInWater()},
565 
566  {"SGMAX", UnitSystem::measure::identity, enableNonWettingHysteresis && oilActive && gasActive},
567  {"SHMAX", UnitSystem::measure::identity, enableWettingHysteresis && oilActive && gasActive},
568 
569  {"SOMAX", UnitSystem::measure::identity,
570  (enableNonWettingHysteresis && oilActive && waterActive)
571  || simulator_.problem().vapparsActive(simulator_.episodeIndex())},
572 
573  {"SOMIN", UnitSystem::measure::identity, enablePCHysteresis && oilActive && gasActive},
574  {"SWHY1", UnitSystem::measure::identity, enablePCHysteresis && oilActive && waterActive},
575  {"SWMAX", UnitSystem::measure::identity, enableWettingHysteresis && oilActive && waterActive},
576 
577  {"PPCW", UnitSystem::measure::pressure, enableSwatinit},
578  };
579 
580  {
581  const auto& tracers = simulator_.vanguard().eclState().tracer();
582 
583  for (const auto& tracer : tracers) {
584  const auto enableSolTracer =
585  ((tracer.phase == Phase::GAS) && FluidSystem::enableDissolvedGas()) ||
586  ((tracer.phase == Phase::OIL) && FluidSystem::enableVaporizedOil());
587 
588  solutionKeys.emplace_back(tracer.fname(), UnitSystem::measure::identity, true);
589  solutionKeys.emplace_back(tracer.sname(), UnitSystem::measure::identity, enableSolTracer);
590  }
591  }
592 
593  const auto& inputThpres = eclState().getSimulationConfig().getThresholdPressure();
594  const std::vector<RestartKey> extraKeys {
595  {"OPMEXTRA", UnitSystem::measure::identity, false},
596  {"THRESHPR", UnitSystem::measure::pressure, inputThpres.active()},
597  };
598 
599  const auto& gridView = this->simulator_.vanguard().gridView();
600  const auto numElements = gridView.size(/*codim=*/0);
601 
602  // Try to load restart step 0 to calculate initial FIP
603  {
604  this->outputModule_->allocBuffers(numElements,
605  0,
606  /*isSubStep = */false,
607  /*log = */ false,
608  /*isRestart = */true);
609 
610  const auto restartSolution =
611  loadParallelRestartSolution(this->eclIO_.get(),
612  solutionKeys, gridView.comm(), 0);
613 
614  if (!restartSolution.empty()) {
615  for (auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
616  const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
617  this->outputModule_->setRestart(restartSolution, elemIdx, globalIdx);
618  }
619 
620  this->simulator_.problem().readSolutionFromOutputModule(0, true);
621  this->simulator_.problem().temperatureModel().init();
622  ElementContext elemCtx(this->simulator_);
623  for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
624  elemCtx.updatePrimaryStencil(elem);
625  elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
626 
627  this->outputModule_->updateFluidInPlace(elemCtx);
628  }
629 
630  this->outputModule_->calc_initial_inplace(this->simulator_.gridView().comm());
631  }
632  }
633 
634  {
635  // The episodeIndex is rewound one step back before calling
636  // beginRestart() and cannot be used here. We just ask the
637  // initconfig directly to be sure that we use the correct index.
638  const auto restartStepIdx = this->simulator_.vanguard()
639  .eclState().getInitConfig().getRestartStep();
640 
641  this->outputModule_->allocBuffers(numElements,
642  restartStepIdx,
643  /*isSubStep = */false,
644  /*log = */ false,
645  /*isRestart = */true);
646  }
647 
648  {
649  const auto restartValues =
650  loadParallelRestart(this->eclIO_.get(),
651  this->actionState(),
652  this->summaryState(),
653  solutionKeys, extraKeys, gridView.comm());
654 
655  for (auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
656  const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
657  this->outputModule_->setRestart(restartValues.solution, elemIdx, globalIdx);
658  }
659 
660  auto& tracer_model = simulator_.problem().tracerModel();
661  for (int tracer_index = 0; tracer_index < tracer_model.numTracers(); ++tracer_index) {
662  // Free tracers
663  {
664  const auto& free_tracer_name = tracer_model.fname(tracer_index);
665  const auto& free_tracer_solution = restartValues.solution
666  .template data<double>(free_tracer_name);
667 
668  for (auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
669  const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
670  tracer_model.setFreeTracerConcentration
671  (tracer_index, elemIdx, free_tracer_solution[globalIdx]);
672  }
673  }
674 
675  // Solution tracer (only if DISGAS/VAPOIL are active for gas/oil tracers)
676  if ((tracer_model.phase(tracer_index) == Phase::GAS && FluidSystem::enableDissolvedGas()) ||
677  (tracer_model.phase(tracer_index) == Phase::OIL && FluidSystem::enableVaporizedOil()))
678  {
679  tracer_model.setEnableSolTracers(tracer_index, true);
680 
681  const auto& sol_tracer_name = tracer_model.sname(tracer_index);
682  const auto& sol_tracer_solution = restartValues.solution
683  .template data<double>(sol_tracer_name);
684 
685  for (auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
686  const auto globalIdx = this->collectOnIORank_.localIdxToGlobalIdx(elemIdx);
687  tracer_model.setSolTracerConcentration
688  (tracer_index, elemIdx, sol_tracer_solution[globalIdx]);
689  }
690  }
691  else {
692  tracer_model.setEnableSolTracers(tracer_index, false);
693 
694  for (auto elemIdx = 0*numElements; elemIdx < numElements; ++elemIdx) {
695  tracer_model.setSolTracerConcentration(tracer_index, elemIdx, 0.0);
696  }
697  }
698  }
699 
700  if (inputThpres.active()) {
701  const_cast<Simulator&>(this->simulator_)
702  .problem().thresholdPressure()
703  .setFromRestart(restartValues.getExtra("THRESHPR"));
704  }
705 
706  restartTimeStepSize_ = restartValues.getExtra("OPMEXTRA")[0];
707  if (restartTimeStepSize_ <= 0) {
708  restartTimeStepSize_ = std::numeric_limits<double>::max();
709  }
710 
711  // Initialize the well model from restart values
712  this->simulator_.problem().wellModel()
713  .initFromRestartFile(restartValues);
714 
715  if (!restartValues.aquifer.empty()) {
716  this->simulator_.problem().mutableAquiferModel()
717  .initFromRestart(restartValues.aquifer);
718  }
719  }
720  }
721 
722  void endRestart()
723  {
724  // Calculate initial in-place volumes.
725  // Does nothing if they have already been calculated,
726  // e.g. from restart data at T=0.
727  this->outputModule_->calc_initial_inplace(this->simulator_.gridView().comm());
728 
729  if (this->collectOnIORank_.isIORank()) {
730  if (const auto* iip = this->outputModule_->initialInplace(); iip != nullptr) {
731  this->inplace_ = *iip;
732  }
733  }
734  }
735 
736  const OutputModule& outputModule() const
737  { return *outputModule_; }
738 
739  OutputModule& mutableOutputModule() const
740  { return *outputModule_; }
741 
742  Scalar restartTimeStepSize() const
743  { return restartTimeStepSize_; }
744 
745  template <class Serializer>
746  void serializeOp(Serializer& serializer)
747  {
748  serializer(*outputModule_);
749  }
750 
751 private:
752  static bool enableEclOutput_()
753  {
754  static bool enable = Parameters::Get<Parameters::EnableEclOutput>();
755  return enable;
756  }
757 
758  const EclipseState& eclState() const
759  { return simulator_.vanguard().eclState(); }
760 
761  SummaryState& summaryState()
762  { return simulator_.vanguard().summaryState(); }
763 
764  Action::State& actionState()
765  { return simulator_.vanguard().actionState(); }
766 
767  UDQState& udqState()
768  { return simulator_.vanguard().udqState(); }
769 
770  const Schedule& schedule() const
771  { return simulator_.vanguard().schedule(); }
772 
775  std::optional<data::ReservoirCouplingGroupRates> collectReservoirCouplingGroupRates_()
776  {
777 #ifdef RESERVOIR_COUPLING_ENABLED
778  // Guard: only BlackoilWellModel has reservoir coupling support.
779  // CompWellModel (compositional) does not, so we use if constexpr
780  // to avoid compilation errors when EclWriter is instantiated with
781  // a compositional TypeTag.
782  using WellModelType = std::remove_cvref_t<
783  decltype(simulator_.problem().wellModel())>;
784  if constexpr (requires(WellModelType& wm) { wm.isReservoirCouplingMaster(); }) {
785  auto& wellModel = simulator_.problem().wellModel();
786  if (!wellModel.isReservoirCouplingMaster()) {
787  return std::nullopt;
788  }
789  return wellModel.reservoirCouplingMaster()
790  .collectGroupRatesForSummary();
791  }
792 #endif
793  return std::nullopt;
794  }
795 
796  void prepareLocalCellData(const bool isSubStep,
797  const int reportStepNum)
798  {
799  OPM_TIMEBLOCK(prepareLocalCellData);
800 
801  if (this->outputModule_->localDataValid()) {
802  return;
803  }
804 
805  const auto& gridView = simulator_.vanguard().gridView();
806  const bool log = this->collectOnIORank_.isIORank();
807 
808  const int num_interior = detail::
809  countLocalInteriorCellsGridView(gridView);
810  this->outputModule_->
811  allocBuffers(num_interior, reportStepNum,
812  isSubStep && !Parameters::Get<Parameters::EnableWriteAllSolutions>(),
813  log, /*isRestart*/ false);
814 
815  ElementContext elemCtx(simulator_);
816 
817  OPM_BEGIN_PARALLEL_TRY_CATCH();
818 
819  {
820  OPM_TIMEBLOCK(prepareCellBasedData);
821 
822  this->outputModule_->prepareDensityAccumulation();
823  this->outputModule_->setupExtractors(isSubStep, reportStepNum);
824  for (const auto& elem : elements(gridView, Dune::Partitions::interior)) {
825  elemCtx.updatePrimaryStencil(elem);
826  elemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
827 
828  this->outputModule_->processElement(elemCtx);
829  this->outputModule_->processElementBlockData(elemCtx);
830  }
831  this->outputModule_->clearExtractors();
832 
833  this->outputModule_->accumulateDensityParallel();
834  }
835 
836  {
837  OPM_TIMEBLOCK(prepareFluidInPlace);
838 
839 #ifdef _OPENMP
840 #pragma omp parallel for
841 #endif
842  for (int dofIdx = 0; dofIdx < num_interior; ++dofIdx) {
843  const auto& intQuants = *simulator_.model().cachedIntensiveQuantities(dofIdx, /*timeIdx=*/0);
844  const auto totVolume = simulator_.model().dofTotalVolume(dofIdx);
845 
846  this->outputModule_->updateFluidInPlace(dofIdx, intQuants, totVolume);
847  }
848  }
849 
850  this->outputModule_->validateLocalData();
851 
852  OPM_END_PARALLEL_TRY_CATCH("EclWriter::prepareLocalCellData() failed: ",
853  this->simulator_.vanguard().grid().comm());
854  }
855 
856  void captureLocalFluxData()
857  {
858  OPM_TIMEBLOCK(captureLocalData);
859 
860  const auto& gridView = this->simulator_.vanguard().gridView();
861  const auto timeIdx = 0u;
862 
863  auto elemCtx = ElementContext { this->simulator_ };
864 
865  const auto elemMapper = ElementMapper { gridView, Dune::mcmgElementLayout() };
866  const auto activeIndex = [&elemMapper](const Element& e)
867  {
868  return elemMapper.index(e);
869  };
870 
871  const auto cartesianIndex = [this](const int elemIndex)
872  {
873  return this->cartMapper_.cartesianIndex(elemIndex);
874  };
875 
876  this->outputModule_->initializeFluxData();
877 
878  OPM_BEGIN_PARALLEL_TRY_CATCH();
879 
880  for (const auto& elem : elements(gridView, Dune::Partitions::interiorBorder)) {
881  elemCtx.updateStencil(elem);
882  elemCtx.updateIntensiveQuantities(timeIdx);
883  elemCtx.updateExtensiveQuantities(timeIdx);
884 
885  this->outputModule_->processFluxes(elemCtx, activeIndex, cartesianIndex);
886  }
887 
888  OPM_END_PARALLEL_TRY_CATCH("EclWriter::captureLocalFluxData() failed: ",
889  this->simulator_.vanguard().grid().comm())
890 
891  this->outputModule_->finalizeFluxData();
892  }
893 
894  void writeWellspecReport(const SimulatorTimer& timer) const
895  {
896  const auto changedWells = this->schedule_
897  .changed_wells(timer.reportStepNum(), this->initialStep());
898 
899  const auto changedWellLists = this->schedule_
900  .changedWellLists(timer.reportStepNum(), this->initialStep());
901 
902  if (changedWells.empty() && !changedWellLists) {
903  return;
904  }
905 
906  this->outputModule_->outputWellspecReport(changedWells,
907  changedWellLists,
908  timer.reportStepNum(),
909  timer.simulationTimeElapsed(),
910  timer.currentDateTime());
911  }
912 
913  void writeWellflowReport(const SimulatorTimer& timer,
914  const int simStep,
915  const int wellsRequest) const
916  {
917  this->outputModule_->outputTimeStamp("WELLS",
918  timer.simulationTimeElapsed(),
919  timer.reportStepNum(),
920  timer.currentDateTime());
921 
922  const auto wantConnData = wellsRequest > 1;
923 
924  this->outputModule_->outputProdLog(simStep, wantConnData);
925  this->outputModule_->outputInjLog(simStep, wantConnData);
926  this->outputModule_->outputCumLog(simStep, wantConnData);
927  this->outputModule_->outputMSWLog(simStep);
928  }
929 
930  int initialStep() const
931  {
932  const auto& initConfig = this->eclState().cfg().init();
933 
934  return initConfig.restartRequested()
935  ? initConfig.getRestartStep()
936  : 0;
937  }
938 
939  Simulator& simulator_;
940  std::unique_ptr<OutputModule> outputModule_;
941  Scalar restartTimeStepSize_;
942  int rank_ ;
943  Inplace inplace_;
944 };
945 
946 } // namespace Opm
947 
948 #endif // OPM_ECL_WRITER_HPP
void evalSummaryState(bool isSubStep)
collect and pass data and pass it to eclIO writer
Definition: EclWriter.hpp:209
Definition: ActionHandler.hpp:34
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(...))
Definition: propertysystem.hh:233
double simulationTimeElapsed() const override
Time elapsed since the start of the simulation until the beginning of the current time step [s]...
Definition: SimulatorTimer.cpp:122
Definition: EclWriter.hpp:81
Helper class for grid instantiation of ECL file-format using problems.
virtual int reportStepNum() const
Current report step number. This might differ from currentStepNum in case of sub stepping.
Definition: SimulatorTimerInterface.hpp:109
Defines the common properties required by the porous medium multi-phase models.
void compress()
Form CSR adjacency matrix representation of input graph from connections established in previous call...
Definition: InterRegFlows.cpp:164
Definition: EclWriter.hpp:77
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Definition: blackoilnewtonmethodparams.hpp:31
Definition: EclWriter.hpp:84
Declares the properties required by the black oil model.
Definition: EclWriter.hpp:74
void writeInitialFIPReport()
Writes the initial FIP report as configured in RPTSOL.
Definition: EclWriter.hpp:368
Definition: EclGenericWriter.hpp:68
Collects necessary output values and pass it to opm-common&#39;s ECL output.
Collects necessary output values and pass it to opm-common&#39;s ECL output.
Definition: EclWriter.hpp:114
Definition: SimulatorTimer.hpp:38
virtual boost::posix_time::ptime currentDateTime() const
Return the current time as a posix time object.
Definition: SimulatorTimerInterface.cpp:28