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