EclGenericWriter_impl.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*/
23#ifndef OPM_ECL_GENERIC_WRITER_IMPL_HPP
24#define OPM_ECL_GENERIC_WRITER_IMPL_HPP
25
26#include <dune/grid/common/mcmgmapper.hh>
27
28#include <opm/grid/cpgrid/LgrOutputHelpers.hpp>
29#include <opm/grid/GridHelpers.hpp>
30#include <opm/grid/utility/cartesianToCompressed.hpp>
31
32#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
33#include <opm/input/eclipse/EclipseState/Grid/RegionSetMatcher.hpp>
34#include <opm/input/eclipse/EclipseState/Grid/NNC.hpp>
35#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
36
37#include <opm/input/eclipse/Schedule/Action/State.hpp>
38#include <opm/input/eclipse/Schedule/RPTConfig.hpp>
39#include <opm/input/eclipse/Schedule/Schedule.hpp>
40#include <opm/input/eclipse/Schedule/SummaryState.hpp>
41#include <opm/input/eclipse/Schedule/UDQ/UDQConfig.hpp>
42#include <opm/input/eclipse/Schedule/UDQ/UDQState.hpp>
43#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
44#include <opm/input/eclipse/Schedule/Well/WellMatcher.hpp>
45
46#include <opm/input/eclipse/Units/UnitSystem.hpp>
47
48#include <opm/output/data/RegionVariableMapping.hpp>
49
50#include <opm/output/eclipse/EclipseIO.hpp>
51#include <opm/output/eclipse/RegionVariableCollection.hpp>
52#include <opm/output/eclipse/RestartValue.hpp>
53#include <opm/output/eclipse/Summary.hpp>
54
56
57#if HAVE_MPI
59#endif
60
61#if HAVE_MPI
62#include <mpi.h>
63#endif
64
65#include <algorithm>
66#include <array>
67#include <cassert>
68#include <cmath>
69#include <functional>
70#include <map>
71#include <memory>
72#include <string>
73#include <unordered_map>
74#include <utility>
75#include <vector>
76
77namespace {
78
93bool directVerticalNeighbors(const std::array<int, 3>& cartDims,
94 const std::unordered_map<int,int>& cartesianToActive,
95 int smallGlobalIndex, int largeGlobalIndex)
96{
97 assert(smallGlobalIndex <= largeGlobalIndex);
98 std::array<int, 3> ijk1, ijk2;
99 auto globalToIjk = [cartDims](int gc) {
100 std::array<int, 3> ijk;
101 ijk[0] = gc % cartDims[0];
102 gc /= cartDims[0];
103 ijk[1] = gc % cartDims[1];
104 ijk[2] = gc / cartDims[1];
105 return ijk;
106 };
107 ijk1 = globalToIjk(smallGlobalIndex);
108 ijk2 = globalToIjk(largeGlobalIndex);
109 assert(ijk2[2]>=ijk1[2]);
110
111 if ( ijk1[0] == ijk2[0] && ijk1[1] == ijk2[1] && (ijk2[2] - ijk1[2]) > 1)
112 {
113 assert((largeGlobalIndex-smallGlobalIndex)%(cartDims[0]*cartDims[1])==0);
114 for ( int gi = smallGlobalIndex + cartDims[0] * cartDims[1]; gi < largeGlobalIndex;
115 gi += cartDims[0] * cartDims[1] )
116 {
117 if ( cartesianToActive.find( gi ) != cartesianToActive.end() )
118 {
119 return false;
121 }
122 return true;
123 } else
124 return false;
125}
127std::unordered_map<std::string, Opm::data::InterRegFlowMap>
128getInterRegFlowsAsMap(const Opm::InterRegFlowMap& map)
129{
130 auto maps = std::unordered_map<std::string, Opm::data::InterRegFlowMap>{};
131
132 const auto& regionNames = map.names();
133 auto flows = map.getInterRegFlows();
134 const auto nmap = regionNames.size();
135
136 maps.reserve(nmap);
137 for (auto mapID = 0*nmap; mapID < nmap; ++mapID) {
138 maps.emplace(regionNames[mapID], std::move(flows[mapID]));
139 }
140
141 return maps;
142}
143
144struct EclWriteTasklet : public Opm::TaskletInterface
145{
146 Opm::Action::State actionState_;
147 Opm::WellTestState wtestState_;
148 Opm::SummaryState summaryState_;
149 Opm::UDQState udqState_;
150 Opm::EclipseIO& eclIO_;
151 int reportStepNum_;
152 std::optional<int> timeStepNum_;
153 bool isSubStep_;
154 double secondsElapsed_;
155 std::vector<Opm::RestartValue> restartValue_;
156 bool writeDoublePrecision_;
158 bool forcedSimulationFinished_;
159
160 explicit EclWriteTasklet(const Opm::Action::State& actionState,
161 const Opm::WellTestState& wtestState,
162 const Opm::SummaryState& summaryState,
163 const Opm::UDQState& udqState,
164 Opm::EclipseIO& eclIO,
165 int reportStepNum,
166 std::optional<int> timeStepNum,
167 bool isSubStep,
168 double secondsElapsed,
169 std::vector<Opm::RestartValue> restartValue,
170 bool writeDoublePrecision,
171 bool forcedSimulationFinished)
172 : actionState_(actionState)
173 , wtestState_(wtestState)
174 , summaryState_(summaryState)
175 , udqState_(udqState)
176 , eclIO_(eclIO)
177 , reportStepNum_(reportStepNum)
178 , timeStepNum_(timeStepNum)
179 , isSubStep_(isSubStep)
180 , secondsElapsed_(secondsElapsed)
181 , restartValue_(std::move(restartValue))
182 , writeDoublePrecision_(writeDoublePrecision)
183 , forcedSimulationFinished_(forcedSimulationFinished)
184 {}
185
186 // callback to eclIO serial writeTimeStep method
187 void run() override
188 {
189 if (this->restartValue_.size() == 1) {
190 this->eclIO_.writeTimeStep(this->actionState_,
191 this->wtestState_,
192 this->summaryState_,
193 this->udqState_,
194 this->reportStepNum_,
195 this->isSubStep_,
196 this->secondsElapsed_,
197 std::move(this->restartValue_.back()),
198 this->writeDoublePrecision_,
199 this->timeStepNum_,
200 forcedSimulationFinished_);
201 }
202 else{
203 this->eclIO_.writeTimeStep(this->actionState_,
204 this->wtestState_,
205 this->summaryState_,
206 this->udqState_,
207 this->reportStepNum_,
208 this->isSubStep_,
209 this->secondsElapsed_,
210 std::move(this->restartValue_),
211 this->writeDoublePrecision_,
212 this->timeStepNum_,
213 forcedSimulationFinished_);
214 }
215 }
216};
217
218}
219
220namespace Opm {
221
222template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
224EclGenericWriter(const Schedule& schedule,
225 const EclipseState& eclState,
226 const SummaryConfig& summaryConfig,
227 const Grid& grid,
228 const EquilGrid* equilGrid,
229 const GridView& gridView,
230 const Dune::CartesianIndexMapper<Grid>& cartMapper,
231 const Dune::CartesianIndexMapper<EquilGrid>* equilCartMapper,
232 bool enableAsyncOutput,
233 bool enableEsmry )
234 : collectOnIORank_(grid,
235 equilGrid,
236 gridView,
237 cartMapper,
238 equilCartMapper,
239 summaryConfig.fip_regions_interreg_flow())
240 , grid_ (grid)
241 , gridView_ (gridView)
242 , schedule_ (schedule)
243 , eclState_ (eclState)
244 , cartMapper_ (cartMapper)
245 , equilCartMapper_(equilCartMapper)
246 , equilGrid_ (equilGrid)
247{
248 // Make sure outputNnc_ vector has at least 1 entry in all ranks.
249 outputNnc_.resize(1);
250
251 if (this->collectOnIORank_.isIORank()) {
252 this->eclIO_ = std::make_unique<EclipseIO>
253 (this->eclState_,
254 UgGridHelpers::createEclipseGrid(*equilGrid, eclState_.getInputGrid()),
255 this->schedule_, summaryConfig, "", enableEsmry);
256 }
257
258 // create output thread if enabled and rank is I/O rank
259 // async output is enabled by default if pthread are enabled
260 int numWorkerThreads = 0;
261 if (enableAsyncOutput && collectOnIORank_.isIORank()) {
262 numWorkerThreads = 1;
263 }
264
265 this->taskletRunner_.reset(new TaskletRunner(numWorkerThreads));
266}
267
268template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
270eclIO() const
271{
272 assert(eclIO_);
273 return *eclIO_;
274}
275
276template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
278writeInit()
279{
280 if (collectOnIORank_.isIORank()) {
281 std::map<std::string, std::vector<int>> integerVectors;
282 if (collectOnIORank_.isParallel()) {
283 integerVectors.emplace("MPI_RANK", collectOnIORank_.globalRanks());
284 }
285
286 if (const auto& lgrs = this->eclState_.getLgrs(); lgrs.size() > 0) {
287
288 const auto nncCollection = Opm::NNCCollection::fromLGROutputContainers(this->outputNnc_,
289 this->outputNncGlobalLocal_,
290 this->outputAmalgamatedNnc_);
291 eclIO_->writeInitial(*this->outputTrans_,
292 integerVectors,
293 nncCollection);
294 } else {
295 eclIO_->writeInitial(*this->outputTrans_,
296 integerVectors,
297 this->outputNnc_.front());
298 }
299 this->outputTrans_.reset();
300 }
301}
302
303template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
304void
306extractOutputTransAndNNC(const std::function<unsigned int(unsigned int)>& map)
307{
308 if (collectOnIORank_.isIORank()) {
309 constexpr bool equilGridIsCpGrid = std::is_same_v<EquilGrid, Dune::CpGrid>;
310
311 const auto levelCartMapp = this->createLevelCartMapp_<equilGridIsCpGrid>();
312 const auto levelCartToLevelCompressed = this->createCartesianToActiveMaps_<equilGridIsCpGrid>(levelCartMapp);
313 auto computeLevelIndices = this->computeLevelIndices_<equilGridIsCpGrid>();
314 auto computeLevelCartIdx = this->computeLevelCartIdx_<equilGridIsCpGrid>(levelCartMapp, *(this->equilCartMapper_));
315 auto computeLevelCartDimensions = this->computeLevelCartDimensions_<equilGridIsCpGrid>(levelCartMapp, *(this->equilCartMapper_));
316 auto computeOriginIndices = this->computeOriginIndices_<equilGridIsCpGrid>();
317
318 computeTrans_(levelCartToLevelCompressed, map, computeLevelIndices,
319 computeLevelCartIdx, computeLevelCartDimensions, computeOriginIndices);
320 exportNncStructure_(levelCartToLevelCompressed, map, computeLevelIndices, computeLevelCartIdx,
321 computeLevelCartDimensions, computeOriginIndices);
322 }
323
324#if HAVE_MPI
325 if (collectOnIORank_.isParallel()) {
326 const auto& comm = grid_.comm();
327 Parallel::MpiSerializer ser(comm);
328 ser.broadcast(Parallel::RootRank{0}, outputNnc_);
329 }
330#endif
331}
332
333template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
334bool
336isNumAquCell_(const std::size_t cartIdx) const
337{
338 const auto& numAquCell = this->eclState_.aquifer().hasNumericalAquifer()
339 ? this->eclState_.aquifer().numericalAquifers().allAquiferCellIds()
340 : std::vector<std::size_t>{};
341
342 return std::ranges::binary_search(numAquCell.begin(), numAquCell.end(), cartIdx);
343}
344
345template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
346bool
347EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
348isNumAquConn_(const std::size_t cartIdx1,
349 const std::size_t cartIdx2) const
350{
351 return isNumAquCell_(cartIdx1) || isNumAquCell_(cartIdx2);
352}
353
354template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
355template<bool equilGridIsCpGrid>
357EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
358createLevelCartMapp_() const
359{
360 if constexpr (equilGridIsCpGrid) {
361 return Opm::LevelCartesianIndexMapper<EquilGrid>(*this->equilGrid_);
362 } else {
363 return Opm::LevelCartesianIndexMapper<EquilGrid>(*equilCartMapper_); }
364}
365
366template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
367template<bool equilGridIsCpGrid>
368std::vector<std::unordered_map<int,int>>
369EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
370createCartesianToActiveMaps_(const Opm::LevelCartesianIndexMapper<EquilGrid>& levelCartMapp) const
371{
372 if constexpr (equilGridIsCpGrid) {
373 if (this->equilGrid_->maxLevel()) {
374 return Opm::Lgr::levelCartesianToLevelCompressedMaps(*this->equilGrid_, levelCartMapp); }
375 else {
376 return std::vector<std::unordered_map<int,int>>{ cartesianToCompressed(equilGrid_->size(0), UgGridHelpers::globalCell(*equilGrid_)) };
377 }
378 }
379 return std::vector<std::unordered_map<int,int>>{ cartesianToCompressed(equilGrid_->size(0), UgGridHelpers::globalCell(*equilGrid_)) };
380}
381
382template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
383template<bool equilGridIsCpGrid>
384std::function<std::array<int,3>(int)>
385EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
386computeLevelCartDimensions_(const Opm::LevelCartesianIndexMapper<EquilGrid>& levelCartMapp,
387 const Dune::CartesianIndexMapper<EquilGrid>& equilCartMapp) const
388{
389 if constexpr (equilGridIsCpGrid) {
390 return [&](int level)
391 {
392 return levelCartMapp.cartesianDimensions(level);
393 };
394 }
395 else {
396 return [&]([[maybe_unused]] int level)
397 {
398 assert(level == 0); // refinement only supported for CpGrid for now
399 return equilCartMapp.cartesianDimensions();
400 };
401 }
402}
403
404template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
405template<bool equilGridIsCpGrid>
406std::function<int(int, int)>
407EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
408computeLevelCartIdx_(const Opm::LevelCartesianIndexMapper<EquilGrid>& levelCartMapp,
409 const Dune::CartesianIndexMapper<EquilGrid>& equilCartMapp) const
410{
411 if constexpr (equilGridIsCpGrid) {
412 return [&](int levelCompressedIdx,
413 int level)
414 {
415 return levelCartMapp.cartesianIndex(levelCompressedIdx, level);
416 };
417 }
418 else {
419 return [&](int levelCompressedIdx,
420 [[maybe_unused]] int level)
421 {
422 assert(level == 0); // refinement only supported for CpGrid for now
423 return equilCartMapp.cartesianIndex(levelCompressedIdx);
424 };
425 }
426}
427
428template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
429template <bool equilGridIsCpGrid>
430auto
431EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
432computeLevelIndices_() const
433{
434 if constexpr (equilGridIsCpGrid) {
435 return [](const auto& intersection,
436 const auto&,
437 const auto&)
438 {
439 return std::pair{intersection.inside().getLevelElem().index(), intersection.outside().getLevelElem().index()};
440 };
441 }
442 else {
443 return [](const auto&,
444 const auto& intersectionInsideLeafIdx,
445 const auto& intersectionOutsideLeafIdx)
446 {
447 return std::pair{intersectionInsideLeafIdx, intersectionOutsideLeafIdx};
448 };
449 }
450}
451
452template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
453template <bool equilGridIsCpGrid>
454auto
455EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
456computeOriginIndices_() const
457{
458 if constexpr (equilGridIsCpGrid) {
459 return [](const auto& intersection,
460 const auto&,
461 const auto&)
462 {
463 return std::pair{intersection.inside().getOrigin().index(), intersection.outside().getOrigin().index()};
464 };
465 }
466 else {
467 return [](const auto&,
468 const auto& intersectionInsideLeafIdx,
469 const auto& intersectionOutsideLeafIdx)
470 {
471 return std::pair{intersectionInsideLeafIdx, intersectionOutsideLeafIdx};
472 };
473 }
474}
475
476template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
477void
478EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
479allocateLevelTrans_(const std::array<int,3>& levelCartDims,
480 data::Solution& levelTrans) const
481{
482 auto createLevelCellData = [&levelCartDims]() {
483 return Opm::data::CellData{
484 Opm::UnitSystem::measure::transmissibility,
485 std::vector<double>(levelCartDims[0] * levelCartDims[1] * levelCartDims[2], 0.0),
486 Opm::data::TargetType::INIT
487 };
488 };
489
490 levelTrans.clear();
491 levelTrans.emplace("TRANX", createLevelCellData());
492 levelTrans.emplace("TRANY", createLevelCellData());
493 levelTrans.emplace("TRANZ", createLevelCellData());
494}
495
496template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
497template<typename LevelIndicesFunction, typename OriginIndicesFunction>
498void
499EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
500computeTrans_(const std::vector<std::unordered_map<int,int>>& levelCartToLevelCompressed,
501 const std::function<unsigned int(unsigned int)>& map,
502 const LevelIndicesFunction& computeLevelIndices,
503 const std::function<int(int, int)>& computeLevelCartIdx,
504 const std::function<std::array<int,3>(int)>& computeLevelCartDims,
505 const OriginIndicesFunction& computeOriginIndices) const
506{
507 if (!outputTrans_) {
508 outputTrans_ = std::make_unique<std::vector<data::Solution>>(std::vector<data::Solution>{});
509 }
510
511 using GlobalGridView = typename EquilGrid::LeafGridView;
512 using GlobElementMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GlobalGridView>;
513 const GlobalGridView& globalGridView = this->equilGrid_->leafGridView();
514 const GlobElementMapper globalElemMapper { globalGridView, Dune::mcmgElementLayout() };
515
516 // Refinement supported only for CpGrid for now.
517 int maxLevel = this->equilGrid_->maxLevel();
518
519 outputTrans_->resize(maxLevel+1); // including level zero grid
520
521 for (int level = 0; level <= maxLevel; ++level) {
522 allocateLevelTrans_(computeLevelCartDims(level), this->outputTrans_->at(level));
523 }
524
525 for (const auto& elem : elements(globalGridView)) {
526 for (const auto& is : intersections(globalGridView, elem)) {
527 if (!is.neighbor())
528 continue; // intersection is on the domain boundary
529
530 if ( is.inside().level() != is.outside().level() ) // Those are treated as NNCs
531 continue;
532
533 // Not 'const' because remapped if 'map' is non-null.
534 unsigned c1 = globalElemMapper.index(is.inside());
535 unsigned c2 = globalElemMapper.index(is.outside());
536
537 if (c1 > c2)
538 continue; // we only need to handle each connection once, thank you.
539
540 int level = is.inside().level();
541
542 // For CpGrid with LGRs, level*Idx and c* do not coincide.
543 const auto& [levelInIdx, levelOutIdx] = computeLevelIndices(is, c1, c2);
544
545 const int levelCartIdxIn = computeLevelCartIdx(levelInIdx, level);
546 const int levelCartIdxOut = computeLevelCartIdx(levelOutIdx, level);
547
548 // For CpGrid with LGRs, the origin cell index refers to the coarsest
549 // ancestor cell when the cell is refined. For cells not involved in
550 // any refinement, it corresponds to the geometrically equivalent
551 // cell in the level-zero grid.
552 const auto [originInIdx, originOutIdx] = computeOriginIndices(is, c1, c2);
553
554 const auto originCartIdxIn = computeLevelCartIdx(originInIdx, /* level = */ 0);
555 const auto originCartIdxOut = computeLevelCartIdx(originOutIdx, /* level = */ 0);
556
557 // For level-zero grid, level Cartesian indices coincide with the grid Cartesian indices.
558 if (isNumAquCell_(originCartIdxIn) || isNumAquCell_(originCartIdxOut)) {
559 // Check there are no refined aquifer cells.
560 assert(level == 0);
561 // Connections involving numerical aquifers are always NNCs
562 // for the purpose of file output. This holds even for
563 // connections between cells like (I,J,K) and (I+1,J,K)
564 // which are nominally neighbours in the Cartesian grid.
565 continue;
566 }
567
568 const auto minLevelCartIdx = std::min(levelCartIdxIn, levelCartIdxOut);
569 const auto maxLevelCartIdx = std::max(levelCartIdxIn, levelCartIdxOut);
570
571 const auto& levelCartDims = computeLevelCartDims(level);
572
573 // Re-ordering in case of non-empty mapping between equilGrid to grid
574 if (map) {
575 c1 = map(c1); // equilGridToGrid map
576 c2 = map(c2);
577 }
578
579 if (maxLevelCartIdx - minLevelCartIdx == 1 && levelCartDims[0] > 1 ) {
580 outputTrans_->at(level).at("TRANX").template data<double>()[minLevelCartIdx] = globalTrans().transmissibility(c1, c2);
581 continue; // skip other if clauses as they are false, last one needs some computation
582 }
583
584 if (maxLevelCartIdx - minLevelCartIdx == levelCartDims[0] && levelCartDims[1] > 1) {
585 outputTrans_->at(level).at("TRANY").template data<double>()[minLevelCartIdx] = globalTrans().transmissibility(c1, c2);
586 continue; // skipt next if clause as it needs some computation
587 }
588
589 if ( maxLevelCartIdx - minLevelCartIdx == levelCartDims[0]*levelCartDims[1] ||
590 directVerticalNeighbors(levelCartDims,
591 levelCartToLevelCompressed[level],
592 minLevelCartIdx,
593 maxLevelCartIdx)) {
594 outputTrans_->at(level).at("TRANZ").template data<double>()[minLevelCartIdx] = globalTrans().transmissibility(c1, c2);
595 }
596 }
597 }
598}
599
600template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
601bool
602EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
603isCartesianNeighbour_(const std::array<int,3>& levelCartDims,
604 const std::size_t levelCartIdx1,
605 const std::size_t levelCartIdx2) const
606{
607 const int diff = levelCartIdx2 - levelCartIdx1;
608
609 return (diff == 1)
610 || (diff == levelCartDims[0])
611 || (diff == (levelCartDims[0] * levelCartDims[1]));
612}
613
614template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
615bool
616EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
617isDirectNeighbours_(const std::unordered_map<int,int>& levelCartesianToActive,
618 const std::array<int,3>& levelCartDims,
619 const std::size_t levelCartIdx1,
620 const std::size_t levelCartIdx2) const
621{
622 return isCartesianNeighbour_(levelCartDims, levelCartIdx1, levelCartIdx2)
623 || directVerticalNeighbors(levelCartDims, levelCartesianToActive, levelCartIdx1, levelCartIdx2);
624}
625
626template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
627auto
628EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
629activeCell_(const std::unordered_map<int,int>& levelCartToLevelCompressed,
630 const std::size_t levelCartIdx) const
631{
632 auto pos = levelCartToLevelCompressed.find(levelCartIdx);
633 return (pos == levelCartToLevelCompressed.end()) ? -1 : pos->second;
634}
635
636template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
637void
638EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
639allocateAllNncs_(int maxLevel) const
640{
641 this->outputNnc_.resize(maxLevel+1); // level 0,1,..., maxLevel
642
643 if (maxLevel) {
644 // NNCs between main (level zero) grid and LGRs: level 1, ...., maxLevel.
645 // Example: grid with maxLevel == 3, outputNncGlobalLocal_.size() is maxLevel = 3
646 // outputNncGlobalLocal_[0] -> NNCs between level 0 and level 1
647 // outputNncGlobalLocal_[1] -> NNCs between level 0 and level 2
648 // outputAmalgamatedNnc_[2] -> NNCs between level 0 and level 3
649 this->outputNncGlobalLocal_.resize(maxLevel);
650
651 // NNCs between different refined level grids: (level1, level2)
652 // with 0 < level1 < level2 <= maxLevel
653 // Example: grid with maxLevel == 3, outputAmalgamatedNnc_.size() is maxLevel-1 = 2
654 // outputAmalgamatedNnc_[0][0] -> NNCs between level 1 and level 2
655 // outputAmalgamatedNnc_[0][1] -> NNCs between level 1 and level 3
656 // outputAmalgamatedNnc_[1][2] -> NNCs between level 2 and level 3
657 this->outputAmalgamatedNnc_.resize(maxLevel-1);
658 for (int i = 0; i < maxLevel-1; ++i) {
659 this->outputAmalgamatedNnc_[i].resize(maxLevel-1-i);
660 }
661 }
662}
663
664template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
665template<typename LevelIndicesFunction, typename OriginIndicesFunction>
666std::vector<std::vector<NNCdata>>
667EclGenericWriter<Grid,EquilGrid,GridView,ElementMapper,Scalar>::
668exportNncStructure_(const std::vector<std::unordered_map<int,int>>& levelCartToLevelCompressed,
669 const std::function<unsigned int(unsigned int)>& map,
670 const LevelIndicesFunction& computeLevelIndices,
671 const std::function<int(int, int)>& computeLevelCartIdx,
672 const std::function<std::array<int,3>(int)>& computeLevelCartDims,
673 const OriginIndicesFunction& computeOriginIndices) const
674{
675 const auto& nncData = this->eclState_.getInputNNC().input();
676 const auto& nncEdit = this->eclState_.getInputNNC().edit();
677 const auto& nncEditr = this->eclState_.getInputNNC().editr();
678 const auto& unitSystem = this->eclState_.getDeckUnitSystem();
679 const auto& transMult = this->eclState_.getTransMult();
680
681 // Cartesian index mapper for the serial I/O grid
682 const auto& equilCartMapper = *equilCartMapper_;
683
684 const auto& level0CartDims = equilCartMapper.cartesianDimensions();
685
686 int maxLevel = this->equilGrid_->maxLevel();
687 allocateAllNncs_(maxLevel);
688
689 using GlobalGridView = typename EquilGrid::LeafGridView;
690 using GlobElementMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GlobalGridView>;
691 const GlobalGridView& globalGridView = this->equilGrid_->leafGridView();
692 const GlobElementMapper globalElemMapper { globalGridView, Dune::mcmgElementLayout() };
693
694 for (const auto& elem : elements(globalGridView)) {
695 for (const auto& is : intersections(globalGridView, elem)) {
696 if (!is.neighbor())
697 continue; // intersection is on the domain boundary
698
699 // Not 'const' because remapped if 'map' is non-null.
700 unsigned c1 = globalElemMapper.index(is.inside());
701 unsigned c2 = globalElemMapper.index(is.outside());
702
703 if (c1 > c2)
704 continue; // we only need to handle each connection once, thank you.
705
706 if ( is.inside().level() != is.outside().level() ) { // TRANGL and TRANLL
707 // For CpGrid with LGRs, level*Idx and c* do not coincide.
708 const auto& [levelInIdx, levelOutIdx] = computeLevelIndices(is, c1, c2);
709
710 const int levelIn = is.inside().level();
711 const int levelOut = is.outside().level();
712
713 auto levelCartIdxIn = computeLevelCartIdx(levelInIdx, levelIn);
714 auto levelCartIdxOut = computeLevelCartIdx(levelOutIdx, levelOut);
715
716 // To store correctly and only once the corresponding NNC
717 std::pair<int,int> smallerPair = {levelIn, levelCartIdxIn},
718 largerPair = {levelOut, levelCartIdxOut};
719 if (smallerPair.first > largerPair.first) {
720 std::swap(smallerPair, largerPair);
721 }
722
723 const auto& [smallerLevel, smallerLevelCartIdx] = smallerPair;
724 const auto& [largerLevel, largerLevelCartIdx] = largerPair;
725
726 auto t = this->globalTrans().transmissibility(c1, c2);
727
728 // ECLIPSE ignores NNCs with zero transmissibility
729 // (different threshold than for NNC with corresponding
730 // EDITNNC above). In addition we do set small
731 // transmissibilities to zero when setting up the simulator.
732 // These will be ignored here, too.
733 const auto tt = unitSystem
734 .from_si(UnitSystem::measure::transmissibility, t);
735
736 if (std::isnormal(tt) && (tt > 1.0e-12)) {
737 // Store always FIRST the level Cartesian index of the cell belonging to the smaller level grid involved.
738 if (smallerLevel == 0) { // NNC between main (level zero) grid and a refined level/local grid
739 this->outputNncGlobalLocal_[largerLevel-1].emplace_back(smallerLevelCartIdx, largerLevelCartIdx, t);
740 }
741 else { // NNC between different refined level/local grids -> amlgamated NNC
742 assert(smallerLevel >= 1);
743 this->outputAmalgamatedNnc_[smallerLevel-1][largerLevel-smallerLevel-1].emplace_back(smallerLevelCartIdx, largerLevelCartIdx, t);
744 }
745 }
746 }
747 else {
748 // the cells sharing the intersection belong to the same level
749 assert(is.inside().level() == is.outside().level());
750 const int level = is.inside().level();
751
752 // For CpGrid with LGRs, the origin cell index refers to the coarsest
753 // ancestor cell when the cell is refined. For cells not involved in
754 // any refinement, it corresponds to the geometrically equivalent
755 // cell in the level-zero grid.
756 const auto [originInIdx, originOutIdx] = computeOriginIndices(is, c1, c2);
757
758 const std::size_t originCartIdxIn = computeLevelCartIdx(originInIdx, /* level = */ 0);
759 const std::size_t originCartIdxOut = computeLevelCartIdx(originOutIdx, /* level = */ 0);
760
761 // For CpGrid with LGRs, level*Idx and c* do not coincide.
762 const auto& [levelInIdx, levelOutIdx] = computeLevelIndices(is, c1, c2);
763
764 auto levelCartIdxIn = computeLevelCartIdx(levelInIdx, level);
765 auto levelCartIdxOut = computeLevelCartIdx(levelOutIdx, level);
766
767 if ( levelCartIdxOut < levelCartIdxIn )
768 std::swap(levelCartIdxIn, levelCartIdxOut);
769
770 // Re-ordering in case of non-empty mapping between equilGrid to grid
771 if (map) {
772 c1 = map(c1); // equilGridToGrid map
773 c2 = map(c2);
774 }
775
776 const auto& levelCartDims = computeLevelCartDims(level);
777
778 // Check there are no refined aquifer connections
779 assert(!isNumAquConn_(originCartIdxIn, originCartIdxOut) || level == 0);
780
781 if (isNumAquConn_(originCartIdxIn, originCartIdxOut) ||
782 ! isDirectNeighbours_(levelCartToLevelCompressed[level],
783 levelCartDims,
784 levelCartIdxIn, levelCartIdxOut)) {
785 // We need to check whether an NNC for this face was also
786 // specified via the NNC keyword in the deck.
787 auto t = this->globalTrans().transmissibility(c1, c2);
788
789 if (level == 0) {
790 auto candidate = std::lower_bound(nncData.begin(), nncData.end(),
791 NNCdata { originCartIdxIn, originCartIdxOut, 0.0 });
792 const auto transMlt = transMult.getRegionMultiplierNNC(originCartIdxIn, originCartIdxOut);
793 bool foundNncEditr = false;
794
795 while ((candidate != nncData.end()) &&
796 (candidate->cell1 == originCartIdxIn) &&
797 (candidate->cell2 == originCartIdxOut))
798 {
799 auto trans = candidate->trans;
800 trans *= transMlt;
801 if (! nncEditr.empty()) {
802 auto it = std::lower_bound(nncEditr.begin(), nncEditr.end(),
803 NNCdata { originCartIdxIn, originCartIdxOut, 0.0 });
804 foundNncEditr = it != nncEditr.end() && it->cell1 == originCartIdxIn && it->cell2 == originCartIdxOut;
805 }
806 if (foundNncEditr) {
807 // Only write one value for EDITNNCR, then skip it here and add it on the second loop below
808 break;
809 }
810 if (! nncEdit.empty()) {
811 auto it = std::lower_bound(nncEdit.begin(), nncEdit.end(),
812 NNCdata { originCartIdxIn, originCartIdxOut, 0.0 });
813 if (it != nncEdit.end() && it->cell1 == originCartIdxIn && it->cell2 == originCartIdxOut) {
814 trans *= it->trans;
815 }
816 }
817 t -= trans;
818 ++candidate;
819 }
820 if (foundNncEditr) {
821 // Only write one value for EDITNNCR, then skip it here and add it on the second loop below
822 continue;
823 }
824 }
825
826 // ECLIPSE ignores NNCs with zero transmissibility
827 // (different threshold than for NNC with corresponding
828 // EDITNNC above). In addition we do set small
829 // transmissibilities to zero when setting up the simulator.
830 // These will be ignored here, too.
831 const auto tt = unitSystem
832 .from_si(UnitSystem::measure::transmissibility, t);
833
834 if (std::isnormal(tt) && (tt > 1.0e-12)) {
835 this->outputNnc_[level].emplace_back(levelCartIdxIn, levelCartIdxOut, t);
836 }
837 }
838 }
839 }
840 }
841
842 // Do not include the generated NNCs transsmisibilities in the input NNCs
843 std::vector<NNCdata> inputedNnc{};
844 const auto generatedNnc = outputNnc_[0];
845
846 // The NNC keyword in the deck is defined only for faces in the level-0 grid.
847 // The same limitation applies to aquifer data.
848 for (const auto& entry : nncData) {
849 // Ignore most explicit NNCs between otherwise neighbouring cells.
850 // We keep NNCs that involve cells with numerical aquifers even if
851 // these might be between neighbouring cells in the Cartesian
852 // grid--e.g., between cells (I,J,K) and (I+1,J,K). All such
853 // connections should be written to NNC output arrays provided the
854 // transmissibility value is sufficiently large.
855 //
856 // The condition cell2 >= cell1 holds by construction of nncData.
857 assert (entry.cell2 >= entry.cell1);
858
859 if (! isCartesianNeighbour_(level0CartDims, entry.cell1, entry.cell2) ||
860 isNumAquConn_(entry.cell1, entry.cell2))
861 {
862 bool foundNncEdit = false;
863 auto trans = entry.trans;
864 if (! nncEdit.empty()) {
865 auto it = std::lower_bound(nncEdit.begin(), nncEdit.end(),
866 NNCdata {entry.cell1, entry.cell2, 0.0 });
867 if (it != nncEdit.end() && it->cell1 == entry.cell1 && it->cell2 == entry.cell2) {
868 trans *= it->trans;
869 foundNncEdit = true;
870 }
871 }
872 if (! foundNncEdit) {
873 // Pick up transmissibility value from 'globalTrans()' since
874 // multiplier keywords like MULTREGT might have impacted the
875 // values entered in primary sources like NNC/EDITNNC/EDITNNCR.
876 const auto c1 = activeCell_(levelCartToLevelCompressed[/* level */0], entry.cell1);
877 const auto c2 = activeCell_(levelCartToLevelCompressed[/* level */0], entry.cell2);
878
879 if ((c1 < 0) || (c2 < 0)) {
880 // Connection between inactive cells? Unexpected at this
881 // level. Might consider 'throw'ing if this happens...
882 continue;
883 }
884
885 trans = this->globalTrans().transmissibility(c1, c2);
886
887 if (! generatedNnc.empty()) {
888 for (const auto& generated : generatedNnc) {
889 if (entry.cell1 == generated.cell1 && entry.cell2 == generated.cell2) {
890 trans -= generated.trans;
891 break;
892 }
893 }
894 }
895 }
896 const auto tt = unitSystem
897 .from_si(UnitSystem::measure::transmissibility, trans);
898
899 // ECLIPSE ignores NNCs (with EDITNNC/EDITNNCR applied) with
900 // small transmissibility values. Seems like the threshold is
901 // 1.0e-6 in output units.
902 if (std::isnormal(tt) && ! (tt < 1.0e-6)) {
903 inputedNnc.emplace_back(entry.cell1, entry.cell2, trans);
904 }
905 }
906 }
907 // Write first the inputed NNCs and after the internally computed NNCs
908 this->outputNnc_[0].insert(this->outputNnc_[0].begin(), inputedNnc.begin(), inputedNnc.end());
909 return this->outputNnc_;
910}
911
912template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
914doWriteOutput(const int reportStepNum,
915 const std::optional<int> timeStepNum,
916 const bool isSubStep,
917 const bool isForcedFinalOutput,
918 data::Solution&& localCellData,
919 data::Wells&& localWellData,
920 data::GroupAndNetworkValues&& localGroupAndNetworkData,
921 data::Aquifers&& localAquiferData,
922 WellTestState&& localWTestState,
923 const Action::State& actionState,
924 const UDQState& udqState,
925 const SummaryState& summaryState,
926 const std::vector<Scalar>& thresholdPressure,
927 Scalar curTime,
928 Scalar nextStepSize,
929 bool doublePrecision,
930 bool isFlowsn,
931 std::array<FlowsData<double>, 3>&& flowsn,
932 bool isFloresn,
933 std::array<FlowsData<double>, 3>&& floresn)
934{
935 const auto isParallel = this->collectOnIORank_.isParallel();
936 const bool needsReordering = this->collectOnIORank_.doesNeedReordering();
937
938 RestartValue restartValue {
939 (isParallel || needsReordering)
940 ? this->collectOnIORank_.globalCellData()
941 : std::move(localCellData),
942
943 isParallel ? this->collectOnIORank_.globalWellData()
944 : std::move(localWellData),
945
946 isParallel ? this->collectOnIORank_.globalGroupAndNetworkData()
947 : std::move(localGroupAndNetworkData),
948
949 isParallel ? this->collectOnIORank_.globalAquiferData()
950 : std::move(localAquiferData)
951 };
952
953 if (eclState_.getSimulationConfig().useThresholdPressure()) {
954 restartValue.addExtra("THRESHPR", UnitSystem::measure::pressure,
955 thresholdPressure);
956 }
957
958 // Add suggested next timestep to extra data.
959 if (! isSubStep) {
960 restartValue.addExtra("OPMEXTRA", std::vector<double>(1, nextStepSize));
961 }
962
963 // Add nnc flows and flores.
964 if (isFlowsn) {
965 const auto flowsn_global = isParallel ? this->collectOnIORank_.globalFlowsn() : std::move(flowsn);
966 for (const auto& flows : flowsn_global) {
967 if (flows.name.empty())
968 continue;
969 if (flows.name == "FLOGASN+") {
970 restartValue.addExtra(flows.name, UnitSystem::measure::gas_surface_rate, flows.values);
971 } else {
972 restartValue.addExtra(flows.name, UnitSystem::measure::liquid_surface_rate, flows.values);
973 }
974 }
975 }
976 if (isFloresn) {
977 const auto floresn_global = isParallel ? this->collectOnIORank_.globalFloresn() : std::move(floresn);
978 for (const auto& flores : floresn_global) {
979 if (flores.name.empty()) {
980 continue;
981 }
982 restartValue.addExtra(flores.name, UnitSystem::measure::rate, flores.values);
983 }
984 }
985
986 std::vector<Opm::RestartValue> restartValues{};
987 // only serial, only CpGrid (for now)
988 if ( !isParallel && !needsReordering && (this->eclState_.getLgrs().size()>0) && (this->grid_.maxLevel()>0) ) {
989 // Level cells that appear on the leaf grid view get the data::Solution values from there.
990 // Other cells (i.e., parent cells that vanished due to refinement) get rubbish values for now.
991 // Only data::Solution is restricted to the level grids. Well, GroupAndNetwork, Aquifer are
992 // not modified in this method.
993 Opm::Lgr::extractRestartValueLevelGrids<Grid>(this->grid_, restartValue, restartValues);
994 }
995 else {
996 restartValues.reserve(1); // minimum size
997 restartValues.push_back(std::move(restartValue)); // no LGRs-> only one restart value
998 }
999
1000 // make sure that the previous I/O request has been completed
1001 // and the number of incomplete tasklets does not increase between
1002 // time steps
1003 this->taskletRunner_->barrier();
1004
1005 // check if there might have been a failure in the TaskletRunner
1006 if (this->taskletRunner_->failure()) {
1007 throw std::runtime_error("Failure in the TaskletRunner while writing output.");
1008 }
1009
1010 // create a tasklet to write the data for the current time step to disk
1011 auto eclWriteTasklet = std::make_shared<EclWriteTasklet>(
1012 actionState,
1013 isParallel ? this->collectOnIORank_.globalWellTestState() : std::move(localWTestState),
1014 summaryState, udqState, *this->eclIO_,
1015 reportStepNum, timeStepNum, isSubStep, curTime, std::move(restartValues), doublePrecision,
1016 isForcedFinalOutput);
1017
1018 // finally, start a new output writing job
1019 this->taskletRunner_->dispatch(std::move(eclWriteTasklet));
1020}
1021
1022template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
1024evalSummary(const int reportStepNum,
1025 const Scalar curTime,
1026 const data::Wells& localWellData,
1027 const data::WellBlockAveragePressures& localWBPData,
1028 const data::GroupAndNetworkValues& localGroupAndNetworkData,
1029 const std::map<int,data::AquiferData>& localAquiferData,
1030 const std::map<std::pair<std::string, int>, double>& blockData,
1031 const std::map<std::tuple<std::string, int, int>, double>& lgrBlockData,
1032 const std::map<std::string, double>& miscSummaryData,
1033 const std::map<std::string, std::vector<double>>& regionData,
1034 const data::RegionVariableMapping& regVarMap,
1035 const RegionVariableCollection& regVars,
1036 const Inplace& inplace,
1037 const Inplace* initialInPlace,
1038 const InterRegFlowMap& interRegFlows,
1039 SummaryState& summaryState,
1040 UDQState& udqState,
1041 const data::ReservoirCouplingGroupRates* rcGroupRates)
1042{
1043 if (collectOnIORank_.isIORank()) {
1044 const auto& wellData = this->collectOnIORank_.isParallel()
1045 ? this->collectOnIORank_.globalWellData()
1046 : localWellData;
1047
1048 const auto& wbpData = this->collectOnIORank_.isParallel()
1049 ? this->collectOnIORank_.globalWBPData()
1050 : localWBPData;
1051
1052 const auto& groupAndNetworkData = this->collectOnIORank_.isParallel()
1053 ? this->collectOnIORank_.globalGroupAndNetworkData()
1054 : localGroupAndNetworkData;
1055
1056 const auto& aquiferData = this->collectOnIORank_.isParallel()
1057 ? this->collectOnIORank_.globalAquiferData()
1058 : localAquiferData;
1059
1060 const auto interreg_flows = getInterRegFlowsAsMap(interRegFlows);
1061
1062 const auto values = out::Summary::DynamicSimulatorState {
1063 .well_solution = &wellData,
1064 .wbp = &wbpData,
1065 .group_and_nwrk_solution = &groupAndNetworkData,
1066 .single_values = &miscSummaryData,
1067 .region_values = &regionData,
1068 .reg_var_map = &regVarMap,
1069 .reg_var_coll = &regVars,
1070 .block_values = &blockData,
1071 .aquifer_values = &aquiferData,
1072 .interreg_flows = &interreg_flows,
1073 .rc_group_rates = rcGroupRates,
1074 .inplace = {
1075 .current = &inplace,
1076 .initial = initialInPlace
1077 },
1078 .lgr_block_values = &lgrBlockData
1079 };
1080
1081 this->eclIO_->summary()
1082 .eval(reportStepNum, curTime, values, summaryState);
1083
1084 // Off-by-one-fun: The reportStepNum argument corresponds to the
1085 // report step these results will be written to, whereas the
1086 // argument to UDQ function evaluation corresponds to the report
1087 // step we are currently on.
1088 const auto udq_step = reportStepNum - 1;
1089
1090 this->schedule_[udq_step].udq()
1091 .eval(udq_step,
1092 this->schedule_.wellMatcher(udq_step),
1093 this->schedule_[udq_step].group_order(),
1094 this->schedule_.segmentMatcherFactory(udq_step),
1095 [es = std::cref(this->eclState_)]() {
1096 return std::make_unique<RegionSetMatcher>
1097 (es.get().fipRegionStatistics());
1098 },
1099 summaryState, udqState);
1100 }
1101
1102#if HAVE_MPI
1103 if (collectOnIORank_.isParallel()) {
1104 Parallel::MpiSerializer ser(grid_.comm());
1105 ser.append(summaryState);
1106 }
1107#endif
1108}
1109
1110template<class Grid, class EquilGrid, class GridView, class ElementMapper, class Scalar>
1113globalTrans() const
1114{
1115 assert (globalTrans_);
1116 return *globalTrans_;
1117}
1118
1119} // namespace Opm
1120
1121#endif // OPM_ECL_GENERIC_WRITER_IMPL_HPP
Definition: CollectDataOnIORank.hpp:50
bool isIORank() const
Definition: CollectDataOnIORank.hpp:131
Definition: EclGenericWriter.hpp:74
std::vector< std::vector< NNCdata > > outputNnc_
Definition: EclGenericWriter.hpp:186
const EclipseState & eclState_
Definition: EclGenericWriter.hpp:170
void evalSummary(int reportStepNum, Scalar curTime, const data::Wells &localWellData, const data::WellBlockAveragePressures &localWBPData, const data::GroupAndNetworkValues &localGroupAndNetworkData, const std::map< int, data::AquiferData > &localAquiferData, const std::map< std::pair< std::string, int >, double > &blockData, const std::map< std::tuple< std::string, int, int >, double > &lgrBlockData, const std::map< std::string, double > &miscSummaryData, const std::map< std::string, std::vector< double > > &regionData, const data::RegionVariableMapping &regVarMap, const RegionVariableCollection &regVars, const Inplace &inplace, const Inplace *initialInPlace, const InterRegFlowMap &interRegFlows, SummaryState &summaryState, UDQState &udqState, const data::ReservoirCouplingGroupRates *rcGroupRates=nullptr)
Definition: EclGenericWriter_impl.hpp:1024
CollectDataOnIORankType collectOnIORank_
Definition: EclGenericWriter.hpp:166
void extractOutputTransAndNNC(const std::function< unsigned int(unsigned int)> &map)
Definition: EclGenericWriter_impl.hpp:306
std::unique_ptr< TaskletRunner > taskletRunner_
Definition: EclGenericWriter.hpp:172
void writeInit()
Definition: EclGenericWriter_impl.hpp:278
EclGenericWriter(const Schedule &schedule, const EclipseState &eclState, const SummaryConfig &summaryConfig, const Grid &grid, const EquilGrid *equilGrid, const GridView &gridView, const Dune::CartesianIndexMapper< Grid > &cartMapper, const Dune::CartesianIndexMapper< EquilGrid > *equilCartMapper, bool enableAsyncOutput, bool enableEsmry)
Definition: EclGenericWriter_impl.hpp:224
const EclipseIO & eclIO() const
Definition: EclGenericWriter_impl.hpp:270
const TransmissibilityType & globalTrans() const
Definition: EclGenericWriter_impl.hpp:1113
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< Scalar > &thresholdPressure, Scalar curTime, Scalar nextStepSize, bool doublePrecision, bool isFlowsn, std::array< FlowsData< double >, 3 > &&flowsn, bool isFloresn, std::array< FlowsData< double >, 3 > &&floresn)
Definition: EclGenericWriter_impl.hpp:914
Inter-region flow accumulation maps for all region definition arrays.
Definition: InterRegFlows.hpp:179
std::vector< data::InterRegFlowMap > getInterRegFlows() const
const std::vector< std::string > & names() const
Definition: EclGenericWriter.hpp:52
Class for serializing and broadcasting data using MPI.
Definition: MPISerializer.hpp:38
void append(T &data, int root=0)
Serialize and broadcast on root process, de-serialize and append on others.
Definition: MPISerializer.hpp:82
void broadcast(RootRank rootrank, Args &&... args)
Definition: MPISerializer.hpp:47
The base class for tasklets.
Definition: tasklets.hpp:45
virtual void run()=0
Handles where a given tasklet is run.
Definition: tasklets.hpp:93
Definition: Transmissibility.hpp:54
Definition: blackoilbioeffectsmodules.hh:45
Avoid mistakes in calls to broadcast() by wrapping the root argument in an explicit type.
Definition: MPISerializer.hpp:33