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
54
56#ifdef RESERVOIR_COUPLING_ENABLED
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
71namespace Opm::Parameters {
72
73// If available, write the ECL output in a non-blocking manner
74struct EnableAsyncEclOutput { static constexpr bool value = true; };
75
76// By default, use single precision for the ECL formated results
77struct EclOutputDoublePrecision { static constexpr bool value = false; };
78
79// Write all solutions for visualization, not just the ones for the
80// report steps...
81struct EnableWriteAllSolutions { static constexpr bool value = false; };
82
83// Write ESMRY file for fast loading of summary data
84struct EnableEsmry { static constexpr bool value = true; };
85
86} // namespace Opm::Parameters
87
88namespace Opm::Action {
89 class State;
90} // namespace Opm::Action
91
92namespace Opm {
93 class EclipseIO;
94 class UDQState;
95} // namespace Opm
96
97namespace Opm {
113template <class TypeTag, class OutputModule>
114class 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
142public:
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
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()) {
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 }
308 miscSummaryData["NEWTON"] = this->sub_step_report_.total_newton_iterations;
309 }
311 miscSummaryData["MLINEARS"] = this->sub_step_report_.total_linear_iterations;
312 }
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 }
320 miscSummaryData["NLINSMAX"] = this->sub_step_report_.max_linear_iterations;
321 }
323 miscSummaryData["MSUMLINS"] = this->simulation_report_.success.total_linear_iterations;
324 }
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()
338 : this->outputModule_->getBlockData();
339
340 const auto& lgrBlockData = this->collectOnIORank_.isParallel()
342 : this->outputModule_->getLgrBlockData();
343
344 const auto& interRegFlows = this->collectOnIORank_.isParallel()
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::
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
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 =
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
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
751private:
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::
810 this->outputModule_->
811 allocBuffers(num_interior, reportStepNum,
812 isSubStep && !Parameters::Get<Parameters::EnableWriteAllSolutions>(),
813 log, /*isRestart*/ false);
814
815 ElementContext elemCtx(simulator_);
816
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
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
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:192
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:158
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:69
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 Inplace &inplace, const Inplace *initialInPlace, const InterRegFlowMap &interRegFlows, SummaryState &summaryState, UDQState &udqState, const data::ReservoirCouplingGroupRates *rcGroupRates=nullptr)
Definition: EclGenericWriter_impl.hpp:1021
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:911
Collects necessary output values and pass it to opm-common's ECL output.
Definition: EclWriter.hpp:119
OutputModule & mutableOutputModule() const
Definition: EclWriter.hpp:739
const OutputModule & outputModule() const
Definition: EclWriter.hpp:736
void writeOutput(data::Solution &&localCellData, const bool isSubStep, const bool isForcedFinalOutput)
Definition: EclWriter.hpp:458
void evalSummaryState(bool isSubStep)
collect and pass data and pass it to eclIO writer
Definition: EclWriter.hpp:209
static void registerParameters()
Definition: EclWriter.hpp:144
void serializeOp(Serializer &serializer)
Definition: EclWriter.hpp:746
void writeInitialFIPReport()
Writes the initial FIP report as configured in RPTSOL.
Definition: EclWriter.hpp:368
void beginRestart()
Definition: EclWriter.hpp:544
EclWriter(Simulator &simulator)
Definition: EclWriter.hpp:158
void writeReports(const SimulatorTimer &timer)
Definition: EclWriter.hpp:413
void endRestart()
Definition: EclWriter.hpp:722
Scalar restartTimeStepSize() const
Definition: EclWriter.hpp:742
~EclWriter()
Definition: EclWriter.hpp:198
const EquilGrid & globalGrid() const
Definition: EclWriter.hpp:201
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:39
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:190
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:77
static constexpr bool value
Definition: EclWriter.hpp:77
Definition: EclWriter.hpp:74
static constexpr bool value
Definition: EclWriter.hpp:74
Definition: EclWriter.hpp:84
static constexpr bool value
Definition: EclWriter.hpp:84
Definition: EclWriter.hpp:81
static constexpr bool value
Definition: EclWriter.hpp:81
SimulatorReportSingle success
Definition: SimulatorReport.hpp:123
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