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