FlowGenericProblem_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_FLOW_GENERIC_PROBLEM_IMPL_HPP
24#define OPM_FLOW_GENERIC_PROBLEM_IMPL_HPP
25
26#ifndef OPM_FLOW_GENERIC_PROBLEM_HPP
27#include <config.h>
29#endif
30
31#include <dune/common/parametertree.hh>
32
33#include <opm/common/ErrorMacros.hpp>
34#include <opm/common/utility/TimeService.hpp>
35
36#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
37#include <opm/input/eclipse/EclipseState/Tables/OverburdTable.hpp>
38#include <opm/input/eclipse/EclipseState/Tables/RockwnodTable.hpp>
39#include <opm/input/eclipse/Schedule/Schedule.hpp>
40#include <opm/input/eclipse/Units/Units.hpp>
41
45
48
50
51#include <boost/date_time.hpp>
52
53#include <fmt/format.h>
54#include <fmt/ranges.h>
55
56#include <iostream>
57#include <stdexcept>
58
59namespace Opm {
60
61template<class GridView, class FluidSystem>
63FlowGenericProblem(const EclipseState& eclState,
64 const Schedule& schedule,
65 const GridView& gridView)
66 : eclState_(eclState)
67 , schedule_(schedule)
68 , gridView_(gridView)
69 , lookUpData_(gridView)
70{
71 // we need to update the FluidSystem based on EclipseState before it is passed around
73
74 enableTuning_ = Parameters::Get<Parameters::EnableTuning>();
75 enableDriftCompensation_ = Parameters::Get<Parameters::EnableDriftCompensation>();
76 initialTimeStepSize_ = Parameters::Get<Parameters::InitialTimeStepSize<Scalar>>();
77 maxTimeStepAfterWellEvent_ = unit::convert::from
79
80 // The value N for this parameter is defined in the following order of precedence:
81 //
82 // 1. Command line value (--num-pressure-points-equil=N)
83 //
84 // 2. EQLDIMS item 2. Default value from
85 // opm-common/opm/input/eclipse/share/keywords/000_Eclipse100/E/EQLDIMS
86
87 if (Parameters::IsSet<Parameters::NumPressurePointsEquil>()) {
88 numPressurePointsEquil_ = Parameters::Get<Parameters::NumPressurePointsEquil>();
90 throw std::invalid_argument {
91 fmt::format("--num-pressure-points-equil must be at least 1, "
92 "but {} was given.", numPressurePointsEquil_)
93 };
94 }
95 }
96 else {
97 numPressurePointsEquil_ = eclState.getTableManager().getEqldims().getNumDepthNodesP();
99 throw std::invalid_argument {
100 fmt::format("EQLDIMS item 2, the number of depth nodes in the "
101 "equilibration pressure tables, must be at least 1, "
102 "but {} was given.", numPressurePointsEquil_)
103 };
104 }
105 }
106
107 explicitRockCompaction_ = Parameters::Get<Parameters::ExplicitRockCompaction>();
108}
110template<class GridView, class FluidSystem>
113serializationTestObject(const EclipseState& eclState,
114 const Schedule& schedule,
115 const GridView& gridView)
116{
117 FlowGenericProblem result(eclState, schedule, gridView);
118 result.maxOilSaturation_ = {1.0, 2.0};
119 result.maxWaterSaturation_ = {6.0};
120 result.minRefPressure_ = {7.0, 8.0, 9.0, 10.0};
121 result.overburdenPressure_ = {11.0};
122 result.solventSaturation_ = {15.0};
123 result.solventRsw_ = {18.0};
128 return result;
129}
130
131template<class GridView, class FluidSystem>
132std::string
134helpPreamble(int,
135 const char **argv)
136{
137 std::string desc = FlowGenericProblem::briefDescription();
138 if (!desc.empty())
139 desc = desc + "\n";
140
141 return
142 "Usage: "+std::string(argv[0]) + " [OPTIONS] [DECK_FILENAME]\n"
143 + desc;
144}
145
146template<class GridView, class FluidSystem>
147std::string
150{
151 return briefDescription_;
152}
153
154template<class GridView, class FluidSystem>
156readRockParameters_(const std::vector<Scalar>& cellCenterDepths,
157 std::function<std::array<int,3>(const unsigned)> ijkIndex)
158{
159 const auto& rock_config = eclState_.getSimulationConfig().rock_config();
160
161 // read the rock compressibility parameters
162 {
163 const auto& comp = rock_config.comp();
164 rockParams_.clear();
165 std::ranges::transform(comp, std::back_inserter(rockParams_),
166 [](const auto& c)
167 {
168 return RockParams{
169 static_cast<Scalar>(c.pref),
170 static_cast<Scalar>(c.compressibility)
171 };
172 });
173 }
174
175 // Warn that ROCK and ROCKOPTS item 2 = STORE is used together
176 if (rock_config.store()) {
177 OpmLog::warning("ROCKOPTS item 2 set to STORE, ROCK item 1 replaced with initial (equilibrated) pressures");
178 }
179
180 // read the parameters for water-induced rock compaction
181 readRockCompactionParameters_();
182
183 unsigned numElem = gridView_.size(0);
184 if (eclState_.fieldProps().has_int(rock_config.rocknum_property())) {
185 // Auxiliary function to check rockTableIdx_ values belong to the right range. Otherwise, throws.
186 std::function<void(int, int)> valueCheck = [&ijkIndex,&rock_config,this](int fieldPropValue, int coarseElemIdx)
187 {
188 auto fmtError = [fieldPropValue, coarseElemIdx,&ijkIndex,&rock_config](const char* type, std::size_t size)
189 {
190 return fmt::format("{} table index {} for elem {} read from {}"
191 " is out of bounds for number of tables {}",
192 type, fieldPropValue,
193 ijkIndex(coarseElemIdx),
194 rock_config.rocknum_property(), size);
195 };
196 if (!rockCompPoroMult_.empty() &&
197 fieldPropValue > static_cast<int>(rockCompPoroMult_.size())) {
198 throw std::runtime_error(fmtError("Rock compaction",
199 rockCompPoroMult_.size()));
200 }
201 if (!rockCompPoroMultWc_.empty() &&
202 fieldPropValue > static_cast<int>(rockCompPoroMultWc_.size())) {
203 throw std::runtime_error(fmtError("Rock water compaction",
204 rockCompPoroMultWc_.size()));
205 }
206 };
208 rockTableIdx_ = this->lookUpData_.template assignFieldPropsIntOnLeaf<short unsigned int>(eclState_.fieldProps(),
209 rock_config.rocknum_property(),
210 true /*needsTranslation*/,
211 valueCheck);
213
214 // Store overburden pressure pr element
215 const auto& overburdTables = eclState_.getTableManager().getOverburdTables();
216 if (!overburdTables.empty() && !rock_config.store()) {
217 overburdenPressure_.resize(numElem,0.0);
218 std::size_t numRocktabTables = rock_config.num_rock_tables();
219
220 if (overburdTables.size() != numRocktabTables)
221 throw std::runtime_error(fmt::format("{} OVERBURD tables is expected, but {} is provided",
222 numRocktabTables, overburdTables.size()));
224 std::vector<Tabulated1DFunction<Scalar>> overburdenTables(numRocktabTables);
225 for (std::size_t regionIdx = 0; regionIdx < numRocktabTables; ++regionIdx) {
226 const OverburdTable& overburdTable = overburdTables.template getTable<OverburdTable>(regionIdx);
227 overburdenTables[regionIdx].setXYContainers(overburdTable.getDepthColumn(),overburdTable.getOverburdenPressureColumn());
229
230 for (std::size_t elemIdx = 0; elemIdx < numElem; ++ elemIdx) {
231 unsigned tableIdx = 0;
232 if (!rockTableIdx_.empty()) {
233 tableIdx = rockTableIdx_[elemIdx];
234 }
235 overburdenPressure_[elemIdx] =
236 overburdenTables[tableIdx].eval(cellCenterDepths[elemIdx], /*extrapolation=*/true);
237 }
239 else if (!overburdTables.empty() && rock_config.store()) {
240 OpmLog::warning("ROCKOPTS item 2 set to STORE, OVERBURD ignored!");
241 }
242}
244template<class GridView, class FluidSystem>
247{
248 const auto& rock_config = eclState_.getSimulationConfig().rock_config();
249
250 if (!rock_config.active())
251 return; // deck does not enable rock compaction
252
253 unsigned numElem = gridView_.size(0);
254 switch (rock_config.hysteresis_mode()) {
255 case RockConfig::Hysteresis::REVERS:
256 break;
257 case RockConfig::Hysteresis::IRREVERS:
258 // interpolate the porv volume multiplier using the minimum pressure in the cell
259 // i.e. don't allow re-inflation.
260 minRefPressure_.resize(numElem, 1e99);
261 break;
262 case RockConfig::Hysteresis::HYSTER:
263 // Track the lowest pressure each cell has ever reached, exactly as for
264 // IRREVERS. It doubles as the "turning pressure" that selects/anchors
265 // the ROCKTABH elastic reload curve once the cell re-pressurizes above it.
266 minRefPressure_.resize(numElem, 1e99);
267 break;
268 default:
269 throw std::runtime_error("Not support ROCKOMP hysteresis option ");
270 }
272 std::size_t numRocktabTables = rock_config.num_rock_tables();
273 bool waterCompaction = rock_config.water_compaction();
274
275 if (waterCompaction && rock_config.hysteresis_mode() == RockConfig::Hysteresis::HYSTER)
276 throw std::runtime_error("ROCKCOMP: combining WATER_COMPACTION with HYSTERESIS=HYSTER is not supported");
277
278 if (!waterCompaction && rock_config.hysteresis_mode() == RockConfig::Hysteresis::HYSTER) {
279 const auto& rocktabhTables = eclState_.getTableManager().getRocktabhTables();
280 if (rocktabhTables.size() != numRocktabTables)
281 throw std::runtime_error("ROCKCOMP HYSTERESIS is set to HYSTER. " + std::to_string(numRocktabTables)
282 +" ROCKTABH tables is expected, but " + std::to_string(rocktabhTables.size()) +" is provided");
283
284 // The deflation (virgin/plastic loading) curve, taken from the first row of
285 // each of ROCKTABH's elastic curves, plays the same role that ROCKTAB plays
286 // for REVERS/IRREVERS, so it is stored in the very same tables.
287 rockCompPoroMult_.resize(numRocktabTables);
288 rockCompTransMult_.resize(numRocktabTables);
289 rockCompPoroMultElastic_.resize(numRocktabTables, TabulatedTwoDFunction(TabulatedTwoDFunction::InterpolationPolicy::LeftExtreme));
290 rockCompTransMultElastic_.resize(numRocktabTables, TabulatedTwoDFunction(TabulatedTwoDFunction::InterpolationPolicy::LeftExtreme));
291 for (std::size_t regionIdx = 0; regionIdx < numRocktabTables; ++regionIdx) {
292 const auto& rocktabhTable = rocktabhTables[regionIdx];
293
294 const auto& deflation = rocktabhTable.deflationCurve();
295 rockCompPoroMult_[regionIdx].setXYContainers(deflation.getColumn(0), deflation.getColumn(1));
296 rockCompTransMult_[regionIdx].setXYContainers(deflation.getColumn(0), deflation.getColumn(2));
298 const std::size_t numCurves = rocktabhTable.numElasticCurves();
299 for (std::size_t curveIdx = 0; curveIdx < numCurves; ++curveIdx) {
300 const auto& curve = rocktabhTable.elasticCurve(curveIdx);
301 const auto& pressure = curve.getColumn(0);
302 const auto& poro = curve.getColumn(1);
303 const auto& trans = curve.getColumn(2);
304
305 rockCompPoroMultElastic_[regionIdx].appendXPos(pressure[0]);
306 rockCompTransMultElastic_[regionIdx].appendXPos(pressure[0]);
307 for (std::size_t rowIdx = 0; rowIdx < curve.numRows(); ++rowIdx) {
308 rockCompPoroMultElastic_[regionIdx].appendSamplePoint(curveIdx, pressure[rowIdx], poro[rowIdx]);
309 rockCompTransMultElastic_[regionIdx].appendSamplePoint(curveIdx, pressure[rowIdx], trans[rowIdx]);
310 }
311 }
312 }
313 } else if (!waterCompaction) {
314 const auto& rocktabTables = eclState_.getTableManager().getRocktabTables();
315 if (rocktabTables.size() != numRocktabTables)
316 throw std::runtime_error("ROCKCOMP is activated." + std::to_string(numRocktabTables)
317 +" ROCKTAB tables is expected, but " + std::to_string(rocktabTables.size()) +" is provided");
318
319 rockCompPoroMult_.resize(numRocktabTables);
320 rockCompTransMult_.resize(numRocktabTables);
321 for (std::size_t regionIdx = 0; regionIdx < numRocktabTables; ++regionIdx) {
322 const auto& rocktabTable = rocktabTables.template getTable<RocktabTable>(regionIdx);
323 const auto& pressureColumn = rocktabTable.getPressureColumn();
324 const auto& poroColumn = rocktabTable.getPoreVolumeMultiplierColumn();
325 const auto& transColumn = rocktabTable.getTransmissibilityMultiplierColumn();
326 rockCompPoroMult_[regionIdx].setXYContainers(pressureColumn, poroColumn);
327 rockCompTransMult_[regionIdx].setXYContainers(pressureColumn, transColumn);
328 }
329 } else {
330 const auto& rock2dTables = eclState_.getTableManager().getRock2dTables();
331 const auto& rock2dtrTables = eclState_.getTableManager().getRock2dtrTables();
332 const auto& rockwnodTables = eclState_.getTableManager().getRockwnodTables();
333 maxWaterSaturation_.resize(numElem, 0.0);
334
335 if (rock2dTables.size() != numRocktabTables)
336 throw std::runtime_error(fmt::format("Water compation option is selected in ROCKCOMP."
337 " {} ROCK2D tables is expected, but {} is provided",
338 numRocktabTables, rock2dTables.size()));
339
340 if (rockwnodTables.size() != numRocktabTables)
341 throw std::runtime_error(fmt::format("Water compation option is selected in ROCKCOMP."
342 " {} ROCKWNOD tables is expected, but {} is provided",
343 numRocktabTables, rockwnodTables.size()));
344 //TODO check size match
345 rockCompPoroMultWc_.resize(numRocktabTables, TabulatedTwoDFunction(TabulatedTwoDFunction::InterpolationPolicy::Vertical));
346 for (std::size_t regionIdx = 0; regionIdx < numRocktabTables; ++regionIdx) {
347 const RockwnodTable& rockwnodTable = rockwnodTables.template getTable<RockwnodTable>(regionIdx);
348 const auto& rock2dTable = rock2dTables[regionIdx];
349
350 if (rockwnodTable.getSaturationColumn().size() != rock2dTable.sizeMultValues())
351 throw std::runtime_error("Number of entries in ROCKWNOD and ROCK2D needs to match.");
353 for (std::size_t xIdx = 0; xIdx < rock2dTable.size(); ++xIdx) {
354 rockCompPoroMultWc_[regionIdx].appendXPos(rock2dTable.getPressureValue(xIdx));
355 for (std::size_t yIdx = 0; yIdx < rockwnodTable.getSaturationColumn().size(); ++yIdx)
356 rockCompPoroMultWc_[regionIdx].appendSamplePoint(xIdx,
357 rockwnodTable.getSaturationColumn()[yIdx],
358 rock2dTable.getPvmultValue(xIdx, yIdx));
359 }
360 }
361
362 if (!rock2dtrTables.empty()) {
363 rockCompTransMultWc_.resize(numRocktabTables, TabulatedTwoDFunction(TabulatedTwoDFunction::InterpolationPolicy::Vertical));
364 for (std::size_t regionIdx = 0; regionIdx < numRocktabTables; ++regionIdx) {
365 const RockwnodTable& rockwnodTable = rockwnodTables.template getTable<RockwnodTable>(regionIdx);
366 const auto& rock2dtrTable = rock2dtrTables[regionIdx];
367
368 if (rockwnodTable.getSaturationColumn().size() != rock2dtrTable.sizeMultValues())
369 throw std::runtime_error("Number of entries in ROCKWNOD and ROCK2DTR needs to match.");
370
371 for (std::size_t xIdx = 0; xIdx < rock2dtrTable.size(); ++xIdx) {
372 rockCompTransMultWc_[regionIdx].appendXPos(rock2dtrTable.getPressureValue(xIdx));
373 for (std::size_t yIdx = 0; yIdx < rockwnodTable.getSaturationColumn().size(); ++yIdx)
374 rockCompTransMultWc_[regionIdx].appendSamplePoint(xIdx,
375 rockwnodTable.getSaturationColumn()[yIdx],
376 rock2dtrTable.getTransMultValue(xIdx, yIdx));
377 }
378 }
379 }
380 }
381}
382
383template<class GridView, class FluidSystem>
386rockCompressibility(unsigned globalSpaceIdx) const
387{
388 if (this->rockParams_.empty())
389 return 0.0;
390
391 unsigned tableIdx = 0;
392 if (!this->rockTableIdx_.empty()) {
393 tableIdx = this->rockTableIdx_[globalSpaceIdx];
394 }
395 return this->rockParams_[tableIdx].compressibility;
396}
397
398template<class GridView, class FluidSystem>
401porosity(unsigned globalSpaceIdx, unsigned timeIdx) const
402{
403 return this->referencePorosity_[timeIdx][globalSpaceIdx];
404}
405
406template<class GridView, class FluidSystem>
409rockBiotComp(unsigned elementIdx) const
410{
411 // Additional compressibility of the rock due to Biot poroelasticity
412 const auto biot = biotCoeff(elementIdx);
413 const auto lameParam = lame(elementIdx);
414 return biot * biot / lameParam;
415}
416
417template <class GridView, class FluidSystem>
420rockBiotTemp(unsigned elementIdx) const
421{
422 // Additional compressibility due to Biot thermoelasticity
423 const auto biotT = biotTemp(elementIdx);
424 const auto biotC = biotCoeff(elementIdx);
425 const auto lameParam = lame(elementIdx);
426 return biotC * biotT / lameParam;
427}
428
429template<class GridView, class FluidSystem>
432lame(unsigned elementIdx) const
433{
434 Scalar lameParam;
435 const auto& fp = eclState_.fieldProps();
436 if (fp.has_double("LAME")) {
437 lameParam = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "LAME", elementIdx);
438 }
439 else if (fp.has_double("YMODULE") && fp.has_double("SMODULUS")) {
440 const auto& yModulus = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "YMODULE", elementIdx);
441 const auto& sModulus = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "SMODULUS", elementIdx);
442 lameParam = sModulus * (yModulus - 2 * sModulus) / (3 * sModulus - yModulus);
443 }
444 else if (fp.has_double("YMODULE") && fp.has_double("PRATIO")) {
445 const auto& yModulus = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "YMODULE", elementIdx);
446 const auto& pRatio = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "PRATIO", elementIdx);
447 lameParam = yModulus * pRatio / ((1 + pRatio) * (1 - 2 * pRatio));
448 }
449 else if (fp.has_double("SMODULUS") && fp.has_double("PRATIO")) {
450 const auto& sModulus = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "SMODULUS", elementIdx);
451 const auto& pRatio = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "PRATIO", elementIdx);
452 lameParam = 2 * sModulus * pRatio / (1 - 2 * pRatio);
453 }
454 else {
455 return 0.0;
456 }
457 return lameParam;
458}
459
460template<class GridView, class FluidSystem>
463biotCoeff(unsigned elementIdx) const
464{
465 Scalar biotC;
466 const auto& fp = eclState_.fieldProps();
467 if (fp.has_double("BIOTCOEF")) {
468 biotC = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "BIOTCOEF", elementIdx);
469 }
470 else if (fp.has_double("POELCOEF") && fp.has_double("PRATIO")) {
471 const auto& poelC = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "POELCOEF", elementIdx);
472 const auto& pRatio = this->lookUpData_.fieldPropDouble(this->eclState_.fieldProps(), "PRATIO", elementIdx);
473 biotC = poelC * (1 - pRatio) / (1 - 2 * pRatio);
474 }
475 else {
476 return 0.0;
477 }
478 return biotC;
479}
480
481template <class GridView, class FluidSystem>
484biotTemp(unsigned elementIdx) const
485{
486 const auto& fp = eclState_.fieldProps();
487
488 // Return early if deck does not have necessary parameters
489 if (!fp.has_double("THERMEXR") && !fp.has_double("THELCOEF")) {
490 return 0.0;
491 }
492
493 // Calculate from THERMEXR or THELCOEF
494 // OBS: Only accept (YMODULE, PRATIO) or (LAME, SMODULUS) conversions for now
495 Scalar biotT = 0.0;
496 if (fp.has_double("THERMEXR") && !fp.has_double("THELCOEF")) {
497 const auto thermexr = this->lookUpData_.fieldPropDouble(fp, "THERMEXR", elementIdx);
498
499 if (fp.has_double("LAME") && fp.has_double("SMODULUS")) {
500 const auto lamParam = lame(elementIdx);
501 const auto sMod = this->lookUpData_.fieldPropDouble(fp, "SMODULUS", elementIdx);
502 biotT = thermexr * (3.0 * lamParam + 2.0 * sMod);
503 } else if (fp.has_double("YMODULE") && fp.has_double("PRATIO")) {
504 const auto yMod = this->lookUpData_.fieldPropDouble(fp, "YMODULE", elementIdx);
505 const auto pRatio = this->lookUpData_.fieldPropDouble(fp, "PRATIO", elementIdx);
506 biotT = thermexr * yMod / (1.0 - 2.0 * pRatio);
507 } else if (fp.has_double("LAME") && fp.has_double("PRATIO")) {
508 const auto lamParam = lame(elementIdx);
509 const auto pRatio = this->lookUpData_.fieldPropDouble(fp, "PRATIO", elementIdx);
510 biotT =
511 thermexr * lamParam * (1.0 / pRatio - 2.0 * pRatio - 1.0) / (1.0 - 2.0 * pRatio);
512 } else if (fp.has_double("SMODULUS") && fp.has_double("PRATIO")) {
513 const auto sMod = this->lookUpData_.fieldPropDouble(fp, "SMODULUS", elementIdx);
514 const auto pRatio = this->lookUpData_.fieldPropDouble(fp, "PRATIO", elementIdx);
515 biotT = thermexr * 2.0 * sMod * (1 + pRatio) / (1.0 - 2.0 * pRatio);
516 } else if (fp.has_double("YMODULE") && fp.has_double("SMODULUS")) {
517 const auto yMod = this->lookUpData_.fieldPropDouble(fp, "YMODULE", elementIdx);
518 const auto sMod = this->lookUpData_.fieldPropDouble(fp, "SMODULUS", elementIdx);
519 biotT = thermexr * yMod * sMod / (3.0 * sMod - yMod);
520 } else {
521 OPM_THROW(std::runtime_error,
522 "THERMEXR requires one of (LAME, SMODULUS), (YMODULE, PRATIO), "
523 "(LAME, PRATIO), (SMODULUS, PRATIO) or (YMODULE, SMODULUS) "
524 "to compute the Biot temperature coefficient. "
525 "(YMODULE, LAME) is not supported.");
526 }
527
528 } else if (!fp.has_double("THERMEXR") && fp.has_double("THELCOEF")) {
529 const auto thelcoef = this->lookUpData_.fieldPropDouble(fp, "THELCOEF", elementIdx);
530
531 if (fp.has_double("LAME") && fp.has_double("SMODULUS")) {
532 const auto lamParam = lame(elementIdx);
533 const auto sMod = this->lookUpData_.fieldPropDouble(fp, "SMODULUS", elementIdx);
534 biotT = thelcoef * (lamParam + 2 * sMod) / (2 * sMod);
535 } else if (fp.has_double("PRATIO")) {
536 const auto pRatio = this->lookUpData_.fieldPropDouble(fp, "PRATIO", elementIdx);
537 biotT = thelcoef * (1 - pRatio) / (1 - 2 * pRatio);
538 } else if (fp.has_double("YMODULE") && fp.has_double("SMODULUS")) {
539 const auto yMod = this->lookUpData_.fieldPropDouble(fp, "YMODULE", elementIdx);
540 const auto sMod = this->lookUpData_.fieldPropDouble(fp, "SMODULUS", elementIdx);
541 biotT = thelcoef * (4.0 * sMod - yMod) / (6.0 * sMod - 2.0 * yMod);
542 } else {
543 OPM_THROW(std::runtime_error,
544 "THELCOEF requires one of (LAME, SMODULUS), (YMODULE, SMODULUS) "
545 "to compute the Biot temperature coefficient. "
546 "(YMODULE, LAME) is not supported.");
547 }
548 } else {
549 OPM_THROW(std::runtime_error,
550 "THERMEXR and THELCOEF cannot both be specified for the "
551 "Biot temperature coefficient");
552 }
553
554 return biotT;
555}
556
557template<class GridView, class FluidSystem>
558template<class T>
560updateNum(const std::string& name, std::vector<T>& numbers, std::size_t num_regions)
561{
562 if (!eclState_.fieldProps().has_int(name))
563 return;
564
565 std::function<void(T, int)> valueCheck = [num_regions,name](T fieldPropValue, [[maybe_unused]] int fieldPropIdx) {
566 if (fieldPropValue > static_cast<int>(num_regions)) {
567 throw std::runtime_error(fmt::format("Values larger than maximum number of regions {} provided in {}",
568 num_regions, name));
569 }
570 if (fieldPropValue <= 0) {
571 throw std::runtime_error("zero or negative values provided for region array: " + name);
572 }
573 };
574
575 numbers = this->lookUpData_.template assignFieldPropsIntOnLeaf<T>(eclState_.fieldProps(), name,
576 true /*needsTranslation*/, valueCheck);
577}
578
579template<class GridView, class FluidSystem>
582{
583 const auto num_regions = eclState_.getTableManager().getTabdims().getNumPVTTables();
584 updateNum("PVTNUM", pvtnum_, num_regions);
585}
586
587template<class GridView, class FluidSystem>
590{
591 const auto num_regions = eclState_.getTableManager().getTabdims().getNumSatTables();
592 updateNum("SATNUM", satnum_, num_regions);
593}
594
595template<class GridView, class FluidSystem>
598{
599 const auto num_regions = 1; // we only support single region
600 updateNum("MISCNUM", miscnum_, num_regions);
601}
602
603template<class GridView, class FluidSystem>
606{
607 const auto num_regions = 1; // we only support single region
608 updateNum("PLMIXNUM", plmixnum_, num_regions);
609}
610
611template<class GridView, class FluidSystem>
613vapparsActive(int episodeIdx) const
614{
615 const auto& oilVaporizationControl = schedule_[episodeIdx].oilvap();
616 return (oilVaporizationControl.getType() == OilVaporizationProperties::OilVaporization::VAPPARS);
617}
618
619template<class GridView, class FluidSystem>
621beginEpisode_(bool enableExperiments,
622 int episodeIdx)
623{
624 if (enableExperiments && gridView_.comm().rank() == 0 && episodeIdx >= 0) {
625 // print some useful information in experimental mode. (the production
626 // simulator does this externally.)
627 std::ostringstream ss;
628 boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
629 boost::posix_time::ptime curDateTime =
630 boost::posix_time::from_time_t(TimeService::to_time_t(schedule_.simTime(episodeIdx)));
631 ss.imbue(std::locale(std::locale::classic(), facet));
632 ss << "Report step " << episodeIdx + 1
633 << "/" << schedule_.size() - 1
634 << " at day " << schedule_.seconds(episodeIdx)/(24*3600)
635 << "/" << schedule_.seconds(schedule_.size() - 1)/(24*3600)
636 << ", date = " << curDateTime.date()
637 << "\n ";
638 OpmLog::info(ss.str());
639 }
640
641 const auto& events = schedule_[episodeIdx].events();
642
643 // react to TUNING changes
644 if (episodeIdx > 0 && enableTuning_ && events.hasEvent(ScheduleEvents::TUNING_CHANGE))
645 {
646 const auto& sched_state = schedule_[episodeIdx];
647 const auto& tuning = sched_state.tuning();
648 initialTimeStepSize_ = sched_state.max_next_tstep(enableTuning_);
649 maxTimeStepAfterWellEvent_ = tuning.TMAXWC;
650 return true;
651 }
652
653 return false;
654}
655
656template<class GridView, class FluidSystem>
658beginTimeStep_(bool enableExperiments,
659 int episodeIdx,
660 int timeStepIndex,
661 Scalar startTime,
662 Scalar time,
663 Scalar timeStepSize,
664 Scalar endTime)
665{
666 if (enableExperiments && gridView_.comm().rank() == 0 && episodeIdx >= 0) {
667 std::ostringstream ss;
668 boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
669 boost::posix_time::ptime date = boost::posix_time::from_time_t(startTime) +
670 boost::posix_time::milliseconds(static_cast<long long>(time / prefix::milli));
671 ss.imbue(std::locale(std::locale::classic(), facet));
672 ss <<"\nTime step " << timeStepIndex << ", stepsize "
673 << unit::convert::to(timeStepSize, unit::day) << " days,"
674 << " at day " << (double)unit::convert::to(time, unit::day)
675 << "/" << (double)unit::convert::to(endTime, unit::day)
676 << ", date = " << date;
677 OpmLog::info(ss.str());
678 }
679}
680
681template<class GridView, class FluidSystem>
684{
685 FluidSystem::initFromState(eclState_, schedule_);
686}
687
688template<class GridView, class FluidSystem>
691 bool enableSolvent,
692 bool enablePolymer,
693 bool enablePolymerMolarWeight,
694 bool enableBioeffects,
695 bool enableMICP)
696{
697 // LGR (local grid refinement / CARFIN) is supported for black-oil only. The
698 // solvent/polymer/biofilm/MICP initial conditions below are read straight
699 // from the input-grid field properties and indexed by leaf cell; they are
700 // NOT mapped onto refined cells (see the black-oil EQLNUM/FIPNUM handling via
701 // LookUpData), so refined cells would get wrong/out-of-range values. Fail
702 // early with a clear message rather than producing silently wrong results.
703 if ((enableSolvent || enablePolymer || enablePolymerMolarWeight ||
704 enableBioeffects || enableMICP) &&
705 (eclState_.getLgrs().size() > 0))
706 {
707 throw std::runtime_error(
708 "Local grid refinement (LGR/CARFIN) is only supported for black-oil "
709 "runs. It is not supported together with the solvent, polymer, "
710 "biofilm or MICP extensions.");
711 }
712
713 auto getArray = [](const std::vector<double>& input)
714 {
715 if constexpr (std::is_same_v<Scalar,double>) {
716 return input;
717 } else {
718 return std::vector<Scalar>{input.begin(), input.end()};
719 }
720 };
721
722 if (enableSolvent) {
723 if (eclState_.fieldProps().has_double("SSOL")) {
724 solventSaturation_ = getArray(eclState_.fieldProps().get_double("SSOL"));
725 } else {
726 solventSaturation_.resize(numDof, 0.0);
727 }
728
729 solventRsw_.resize(numDof, 0.0);
730 }
731
732 if (enablePolymer) {
733 if (eclState_.fieldProps().has_double("SPOLY")) {
734 polymer_.concentration = getArray(eclState_.fieldProps().get_double("SPOLY"));
735 } else {
736 polymer_.concentration.resize(numDof, 0.0);
737 }
738 }
739
740 if (enablePolymerMolarWeight) {
741 if (eclState_.fieldProps().has_double("SPOLYMW")) {
742 polymer_.moleWeight = getArray(eclState_.fieldProps().get_double("SPOLYMW"));
743 } else {
744 polymer_.moleWeight.resize(numDof, 0.0);
745 }
746 }
747
748 if (enableBioeffects) {
749 if (eclState_.fieldProps().has_double("SMICR")) {
750 bioeffects_.microbialConcentration = getArray(eclState_.fieldProps().get_double("SMICR"));
751 } else {
752 bioeffects_.microbialConcentration.resize(numDof, 0.0);
753 }
754 if (eclState_.fieldProps().has_double("SBIOF")) {
755 bioeffects_.biofilmVolumeFraction = getArray(eclState_.fieldProps().get_double("SBIOF"));
756 } else {
757 bioeffects_.biofilmVolumeFraction.resize(numDof, 0.0);
758 }
759 if (enableMICP) {
760 if (eclState_.fieldProps().has_double("SOXYG")) {
761 bioeffects_.oxygenConcentration = getArray(eclState_.fieldProps().get_double("SOXYG"));
762 } else {
763 bioeffects_.oxygenConcentration.resize(numDof, 0.0);
764 }
765 if (eclState_.fieldProps().has_double("SUREA")) {
766 bioeffects_.ureaConcentration = getArray(eclState_.fieldProps().get_double("SUREA"));
767 } else {
768 bioeffects_.ureaConcentration.resize(numDof, 0.0);
769 }
770 if (eclState_.fieldProps().has_double("SCALC")) {
771 bioeffects_.calciteVolumeFraction = getArray(eclState_.fieldProps().get_double("SCALC"));
772 } else {
773 bioeffects_.calciteVolumeFraction.resize(numDof, 0.0);
774 }
775 }
776 }
777}
778
779template<class GridView, class FluidSystem>
782maxWaterSaturation(unsigned globalDofIdx) const
783{
784 if (maxWaterSaturation_.empty())
785 return 0.0;
786
787 return maxWaterSaturation_[globalDofIdx];
788}
789
790template<class GridView, class FluidSystem>
793minOilPressure(unsigned globalDofIdx) const
794{
795 if (minRefPressure_.empty())
796 return 0.0;
797
798 return minRefPressure_[globalDofIdx];
799}
800
801template<class GridView, class FluidSystem>
804overburdenPressure(unsigned elementIdx) const
805{
806 if (overburdenPressure_.empty())
807 return 0.0;
808
809 return overburdenPressure_[elementIdx];
810}
811
812template<class GridView, class FluidSystem>
815solventSaturation(unsigned elemIdx) const
816{
817 if (solventSaturation_.empty())
818 return 0;
819
820 return solventSaturation_[elemIdx];
821}
822
823template<class GridView, class FluidSystem>
826solventRsw(unsigned elemIdx) const
827{
828 if (solventRsw_.empty())
829 return 0;
830
831 return solventRsw_[elemIdx];
832}
833
834
835
836template<class GridView, class FluidSystem>
839polymerConcentration(unsigned elemIdx) const
840{
841 if (polymer_.concentration.empty()) {
842 return 0;
843 }
844
845 return polymer_.concentration[elemIdx];
846}
847
848template<class GridView, class FluidSystem>
851polymerMolecularWeight(const unsigned elemIdx) const
852{
853 if (polymer_.moleWeight.empty()) {
854 return 0.0;
855 }
856
857 return polymer_.moleWeight[elemIdx];
858}
859
860template<class GridView, class FluidSystem>
863microbialConcentration(unsigned elemIdx) const
864{
865 if (bioeffects_.microbialConcentration.empty()) {
866 return 0;
867 }
868
869 return bioeffects_.microbialConcentration[elemIdx];
870}
871
872template<class GridView, class FluidSystem>
875oxygenConcentration(unsigned elemIdx) const
876{
877 if (bioeffects_.oxygenConcentration.empty()) {
878 return 0;
879 }
880
881 return bioeffects_.oxygenConcentration[elemIdx];
882}
883
884template<class GridView, class FluidSystem>
887ureaConcentration(unsigned elemIdx) const
888{
889 if (bioeffects_.ureaConcentration.empty()) {
890 return 0;
891 }
892
893 return bioeffects_.ureaConcentration[elemIdx];
894}
895
896template<class GridView, class FluidSystem>
899biofilmVolumeFraction(unsigned elemIdx) const
900{
901 if (bioeffects_.biofilmVolumeFraction.empty()) {
902 return 0;
903 }
904
905 return bioeffects_.biofilmVolumeFraction[elemIdx];
906}
907
908template<class GridView, class FluidSystem>
911calciteVolumeFraction(unsigned elemIdx) const
912{
913 if (bioeffects_.calciteVolumeFraction.empty()) {
914 return 0;
915 }
916
917 return bioeffects_.calciteVolumeFraction[elemIdx];
918}
919
920template<class GridView, class FluidSystem>
922pvtRegionIndex(unsigned elemIdx) const
923{
924 if (pvtnum_.empty())
925 return 0;
926
927 return pvtnum_[elemIdx];
928}
929
930template<class GridView, class FluidSystem>
932satnumRegionIndex(unsigned elemIdx) const
933{
934 if (satnum_.empty())
935 return 0;
936
937 return satnum_[elemIdx];
938}
939
940template<class GridView, class FluidSystem>
942miscnumRegionIndex(unsigned elemIdx) const
943{
944 if (miscnum_.empty())
945 return 0;
946
947 return miscnum_[elemIdx];
948}
949
950template<class GridView, class FluidSystem>
952plmixnumRegionIndex(unsigned elemIdx) const
953{
954 if (plmixnum_.empty())
955 return 0;
956
957 return plmixnum_[elemIdx];
958}
959
960template<class GridView, class FluidSystem>
963maxPolymerAdsorption(unsigned elemIdx) const
964{
965 if (polymer_.maxAdsorption.empty()) {
966 return 0;
967 }
968
969 return polymer_.maxAdsorption[elemIdx];
970}
971
972template<class GridView, class FluidSystem>
974operator==(const FlowGenericProblem& rhs) const
975{
976 return this->maxWaterSaturation_ == rhs.maxWaterSaturation_ &&
977 this->minRefPressure_ == rhs.minRefPressure_ &&
978 this->overburdenPressure_ == rhs.overburdenPressure_ &&
979 this->solventSaturation_ == rhs.solventSaturation_ &&
980 this->solventRsw_ == rhs.solventRsw_ &&
981 this->polymer_ == rhs.polymer_ &&
982 this->bioeffects_ == rhs.bioeffects_;
983}
984
985} // namespace Opm
986
987#endif // OPM_FLOW_GENERIC_PROBLEM_IMPL_HPP
Defines some fundamental parameters for all models.
This problem simulates an input file given in the data format used by the commercial ECLiPSE simulato...
Definition: FlowGenericProblem.hpp:61
UniformXTabulated2DFunction< Scalar > TabulatedTwoDFunction
Definition: FlowGenericProblem.hpp:64
Scalar maxPolymerAdsorption(unsigned elemIdx) const
Returns the max polymer adsorption value.
Definition: FlowGenericProblem_impl.hpp:963
unsigned pvtRegionIndex(unsigned elemIdx) const
Returns the index the relevant PVT region given a cell index.
Definition: FlowGenericProblem_impl.hpp:922
Scalar oxygenConcentration(unsigned elemIdx) const
Returns the initial oxygen concentration for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:875
Scalar microbialConcentration(unsigned elemIdx) const
Returns the initial microbial concentration for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:863
PolymerSolutionContainer< Scalar > polymer_
Definition: FlowGenericProblem.hpp:383
Scalar rockBiotTemp(unsigned elementIdx) const
Returns the rock compressibility of an element due to thermoelasticity.
Definition: FlowGenericProblem_impl.hpp:420
static std::string briefDescription()
Returns a human readable description of the problem for the help message.
Definition: FlowGenericProblem_impl.hpp:149
Scalar solventRsw(unsigned elemIdx) const
Returns the initial solvent dissolved in water for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:826
Scalar overburdenPressure(unsigned elementIdx) const
Get the pressure of the overburden.
Definition: FlowGenericProblem_impl.hpp:804
void updateMiscnum_()
Definition: FlowGenericProblem_impl.hpp:597
Scalar porosity(unsigned globalSpaceIdx, unsigned timeIdx) const
Direct indexed access to the porosity.
Definition: FlowGenericProblem_impl.hpp:401
Scalar rockCompressibility(unsigned globalSpaceIdx) const
Definition: FlowGenericProblem_impl.hpp:386
Scalar initialTimeStepSize_
Definition: FlowGenericProblem.hpp:395
unsigned miscnumRegionIndex(unsigned elemIdx) const
Returns the index the relevant MISC region given a cell index.
Definition: FlowGenericProblem_impl.hpp:942
bool vapparsActive(int episodeIdx) const
Definition: FlowGenericProblem_impl.hpp:613
unsigned satnumRegionIndex(unsigned elemIdx) const
Returns the index the relevant saturation function region given a cell index.
Definition: FlowGenericProblem_impl.hpp:932
Scalar maxWaterSaturation(unsigned globalDofIdx) const
Returns an element's historic maximum water phase saturation that was observed during the simulation.
Definition: FlowGenericProblem_impl.hpp:782
std::vector< Scalar > solventSaturation_
Definition: FlowGenericProblem.hpp:388
void readRockCompactionParameters_()
Definition: FlowGenericProblem_impl.hpp:246
void updateSatnum_()
Definition: FlowGenericProblem_impl.hpp:589
void updatePvtnum_()
Definition: FlowGenericProblem_impl.hpp:581
bool enableDriftCompensation_
Definition: FlowGenericProblem.hpp:401
Scalar lame(unsigned elementIdx) const
Direct access to Lame's second parameter in an element.
Definition: FlowGenericProblem_impl.hpp:432
static FlowGenericProblem serializationTestObject(const EclipseState &eclState, const Schedule &schedule, const GridView &gridView)
Definition: FlowGenericProblem_impl.hpp:113
void beginTimeStep_(bool enableExperiments, int episodeIdx, int timeStepIndex, Scalar startTime, Scalar time, Scalar timeStepSize, Scalar endTime)
Definition: FlowGenericProblem_impl.hpp:658
Scalar calciteVolumeFraction(unsigned elemIdx) const
Returns the initial calcite volume fraction for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:911
CO2H2SolutionContainer< Scalar > CO2H2_
Definition: FlowGenericProblem.hpp:391
unsigned plmixnumRegionIndex(unsigned elemIdx) const
Returns the index the relevant PLMIXNUM (for polymer module) region given a cell index.
Definition: FlowGenericProblem_impl.hpp:952
FlowGenericProblem(const EclipseState &eclState, const Schedule &schedule, const GridView &gridView)
Definition: FlowGenericProblem_impl.hpp:63
BioeffectsSolutionContainer< Scalar > bioeffects_
Definition: FlowGenericProblem.hpp:390
bool enableTuning_
Definition: FlowGenericProblem.hpp:394
Scalar solventSaturation(unsigned elemIdx) const
Returns the initial solvent saturation for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:815
Scalar polymerMolecularWeight(const unsigned elemIdx) const
Returns the polymer molecule weight for a given cell index.
Definition: FlowGenericProblem_impl.hpp:851
void readRockParameters_(const std::vector< Scalar > &cellCenterDepths, std::function< std::array< int, 3 >(const unsigned)> ijkIndex)
Definition: FlowGenericProblem_impl.hpp:156
std::vector< Scalar > maxOilSaturation_
Definition: FlowGenericProblem.hpp:384
Scalar rockBiotComp(unsigned elementIdx) const
Returns the rock compressibility of an element due to poroelasticity.
Definition: FlowGenericProblem_impl.hpp:409
int numPressurePointsEquil_
Definition: FlowGenericProblem.hpp:399
Scalar minOilPressure(unsigned globalDofIdx) const
Returns an element's historic minimum pressure of the oil phase that was observed during the simulati...
Definition: FlowGenericProblem_impl.hpp:793
std::vector< Scalar > maxWaterSaturation_
Definition: FlowGenericProblem.hpp:385
void initFluidSystem_()
Definition: FlowGenericProblem_impl.hpp:683
bool operator==(const FlowGenericProblem &rhs) const
Definition: FlowGenericProblem_impl.hpp:974
Scalar maxTimeStepAfterWellEvent_
Definition: FlowGenericProblem.hpp:396
Scalar ureaConcentration(unsigned elemIdx) const
Returns the initial urea concentration for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:887
std::vector< Scalar > minRefPressure_
Definition: FlowGenericProblem.hpp:386
bool beginEpisode_(bool enableExperiments, int episodeIdx)
Definition: FlowGenericProblem_impl.hpp:621
Scalar biofilmVolumeFraction(unsigned elemIdx) const
Returns the initial biofilm volume fraction for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:899
std::vector< Scalar > solventRsw_
Definition: FlowGenericProblem.hpp:389
static std::string helpPreamble(int, const char **argv)
Returns the string that is printed before the list of command line parameters in the help message.
Definition: FlowGenericProblem_impl.hpp:134
std::vector< Scalar > overburdenPressure_
Definition: FlowGenericProblem.hpp:387
void readBlackoilExtentionsInitialConditions_(std::size_t numDof, bool enableSolvent, bool enablePolymer, bool enablePolymerMolarWeight, bool enableBioeffects, bool enableMICP)
Definition: FlowGenericProblem_impl.hpp:690
Scalar biotTemp(unsigned elementIdx) const
Direct access to Biot temperature coefficient in an element.
Definition: FlowGenericProblem_impl.hpp:484
Scalar biotCoeff(unsigned elementIdx) const
Direct access to Biot coefficient in an element.
Definition: FlowGenericProblem_impl.hpp:463
void updatePlmixnum_()
Definition: FlowGenericProblem_impl.hpp:605
bool explicitRockCompaction_
Definition: FlowGenericProblem.hpp:403
typename FluidSystem::Scalar Scalar
Definition: FlowGenericProblem.hpp:63
Scalar polymerConcentration(unsigned elemIdx) const
Returns the initial polymer concentration for a given a cell index.
Definition: FlowGenericProblem_impl.hpp:839
Declare the properties used by the infrastructure code of the finite volume discretizations.
auto Get(bool errorIfNotRegistered=true)
Retrieve a runtime parameter.
Definition: parametersystem.hpp:192
Definition: blackoilbioeffectsmodules.hh:45
std::string to_string(const ConvergenceReport::ReservoirFailure::Type t)
This file provides the infrastructure to retrieve run-time parameters.
static BioeffectsSolutionContainer serializationTestObject()
static CO2H2SolutionContainer serializationTestObject()
Definition: FlowGenericProblem.hpp:67
Definition: EclTimeSteppingParams.hpp:49
static PolymerSolutionContainer serializationTestObject()