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/SummaryConfig/SummaryConfig.hpp>
40
41#include <opm/material/common/Valgrind.hpp>
42
48
53
54#include <algorithm>
55#include <cstddef>
56#include <fstream>
57#include <memory>
58#include <sstream>
59#include <stdexcept>
60#include <string>
61#include <type_traits>
62#include <utility>
63#include <vector>
64
65
66namespace Opm {
67
68// forward declaration
69template <class TypeTag>
70class EcfvDiscretization;
71
78template <class TypeTag>
79class OutputCompositionalModule : public GenericOutputBlackoilModule<GetPropType<TypeTag, Properties::FluidSystem>>
80{
89
90 enum { numPhases = FluidSystem::numPhases };
91 enum { numComponents = FluidSystem::numComponents };
92 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
93 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
94 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
95
96public:
97 static void registerParameters()
98 {
101 }
102
103 template <class CollectDataToIORankType>
104 OutputCompositionalModule(const Simulator& simulator,
105 const SummaryConfig& smryCfg,
106 const CollectDataToIORankType& collectToIORank)
107 : BaseType(simulator.vanguard().eclState(),
108 simulator.vanguard().schedule(),
109 smryCfg,
110 simulator.vanguard().summaryState(),
112 [this](const int idx)
113 { return simulator_.problem().eclWriter().collectOnIORank().localIdxToGlobalIdx(idx); },
114 [&collectToIORank](const int idx)
115 { return collectToIORank.isCartIdxOnThisRank(idx); },
116 simulator.vanguard().grid().comm(),
117 getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal,
118 getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::ConstantTemperature,
119 getPropValue<TypeTag, Properties::EnableMech>(),
120 getPropValue<TypeTag, Properties::EnableSolvent>(),
121 getPropValue<TypeTag, Properties::EnablePolymer>(),
122 getPropValue<TypeTag, Properties::EnableFoam>(),
123 getPropValue<TypeTag, Properties::EnableBrine>(),
124 getPropValue<TypeTag, Properties::EnableSaltPrecipitation>(),
125 getPropValue<TypeTag, Properties::EnableExtbo>(),
126 getPropValue<TypeTag, Properties::EnableBioeffects>(),
127 getPropValue<TypeTag, Properties::EnableGeochemistry>())
128 , simulator_(simulator)
129 {
130 for (auto& region_pair : this->regions_) {
131 this->createLocalRegion_(region_pair.second);
132 }
133
134 auto isCartIdxOnThisRank = [&collectToIORank](const int idx) {
135 return collectToIORank.isCartIdxOnThisRank(idx);
136 };
137
138 this->setupBlockData(isCartIdxOnThisRank);
139
140 // Allocate LB* summary slots. The compositional block-data fill
141 // (processElementBlockData) is a stub, so no LB* values are produced
142 // here and the parallel gather has nothing to collect; the allocation
143 // is kept single-process and the ownership predicate is trivially true
144 // (there are no per-rank values to keep disjoint).
145 if (! collectToIORank.isParallel()) {
146 // Empty name->level map: the compositional block-data fill is a stub,
147 // so no LB* values are produced and nothing needs allocating.
148 this->setupLgrBlockData({}, [](const int, const int) { return true; });
149 }
150
151 if (! Parameters::Get<Parameters::OwnerCellsFirst>()) {
152 const std::string msg = "The output code does not support --owner-cells-first=false.";
153 if (collectToIORank.isIORank()) {
154 OpmLog::error(msg);
155 }
156 OPM_THROW_NOLOG(std::runtime_error, msg);
157 }
158
159 if (smryCfg.match("[FB]PP[OGW]") || smryCfg.match("RPP[OGW]*")) {
160 auto rset = this->eclState_.fieldProps().fip_regions();
161 rset.push_back("PVTNUM");
162
163 // Note: We explicitly use decltype(auto) here because the
164 // default scheme (-> auto) will deduce an undesirable type. We
165 // need the "reference to vector" semantics in this instance.
167 .emplace(this->simulator_.gridView().comm(),
168 FluidSystem::numPhases, rset,
169 [fp = std::cref(this->eclState_.fieldProps())]
170 (const std::string& rsetName) -> decltype(auto)
171 { return fp.get().get_int(rsetName); });
172 }
173 }
174
179 void
180 allocBuffers(const unsigned bufferSize,
181 const unsigned reportStepNum,
182 const bool substep,
183 const bool log,
184 const bool isRestart)
185 {
186 if (! std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
187 return;
188 }
189
190 auto rstKeywords = this->schedule_.rst_keywords(reportStepNum);
191 this->compC_.allocate(bufferSize, rstKeywords);
192
193 this->doAllocBuffers(bufferSize, reportStepNum, substep, log, isRestart,
194 /* hysteresisConfig = */ nullptr,
195 /* numOutputNnc =*/ 0,
196 std::move(rstKeywords));
197 }
198
199 void assignToSolution(data::Solution& sol)
200 {
201 this->compC_.outputRestart(sol, this->saturation_[oilPhaseIdx]);
203 }
204
205 void outputFipAndResvLog(const Inplace& inplace,
206 const std::size_t reportStepNum,
207 double elapsed,
208 boost::posix_time::ptime currentDate,
209 const bool substep,
210 const Parallel::Communication& comm)
211 {
212 if (comm.rank() != 0) {
213 return;
214 }
215
216 std::unique_ptr<FIPConfig> fipSched;
217 if (reportStepNum > 0) {
218 const auto& rpt = this->schedule_[reportStepNum - 1].rpt_config.get();
219 fipSched = std::make_unique<FIPConfig>(rpt);
220 }
221
222 const FIPConfig& fipc = reportStepNum == 0
223 ? this->eclState_.getEclipseConfig().fip()
224 : *fipSched;
225
226 if (!substep && !this->forceDisableFipOutput_ && fipc.output(FIPConfig::OutputField::FIELD)) {
227 this->logOutput_.timeStamp("BALANCE", elapsed, reportStepNum, currentDate);
228
229 const auto& initial_inplace = *this->initialInplace();
230 this->logOutput_.fip(inplace, initial_inplace, "");
231
232 if (fipc.output(FIPConfig::OutputField::FIPNUM)) {
233 this->logOutput_.fip(inplace, initial_inplace, "FIPNUM");
234
235 if (fipc.output(FIPConfig::OutputField::RESV)) {
236 this->logOutput_.fipResv(inplace, "FIPNUM");
237 }
238 }
239
240 if (fipc.output(FIPConfig::OutputField::FIP)) {
241 for (const auto& reg : this->regions_) {
242 if (reg.first != "FIPNUM") {
243 std::ostringstream ss;
244 ss << "BAL" << reg.first.substr(3);
245 this->logOutput_.timeStamp(ss.str(), elapsed, reportStepNum, currentDate);
246 this->logOutput_.fip(inplace, initial_inplace, reg.first);
247
248 if (fipc.output(FIPConfig::OutputField::RESV)) {
249 this->logOutput_.fipResv(inplace, reg.first);
250 }
251 }
252 }
253 }
254 }
255 }
256
257 void outputFipAndResvLogToCSV(const std::size_t reportStepNum,
258 const bool substep,
259 const Parallel::Communication& comm)
260 {
261 if (comm.rank() != 0) {
262 return;
263 }
264
265 if ((reportStepNum == 0) && (!substep) &&
266 (this->schedule_.initialReportConfiguration().has_value()) &&
267 (this->schedule_.initialReportConfiguration()->contains("CSVFIP"))) {
268
269 std::ostringstream csv_stream;
270
271 this->logOutput_.csv_header(csv_stream);
272
273 const auto& initial_inplace = *this->initialInplace();
274
275 this->logOutput_.fip_csv(csv_stream, initial_inplace, "FIPNUM");
276
277 for (const auto& reg : this->regions_) {
278 if (reg.first != "FIPNUM") {
279 this->logOutput_.fip_csv(csv_stream, initial_inplace, reg.first);
280 }
281 }
282
283 const IOConfig& io = this->eclState_.getIOConfig();
284 auto csv_fname = io.getOutputDir() + "/" + io.getBaseName() + ".CSV";
285
286 std::ofstream outputFile(csv_fname);
287 outputFile << csv_stream.str();
288 }
289 }
290
292 void setupExtractors(const bool /*isSubStep*/,
293 const std::size_t /*reportStepNum*/)
294 {
295 using Entry = typename Extractor::Entry;
296 using ExtractContext = typename Extractor::Context;
297 using ScalarEntry = typename Extractor::ScalarEntry;
298 using PhaseEntry = typename Extractor::PhaseEntry;
299
300 auto extractors = std::array{
301 Entry{PhaseEntry{&this->saturation_,
302 [](const unsigned phase, const ExtractContext& ectx)
303 { return getValue(ectx.fs.saturation(phase)); }}
304 },
305 Entry{ScalarEntry{&this->fluidPressure_,
306 [](const ExtractContext& ectx)
307 {
308 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
309 // Output oil pressure as default
310 return getValue(ectx.fs.pressure(oilPhaseIdx));
311 }
312 else if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
313 // Output gas if oil is not present
314 return getValue(ectx.fs.pressure(gasPhaseIdx));
315 }
316 else {
317 // Output water if neither oil nor gas is present
318 return getValue(ectx.fs.pressure(waterPhaseIdx));
319 }
320 }}
321 },
322 Entry{ScalarEntry{&this->temperature_,
323 [](const ExtractContext& ectx)
324 { return getValue(ectx.fs.temperature(oilPhaseIdx)); }}
325 },
326 Entry{[&compC = this->compC_](const ExtractContext& ectx)
327 {
328 compC.assignMoleFractions(ectx.globalDofIdx,
329 [&fs = ectx.fs](const unsigned compIdx)
330 { return getValue(fs.moleFraction(compIdx)); });
331
332 if (FluidSystem::phaseIsActive(gasPhaseIdx)) {
333 compC.assignGasFractions(ectx.globalDofIdx,
334 [&fs = ectx.fs](const unsigned compIdx)
335 { return getValue(fs.moleFraction(gasPhaseIdx, compIdx)); });
336 }
337
338 if (FluidSystem::phaseIsActive(oilPhaseIdx)) {
339 compC.assignOilFractions(ectx.globalDofIdx,
340 [&fs = ectx.fs](const unsigned compIdx)
341 { return getValue(fs.moleFraction(oilPhaseIdx, compIdx)); });
342 }
343 }, this->compC_.allocated()
344 },
345 };
346
347 this->extractors_ = Extractor::removeInactive(extractors);
348 }
349
352 { this->extractors_.clear(); }
353
358 void processElement(const ElementContext& elemCtx)
359 {
360 OPM_TIMEBLOCK_LOCAL(processElement, Subsystem::Output);
361 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value) {
362 return;
363 }
364
365 typename Extractor::HysteresisParams hysterParams{};
366 for (unsigned dofIdx = 0; dofIdx < elemCtx.numPrimaryDof(/*timeIdx=*/0); ++dofIdx) {
367 const auto& intQuants = elemCtx.intensiveQuantities(dofIdx, /*timeIdx=*/0);
368 const auto& fs = intQuants.fluidState();
369
370 const typename Extractor::Context ectx{
371 elemCtx.globalSpaceIndex(dofIdx, /*timeIdx=*/0),
372 0, // elemCtx.primaryVars(dofIdx, /*timeIdx=*/0).pvtRegionIndex(),
373 elemCtx.simulator().episodeIndex(),
374 fs,
375 intQuants,
376 hysterParams
377 };
378
379 Extractor::process(ectx, extractors_);
380 }
381 }
382
383 void processElementFlows(const ElementContext& /* elemCtx */)
384 {
385 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
386 if (!std::is_same_v<Discretization, EcfvDiscretization<TypeTag>>)
387 return;
388 }
389
390 void processElementBlockData(const ElementContext& /* elemCtx */)
391 {
392 OPM_TIMEBLOCK_LOCAL(processElementBlockData, Subsystem::Output);
393 if (!std::is_same<Discretization, EcfvDiscretization<TypeTag>>::value)
394 return;
395 }
396
425 template <class ActiveIndex, class CartesianIndex>
426 void processFluxes(const ElementContext& /* elemCtx */,
427 ActiveIndex&& /* activeIndex*/,
428 CartesianIndex&& /* cartesianIndex */)
429 {
430 }
431
437 {
438 // Inter-region flow rates. Note: ".clear()" prepares to accumulate
439 // contributions per bulk connection between FIP regions.
440 this->interRegionFlows_.clear();
441 }
442
447 {
449 }
450
455 {
456 return this->interRegionFlows_;
457 }
458
459 void updateFluidInPlace(const unsigned /* globalDofIdx */,
460 const IntensiveQuantities& /* intQuants */,
461 const double /* totVolume */)
462 {
463 // this->updateFluidInPlace_(globalDofIdx, intQuants, totVolume);
464 }
465
466private:
467 bool isDefunctParallelWell(const std::string& wname) const override
468 {
469 if (simulator_.gridView().comm().size() == 1)
470 return false;
471 const auto& parallelWells = simulator_.vanguard().parallelWells();
472 std::pair<std::string, bool> value {wname, true};
473 auto candidate = std::lower_bound(parallelWells.begin(), parallelWells.end(), value);
474 return candidate == parallelWells.end() || *candidate != value;
475 }
476
477 bool isOwnedByCurrentRank(const std::string& wname) const override
478 {
479 // Note: This statement is not correct for distributed wells and
480 // will need additional logic once those are supported for
481 // compositional flows.
482 return ! this->isDefunctParallelWell(wname);
483 }
484
485 bool isOnCurrentRank(const std::string& wname) const override
486 {
487 // Note: This statement is not correct for distributed wells and
488 // will need additional logic once those are supported for
489 // compositional flows.
490 return ! this->isDefunctParallelWell(wname);
491 }
492
493 void createLocalRegion_(std::vector<int>& region)
494 {
495 std::size_t elemIdx = 0;
496 for (const auto& elem : elements(simulator_.gridView())) {
497 if (elem.partitionType() != Dune::InteriorEntity) {
498 region[elemIdx] = 0;
499 }
500
501 ++elemIdx;
502 }
503 }
504
505 const Simulator& simulator_;
506 CompositionalContainer<FluidSystem> compC_;
507 std::vector<typename Extractor::Entry> extractors_;
508};
509
510} // namespace Opm
511
512#endif // OPM_OUTPUT_COMPOSITIONAL_MODULE_HPP
Declares the properties required by the black oil model.
The base class for the element-centered finite-volume discretization scheme.
Definition: ecfvdiscretization.hh:160
Definition: GenericOutputBlackoilModule.hpp:80
const Inplace * initialInplace() const
Definition: GenericOutputBlackoilModule.hpp:263
ScalarBuffer fluidPressure_
Definition: GenericOutputBlackoilModule.hpp:437
const EclipseState & eclState_
Definition: GenericOutputBlackoilModule.hpp:396
LogOutputHelper< Scalar > logOutput_
Definition: GenericOutputBlackoilModule.hpp:403
std::optional< RegionPhasePoreVolAverage > regionAvgDensity_
Definition: GenericOutputBlackoilModule.hpp:512
static void registerParameters()
Register all run-time parameters for the Vtk output module.
ScalarBuffer temperature_
Definition: GenericOutputBlackoilModule.hpp:438
void doAllocBuffers(unsigned bufferSize, unsigned reportStepNum, const bool substep, const bool log, const bool isRestart, const EclHysteresisConfig *hysteresisConfig, unsigned numOutputNnc=0, std::map< std::string, int > rstKeywords={})
const Schedule & schedule_
Definition: GenericOutputBlackoilModule.hpp:397
InterRegFlowMap interRegionFlows_
Definition: GenericOutputBlackoilModule.hpp:402
void setupBlockData(std::function< bool(int)> isCartIdxOnThisRank)
std::array< ScalarBuffer, numPhases > saturation_
Definition: GenericOutputBlackoilModule.hpp:480
std::unordered_map< std::string, std::vector< int > > regions_
Definition: GenericOutputBlackoilModule.hpp:423
bool forceDisableFipOutput_
Definition: GenericOutputBlackoilModule.hpp:418
void assignToSolution(data::Solution &sol)
Move all buffers to data::Solution.
void setupLgrBlockData(const std::map< std::string, int > &lgrNameToLevel, const std::function< bool(int, int)> &isLgrCellOnThisRank)
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 the results black oil model writing in ECL binary format.
Definition: OutputCompositionalModule.hpp:80
void processElementFlows(const ElementContext &)
Definition: OutputCompositionalModule.hpp:383
void clearExtractors()
Clear list of active element-level data extractors.
Definition: OutputCompositionalModule.hpp:351
static void registerParameters()
Definition: OutputCompositionalModule.hpp:97
void initializeFluxData()
Prepare for capturing connection fluxes, particularly to account for inter-region flows.
Definition: OutputCompositionalModule.hpp:436
void setupExtractors(const bool, const std::size_t)
Setup list of active element-level data extractors.
Definition: OutputCompositionalModule.hpp:292
void outputFipAndResvLogToCSV(const std::size_t reportStepNum, const bool substep, const Parallel::Communication &comm)
Definition: OutputCompositionalModule.hpp:257
void assignToSolution(data::Solution &sol)
Definition: OutputCompositionalModule.hpp:199
void finalizeFluxData()
Finalize capturing connection fluxes.
Definition: OutputCompositionalModule.hpp:446
void processElement(const ElementContext &elemCtx)
Modify the internal buffers according to the intensive quanties relevant for an element.
Definition: OutputCompositionalModule.hpp:358
OutputCompositionalModule(const Simulator &simulator, const SummaryConfig &smryCfg, const CollectDataToIORankType &collectToIORank)
Definition: OutputCompositionalModule.hpp:104
void allocBuffers(const unsigned bufferSize, const unsigned reportStepNum, const bool substep, const bool log, const bool isRestart)
Allocate memory for the scalar fields we would like to write to ECL output files.
Definition: OutputCompositionalModule.hpp:180
void processElementBlockData(const ElementContext &)
Definition: OutputCompositionalModule.hpp:390
void processFluxes(const ElementContext &, ActiveIndex &&, CartesianIndex &&)
Capture connection fluxes, particularly to account for inter-region flows.
Definition: OutputCompositionalModule.hpp:426
const InterRegFlowMap & getInterRegFlows() const
Get read-only access to collection of inter-region flows.
Definition: OutputCompositionalModule.hpp:454
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:205
void updateFluidInPlace(const unsigned, const IntensiveQuantities &, const double)
Definition: OutputCompositionalModule.hpp:459
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.
static void registerParameters()
Registers the parameters in parameter system.
Context passed to extractor functions.
Definition: OutputExtractor.hpp:75
int episodeIndex
Current report step.
Definition: OutputExtractor.hpp:78
Descriptor for extractors.
Definition: OutputExtractor.hpp:114
Struct holding hysteresis parameters.
Definition: OutputExtractor.hpp:64
A phase buffer extractor descriptor.
Definition: OutputExtractor.hpp:107
A scalar extractor descriptor.
Definition: OutputExtractor.hpp:100
Wrapping struct holding types used for element-level data extraction.
Definition: OutputExtractor.hpp:55
static void process(const Context &ectx, const std::vector< Entry > &extractors)
Process the given extractor entries.
Definition: OutputExtractor.hpp:158
static std::vector< Entry > removeInactive(std::array< Entry, size > &input)
Obtain vector of active extractors from an array of extractors.
Definition: OutputExtractor.hpp:121