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