opm-simulators
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 #include <opm/models/discretization/common/tpfalinearizerstructs.hh>
51 
52 #include <opm/simulators/linalg/exportSystem.hpp>
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
80 #include <opm/models/discretization/common/tpfalinearizergpukernels.hh>
82 #include <opm/simulators/flow/ThermalGasWaterFlowProblem.hpp>
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
90 #include <opm/simulators/linalg/gpuistl/GpuSparseMatrixWrapper.hpp>
91 #include <opm/simulators/linalg/gpuistl/MiniMatrix.hpp>
92 #include <opm/simulators/linalg/gpuistl/MiniVector.hpp>
93 #include <opm/simulators/linalg/gpuistl/detail/gpusparse_matrix_operations.hpp>
94 #endif
95 #endif
96 
97 namespace Opm::Parameters {
98 
99 struct SeparateSparseSourceTerms { static constexpr bool value = false; };
100 
101 } // namespace Opm::Parameters
102 
103 namespace Opm {
104 
105 // forward declarations
106 template<class TypeTag>
107 class EcfvDiscretization;
108 
118 template<class TypeTag>
120 {
128 
131  using SparseMatrixAdapter = GetPropType<TypeTag, Properties::SparseMatrixAdapter>;
136  using IntensiveQuantities = GetPropType<TypeTag, Properties::IntensiveQuantities>;
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 
180 public:
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 
220  void eraseMatrix()
221  {
222  jacobian_.reset();
223  }
224 
234  void linearize()
235  {
236  linearizeDomain();
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 
383  void setLinearizationType(LinearizationType linearizationType)
384  { linearizationType_ = linearizationType; }
385 
386  const LinearizationType& getLinearizationType() const
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 
419  void updateDiscretizationParameters()
420  {
421  updateStoredTransmissibilities();
422  }
423 
424  void updateBoundaryConditionData()
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 
463 private:
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>(
619  gpuistl::GpuSparseMatrixWrapper<Scalar>::fromMatrix(jacobian_->istlMatrix())));
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 
766 public:
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 
786  void updateFlowsInfo()
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 
866 private:
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 
1069 public:
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  {
1225  using Scalar = GetPropType<TypeTag, Properties::Scalar>;
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;
1239  TpfaLinearizer<TypeTag>::setResAndJacobi(res, bMat, adres);
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 
1255 private:
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
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
Definition: tpfalinearizer.hh:99
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
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
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 base class for the element-centered finite-volume discretization scheme.
Definition: fvbasegradientcalculator.hh:42
Problem & problem()
Return the object which specifies the pysical setup of the simulation.
Definition: simulator.hh:270
Base class for specifying auxiliary equations.
const auto & getFlowsInfo() const
Return constant reference to the flowsInfo.
Definition: tpfalinearizer.hh:394
Defines the common properties required by the porous medium multi-phase models.
const auto & getVelocityInfo() const
Return constant reference to the velocityInfo.
Definition: tpfalinearizer.hh:411
const GlobalEqVector & residual() const
Return constant reference to global residual vector.
Definition: tpfalinearizer.hh:359
void eraseMatrix()
Causes the Jacobian matrix to be recreated from scratch before the next iteration.
Definition: tpfalinearizer.hh:220
Definition: matrixblock.hh:228
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
static void registerParameters()
Register all run-time parameters for the Jacobian linearizer.
Definition: tpfalinearizer.hh:192
Definition: blackoilnewtonmethodparams.hpp:31
Scalar timeStepSize() const
Returns the time step length so that we don&#39;t miss the beginning of the next episode or cross the en...
Definition: simulator.hh:418
std::map< unsigned, Constraints > constraintsMap() const
Returns the map of constraint degrees of freedom.
Definition: tpfalinearizer.hh:448
Declares the properties required by the black oil model.
const auto & getFloresInfo() const
Return constant reference to the floresInfo.
Definition: tpfalinearizer.hh:402
Declare the properties used by the infrastructure code of the finite volume discretizations.
Definition: linearizationtype.hh:33
void linearizeDomain()
Linearize the part of the non-linear system of equations that is associated with the spatial domain...
Definition: tpfalinearizer.hh:250
The common code for the linearizers of non-linear systems of equations.
Definition: tpfalinearizer.hh:119
The common code for the linearizers of non-linear systems of equations.
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
A small fixed-size square matrix class for use in CUDA kernels.
Definition: MiniMatrix.hpp:37
GPU parameter setup class for TpfaLinearizer.
void linearizeAuxiliaryEquations()
Linearize the part of the non-linear system of equations that is associated with the spatial domain...
Definition: tpfalinearizer.hh:318
void exportSystem(const int idx, std::string &tag, const char *path="export")
Export block sparse linear system.
Definition: tpfalinearizer.hh:368
Manages the initializing and running of time dependent problems.
Definition: simulator.hh:83
void init(Simulator &simulator)
Initialize the linearizer.
Definition: tpfalinearizer.hh:207
void linearize()
Linearize the full system of non-linear equations.
Definition: tpfalinearizer.hh:234
const SparseMatrixAdapter & jacobian() const
Return constant reference to global Jacobian matrix backend.
Definition: tpfalinearizer.hh:350
Definition: MiniVector.hpp:49
Vanguard & vanguard()
Return a reference to the grid manager of simulation.
Definition: simulator.hh:239