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 <dune/common/version.hh>
32#include <dune/common/fvector.hh>
33#include <dune/common/fmatrix.hh>
34
35#include <opm/common/Exceptions.hpp>
36#include <opm/common/TimingMacros.hpp>
37
38#include <opm/grid/utility/SparseTable.hpp>
39
40#include <opm/material/common/ConditionalStorage.hpp>
41
42#include <opm/input/eclipse/EclipseState/Grid/FaceDir.hpp>
43#include <opm/input/eclipse/Schedule/BCProp.hpp>
44
50
51#include <cassert>
52#include <cstddef>
53#include <exception> // current_exception, rethrow_exception
54#include <iostream>
55#include <map>
56#include <memory>
57#include <numeric>
58#include <set>
59#include <stdexcept>
60#include <unordered_map>
61#include <vector>
62
63#include <opm/common/utility/gpuDecorators.hpp>
64#if HAVE_CUDA
65#if USE_HIP
66#include <opm/simulators/linalg/gpuistl_hip/GpuBuffer.hpp>
67#include <opm/simulators/linalg/gpuistl_hip/GpuView.hpp>
68#include <opm/simulators/linalg/gpuistl_hip/MiniMatrix.hpp>
69#include <opm/simulators/linalg/gpuistl_hip/MiniVector.hpp>
70#else
75#endif
76#endif
77
78namespace Opm::Parameters {
79
80struct SeparateSparseSourceTerms { static constexpr bool value = false; };
81
82} // namespace Opm::Parameters
83
84namespace Opm {
85
86// forward declarations
87template<class TypeTag>
89
90// Moved these structs out of the class to make them visible in the GPU code.
91template<class Storage = std::vector<int>>
93{
94 Storage cells;
95};
96
97#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
99 {
100 if (CPUDomain.cells.size() == 0) {
101 OPM_THROW(std::runtime_error, "Cannot copy empty full domain to GPU.");
102 }
103 return FullDomain<gpuistl::GpuBuffer<int>>{
104 gpuistl::GpuBuffer<int>(CPUDomain.cells)
105 };
106 };
107
108 FullDomain<gpuistl::GpuView<int>> make_view(FullDomain<gpuistl::GpuBuffer<int>>& buffer)
109 {
110 if (buffer.cells.size() == 0) {
111 OPM_THROW(std::runtime_error, "Cannot make view of empty full domain buffer.");
112 }
113 return FullDomain<gpuistl::GpuView<int>>{
114 gpuistl::make_view(buffer.cells)
115 };
116 };
117#endif
118
119template <class ResidualNBInfoType,class BlockType>
121{
122 unsigned int neighbor;
123 ResidualNBInfoType res_nbinfo;
124 BlockType* matBlockAddress;
125
126 template <class OtherBlockType>
128 : neighbor(other.neighbor)
129 , res_nbinfo(other.res_nbinfo)
130 , matBlockAddress(nullptr)
131 {
132 if (other.matBlockAddress) {
133 matBlockAddress = reinterpret_cast<BlockType*>(other.matBlockAddress);
134 }
135 }
136
137 template <class PtrType>
138 NeighborInfoStruct(unsigned int n, const ResidualNBInfoType& r, PtrType ptr)
139 : neighbor(n)
140 , res_nbinfo(r)
141 , matBlockAddress(static_cast<BlockType*>(ptr))
142 {
143 }
144
145 // Add a default constructor
147 : neighbor(0)
148 , res_nbinfo()
149 , matBlockAddress(nullptr)
150 {
151 }
152};
153
154#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
155namespace gpuistl {
156 template<class MiniMatrixType, class MatrixBlockType, class ResidualNBInfoType>
157 auto copy_to_gpu(const SparseTable<NeighborInfoStruct<ResidualNBInfoType, MatrixBlockType>>& cpuNeighborInfoTable)
158 {
159 // Convert the DUNE FieldMatrix/MatrixBlock to MiniMatrix types
160 using StructWithMinimatrix = NeighborInfoStruct<ResidualNBInfoType, MiniMatrixType>;
161 std::vector<StructWithMinimatrix> minimatrices(cpuNeighborInfoTable.dataSize());
162 size_t idx = 0;
163 for (auto e : cpuNeighborInfoTable.dataStorage()) {
164 minimatrices[idx++] = StructWithMinimatrix(e);
165 }
166
167 return SparseTable<StructWithMinimatrix, gpuistl::GpuBuffer>(
168 gpuistl::GpuBuffer<StructWithMinimatrix>(minimatrices),
169 gpuistl::GpuBuffer<int>(cpuNeighborInfoTable.rowStarts())
170 );
171 }
172}
173#endif
174
184template<class TypeTag>
186{
194
204
205 using Element = typename GridView::template Codim<0>::Entity;
206 using ElementIterator = typename GridView::template Codim<0>::Iterator;
207
208 using Vector = GlobalEqVector;
209
210 enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
211 enum { historySize = getPropValue<TypeTag, Properties::TimeDiscHistorySize>() };
212 enum { dimWorld = GridView::dimensionworld };
213
214 using MatrixBlock = typename SparseMatrixAdapter::MatrixBlock;
215 using VectorBlock = Dune::FieldVector<Scalar, numEq>;
217
218#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
219 using MatrixBlockGPU = gpuistl::MiniMatrix<Scalar, numEq * numEq>;
220 using VectorBlockGPU = gpuistl::MiniVector<Scalar, numEq>;
221#endif
222
223 static constexpr bool linearizeNonLocalElements =
224 getPropValue<TypeTag, Properties::LinearizeNonLocalElements>();
225 static constexpr bool enableFullyImplicitThermal = (getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal);
226 static constexpr bool enableDiffusion = getPropValue<TypeTag, Properties::EnableDiffusion>();
227 static constexpr bool enableDispersion = getPropValue<TypeTag, Properties::EnableDispersion>();
228 static const bool enableBioeffects = getPropValue<TypeTag, Properties::EnableBioeffects>();
229
230 // copying the linearizer is not a good idea
231 TpfaLinearizer(const TpfaLinearizer&) = delete;
233
234public:
236 {
237 simulatorPtr_ = nullptr;
238 separateSparseSourceTerms_ = Parameters::Get<Parameters::SeparateSparseSourceTerms>();
239 }
240
244 static void registerParameters()
245 {
246 Parameters::Register<Parameters::SeparateSparseSourceTerms>
247 ("Treat well source terms all in one go, instead of on a cell by cell basis.");
248 }
249
259 void init(Simulator& simulator)
260 {
261 simulatorPtr_ = &simulator;
262 eraseMatrix();
263 }
264
273 {
274 jacobian_.reset();
275 }
276
287 {
290 }
291
303 {
304 int succeeded;
305 try {
306 linearizeDomain(fullDomain_);
307 succeeded = 1;
308 }
309 catch (const std::exception& e) {
310 std::cout << "rank " << simulator_().gridView().comm().rank()
311 << " caught an exception while linearizing:" << e.what()
312 << "\n" << std::flush;
313 succeeded = 0;
314 }
315 catch (...) {
316 std::cout << "rank " << simulator_().gridView().comm().rank()
317 << " caught an exception while linearizing"
318 << "\n" << std::flush;
319 succeeded = 0;
320 }
321 succeeded = simulator_().gridView().comm().min(succeeded);
322
323 if (!succeeded) {
324 throw NumericalProblem("A process did not succeed in linearizing the system");
325 }
326 }
327
341 template <class SubDomainType>
342 void linearizeDomain(const SubDomainType& domain, const bool isNlddLocalSolve = false)
343 {
344 OPM_TIMEBLOCK(linearizeDomain);
345 // we defer the initialization of the Jacobian matrix until here because the
346 // auxiliary modules usually assume the problem, model and grid to be fully
347 // initialized...
348 if (!jacobian_) {
349 initFirstIteration_();
350 }
351
352 // Called here because it is no longer called from linearize_().
353 if (isNlddLocalSolve) {
354 resetSystem_(domain);
355 }
356 else {
357 resetSystem_();
358 }
359
360 linearize_(domain, isNlddLocalSolve);
361 }
362
363 void finalize()
364 { jacobian_->finalize(); }
365
371 {
372 OPM_TIMEBLOCK(linearizeAuxilaryEquations);
373 // flush possible local caches into matrix structure
374 jacobian_->commit();
375
376 auto& model = model_();
377 const auto& comm = simulator_().gridView().comm();
378 for (unsigned auxModIdx = 0; auxModIdx < model.numAuxiliaryModules(); ++auxModIdx) {
379 bool succeeded = true;
380 try {
381 model.auxiliaryModule(auxModIdx)->linearize(*jacobian_, residual_);
382 }
383 catch (const std::exception& e) {
384 succeeded = false;
385
386 std::cout << "rank " << simulator_().gridView().comm().rank()
387 << " caught an exception while linearizing:" << e.what()
388 << "\n" << std::flush;
389 }
390
391 succeeded = comm.min(succeeded);
392
393 if (!succeeded) {
394 throw NumericalProblem("linearization of an auxiliary equation failed");
395 }
396 }
397 }
398
402 const SparseMatrixAdapter& jacobian() const
403 { return *jacobian_; }
404
405 SparseMatrixAdapter& jacobian()
406 { return *jacobian_; }
407
411 const GlobalEqVector& residual() const
412 { return residual_; }
413
414 GlobalEqVector& residual()
415 { return residual_; }
416
418 { linearizationType_ = linearizationType; }
419
421 { return linearizationType_; }
422
428 const auto& getFlowsInfo() const
429 { return flowsInfo_; }
430
436 const auto& getFloresInfo() const
437 { return floresInfo_; }
438
444 const auto& getVelocityInfo() const
445 { return velocityInfo_; }
446
447 const auto& getNeighborInfo() const {
448 return neighborInfo_;
449 }
450
451
453 {
454 updateStoredTransmissibilities();
455 }
456
458 {
459 for (auto& bdyInfo : boundaryInfo_) {
460 const auto [type, massrateAD] = problem_().boundaryCondition(bdyInfo.cell, bdyInfo.dir);
461
462 // Strip the unnecessary (and zero anyway) derivatives off massrate.
463 VectorBlock massrate(0.0);
464 for (std::size_t ii = 0; ii < massrate.size(); ++ii) {
465 massrate[ii] = massrateAD[ii].value();
466 }
467 if (type != BCType::NONE) {
468 const auto& exFluidState = problem_().boundaryFluidState(bdyInfo.cell, bdyInfo.dir);
469 bdyInfo.bcdata.type = type;
470 bdyInfo.bcdata.massRate = massrate;
471 bdyInfo.bcdata.exFluidState = exFluidState;
472 }
473 }
474 }
475
481 std::map<unsigned, Constraints> constraintsMap() const
482 { return {}; }
483
484 template <class SubDomainType>
485 void resetSystem_(const SubDomainType& domain)
486 {
487 if (!jacobian_) {
488 initFirstIteration_();
489 }
490 for (int globI : domain.cells) {
491 residual_[globI] = 0.0;
492 jacobian_->clearRow(globI, 0.0);
493 }
494 }
495
496private:
497 Simulator& simulator_()
498 { return *simulatorPtr_; }
499
500 const Simulator& simulator_() const
501 { return *simulatorPtr_; }
502
503 Problem& problem_()
504 { return simulator_().problem(); }
505
506 const Problem& problem_() const
507 { return simulator_().problem(); }
508
509 Model& model_()
510 { return simulator_().model(); }
511
512 const Model& model_() const
513 { return simulator_().model(); }
514
515 const GridView& gridView_() const
516 { return problem_().gridView(); }
517
518 void initFirstIteration_()
519 {
520 // initialize the BCRS matrix for the Jacobian of the residual function
521 createMatrix_();
522
523 // initialize the Jacobian matrix and the vector for the residual function
524 residual_.resize(model_().numTotalDof());
525 resetSystem_();
526
527 // initialize the sparse tables for Flows and Flores
528 createFlows_();
529 }
530
531 // Construct the BCRS matrix for the Jacobian of the residual function
532 void createMatrix_()
533 {
534 OPM_TIMEBLOCK(createMatrix);
535 if (!neighborInfo_.empty()) {
536 // It is ok to call this function multiple times, but it
537 // should not do anything if already called.
538 return;
539 }
540 const auto& model = model_();
541 Stencil stencil(gridView_(), model_().dofMapper());
542
543 // for the main model, find out the global indices of the neighboring degrees of
544 // freedom of each primary degree of freedom
545 using NeighborSet = std::set<unsigned>;
546 std::vector<NeighborSet> sparsityPattern(model.numTotalDof());
547 const Scalar gravity = problem_().gravity()[dimWorld - 1];
548 unsigned numCells = model.numTotalDof();
549 neighborInfo_.reserve(numCells, 6 * numCells);
550 std::vector<NeighborInfoCPU> loc_nbinfo;
551 for (const auto& elem : elements(gridView_())) {
552 stencil.update(elem);
553
554 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
555 const unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
556 loc_nbinfo.resize(stencil.numDof() - 1); // Do not include the primary dof in neighborInfo_
557
558 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
559 const unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
560 sparsityPattern[myIdx].insert(neighborIdx);
561 if (dofIdx > 0) {
562 const Scalar trans = problem_().transmissibility(myIdx, neighborIdx);
563 const auto scvfIdx = dofIdx - 1;
564 const auto& scvf = stencil.interiorFace(scvfIdx);
565 const Scalar area = scvf.area();
566 const Scalar Vin = problem_().model().dofTotalVolume(myIdx);
567 const Scalar Vex = problem_().model().dofTotalVolume(neighborIdx);
568 const Scalar zIn = problem_().dofCenterDepth(myIdx);
569 const Scalar zEx = problem_().dofCenterDepth(neighborIdx);
570 const Scalar dZg = (zIn - zEx)*gravity;
571 const Scalar thpres = problem_().thresholdPressure(myIdx, neighborIdx);
572 const auto dirId = scvf.dirId();
573 auto faceDir = dirId < 0 ? FaceDir::DirEnum::Unknown
574 : FaceDir::FromIntersectionIndex(dirId);
575 ResidualNBInfo nbinfo{trans, area, thpres, dZg, faceDir, Vin, Vex, {}, {}, {}, {}};
576 if constexpr (enableFullyImplicitThermal) {
577 nbinfo.inAlpha = problem_().thermalHalfTransmissibility(myIdx, neighborIdx);
578 nbinfo.outAlpha = problem_().thermalHalfTransmissibility(neighborIdx, myIdx);
579 }
580 if constexpr (enableDiffusion) {
581 nbinfo.diffusivity = problem_().diffusivity(myIdx, neighborIdx);
582 }
583 if constexpr (enableDispersion) {
584 nbinfo.dispersivity = problem_().dispersivity(myIdx, neighborIdx);
585 }
586 loc_nbinfo[dofIdx - 1] = NeighborInfoCPU{neighborIdx, nbinfo, nullptr};
587 }
588 }
589 neighborInfo_.appendRow(loc_nbinfo.begin(), loc_nbinfo.end());
590 if (problem_().nonTrivialBoundaryConditions()) {
591 for (unsigned bfIndex = 0; bfIndex < stencil.numBoundaryFaces(); ++bfIndex) {
592 const auto& bf = stencil.boundaryFace(bfIndex);
593 const int dir_id = bf.dirId();
594 // not for NNCs
595 if (dir_id < 0) {
596 continue;
597 }
598 const auto [type, massrateAD] = problem_().boundaryCondition(myIdx, dir_id);
599 // Strip the unnecessary (and zero anyway) derivatives off massrate.
600 VectorBlock massrate(0.0);
601 for (std::size_t ii = 0; ii < massrate.size(); ++ii) {
602 massrate[ii] = massrateAD[ii].value();
603 }
604 const auto& exFluidState = problem_().boundaryFluidState(myIdx, dir_id);
605 BoundaryConditionData bcdata{type,
606 massrate,
607 exFluidState.pvtRegionIndex(),
608 bfIndex,
609 bf.area(),
610 bf.integrationPos()[dimWorld - 1],
611 exFluidState};
612 boundaryInfo_.push_back({myIdx, dir_id, bfIndex, bcdata});
613 }
614 }
615 }
616 }
617
618 // add the additional neighbors and degrees of freedom caused by the auxiliary
619 // equations
620 const std::size_t numAuxMod = model.numAuxiliaryModules();
621 for (unsigned auxModIdx = 0; auxModIdx < numAuxMod; ++auxModIdx) {
622 model.auxiliaryModule(auxModIdx)->addNeighbors(sparsityPattern);
623 }
624
625 // allocate raw matrix
626 jacobian_ = std::make_unique<SparseMatrixAdapter>(simulator_());
627 diagMatAddress_.resize(numCells);
628 // create matrix structure based on sparsity pattern
629 jacobian_->reserve(sparsityPattern);
630 for (unsigned globI = 0; globI < numCells; globI++) {
631 const auto& nbInfos = neighborInfo_[globI];
632 diagMatAddress_[globI] = jacobian_->blockAddress(globI, globI);
633 for (auto& nbInfo : nbInfos) {
634 nbInfo.matBlockAddress = jacobian_->blockAddress(nbInfo.neighbor, globI);
635 }
636 }
637
638 // Create dummy full domain.
639 fullDomain_.cells.resize(numCells);
640 std::iota(fullDomain_.cells.begin(), fullDomain_.cells.end(), 0);
641 }
642
643 // reset the global linear system of equations.
644 void resetSystem_()
645 {
646 residual_ = 0.0;
647 // zero all matrix entries
648 jacobian_->clear();
649 }
650
651 // Initialize the flows, flores, and velocity sparse tables
652 void createFlows_()
653 {
654 OPM_TIMEBLOCK(createFlows);
655 // If FLOWS/FLORES is set in any RPTRST in the schedule, then we initializate the sparse tables
656 // For now, do the same also if any block flows are requested (TODO: only save requested cells...)
657 // If DISPERC is in the deck, we initialize the sparse table here as well.
658 const bool anyFlows = simulator_().problem().eclWriter().outputModule().getFlows().anyFlows() ||
659 simulator_().problem().eclWriter().outputModule().getFlows().hasBlockFlows();
660 const bool anyFlores = simulator_().problem().eclWriter().outputModule().getFlows().anyFlores();
661 const bool dispersionActive = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
662 if (((!anyFlows || !flowsInfo_.empty()) && (!anyFlores || !floresInfo_.empty())) && (!dispersionActive && !enableBioeffects)) {
663 return;
664 }
665 const auto& model = model_();
666 const auto& nncOutput = simulator_().problem().eclWriter().getOutputNnc();
667 Stencil stencil(gridView_(), model_().dofMapper());
668 const unsigned numCells = model.numTotalDof();
669 std::unordered_multimap<int, std::pair<int, int>> nncIndices;
670 std::vector<FlowInfo> loc_flinfo;
671 std::vector<VelocityInfo> loc_vlinfo;
672 unsigned int nncId = 0;
673 VectorBlock flow(0.0);
674
675 // Create a nnc structure to use fast lookup
676 for (unsigned nncIdx = 0; nncIdx < nncOutput.size(); ++nncIdx) {
677 const int ci1 = nncOutput[nncIdx].cell1;
678 const int ci2 = nncOutput[nncIdx].cell2;
679 nncIndices.emplace(ci1, std::make_pair(ci2, nncIdx));
680 }
681
682 if (anyFlows) {
683 flowsInfo_.reserve(numCells, 6 * numCells);
684 }
685 if (anyFlores) {
686 floresInfo_.reserve(numCells, 6 * numCells);
687 }
688 if (dispersionActive || enableBioeffects) {
689 velocityInfo_.reserve(numCells, 6 * numCells);
690 }
691
692 for (const auto& elem : elements(gridView_())) {
693 stencil.update(elem);
694 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
695 const unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
696 const int numFaces = stencil.numBoundaryFaces() + stencil.numInteriorFaces();
697 loc_flinfo.resize(numFaces);
698 loc_vlinfo.resize(stencil.numDof() - 1);
699
700 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
701 const unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
702 if (dofIdx > 0) {
703 const auto scvfIdx = dofIdx - 1;
704 const auto& scvf = stencil.interiorFace(scvfIdx);
705 int faceId = scvf.dirId();
706 const int cartMyIdx = simulator_().vanguard().cartesianIndex(myIdx);
707 const int cartNeighborIdx = simulator_().vanguard().cartesianIndex(neighborIdx);
708 const auto& range = nncIndices.equal_range(cartMyIdx);
709 for (auto it = range.first; it != range.second; ++it) {
710 if (it->second.first == cartNeighborIdx){
711 // -1 gives problem since is used for the nncInput from the deck
712 faceId = -2;
713 // the index is stored to be used for writting the outputs
714 nncId = it->second.second;
715 }
716 }
717 loc_flinfo[dofIdx - 1] = FlowInfo{faceId, flow, nncId};
718 loc_vlinfo[dofIdx - 1] = VelocityInfo{flow};
719 }
720 }
721
722 for (unsigned bdfIdx = 0; bdfIdx < stencil.numBoundaryFaces(); ++bdfIdx) {
723 const auto& scvf = stencil.boundaryFace(bdfIdx);
724 const int faceId = scvf.dirId();
725 loc_flinfo[stencil.numInteriorFaces() + bdfIdx] = FlowInfo{faceId, flow, nncId};
726 }
727
728 if (anyFlows) {
729 flowsInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
730 }
731 if (anyFlores) {
732 floresInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
733 }
734 if (dispersionActive || enableBioeffects) {
735 velocityInfo_.appendRow(loc_vlinfo.begin(), loc_vlinfo.end());
736 }
737 }
738 }
739 }
740
741public:
742 void setResAndJacobi(VectorBlock& res, MatrixBlock& bMat, const ADVectorBlock& resid) const
743 {
744 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
745 res[eqIdx] = resid[eqIdx].value();
746 }
747
748 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
749 for (unsigned pvIdx = 0; pvIdx < numEq; ++pvIdx) {
750 // A[dofIdx][focusDofIdx][eqIdx][pvIdx] is the partial derivative of
751 // the residual function 'eqIdx' for the degree of freedom 'dofIdx'
752 // with regard to the focus variable 'pvIdx' of the degree of freedom
753 // 'focusDofIdx'
754 bMat[eqIdx][pvIdx] = resid[eqIdx].derivative(pvIdx);
755 }
756 }
757 }
758
760 {
761 OPM_TIMEBLOCK(updateFlows);
762 const bool enableFlows = simulator_().problem().eclWriter().outputModule().getFlows().hasFlows() ||
763 simulator_().problem().eclWriter().outputModule().getFlows().hasBlockFlows();
764 const bool enableFlores = simulator_().problem().eclWriter().outputModule().getFlows().hasFlores();
765 if (!enableFlows && !enableFlores) {
766 return;
767 }
768 const unsigned int numCells = model_().numTotalDof();
769#ifdef _OPENMP
770#pragma omp parallel for
771#endif
772 for (unsigned globI = 0; globI < numCells; ++globI) {
773 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell, Subsystem::Assembly);
774 const auto& nbInfos = neighborInfo_[globI];
775 ADVectorBlock adres(0.0);
776 ADVectorBlock darcyFlux(0.0);
777 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
778 // Flux term.
779 {
780 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell, Subsystem::Assembly);
781 short loc = 0;
782 for (const auto& nbInfo : nbInfos) {
783 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachFace, Subsystem::Assembly);
784 const unsigned globJ = nbInfo.neighbor;
785 assert(globJ != globI);
786 adres = 0.0;
787 darcyFlux = 0.0;
788 const IntensiveQuantities& intQuantsEx = model_().intensiveQuantities(globJ, /*timeIdx*/ 0);
789 LocalResidual::computeFlux(adres, darcyFlux, globI, globJ, intQuantsIn,
790 intQuantsEx, nbInfo.res_nbinfo, problem_().moduleParams());
791 adres *= nbInfo.res_nbinfo.faceArea;
792 if (enableFlows) {
793 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
794 flowsInfo_[globI][loc].flow[eqIdx] = adres[eqIdx].value();
795 }
796 }
797 if (enableFlores) {
798 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
799 floresInfo_[globI][loc].flow[eqIdx] = darcyFlux[eqIdx].value();
800 }
801 }
802 ++loc;
803 }
804 }
805 }
806
807 // Boundary terms. Only looping over cells with nontrivial bcs.
808 for (const auto& bdyInfo : boundaryInfo_) {
809 if (bdyInfo.bcdata.type == BCType::NONE) {
810 continue;
811 }
812
813 ADVectorBlock adres(0.0);
814 const unsigned globI = bdyInfo.cell;
815 const auto& nbInfos = neighborInfo_[globI];
816 const IntensiveQuantities& insideIntQuants = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
817 LocalResidual::computeBoundaryFlux(adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
818 adres *= bdyInfo.bcdata.faceArea;
819 const unsigned bfIndex = bdyInfo.bfIndex;
820 if (enableFlows) {
821 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
822 flowsInfo_[globI][nbInfos.size() + bfIndex].flow[eqIdx] = adres[eqIdx].value();
823 }
824 }
825 // TODO also store Flores?
826 }
827 }
828
829private:
830 template <class SubDomainType>
831 void linearize_(const SubDomainType& domain, bool isNlddLocalSolve)
832 {
833 // This check should be removed once this is addressed by
834 // for example storing the previous timesteps' values for
835 // rsmax (for DRSDT) and similar.
836 if (!problem_().recycleFirstIterationStorage()) {
837 if (!model_().storeIntensiveQuantities() && !model_().enableStorageCache()) {
838 OPM_THROW(std::runtime_error, "Must have cached either IQs or storage when we cannot recycle.");
839 }
840 }
841
842 OPM_TIMEBLOCK(linearize);
843
844 // We do not call resetSystem_() here, since that will set
845 // the full system to zero, not just our part.
846 // Instead, that must be called before starting the linearization.
847 const bool dispersionActive = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
848 const unsigned int numCells = domain.cells.size();
849
850 // Fetch timestepsize used later in accumulation term.
851 const double dt = simulator_().timeStepSize();
852
853#ifdef _OPENMP
854#pragma omp parallel for
855#endif
856 for (unsigned ii = 0; ii < numCells; ++ii) {
857 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell, Subsystem::Assembly);
858 const unsigned globI = domain.cells[ii];
859 const auto& nbInfos = neighborInfo_[globI];
860 VectorBlock res(0.0);
861 MatrixBlock bMat(0.0);
862 ADVectorBlock adres(0.0);
863 ADVectorBlock darcyFlux(0.0);
864 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
865
866 // Flux term.
867 {
868 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell, Subsystem::Assembly);
869 short loc = 0;
870 for (const auto& nbInfo : nbInfos) {
871 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachFace, Subsystem::Assembly);
872 const unsigned globJ = nbInfo.neighbor;
873 assert(globJ != globI);
874 res = 0.0;
875 bMat = 0.0;
876 adres = 0.0;
877 darcyFlux = 0.0;
878 const IntensiveQuantities& intQuantsEx = model_().intensiveQuantities(globJ, /*timeIdx*/ 0);
879 LocalResidual::computeFlux(adres, darcyFlux, globI, globJ, intQuantsIn, intQuantsEx,
880 nbInfo.res_nbinfo, problem_().moduleParams());
881 adres *= nbInfo.res_nbinfo.faceArea;
882 if (dispersionActive || enableBioeffects) {
883 for (unsigned phaseIdx = 0; phaseIdx < numEq; ++phaseIdx) {
884 velocityInfo_[globI][loc].velocity[phaseIdx] =
885 darcyFlux[phaseIdx].value() / nbInfo.res_nbinfo.faceArea;
886 }
887 }
888 setResAndJacobi(res, bMat, adres);
889 residual_[globI] += res;
890 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
891 *diagMatAddress_[globI] += bMat;
892 bMat *= -1.0;
893 //SparseAdapter syntax: jacobian_->addToBlock(globJ, globI, bMat);
894 *nbInfo.matBlockAddress += bMat;
895 ++loc;
896 }
897 }
898
899 // Accumulation term.
900 const double volume = model_().dofTotalVolume(globI);
901 const Scalar storefac = volume / dt;
902 adres = 0.0;
903 {
904 OPM_TIMEBLOCK_LOCAL(computeStorage, Subsystem::Assembly);
905 LocalResidual::computeStorage(adres, intQuantsIn);
906 }
907 setResAndJacobi(res, bMat, adres);
908 // Either use cached storage term, or compute it on the fly.
909 if (model_().enableStorageCache()) {
910 // The cached storage for timeIdx 0 (current time) is not
911 // used, but after storage cache is shifted at the end of the
912 // timestep, it will become cached storage for timeIdx 1.
913 model_().updateCachedStorage(globI, /*timeIdx=*/0, res);
914 // We should not update the storage cache here for NLDD local solves.
915 // This will reset the start-of-step storage to incorrect numbers when
916 // we do local solves, where the iteration number will start from 0,
917 // but the starting state may not be identical to the start-of-step state.
918 // Note that a full assembly must be done before local solves
919 // otherwise this will be left un-updated.
920 if (model_().newtonMethod().numIterations() == 0 && !isNlddLocalSolve) {
921 // Need to update the storage cache.
922 if (problem_().recycleFirstIterationStorage()) {
923 // Assumes nothing have changed in the system which
924 // affects masses calculated from primary variables.
925 model_().updateCachedStorage(globI, /*timeIdx=*/1, res);
926 }
927 else {
928 Dune::FieldVector<Scalar, numEq> tmp;
929 const IntensiveQuantities intQuantOld = model_().intensiveQuantities(globI, 1);
930 LocalResidual::computeStorage(tmp, intQuantOld);
931 model_().updateCachedStorage(globI, /*timeIdx=*/1, tmp);
932 }
933 }
934 res -= model_().cachedStorage(globI, 1);
935 }
936 else {
937 OPM_TIMEBLOCK_LOCAL(computeStorage0, Subsystem::Assembly);
938 Dune::FieldVector<Scalar, numEq> tmp;
939 const IntensiveQuantities intQuantOld = model_().intensiveQuantities(globI, 1);
940 LocalResidual::computeStorage(tmp, intQuantOld);
941 // assume volume do not change
942 res -= tmp;
943 }
944 res *= storefac;
945 bMat *= storefac;
946 residual_[globI] += res;
947 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
948 *diagMatAddress_[globI] += bMat;
949
950 // Cell-wise source terms.
951 // This will include well sources if SeparateSparseSourceTerms is false.
952 res = 0.0;
953 bMat = 0.0;
954 adres = 0.0;
955 if (separateSparseSourceTerms_) {
956 LocalResidual::computeSourceDense(adres, problem_(), intQuantsIn, globI, 0);
957 }
958 else {
959 LocalResidual::computeSource(adres, problem_(), intQuantsIn, globI, 0);
960 }
961 adres *= -volume;
962 setResAndJacobi(res, bMat, adres);
963 residual_[globI] += res;
964 //SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
965 *diagMatAddress_[globI] += bMat;
966 } // end of loop for cell globI.
967
968 // Add sparse source terms. For now only wells.
969 if (separateSparseSourceTerms_) {
970 problem_().wellModel().addReservoirSourceTerms(residual_, diagMatAddress_);
971 }
972
973 // Boundary terms. Only looping over cells with nontrivial bcs.
974 for (const auto& bdyInfo : boundaryInfo_) {
975 if (bdyInfo.bcdata.type == BCType::NONE) {
976 continue;
977 }
978
979 VectorBlock res(0.0);
980 MatrixBlock bMat(0.0);
981 ADVectorBlock adres(0.0);
982 const unsigned globI = bdyInfo.cell;
983 const IntensiveQuantities& insideIntQuants = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
984 LocalResidual::computeBoundaryFlux(adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
985 adres *= bdyInfo.bcdata.faceArea;
986 setResAndJacobi(res, bMat, adres);
987 residual_[globI] += res;
989 *diagMatAddress_[globI] += bMat;
990 }
991 }
992
993 void updateStoredTransmissibilities()
994 {
995 if (neighborInfo_.empty()) {
996 // This function was called before createMatrix_() was called.
997 // We call initFirstIteration_(), not createMatrix_(), because
998 // that will also initialize the residual consistently.
999 initFirstIteration_();
1000 }
1001
1002 const unsigned numCells = model_().numTotalDof();
1003#ifdef _OPENMP
1004#pragma omp parallel for
1005#endif
1006 for (unsigned globI = 0; globI < numCells; globI++) {
1007 auto nbInfos = neighborInfo_[globI]; // nbInfos will be a SparseTable<...>::mutable_iterator_range.
1008 for (auto& nbInfo : nbInfos) {
1009 const unsigned globJ = nbInfo.neighbor;
1010 nbInfo.res_nbinfo.trans = problem_().transmissibility(globI, globJ);
1011 }
1012 }
1013 }
1014
1015 Simulator* simulatorPtr_{};
1016
1017 // the jacobian matrix
1018 std::unique_ptr<SparseMatrixAdapter> jacobian_{};
1019
1020 // the right-hand side
1021 GlobalEqVector residual_;
1022
1023 LinearizationType linearizationType_{};
1024
1025 using ResidualNBInfo = typename LocalResidual::ResidualNBInfo;
1026 using NeighborInfoCPU = NeighborInfoStruct<ResidualNBInfo, MatrixBlock>;
1027
1028 SparseTable<NeighborInfoCPU> neighborInfo_{};
1029 std::vector<MatrixBlock*> diagMatAddress_{};
1030
1031 struct FlowInfo
1032 {
1033 int faceId;
1034 VectorBlock flow;
1035 unsigned int nncId;
1036 };
1037 SparseTable<FlowInfo> flowsInfo_;
1038 SparseTable<FlowInfo> floresInfo_;
1039
1040 struct VelocityInfo
1041 {
1042 VectorBlock velocity;
1043 };
1044 SparseTable<VelocityInfo> velocityInfo_;
1045
1046 using ScalarFluidState = typename IntensiveQuantities::ScalarFluidState;
1047 struct BoundaryConditionData
1048 {
1049 BCType type;
1050 VectorBlock massRate;
1051 unsigned pvtRegionIdx;
1052 unsigned boundaryFaceIndex;
1053 double faceArea;
1054 double faceZCoord;
1055 ScalarFluidState exFluidState;
1056 };
1057
1058 struct BoundaryInfo
1059 {
1060 unsigned int cell;
1061 int dir;
1062 unsigned int bfIndex;
1063 BoundaryConditionData bcdata;
1064 };
1065 std::vector<BoundaryInfo> boundaryInfo_;
1066
1067 bool separateSparseSourceTerms_ = false;
1068
1069 FullDomain<> fullDomain_;
1070};
1071} // namespace Opm
1072
1073#endif // TPFA_LINEARIZER
Declares the properties required by the black oil model.
The base class for the element-centered finite-volume discretization scheme.
Definition: ecfvdiscretization.hh:160
Definition: matrixblock.hh:229
Manages the initializing and running of time dependent problems.
Definition: simulator.hh:84
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:413
Vanguard & vanguard()
Return a reference to the grid manager of simulation.
Definition: simulator.hh:234
Problem & problem()
Return the object which specifies the pysical setup of the simulation.
Definition: simulator.hh:265
const GridView & gridView() const
Return the grid view for which the simulation is done.
Definition: simulator.hh:246
Model & model()
Return the physical model used in the simulation.
Definition: simulator.hh:252
The common code for the linearizers of non-linear systems of equations.
Definition: tpfalinearizer.hh:186
const auto & getFloresInfo() const
Return constant reference to the floresInfo.
Definition: tpfalinearizer.hh:436
const LinearizationType & getLinearizationType() const
Definition: tpfalinearizer.hh:420
const auto & getNeighborInfo() const
Definition: tpfalinearizer.hh:447
void updateBoundaryConditionData()
Definition: tpfalinearizer.hh:457
void linearize()
Linearize the full system of non-linear equations.
Definition: tpfalinearizer.hh:286
SparseMatrixAdapter & jacobian()
Definition: tpfalinearizer.hh:405
const auto & getFlowsInfo() const
Return constant reference to the flowsInfo.
Definition: tpfalinearizer.hh:428
std::map< unsigned, Constraints > constraintsMap() const
Returns the map of constraint degrees of freedom.
Definition: tpfalinearizer.hh:481
TpfaLinearizer()
Definition: tpfalinearizer.hh:235
void finalize()
Definition: tpfalinearizer.hh:363
void init(Simulator &simulator)
Initialize the linearizer.
Definition: tpfalinearizer.hh:259
void updateFlowsInfo()
Definition: tpfalinearizer.hh:759
void setResAndJacobi(VectorBlock &res, MatrixBlock &bMat, const ADVectorBlock &resid) const
Definition: tpfalinearizer.hh:742
static void registerParameters()
Register all run-time parameters for the Jacobian linearizer.
Definition: tpfalinearizer.hh:244
void linearizeDomain()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:302
const SparseMatrixAdapter & jacobian() const
Return constant reference to global Jacobian matrix backend.
Definition: tpfalinearizer.hh:402
void setLinearizationType(LinearizationType linearizationType)
Definition: tpfalinearizer.hh:417
void linearizeDomain(const SubDomainType &domain, const bool isNlddLocalSolve=false)
Linearize the part of the non-linear system of equations that is associated with a part of the spatia...
Definition: tpfalinearizer.hh:342
GlobalEqVector & residual()
Definition: tpfalinearizer.hh:414
void eraseMatrix()
Causes the Jacobian matrix to be recreated from scratch before the next iteration.
Definition: tpfalinearizer.hh:272
const auto & getVelocityInfo() const
Return constant reference to the velocityInfo.
Definition: tpfalinearizer.hh:444
void updateDiscretizationParameters()
Definition: tpfalinearizer.hh:452
void resetSystem_(const SubDomainType &domain)
Definition: tpfalinearizer.hh:485
void linearizeAuxiliaryEquations()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:370
const GlobalEqVector & residual() const
Return constant reference to global residual vector.
Definition: tpfalinearizer.hh:411
A small fixed-size square matrix class for use in CUDA kernels.
Definition: MiniMatrix.hpp:37
Definition: MiniVector.hpp:52
Declare the properties used by the infrastructure code of the finite volume discretizations.
Defines the common properties required by the porous medium multi-phase models.
@ NONE
Definition: DeferredLogger.hpp:46
Definition: blackoilnewtonmethodparams.hpp:31
HYPRE_IJMatrix createMatrix(HYPRE_Int N, HYPRE_Int dof_offset, const CommType &comm)
Create Hypre matrix.
Definition: HypreSetup.hpp:170
PointerView< T > make_view(const std::shared_ptr< T > &ptr)
Definition: gpu_smart_pointer.hpp:318
Definition: blackoilbioeffectsmodules.hh:43
typename Properties::Detail::GetPropImpl< TypeTag, Property >::type::type GetPropType
get the type alias defined in the property (equivalent to old macro GET_PROP_TYPE(....
Definition: propertysystem.hh:233
Definition: tpfalinearizer.hh:93
Storage cells
Definition: tpfalinearizer.hh:94
Definition: linearizationtype.hh:34
Definition: tpfalinearizer.hh:121
NeighborInfoStruct(unsigned int n, const ResidualNBInfoType &r, PtrType ptr)
Definition: tpfalinearizer.hh:138
NeighborInfoStruct()
Definition: tpfalinearizer.hh:146
NeighborInfoStruct(const NeighborInfoStruct< ResidualNBInfoType, OtherBlockType > &other)
Definition: tpfalinearizer.hh:127
BlockType * matBlockAddress
Definition: tpfalinearizer.hh:124
ResidualNBInfoType res_nbinfo
Definition: tpfalinearizer.hh:123
unsigned int neighbor
Definition: tpfalinearizer.hh:122
Definition: tpfalinearizer.hh:80
static constexpr bool value
Definition: tpfalinearizer.hh:80