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