OutputCompositionalModule.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*/
27#ifndef OPM_OUTPUT_COMPOSITIONAL_MODULE_HPP
28#define OPM_OUTPUT_COMPOSITIONAL_MODULE_HPP
29
30#include <dune/grid/common/gridenums.hh>
31
33
34#include <opm/common/Exceptions.hpp>
35#include <opm/common/ErrorMacros.hpp>
36#include <opm/common/TimingMacros.hpp>
37#include <opm/common/OpmLog/OpmLog.hpp>
38
39#include <opm/input/eclipse/EclipseState/Compositional/CompositionalConfig.hpp>
40#include <opm/input/eclipse/EclipseState/SummaryConfig/SummaryConfig.hpp>
41
42#include <opm/material/common/Valgrind.hpp>
43
49
54
55#include <algorithm>
56#include <array>
57#include <cstddef>
58#include <fstream>
59#include <memory>
60#include <sstream>
61#include <stdexcept>
62#include <string>
63#include <type_traits>
64#include <utility>
65#include <vector>
66
67#include <fmt/format.h>
68
69namespace Opm {
70
71// forward declaration
72template <class TypeTag>
73class EcfvDiscretization;
74
81template <class TypeTag>
82class OutputCompositionalModule : public GenericOutputModule<GetPropType<TypeTag, Properties::FluidSystem>>
83{
91 using RestartOutput = typename CompositionalContainer<FluidSystem>::RestartOutput;
94
95 enum { numPhases = FluidSystem::numPhases };
96 enum { numComponents = FluidSystem::numComponents };
97 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
98 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
99 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
100
101public:
102 static void registerParameters()
103 {
106 }
107
108 template <class CollectDataToIORankType>
109 OutputCompositionalModule(const Simulator& simulator,
110 const SummaryConfig& smryCfg,
111 const CollectDataToIORankType& collectToIORank)
112 : BaseType(simulator.vanguard().eclState(),
113 simulator.vanguard().schedule(),
114 smryCfg,
115 simulator.vanguard().summaryState(),
117 [this](const int idx)
118 { return simulator_.problem().eclWriter().collectOnIORank().localIdxToGlobalIdx(idx); },
119 [&collectToIORank](const int idx)
120 { return collectToIORank.isCartIdxOnThisRank(idx); },
121 simulator.vanguard().grid().comm(),
122 getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal,
123 getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::ConstantTemperature,
124 getPropValue<TypeTag, Properties::EnableMech>(),
125 getPropValue<TypeTag, Properties::EnableSolvent>(),
126 getPropValue<TypeTag, Properties::EnablePolymer>(),
127 getPropValue<TypeTag, Properties::EnableFoam>(),
128 getPropValue<TypeTag, Properties::EnableBrine>(),
129 getPropValue<TypeTag, Properties::EnableSaltPrecipitation>(),
130 getPropValue<TypeTag, Properties::EnableExtbo>(),
131 getPropValue<TypeTag, Properties::EnableBioeffects>(),
132 getPropValue<TypeTag, Properties::EnableGeochemistry>())
133 , simulator_(simulator)
134 , eosType_(simulator.vanguard().eclState().compositionalConfig().eosType(0))
135 {
136 for (auto& region_pair : this->regions_) {
137 this->createLocalRegion_(region_pair.second);
138 }
139
140 auto isCartIdxOnThisRank = [&collectToIORank](const int idx) {
141 return collectToIORank.isCartIdxOnThisRank(idx);
142 };
143
144 this->setupBlockData(isCartIdxOnThisRank);
145
146 // Compositional runs currently fill block data on the global grid only.
147 // The empty level map intentionally leaves LB* values unallocated.
148 if (! collectToIORank.isParallel()) {
149 this->setupLgrBlockData({}, [](const int, const int) { return true; });
150 }
151
152 if (! Parameters::Get<Parameters::OwnerCellsFirst>()) {
153 const std::string msg = "The output code does not support --owner-cells-first=false.";
154 if (collectToIORank.isIORank()) {
155 OpmLog::error(msg);
156 }
157 OPM_THROW_NOLOG(std::runtime_error, msg);
158 }
159
160 if (smryCfg.match("[FB]PP[OGW]") || smryCfg.match("RPP[OGW]*")) {
161 auto rset = this->eclState_.fieldProps().fip_regions();
162 rset.push_back("PVTNUM");
163
164 // Note: We explicitly use decltype(auto) here because the
165 // default scheme (-> auto) will deduce an undesirable type. We
166 // need the "reference to vector" semantics in this instance.
168 .emplace(this->simulator_.gridView().comm(),
169 FluidSystem::numPhases, rset,
170 [fp = std::cref(this->eclState_.fieldProps())]
171 (const std::string& rsetName) -> decltype(auto)
172 { return fp.get().get_int(rsetName); });
173 }
174 }
175
180 void
181 allocBuffers(const unsigned bufferSize,
182 const unsigned reportStepNum,
183 const bool substep,
184 const bool log,
185 const bool forceRestartFieldAllocation)
186 {
187 if (! std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
188 return;
189 }
190
191 auto rstKeywords = this->schedule_.rst_keywords(reportStepNum);
192 const bool isRestartOutput = forceRestartFieldAllocation ||
193 (!substep && this->schedule_.write_rst_file(reportStepNum));
194 const auto restartOutput = isRestartOutput
195 ? RestartOutput::Enabled
196 : RestartOutput::Disabled;
197 this->compC_.allocate(bufferSize, rstKeywords, restartOutput);
198 this->numUnresolvedSaturationPressures_ = 0;
199
200 this->doAllocBuffers(bufferSize, reportStepNum, substep, log,
201 forceRestartFieldAllocation,
202 /* hysteresisConfig = */ nullptr,
203 /* numOutputNnc =*/ 0,
204 std::move(rstKeywords));
205 }
206
207 void assignToSolution(data::Solution& sol) override
208 {
209 this->compC_.outputRestart(sol, this->saturation_[oilPhaseIdx]);
211
212 // Use the compositional restart names for phase densities and viscosities.
214 .oilDensity = "DENO",
215 .gasDensity = "DENG",
216 .waterDensity = "DENW",
217 .oilViscosity = "VOIL",
218 .gasViscosity = "VGAS",
219 .waterViscosity = "VWAT",
220 });
221
222 // Report compositional relative permeabilities under their restart names.
223 using M = UnitSystem::measure;
224 this->assignBuffer(sol, "KRO", M::identity,
225 relativePermeability_[oilPhaseIdx], oilPhaseIdx);
226 this->assignBuffer(sol, "KRG", M::identity,
227 relativePermeability_[gasPhaseIdx], gasPhaseIdx);
228 if constexpr (numPhases > 2) {
229 this->assignBuffer(sol, "KRW", M::identity,
230 relativePermeability_[waterPhaseIdx], waterPhaseIdx);
231 }
232 }
233
234 void outputFipAndResvLog(const Inplace& inplace,
235 const std::size_t reportStepNum,
236 double elapsed,
237 boost::posix_time::ptime currentDate,
238 const bool substep,
239 const Parallel::Communication& comm)
240 {
241 if (comm.rank() != 0) {
242 return;
243 }
244
245 std::unique_ptr<FIPConfig> fipSched;
246 if (reportStepNum > 0) {
247 const auto& rpt = this->schedule_[reportStepNum - 1].rpt_config.get();
248 fipSched = std::make_unique<FIPConfig>(rpt);
249 }
250
251 const FIPConfig& fipc = reportStepNum == 0
252 ? this->eclState_.getEclipseConfig().fip()
253 : *fipSched;
254
255 if (!substep && !this->forceDisableFipOutput_ && fipc.output(FIPConfig::OutputField::FIELD)) {
256 this->logOutput_.timeStamp("BALANCE", elapsed, reportStepNum, currentDate);
257
258 const auto& initial_inplace = *this->initialInplace();
259 this->logOutput_.fip(inplace, initial_inplace, "");
260
261 if (fipc.output(FIPConfig::OutputField::FIPNUM)) {
262 this->logOutput_.fip(inplace, initial_inplace, "FIPNUM");
263
264 if (fipc.output(FIPConfig::OutputField::RESV)) {
265 this->logOutput_.fipResv(inplace, "FIPNUM");
266 }
267 }
268
269 if (fipc.output(FIPConfig::OutputField::FIP)) {
270 for (const auto& reg : this->regions_) {
271 if (reg.first != "FIPNUM") {
272 std::ostringstream ss;
273 ss << "BAL" << reg.first.substr(3);
274 this->logOutput_.timeStamp(ss.str(), elapsed, reportStepNum, currentDate);
275 this->logOutput_.fip(inplace, initial_inplace, reg.first);
276
277 if (fipc.output(FIPConfig::OutputField::RESV)) {
278 this->logOutput_.fipResv(inplace, reg.first);
279 }
280 }
281 }
282 }
283 }
284 }
285
286 void outputFipAndResvLogToCSV(const std::size_t reportStepNum,
287 const bool substep,
288 const Parallel::Communication& comm)
289 {
290 if (comm.rank() != 0) {
291 return;
292 }
293
294 if ((reportStepNum == 0) && (!substep) &&
295 (this->schedule_.initialReportConfiguration().has_value()) &&
296 (this->schedule_.initialReportConfiguration()->contains("CSVFIP"))) {
297
298 std::ostringstream csv_stream;
299
300 this->logOutput_.csv_header(csv_stream);
301
302 const auto& initial_inplace = *this->initialInplace();
303
304 this->logOutput_.fip_csv(csv_stream, initial_inplace, "FIPNUM");
305
306 for (const auto& reg : this->regions_) {
307 if (reg.first != "FIPNUM") {
308 this->logOutput_.fip_csv(csv_stream, initial_inplace, reg.first);
309 }
310 }
311
312 const IOConfig& io = this->eclState_.getIOConfig();
313 auto csv_fname = io.getOutputDir() + "/" + io.getBaseName() + ".CSV";
314
315 std::ofstream outputFile(csv_fname);
316 outputFile << csv_stream.str();
317 }
318 }
319
321 void setupExtractors(const bool /*isSubStep*/,
322 const std::size_t /*reportStepNum*/)
323 {
324 using Entry = typename Extractor::Entry;
325 using ExtractContext = typename Extractor::Context;
326 using ScalarEntry = typename Extractor::ScalarEntry;
327 using PhaseEntry = typename Extractor::PhaseEntry;
328
329 auto extractors = std::array{
330 Entry{PhaseEntry{&this->saturation_,
331 [](const unsigned phase, const ExtractContext& ectx)
332 { return ectx.intQuants.saturationForOutput(phase); }}
333 },
334 Entry{ScalarEntry{&this->fluidPressure_,
335 [](const ExtractContext& ectx)
336 {
337 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
338 // Output oil pressure as default
339 return getValue(ectx.fs.pressure(oilPhaseIdx));
340 }
341 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
342 // Output gas if oil is not present
343 return getValue(ectx.fs.pressure(gasPhaseIdx));
344 }
345 else {
346 // Output water if neither oil nor gas is present
347 return getValue(ectx.fs.pressure(waterPhaseIdx));
348 }
349 }}
350 },
351 Entry{ScalarEntry{&this->temperature_,
352 [](const ExtractContext& ectx)
353 { return getValue(ectx.fs.temperature(oilPhaseIdx)); }}
354 },
355 Entry{[&compC = this->compC_](const ExtractContext& ectx)
356 {
357 compC.assignMoleFractions(ectx.globalDofIdx,
358 [&ectx](const unsigned compIdx)
359 {
360 return ectx.intQuants.hasHydrocarbon()
361 ? getValue(ectx.fs.moleFraction(compIdx))
362 : Scalar{0};
363 });
364 }, this->compC_.moleFractionsAllocated()
365 },
366
367 // Use presence before regularization so the numerical hydrocarbon
368 // floor does not create reported phase properties in a water-only cell.
369 Entry{[&compC = this->compC_](const ExtractContext& ectx)
370 {
371 const bool hasGas =
372 ectx.intQuants.phaseIsPresent(gasPhaseIdx);
373 compC.assignGasFractions(ectx.globalDofIdx,
374 [&fs = ectx.fs, hasGas](const unsigned compIdx)
375 {
376 return hasGas
377 ? getValue(fs.moleFraction(gasPhaseIdx, compIdx))
378 : Scalar{0};
379 });
380 }, FluidSystem::phaseIsActive(gasPhaseIdx) &&
381 this->compC_.gasFractionsAllocated()
382 },
383 Entry{[&compC = this->compC_](const ExtractContext& ectx)
384 {
385 const bool hasOil =
386 ectx.intQuants.phaseIsPresent(oilPhaseIdx);
387 compC.assignOilFractions(ectx.globalDofIdx,
388 [&fs = ectx.fs, hasOil](const unsigned compIdx)
389 {
390 return hasOil
391 ? getValue(fs.moleFraction(oilPhaseIdx, compIdx))
392 : Scalar{0};
393 });
394 }, FluidSystem::phaseIsActive(oilPhaseIdx) &&
395 this->compC_.oilFractionsAllocated()
396 },
397 Entry{[&compC = this->compC_](const ExtractContext& ectx)
398 {
399 compC.assignPhasePressures(ectx.globalDofIdx,
400 getValue(ectx.fs.pressure(oilPhaseIdx)),
401 getValue(ectx.fs.pressure(gasPhaseIdx)));
402 }, this->compC_.phasePressuresAllocated()
403 },
404 // The reference convention for a water-only cell is VMF = 1.
405 // Otherwise use the hydrocarbon flash's vapour mole fraction.
406 Entry{[&compC = this->compC_](const ExtractContext& ectx)
407 {
408 const Scalar liquidFraction = getValue(ectx.fs.L());
409 compC.assignVaporFraction(ectx.globalDofIdx,
410 ectx.intQuants.hasHydrocarbon()
411 ? std::clamp(Scalar{1} - liquidFraction,
412 Scalar{0}, Scalar{1})
413 : Scalar{1});
414 }, this->compC_.vaporFractionAllocated()
415 },
416 // The phase densities and viscosities, reported where the phase is present.
417 Entry{PhaseEntry{&this->relativePermeability_,
418 [](const unsigned phaseIdx, const ExtractContext& ectx)
419 {
420 return ectx.intQuants.phaseIsPresent(phaseIdx)
421 ? getValue(ectx.intQuants.relativePermeability(phaseIdx))
422 : Scalar{0};
423 }}
424 },
425 Entry{PhaseEntry{&this->density_,
426 [](const unsigned phaseIdx, const ExtractContext& ectx)
427 {
428 return ectx.intQuants.phaseIsPresent(phaseIdx)
429 ? getValue(ectx.fs.density(phaseIdx))
430 : Scalar{0};
431 }}
432 },
433 Entry{PhaseEntry{&this->viscosity_,
434 [](const unsigned phaseIdx, const ExtractContext& ectx)
435 {
436 return ectx.intQuants.phaseIsPresent(phaseIdx)
437 ? getValue(ectx.fs.viscosity(phaseIdx))
438 : Scalar{0};
439 }}
440 },
441 };
442
443 this->extractors_ = Extractor::removeInactive(extractors);
444
445 this->setupBlockExtractors_();
446 }
447
457 {
458 using Entry = typename BlockExtractor::Entry;
459 using Context = typename BlockExtractor::Context;
460 using ScalarEntry = typename BlockExtractor::ScalarEntry;
461
462 using namespace std::string_view_literals;
463
464 const auto densityIfPresent = [](const unsigned phaseIdx) {
465 return [phaseIdx](const Context& ectx) -> Scalar {
466 if (!ectx.intQuants.phaseIsPresent(phaseIdx)) {
467 return Scalar{0};
468 }
469
470 return getValue(ectx.fs.density(phaseIdx));
471 };
472 };
473
474 const auto viscosityIfPresent = [](const unsigned phaseIdx) {
475 return [phaseIdx](const Context& ectx) -> Scalar {
476 if (!ectx.intQuants.phaseIsPresent(phaseIdx)) {
477 return Scalar{0};
478 }
479
480 return getValue(ectx.fs.viscosity(phaseIdx));
481 };
482 };
483
484 const auto reservoirPoreVolume = [&model = this->simulator_.model()]
485 (const Context& ectx) -> Scalar
486 {
487 return getValue(ectx.intQuants.porosity()) *
488 model.dofTotalVolume(ectx.globalDofIdx);
489 };
490
491 const auto phasePoreVolume = [reservoirPoreVolume](const unsigned phaseIdx) {
492 return [phaseIdx, reservoirPoreVolume](const Context& ectx) -> Scalar {
493 return ectx.intQuants.saturationForOutput(phaseIdx) * reservoirPoreVolume(ectx);
494 };
495 };
496
497 const auto handlers = std::array{
498 Entry{ScalarEntry{std::vector{"BPR"sv, "BPRESSUR"sv},
499 [](const Context& ectx)
500 {
501 return FluidSystem::phaseIsActive(oilPhaseIdx)
502 ? getValue(ectx.fs.pressure(oilPhaseIdx))
503 : getValue(ectx.fs.pressure(gasPhaseIdx));
504 }
505 }
506 },
507 Entry{ScalarEntry{"BGPR"sv,
508 [](const Context& ectx)
509 { return getValue(ectx.fs.pressure(gasPhaseIdx)); }
510 }
511 },
512 Entry{ScalarEntry{"BWPR"sv,
513 [](const Context& ectx)
514 {
515 return FluidSystem::phaseIsActive(waterPhaseIdx)
516 ? getValue(ectx.fs.pressure(waterPhaseIdx))
517 : Scalar{0};
518 }
519 }
520 },
521 Entry{ScalarEntry{"BRPV"sv, reservoirPoreVolume}},
522 Entry{ScalarEntry{"BWPV"sv, phasePoreVolume(waterPhaseIdx)}},
523 Entry{ScalarEntry{"BOPV"sv, phasePoreVolume(oilPhaseIdx)}},
524 Entry{ScalarEntry{"BGPV"sv, phasePoreVolume(gasPhaseIdx)}},
525 Entry{ScalarEntry{std::vector{"BSOIL"sv, "BOSAT"sv},
526 [](const Context& ectx)
527 { return ectx.intQuants.saturationForOutput(oilPhaseIdx); }
528 }
529 },
530 Entry{ScalarEntry{std::vector{"BSGAS"sv, "BGSAT"sv},
531 [](const Context& ectx)
532 { return ectx.intQuants.saturationForOutput(gasPhaseIdx); }
533 }
534 },
535 Entry{ScalarEntry{std::vector{"BSWAT"sv, "BWSAT"sv},
536 [](const Context& ectx)
537 {
538 return ectx.intQuants.saturationForOutput(waterPhaseIdx);
539 }
540 }
541 },
542 Entry{ScalarEntry{std::vector{"BDENO"sv, "BODEN"sv},
543 densityIfPresent(oilPhaseIdx)
544 }
545 },
546 Entry{ScalarEntry{std::vector{"BDENG"sv, "BGDEN"sv},
547 densityIfPresent(gasPhaseIdx)
548 }
549 },
550 Entry{ScalarEntry{std::vector{"BDENW"sv, "BWDEN"sv},
551 densityIfPresent(waterPhaseIdx)
552 }
553 },
554 Entry{ScalarEntry{std::vector{"BVOIL"sv, "BOVIS"sv},
555 viscosityIfPresent(oilPhaseIdx)
556 }
557 },
558 Entry{ScalarEntry{std::vector{"BVGAS"sv, "BGVIS"sv},
559 viscosityIfPresent(gasPhaseIdx)
560 }
561 },
562 Entry{ScalarEntry{std::vector{"BVWAT"sv, "BWVIS"sv},
563 viscosityIfPresent(waterPhaseIdx)
564 }
565 },
566 Entry{ScalarEntry{std::vector{"BTEMP"sv, "BTCNFHEA"sv},
567 [](const Context& ectx)
568 {
569 return FluidSystem::phaseIsActive(oilPhaseIdx)
570 ? getValue(ectx.fs.temperature(oilPhaseIdx))
571 : getValue(ectx.fs.temperature(gasPhaseIdx));
572 }
573 }
574 },
575 };
576
577 this->blockExtractors_ = BlockExtractor::setupExecMap(this->blockData_, handlers);
578 }
579
582 {
583 this->extractors_.clear();
584 this->blockExtractors_.clear();
585 }
586
591 void processElement(const ElementContext& elemCtx)
592 {
593 OPM_TIMEBLOCK_LOCAL(processElement, Subsystem::Output);
594 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
595 return;
596 }
597
598 typename Extractor::HysteresisParams hysterParams{};
599 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
600 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
601 const auto& fs = intQuants.fluidState();
602
603 const typename Extractor::Context ectx{
604 elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0),
605 0, // elemCtx.primaryVars(dofIdx, /*timeIdx=*/0).pvtRegionIndex(),
606 elemCtx.simulator().episodeIndex(),
607 fs,
608 intQuants,
609 hysterParams
610 };
611
612 Extractor::process(ectx, extractors_);
613 }
614 }
615
616 void processElementFlows(const ElementContext& /* elemCtx */)
617 {
618 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
619 if (!std::is_same_v<Discretization, EcfvDiscretization<TypeTag>>)
620 return;
621 }
622
623 void processElementBlockData(const ElementContext& elemCtx)
624 {
625 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
626 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value)
627 return;
628
629 if (this->blockExtractors_.empty() || elemCtx.element().level() != 0) {
630 return;
631 }
632
633 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
634 const auto globalDofIdx = elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0);
635 const auto cartesianIdx = elemCtx.simulator().vanguard().cartesianIndex(globalDofIdx);
636
637 const auto be_it = this->blockExtractors_.find(cartesianIdx);
638 if (be_it == this->blockExtractors_.end()) {
639 continue;
640 }
641
642 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
643
644 const typename BlockExtractor::Context ectx{
645 globalDofIdx,
646 dofIdx,
647 intQuants.fluidState(),
648 intQuants,
649 elemCtx,
650 };
651
652 BlockExtractor::process(be_it->second, ectx);
653 }
654 }
655
684 template <class ActiveIndex, class CartesianIndex>
685 void processFluxes(const ElementContext& /* elemCtx */,
686 ActiveIndex&& /* activeIndex*/,
687 CartesianIndex&& /* cartesianIndex */)
688 {
689 }
690
696 {
697 // Inter-region flow rates. Note: ".clear()" prepares to accumulate
698 // contributions per bulk connection between FIP regions.
699 this->interRegionFlows_.clear();
700 }
701
706 {
708 }
709
714 {
715 return this->interRegionFlows_;
716 }
717
718 void updateFluidInPlace(const unsigned globalDofIdx,
719 const IntensiveQuantities& intQuants,
720 const double totVolume)
721 {
722 // Pressure and pore-volume summaries do not require a surface-condition
723 // flash. Phase in-place volumes do, so those remain unavailable here.
724 const auto referencePorosity =
725 this->simulator_.problem().referencePorosity(globalDofIdx, /*timeIdx=*/0);
726 this->updateTotalVolumesAndPressures_(globalDofIdx,
727 intQuants,
728 totVolume,
729 referencePorosity);
730
731 // Run the nonlinear PSAT solve in the caller's OpenMP loop. The
732 // assignment is a no-op unless a PSAT restart buffer is allocated.
733 this->assignSaturationPressure_(globalDofIdx, intQuants);
734 }
735
738 void validateLocalData() override
739 {
740 if (this->compC_.saturationPressureRequested()) {
741 const auto& comm = this->simulator_.gridView().comm();
742 const auto totalUnresolved = comm.sum(this->numUnresolvedSaturationPressures_);
743 if (totalUnresolved > 0 && comm.rank() == 0) {
744 const std::string_view cell = totalUnresolved == 1 ? "cell" : "cells";
745 // Some unresolved searches correspond to states with no saturation
746 // boundary at this composition and temperature, so report the aggregate
747 // informationally rather than treating every result as a solver failure.
748 OpmLog::info(fmt::format("No saturation pressure was resolved in {} {}; "
749 "PSAT is written as zero there. This includes "
750 "mixtures that have none.",
751 totalUnresolved,
752 cell));
753 }
754 }
755 this->numUnresolvedSaturationPressures_ = 0;
757 }
758
759protected:
761 void allocFormulationBuffers(std::map<std::string, int>& rstKeywords,
762 const unsigned bufferSize) override
763 {
764 // Name each phase explicitly: the compositional phase ordering is not
765 // the one the black-oil keyword names assume.
766 const auto named = std::array{
767 std::pair{static_cast<unsigned>(oilPhaseIdx), std::string_view{"KRO"}},
768 std::pair{static_cast<unsigned>(gasPhaseIdx), std::string_view{"KRG"}},
769 std::pair{static_cast<unsigned>(waterPhaseIdx), std::string_view{"KRW"}},
770 };
771 for (const auto& [phase, kw] : named) {
772 if (phase >= numPhases || !FluidSystem::phaseIsActive(phase)) {
773 continue;
774 }
775 BaseType::allocBufferIfRequested(rstKeywords, bufferSize,
776 relativePermeability_[phase], kw, true);
777 }
778 }
779
780private:
781 using ScalarBuffer = typename BaseType::ScalarBuffer;
782 std::array<ScalarBuffer, numPhases> relativePermeability_;
783
784 bool isDefunctParallelWell(const std::string& wname) const override
785 {
786 if (simulator_.gridView().comm().size() == 1)
787 return false;
788 const auto& parallelWells = simulator_.vanguard().parallelWells();
789 std::pair<std::string, bool> value {wname, true};
790 auto candidate = std::lower_bound(parallelWells.begin(), parallelWells.end(), value);
791 return candidate == parallelWells.end() || *candidate != value;
792 }
793
794 bool isOwnedByCurrentRank(const std::string& wname) const override
795 {
796 // Note: This statement is not correct for distributed wells and
797 // will need additional logic once those are supported for
798 // compositional flows.
799 return ! this->isDefunctParallelWell(wname);
800 }
801
802 bool isOnCurrentRank(const std::string& wname) const override
803 {
804 // Note: This statement is not correct for distributed wells and
805 // will need additional logic once those are supported for
806 // compositional flows.
807 return ! this->isDefunctParallelWell(wname);
808 }
809
810 void createLocalRegion_(std::vector<int>& region)
811 {
812 std::size_t elemIdx = 0;
813 for (const auto& elem : elements(simulator_.gridView())) {
814 if (elem.partitionType() != Dune::InteriorEntity) {
815 region[elemIdx] = 0;
816 }
817
818 ++elemIdx;
819 }
820 }
821
825 void assignSaturationPressure_(const unsigned globalDofIdx,
826 const IntensiveQuantities& intQuants)
827 {
828 if (!this->compC_.saturationPressureAllocated()) {
829 return;
830 }
831
832 const auto& fluidState = intQuants.fluidState();
833 if (!intQuants.hasHydrocarbon()) {
834 this->compC_.assignSaturationPressure(globalDofIdx,
835 getValue(fluidState.pressure(oilPhaseIdx)));
836 return;
837 }
838
839 std::array<Scalar, numComponents> moleFractions{};
840 for (int c = 0; c < numComponents; ++c) {
841 moleFractions[c] = getValue(fluidState.moleFraction(c));
842 }
844 getValue(fluidState.L()),
845 getValue(fluidState.pressure(oilPhaseIdx)),
846 moleFractions,
847 getValue(fluidState.temperature(oilPhaseIdx)),
848 this->eosType_);
849 if (!psat) {
850 // A false result means only that no saturation pressure was resolved;
851 // it does not distinguish a physically absent boundary from numerical
852 // nonconvergence.
853#ifdef _OPENMP
854#pragma omp atomic
855#endif
856 ++this->numUnresolvedSaturationPressures_;
857 }
858
859 this->compC_.assignSaturationPressure(globalDofIdx, psat.value_or(Scalar{0}));
860 }
861
862 const Simulator& simulator_;
863 CompositionalContainer<FluidSystem> compC_;
864 CompositionalConfig::EOSType eosType_;
865 std::size_t numUnresolvedSaturationPressures_{};
866 std::vector<typename Extractor::Entry> extractors_;
867 typename BlockExtractor::ExecMap blockExtractors_;
868};
869
870} // namespace Opm
871
872#endif // OPM_OUTPUT_COMPOSITIONAL_MODULE_HPP
Restart-output buffers specific to compositional simulations.
Common output functionality shared by simulator formulations.
Declares the properties required by the black oil model.
RestartOutput
Definition: CompositionalContainer.hpp:56
static std::optional< Scalar > cellSaturationPressure(const Scalar liquidFraction, const Scalar oilPressure, const std::array< Scalar, numComponents > &moleFractions, const Scalar temperature, const CompositionalConfig::EOSType eosType)
The base class for the element-centered finite-volume discretization scheme.
Definition: ecfvdiscretization.hh:160
Definition: GenericOutputModule.hpp:91
const Schedule & schedule_
Definition: GenericOutputModule.hpp:536
bool forceDisableFipOutput_
Definition: GenericOutputModule.hpp:557
virtual void validateLocalData()
Definition: GenericOutputModule.hpp:291
void doAllocBuffers(unsigned bufferSize, unsigned reportStepNum, const bool substep, const bool log, const bool forceRestartFieldAllocation, const EclHysteresisConfig *hysteresisConfig, unsigned numOutputNnc=0, std::map< std::string, int > rstKeywords={})
void setupBlockData(std::function< bool(int)> isCartIdxOnThisRank)
ScalarBuffer temperature_
Definition: GenericOutputModule.hpp:591
std::optional< RegionPhasePoreVolAverage > regionAvgDensity_
Definition: GenericOutputModule.hpp:660
std::array< ScalarBuffer, numPhases > viscosity_
Definition: GenericOutputModule.hpp:632
void setupLgrBlockData(const std::map< std::string, int > &lgrNameToLevel, const std::function< bool(int, int)> &isLgrCellOnThisRank)
void assignBuffer(data::Solution &sol, std::string_view name, UnitSystem::measure measure, std::vector< Scalar > &buffer)
static bool allocBufferIfRequested(std::map< std::string, int > &rstKeywords, unsigned bufferSize, std::vector< Scalar > &buffer, std::string_view kw, bool supported, bool required=false)
std::unordered_map< std::string, std::vector< int > > regions_
Definition: GenericOutputModule.hpp:571
std::map< std::pair< std::string, int >, double > blockData_
Definition: GenericOutputModule.hpp:645
LogOutputHelper< Scalar > logOutput_
Definition: GenericOutputModule.hpp:542
void assignPhaseProperties(data::Solution &sol, const PhasePropertyNames &names)
Move the phase density and viscosity buffers to sol under names.
std::vector< Scalar > ScalarBuffer
Definition: GenericOutputModule.hpp:325
void updateTotalVolumesAndPressures_(const unsigned globalDofIdx, const IntensiveQuantities &intQuants, const double totVolume, const Scalar referencePorosity)
Definition: GenericOutputModule.hpp:362
InterRegFlowMap interRegionFlows_
Definition: GenericOutputModule.hpp:541
std::array< ScalarBuffer, numPhases > density_
Definition: GenericOutputModule.hpp:631
std::array< ScalarBuffer, numPhases > saturation_
Definition: GenericOutputModule.hpp:630
const EclipseState & eclState_
Definition: GenericOutputModule.hpp:535
virtual void assignToSolution(data::Solution &sol)
Move all buffers to data::Solution.
const Inplace * initialInplace() const
Definition: GenericOutputModule.hpp:274
static void registerParameters()
Register all run-time parameters for the Vtk output module.
ScalarBuffer fluidPressure_
Definition: GenericOutputModule.hpp:590
Inter-region flow accumulation maps for all region definition arrays.
Definition: InterRegFlows.hpp:179
void clear()
Clear all internal buffers, but preserve allocated capacity.
Output module for compositional-model results written in ECL binary format.
Definition: OutputCompositionalModule.hpp:83
void processElementFlows(const ElementContext &)
Definition: OutputCompositionalModule.hpp:616
void clearExtractors()
Clear the active element and block extractors.
Definition: OutputCompositionalModule.hpp:581
static void registerParameters()
Definition: OutputCompositionalModule.hpp:102
void initializeFluxData()
Prepare for capturing connection fluxes, particularly to account for inter-region flows.
Definition: OutputCompositionalModule.hpp:695
void setupBlockExtractors_()
Setup the block (B*) summary extractors.
Definition: OutputCompositionalModule.hpp:456
void setupExtractors(const bool, const std::size_t)
Setup list of active element-level data extractors.
Definition: OutputCompositionalModule.hpp:321
void updateFluidInPlace(const unsigned globalDofIdx, const IntensiveQuantities &intQuants, const double totVolume)
Definition: OutputCompositionalModule.hpp:718
void allocFormulationBuffers(std::map< std::string, int > &rstKeywords, const unsigned bufferSize) override
Allocate compositional relative-permeability buffers.
Definition: OutputCompositionalModule.hpp:761
void outputFipAndResvLogToCSV(const std::size_t reportStepNum, const bool substep, const Parallel::Communication &comm)
Definition: OutputCompositionalModule.hpp:286
void allocBuffers(const unsigned bufferSize, const unsigned reportStepNum, const bool substep, const bool log, const bool forceRestartFieldAllocation)
Allocate memory for the scalar fields we would like to write to ECL output files.
Definition: OutputCompositionalModule.hpp:181
void finalizeFluxData()
Finalize capturing connection fluxes.
Definition: OutputCompositionalModule.hpp:705
void processElement(const ElementContext &elemCtx)
Modify the internal buffers according to the intensive quanties relevant for an element.
Definition: OutputCompositionalModule.hpp:591
OutputCompositionalModule(const Simulator &simulator, const SummaryConfig &smryCfg, const CollectDataToIORankType &collectToIORank)
Definition: OutputCompositionalModule.hpp:109
void processElementBlockData(const ElementContext &elemCtx)
Definition: OutputCompositionalModule.hpp:623
void validateLocalData() override
Definition: OutputCompositionalModule.hpp:738
void assignToSolution(data::Solution &sol) override
Move all buffers to data::Solution.
Definition: OutputCompositionalModule.hpp:207
void processFluxes(const ElementContext &, ActiveIndex &&, CartesianIndex &&)
Capture connection fluxes, particularly to account for inter-region flows.
Definition: OutputCompositionalModule.hpp:685
const InterRegFlowMap & getInterRegFlows() const
Get read-only access to collection of inter-region flows.
Definition: OutputCompositionalModule.hpp:713
void outputFipAndResvLog(const Inplace &inplace, const std::size_t reportStepNum, double elapsed, boost::posix_time::ptime currentDate, const bool substep, const Parallel::Communication &comm)
Definition: OutputCompositionalModule.hpp:234
Defines the common properties required by the porous medium multi-phase models.
Dune::Communication< MPIComm > Communication
Definition: ParallelCommunication.hpp:30
Definition: blackoilbioeffectsmodules.hh:45
std::string moduleVersionName()
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
This file provides the infrastructure to retrieve run-time parameters.
The Opm property system, traits with inheritance.
Definition: GenericOutputModule.hpp:410
std::string_view oilDensity
Definition: GenericOutputModule.hpp:411
static void registerParameters()
Registers the parameters in parameter system.
Context passed to element extractor functions.
Definition: OutputExtractor.hpp:208
Definition: OutputExtractor.hpp:228
Wrapping struct holding types used for block-level data extraction.
Definition: OutputExtractor.hpp:195
std::variant< ScalarEntry, PhaseEntry, TensorEntry > Entry
Descriptor for extractors.
Definition: OutputExtractor.hpp:262
std::unordered_map< int, std::vector< Exec > > ExecMap
A map of extraction executors, keyed by cartesian cell index.
Definition: OutputExtractor.hpp:277
static ExecMap setupExecMap(std::map< std::pair< std::string, int >, double > &blockData, const std::span< const Entry > handlers)
Setup an extractor executor map from a map of evaluations to perform.
Definition: OutputExtractor.hpp:410
static void process(const std::vector< Exec > &blockExtractors, const Context &ectx)
Process a list of block extractors.
Definition: OutputExtractor.hpp:435
Context passed to extractor functions.
Definition: OutputExtractor.hpp:77
int episodeIndex
Current report step.
Definition: OutputExtractor.hpp:80
Descriptor for extractors.
Definition: OutputExtractor.hpp:116
Struct holding hysteresis parameters.
Definition: OutputExtractor.hpp:66
A phase buffer extractor descriptor.
Definition: OutputExtractor.hpp:109
A scalar extractor descriptor.
Definition: OutputExtractor.hpp:102
Wrapping struct holding types used for element-level data extraction.
Definition: OutputExtractor.hpp:57
static void process(const Context &ectx, const std::vector< Entry > &extractors)
Process the given extractor entries.
Definition: OutputExtractor.hpp:160
static std::vector< Entry > removeInactive(std::array< Entry, size > &input)
Obtain vector of active extractors from an array of extractors.
Definition: OutputExtractor.hpp:123