getQuasiImpesWeights.hpp
Go to the documentation of this file.
1/*
2 Copyright 2019 SINTEF Digital, Mathematics and Cybernetics.
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 3 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
20#ifndef OPM_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED
21#define OPM_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED
22
23#include <dune/common/fvector.hh>
24
25#include <opm/grid/utility/ElementChunks.hpp>
27#include <opm/material/common/MathToolbox.hpp>
29#include <algorithm>
30#include <cmath>
31
32namespace Opm
33{
34
35namespace Details
36{
37 template <class DenseMatrix>
38 DenseMatrix transposeDenseMatrix(const DenseMatrix& M)
39 {
40 DenseMatrix tmp;
41 for (int i = 0; i < M.rows; ++i)
42 for (int j = 0; j < M.cols; ++j)
43 tmp[j][i] = M[i][j];
44
45 return tmp;
46 }
47} // namespace Details
48
49namespace Amg
50{
51 template <class Matrix, class Vector>
52 void getQuasiImpesWeights(const Matrix& matrix, const int pressureVarIndex, const bool transpose, Vector& weights)
53 {
54 using VectorBlockType = typename Vector::block_type;
55 using MatrixBlockType = typename Matrix::block_type;
56 const Matrix& A = matrix;
57
58 VectorBlockType rhs(0.0);
59 rhs[pressureVarIndex] = 1.0;
60
61 // Declare variables outside the loop to avoid repetitive allocation
62 MatrixBlockType diag_block;
63 VectorBlockType bweights;
64 MatrixBlockType diag_block_transpose;
65
66 // Use OpenMP to parallelize over matrix rows
67 #ifdef _OPENMP
68 #pragma omp parallel for private(diag_block, bweights, diag_block_transpose)
69 #endif
70 for (int row_idx = 0; row_idx < static_cast<int>(A.N()); ++row_idx) {
71 diag_block = MatrixBlockType(0.0);
72 // Find diagonal block for this row
73 const auto row_it = A.begin() + row_idx;
74 const auto endj = (*row_it).end();
75 for (auto j = (*row_it).begin(); j != endj; ++j) {
76 if (row_it.index() == j.index()) {
77 diag_block = (*j);
78 break;
79 }
80 }
81 if (transpose) {
82 diag_block.solve(bweights, rhs);
83 } else {
84 diag_block_transpose = Details::transposeDenseMatrix(diag_block);
85 diag_block_transpose.solve(bweights, rhs);
86 }
87
88 double abs_max = *std::max_element(
89 bweights.begin(), bweights.end(), [](double a, double b) { return std::fabs(a) < std::fabs(b); });
90 bweights /= std::fabs(abs_max);
91 weights[row_idx] = bweights;
92 }
93 }
94
95 template <class Matrix, class Vector>
96 Vector getQuasiImpesWeights(const Matrix& matrix, const int pressureVarIndex, const bool transpose)
97 {
98 Vector weights(matrix.N());
99 getQuasiImpesWeights(matrix, pressureVarIndex, transpose, weights);
100 return weights;
101 }
102
103 template<class Vector, class ElementContext, class Model, class ElementChunksType>
104 void getTrueImpesWeights(int pressureVarIndex, Vector& weights,
105 const ElementContext& elemCtx,
106 const Model& model,
107 const ElementChunksType& element_chunks)
108 {
109 using VectorBlockType = typename Vector::block_type;
110 using Matrix = typename std::decay_t<decltype(model.linearizer().jacobian())>;
111 using MatrixBlockType = typename Matrix::MatrixBlock;
112 constexpr int numEq = VectorBlockType::size();
113 using Evaluation = typename std::decay_t<decltype(model.localLinearizer(ThreadManager::threadId()).localResidual().residual(0))>
114 ::block_type;
115
116 VectorBlockType rhs(0.0);
117 rhs[pressureVarIndex] = 1.0;
118
119 // Declare variables outside the loop to avoid repetitive allocation
120 MatrixBlockType block;
121 VectorBlockType bweights;
122 MatrixBlockType block_transpose;
123 Dune::FieldVector<Evaluation, numEq> storage;
124
126 #ifdef _OPENMP
127 #pragma omp parallel for private(block, bweights, block_transpose, storage)
128 #endif
129 for (const auto& chunk : element_chunks) {
130 const std::size_t thread_id = ThreadManager::threadId();
131 ElementContext localElemCtx(elemCtx.simulator());
132
133 for (const auto& elem : chunk) {
134 localElemCtx.updatePrimaryStencil(elem);
135 localElemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
136
137 model.localLinearizer(thread_id).localResidual().computeStorage(storage, localElemCtx, /*spaceIdx=*/0, /*timeIdx=*/0);
138
139 auto extrusionFactor = localElemCtx.intensiveQuantities(0, /*timeIdx=*/0).extrusionFactor();
140 auto scvVolume = localElemCtx.stencil(/*timeIdx=*/0).subControlVolume(0).volume() * extrusionFactor;
141 auto storage_scale = scvVolume / localElemCtx.simulator().timeStepSize();
142 const double pressure_scale = 50e5;
143
144 // Build the transposed matrix directly to avoid separate transpose step
145 for (int ii = 0; ii < numEq; ++ii) {
146 for (int jj = 0; jj < numEq; ++jj) {
147 block_transpose[jj][ii] = storage[ii].derivative(jj)/storage_scale;
148 if (jj == pressureVarIndex) {
149 block_transpose[jj][ii] *= pressure_scale;
150 }
151 }
152 }
153 block_transpose.solve(bweights, rhs);
154
155 double abs_max = *std::max_element(
156 bweights.begin(), bweights.end(), [](double a, double b) { return std::fabs(a) < std::fabs(b); });
157 // probably a scaling which could give approximately total compressibility would be better
158 bweights /= std::fabs(abs_max); // given normal densities this scales weights to about 1.
159
160 const auto index = localElemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
161 weights[index] = bweights;
162 }
163 }
164 OPM_END_PARALLEL_TRY_CATCH("getTrueImpesWeights() failed: ", elemCtx.simulator().vanguard().grid().comm());
165 }
166
167 template <class Vector, class ElementContext, class Model, class ElementChunksType>
168 void getTrueImpesWeightsAnalytic(int /*pressureVarIndex*/,
169 Vector& weights,
170 const ElementContext& elemCtx,
171 const Model& model,
172 const ElementChunksType& element_chunks)
173 {
174 // The sequential residual is a linear combination of the
175 // mass balance residuals, with coefficients equal to (for
176 // water, oil, gas):
177 // 1/bw,
178 // (1/bo - rs/bg)/(1-rs*rv)
179 // (1/bg - rv/bo)/(1-rs*rv)
180 // These coefficients must be applied for both the residual and
181 // Jacobian.
182 using FluidSystem = typename Model::FluidSystem;
183 using LhsEval = double;
184
185 using PrimaryVariables = typename Model::PrimaryVariables;
186 using VectorBlockType = typename Vector::block_type;
187 using Evaluation =
188 typename std::decay_t<decltype(model.localLinearizer(ThreadManager::threadId()).localResidual().residual(0))>::block_type;
189 using Toolbox = MathToolbox<Evaluation>;
190
191 const auto& solution = model.solution(/*timeIdx*/ 0);
192 VectorBlockType bweights;
193
194 // Use OpenMP to parallelize over element chunks
196 #ifdef _OPENMP
197 #pragma omp parallel for private(bweights)
198 #endif
199 for (const auto& chunk : element_chunks) {
200
201 // Each thread gets a unique copy of elemCtx
202 ElementContext localElemCtx(elemCtx.simulator());
203
204 for (const auto& elem : chunk) {
205 localElemCtx.updatePrimaryStencil(elem);
206 localElemCtx.updatePrimaryIntensiveQuantities(/*timeIdx=*/0);
207
208 const auto index = localElemCtx.globalSpaceIndex(/*spaceIdx=*/0, /*timeIdx=*/0);
209 const auto& intQuants = localElemCtx.intensiveQuantities(/*spaceIdx=*/0, /*timeIdx=*/0);
210 const auto& fs = intQuants.fluidState();
211
212 if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
213 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(
214 FluidSystem::solventComponentIndex(FluidSystem::waterPhaseIdx));
215 bweights[activeCompIdx]
216 = Toolbox::template decay<LhsEval>(1 / fs.invB(FluidSystem::waterPhaseIdx));
217 }
218
219 double denominator = 1.0;
220 double rs = Toolbox::template decay<double>(fs.Rs());
221 double rv = Toolbox::template decay<double>(fs.Rv());
222 const auto& priVars = solution[index];
223 if (priVars.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rv) {
224 rs = 0.0;
225 }
226 if (priVars.primaryVarsMeaningGas() == PrimaryVariables::GasMeaning::Rs) {
227 rv = 0.0;
228 }
229 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)
230 && FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
231 denominator = Toolbox::template decay<LhsEval>(1 - rs * rv);
232 }
233
234 if (FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx)) {
235 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(
236 FluidSystem::solventComponentIndex(FluidSystem::oilPhaseIdx));
237 bweights[activeCompIdx] = Toolbox::template decay<LhsEval>(
238 (1 / fs.invB(FluidSystem::oilPhaseIdx) - rs / fs.invB(FluidSystem::gasPhaseIdx))
239 / denominator);
240 }
241 if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
242 const unsigned activeCompIdx = FluidSystem::canonicalToActiveCompIdx(
243 FluidSystem::solventComponentIndex(FluidSystem::gasPhaseIdx));
244 bweights[activeCompIdx] = Toolbox::template decay<LhsEval>(
245 (1 / fs.invB(FluidSystem::gasPhaseIdx) - rv / fs.invB(FluidSystem::oilPhaseIdx))
246 / denominator);
247 }
248
249 weights[index] = bweights;
250 }
251 }
252 OPM_END_PARALLEL_TRY_CATCH("getTrueImpesAnalyticWeights() failed: ", elemCtx.simulator().vanguard().grid().comm());
253 }
254} // namespace Amg
255
256} // namespace Opm
257
258#endif // OPM_GET_QUASI_IMPES_WEIGHTS_HEADER_INCLUDED
#define OPM_END_PARALLEL_TRY_CATCH(prefix, comm)
Catch exception and throw in a parallel try-catch clause.
Definition: DeferredLoggingErrorHelpers.hpp:192
#define OPM_BEGIN_PARALLEL_TRY_CATCH()
Macro to setup the try of a parallel try-catch.
Definition: DeferredLoggingErrorHelpers.hpp:158
static unsigned threadId()
Return the index of the current OpenMP thread.
void getQuasiImpesWeights(const Matrix &matrix, const int pressureVarIndex, const bool transpose, Vector &weights)
Definition: getQuasiImpesWeights.hpp:52
void getTrueImpesWeightsAnalytic(int, Vector &weights, const ElementContext &elemCtx, const Model &model, const ElementChunksType &element_chunks)
Definition: getQuasiImpesWeights.hpp:168
void getTrueImpesWeights(int pressureVarIndex, Vector &weights, const ElementContext &elemCtx, const Model &model, const ElementChunksType &element_chunks)
Definition: getQuasiImpesWeights.hpp:104
DenseMatrix transposeDenseMatrix(const DenseMatrix &M)
Definition: getQuasiImpesWeights.hpp:38
Definition: blackoilboundaryratevector.hh:39