opm-simulators
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 
32 #include <opm/simulators/utils/moduleVersion.hpp>
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 
66 namespace Opm {
67 
68 // forward declaration
69 template <class TypeTag>
70 class EcfvDiscretization;
71 
78 template <class TypeTag>
79 class 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 
96 public:
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.
166  this->regionAvgDensity_
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  {
448  this->interRegionFlows_.compress();
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 
466 private:
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
Output module for the results black oil model writing in ECL binary format.
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
void finalizeFluxData()
Finalize capturing connection fluxes.
Definition: OutputCompositionalModule.hpp:446
static void registerParameters()
Register all run-time parameters for the Vtk output module.
Definition: GenericOutputBlackoilModule.cpp:194
Descriptor for extractors.
Definition: OutputExtractor.hpp:113
The base class for the element-centered finite-volume discretization scheme.
Definition: fvbasegradientcalculator.hh:42
std::string moduleVersionName()
Return the version name of the module, for example "2015.10" (for a release branch) or "2016...
Definition: moduleVersion.cpp:34
VTK output module for the black oil model&#39;s parameters.
Helper class for grid instantiation of ECL file-format using problems.
This file provides the infrastructure to retrieve run-time parameters.
Wrapping struct holding types used for element-level data extraction.
Definition: OutputExtractor.hpp:54
Output module for the results black oil model writing in ECL binary format.
const InterRegFlowMap & getInterRegFlows() const
Get read-only access to collection of inter-region flows.
Definition: OutputCompositionalModule.hpp:454
Defines the common properties required by the porous medium multi-phase models.
void clearExtractors()
Clear list of active element-level data extractors.
Definition: OutputCompositionalModule.hpp:351
void compress()
Form CSR adjacency matrix representation of input graph from connections established in previous call...
Definition: InterRegFlows.cpp:164
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Struct holding hysteresis parameters.
Definition: OutputExtractor.hpp:63
Declares the properties required by the black oil model.
Definition: GenericOutputBlackoilModule.hpp:80
Inter-region flow accumulation maps for all region definition arrays.
Definition: InterRegFlows.hpp:178
Output module for the results black oil model writing in ECL binary format.
void setupExtractors(const bool, const std::size_t)
Setup list of active element-level data extractors.
Definition: OutputCompositionalModule.hpp:292
A scalar extractor descriptor.
Definition: OutputExtractor.hpp:99
static void process(const Context &ectx, const std::vector< Entry > &extractors)
Process the given extractor entries.
Definition: OutputExtractor.hpp:158
void clear()
Clear all internal buffers, but preserve allocated capacity.
Definition: InterRegFlows.cpp:171
void initializeFluxData()
Prepare for capturing connection fluxes, particularly to account for inter-region flows...
Definition: OutputCompositionalModule.hpp:436
void processElement(const ElementContext &elemCtx)
Modify the internal buffers according to the intensive quanties relevant for an element.
Definition: OutputCompositionalModule.hpp:358
Output module for the results black oil model writing in ECL binary format.
Definition: OutputCompositionalModule.hpp:79
The Opm property system, traits with inheritance.
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
static std::vector< Entry > removeInactive(std::array< Entry, size > &input)
Obtain vector of active extractors from an array of extractors.
Definition: OutputExtractor.hpp:121
void processFluxes(const ElementContext &, ActiveIndex &&, CartesianIndex &&)
Capture connection fluxes, particularly to account for inter-region flows.
Definition: OutputCompositionalModule.hpp:426
Context passed to extractor functions.
Definition: OutputExtractor.hpp:74
void assignToSolution(data::Solution &sol)
Move all buffers to data::Solution.
Definition: GenericOutputBlackoilModule.cpp:316
int episodeIndex
Current report step.
Definition: OutputExtractor.hpp:78
A phase buffer extractor descriptor.
Definition: OutputExtractor.hpp:106
static void registerParameters()
Registers the parameters in parameter system.
Definition: vtkblackoilparams.cpp:31