tpfalinearizer.hh
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 TPFA_LINEARIZER_HH
29#define TPFA_LINEARIZER_HH
30
31#include "fvbaseproperties.hh"
32#include "linearizationtype.hh"
33
34#include <opm/common/Exceptions.hpp>
35#include <opm/common/TimingMacros.hpp>
36
37#include <opm/grid/utility/SparseTable.hpp>
38
39#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
40#include <opm/input/eclipse/Schedule/BCProp.hpp>
41
43
44#include <dune/common/version.hh>
45#include <dune/common/fvector.hh>
46#include <dune/common/fmatrix.hh>
47
48#include <type_traits>
49#include <iostream>
50#include <vector>
51#include <thread>
52#include <set>
53#include <exception> // current_exception, rethrow_exception
54#include <mutex>
55#include <numeric>
56
57namespace Opm::Properties {
58 template<class TypeTag, class MyTypeTag>
60 using type = bool;
61 static constexpr type value = false;
62 };
63}
64
65namespace Opm {
66
67// forward declarations
68template<class TypeTag>
70
80template<class TypeTag>
82{
90
99
100 using Element = typename GridView::template Codim<0>::Entity;
101 using ElementIterator = typename GridView::template Codim<0>::Iterator;
102
103 using Vector = GlobalEqVector;
104
105 enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
106 enum { historySize = getPropValue<TypeTag, Properties::TimeDiscHistorySize>() };
107 enum { dimWorld = GridView::dimensionworld };
108
109 using MatrixBlock = typename SparseMatrixAdapter::MatrixBlock;
110 using VectorBlock = Dune::FieldVector<Scalar, numEq>;
112
113 static const bool linearizeNonLocalElements = getPropValue<TypeTag, Properties::LinearizeNonLocalElements>();
114 static const bool enableEnergy = getPropValue<TypeTag, Properties::EnableEnergy>();
115 static const bool enableDiffusion = getPropValue<TypeTag, Properties::EnableDiffusion>();
116 // copying the linearizer is not a good idea
119
120public:
122 : jacobian_()
123 {
124 simulatorPtr_ = 0;
125 separateSparseSourceTerms_ = Parameters::get<TypeTag, Properties::SeparateSparseSourceTerms>();
126 }
127
129 {
130 }
131
135 static void registerParameters()
136 {
137 Parameters::registerParam<TypeTag, Properties::SeparateSparseSourceTerms>
138 ("Treat well source terms all in one go, instead of on a cell by cell basis.");
139 }
140
150 void init(Simulator& simulator)
151 {
152 simulatorPtr_ = &simulator;
153 eraseMatrix();
154 }
155
164 {
165 jacobian_.reset();
166 }
167
178 {
181 }
182
194 {
195 int succeeded;
196 try {
197 linearizeDomain(fullDomain_);
198 succeeded = 1;
199 }
200 catch (const std::exception& e)
201 {
202 std::cout << "rank " << simulator_().gridView().comm().rank()
203 << " caught an exception while linearizing:" << e.what()
204 << "\n" << std::flush;
205 succeeded = 0;
206 }
207 catch (...)
208 {
209 std::cout << "rank " << simulator_().gridView().comm().rank()
210 << " caught an exception while linearizing"
211 << "\n" << std::flush;
212 succeeded = 0;
213 }
214 succeeded = simulator_().gridView().comm().min(succeeded);
215
216 if (!succeeded)
217 throw NumericalProblem("A process did not succeed in linearizing the system");
218 }
219
230 template <class SubDomainType>
231 void linearizeDomain(const SubDomainType& domain)
232 {
233 OPM_TIMEBLOCK(linearizeDomain);
234 // we defer the initialization of the Jacobian matrix until here because the
235 // auxiliary modules usually assume the problem, model and grid to be fully
236 // initialized...
237 if (!jacobian_)
238 initFirstIteration_();
239
240 // Called here because it is no longer called from linearize_().
241 if (domain.cells.size() == model_().numTotalDof()) {
242 // We are on the full domain.
243 resetSystem_();
244 } else {
245 resetSystem_(domain);
246 }
247
248 linearize_(domain);
249 }
250
251 void finalize()
252 { jacobian_->finalize(); }
253
259 {
260 OPM_TIMEBLOCK(linearizeAuxilaryEquations);
261 // flush possible local caches into matrix structure
262 jacobian_->commit();
263
264 auto& model = model_();
265 const auto& comm = simulator_().gridView().comm();
266 for (unsigned auxModIdx = 0; auxModIdx < model.numAuxiliaryModules(); ++auxModIdx) {
267 bool succeeded = true;
268 try {
269 model.auxiliaryModule(auxModIdx)->linearize(*jacobian_, residual_);
270 }
271 catch (const std::exception& e) {
272 succeeded = false;
273
274 std::cout << "rank " << simulator_().gridView().comm().rank()
275 << " caught an exception while linearizing:" << e.what()
276 << "\n" << std::flush;
277 }
278
279 succeeded = comm.min(succeeded);
280
281 if (!succeeded)
282 throw NumericalProblem("linearization of an auxiliary equation failed");
283 }
284 }
285
289 const SparseMatrixAdapter& jacobian() const
290 { return *jacobian_; }
291
292 SparseMatrixAdapter& jacobian()
293 { return *jacobian_; }
294
298 const GlobalEqVector& residual() const
299 { return residual_; }
300
301 GlobalEqVector& residual()
302 { return residual_; }
303
305 linearizationType_ = linearizationType;
306 };
307
309 return linearizationType_;
310 };
311
317 const auto& getFlowsInfo() const{
318
319 return flowsInfo_;
320 }
321
327 const auto& getFloresInfo() const{
328
329 return floresInfo_;
330 }
331
337 const auto& getVelocityInfo() const{
338
339 return velocityInfo_;
340 }
341
343 {
344 updateStoredTransmissibilities();
345 }
346
348 for (auto& bdyInfo : boundaryInfo_) {
349 const auto [type, massrateAD] = problem_().boundaryCondition(bdyInfo.cell, bdyInfo.dir);
350
351 // Strip the unnecessary (and zero anyway) derivatives off massrate.
352 VectorBlock massrate(0.0);
353 for (size_t ii = 0; ii < massrate.size(); ++ii) {
354 massrate[ii] = massrateAD[ii].value();
355 }
356 if (type != BCType::NONE) {
357 const auto& exFluidState = problem_().boundaryFluidState(bdyInfo.cell, bdyInfo.dir);
358 bdyInfo.bcdata.type = type;
359 bdyInfo.bcdata.massRate = massrate;
360 bdyInfo.bcdata.exFluidState = exFluidState;
361 }
362 }
363 }
364
370 const std::map<unsigned, Constraints> constraintsMap() const
371 { return {}; }
372
373 template <class SubDomainType>
374 void resetSystem_(const SubDomainType& domain)
375 {
376 if (!jacobian_) {
377 initFirstIteration_();
378 }
379 for (int globI : domain.cells) {
380 residual_[globI] = 0.0;
381 jacobian_->clearRow(globI, 0.0);
382 }
383 }
384
385private:
386 Simulator& simulator_()
387 { return *simulatorPtr_; }
388 const Simulator& simulator_() const
389 { return *simulatorPtr_; }
390
391 Problem& problem_()
392 { return simulator_().problem(); }
393 const Problem& problem_() const
394 { return simulator_().problem(); }
395
396 Model& model_()
397 { return simulator_().model(); }
398 const Model& model_() const
399 { return simulator_().model(); }
400
401 const GridView& gridView_() const
402 { return problem_().gridView(); }
403
404 void initFirstIteration_()
405 {
406 // initialize the BCRS matrix for the Jacobian of the residual function
407 createMatrix_();
408
409 // initialize the Jacobian matrix and the vector for the residual function
410 residual_.resize(model_().numTotalDof());
411 resetSystem_();
412
413 // initialize the sparse tables for Flows and Flores
414 createFlows_();
415 }
416
417 // Construct the BCRS matrix for the Jacobian of the residual function
418 void createMatrix_()
419 {
420 OPM_TIMEBLOCK(createMatrix);
421 if (!neighborInfo_.empty()) {
422 // It is ok to call this function multiple times, but it
423 // should not do anything if already called.
424 return;
425 }
426 const auto& model = model_();
427 Stencil stencil(gridView_(), model_().dofMapper());
428
429 // for the main model, find out the global indices of the neighboring degrees of
430 // freedom of each primary degree of freedom
431 using NeighborSet = std::set< unsigned >;
432 std::vector<NeighborSet> sparsityPattern(model.numTotalDof());
433 const Scalar gravity = problem_().gravity()[dimWorld - 1];
434 unsigned numCells = model.numTotalDof();
435 neighborInfo_.reserve(numCells, 6 * numCells);
436 std::vector<NeighborInfo> loc_nbinfo;
437 for (const auto& elem : elements(gridView_())) {
438 stencil.update(elem);
439
440 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
441 unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
442 loc_nbinfo.resize(stencil.numDof() - 1); // Do not include the primary dof in neighborInfo_
443
444 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
445 unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
446 sparsityPattern[myIdx].insert(neighborIdx);
447 if (dofIdx > 0) {
448 const Scalar trans = problem_().transmissibility(myIdx, neighborIdx);
449 const auto scvfIdx = dofIdx - 1;
450 const auto& scvf = stencil.interiorFace(scvfIdx);
451 const Scalar area = scvf.area();
452 const Scalar Vin = problem_().model().dofTotalVolume(myIdx);
453 const Scalar Vex = problem_().model().dofTotalVolume(neighborIdx);
454 const Scalar zIn = problem_().dofCenterDepth(myIdx);
455 const Scalar zEx = problem_().dofCenterDepth(neighborIdx);
456 const Scalar dZg = (zIn - zEx)*gravity;
457 const Scalar thpres = problem_().thresholdPressure(myIdx, neighborIdx);
458 Scalar inAlpha {0.};
459 Scalar outAlpha {0.};
460 Scalar diffusivity {0.};
461 Scalar dispersivity {0.};
462 if constexpr(enableEnergy){
463 inAlpha = problem_().thermalHalfTransmissibility(myIdx, neighborIdx);
464 outAlpha = problem_().thermalHalfTransmissibility(neighborIdx, myIdx);
465 }
466 if constexpr(enableDiffusion){
467 diffusivity = problem_().diffusivity(myIdx, neighborIdx);
468 }
469 if (simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion()) {
470 dispersivity = problem_().dispersivity(myIdx, neighborIdx);
471 }
472 const auto dirId = scvf.dirId();
473 auto faceDir = dirId < 0 ? FaceDir::DirEnum::Unknown
474 : FaceDir::FromIntersectionIndex(dirId);
475 loc_nbinfo[dofIdx - 1] = NeighborInfo{neighborIdx, {trans, area, thpres, dZg, faceDir, Vin, Vex, inAlpha, outAlpha, diffusivity, dispersivity}, nullptr};
476
477 }
478 }
479 neighborInfo_.appendRow(loc_nbinfo.begin(), loc_nbinfo.end());
480 if (problem_().nonTrivialBoundaryConditions()) {
481 for (unsigned bfIndex = 0; bfIndex < stencil.numBoundaryFaces(); ++bfIndex) {
482 const auto& bf = stencil.boundaryFace(bfIndex);
483 const int dir_id = bf.dirId();
484 // not for NNCs
485 if (dir_id < 0)
486 continue;
487 const auto [type, massrateAD] = problem_().boundaryCondition(myIdx, dir_id);
488 // Strip the unnecessary (and zero anyway) derivatives off massrate.
489 VectorBlock massrate(0.0);
490 for (size_t ii = 0; ii < massrate.size(); ++ii) {
491 massrate[ii] = massrateAD[ii].value();
492 }
493 const auto& exFluidState = problem_().boundaryFluidState(myIdx, dir_id);
494 BoundaryConditionData bcdata{type,
495 massrate,
496 exFluidState.pvtRegionIndex(),
497 bfIndex,
498 bf.area(),
499 bf.integrationPos()[dimWorld - 1],
500 exFluidState};
501 boundaryInfo_.push_back({myIdx, dir_id, bfIndex, bcdata});
502 }
503 }
504 }
505 }
506
507 // add the additional neighbors and degrees of freedom caused by the auxiliary
508 // equations
509 size_t numAuxMod = model.numAuxiliaryModules();
510 for (unsigned auxModIdx = 0; auxModIdx < numAuxMod; ++auxModIdx)
511 model.auxiliaryModule(auxModIdx)->addNeighbors(sparsityPattern);
512
513 // allocate raw matrix
514 jacobian_.reset(new SparseMatrixAdapter(simulator_()));
515 diagMatAddress_.resize(numCells);
516 // create matrix structure based on sparsity pattern
517 jacobian_->reserve(sparsityPattern);
518 for (unsigned globI = 0; globI < numCells; globI++) {
519 const auto& nbInfos = neighborInfo_[globI];
520 diagMatAddress_[globI] = jacobian_->blockAddress(globI, globI);
521 for (auto& nbInfo : nbInfos) {
522 nbInfo.matBlockAddress = jacobian_->blockAddress(nbInfo.neighbor, globI);
523 }
524 }
525
526 // Create dummy full domain.
527 fullDomain_.cells.resize(numCells);
528 std::iota(fullDomain_.cells.begin(), fullDomain_.cells.end(), 0);
529 }
530
531 // reset the global linear system of equations.
532 void resetSystem_()
533 {
534 residual_ = 0.0;
535 // zero all matrix entries
536 jacobian_->clear();
537 }
538
539 // Initialize the flows, flores, and velocity sparse tables
540 void createFlows_()
541 {
542 OPM_TIMEBLOCK(createFlows);
543 // If FLOWS/FLORES is set in any RPTRST in the schedule, then we initializate the sparse tables
544 // For now, do the same also if any block flows are requested (TODO: only save requested cells...)
545 // If DISPERC is in the deck, we initialize the sparse table here as well.
546 const bool anyFlows = simulator_().problem().eclWriter()->outputModule().anyFlows();
547 const bool anyFlores = simulator_().problem().eclWriter()->outputModule().anyFlores();
548 const bool enableDispersion = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
549 if (((!anyFlows || !flowsInfo_.empty()) && (!anyFlores || !floresInfo_.empty())) && !enableDispersion) {
550 return;
551 }
552 const auto& model = model_();
553 const auto& nncOutput = simulator_().problem().eclWriter()->getOutputNnc();
554 Stencil stencil(gridView_(), model_().dofMapper());
555 unsigned numCells = model.numTotalDof();
556 std::unordered_multimap<int, std::pair<int, int>> nncIndices;
557 std::vector<FlowInfo> loc_flinfo;
558 std::vector<VelocityInfo> loc_vlinfo;
559 unsigned int nncId = 0;
560 VectorBlock flow(0.0);
561
562 // Create a nnc structure to use fast lookup
563 for (unsigned int nncIdx = 0; nncIdx < nncOutput.size(); ++nncIdx) {
564 const int ci1 = nncOutput[nncIdx].cell1;
565 const int ci2 = nncOutput[nncIdx].cell2;
566 nncIndices.emplace(ci1, std::make_pair(ci2, nncIdx));
567 }
568
569 if (anyFlows) {
570 flowsInfo_.reserve(numCells, 6 * numCells);
571 }
572 if (anyFlores) {
573 floresInfo_.reserve(numCells, 6 * numCells);
574 }
575 if (enableDispersion) {
576 velocityInfo_.reserve(numCells, 6 * numCells);
577 }
578
579 for (const auto& elem : elements(gridView_())) {
580 stencil.update(elem);
581 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
582 unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
583 int numFaces = stencil.numBoundaryFaces() + stencil.numInteriorFaces();
584 loc_flinfo.resize(numFaces);
585 loc_vlinfo.resize(stencil.numDof() - 1);
586
587 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
588 unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
589 if (dofIdx > 0) {
590 const auto scvfIdx = dofIdx - 1;
591 const auto& scvf = stencil.interiorFace(scvfIdx);
592 int faceId = scvf.dirId();
593 const int cartMyIdx = simulator_().vanguard().cartesianIndex(myIdx);
594 const int cartNeighborIdx = simulator_().vanguard().cartesianIndex(neighborIdx);
595 const auto& range = nncIndices.equal_range(cartMyIdx);
596 for (auto it = range.first; it != range.second; ++it) {
597 if (it->second.first == cartNeighborIdx){
598 // -1 gives problem since is used for the nncInput from the deck
599 faceId = -2;
600 // the index is stored to be used for writting the outputs
601 nncId = it->second.second;
602 }
603 }
604 loc_flinfo[dofIdx - 1] = FlowInfo{faceId, flow, nncId};
605 loc_vlinfo[dofIdx - 1] = VelocityInfo{flow};
606 }
607 }
608
609 for (unsigned bdfIdx = 0; bdfIdx < stencil.numBoundaryFaces(); ++bdfIdx) {
610 const auto& scvf = stencil.boundaryFace(bdfIdx);
611 int faceId = scvf.dirId();
612 loc_flinfo[stencil.numInteriorFaces() + bdfIdx] = FlowInfo{faceId, flow, nncId};
613 }
614
615
616 if (anyFlows) {
617 flowsInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
618 }
619 if (anyFlores) {
620 floresInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
621 }
622 if (enableDispersion) {
623 velocityInfo_.appendRow(loc_vlinfo.begin(), loc_vlinfo.end());
624 }
625 }
626 }
627 }
628
629public:
630 void setResAndJacobi(VectorBlock& res, MatrixBlock& bMat, const ADVectorBlock& resid) const
631 {
632 for (unsigned eqIdx = 0; eqIdx < numEq; eqIdx++)
633 res[eqIdx] = resid[eqIdx].value();
634
635 for (unsigned eqIdx = 0; eqIdx < numEq; eqIdx++) {
636 for (unsigned pvIdx = 0; pvIdx < numEq; pvIdx++) {
637 // A[dofIdx][focusDofIdx][eqIdx][pvIdx] is the partial derivative of
638 // the residual function 'eqIdx' for the degree of freedom 'dofIdx'
639 // with regard to the focus variable 'pvIdx' of the degree of freedom
640 // 'focusDofIdx'
641 bMat[eqIdx][pvIdx] = resid[eqIdx].derivative(pvIdx);
642 }
643 }
644 }
645
647 OPM_TIMEBLOCK(updateFlows);
648 const bool& enableFlows = simulator_().problem().eclWriter()->outputModule().hasFlows() ||
649 simulator_().problem().eclWriter()->outputModule().hasBlockFlows();
650 const bool& enableFlores = simulator_().problem().eclWriter()->outputModule().hasFlores();
651 if (!enableFlows && !enableFlores) {
652 return;
653 }
654 const unsigned int numCells = model_().numTotalDof();
655
656#ifdef _OPENMP
657#pragma omp parallel for
658#endif
659 for (unsigned globI = 0; globI < numCells; ++globI) {
660 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell);
661 const auto& nbInfos = neighborInfo_[globI];
662 ADVectorBlock adres(0.0);
663 ADVectorBlock darcyFlux(0.0);
664 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
665 // Flux term.
666 {
667 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell);
668 short loc = 0;
669 for (const auto& nbInfo : nbInfos) {
670 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachFace);
671 unsigned globJ = nbInfo.neighbor;
672 assert(globJ != globI);
673 adres = 0.0;
674 darcyFlux = 0.0;
675 const IntensiveQuantities& intQuantsEx = model_().intensiveQuantities(globJ, /*timeIdx*/ 0);
676 LocalResidual::computeFlux(adres,darcyFlux, globI, globJ, intQuantsIn, intQuantsEx, nbInfo.res_nbinfo);
677 adres *= nbInfo.res_nbinfo.faceArea;
678 if (enableFlows) {
679 for (unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
680 flowsInfo_[globI][loc].flow[eqIdx] = adres[eqIdx].value();
681 }
682 }
683 if (enableFlores) {
684 for (unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
685 floresInfo_[globI][loc].flow[eqIdx] = darcyFlux[eqIdx].value();
686 }
687 }
688 ++loc;
689 }
690 }
691 }
692
693 // Boundary terms. Only looping over cells with nontrivial bcs.
694 for (const auto& bdyInfo : boundaryInfo_) {
695 if (bdyInfo.bcdata.type == BCType::NONE)
696 continue;
697
698 ADVectorBlock adres(0.0);
699 const unsigned globI = bdyInfo.cell;
700 const auto& nbInfos = neighborInfo_[globI];
701 const IntensiveQuantities& insideIntQuants = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
702 LocalResidual::computeBoundaryFlux(adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
703 adres *= bdyInfo.bcdata.faceArea;
704 const unsigned bfIndex = bdyInfo.bfIndex;
705 if (enableFlows) {
706 for (unsigned eqIdx = 0; eqIdx < numEq; ++ eqIdx) {
707 flowsInfo_[globI][nbInfos.size() + bfIndex].flow[eqIdx] = adres[eqIdx].value();
708 }
709 }
710 // TODO also store Flores?
711 }
712 }
713
714private:
715 template <class SubDomainType>
716 void linearize_(const SubDomainType& domain)
717 {
718 // This check should be removed once this is addressed by
719 // for example storing the previous timesteps' values for
720 // rsmax (for DRSDT) and similar.
721 if (!problem_().recycleFirstIterationStorage()) {
722 if (!model_().storeIntensiveQuantities() && !model_().enableStorageCache()) {
723 OPM_THROW(std::runtime_error, "Must have cached either IQs or storage when we cannot recycle.");
724 }
725 }
726
727 OPM_TIMEBLOCK(linearize);
728
729 // We do not call resetSystem_() here, since that will set
730 // the full system to zero, not just our part.
731 // Instead, that must be called before starting the linearization.
732 const bool& enableDispersion = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
733 const unsigned int numCells = domain.cells.size();
734 const bool on_full_domain = (numCells == model_().numTotalDof());
735
736#ifdef _OPENMP
737#pragma omp parallel for
738#endif
739 for (unsigned ii = 0; ii < numCells; ++ii) {
740 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell);
741 const unsigned globI = domain.cells[ii];
742 const auto& nbInfos = neighborInfo_[globI];
743 VectorBlock res(0.0);
744 MatrixBlock bMat(0.0);
745 ADVectorBlock adres(0.0);
746 ADVectorBlock darcyFlux(0.0);
747 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
748
749 // Flux term.
750 {
751 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell);
752 short loc = 0;
753 for (const auto& nbInfo : nbInfos) {
754 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachFace);
755 unsigned globJ = nbInfo.neighbor;
756 assert(globJ != globI);
757 res = 0.0;
758 bMat = 0.0;
759 adres = 0.0;
760 darcyFlux = 0.0;
761 const IntensiveQuantities& intQuantsEx = model_().intensiveQuantities(globJ, /*timeIdx*/ 0);
762 LocalResidual::computeFlux(adres,darcyFlux, globI, globJ, intQuantsIn, intQuantsEx, nbInfo.res_nbinfo);
763 adres *= nbInfo.res_nbinfo.faceArea;
764 if (enableDispersion) {
765 for (unsigned phaseIdx = 0; phaseIdx < numEq; ++ phaseIdx) {
766 velocityInfo_[globI][loc].velocity[phaseIdx] = darcyFlux[phaseIdx].value() / nbInfo.res_nbinfo.faceArea;
767 }
768 }
769 setResAndJacobi(res, bMat, adres);
770 residual_[globI] += res;
771 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
772 *diagMatAddress_[globI] += bMat;
773 bMat *= -1.0;
774 //SparseAdapter syntax: jacobian_->addToBlock(globJ, globI, bMat);
775 *nbInfo.matBlockAddress += bMat;
776 ++loc;
777 }
778 }
779
780 // Accumulation term.
781 double dt = simulator_().timeStepSize();
782 double volume = model_().dofTotalVolume(globI);
783 Scalar storefac = volume / dt;
784 adres = 0.0;
785 {
786 OPM_TIMEBLOCK_LOCAL(computeStorage);
787 LocalResidual::computeStorage(adres, intQuantsIn);
788 }
789 setResAndJacobi(res, bMat, adres);
790 // Either use cached storage term, or compute it on the fly.
791 if (model_().enableStorageCache()) {
792 // The cached storage for timeIdx 0 (current time) is not
793 // used, but after storage cache is shifted at the end of the
794 // timestep, it will become cached storage for timeIdx 1.
795 model_().updateCachedStorage(globI, /*timeIdx=*/0, res);
796 if (model_().newtonMethod().numIterations() == 0) {
797 // Need to update the storage cache.
798 if (problem_().recycleFirstIterationStorage()) {
799 // Assumes nothing have changed in the system which
800 // affects masses calculated from primary variables.
801 if (on_full_domain) {
802 // This is to avoid resetting the start-of-step storage
803 // to incorrect numbers when we do local solves, where the iteration
804 // number will start from 0, but the starting state may not be identical
805 // to the start-of-step state.
806 // Note that a full assembly must be done before local solves
807 // otherwise this will be left un-updated.
808 model_().updateCachedStorage(globI, /*timeIdx=*/1, res);
809 }
810 } else {
811 Dune::FieldVector<Scalar, numEq> tmp;
812 IntensiveQuantities intQuantOld = model_().intensiveQuantities(globI, 1);
813 LocalResidual::computeStorage(tmp, intQuantOld);
814 model_().updateCachedStorage(globI, /*timeIdx=*/1, tmp);
815 }
816 }
817 res -= model_().cachedStorage(globI, 1);
818 } else {
819 OPM_TIMEBLOCK_LOCAL(computeStorage0);
820 Dune::FieldVector<Scalar, numEq> tmp;
821 IntensiveQuantities intQuantOld = model_().intensiveQuantities(globI, 1);
822 LocalResidual::computeStorage(tmp, intQuantOld);
823 // assume volume do not change
824 res -= tmp;
825 }
826 res *= storefac;
827 bMat *= storefac;
828 residual_[globI] += res;
829 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
830 *diagMatAddress_[globI] += bMat;
831
832 // Cell-wise source terms.
833 // This will include well sources if SeparateSparseSourceTerms is false.
834 res = 0.0;
835 bMat = 0.0;
836 adres = 0.0;
837 if (separateSparseSourceTerms_) {
838 LocalResidual::computeSourceDense(adres, problem_(), globI, 0);
839 } else {
840 LocalResidual::computeSource(adres, problem_(), globI, 0);
841 }
842 adres *= -volume;
843 setResAndJacobi(res, bMat, adres);
844 residual_[globI] += res;
845 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
846 *diagMatAddress_[globI] += bMat;
847 } // end of loop for cell globI.
848
849 // Add sparse source terms. For now only wells.
850 if (separateSparseSourceTerms_) {
851 problem_().wellModel().addReservoirSourceTerms(residual_, diagMatAddress_);
852 }
853
854 // Boundary terms. Only looping over cells with nontrivial bcs.
855 for (const auto& bdyInfo : boundaryInfo_) {
856 if (bdyInfo.bcdata.type == BCType::NONE)
857 continue;
858
859 VectorBlock res(0.0);
860 MatrixBlock bMat(0.0);
861 ADVectorBlock adres(0.0);
862 const unsigned globI = bdyInfo.cell;
863 const IntensiveQuantities& insideIntQuants = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
864 LocalResidual::computeBoundaryFlux(adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
865 adres *= bdyInfo.bcdata.faceArea;
866 setResAndJacobi(res, bMat, adres);
867 residual_[globI] += res;
869 *diagMatAddress_[globI] += bMat;
870 }
871 }
872
873 void updateStoredTransmissibilities()
874 {
875 if (neighborInfo_.empty()) {
876 // This function was called before createMatrix_() was called.
877 // We call initFirstIteration_(), not createMatrix_(), because
878 // that will also initialize the residual consistently.
879 initFirstIteration_();
880 }
881 unsigned numCells = model_().numTotalDof();
882#ifdef _OPENMP
883#pragma omp parallel for
884#endif
885 for (unsigned globI = 0; globI < numCells; globI++) {
886 auto nbInfos = neighborInfo_[globI]; // nbInfos will be a SparseTable<...>::mutable_iterator_range.
887 for (auto& nbInfo : nbInfos) {
888 unsigned globJ = nbInfo.neighbor;
889 nbInfo.res_nbinfo.trans = problem_().transmissibility(globI, globJ);
890 }
891 }
892 }
893
894
895 Simulator *simulatorPtr_;
896
897 // the jacobian matrix
898 std::unique_ptr<SparseMatrixAdapter> jacobian_;
899
900 // the right-hand side
901 GlobalEqVector residual_;
902
903 LinearizationType linearizationType_;
904
905 using ResidualNBInfo = typename LocalResidual::ResidualNBInfo;
906 struct NeighborInfo
907 {
908 unsigned int neighbor;
909 ResidualNBInfo res_nbinfo;
910 MatrixBlock* matBlockAddress;
911 };
912 SparseTable<NeighborInfo> neighborInfo_;
913 std::vector<MatrixBlock*> diagMatAddress_;
914
915 struct FlowInfo
916 {
917 int faceId;
918 VectorBlock flow;
919 unsigned int nncId;
920 };
921 SparseTable<FlowInfo> flowsInfo_;
922 SparseTable<FlowInfo> floresInfo_;
923
924 struct VelocityInfo
925 {
926 VectorBlock velocity;
927 };
928 SparseTable<VelocityInfo> velocityInfo_;
929
930 using ScalarFluidState = typename IntensiveQuantities::ScalarFluidState;
931 struct BoundaryConditionData
932 {
933 BCType type;
934 VectorBlock massRate;
935 unsigned pvtRegionIdx;
936 unsigned boundaryFaceIndex;
937 double faceArea;
938 double faceZCoord;
939 ScalarFluidState exFluidState;
940 };
941 struct BoundaryInfo
942 {
943 unsigned int cell;
944 int dir;
945 unsigned int bfIndex;
946 BoundaryConditionData bcdata;
947 };
948 std::vector<BoundaryInfo> boundaryInfo_;
949 bool separateSparseSourceTerms_ = false;
950 struct FullDomain
951 {
952 std::vector<int> cells;
953 std::vector<bool> interior;
954 };
955 FullDomain fullDomain_;
956};
957
958} // namespace Opm
959
960#endif // TPFA_LINEARIZER
The base class for the element-centered finite-volume discretization scheme.
Definition: ecfvdiscretization.hh:147
Definition: matrixblock.hh:227
Manages the initializing and running of time dependent problems.
Definition: simulator.hh:102
Scalar timeStepSize() const
Returns the time step length so that we don't miss the beginning of the next episode or cross the en...
Definition: simulator.hh:455
Vanguard & vanguard()
Return a reference to the grid manager of simulation.
Definition: simulator.hh:275
Problem & problem()
Return the object which specifies the pysical setup of the simulation.
Definition: simulator.hh:306
const GridView & gridView() const
Return the grid view for which the simulation is done.
Definition: simulator.hh:287
Model & model()
Return the physical model used in the simulation.
Definition: simulator.hh:293
The common code for the linearizers of non-linear systems of equations.
Definition: tpfalinearizer.hh:82
const auto & getFloresInfo() const
Return constant reference to the floresInfo.
Definition: tpfalinearizer.hh:327
const LinearizationType & getLinearizationType() const
Definition: tpfalinearizer.hh:308
void updateBoundaryConditionData()
Definition: tpfalinearizer.hh:347
void linearize()
Linearize the full system of non-linear equations.
Definition: tpfalinearizer.hh:177
SparseMatrixAdapter & jacobian()
Definition: tpfalinearizer.hh:292
const auto & getFlowsInfo() const
Return constant reference to the flowsInfo.
Definition: tpfalinearizer.hh:317
TpfaLinearizer()
Definition: tpfalinearizer.hh:121
void linearizeDomain(const SubDomainType &domain)
Linearize the part of the non-linear system of equations that is associated with a part of the spatia...
Definition: tpfalinearizer.hh:231
void finalize()
Definition: tpfalinearizer.hh:251
void init(Simulator &simulator)
Initialize the linearizer.
Definition: tpfalinearizer.hh:150
void updateFlowsInfo()
Definition: tpfalinearizer.hh:646
void setResAndJacobi(VectorBlock &res, MatrixBlock &bMat, const ADVectorBlock &resid) const
Definition: tpfalinearizer.hh:630
~TpfaLinearizer()
Definition: tpfalinearizer.hh:128
static void registerParameters()
Register all run-time parameters for the Jacobian linearizer.
Definition: tpfalinearizer.hh:135
void linearizeDomain()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:193
const SparseMatrixAdapter & jacobian() const
Return constant reference to global Jacobian matrix backend.
Definition: tpfalinearizer.hh:289
void setLinearizationType(LinearizationType linearizationType)
Definition: tpfalinearizer.hh:304
GlobalEqVector & residual()
Definition: tpfalinearizer.hh:301
void eraseMatrix()
Causes the Jacobian matrix to be recreated from scratch before the next iteration.
Definition: tpfalinearizer.hh:163
const auto & getVelocityInfo() const
Return constant reference to the velocityInfo.
Definition: tpfalinearizer.hh:337
void updateDiscretizationParameters()
Definition: tpfalinearizer.hh:342
void resetSystem_(const SubDomainType &domain)
Definition: tpfalinearizer.hh:374
const std::map< unsigned, Constraints > constraintsMap() const
Returns the map of constraint degrees of freedom.
Definition: tpfalinearizer.hh:370
void linearizeAuxiliaryEquations()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:258
const GlobalEqVector & residual() const
Return constant reference to global residual vector.
Definition: tpfalinearizer.hh:298
Declare the properties used by the infrastructure code of the finite volume discretizations.
Definition: blackoilmodel.hh:72
Definition: blackoilboundaryratevector.hh:37
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:242
Definition: linearizationtype.hh:35
Definition: tpfalinearizer.hh:59
bool type
Definition: tpfalinearizer.hh:60
static constexpr type value
Definition: tpfalinearizer.hh:61