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