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
51
53
54#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
55#include <opm/material/fluidsystems/BlackOilFluidSystemNonStatic.hpp>
56
57#include <cassert>
58#include <cstddef>
59#include <exception> // current_exception, rethrow_exception
60#include <iostream>
61#include <map>
62#include <memory>
63#include <numeric>
64#include <set>
65#include <stdexcept>
66#include <unordered_map>
67#include <utility>
68#include <vector>
69
70#if HAVE_OPENMP
71#include <omp.h>
72#endif
73
74#include <fmt/format.h>
75
76#include <opm/common/utility/gpuDecorators.hpp>
77#include <opm/common/utility/gpuistl_if_available.hpp>
78#include <opm/common/utility/pointerArithmetic.hpp>
79#if HAVE_CUDA
84#if USE_HIP
85#include <opm/simulators/linalg/gpuistl_hip/GpuSparseMatrixWrapper.hpp>
86#include <opm/simulators/linalg/gpuistl_hip/MiniMatrix.hpp>
87#include <opm/simulators/linalg/gpuistl_hip/MiniVector.hpp>
88#include <opm/simulators/linalg/gpuistl_hip/detail/gpusparse_matrix_operations.hpp>
89#else
94#endif
95#endif
96
97namespace Opm::Parameters {
98
99struct SeparateSparseSourceTerms { static constexpr bool value = false; };
100
101} // namespace Opm::Parameters
102
103namespace Opm {
104
105// forward declarations
106template<class TypeTag>
108
118template<class TypeTag>
120{
128
138
139 using Element = typename GridView::template Codim<0>::Entity;
140 using ElementIterator = typename GridView::template Codim<0>::Iterator;
141
142 using Vector = GlobalEqVector;
143
145
146 enum { numEq = getPropValue<TypeTag, Properties::NumEq>() };
147 enum { historySize = getPropValue<TypeTag, Properties::TimeDiscHistorySize>() };
148 enum { dimWorld = GridView::dimensionworld };
149
150 constexpr static bool runAssemblyOnGpu = getPropValue<TypeTag, Properties::RunAssemblyOnGpu>();
151
152 using MatrixBlockCPU = typename SparseMatrixAdapter::MatrixBlock;
153 using VectorBlockCPU = Dune::FieldVector<Scalar, numEq>;
154 using ADVectorBlockCPU = GetPropType<TypeTag, Properties::RateVector>;
155
156#if HAVE_CUDA
157 using MatrixBlockGPU = gpuistl::MiniMatrix<Scalar, numEq>;
158 using VectorBlockGPU = gpuistl::MiniVector<Scalar, numEq>;
159 using ADVectorBlockGPU = gpuistl::MiniVector<Evaluation, numEq>;
160 using MatrixBlock = std::conditional_t<runAssemblyOnGpu, MatrixBlockGPU, MatrixBlockCPU>;
161 using VectorBlock = std::conditional_t<runAssemblyOnGpu, VectorBlockGPU, VectorBlockCPU>;
162 using ADVectorBlock = std::conditional_t<runAssemblyOnGpu, ADVectorBlockGPU, ADVectorBlockCPU>;
163#else
164 using MatrixBlock = MatrixBlockCPU;
165 using VectorBlock = VectorBlockCPU;
166 using ADVectorBlock = ADVectorBlockCPU;
167#endif
168
169 static constexpr bool linearizeNonLocalElements =
170 getPropValue<TypeTag, Properties::LinearizeNonLocalElements>();
171 static constexpr bool enableFullyImplicitThermal = (getPropValue<TypeTag, Properties::EnergyModuleType>() == EnergyModules::FullyImplicitThermal);
172 static constexpr bool enableDiffusion = getPropValue<TypeTag, Properties::EnableDiffusion>();
173 static constexpr bool enableDispersion = getPropValue<TypeTag, Properties::EnableDispersion>();
174 static const bool enableBioeffects = getPropValue<TypeTag, Properties::EnableBioeffects>();
175
176 // copying the linearizer is not a good idea
177 TpfaLinearizer(const TpfaLinearizer&) = delete;
179
180public:
182 {
183 simulatorPtr_ = nullptr;
184 separateSparseSourceTerms_ = Parameters::Get<Parameters::SeparateSparseSourceTerms>();
185 exportIndex_=-1;
186 exportCount_=-1;
187 }
188
192 static void registerParameters()
193 {
194 Parameters::Register<Parameters::SeparateSparseSourceTerms>
195 ("Treat well source terms all in one go, instead of on a cell by cell basis.");
196 }
197
207 void init(Simulator& simulator)
208 {
209 simulatorPtr_ = &simulator;
210 eraseMatrix();
211 }
212
221 {
222 jacobian_.reset();
223 }
224
235 {
238 }
239
251 {
252 int succeeded;
253 try {
254 linearizeDomain(fullDomain_);
255 succeeded = 1;
256 }
257 catch (const std::exception& e) {
258 std::cout << "rank " << simulator_().gridView().comm().rank()
259 << " caught an exception while linearizing:" << e.what()
260 << "\n" << std::flush;
261 succeeded = 0;
262 }
263 catch (...) {
264 std::cout << "rank " << simulator_().gridView().comm().rank()
265 << " caught an exception while linearizing"
266 << "\n" << std::flush;
267 succeeded = 0;
268 }
269 OPM_TIMEBLOCK(linearizationSynch);
270 succeeded = simulator_().gridView().comm().min(succeeded);
271
272 if (!succeeded) {
273 throw NumericalProblem("A process did not succeed in linearizing the system");
274 }
275 }
276
289 template <class SubDomainType>
290 void linearizeDomain(const SubDomainType& domain)
291 {
292 OPM_TIMEBLOCK(linearizeDomain);
293 // we defer the initialization of the Jacobian matrix until here because the
294 // auxiliary modules usually assume the problem, model and grid to be fully
295 // initialized...
296 if (!jacobian_) {
297 initFirstIteration_();
298 }
299
300 // Called here because it is no longer called from linearize_().
301 if (problem_().iterationContext().inLocalSolve()) {
302 resetSystem_(domain);
303 }
304 else {
305 resetSystem_();
306 }
307
308 linearize_(domain);
309 }
310
311 void finalize()
312 { jacobian_->finalize(); }
313
319 {
320 OPM_TIMEBLOCK(linearizeAuxilaryEquations);
321 // flush possible local caches into matrix structure
322 jacobian_->commit();
323
324 auto& model = model_();
325 const auto& comm = simulator_().gridView().comm();
326 for (unsigned auxModIdx = 0; auxModIdx < model.numAuxiliaryModules(); ++auxModIdx) {
327 bool succeeded = true;
328 try {
329 model.auxiliaryModule(auxModIdx)->linearize(*jacobian_, residual_);
330 }
331 catch (const std::exception& e) {
332 succeeded = false;
333
334 std::cout << "rank " << simulator_().gridView().comm().rank()
335 << " caught an exception while linearizing:" << e.what()
336 << "\n" << std::flush;
337 }
338
339 succeeded = comm.min(succeeded);
340
341 if (!succeeded) {
342 throw NumericalProblem("linearization of an auxiliary equation failed");
343 }
344 }
345 }
346
350 const SparseMatrixAdapter& jacobian() const
351 { return *jacobian_; }
352
353 SparseMatrixAdapter& jacobian()
354 { return *jacobian_; }
355
359 const GlobalEqVector& residual() const
360 { return residual_; }
361
362 GlobalEqVector& residual()
363 { return residual_; }
364
368 void exportSystem(const int idx, std::string& tag, const char *path="export")
369 {
370 const bool export_sparsity = exportIndex_ == -1;
371
372 // increment indices and generate tag
373 exportCount_ = exportIndex_ == idx ? ++exportCount_ : 0;
374 exportIndex_ = idx;
375 tag = fmt::format(fmt::runtime("_{:03d}_{:02d}"), exportIndex_, exportCount_);
376
377 fmt::print(fmt::runtime("index = {:d}\n"), exportIndex_);
378 fmt::print(fmt::runtime("count = {:d}\n"), exportCount_);
379
380 Opm::exportSystem(jacobian_->istlMatrix(), residual_, export_sparsity, tag.c_str(), path);
381 }
382
384 { linearizationType_ = linearizationType; }
385
387 { return linearizationType_; }
388
394 const auto& getFlowsInfo() const
395 { return flowsInfo_; }
396
402 const auto& getFloresInfo() const
403 { return floresInfo_; }
404
411 const auto& getVelocityInfo() const
412 { return velocityInfo_; }
413
414 const auto& getNeighborInfo() const {
415 return neighborInfo_;
416 }
417
418
420 {
421 updateStoredTransmissibilities();
422 }
423
425 {
426 for (auto& bdyInfo : boundaryInfo_) {
427 const auto [type, massrateAD] = problem_().boundaryCondition(bdyInfo.cell, bdyInfo.dir);
428
429 // Strip the unnecessary (and zero anyway) derivatives off massrate.
430 VectorBlockCPU massrate(0.0);
431 for (std::size_t ii = 0; ii < massrate.size(); ++ii) {
432 massrate[ii] = massrateAD[ii].value();
433 }
434 if (type != BCType::NONE) {
435 const auto& exFluidState = problem_().boundaryFluidState(bdyInfo.cell, bdyInfo.dir);
436 bdyInfo.bcdata.type = type;
437 bdyInfo.bcdata.massRate = massrate;
438 bdyInfo.bcdata.exFluidState = exFluidState;
439 }
440 }
441 }
442
448 std::map<unsigned, Constraints> constraintsMap() const
449 { return {}; }
450
451 template <class SubDomainType>
452 void resetSystem_(const SubDomainType& domain)
453 {
454 if (!jacobian_) {
455 initFirstIteration_();
456 }
457 for (int globI : domain.cells) {
458 residual_[globI] = 0.0;
459 jacobian_->clearRow(globI, 0.0);
460 }
461 }
462
463private:
464 Simulator& simulator_()
465 { return *simulatorPtr_; }
466
467 const Simulator& simulator_() const
468 { return *simulatorPtr_; }
469
470 Problem& problem_()
471 { return simulator_().problem(); }
472
473 const Problem& problem_() const
474 { return simulator_().problem(); }
475
476 Model& model_()
477 { return simulator_().model(); }
478
479 const Model& model_() const
480 { return simulator_().model(); }
481
482 const GridView& gridView_() const
483 { return problem_().gridView(); }
484
485 void initFirstIteration_()
486 {
487 // initialize the BCRS matrix for the Jacobian of the residual function
488 createMatrix_();
489
490 // initialize the Jacobian matrix and the vector for the residual function
491 residual_.resize(model_().numTotalDof());
492 resetSystem_();
493
494 // initialize the sparse tables for Flows and Flores
495 createFlows_();
496 }
497
498 // Construct the BCRS matrix for the Jacobian of the residual function
499 void createMatrix_()
500 {
501 OPM_TIMEBLOCK(createMatrix);
502 if (!neighborInfo_.empty()) {
503 // It is ok to call this function multiple times, but it
504 // should not do anything if already called.
505 return;
506 }
507 const auto& model = model_();
508 Stencil stencil(gridView_(), model_().dofMapper());
509
510 // for the main model, find out the global indices of the neighboring degrees of
511 // freedom of each primary degree of freedom
512 using NeighborSet = std::set<unsigned>;
513 std::vector<NeighborSet> sparsityPattern(model.numTotalDof());
514 const Scalar gravity = problem_().gravity()[dimWorld - 1];
515 unsigned numCells = model.numTotalDof();
516 neighborInfo_.reserve(numCells, 6 * numCells); // Expect ~6 neighbors per cell
517 std::vector<NeighborInfoCPU> loc_nbinfo;
518 for (const auto& elem : elements(gridView_())) {
519 stencil.update(elem);
520
521 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
522 const unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
523 loc_nbinfo.resize(stencil.numDof() - 1); // Do not include the primary dof in neighborInfo_
524
525 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
526 const unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
527 sparsityPattern[myIdx].insert(neighborIdx);
528 if (dofIdx > 0) {
529 const Scalar trans = problem_().transmissibility(myIdx, neighborIdx);
530 const auto scvfIdx = dofIdx - 1;
531 const auto& scvf = stencil.interiorFace(scvfIdx);
532 const Scalar area = scvf.area();
533 const Scalar Vin = problem_().model().dofTotalVolume(myIdx);
534 const Scalar Vex = problem_().model().dofTotalVolume(neighborIdx);
535 const Scalar zIn = problem_().dofCenterDepth(myIdx);
536 const Scalar zEx = problem_().dofCenterDepth(neighborIdx);
537 const Scalar dZg = (zIn - zEx)*gravity;
538 const Scalar thpresInToEx = problem_().thresholdPressure(myIdx, neighborIdx);
539 const Scalar thpresExToIn = problem_().thresholdPressure(neighborIdx, myIdx);
540 const auto dirId = scvf.dirId();
541 auto faceDir = dirId < 0 ? FaceDir::DirEnum::Unknown
542 : FaceDir::FromIntersectionIndex(dirId);
543 ResidualNBInfo nbinfo{trans,
544 area,
545 thpresInToEx,
546 thpresExToIn,
547 dZg,
548 faceDir,
549 Vin,
550 Vex,
551 {},
552 {},
553 {},
554 {}};
555 if constexpr (enableFullyImplicitThermal) {
556 nbinfo.inAlpha = problem_().thermalHalfTransmissibility(myIdx, neighborIdx);
557 nbinfo.outAlpha = problem_().thermalHalfTransmissibility(neighborIdx, myIdx);
558 }
559 if constexpr (enableDiffusion) {
560 nbinfo.diffusivity = problem_().diffusivity(myIdx, neighborIdx);
561 }
562 if constexpr (enableDispersion) {
563 nbinfo.dispersivity = problem_().dispersivity(myIdx, neighborIdx);
564 }
565 loc_nbinfo[dofIdx - 1] = NeighborInfoCPU{neighborIdx, nbinfo, nullptr};
566 }
567 }
568 neighborInfo_.appendRow(loc_nbinfo.begin(), loc_nbinfo.end());
569 if (problem_().nonTrivialBoundaryConditions()) {
570 for (unsigned bfIndex = 0; bfIndex < stencil.numBoundaryFaces(); ++bfIndex) {
571 const auto& bf = stencil.boundaryFace(bfIndex);
572 const int dir_id = bf.dirId();
573 // not for NNCs
574 if (dir_id < 0) {
575 continue;
576 }
577 const auto [type, massrateAD] = problem_().boundaryCondition(myIdx, dir_id);
578 // Strip the unnecessary (and zero anyway) derivatives off massrate.
579 VectorBlockCPU massrate(0.0);
580 for (std::size_t ii = 0; ii < massrate.size(); ++ii) {
581 massrate[ii] = massrateAD[ii].value();
582 }
583 const auto& exFluidState = problem_().boundaryFluidState(myIdx, dir_id);
584 BoundaryConditionDataCPU bcdata {type,
585 massrate,
586 exFluidState.pvtRegionIndex(),
587 bfIndex,
588 bf.area(),
589 bf.integrationPos()[dimWorld - 1],
590 exFluidState};
591 boundaryInfo_.push_back({myIdx, dir_id, bfIndex, bcdata});
592 }
593 }
594 }
595 }
596
597 // add the additional neighbors and degrees of freedom caused by the auxiliary
598 // equations
599 const std::size_t numAuxMod = model.numAuxiliaryModules();
600 for (unsigned auxModIdx = 0; auxModIdx < numAuxMod; ++auxModIdx) {
601 model.auxiliaryModule(auxModIdx)->addNeighbors(sparsityPattern);
602 }
603
604 // allocate raw matrix
605 jacobian_ = std::make_unique<SparseMatrixAdapter>(simulator_());
606 diagMatAddress_.resize(numCells);
607 // create matrix structure based on sparsity pattern
608 jacobian_->reserve(sparsityPattern);
609 for (unsigned globI = 0; globI < numCells; globI++) {
610 const auto& nbInfos = neighborInfo_[globI];
611 diagMatAddress_[globI] = jacobian_->blockAddress(globI, globI);
612 for (auto& nbInfo : nbInfos) {
613 nbInfo.matBlockAddress = jacobian_->blockAddress(nbInfo.neighbor, globI);
614 }
615 }
616
617#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
618 gpuJacobian_.reset(new gpuistl::GpuSparseMatrixWrapper<Scalar>(
620 gpuBufferDiagMatAddress_.reset(new gpuistl::GpuBuffer<MatrixBlockGPU*>(
621 gpuistl::detail::getDiagPtrsTyped<MatrixBlockGPU>(*gpuJacobian_)));
622#endif
623
624 // Create dummy full domain.
625 fullDomain_.cells.resize(numCells);
626 std::iota(fullDomain_.cells.begin(), fullDomain_.cells.end(), 0);
627 }
628
629 // reset the global linear system of equations.
630 void resetSystem_()
631 {
632 residual_ = 0.0;
633 // zero all matrix entries
634 jacobian_->clear();
635
636#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
637 gpuJacobian_->setToZero();
638#endif
639 }
640
641 // Initialize the flows, flores, and velocity sparse tables
642 void createFlows_()
643 {
644 OPM_TIMEBLOCK(createFlows);
645 // If FLOWS/FLORES is set in any RPTRST in the schedule, then we initializate the sparse tables.
646 // If DISPERC is in the deck, we initialize the sparse table here as well.
647 const bool anyFlows = simulator_().problem().eclWriter().outputModule().getFlows().anyFlows();
648 const auto& blockFlows = simulator_().problem().eclWriter().outputModule().getFlows().blockFlows();
649 const auto& blockVelocity = simulator_().problem().eclWriter().outputModule().getFlows().blockVelocity();
650 const bool isTemp = simulator_().vanguard().eclState().getSimulationConfig().isTemp();
651 const bool anyFlores = simulator_().problem().eclWriter().outputModule().getFlows().anyFlores() || isTemp;
652 const bool dispersionActive = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
653 if (!dispersionActive && !enableBioeffects && blockVelocity.empty()
654 && !((anyFlows || !blockFlows.empty()) && flowsInfo_.empty())
655 && !(anyFlores && floresInfo_.empty())) {
656 return;
657 }
658 const auto& model = model_();
659 const auto& nncOutput = simulator_().problem().eclWriter().getOutputNnc().front();
660 Stencil stencil(gridView_(), model_().dofMapper());
661 const unsigned numCells = model.numTotalDof();
662 std::unordered_multimap<int, std::pair<int, int>> nncIndices;
663 std::vector<FlowInfo> loc_flinfo;
664 std::vector<VelocityInfo> loc_vlinfo;
665 unsigned int nncId = 0;
666 VectorBlock flow(0.0);
667
668 // Create a nnc structure to use fast lookup
669 for (unsigned nncIdx = 0; nncIdx < nncOutput.size(); ++nncIdx) {
670 const int ci1 = nncOutput[nncIdx].cell1;
671 const int ci2 = nncOutput[nncIdx].cell2;
672 nncIndices.emplace(ci1, std::make_pair(ci2, nncIdx));
673 }
674
675 if (anyFlows) {
676 flowsInfo_.reserve(numCells, 6 * numCells);
677 }
678 else if (!blockFlows.empty()) {
679 flowsInfo_.reserve(numCells, 6 * blockFlows.size());
680 }
681 if (anyFlores) {
682 floresInfo_.reserve(numCells, 6 * numCells);
683 }
684 if (dispersionActive || enableBioeffects) {
685 velocityInfo_.reserve(numCells, 6 * numCells);
686 }
687 else if (!blockVelocity.empty()) {
688 velocityInfo_.reserve(numCells, 6 * blockVelocity.size());
689 }
690
691 for (const auto& elem : elements(gridView_())) {
692 stencil.update(elem);
693 for (unsigned primaryDofIdx = 0; primaryDofIdx < stencil.numPrimaryDof(); ++primaryDofIdx) {
694 const unsigned myIdx = stencil.globalSpaceIndex(primaryDofIdx);
695 bool blockFlowFound = false;
696 bool blockVelocityFound = false;
697 if (!blockFlows.empty()) {
698 if (std::ranges::binary_search(blockFlows,
699 simulator_().vanguard().cartesianIndex(myIdx))) {
700 blockFlowFound = true;
701 }
702 else {
703 flowsInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.begin());
704 if (!dispersionActive && !enableBioeffects && !anyFlores && blockVelocity.empty()) {
705 continue;
706 }
707 }
708 }
709 if (!blockVelocity.empty() && !(dispersionActive || enableBioeffects)) {
710 if (std::ranges::binary_search(blockVelocity,
711 simulator_().vanguard().cartesianIndex(myIdx))) {
712 blockVelocityFound = true;
713 }
714 else {
715 velocityInfo_.appendRow(loc_vlinfo.begin(), loc_vlinfo.begin());
716 if (!anyFlows && blockFlows.empty() && !anyFlores) {
717 continue;
718 }
719 }
720 }
721 const int numFaces = stencil.numBoundaryFaces() + stencil.numInteriorFaces();
722 loc_flinfo.resize(numFaces);
723 loc_vlinfo.resize(stencil.numDof() - 1);
724
725 for (unsigned dofIdx = 0; dofIdx < stencil.numDof(); ++dofIdx) {
726 const unsigned neighborIdx = stencil.globalSpaceIndex(dofIdx);
727 if (dofIdx > 0) {
728 const auto scvfIdx = dofIdx - 1;
729 const auto& scvf = stencil.interiorFace(scvfIdx);
730 int faceId = scvf.dirId();
731 const int cartMyIdx = simulator_().vanguard().cartesianIndex(myIdx);
732 const int cartNeighborIdx = simulator_().vanguard().cartesianIndex(neighborIdx);
733 const auto& range = nncIndices.equal_range(cartMyIdx);
734 for (auto it = range.first; it != range.second; ++it) {
735 if (it->second.first == cartNeighborIdx){
736 // -1 gives problem since is used for the nncInput from the deck
737 faceId = -2;
738 // the index is stored to be used for writting the outputs
739 nncId = it->second.second;
740 }
741 }
742 loc_flinfo[dofIdx - 1] = FlowInfo{faceId, flow, nncId};
743 loc_vlinfo[dofIdx - 1] = VelocityInfo{faceId, flow};
744 }
745 }
746
747 for (unsigned bdfIdx = 0; bdfIdx < stencil.numBoundaryFaces(); ++bdfIdx) {
748 const auto& scvf = stencil.boundaryFace(bdfIdx);
749 const int faceId = scvf.dirId();
750 loc_flinfo[stencil.numInteriorFaces() + bdfIdx] = FlowInfo{faceId, flow, nncId};
751 }
752
753 if (anyFlows || blockFlowFound) {
754 flowsInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
755 }
756 if (anyFlores) {
757 floresInfo_.appendRow(loc_flinfo.begin(), loc_flinfo.end());
758 }
759 if (dispersionActive || enableBioeffects || blockVelocityFound) {
760 velocityInfo_.appendRow(loc_vlinfo.begin(), loc_vlinfo.end());
761 }
762 }
763 }
764 }
765
766public:
767 template <class VectorBlockType, class MatrixBlockType, class ADVectorBlockType>
768 OPM_HOST_DEVICE static void
769 setResAndJacobi(VectorBlockType& res, MatrixBlockType& bMat, const ADVectorBlockType& resid)
770 {
771 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
772 res[eqIdx] = resid[eqIdx].value();
773 }
774
775 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
776 for (unsigned pvIdx = 0; pvIdx < numEq; ++pvIdx) {
777 // A[dofIdx][focusDofIdx][eqIdx][pvIdx] is the partial derivative of
778 // the residual function 'eqIdx' for the degree of freedom 'dofIdx'
779 // with regard to the focus variable 'pvIdx' of the degree of freedom
780 // 'focusDofIdx'
781 bMat[eqIdx][pvIdx] = resid[eqIdx].derivative(pvIdx);
782 }
783 }
784 }
785
787 {
788 OPM_TIMEBLOCK(updateFlows);
789 const bool enableFlows = simulator_().problem().eclWriter().outputModule().getFlows().hasFlows();
790 const auto& blockFlows = simulator_().problem().eclWriter().outputModule().getFlows().blockFlows();
791 // We reuse the fluxes in the TEMP option
792 const bool isTemp = simulator_().vanguard().eclState().getSimulationConfig().isTemp();
793 const bool enableFlores = simulator_().problem().eclWriter().outputModule().getFlows().hasFlores() || isTemp;
794 if (!enableFlows && !enableFlores && blockFlows.empty()) {
795 return;
796 }
797 const unsigned int numCells = model_().numTotalDof();
798#ifdef _OPENMP
799#pragma omp parallel for
800#endif
801 for (unsigned globI = 0; globI < numCells; ++globI) {
802 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell, Subsystem::Assembly);
803 const auto& nbInfos = neighborInfo_[globI];
804 ADVectorBlock adres(0.0);
805 ADVectorBlock darcyFlux(0.0);
806 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
807 // Flux term.
808 {
809 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell, Subsystem::Assembly);
810 short loc = 0;
811 for (const auto& nbInfo : nbInfos) {
812 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachFace, Subsystem::Assembly);
813 const unsigned globJ = nbInfo.neighbor;
814 assert(globJ != globI);
815 adres = 0.0;
816 darcyFlux = 0.0;
817 const IntensiveQuantities& intQuantsEx = model_().intensiveQuantities(globJ, /*timeIdx*/ 0);
818 LocalResidual::computeFlux(adres, darcyFlux, globI, globJ, intQuantsIn,
819 intQuantsEx, nbInfo.res_nbinfo, problem_().moduleParams());
820 adres *= nbInfo.res_nbinfo.faceArea;
821 if (!blockFlows.empty()) {
822 if (std::ranges::binary_search(blockFlows,
823 simulator_().vanguard().cartesianIndex(globI))) {
824 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
825 flowsInfo_[globI][loc].flow[eqIdx] = adres[eqIdx].value();
826 }
827 }
828 }
829 else if (enableFlows) {
830 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
831 flowsInfo_[globI][loc].flow[eqIdx] = adres[eqIdx].value();
832 }
833 }
834 if (enableFlores) {
835 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
836 floresInfo_[globI][loc].flow[eqIdx] = darcyFlux[eqIdx].value();
837 }
838 }
839 ++loc;
840 }
841 }
842 }
843
844 // Boundary terms. Only looping over cells with nontrivial bcs.
845 for (const auto& bdyInfo : boundaryInfo_) {
846 if (bdyInfo.bcdata.type == BCType::NONE) {
847 continue;
848 }
849
850 ADVectorBlockCPU adres(0.0);
851 const unsigned globI = bdyInfo.cell;
852 const auto& nbInfos = neighborInfo_[globI];
853 const IntensiveQuantities& insideIntQuants = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
854 LocalResidual::computeBoundaryFlux(adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
855 adres *= bdyInfo.bcdata.faceArea;
856 const unsigned bfIndex = bdyInfo.bfIndex;
857 if (enableFlows) {
858 for (unsigned eqIdx = 0; eqIdx < numEq; ++eqIdx) {
859 flowsInfo_[globI][nbInfos.size() + bfIndex].flow[eqIdx] = adres[eqIdx].value();
860 }
861 }
862 // TODO also store Flores?
863 }
864 }
865
866private:
867 template <class SubDomainType>
868 void linearize_(const SubDomainType& domain)
869 {
870 constexpr bool run_assembly_on_gpu = getPropValue<TypeTag, Properties::RunAssemblyOnGpu>();
871
872 // This check should be removed once this is addressed by
873 // for example storing the previous timesteps' values for
874 // rsmax (for DRSDT) and similar.
875 if (!problem_().recycleFirstIterationStorage()) {
876 if (!model_().storeIntensiveQuantities() && !model_().enableStorageCache()) {
877 OPM_THROW(std::runtime_error, "Must have cached either IQs or storage when we cannot recycle.");
878 }
879 }
880
881 const double dt = simulator_().timeStepSize();
882
883 OPM_TIMEBLOCK(linearize);
884
885 // We do not call resetSystem_() here, since that will set
886 // the full system to zero, not just our part.
887 // Instead, that must be called before starting the linearization.
888 const bool dispersionActive = simulator_().vanguard().eclState().getSimulationConfig().rock_config().dispersion();
889 const unsigned int numCells = domain.cells.size();
890
891 if constexpr (!run_assembly_on_gpu) {
892 linearize_parallelization_wrapper<run_assembly_on_gpu, LocalResidual>(
893 numCells /*numCells*/,
894 domain,
895 neighborInfo_,
896 diagMatAddress_,
897 residual_,
898 model_(),
899 dt,
900 dispersionActive,
901 problem_());
902
903 linearize_bc<IntensiveQuantities, Model, LocalResidual>(
904 diagMatAddress_, residual_, boundaryInfo_);
905 } else {
906#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
907 // Make sure we have can have the domain on the GPU.
908 if constexpr (std::is_same_v<SubDomainType, FullDomain<>>) {
909
910 using GpuParams = TpfaLinearizerGpuParams<TypeTag>;
911
912 int constexpr blockSize = 256; // Experimentally this is a good value for multiple
913 // GPUs. Autotune this later.
914
915 GpuParams gpuParams(domain,
916 neighborInfo_,
917 *gpuJacobian_,
918 *gpuBufferDiagMatAddress_,
919 jacobian_->istlMatrix(),
920 residual_,
921 boundaryInfo_,
922 model_(),
923 problem_(),
924 numCells);
925
926 linearize_parallelization_wrapper<run_assembly_on_gpu,
927 typename GpuParams::LocalResidualGPU>(
928 numCells,
929 gpuParams.domainView(),
930 gpuParams.neighborInfoView(),
931 gpuParams.diagMatAddressView(),
932 gpuParams.residualView(),
933 gpuParams.modelView(),
934 dt,
935 dispersionActive,
936 gpuParams.flowProblemView());
937
938 if (gpuParams.boundaryInfoSize() > 0) {
939 auto boundaryInfoView = gpuParams.boundaryInfoView();
940 linearize_bc_threadsafe<TpfaLinearizer<TypeTag>,
941 typename GpuParams::GPUBOIQ,
942 decltype(gpuParams.modelView()),
943 typename GpuParams::LocalResidualGPU>
944 <<<((gpuParams.boundaryInfoSize() + blockSize - 1) / blockSize),
945 blockSize>>>(gpuParams.diagMatAddressView(),
946 gpuParams.residualView(),
947 boundaryInfoView,
948 gpuParams.modelView(),
949 gpuParams.flowProblemView());
950 }
951
952 // The memory copies here are synchronous and in the default stream,
953 // guaranteeing that the GPU kernels have completed.
954 gpuParams.copyResidualToHost(residual_, numCells);
955 gpuParams.copyJacobianToHost(*jacobian_, *gpuJacobian_);
956 } else {
957 OPM_THROW(std::logic_error, "Only FullDomain is supported on GPU");
958 }
959#else
960 OPM_THROW(std::logic_error,
961 "Trying to run GPU assembly without compiling with GPU support");
962#endif
963 }
964
965 // Handle source terms separately as we want the functionality for CPU and GPU cases
966 // but for now we cannot handle this inside gpu kernels due to the use of problem_()
967 linearize_source_terms(numCells, domain);
968 }
969
970 template <bool useGPU,
971 class LocalResidualT,
972 class ModelClass,
973 class DiagPtrType,
974 class DomainType,
975 class NeighborSparseTable,
976 class ResidualType,
977 class ProblemT>
978 void linearize_parallelization_wrapper(const unsigned int numCells,
979 const DomainType& domain,
980 const NeighborSparseTable& neighborInfo,
981 DiagPtrType& diagMatAddress,
982 ResidualType& residual,
983 const ModelClass& model,
984 Scalar dt,
985 [[maybe_unused]] bool dispersionActive,
986 const ProblemT& problem)
987 {
988 if constexpr (useGPU) {
989 static_assert(!enableBioeffects && "Bioeffects not yet supported on GPU");
990 assert(!dispersionActive && "Dispersion not yet supported on GPU");
991#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
992 int constexpr blockSize = 256;
993 kernel_linearize<TpfaLinearizer<TypeTag>,
994 ModelClass,
995 LocalResidualT,
996 DiagPtrType,
997 Scalar,
998 DomainType,
999 NeighborSparseTable,
1000 ResidualType,
1001 ProblemT>
1002 <<<((numCells + blockSize - 1) / blockSize), blockSize>>>(numCells,
1003 domain,
1004 neighborInfo,
1005 diagMatAddress,
1006 residual,
1007 model,
1008 dt,
1009 problem);
1010#else
1011 OPM_THROW(std::runtime_error, "Trying to run GPU code without GPU support");
1012#endif
1013 } else {
1014#ifdef _OPENMP
1015#pragma omp parallel for
1016#endif
1017 for (unsigned ii = 0; ii < numCells; ++ii) {
1018 linearize_cell<false, LocalResidual>(ii,
1019 domain,
1020 neighborInfo,
1021 diagMatAddress,
1022 residual,
1023 model,
1024 velocityInfo_,
1025 dt,
1026 problem);
1027 }
1028 }
1029 }
1030
1031 template <class SubDomainType>
1032 void linearize_source_terms(unsigned int numCells, const SubDomainType& domain)
1033 {
1034#ifdef _OPENMP
1035#pragma omp parallel for
1036#endif
1037 for (unsigned ii = 0; ii < numCells; ++ii) {
1038 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell, Subsystem::Assembly);
1039 const unsigned globI = domain.cells[ii];
1040 VectorBlockCPU res(0.0);
1041 MatrixBlockCPU bMat(0.0);
1042 ADVectorBlockCPU adres(0.0);
1043 const IntensiveQuantities& intQuantsIn = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
1044 const double volume = model_().dofTotalVolume(globI);
1045
1046 // Cell-wise source terms.
1047 // This will include well sources if SeparateSparseSourceTerms is false.
1048 res = 0.0;
1049 bMat = 0.0;
1050 adres = 0.0;
1051 if (separateSparseSourceTerms_) {
1052 LocalResidual::computeSourceDense(adres, problem_(), intQuantsIn, globI, 0);
1053 } else {
1054 LocalResidual::computeSource(adres, problem_(), intQuantsIn, globI, 0);
1055 }
1056 adres *= -volume;
1057 setResAndJacobi(res, bMat, adres);
1058 residual_[globI] += res;
1059 // SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
1060 *diagMatAddress_[globI] += bMat;
1061 } // end of loop for cell globI.
1062
1063 // Add sparse source terms. For now only wells.
1064 if (separateSparseSourceTerms_) {
1065 problem_().wellModel().addReservoirSourceTerms(residual_, diagMatAddress_);
1066 }
1067 }
1068
1069public:
1070 template <bool useGPU,
1071 class LocalResidualT,
1072 class ProblemType,
1073 class VelocityInfoType,
1074 class ModelClass,
1075 class DiagPtrType,
1076 class DomainType,
1077 class NeighborSparseTable,
1078 class ResidualType>
1079 OPM_HOST_DEVICE static void linearize_cell(const unsigned int ii,
1080 const DomainType& domain,
1081 const NeighborSparseTable& neighborInfo,
1082 const DiagPtrType& diagMatAddress,
1083 ResidualType& residual,
1084 const ModelClass& model,
1085 VelocityInfoType& velocityInfo,
1086 const Scalar dt,
1087 const ProblemType& problem)
1088 {
1089#if OPM_IS_INSIDE_HOST_FUNCTION
1090 OPM_TIMEBLOCK_LOCAL(linearizationForEachCell, Subsystem::Assembly);
1091#endif
1092 const unsigned globI = domain.cells[ii];
1093 const auto& nbInfos = neighborInfo[globI];
1094 VectorBlock res(0.0);
1095 MatrixBlock bMat(0.0);
1096 ADVectorBlock adres(0.0);
1097 ADVectorBlock darcyFlux(0.0);
1098 const auto& intQuantsIn = model.intensiveQuantities(globI, /*timeIdx*/ 0);
1099
1100 // Flux term.
1101 {
1102#if OPM_IS_INSIDE_HOST_FUNCTION
1103 OPM_TIMEBLOCK_LOCAL(fluxCalculationForEachCell, Subsystem::Assembly);
1104#endif
1105 short loc = 0;
1106 for (const auto& nbInfo : nbInfos) {
1107 const unsigned globJ = nbInfo.neighbor;
1108 res = 0.0;
1109 bMat = 0.0;
1110 adres = 0.0;
1111 darcyFlux = 0.0;
1112
1113 const auto& intQuantsEx = model.intensiveQuantities(globJ, /*timeIdx*/ 0);
1114
1115 LocalResidualT::computeFlux(adres,
1116 darcyFlux,
1117 globI,
1118 globJ,
1119 intQuantsIn,
1120 intQuantsEx,
1121 nbInfo.res_nbinfo,
1122 problem.moduleParams());
1123
1124 adres *= nbInfo.res_nbinfo.faceArea;
1125
1126 // currently not supported on GPU
1127 if constexpr (!useGPU) {
1128 // Use std::cmp_less because sparseTable.size() returns signed integer, and globI is unsigned
1129 if (std::cmp_less(globI, velocityInfo.size())) {
1130 if (velocityInfo.rowSize(globI) > 0) {
1131 for (unsigned phaseIdx = 0; phaseIdx < numEq; ++phaseIdx) {
1132 velocityInfo[globI][loc].velocity[phaseIdx]
1133 = darcyFlux[phaseIdx].value() / nbInfo.res_nbinfo.faceArea;
1134 }
1135 }
1136 }
1137 }
1138
1139 setResAndJacobi(res, bMat, adres);
1140 residual[globI] += res;
1141 // SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
1142 *diagMatAddress[globI] += bMat;
1143 bMat *= -1.0;
1144 // SparseAdapter syntax: jacobian_->addToBlock(globJ, globI, bMat);
1145 *nbInfo.matBlockAddress += bMat;
1146 ++loc;
1147 }
1148 }
1149
1150 // Accumulation term.
1151 adres = 0.0;
1152 {
1153 LocalResidualT::template computeStorage<Evaluation>(adres, intQuantsIn);
1154 }
1155 setResAndJacobi(res, bMat, adres);
1156
1157 if constexpr (!useGPU) { // Cached storage not enabled for GPU
1158 // Either use cached storage term, or compute it on the fly.
1159 if (model.enableStorageCache()) {
1160 // The cached storage for timeIdx 0 (current time) is not
1161 // used, but after storage cache is shifted at the end of the
1162 // timestep, it will become cached storage for timeIdx 1.
1163 model.updateCachedStorage(globI, /*timeIdx=*/0, res);
1164 // We should not update the storage cache here for NLDD local solves.
1165 // This will reset the start-of-step storage to incorrect numbers when
1166 // we do local solves, where the iteration number will start from 0,
1167 // but the starting state may not be identical to the start-of-step state.
1168 // Note that a full assembly must be done before local solves
1169 // otherwise this will be left un-updated.
1170 if (problem.iterationContext().isFirstGlobalIteration()) {
1171 // Need to update the storage cache.
1172 if (problem.recycleFirstIterationStorage()) {
1173 // Assumes nothing have changed in the system which
1174 // affects masses calculated from primary variables.
1175 model.updateCachedStorage(globI, 1, res);
1176 } else {
1177 VectorBlock tmp;
1178 const auto& intQuantOld = model.intensiveQuantities(globI, 1);
1179 LocalResidualT::template computeStorage<Scalar>(tmp, intQuantOld);
1180 model.updateCachedStorage(globI, 1, tmp);
1181 }
1182 }
1183 res -= model.cachedStorage(globI, 1);
1184 } else {
1185#if OPM_IS_INSIDE_HOST_FUNCTION
1186 OPM_TIMEBLOCK_LOCAL(computeStorage0, Subsystem::Assembly);
1187#endif
1188 VectorBlock tmp;
1189 const auto& intQuantOld = model.intensiveQuantities(globI, 1);
1190 LocalResidualT::template computeStorage<Scalar>(tmp, intQuantOld);
1191 // assume volume do not change
1192 res -= tmp;
1193 }
1194 } else {
1195 VectorBlock tmp;
1196 const auto& intQuantOld = model.intensiveQuantities(globI, 1);
1197 LocalResidualT::template computeStorage<Scalar>(tmp, intQuantOld);
1198 // assume volume do not change
1199 res -= tmp;
1200 }
1201
1202 const Scalar volume = model.dofTotalVolume(globI);
1203 const Scalar storefac = volume / dt;
1204 res *= storefac;
1205 bMat *= storefac;
1206 residual[globI] += res;
1207 // SparseAdapter syntax: jacobian_->addToBlock(globI, globI, bMat);
1208 *diagMatAddress[globI] += bMat;
1209 }
1210
1211#if HAVE_CUDA && OPM_IS_COMPILING_WITH_GPU_COMPILER
1212 template <class LocalIntensiveQuantities,
1213 class LocalResidualT,
1214 class ModelT,
1215 class DiagPtrType,
1216 class ResidualT,
1217 class BoundaryInfoT,
1218 class ProblemT>
1219 OPM_HOST_DEVICE static void linearize_bc_threadsafe_single_cell(DiagPtrType diagMatAdress,
1220 ResidualT residual,
1221 const BoundaryInfoT boundaryInfoElement,
1222 ModelT model,
1223 ProblemT problem)
1224 {
1226 constexpr int numEq = getPropValue<TypeTag, Properties::NumEq>();
1227 if (boundaryInfoElement.bcdata.type != BCType::NONE) {
1228 VectorBlock res(0.0);
1229 MatrixBlock bMat(0.0);
1230 ADVectorBlock adres(0.0);
1231 const unsigned globI = boundaryInfoElement.cell;
1232 const LocalIntensiveQuantities& insideIntQuants
1233 = model.intensiveQuantities(globI, /*timeIdx*/ 0);
1234 if constexpr (!std::is_empty_v<GetPropType<TypeTag, Properties::FluidSystem>>) {
1235 LocalResidualT::computeBoundaryFlux(
1236 adres, problem, boundaryInfoElement.bcdata, insideIntQuants, globI);
1237 }
1238 adres *= boundaryInfoElement.bcdata.faceArea;
1240 auto* residualPtr = &(residual.data()[globI]);
1241 for (int i = 0; i < numEq; ++i) {
1242 atomicAdd(&((*residualPtr)[i]), res[i]);
1243 }
1244 auto* matPtr = diagMatAdress[globI];
1245 for (int row = 0; row < bMat.size(); ++row) {
1246 for (int col = 0; col < bMat.size(); ++col) {
1247 Scalar* elemPtr = &((*matPtr)[row][col]);
1248 atomicAdd(elemPtr, bMat[row][col]);
1249 }
1250 }
1251 }
1252 }
1253#endif
1254
1255private:
1256 template <class LocalIntensiveQuantities,
1257 class ModelClass,
1258 class LocalResidualT,
1259 class DiagPtrType,
1260 class ResidualType,
1261 class BoundaryInfoT>
1262 void linearize_bc(DiagPtrType& diagMatAdress,
1263 ResidualType& residual,
1264 const BoundaryInfoT& boundaryInfo)
1265 {
1266 // Boundary terms. Only looping over cells with nontrivial bcs.
1267 for (const auto& bdyInfo : boundaryInfo) {
1268 if (bdyInfo.bcdata.type == BCType::NONE) {
1269 continue;
1270 }
1271 VectorBlock res(0.0);
1272 MatrixBlock bMat(0.0);
1273 ADVectorBlock adres(0.0);
1274 const unsigned globI = bdyInfo.cell;
1275 const LocalIntensiveQuantities& insideIntQuants
1276 = model_().intensiveQuantities(globI, /*timeIdx*/ 0);
1277 LocalResidual::computeBoundaryFlux(
1278 adres, problem_(), bdyInfo.bcdata, insideIntQuants, globI);
1279 adres *= bdyInfo.bcdata.faceArea;
1280 setResAndJacobi(res, bMat, adres);
1281 residual[globI] += res;
1283 *diagMatAdress[globI] += bMat;
1284 }
1285 }
1286
1287 void updateStoredTransmissibilities()
1288 {
1289 if (neighborInfo_.empty()) {
1290 // This function was called before createMatrix_() was called.
1291 // We call initFirstIteration_(), not createMatrix_(), because
1292 // that will also initialize the residual consistently.
1293 initFirstIteration_();
1294 }
1295
1296 const unsigned numCells = model_().numTotalDof();
1297#ifdef _OPENMP
1298#pragma omp parallel for
1299#endif
1300 for (unsigned globI = 0; globI < numCells; globI++) {
1301 auto nbInfos = neighborInfo_[globI]; // nbInfos will be a SparseTable<...>::mutable_iterator_range.
1302 for (auto& nbInfo : nbInfos) {
1303 const unsigned globJ = nbInfo.neighbor;
1304 nbInfo.res_nbinfo.trans = problem_().transmissibility(globI, globJ);
1305 }
1306 }
1307 }
1308
1309 Simulator* simulatorPtr_{};
1310
1311 // the jacobian matrix
1312 std::unique_ptr<SparseMatrixAdapter> jacobian_{};
1313#if HAVE_CUDA
1314 std::unique_ptr<gpuistl::GpuSparseMatrixWrapper<Scalar>> gpuJacobian_;
1315 std::unique_ptr<gpuistl::GpuBuffer<MatrixBlockGPU*>> gpuBufferDiagMatAddress_;
1316#endif
1317
1318 // the right-hand side
1319 GlobalEqVector residual_;
1320
1321 LinearizationType linearizationType_{};
1322
1323 using ResidualNBInfo = typename LocalResidual::ResidualNBInfo;
1324 using NeighborInfoCPU = NeighborInfoStruct<ResidualNBInfo, MatrixBlockCPU>;
1325
1326 SparseTable<NeighborInfoCPU> neighborInfo_{};
1327 std::vector<MatrixBlockCPU*> diagMatAddress_ {};
1328
1329 struct FlowInfo
1330 {
1331 int faceId;
1332 VectorBlock flow;
1333 unsigned int nncId;
1334 };
1335 SparseTable<FlowInfo> flowsInfo_;
1336 SparseTable<FlowInfo> floresInfo_;
1337
1338 struct VelocityInfo
1339 {
1340 int faceId;
1341 VectorBlock velocity;
1342 };
1343 SparseTable<VelocityInfo> velocityInfo_;
1344
1345 using ScalarFluidState = typename IntensiveQuantities::ScalarFluidState;
1346
1347 using BoundaryConditionDataCPU = BoundaryConditionData<VectorBlockCPU, ScalarFluidState>;
1348
1349 using BoundaryInfoCPU = BoundaryInfo<BoundaryConditionDataCPU>;
1350
1351 std::vector<BoundaryInfoCPU> boundaryInfo_;
1352
1353 bool separateSparseSourceTerms_ = false;
1354
1355 FullDomain<> fullDomain_;
1356
1357 int exportIndex_;
1358 int exportCount_;
1359};
1360} // namespace Opm
1361
1362#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:418
Vanguard & vanguard()
Return a reference to the grid manager of simulation.
Definition: simulator.hh:239
Problem & problem()
Return the object which specifies the pysical setup of the simulation.
Definition: simulator.hh:270
const GridView & gridView() const
Return the grid view for which the simulation is done.
Definition: simulator.hh:251
Model & model()
Return the physical model used in the simulation.
Definition: simulator.hh:257
The common code for the linearizers of non-linear systems of equations.
Definition: tpfalinearizer.hh:120
const auto & getFloresInfo() const
Return constant reference to the floresInfo.
Definition: tpfalinearizer.hh:402
const LinearizationType & getLinearizationType() const
Definition: tpfalinearizer.hh:386
const auto & getNeighborInfo() const
Definition: tpfalinearizer.hh:414
void updateBoundaryConditionData()
Definition: tpfalinearizer.hh:424
void linearize()
Linearize the full system of non-linear equations.
Definition: tpfalinearizer.hh:234
SparseMatrixAdapter & jacobian()
Definition: tpfalinearizer.hh:353
const auto & getFlowsInfo() const
Return constant reference to the flowsInfo.
Definition: tpfalinearizer.hh:394
std::map< unsigned, Constraints > constraintsMap() const
Returns the map of constraint degrees of freedom.
Definition: tpfalinearizer.hh:448
TpfaLinearizer()
Definition: tpfalinearizer.hh:181
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:290
void finalize()
Definition: tpfalinearizer.hh:311
void init(Simulator &simulator)
Initialize the linearizer.
Definition: tpfalinearizer.hh:207
void updateFlowsInfo()
Definition: tpfalinearizer.hh:786
static void registerParameters()
Register all run-time parameters for the Jacobian linearizer.
Definition: tpfalinearizer.hh:192
void exportSystem(const int idx, std::string &tag, const char *path="export")
Export block sparse linear system.
Definition: tpfalinearizer.hh:368
void linearizeDomain()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:250
const SparseMatrixAdapter & jacobian() const
Return constant reference to global Jacobian matrix backend.
Definition: tpfalinearizer.hh:350
void setLinearizationType(LinearizationType linearizationType)
Definition: tpfalinearizer.hh:383
GlobalEqVector & residual()
Definition: tpfalinearizer.hh:362
void eraseMatrix()
Causes the Jacobian matrix to be recreated from scratch before the next iteration.
Definition: tpfalinearizer.hh:220
const auto & getVelocityInfo() const
Return constant reference to the velocityInfo.
Definition: tpfalinearizer.hh:411
static OPM_HOST_DEVICE void linearize_cell(const unsigned int ii, const DomainType &domain, const NeighborSparseTable &neighborInfo, const DiagPtrType &diagMatAddress, ResidualType &residual, const ModelClass &model, VelocityInfoType &velocityInfo, const Scalar dt, const ProblemType &problem)
Definition: tpfalinearizer.hh:1079
void updateDiscretizationParameters()
Definition: tpfalinearizer.hh:419
void resetSystem_(const SubDomainType &domain)
Definition: tpfalinearizer.hh:452
static OPM_HOST_DEVICE void setResAndJacobi(VectorBlockType &res, MatrixBlockType &bMat, const ADVectorBlockType &resid)
Definition: tpfalinearizer.hh:769
void linearizeAuxiliaryEquations()
Linearize the part of the non-linear system of equations that is associated with the spatial domain.
Definition: tpfalinearizer.hh:318
const GlobalEqVector & residual() const
Return constant reference to global residual vector.
Definition: tpfalinearizer.hh:359
static GpuSparseMatrixWrapper< T, ForceLegacy > fromMatrix(const MatrixType &matrix, bool copyNonZeroElementsDirectly=false)
fromMatrix creates a new matrix with the same block size and values as the given matrix
Definition: GpuSparseMatrixWrapper.hpp:183
A small fixed-size square matrix class for use in CUDA kernels.
Definition: MiniMatrix.hpp:37
Definition: MiniVector.hpp:50
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: hypreinterface/HypreSetup.hpp:165
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
void exportSystem(const IstlMatrix &jacobian, const GlobalEqVector &residual, const bool export_sparsity, const char *tag, const char *path="export")
Export blocks-sparse linear system.
Definition: exportSystem.hpp:42
Storage cells
Definition: tpfalinearizerstructs.hh:39
Definition: linearizationtype.hh:34
Definition: tpfalinearizer.hh:99
static constexpr bool value
Definition: tpfalinearizer.hh:99
GPU parameter setup class for TpfaLinearizer.