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#include <opm/common/utility/gpuDecorators.hpp>
33
36
43
44#include <algorithm>
45#include <array>
46#include <cassert>
47#include <cmath>
48#include <cstddef>
49#include <memory>
50#include <limits>
51#include <set>
52#include <vector>
53
54namespace Opm::Properties {
55
56template<class TypeTag, class MyTypeTag>
59};
60
61} // namespace Opm::Properties
62
63namespace Opm {
64
65template<typename Scalar, typename IndexTraits> class WellState;
66
67
68template <class TypeTag>
70{
77
78 static constexpr bool enableBrine = getPropValue<TypeTag, Properties::EnableBrine>();
79 enum { enableVapwat = getPropValue<TypeTag, Properties::EnableVapwat>() };
80 enum { enableDisgasInWater = getPropValue<TypeTag, Properties::EnableDisgasInWater>() };
81 enum { enableSaltPrecipitation = getPropValue<TypeTag, Properties::EnableSaltPrecipitation>() };
82 static constexpr bool enableSolvent = getPropValue<TypeTag, Properties::EnableSolvent>();
83 enum { numPhases = getPropValue<TypeTag, Properties::NumPhases>() };
84 static constexpr bool compositionSwitchEnabled =
85 Indices::compositionSwitchIdx != std::numeric_limits<unsigned>::max();
86
87 public:
88 using EvaluationTemp = DenseAd::Evaluation<Scalar, 1>;
89 using FluidStateTemp = BlackOilFluidState<EvaluationTemp,
90 FluidSystem,
91 true, //store temperature
92 true, // store enthalpy
93 compositionSwitchEnabled,
94 enableVapwat,
95 enableBrine,
96 enableSaltPrecipitation,
97 enableDisgasInWater,
98 enableSolvent,
99 Indices::numPhases>;
100
101
102
103 OPM_HOST_DEVICE void updateTemperature_(const Problem& problem,
104 unsigned globalDofIdx,
105 unsigned timeIdx)
106 {
107 const EvaluationTemp T = EvaluationTemp::createVariable(problem.temperature(globalDofIdx, timeIdx), 0);
108 fluidState_.setTemperature(T);
109 }
110
111 OPM_HOST_DEVICE void updateEnergyQuantities_(const Problem& problem,
112 const unsigned globalSpaceIdx,
113 const unsigned timeIdx)
114 {
115 // compute the specific enthalpy of the fluids, the specific enthalpy of the rock
116 // and the thermal conductivity coefficients
117 for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
118 if (!FluidSystem::phaseIsActive(phaseIdx)) {
119 continue;
120 }
121
122 const auto& h = FluidSystem::enthalpy(fluidState_, phaseIdx, problem.pvtRegionIndex(globalSpaceIdx));
123 fluidState_.setEnthalpy(phaseIdx, h);
124 }
125
126 const auto& solidEnergyLawParams = problem.solidEnergyLawParams(globalSpaceIdx, timeIdx);
127 rockInternalEnergy_ = SolidEnergyLaw::solidInternalEnergy(solidEnergyLawParams, fluidState_);
128
129 const auto& thermalConductionLawParams = problem.thermalConductionLawParams(globalSpaceIdx, timeIdx);
130 totalThermalConductivity_ = ThermalConductionLaw::thermalConductivity(thermalConductionLawParams, fluidState_);
131
132 // Retrieve the rock fraction from the problem
133 // Usually 1 - porosity, but if pvmult is used to modify porosity
134 // we will apply the same multiplier to the rock fraction
135 // i.e. pvmult*(1 - porosity) and thus interpret multpv as a volume
136 // multiplier. This is to avoid negative rock volume for pvmult*porosity > 1
137 rockFraction_ = problem.rockFraction(globalSpaceIdx, timeIdx);
138 }
139
140 OPM_HOST_DEVICE const EvaluationTemp& rockInternalEnergy() const
141 { return rockInternalEnergy_; }
142
143 OPM_HOST_DEVICE const EvaluationTemp& totalThermalConductivity() const
144 { return totalThermalConductivity_; }
145
146 OPM_HOST_DEVICE const Scalar& rockFraction() const
147 { return rockFraction_; }
148
149 OPM_HOST_DEVICE const FluidStateTemp& fluidStateTemp() const
150 { return fluidState_; }
151
152 template <class FluidState>
153 OPM_HOST_DEVICE void setFluidState(const FluidState& fs)
154 {
155 // copy the needed part of the fluid state
156 fluidState_.setPvtRegionIndex(fs.pvtRegionIndex());
157 fluidState_.setRs(getValue(fs.Rs()));
158 fluidState_.setRv(getValue(fs.Rv()));
159 for (int phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
160 fluidState_.setPressure(phaseIdx, getValue(fs.pressure(phaseIdx)));
161 fluidState_.setDensity(phaseIdx, getValue(fs.density(phaseIdx)));
162 fluidState_.setSaturation(phaseIdx, getValue(fs.saturation(phaseIdx)));
163 fluidState_.setInvB(phaseIdx, getValue(fs.invB(phaseIdx)));
164 }
165 }
166
167protected:
172};
173
179template <class TypeTag, bool enableTempV = getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::SequentialImplicitThermal >
180class TemperatureModel : public GenericTemperatureModel<GetPropType<TypeTag, Properties::Grid>,
181 GetPropType<TypeTag, Properties::GridView>,
182 GetPropType<TypeTag, Properties::DofMapper>,
183 GetPropType<TypeTag, Properties::Stencil>,
184 GetPropType<TypeTag, Properties::FluidSystem>,
185 GetPropType<TypeTag, Properties::Scalar>>
186{
203 static constexpr EnergyModules energyModuleType = getPropValue<TypeTag, Properties::EnergyModuleType>();
205 using IndexTraits = typename FluidSystem::IndexTraitsType;
207
209 using FluidStateTemp = typename IntensiveQuantitiesTemp::FluidStateTemp;
210 using Evaluation = typename IntensiveQuantitiesTemp::EvaluationTemp;
211 using EnergyMatrix = typename BaseType::EnergyMatrix;
212 using EnergyVector = typename BaseType::EnergyVector;
213 using MatrixBlockTemp = typename BaseType::MatrixBlockTemp;
214 using SpareMatrixEnergyAdapter = typename Linear::IstlSparseMatrixAdapter<MatrixBlockTemp>;
215
216
217 //using ResidualNBInfo = typename LocalResidual::ResidualNBInfo;
218 struct ResidualNBInfo
219 {
220 double faceArea;
221 double inAlpha;
222 double outAlpha;
223 };
224
226
227 enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
228 enum { numPhases = FluidSystem::numPhases };
229 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx };
230 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx };
231 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx };
232 static constexpr unsigned temperatureIdx = 0;
233
234public:
235 explicit TemperatureModel(Simulator& simulator)
236 : BaseType(simulator.vanguard().gridView(),
237 simulator.vanguard().eclState(),
238 simulator.vanguard().cartesianIndexMapper(),
239 simulator.model().dofMapper())
240 , simulator_(simulator)
241 {}
242
243 static TemperatureModel serializationTestObject(Simulator& simulator)
244 {
245 TemperatureModel result(simulator);
246 result.doInit(3);
247 result.temperature_ = {281.0, 282.0, 283.0};
248 return result;
249 }
250
251 bool operator==(const TemperatureModel& other) const
252 {
253 return this->temperature_ == other.temperature_;
254 }
255
256 void init()
257 {
258 const unsigned int numCells = simulator_.model().numTotalDof();
259 this->doInit(numCells);
260
261 if (!this->doTemp())
262 return;
263
264 // we need the storage term at start of the iteration (timeIdx = 1)
265 storage1_.resize(numCells);
266
267 // set the initial temperature
268 for (unsigned globI = 0; globI < numCells; ++globI) {
269 this->temperature_[globI] = simulator_.problem().initialFluidState(globI).temperature(0);
270 }
271 // keep a copy of the intensive quantities to simplify the update during
272 // the newton iterations
273 intQuants_.resize(numCells);
274
275 // find and store the overlap cells
276 const auto& elemMapper = simulator_.model().elementMapper();
278
279 // set the scaling factor
280 scalingFactor_ = getPropValue<TypeTag, Properties::BlackOilEnergyScalingFactor>();
281
282 // find the sparsity pattern of the temperature matrix
283 using NeighborSet = std::set<unsigned>;
284 std::vector<NeighborSet> neighbors(numCells);
285 Stencil stencil(this->gridView_, this->dofMapper_);
286 neighborInfo_.reserve(numCells, 6 * numCells);
287 std::vector<NeighborInfoCPU> loc_nbinfo;
288 for (const auto& elem : elements(this->gridView_)) {
289 stencil.update(elem);
290 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
291 const unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
292 loc_nbinfo.resize(stencil.numDof() - 1); // Do not include the primary dof in neighborInfo_
293 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
294 const unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
295 neighbors[myIdx].insert(neighborIdx);
296 if (dofIdx > 0) {
297 const auto scvfIdx = dofIdx - 1;
298 const auto& scvf = stencil.interiorFace(scvfIdx);
299 const Scalar area = scvf.area();
300 Scalar inAlpha = simulator_.problem().thermalHalfTransmissibility(myIdx, neighborIdx);
301 Scalar outAlpha = simulator_.problem().thermalHalfTransmissibility(neighborIdx, myIdx);
302 ResidualNBInfo nbinfo{area, inAlpha, outAlpha};
303 loc_nbinfo[dofIdx - 1] = NeighborInfoCPU{neighborIdx, nbinfo, nullptr};
304 }
305 }
306 neighborInfo_.appendRow(loc_nbinfo.begin(), loc_nbinfo.end());
307 }
308 }
309 // allocate matrix for storing the Jacobian of the temperature residual
310 energyMatrix_ = std::make_unique<SpareMatrixEnergyAdapter>(simulator_);
311 diagMatAddress_.resize(numCells);
312 energyMatrix_->reserve(neighbors);
313 for (unsigned globI = 0; globI < numCells; globI++) {
314 const auto& nbInfos = neighborInfo_[globI];
315 diagMatAddress_[globI] = energyMatrix_->blockAddress(globI, globI);
316 for (auto& nbInfo : nbInfos) {
317 nbInfo.matBlockAddress = energyMatrix_->blockAddress(nbInfo.neighbor, globI);
318 }
319 }
320 }
321
323 {
324 if (!this->doTemp()) {
325 return;
326 }
327
328 // We use the specialized intensive quantities here with only the temperature derivative
329 const unsigned int numCells = simulator_.model().numTotalDof();
330 #ifdef _OPENMP
331 #pragma omp parallel for
332 #endif
333 for (unsigned globI = 0; globI < numCells; ++globI) {
334 intQuants_[globI].setFluidState(simulator_.model().intensiveQuantities(globI, /*timeIdx*/ 0).fluidState());
335 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
336 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
337 }
339
340 const int nw = simulator_.problem().wellModel().wellState().numWells();
341 this->energy_rates_.resize(nw, 0.0);
342 }
343
347 void endTimeStep(WellStateType& wellState)
348 {
349 if (!this->doTemp()) {
350 return;
351 }
352 OPM_TIMEBLOCK(TemperatureModel_endTimeStep);
353
354 // We use the specialized intensive quantities here with only the temperature derivative
355 const unsigned int numCells = simulator_.model().numTotalDof();
356 #ifdef _OPENMP
357 #pragma omp parallel for
358 #endif
359 for (unsigned globI = 0; globI < numCells; ++globI) {
360 intQuants_[globI].setFluidState(simulator_.model().intensiveQuantities(globI, /*timeIdx*/ 0).fluidState());
361 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
362 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
363 }
365
366 // update energy_rates
367 const int nw = wellState.numWells();
368 for (auto wellID = 0*nw; wellID < nw; ++wellID) {
369 auto& ws = wellState.well(wellID);
370 ws.energy_rate = this->energy_rates_[wellID];
371 }
372
373 // Update well temperature
374 const auto& wellPtrs = simulator_.problem().wellModel().localNonshutWells();
375 for (const auto& wellPtr : wellPtrs) {
376 auto& ws = wellState.well(wellPtr->name());
377 this->computeWellTemperature(*wellPtr, ws);
378 }
379 }
380
385 template <class Restarter>
386 void serialize(Restarter&)
387 { /* not implemented */ }
388
395 template <class Restarter>
396 void deserialize(Restarter&)
397 { /* not implemented */ }
398
399 template<class Serializer>
400 void serializeOp(Serializer& serializer)
401 {
402 // Only dynamic state is serialized. Geometry-derived caches and
403 // solver/work arrays are rebuilt from the current simulator state.
404 serializer(this->temperature_);
405 }
406
407protected:
409 {
410 // we need the storage term at start of the iteration (timeIdx = 1)
411 const unsigned int numCells = simulator_.model().numTotalDof();
412 #ifdef _OPENMP
413 #pragma omp parallel for
414 #endif
415 for (unsigned globI = 0; globI < numCells; ++globI) {
416 Scalar storage = 0.0;
417 computeStorageTerm(globI, storage);
418 storage1_[globI] = storage;
419 }
420 }
421
423 {
424 OPM_TIMEBLOCK(TemperatureModel_advanceTemperatureFields);
425 const int max_iter = 20;
426 const int min_iter = 1;
427 bool is_converged = false;
428 // solve using Newton
429 for (int iter = 0; iter < max_iter; ++iter) {
431 if (iter >= min_iter && converged(iter)) {
432 is_converged = true;
433 break;
434 }
436 }
437 if (!is_converged) {
438 const auto msg =
439 fmt::format(fmt::runtime("Temperature model (TEMP): Newton did not converge after {} iterations. \n"
440 "The Simulator will continue to the next step with an unconverged solution."),
441 max_iter);
442 OpmLog::debug(msg);
443 }
444 }
445
447 {
448 OPM_TIMEBLOCK(TemperatureModel_solveAndUpdate);
449 const unsigned int numCells = simulator_.model().numTotalDof();
450 EnergyVector dx(numCells);
451 bool conv = this->linearSolve_(this->energyMatrix_->istlMatrix(), dx, this->energyVector_);
452 if (!conv) {
453 if (simulator_.gridView().comm().rank() == 0) {
454 OpmLog::warning("Temp model: Linear solver did not converge. Temperature values not updated.");
455 }
456 }
457 else {
458 OPM_TIMEBLOCK(TemperatureModel_solveAndUpdate_update);
459 #ifdef _OPENMP
460 #pragma omp parallel for
461 #endif
462 for (unsigned globI = 0; globI < numCells; ++globI) {
463 this->temperature_[globI] -= std::clamp(dx[globI][0], -this->maxTempChange_, this->maxTempChange_);
464 intQuants_[globI].updateTemperature_(simulator_.problem(), globI, /*timeIdx*/ 0);
465 intQuants_[globI].updateEnergyQuantities_(simulator_.problem(), globI, /*timeIdx*/ 0);
466 }
467 }
468 }
469
470 bool converged(const int iter)
471 {
472 OPM_TIMEBLOCK(TemperatureModel_converged);
473 Scalar dt = simulator_.timeStepSize();
474 Scalar maxNorm = 0.0;
475 Scalar sumNorm = 0.0;
476 const auto tolerance_cnv_energy_strict = Parameters::Get<Parameters::ToleranceCnvEnergy<Scalar>>();
477 const auto& elemMapper = simulator_.model().elementMapper();
478 const IsNumericalAquiferCell isNumericalAquiferCell(simulator_.gridView().grid());
479 Scalar sum_pv = 0.0;
480 Scalar sum_pv_not_converged = 0.0;
481 for (const auto& elem : elements(simulator_.gridView(), Dune::Partitions::interior)) {
482 unsigned globI = elemMapper.index(elem);
483 const auto pvValue = simulator_.problem().referencePorosity(globI, /*timeIdx=*/0)
484 * simulator_.model().dofTotalVolume(globI);
485
486 const Scalar scaled_norm = dt * std::abs(this->energyVector_[globI][0])/ pvValue;
487 maxNorm = max(maxNorm, scaled_norm);
488 sumNorm += scaled_norm;
489 if (!isNumericalAquiferCell(elem)) {
490 if (scaled_norm > tolerance_cnv_energy_strict) {
491 sum_pv_not_converged += pvValue;
492 }
493 sum_pv += pvValue;
494 }
495 }
496 {
497 OPM_TIMEBLOCK(TemperatureModel_converged_communicate);
498
499 maxNorm = simulator_.gridView().comm().max(maxNorm);
500 sumNorm = simulator_.gridView().comm().sum(sumNorm);
501 sum_pv = simulator_.gridView().comm().sum(sum_pv);
502 sumNorm /= sum_pv;
503
504 // Use relaxed tolerance if the fraction of unconverged cells porevolume is less than relaxed_max_pv_fraction
505 sum_pv_not_converged = simulator_.gridView().comm().sum(sum_pv_not_converged);
506 }
507 Scalar relaxed_max_pv_fraction = Parameters::Get<Parameters::RelaxedMaxPvFraction<Scalar>>();
508 const bool relax = (sum_pv_not_converged / sum_pv) < relaxed_max_pv_fraction;
509 const auto tolerance_energy_balance = relax? Parameters::Get<Parameters::ToleranceEnergyBalanceRelaxed<Scalar>>():
511 const bool tolerance_cnv_energy = relax? Parameters::Get<Parameters::ToleranceCnvEnergyRelaxed<Scalar>>():
512 tolerance_cnv_energy_strict;
513
514 const auto msg = fmt::format(fmt::runtime("Temperature model (TEMP): Newton iter {}: "
515 "CNV(E): {:.1e}, EB: {:.1e}"),
516 iter, maxNorm, sumNorm);
517 OpmLog::debug(msg);
518 if (maxNorm < tolerance_cnv_energy && sumNorm < tolerance_energy_balance) {
519 const auto msg2 =
520 fmt::format(fmt::runtime("Temperature model (TEMP): Newton converged after {} iterations"),
521 iter);
522 OpmLog::debug(msg2);
523 return true;
524 }
525 return false;
526 }
527
528 template< class LhsEval>
529 void computeStorageTerm(unsigned globI, LhsEval& storage)
530 {
531 const auto& intQuants = intQuants_[globI];
532 const auto& poro = getValue(simulator_.model().intensiveQuantities(globI, /*timeIdx*/ 0).porosity());
533 // accumulate the internal energy of the fluids
534 const auto& fs = intQuants.fluidStateTemp();
535 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
536 if (!FluidSystem::phaseIsActive(phaseIdx)) {
537 continue;
538 }
539
540 const auto& u = decay<LhsEval>(fs.internalEnergy(phaseIdx));
541 const auto& S = decay<LhsEval>(fs.saturation(phaseIdx));
542 const auto& rho = decay<LhsEval>(fs.density(phaseIdx));
543
544 storage += poro*S*u*rho;
545 }
546
547 // add the internal energy of the rock
548 const Scalar rockFraction = intQuants.rockFraction();
549 const auto& uRock = decay<LhsEval>(intQuants.rockInternalEnergy());
550 storage += rockFraction*uRock;
551 storage*= scalingFactor_;
552 }
553
554 template <class RateVector>
555 void computeFluxTerm(const FluidStateTemp& fsIn,
556 const FluidStateTemp& fsEx,
557 const RateVector& darcyFlux,
558 Evaluation& flux)
559 {
560 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
561 if (!FluidSystem::phaseIsActive(phaseIdx)) {
562 continue;
563 }
564
565 const unsigned activeCompIdx =
566 FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
567 bool inIsUp = darcyFlux[activeCompIdx] > 0;
568 const auto& fs = inIsUp ? fsIn : fsEx;
569 if (inIsUp) {
570 flux += fs.enthalpy(phaseIdx)
571 * fs.density(phaseIdx)
572 * getValue(darcyFlux[activeCompIdx]);
573 }
574 else {
575 flux += getValue(fs.enthalpy(phaseIdx))
576 * getValue(fs.density(phaseIdx))
577 * getValue(darcyFlux[activeCompIdx]);
578 }
579 }
580 flux *= scalingFactor_;
581 }
582
583 template <class ResidualNBInfo>
585 const IntensiveQuantitiesTemp& intQuantsEx,
586 const ResidualNBInfo& res_nbinfo,
587 Evaluation& heatFlux)
588 {
589 short interiorDofIdx = 0; // NB
590 short exteriorDofIdx = 1; // NB
592 updateEnergy(heatFlux,
593 interiorDofIdx, // focusDofIndex,
594 interiorDofIdx,
595 exteriorDofIdx,
596 intQuantsIn,
597 intQuantsEx,
598 intQuantsIn.fluidStateTemp(),
599 intQuantsEx.fluidStateTemp(),
600 res_nbinfo.inAlpha,
601 res_nbinfo.outAlpha,
602 res_nbinfo.faceArea);
603 heatFlux *= scalingFactor_*res_nbinfo.faceArea;
604 }
605
607 {
608 OPM_TIMEBLOCK(TemperatureModel_assembleEquations);
609
610 const unsigned int numCells = simulator_.model().numTotalDof();
611 for (unsigned globI = 0; globI < numCells; ++globI) {
612 this->energyVector_[globI] = 0.0;
613 energyMatrix_->clearRow(globI, 0.0);
614 }
615 const Scalar dt = simulator_.timeStepSize();
616
617 // Storage term
618 {
619 OPM_TIMEBLOCK(TemperatureModel_assembleEquations_storage);
620 #ifdef _OPENMP
621 #pragma omp parallel for
622 #endif
623 for (unsigned globI = 0; globI < numCells; ++globI) {
624 MatrixBlockTemp bMat;
625 Scalar volume = simulator_.model().dofTotalVolume(globI);
626 Scalar storefac = volume / dt;
627 Evaluation storage = 0.0;
628 computeStorageTerm(globI, storage);
629 this->energyVector_[globI][0] += storefac * ( getValue(storage) - storage1_[globI][0] );
630 bMat[0][0] = storefac * storage.derivative(temperatureIdx);
631 *diagMatAddress_[globI] += bMat;
632 }
633 }
634
635 // Flux term
636 {
637 OPM_TIMEBLOCK(TemperatureModel_assembleEquations_flux);
638 const auto& floresInfo = this->simulator_.problem().model().linearizer().getFloresInfo();
639 const bool enableDriftCompensation = Parameters::Get<Parameters::EnableDriftCompensationTemp>();
640 const auto& problem = simulator_.problem();
641
642 #ifdef _OPENMP
643 #pragma omp parallel for
644 #endif
645 for (unsigned globI = 0; globI < numCells; ++globI) {
646 const auto& nbInfos = neighborInfo_[globI];
647 const auto& floresInfos = floresInfo[globI];
648 int loc = 0;
649 const auto& intQuantsIn = intQuants_[globI];
650 MatrixBlockTemp bMat;
651 for (const auto& nbInfo : nbInfos) {
652 unsigned globJ = nbInfo.neighbor;
653 const auto& intQuantsEx = intQuants_[globJ];
654 assert(globJ != globI);
655 const auto& darcyflux = floresInfos[loc].flow;
656 // compute convective flux
657 Evaluation flux = 0.0;
658 computeFluxTerm(intQuantsIn.fluidStateTemp(), intQuantsEx.fluidStateTemp(), darcyflux, flux);
659 // compute conductive flux
660 Evaluation heatFlux = 0.0;
661 computeHeatFluxTerm(intQuantsIn, intQuantsEx, nbInfo.res_nbinfo, heatFlux);
662 heatFlux += flux;
663 this->energyVector_[globI][0] += getValue(heatFlux);
664 bMat[0][0] = heatFlux.derivative(temperatureIdx);
665 *diagMatAddress_[globI] += bMat;
666 bMat *= -1.0;
667 //SparseAdapter syntax: jacobian_->addToBlock(globJ, globI, bMat);
668 *nbInfo.matBlockAddress += bMat;
669 loc++;
670 }
671
672 if (enableDriftCompensation) {
673 auto dofDriftRate = problem.drift()[globI]/dt;
674 const auto& fs = intQuantsIn.fluidStateTemp();
675 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
676 const unsigned activeCompIdx =
677 FluidSystem::canonicalToActiveCompIdx(FluidSystem::solventComponentIndex(phaseIdx));
678 auto drift_hrate = dofDriftRate[activeCompIdx]*getValue(fs.enthalpy(phaseIdx)) * getValue(fs.density(phaseIdx)) / getValue(fs.invB(phaseIdx));
679 this->energyVector_[globI] -= drift_hrate*scalingFactor_;
680 }
681 }
682 }
683 }
684
685 // Well terms
686 {
687 OPM_TIMEBLOCK(TemperatureModel_assembleEquations_wells);
688 const auto& wellPtrs = simulator_.problem().wellModel().localNonshutWells();
689 for (const auto& wellPtr : wellPtrs) {
690 this->assembleEquationWell(*wellPtr);
691 }
692 }
693
694 // For standard ilu0 we need to set the overlapping cells to identity. But since we use "paroverilu0"
695 // here this is not needed.
696 // if (simulator_.gridView().comm().size() > 1) {
697 // // Set dirichlet conditions for overlapping cells
698 // // loop over precalculated overlap rows and columns
699 // for (const auto row : overlapRows_) {
700 // // Zero out row.
701 // (*this->energyMatrix_)[row] = 0.0;
702 //
703 // //diagonal block set to diag(1.0).
704 // (*this->energyMatrix_)[row][row][0][0] = 1.0;
705 // }
706 //}
707 }
708
709 template<class Well>
710 void assembleEquationWell(const Well& well)
711 {
712 const auto& eclWell = well.wellEcl();
713 std::size_t well_index = simulator_.problem().wellModel().wellState().index(well.name()).value();
714 const auto& ws = simulator_.problem().wellModel().wellState().well(well_index);
715 this->energy_rates_[well_index] = 0.0;
716 MatrixBlockTemp bMat;
717 for (std::size_t i = 0; i < ws.perf_data.size(); ++i) {
718 const auto globI = ws.perf_data.cell_index[i];
719 auto fs = intQuants_[globI].fluidStateTemp(); //copy to make it possible to change the temp in the injector
720 for (unsigned phaseIdx = 0; phaseIdx < numPhases; ++ phaseIdx) {
721 if (!FluidSystem::phaseIsActive(phaseIdx)) {
722 continue;
723 }
724
725 Evaluation rate = well.volumetricSurfaceRateForConnection(globI, phaseIdx);
726 if (rate > 0 && eclWell.isInjector()) {
727 fs.setTemperature(eclWell.inj_temperature());
728 const auto& rho = FluidSystem::density(fs, phaseIdx, fs.pvtRegionIndex());
729 fs.setDensity(phaseIdx, rho);
730 const auto& h = FluidSystem::enthalpy(fs, phaseIdx, fs.pvtRegionIndex());
731 fs.setEnthalpy(phaseIdx, h);
732 rate *= getValue(fs.enthalpy(phaseIdx)) * getValue(fs.density(phaseIdx)) / getValue(fs.invB(phaseIdx));
733 } else {
734 const Evaluation d = 1.0 - fs.Rv() * fs.Rs();
735 if (phaseIdx == gasPhaseIdx && d > 0) {
736 const auto& oilrate = well.volumetricSurfaceRateForConnection(globI, oilPhaseIdx);
737 rate -= oilrate * getValue(fs.Rs());
738 rate /= d;
739 }
740 if (phaseIdx == oilPhaseIdx && d > 0) {
741 const auto& gasrate = well.volumetricSurfaceRateForConnection(globI, gasPhaseIdx);
742 rate -= gasrate * getValue(fs.Rv());
743 rate /= d;
744 }
745 rate *= fs.enthalpy(phaseIdx) * getValue(fs.density(phaseIdx)) / getValue(fs.invB(phaseIdx));
746 }
747 this->energy_rates_[well_index] += getValue(rate);
748 rate *= scalingFactor_;
749 this->energyVector_[globI] -= getValue(rate);
750 bMat[0][0] = -rate.derivative(temperatureIdx);
751 *diagMatAddress_[globI] += bMat;
752 }
753 }
754 }
755
756 template<class Well, class SingleWellState>
757 void computeWellTemperature(const Well& well, SingleWellState& ws)
758 {
759 if (well.isInjector()) {
760 if (ws.status != WellStatus::STOP) {
761 ws.temperature = well.wellEcl().inj_temperature();
762 return;
763 }
764 }
765 const int np = simulator_.problem().wellModel().wellState().numPhases();
766 std::array<Scalar,2> weighted{0.0,0.0};
767 auto& [weighted_temperature, total_weight] = weighted;
768 for (std::size_t i = 0; i < ws.perf_data.size(); ++i) {
769 const auto globI = ws.perf_data.cell_index[i];
770 const auto& fs = intQuants_[globI].fluidStateTemp();
771 Scalar weight_factor = simulator_.problem().wellModel().computeTemperatureWeightFactor(i, np, fs, ws);
772 total_weight += weight_factor;
773 weighted_temperature += weight_factor * fs.temperature(/*phaseIdx*/0).value();
774 }
775 //simulator_.gridView().comm().sum(weighted.data(), 2);
776 ws.temperature = weighted_temperature / total_weight;
777 }
778
779 const Simulator& simulator_;
780 EnergyVector storage1_;
781 std::vector<IntensiveQuantitiesTemp> intQuants_;
783 std::vector<MatrixBlockTemp*> diagMatAddress_{};
784 std::unique_ptr<SpareMatrixEnergyAdapter> energyMatrix_;
785 std::vector<int> overlapRows_;
786 std::vector<int> interiorRows_;
787 Scalar scalingFactor_{1.0};
788};
789
790// need for the old linearizer
791template <class TypeTag>
792class TemperatureModel<TypeTag, false>
793{
797
798public:
800 { }
801
806 template <class Restarter>
807 void serialize(Restarter&)
808 { /* not implemented */ }
809
816 template <class Restarter>
817 void deserialize(Restarter&)
818 { /* not implemented */ }
819
820 template<class Serializer>
821 void serializeOp(Serializer&)
822 {
823 // No dynamic state for the disabled temperature model variant.
824 }
825
826 void init() {}
828 const Scalar temperature(size_t /*globalIdx*/) const
829 {
830 return 273.15; // return 0C to make the compiler happy
831 }
832};
833
834} // namespace Opm
835
836#endif // OPM_TEMPERATURE_MODEL_HPP
Provides the energy specific extensive quantities to the generic black-oil module's extensive quantit...
Definition: blackoilmodules.hpp:73
Definition: TemperatureModel.hpp:70
FluidStateTemp fluidState_
Definition: TemperatureModel.hpp:170
OPM_HOST_DEVICE const Scalar & rockFraction() const
Definition: TemperatureModel.hpp:146
OPM_HOST_DEVICE void setFluidState(const FluidState &fs)
Definition: TemperatureModel.hpp:153
Scalar rockFraction_
Definition: TemperatureModel.hpp:171
EvaluationTemp rockInternalEnergy_
Definition: TemperatureModel.hpp:168
EvaluationTemp totalThermalConductivity_
Definition: TemperatureModel.hpp:169
OPM_HOST_DEVICE void updateTemperature_(const Problem &problem, unsigned globalDofIdx, unsigned timeIdx)
Definition: TemperatureModel.hpp:103
OPM_HOST_DEVICE const EvaluationTemp & totalThermalConductivity() const
Definition: TemperatureModel.hpp:143
OPM_HOST_DEVICE void updateEnergyQuantities_(const Problem &problem, const unsigned globalSpaceIdx, const unsigned timeIdx)
Definition: TemperatureModel.hpp:111
OPM_HOST_DEVICE const EvaluationTemp & rockInternalEnergy() const
Definition: TemperatureModel.hpp:140
DenseAd::Evaluation< Scalar, 1 > EvaluationTemp
Definition: TemperatureModel.hpp:88
OPM_HOST_DEVICE const FluidStateTemp & fluidStateTemp() const
Definition: TemperatureModel.hpp:149
BlackOilFluidState< EvaluationTemp, FluidSystem, true, true, compositionSwitchEnabled, enableVapwat, enableBrine, enableSaltPrecipitation, enableDisgasInWater, enableSolvent, Indices::numPhases > FluidStateTemp
Definition: TemperatureModel.hpp:99
Definition: blackoilmodules.hpp:63
Definition: GenericTemperatureModel.hpp:53
void doInit(std::size_t numGridDof)
Initialize all internal data structures needed by the temperature module.
Definition: GenericTemperatureModel_impl.hpp:115
std::vector< Scalar > temperature_
Definition: GenericTemperatureModel.hpp:92
A sparse matrix interface backend for BCRSMatrix from dune-istl.
Definition: istlsparsematrixadapter.hh:43
Definition: SingleWellState.hpp:44
WellStatus status
Definition: SingleWellState.hpp:103
Scalar temperature
Definition: SingleWellState.hpp:114
PerfData< Scalar > perf_data
Definition: SingleWellState.hpp:163
Definition: BlackoilWellModel.hpp:90
void beginTimeStep()
Definition: TemperatureModel.hpp:827
void init()
Definition: TemperatureModel.hpp:826
const Scalar temperature(size_t) const
Definition: TemperatureModel.hpp:828
void deserialize(Restarter &)
This method restores the complete state of the temperature from disk.
Definition: TemperatureModel.hpp:817
void serializeOp(Serializer &)
Definition: TemperatureModel.hpp:821
void serialize(Restarter &)
This method writes the complete state of all temperature to the hard disk.
Definition: TemperatureModel.hpp:807
TemperatureModel(Simulator &)
Definition: TemperatureModel.hpp:799
A class which handles sequential implicit solution of the energy equation as specified in by TEMP.
Definition: TemperatureModel.hpp:186
void serialize(Restarter &)
This method writes the complete state of all temperature to the hard disk.
Definition: TemperatureModel.hpp:386
void assembleEquationWell(const Well &well)
Definition: TemperatureModel.hpp:710
void computeFluxTerm(const FluidStateTemp &fsIn, const FluidStateTemp &fsEx, const RateVector &darcyFlux, Evaluation &flux)
Definition: TemperatureModel.hpp:555
std::vector< int > interiorRows_
Definition: TemperatureModel.hpp:786
EnergyVector storage1_
Definition: TemperatureModel.hpp:780
bool converged(const int iter)
Definition: TemperatureModel.hpp:470
void computeStorageTerm(unsigned globI, LhsEval &storage)
Definition: TemperatureModel.hpp:529
void deserialize(Restarter &)
This method restores the complete state of the temperature from disk.
Definition: TemperatureModel.hpp:396
void endTimeStep(WellStateType &wellState)
Informs the temperature model that a time step has just been finished.
Definition: TemperatureModel.hpp:347
static TemperatureModel serializationTestObject(Simulator &simulator)
Definition: TemperatureModel.hpp:243
const Simulator & simulator_
Definition: TemperatureModel.hpp:779
void computeHeatFluxTerm(const IntensiveQuantitiesTemp &intQuantsIn, const IntensiveQuantitiesTemp &intQuantsEx, const ResidualNBInfo &res_nbinfo, Evaluation &heatFlux)
Definition: TemperatureModel.hpp:584
void beginTimeStep()
Definition: TemperatureModel.hpp:322
void computeWellTemperature(const Well &well, SingleWellState &ws)
Definition: TemperatureModel.hpp:757
void advanceTemperatureFields()
Definition: TemperatureModel.hpp:422
void updateStorageCache()
Definition: TemperatureModel.hpp:408
Scalar scalingFactor_
Definition: TemperatureModel.hpp:787
void serializeOp(Serializer &serializer)
Definition: TemperatureModel.hpp:400
std::unique_ptr< SpareMatrixEnergyAdapter > energyMatrix_
Definition: TemperatureModel.hpp:784
void assembleEquations()
Definition: TemperatureModel.hpp:606
void solveAndUpdate()
Definition: TemperatureModel.hpp:446
std::vector< MatrixBlockTemp * > diagMatAddress_
Definition: TemperatureModel.hpp:783
bool operator==(const TemperatureModel &other) const
Definition: TemperatureModel.hpp:251
std::vector< IntensiveQuantitiesTemp > intQuants_
Definition: TemperatureModel.hpp:781
std::vector< int > overlapRows_
Definition: TemperatureModel.hpp:785
SparseTable< NeighborInfoCPU > neighborInfo_
Definition: TemperatureModel.hpp:782
TemperatureModel(Simulator &simulator)
Definition: TemperatureModel.hpp:235
void init()
Definition: TemperatureModel.hpp:256
Definition: WellState.hpp:68
int numWells() const
Definition: WellState.hpp:116
const SingleWellState< Scalar, IndexTraits > & well(std::size_t well_index) const
Definition: WellState.hpp:315
Provides data handles for parallel communication which operate on DOFs.
auto Get(bool errorIfNotRegistered=true)
Retrieve a runtime parameter.
Definition: parametersystem.hpp:191
Definition: blackoilmodel.hh:74
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:45
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: AquiferGridUtils.hpp:35
Definition: tpfalinearizerstructs.hh:66
Definition: BlackoilModelParameters.hpp:61
Definition: TemperatureModel.hpp:57
a tag to mark properties as undefined
Definition: propertysystem.hh:38