OGS
FluxCorrectedTransport.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#include <Eigen/Core>
7#include <Eigen/Sparse>
8#include <ranges>
9
10#ifdef USE_PETSC
11#include <petscerror.h>
12#include <petscmat.h>
13#include <petscvec.h>
14
15#include "BaseLib/MPI.h"
16#endif
17
18namespace NumLib
19{
20namespace detail
21{
22
23template <typename MatrixVectorType>
24std::unique_ptr<MatrixVectorType> newZeroedInstance(
25 MathLib::MatrixSpecifications const& matrix_specification)
26{
28 matrix_specification);
29 result->setZero();
30 return result;
31}
32
33#ifndef USE_PETSC
34void calculateFluxCorrectedTransportSerial(
35 [[maybe_unused]] const double t, const double dt,
36 std::vector<GlobalVector*> const& x,
37 std::vector<GlobalVector*> const& x_prev, int const process_id,
38 const MathLib::MatrixSpecifications& matrix_specification, GlobalMatrix& M,
40{
41 auto D = newZeroedInstance<GlobalMatrix>(matrix_specification);
42 auto F = newZeroedInstance<GlobalMatrix>(matrix_specification);
43
44 // compute artificial diffusion operator D
45 using RawMatrixType = Eigen::SparseMatrix<double, Eigen::RowMajor>;
46 auto& K_raw = K.getRawMatrix();
47 K_raw *= -1;
48 for (int k = 0; k < K_raw.outerSize(); ++k)
49 {
50 for (RawMatrixType::InnerIterator it(K_raw, k); it; ++it)
51 {
52 if (it.row() != it.col())
53 {
54 double const kij = it.value();
55 double const kji = K.get(it.col(), it.row());
56 double const dij = std::max({-kij, 0., -kji});
57 D->setValue(it.row(), it.col(), dij);
58 }
59 }
60 }
61 auto& D_raw = D->getRawMatrix();
62 D_raw -= (D_raw * Eigen::VectorXd::Ones(D_raw.cols())).eval().asDiagonal();
63
64 // compute F
65 for (int k = 0; k < D_raw.outerSize(); ++k)
66 {
67 for (RawMatrixType::InnerIterator it(D_raw, k); it; ++it)
68 {
69 double const dij = it.value();
70 double const xj = x[process_id]->get(it.col());
71 double const xi = x[process_id]->get(it.row());
72 double const fij = -dij * (xj - xi);
73 F->setValue(it.row(), it.col(), fij);
74 }
75 }
76
77 auto& M_raw = M.getRawMatrix();
78 for (int k = 0; k < M_raw.outerSize(); ++k)
79 {
80 for (RawMatrixType::InnerIterator it(M_raw, k); it; ++it)
81 {
82 double const mij = it.value();
83 double const xdotj = (x[process_id]->get(it.col()) -
84 x_prev[process_id]->get(it.col())) /
85 dt;
86 double const xdoti = (x[process_id]->get(it.row()) -
87 x_prev[process_id]->get(it.row())) /
88 dt;
89 double const fij = -mij * (xdotj - xdoti);
90 F->add(it.row(), it.col(), fij);
91 }
92 }
93
94 auto P_plus = newZeroedInstance<GlobalVector>(matrix_specification);
95 auto P_minus = newZeroedInstance<GlobalVector>(matrix_specification);
96 auto Q_plus = newZeroedInstance<GlobalVector>(matrix_specification);
97 auto Q_minus = newZeroedInstance<GlobalVector>(matrix_specification);
98 auto R_plus = newZeroedInstance<GlobalVector>(matrix_specification);
99 auto R_minus = newZeroedInstance<GlobalVector>(matrix_specification);
100
101 auto& F_raw = F->getRawMatrix();
102 for (int k = 0; k < F_raw.outerSize(); ++k)
103 {
104 for (RawMatrixType::InnerIterator it(F_raw, k); it; ++it)
105 {
106 if (it.row() != it.col())
107 {
108 double const fij = it.value();
109 P_plus->add(it.row(), std::max(0., fij));
110 P_minus->add(it.row(), std::min(0., fij));
111
112 double const x_prev_i = x_prev[process_id]->get(it.row());
113 double const x_prev_j = x_prev[process_id]->get(it.col());
114
115 double const Q_plus_i = Q_plus->get(it.row());
116 double Q_plus_i_tmp =
117 std::max({Q_plus_i, 0., x_prev_j - x_prev_i});
118 Q_plus->set(it.row(), Q_plus_i_tmp);
119
120 double const Q_minus_i = Q_minus->get(it.row());
121 double Q_minus_i_tmp =
122 std::min({Q_minus_i, 0., x_prev_j - x_prev_i});
123 Q_minus->set(it.row(), Q_minus_i_tmp);
124 }
125 }
126 }
127
128 Eigen::VectorXd const M_L =
129 (M_raw * Eigen::VectorXd::Ones(M_raw.cols())).eval();
130 for (auto k = R_plus->getRangeBegin(); k < R_plus->getRangeEnd(); ++k)
131 {
132 double const P_plus_i = P_plus->get(k);
133
134 if (P_plus_i == 0.0)
135 {
136 continue;
137 }
138
139 double const mi = M_L(k);
140 double const Q_plus_i = Q_plus->get(k);
141 R_plus->set(k, std::min(1.0, mi * Q_plus_i / dt / P_plus_i));
142 }
143
144 for (auto k = R_minus->getRangeBegin(); k < R_minus->getRangeEnd(); ++k)
145 {
146 double const P_minus_i = P_minus->get(k);
147 if (P_minus_i == 0.0)
148 {
149 continue;
150 }
151 double const mi = M_L(k);
152 double const Q_minus_i = Q_minus->get(k);
153 R_minus->set(k, std::min(1.0, mi * Q_minus_i / dt / P_minus_i));
154 }
155
156 for (int k = 0; k < F_raw.outerSize(); ++k)
157 {
158 for (RawMatrixType::InnerIterator it(F_raw, k); it; ++it)
159 {
160 double const fij = it.value();
161 if (fij > 0.)
162 {
163 double const R_plus_i = R_plus->get(it.row());
164 double const R_minus_j = R_minus->get(it.col());
165 double const alpha_ij = std::min(R_plus_i, R_minus_j);
166
167 b.add(it.row(), alpha_ij * fij);
168 }
169 else
170 {
171 double const R_minus_i = R_minus->get(it.row());
172 double const R_plus_j = R_plus->get(it.col());
173 double const alpha_ij = std::min(R_minus_i, R_plus_j);
174
175 b.add(it.row(), alpha_ij * fij);
176 }
177 }
178 }
179
180 // compute low-order operator
181 K_raw += D_raw;
182 K_raw *= -1;
183
184 // overwrite with the lumped mass matrix
185 M.setZero();
186 for (int k = 0; k < M.getNumberOfRows(); ++k)
187 {
188 M.setValue(k, k, M_L(k));
189 }
190}
191#endif // end of ifndef USE_PETSC
192
193#ifdef USE_PETSC
194void finalize(Mat& M)
195{
196 PetscCallAbort(PETSC_COMM_WORLD,
197 MatAssemblyBegin(M, MatAssemblyType::MAT_FINAL_ASSEMBLY));
198 PetscCallAbort(PETSC_COMM_WORLD,
199 MatAssemblyEnd(M, MatAssemblyType::MAT_FINAL_ASSEMBLY));
200}
201
203 [[maybe_unused]] const double t, const double dt,
204 std::vector<GlobalVector*> const& x,
205 std::vector<GlobalVector*> const& x_prev, int const process_id,
206 const MathLib::MatrixSpecifications& matrix_specification, GlobalMatrix& M,
208{
209 BaseLib::MPI::Mpi mpi(PETSC_COMM_WORLD);
210 if (mpi.size > 1)
211 {
212 OGS_FATAL(
213 "The current parallel implementation of flux corrected transport "
214 "supports only one MPI rank, but the simulation uses {} ranks",
215 mpi.size);
216 }
217 Mat K_raw = K.getRawMatrix();
218 Mat D;
219 PetscCallAbort(PETSC_COMM_WORLD,
220 MatDuplicate(K_raw, MAT_DO_NOT_COPY_VALUES, &D));
221 PetscCallAbort(PETSC_COMM_WORLD, MatZeroEntries(D));
222 finalize(D);
223 PetscInt start, end;
224 PetscCallAbort(PETSC_COMM_WORLD,
225 MatGetOwnershipRange(K.getRawMatrix(), &start, &end));
226
227 PetscCallAbort(PETSC_COMM_WORLD, MatScale(K_raw, -1.0));
228 K.finalizeAssembly();
229 // compute artificial diffusion operator D
230 for (PetscInt i = start; i < end; ++i)
231 {
232 PetscInt number_of_columns;
233 PetscInt const* columns;
234 PetscScalar const* values;
235
236 PetscCallAbort(PETSC_COMM_WORLD, MatGetRow(K_raw, i, &number_of_columns,
237 &columns, &values));
238
239 auto const columns_span =
240 std::span<PetscInt const>(columns, number_of_columns);
241 auto const values_span =
242 std::span<PetscScalar const>(values, number_of_columns);
243 for (auto const [j, kij] : std::views::zip(columns_span, values_span))
244 {
245 if (i == j) // skip diagonal entries
246 {
247 continue;
248 }
249 PetscScalar kji;
250 // MatGetValue is not supported for all PETSc matrix types
251 PetscCallAbort(PETSC_COMM_WORLD, MatGetValue(K_raw, j, i, &kji));
252 PetscScalar dij = std::max({-kij, 0.0, -kji});
253 PetscCallAbort(PETSC_COMM_WORLD,
254 MatSetValue(D, i, j, dij, INSERT_VALUES));
255 }
256 // MatRestoreRow() is required after MatGetRow()
257 PetscCallAbort(
258 PETSC_COMM_WORLD,
259 MatRestoreRow(K_raw, i, &number_of_columns, &columns, &values));
260 }
261 finalize(D);
262 auto row_sums = newZeroedInstance<GlobalVector>(matrix_specification);
263 Vec row_sums_raw = row_sums->getRawVector();
264 PetscCallAbort(PETSC_COMM_WORLD, MatGetRowSum(D, row_sums_raw));
265 PetscCallAbort(PETSC_COMM_WORLD, VecScale(row_sums_raw, -1.0));
266 PetscCallAbort(PETSC_COMM_WORLD, VecAssemblyBegin(row_sums_raw));
267 PetscCallAbort(PETSC_COMM_WORLD, VecAssemblyEnd(row_sums_raw));
268 PetscCallAbort(PETSC_COMM_WORLD,
269 MatDiagonalSet(D, row_sums_raw, ADD_VALUES));
270 finalize(D);
271 // need to access x and x_prev in the following loops
272 x[process_id]->setLocalAccessibleVector();
273 x_prev[process_id]->setLocalAccessibleVector();
274
275 // compute F
276 Mat F;
277 PetscCallAbort(PETSC_COMM_WORLD,
278 MatDuplicate(K.getRawMatrix(), MAT_DO_NOT_COPY_VALUES, &F));
279 PetscCallAbort(PETSC_COMM_WORLD, MatZeroEntries(F));
280 for (PetscInt i = start; i < end; ++i)
281 {
282 PetscInt cols;
283 const PetscInt* col_indices;
284 const PetscScalar* values;
285
286 PetscCallAbort(PETSC_COMM_WORLD,
287 MatGetRow(D, i, &cols, &col_indices, &values));
288
289 auto const columns = std::span<PetscInt const>(col_indices, cols);
290 auto const values_D = std::span<PetscScalar const>(values, cols);
291
292 for (auto const [j, dij] : std::views::zip(columns, values_D))
293 {
294 PetscScalar const xi = x[process_id]->get(i);
295 PetscScalar const xj = x[process_id]->get(j);
296 PetscScalar const fij = -dij * (xj - xi);
297 PetscCallAbort(PETSC_COMM_WORLD,
298 MatSetValue(F, i, j, fij, INSERT_VALUES));
299 }
300 PetscCallAbort(PETSC_COMM_WORLD,
301 MatRestoreRow(D, i, &cols, &col_indices, &values));
302 }
303 finalize(F);
304 auto& M_raw = M.getRawMatrix();
305
306 auto xdot = newZeroedInstance<GlobalVector>(matrix_specification);
307 xdot->setLocalAccessibleVector();
308 for (PetscInt i = start; i < end; ++i)
309 {
310 xdot->add(i, (x[process_id]->get(i) - x_prev[process_id]->get(i)) / dt);
311 }
312 xdot->finalizeAssembly();
313 xdot->setLocalAccessibleVector();
314
315 for (PetscInt i = start; i < end; ++i)
316 {
317 PetscInt cols;
318 PetscInt const* col_indices;
319 PetscScalar const* values;
320
321 PetscCallAbort(PETSC_COMM_WORLD,
322 MatGetRow(M_raw, i, &cols, &col_indices, &values));
323
324 auto const columns = std::span<PetscInt const>(col_indices, cols);
325 auto const values_M = std::span<PetscScalar const>(values, cols);
326
327 for (auto const [j, mij] : std::views::zip(columns, values_M))
328 {
329 PetscScalar const fij = -mij * (xdot->get(j) - xdot->get(i));
330 PetscCallAbort(PETSC_COMM_WORLD,
331 MatSetValue(F, i, j, fij, ADD_VALUES));
332 }
333 PetscCallAbort(PETSC_COMM_WORLD,
334 MatRestoreRow(M_raw, i, &cols, &col_indices, &values));
335 }
336 finalize(F);
337 auto P_plus = newZeroedInstance<GlobalVector>(matrix_specification);
338 auto P_minus = newZeroedInstance<GlobalVector>(matrix_specification);
339
340 P_plus->setLocalAccessibleVector();
341 P_minus->setLocalAccessibleVector();
342 // Fill P_plus, P_minus, Q_plus, Q_minus
343 for (PetscInt i = start; i < end; ++i)
344 {
345 PetscInt cols;
346 PetscInt const* col_indices;
347 PetscScalar const* values;
348
349 PetscCallAbort(PETSC_COMM_WORLD,
350 MatGetRow(F, i, &cols, &col_indices, &values));
351
352 auto const column_indices =
353 std::span<PetscInt const>(col_indices, cols);
354 auto const values_span = std::span<PetscScalar const>(values, cols);
355 for (auto const [j, fij] : std::views::zip(column_indices, values_span))
356 {
357 // skip diagonal
358 if (i == j)
359 {
360 continue;
361 }
362 P_plus->add(i, std::max(0., fij));
363 P_minus->add(i, std::min(0., fij));
364 }
365 PetscCallAbort(PETSC_COMM_WORLD,
366 MatRestoreRow(F, i, &cols, &col_indices, &values));
367 }
368 P_plus->finalizeAssembly();
369 P_plus->setLocalAccessibleVector();
370 P_minus->finalizeAssembly();
371 P_minus->setLocalAccessibleVector();
372 auto Q_plus = newZeroedInstance<GlobalVector>(matrix_specification);
373 auto Q_minus = newZeroedInstance<GlobalVector>(matrix_specification);
374 Q_plus->setLocalAccessibleVector();
375 Q_minus->setLocalAccessibleVector();
376
377 for (PetscInt i = start; i < end; ++i)
378 {
379 PetscInt cols;
380 PetscInt const* col_indices;
381
382 PetscScalar Q_plus_i = 0.0;
383 PetscScalar Q_minus_i = 0.0;
384
385 PetscCallAbort(PETSC_COMM_WORLD,
386 MatGetRow(F, i, &cols, &col_indices, nullptr));
387 auto const column_indices =
388 std::span<PetscInt const>(col_indices, cols);
389 for (auto const j : column_indices)
390 {
391 // skip diagonal
392 if (i == j)
393 {
394 continue;
395 }
396 PetscScalar const x_prev_i = x_prev[process_id]->get(i);
397 PetscScalar const x_prev_j = x_prev[process_id]->get(j);
398
399 Q_plus_i = std::max({Q_plus_i, 0., x_prev_j - x_prev_i});
400 Q_minus_i = std::min({Q_minus_i, 0., x_prev_j - x_prev_i});
401 }
402 Q_plus->set(i, Q_plus_i);
403 Q_minus->set(i, Q_minus_i);
404 PetscCallAbort(PETSC_COMM_WORLD,
405 MatRestoreRow(F, i, &cols, &col_indices, nullptr));
406 }
407 Q_plus->finalizeAssembly();
408 Q_plus->setLocalAccessibleVector();
409 Q_minus->finalizeAssembly();
410 Q_minus->setLocalAccessibleVector();
411
412 auto R_plus = newZeroedInstance<GlobalVector>(matrix_specification);
413 R_plus->setLocalAccessibleVector();
414
415 auto row_sums_M = newZeroedInstance<GlobalVector>(matrix_specification);
416 Vec M_L = row_sums_M->getRawVector();
417 PetscCallAbort(PETSC_COMM_WORLD, MatGetRowSum(M_raw, M_L));
418 row_sums_M->finalizeAssembly();
419 row_sums_M->setLocalAccessibleVector();
420
421 for (auto k = R_plus->getRangeBegin(); k < R_plus->getRangeEnd(); ++k)
422 {
423 PetscScalar const P_plus_i = P_plus->get(k);
424 if (P_plus_i == 0.0)
425 {
426 continue;
427 }
428 PetscScalar const mi = row_sums_M->get(k);
429 PetscScalar const Q_plus_i = Q_plus->get(k);
430 R_plus->set(k, std::min(1.0, mi * Q_plus_i / dt / P_plus_i));
431 }
432 R_plus->finalizeAssembly();
433 R_plus->setLocalAccessibleVector();
434 auto R_minus = newZeroedInstance<GlobalVector>(matrix_specification);
435 R_minus->setLocalAccessibleVector();
436
437 for (auto k = R_minus->getRangeBegin(); k < R_minus->getRangeEnd(); ++k)
438 {
439 PetscScalar const P_minus_i = P_minus->get(k);
440 if (P_minus_i == 0.0)
441 {
442 continue;
443 }
444 PetscScalar const mi = row_sums_M->get(k);
445 PetscScalar const Q_minus_i = Q_minus->get(k);
446 R_minus->set(k, std::min(1.0, mi * Q_minus_i / dt / P_minus_i));
447 }
448 R_minus->finalizeAssembly();
449 R_minus->setLocalAccessibleVector();
450 // walk over F, R_plus, and R_minus and compute alpha values; set entries in
451 // the rhs b that limit the antidiffusive flux
452 for (PetscInt i = start; i < end; ++i)
453 {
454 PetscInt cols;
455 PetscInt const* col_indices;
456 PetscScalar const* values;
457
458 PetscCallAbort(PETSC_COMM_WORLD,
459 MatGetRow(F, i, &cols, &col_indices, &values));
460
461 auto col_indices_span = std::span<PetscInt const>(col_indices, cols);
462 auto values_span = std::span<PetscScalar const>(values, cols);
463
464 double alpha_ij;
465
466 for (auto [j, fij] : std::views::zip(col_indices_span, values_span))
467 {
468 if (i == j)
469 {
470 continue;
471 }
472 if (fij > 0.)
473 {
474 alpha_ij = std::min(R_plus->get(i), R_minus->get(j));
475 }
476 else
477 {
478 alpha_ij = std::min(R_minus->get(i), R_plus->get(j));
479 }
480 b.add(i, alpha_ij * fij);
481 }
482 PetscCallAbort(PETSC_COMM_WORLD,
483 MatRestoreRow(F, i, &cols, &col_indices, &values));
484 }
485
486 // compute low-order operator
487 // update K_raw with D: K_raw += D;
488 for (PetscInt i = start; i < end; ++i)
489 {
490 PetscInt cols_D;
491 PetscInt const* col_indices_D;
492 PetscScalar const* values_D;
493 PetscCallAbort(PETSC_COMM_WORLD,
494 MatGetRow(D, i, &cols_D, &col_indices_D, &values_D));
495
496 auto const column_indices =
497 std::span<PetscInt const>(col_indices_D, cols_D);
498 auto const values = std::span<PetscScalar const>(values_D, cols_D);
499 for (auto [j, Dij] : std::views::zip(column_indices, values))
500 {
501 PetscCallAbort(PETSC_COMM_WORLD,
502 MatSetValues(K_raw, 1, &i, 1, &j, &Dij, ADD_VALUES));
503 }
504 PetscCallAbort(PETSC_COMM_WORLD,
505 MatRestoreRow(D, i, &cols_D, &col_indices_D, &values_D));
506 }
507 finalize(K_raw);
508 PetscCallAbort(PETSC_COMM_WORLD, MatScale(K_raw, -1.0));
509 K.finalizeAssembly();
510 M.setZero();
511 for (PetscInt i = start; i < end; ++i)
512 {
513 PetscScalar const row_sum_M_i = row_sums_M->get(i);
514 MatSetValues(M.getRawMatrix(), 1, &i, 1, &i, &row_sum_M_i,
515 INSERT_VALUES);
516 }
517 M.finalizeAssembly();
518 MatDestroy(&D);
519 MatDestroy(&F);
520}
521#endif // end of ifdef USE_PETSC
522
524 const double t, const double dt, std::vector<GlobalVector*> const& x,
525 std::vector<GlobalVector*> const& x_prev, int const process_id,
526 const MathLib::MatrixSpecifications& matrix_specification, GlobalMatrix& M,
528{
529#ifndef USE_PETSC
530 calculateFluxCorrectedTransportSerial(t, dt, x, x_prev, process_id,
531 matrix_specification, M, K, b);
532#endif // end of ifndef USE_PETSC
533
534#ifdef USE_PETSC
535 calculateFluxCorrectedTransportPETSc(t, dt, x, x_prev, process_id,
536 matrix_specification, M, K, b);
537#endif // end of ifdef USE_PETSC
538}
539
540} // namespace detail
541
543 NumericalStabilization const& stabilizer, const double t, const double dt,
544 std::vector<GlobalVector*> const& x,
545 std::vector<GlobalVector*> const& x_prev, int const process_id,
546 const MathLib::MatrixSpecifications& matrix_specification, GlobalMatrix& M,
548{
549 // if needed, calling the Flux-Corrected-Transport function
550 return std::visit(
551 [&](auto&& stabilizer)
552 {
553 using Stabilizer = std::decay_t<decltype(stabilizer)>;
554 if constexpr (std::is_same_v<Stabilizer,
556 {
558 t, dt, x, x_prev, process_id, matrix_specification, M, K,
559 b);
560 }
561 },
562 stabilizer);
563}
564} // namespace NumLib
#define OGS_FATAL(...)
Definition Error.h:19
MathLib::EigenMatrix GlobalMatrix
MathLib::EigenVector GlobalVector
void setZero()
reset data entries to zero.
Definition EigenMatrix.h:58
IndexType getNumberOfRows() const
return the number of rows
Definition EigenMatrix.h:46
int setValue(IndexType row, IndexType col, double val)
Definition EigenMatrix.h:70
double get(IndexType row, IndexType col) const
get value. This function returns zero if the element doesn't exist.
RawMatrixType & getRawMatrix()
void add(IndexType rowId, double v)
add entry
Definition EigenVector.h:70
void calculateFluxCorrectedTransport(const double t, const double dt, std::vector< GlobalVector * > const &x, std::vector< GlobalVector * > const &x_prev, int const process_id, const MathLib::MatrixSpecifications &matrix_specification, GlobalMatrix &M, GlobalMatrix &K, GlobalVector &b)
std::unique_ptr< MatrixVectorType > newZeroedInstance(MathLib::MatrixSpecifications const &matrix_specification)
void calculateFluxCorrectedTransportPETSc(const double t, const double dt, std::vector< GlobalVector * > const &x, std::vector< GlobalVector * > const &x_prev, int const process_id, const MathLib::MatrixSpecifications &matrix_specification, GlobalMatrix &M, GlobalMatrix &K, GlobalVector &b)
void computeFluxCorrectedTransport(NumericalStabilization const &stabilizer, const double t, const double dt, std::vector< GlobalVector * > const &x, std::vector< GlobalVector * > const &x_prev, int const process_id, const MathLib::MatrixSpecifications &matrix_specification, GlobalMatrix &M, GlobalMatrix &K, GlobalVector &b)
std::variant< NoStabilization, IsotropicDiffusionStabilization, FullUpwind, FluxCorrectedTransport > NumericalStabilization
auto eval(Function &f, Tuples &... ts) -> typename detail::GetFunctionReturnType< decltype(&Function::eval)>::type
Definition Apply.h:268