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