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