TemperatureModel.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*/
28#ifndef OPM_TEMPERATURE_MODEL_HPP
29#define OPM_TEMPERATURE_MODEL_HPP
30
31#include <opm/common/OpmLog/OpmLog.hpp>
32
34
38
39#include <array>
40#include <cstddef>
41#include <memory>
42#include <stdexcept>
43#include <string>
44#include <vector>
45
46namespace Opm::Properties {
47
48template<class TypeTag, class MyTypeTag>
51};
52
53} // namespace Opm::Properties
54
55namespace Opm {
56
57template<typename Scalar, typename IndexTraits> class WellState;
58
64template <class TypeTag, bool enableTempV = getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::SequentialImplicitThermal >
65class TemperatureModel : public GenericTemperatureModel<GetPropType<TypeTag, Properties::Grid>,
66 GetPropType<TypeTag, Properties::GridView>,
67 GetPropType<TypeTag, Properties::DofMapper>,
68 GetPropType<TypeTag, Properties::Stencil>,
69 GetPropType<TypeTag, Properties::FluidSystem>,
70 GetPropType<TypeTag, Properties::Scalar>>
71{
87 using TemperatureEvaluation = DenseAd::Evaluation<Scalar,1>;
92 using IndexTraits = typename FluidSystem::IndexTraitsType;
94
95 using EnergyMatrix = typename BaseType::EnergyMatrix;
96 using EnergyVector = typename BaseType::EnergyVector;
97
98 enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
99 enum { numPhases = FluidSystem::numPhases };
100 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
101 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
102 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
103
104public:
105 TemperatureModel(Simulator& simulator)
106 : BaseType(simulator.vanguard().gridView(),
107 simulator.vanguard().eclState(),
108 simulator.vanguard().cartesianIndexMapper(),
109 simulator.model().dofMapper())
110 , simulator_(simulator)
111 { }
112
113 void init()
114 {
115 const unsigned int numCells = simulator_.model().numTotalDof();
116 this->doInit(numCells);
117
118 if (!this->doTemp())
119 return;
120
121 // we need the storage term at start of the iteration (timeIdx = 1)
122 storage1_.resize(numCells);
123
124 // set the initial temperature
125 for (unsigned globI = 0; globI < numCells; ++globI) {
126 this->temperature_[globI] = simulator_.problem().initialFluidState(globI).temperature(0);
127 }
128 // keep a copy of the intensive quantities to simplify the update during
129 // the newton iterations
130 intQuants_.resize(numCells);
131
132 // find and store the overlap cells
133 const auto& elemMapper = simulator_.model().elementMapper();
135 }
136
138 {
139 if (!this->doTemp())
140 return;
141
142 // We copy the intensive quantities here to make it possible to update them
143 const unsigned int numCells = simulator_.model().numTotalDof();
144 for (unsigned globI = 0; globI < numCells; ++globI) {
145 intQuants_[globI] = simulator_.model().intensiveQuantities(globI, /*timeIdx*/ 0);
146 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
147 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
148 }
150
151 const int nw = simulator_.problem().wellModel().wellState().numWells();
152 this->energy_rates_.resize(nw, 0.0);
153 }
154
158 void endTimeStep(WellStateType& wellState)
159 {
160 if (!this->doTemp())
161 return;
162
163 // We copy the intensive quantities here to make it possible to update them
164 const unsigned int numCells = simulator_.model().numTotalDof();
165 for (unsigned globI = 0; globI < numCells; ++globI) {
166 intQuants_[globI] = simulator_.model().intensiveQuantities(globI, /*timeIdx*/ 0);
167 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
168 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
169 }
171
172 // update energy_rates
173 const int nw = wellState.numWells();
174 for (auto wellID = 0*nw; wellID < nw; ++wellID) {
175 auto& ws = wellState.well(wellID);
176 ws.energy_rate = this->energy_rates_[wellID];
177 }
178
179 }
180
185 template <class Restarter>
186 void serialize(Restarter&)
187 { /* not implemented */ }
188
195 template <class Restarter>
196 void deserialize(Restarter&)
197 { /* not implemented */ }
198
199protected:
200
202 {
203 // we need the storage term at start of the iteration (timeIdx = 1)
204 const unsigned int numCells = simulator_.model().numTotalDof();
205 #ifdef _OPENMP
206 #pragma omp parallel for
207 #endif
208 for (unsigned globI = 0; globI < numCells; ++globI) {
209 Scalar storage = 0.0;
210 computeStorageTerm(globI, storage);
211 storage1_[globI] = storage;
212 }
213 }
214
216 {
217 const int max_iter = 20;
218 const int min_iter = 1;
219 // solve using Newton
220 for (int iter = 0; iter < max_iter; ++iter) {
222 if (iter > min_iter && converged(iter)) {
223 break;
224 }
226 }
227 }
228
230 const unsigned int numCells = simulator_.model().numTotalDof();
231 EnergyVector dx(numCells);
232 bool converged = this->linearSolve_(*this->energyMatrix_, dx, this->energyVector_);
233 if (!converged) {
234 if (simulator_.gridView().comm().rank() == 0) {
235 OpmLog::warning("Temp model: Linear solver did not converge. Temperature values not updated.");
236 }
237 } else {
238 for (unsigned globI = 0; globI < numCells; ++globI) {
239 this->temperature_[globI] -= std::clamp(dx[globI][0], -this->maxTempChange_, this->maxTempChange_);
240 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
241 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
242 }
243 }
244 }
245
246 bool converged(const int iter) {
247 const unsigned int numCells = simulator_.model().numTotalDof();
248 Scalar maxNorm = 0.0;
249 Scalar sumNorm = 0.0;
250 for (unsigned globI = 0; globI < numCells; ++globI) {
251 maxNorm = max(maxNorm, std::abs(this->energyVector_[globI]));
252 sumNorm += std::abs(this->energyVector_[globI]);
253 }
254 maxNorm = simulator_.gridView().comm().sum(maxNorm);
255 sumNorm = simulator_.gridView().comm().sum(sumNorm);
256 const int globalNumCells = simulator_.gridView().comm().sum(numCells);
257 sumNorm /= globalNumCells;
258 const auto tolerance_cnv_energy = Parameters::Get<Parameters::ToleranceCnvEnergy<Scalar>>();
259 const auto tolerance_energy_balance = Parameters::Get<Parameters::ToleranceEnergyBalance<Scalar>>();
260 if (maxNorm < tolerance_cnv_energy || sumNorm < tolerance_energy_balance) {
261 const auto msg = fmt::format("Temperature model (TEMP): Newton converged after {} iterations", iter);
262 OpmLog::debug(msg);
263 return true;
264 }
265 return false;
266 }
267
268 template< class LhsEval>
269 void computeStorageTerm(unsigned globI, LhsEval& storage) {
270 const auto& intQuants = intQuants_[globI];
271 const auto& poro = decay<LhsEval>(intQuants.porosity());
272 // accumulate the internal energy of the fluids
273 const auto& fs = intQuants.fluidState();
274 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
275 if (!FluidSystem::phaseIsActive(phaseIdx))
276 continue;
277
278 const auto& u = decay<LhsEval>(fs.internalEnergy(phaseIdx));
279 const auto& S = decay<LhsEval>(fs.saturation(phaseIdx));
280 const auto& rho = decay<LhsEval>(fs.density(phaseIdx));
281
282 storage += poro*S*u*rho;
283 }
284
285 // add the internal energy of the rock
286 Scalar rockFraction = intQuants.rockFraction();
287 const auto& uRock = decay<LhsEval>(intQuants.rockInternalEnergy());
288 storage += rockFraction*uRock;
289 storage*= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
290 }
291
292 template < class ResidualNBInfo>
293 void computeFluxTerm(unsigned globI, unsigned globJ, const ResidualNBInfo& res_nbinfo, Evaluation& flux) {
294
295 const IntensiveQuantities& intQuantsIn = intQuants_[globI];
296 const IntensiveQuantities& intQuantsEx = intQuants_[globJ];
297 RateVector tmp(0.0); //not used
298 RateVector darcyFlux(0.0);
299 LocalResidual::computeFlux(tmp, darcyFlux, globI, globJ, intQuantsIn, intQuantsEx, res_nbinfo, simulator_.problem().moduleParams());
300 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
301 if (!FluidSystem::phaseIsActive(phaseIdx))
302 continue;
303
304 const unsigned activeCompIdx =
305 FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
306
307 bool inIsUp = darcyFlux[activeCompIdx] > 0;
308 const IntensiveQuantities& up = inIsUp ? intQuantsIn : intQuantsEx;
309 const auto& fs = up.fluidState();
310 if (inIsUp) {
311 flux += fs.enthalpy(phaseIdx)
312 * fs.density(phaseIdx)
313 * darcyFlux[activeCompIdx];
314 } else {
315 flux += getValue(fs.enthalpy(phaseIdx))
316 * getValue(fs.density(phaseIdx))
317 * getValue(darcyFlux[activeCompIdx]);
318 }
319 }
320 flux *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
321 }
322
323 template < class ResidualNBInfo>
324 void computeHeatFluxTerm(unsigned globI, unsigned globJ, const ResidualNBInfo& res_nbinfo, Evaluation& heatFlux) {
325 const IntensiveQuantities& intQuantsIn = intQuants_[globI];
326 const IntensiveQuantities& intQuantsEx = intQuants_[globJ];
327 const Scalar inAlpha = simulator_.problem().thermalHalfTransmissibility(globI, globJ);
328 const Scalar outAlpha = simulator_.problem().thermalHalfTransmissibility(globJ, globI);
329 short interiorDofIdx = 0; // NB
330 short exteriorDofIdx = 1; // NB
331 EnergyModule::ExtensiveQuantities::updateEnergy(heatFlux,
332 interiorDofIdx, // focusDofIndex,
333 interiorDofIdx,
334 exteriorDofIdx,
335 intQuantsIn,
336 intQuantsEx,
337 intQuantsIn.fluidState(),
338 intQuantsEx.fluidState(),
339 inAlpha,
340 outAlpha,
341 res_nbinfo.faceArea);
342 heatFlux *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>()*res_nbinfo.faceArea;
343 }
344
346 this->energyVector_ = 0.0;
347 (*this->energyMatrix_) = 0.0;
348 Scalar dt = simulator_.timeStepSize();
349 const unsigned int numCells = simulator_.model().numTotalDof();
350#ifdef _OPENMP
351#pragma omp parallel for
352#endif
353 for (unsigned globI = 0; globI < numCells; ++globI) {
354 Scalar volume = simulator_.model().dofTotalVolume(globI);
355 Scalar storefac = volume / dt;
356 Evaluation storage = 0.0;
357 computeStorageTerm(globI, storage);
358 this->energyVector_[globI] += storefac * ( getValue(storage) - storage1_[globI] );
359 (*this->energyMatrix_)[globI][globI][0][0] += storefac * storage.derivative(Indices::temperatureIdx);
360 }
361
362 const auto& neighborInfo = simulator_.model().linearizer().getNeighborInfo();
363#ifdef _OPENMP
364#pragma omp parallel for
365#endif
366 for (unsigned globI = 0; globI < numCells; ++globI) {
367 const auto& nbInfos = neighborInfo[globI];
368 for (const auto& nbInfo : nbInfos) {
369 unsigned globJ = nbInfo.neighbor;
370 assert(globJ != globI);
371
372 // compute convective flux
373 Evaluation flux = 0.0;
374 computeFluxTerm(globI, globJ, nbInfo.res_nbinfo, flux);
375 this->energyVector_[globI] += getValue(flux);
376 (*this->energyMatrix_)[globI][globI][0][0] += flux.derivative(Indices::temperatureIdx);
377 (*this->energyMatrix_)[globJ][globI][0][0] -= flux.derivative(Indices::temperatureIdx);
378
379 // compute conductive flux
380 Evaluation heatFlux = 0.0;
381 computeHeatFluxTerm(globI, globJ, nbInfo.res_nbinfo, heatFlux);
382 this->energyVector_[globI] += getValue(heatFlux);
383 (*this->energyMatrix_)[globI][globI][0][0] += heatFlux.derivative(Indices::temperatureIdx);
384 (*this->energyMatrix_)[globJ][globI][0][0] -= heatFlux.derivative(Indices::temperatureIdx);
385 }
386 }
387
388 // Well terms
389 const auto& wellPtrs = simulator_.problem().wellModel().localNonshutWells();
390 for (const auto& wellPtr : wellPtrs) {
391 this->assembleEquationWell(*wellPtr);
392 }
393
394 if (simulator_.gridView().comm().size() > 1) {
395 // Set dirichlet conditions for overlapping cells
396 // loop over precalculated overlap rows and columns
397 for (const auto row : overlapRows_)
398 {
399 // Zero out row.
400 (*this->energyMatrix_)[row] = 0.0;
401
402 //diagonal block set to diag(1.0).
403 (*this->energyMatrix_)[row][row][0][0] = 1.0;
404 }
405 }
406 }
407
408 template<class Well>
409 void assembleEquationWell(const Well& well)
410 {
411 const auto& eclWell = well.wellEcl();
412 std::size_t well_index = simulator_.problem().wellModel().wellState().index(well.name()).value();
413 const auto& ws = simulator_.problem().wellModel().wellState().well(well_index);
414 this->energy_rates_[well_index] = 0.0;
415 for (std::size_t i = 0; i < ws.perf_data.size(); ++i) {
416 const auto globI = ws.perf_data.cell_index[i];
417 auto fs = intQuants_[globI].fluidState(); //copy to make it possible to change the temp in the injector
418 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
419 if (!FluidSystem::phaseIsActive(phaseIdx))
420 continue;
421
422 Evaluation rate = well.volumetricSurfaceRateForConnection(globI, phaseIdx);
423 if (rate > 0 && eclWell.isInjector()) {
424 fs.setTemperature(eclWell.inj_temperature());
425 const auto& rho = FluidSystem::density(fs, phaseIdx, fs.pvtRegionIndex());
426 fs.setDensity(phaseIdx, rho);
427 const auto& h = FluidSystem::enthalpy(fs, phaseIdx, fs.pvtRegionIndex());
428 fs.setEnthalpy(phaseIdx, h);
429 rate *= getValue(fs.enthalpy(phaseIdx)) * getValue(fs.density(phaseIdx)) / getValue(fs.invB(phaseIdx));
430 } else {
431 const Evaluation d = 1.0 - fs.Rv() * fs.Rs();
432 if (phaseIdx == gasPhaseIdx && d > 0) {
433 const auto& oilrate = well.volumetricSurfaceRateForConnection(globI, oilPhaseIdx);
434 rate -= oilrate * getValue(fs.Rs());
435 rate /= d;
436 }
437 if (phaseIdx == oilPhaseIdx && d > 0) {
438 const auto& gasrate = well.volumetricSurfaceRateForConnection(globI, gasPhaseIdx);
439 rate -= gasrate * getValue(fs.Rv());
440 rate /= d;
441 }
442 rate *= fs.enthalpy(phaseIdx) * getValue(fs.density(phaseIdx)) / getValue(fs.invB(phaseIdx));
443 }
444 this->energy_rates_[well_index] += getValue(rate);
445 rate *= getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
446 this->energyVector_[globI] -= getValue(rate);
447 (*this->energyMatrix_)[globI][globI][0][0] -= rate.derivative(Indices::temperatureIdx);
448 }
449 }
450 }
451
452 const Simulator& simulator_;
453 EnergyVector storage1_;
454 std::vector<IntensiveQuantities> intQuants_;
455 std::vector<int> overlapRows_;
456 std::vector<int> interiorRows_;
457};
458
459
460// need for the old linearizer
461template <class TypeTag>
462class TemperatureModel<TypeTag, false> {
466public:
468 { }
469
474 template <class Restarter>
475 void serialize(Restarter&)
476 { /* not implemented */ }
477
484 template <class Restarter>
485 void deserialize(Restarter&)
486 { /* not implemented */ }
487
488 void init() {}
490 const Scalar temperature(size_t /*globalIdx*/) const {
491 return 273.15; // return 0C to make the compiler happy
492 }
493 //Scalar dummy_;
494};
495
496} // namespace Opm
497
498#endif // OPM_TEMPERATURE_MODEL_HPP
Contains the high level supplements required to extend the black oil model by energy.
Definition: blackoilenergymodules.hh:60
Definition: GenericTemperatureModel.hpp:55
void beginTimeStep()
Definition: TemperatureModel.hpp:489
void init()
Definition: TemperatureModel.hpp:488
const Scalar temperature(size_t) const
Definition: TemperatureModel.hpp:490
void deserialize(Restarter &)
This method restores the complete state of the temperature from disk.
Definition: TemperatureModel.hpp:485
void serialize(Restarter &)
This method writes the complete state of all temperature to the hard disk.
Definition: TemperatureModel.hpp:475
TemperatureModel(Simulator &)
Definition: TemperatureModel.hpp:467
A class which handles sequential implicit solution of the energy equation as specified in by TEMP.
Definition: TemperatureModel.hpp:71
void serialize(Restarter &)
This method writes the complete state of all temperature to the hard disk.
Definition: TemperatureModel.hpp:186
void assembleEquationWell(const Well &well)
Definition: TemperatureModel.hpp:409
std::vector< int > interiorRows_
Definition: TemperatureModel.hpp:456
EnergyVector storage1_
Definition: TemperatureModel.hpp:453
bool converged(const int iter)
Definition: TemperatureModel.hpp:246
void computeHeatFluxTerm(unsigned globI, unsigned globJ, const ResidualNBInfo &res_nbinfo, Evaluation &heatFlux)
Definition: TemperatureModel.hpp:324
void computeStorageTerm(unsigned globI, LhsEval &storage)
Definition: TemperatureModel.hpp:269
void deserialize(Restarter &)
This method restores the complete state of the temperature from disk.
Definition: TemperatureModel.hpp:196
void endTimeStep(WellStateType &wellState)
Informs the temperature model that a time step has just been finished.
Definition: TemperatureModel.hpp:158
const Simulator & simulator_
Definition: TemperatureModel.hpp:452
void beginTimeStep()
Definition: TemperatureModel.hpp:137
void advanceTemperatureFields()
Definition: TemperatureModel.hpp:215
void updateStorageCache()
Definition: TemperatureModel.hpp:201
void assembleEquations()
Definition: TemperatureModel.hpp:345
void solveAndUpdate()
Definition: TemperatureModel.hpp:229
std::vector< IntensiveQuantities > intQuants_
Definition: TemperatureModel.hpp:454
void computeFluxTerm(unsigned globI, unsigned globJ, const ResidualNBInfo &res_nbinfo, Evaluation &flux)
Definition: TemperatureModel.hpp:293
std::vector< int > overlapRows_
Definition: TemperatureModel.hpp:455
TemperatureModel(Simulator &simulator)
Definition: TemperatureModel.hpp:105
void init()
Definition: TemperatureModel.hpp:113
Definition: WellState.hpp:66
int numWells() const
Definition: WellState.hpp:99
const SingleWellState< Scalar, IndexTraits > & well(std::size_t well_index) const
Definition: WellState.hpp:290
Provides data handles for parallel communication which operate on DOFs.
Definition: blackoilmodel.hh:80
void findOverlapAndInterior(const Grid &grid, const Mapper &mapper, std::vector< int > &overlapRows, std::vector< int > &interiorRows)
Find the rows corresponding to overlap cells.
Definition: findOverlapRowsAndColumns.hpp:92
Definition: blackoilbioeffectsmodules.hh:43
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
The Opm property system, traits with inheritance.
Definition: TemperatureModel.hpp:49
a tag to mark properties as undefined
Definition: propertysystem.hh:38