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