Documentation a537f1bdf2
Loading...
Searching...
No Matches
CSysMatrix.hpp
Go to the documentation of this file.
1/*!
2 * \file CSysMatrix.hpp
3 * \brief Declaration of the block-sparse matrix class.
4 * The implemtation is in <i>CSysMatrix.cpp</i>.
5 * \author F. Palacios, A. Bueno, T. Economon, P. Gomes
6 * \version 8.5.0 "Harrier"
7 *
8 * SU2 Project Website: https://su2code.github.io
9 *
10 * The SU2 Project is maintained by the SU2 Foundation
11 * (http://su2foundation.org)
12 *
13 * Copyright 2012-2026, SU2 Contributors (cf. AUTHORS.md)
14 *
15 * SU2 is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU Lesser General Public
17 * License as published by the Free Software Foundation; either
18 * version 2.1 of the License, or (at your option) any later version.
19 *
20 * SU2 is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 * Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public
26 * License along with SU2. If not, see <http://www.gnu.org/licenses/>.
27 */
28
29#pragma once
30
31#include "../CConfig.hpp"
32#include "../code_config.hpp"
33#include "CSysVector.hpp"
34#include "CPastixWrapper.hpp"
35#include "../toolboxes/graph_toolbox.hpp"
36
37#include <cstdint>
38#include <cstdlib>
39#include <vector>
40#include <cassert>
41#include <optional>
42
43/*--- In forward mode the matrix is not of a built-in type. ---*/
44#if defined(HAVE_MKL) && !defined(CODI_FORWARD_TYPE)
45#include "mkl.h"
46#ifndef __INTEL_MKL__
47#error Could not determine the MKL version
48#endif
49/*--- JIT is only available since 2019. ---*/
50#if __INTEL_MKL__ >= 2019
51#define USE_MKL
52/*---
53 Lapack direct calls only seem to be created for Intel compilers, and it is not worthwhile
54 making "getrf" and "getrs" compatible with AD since they are not used as often as "gemm".
55---*/
56#if defined(__INTEL_COMPILER) && defined(MKL_DIRECT_CALL_SEQ) && !defined(CODI_REVERSE_TYPE)
57#define USE_MKL_LAPACK
58#endif
59template <class T>
60struct mkl_jit_wrapper {
61 using gemm_t = dgemm_jit_kernel_t;
62 template <class... Ts>
63 static void create_gemm(Ts&&... args) {
64 mkl_jit_create_dgemm(args...);
65 }
66 static gemm_t get_gemm(void* jitter) { return mkl_jit_get_dgemm_ptr(jitter); }
67};
68template <>
69struct mkl_jit_wrapper<float> {
70 using gemm_t = sgemm_jit_kernel_t;
71 template <class... Ts>
72 static void create_gemm(Ts&&... args) {
73 mkl_jit_create_sgemm(args...);
74 }
75 static gemm_t get_gemm(void* jitter) { return mkl_jit_get_sgemm_ptr(jitter); }
76};
77#else
78#warning The current version of MKL does not support JIT gemm kernels
79#endif
80#endif
81
82class CGeometry;
83
84/*!
85 * \brief Helper to communicate distributed vectors.
86 * \ingroup SpLinSys
87 */
89 /*!
90 * \brief Routine to load a vector quantity into the data structures for MPI point-to-point
91 * communication and to launch non-blocking sends and recvs.
92 * \param[in] x - CSysVector holding the array of data.
93 * \param[in] geometry - Geometrical definition of the problem.
94 * \param[in] config - Definition of the particular problem.
95 * \param[in] commType - Enumerated type for the quantity to be communicated.
96 */
97 template <class T>
98 static void Initiate(const CSysVector<T>& x, CGeometry* geometry, const CConfig* config,
100
101 /*!
102 * \brief Routine to complete the set of non-blocking communications launched by
103 * Initiate() and unpacking of the data in the vector.
104 * \param[in] x - CSysVector holding the array of data.
105 * \param[in] geometry - Geometrical definition of the problem.
106 * \param[in] config - Definition of the particular problem.
107 * \param[in] commType - Enumerated type for the quantity to be unpacked.
108 */
109 template <class T>
110 static void Complete(CSysVector<T>& x, CGeometry* geometry, const CConfig* config,
112};
113
114/*!
115 * \brief Reconstruct the float row-scale from a stored uint8 binary exponent.
116 * \p e is stored already biased, i.e. it is the IEEE 754 biased-exponent field itself, so
117 * shifting it into place with a zero mantissa gives the exact power of two 2^(e-127).
118 * This is the inverse of the encoding in EncodeQuantBlock.
119 * \note Branches on __CUDA_ARCH__, plain memcpy compiles for the device but does not work!
120 */
122 const uint32_t bits = static_cast<uint32_t>(e) << 23;
123#ifdef __CUDA_ARCH__
124 return __uint_as_float(bits);
125#else
126 float scale;
127 memcpy(&scale, &bits, sizeof(bits));
128 return scale;
129#endif
130}
131
132/*!
133 * \brief Encode one row of an nVar×nVar block into int8 quantized storage: \p qs receives the
134 * row's scale exponent, \p qv (nVar entries) the clamped int8 values for row \p r.
135 * \p f(r,c) is called twice per entry (max-abs scan then encoding); it should be cheap.
136 * \note Shared with the device and thus same __CUDA_ARCH__ branches as DecodeQuantScale. \p f's
137 * return type is cast to float directly on device (only ever instantiated there for plain
138 * ScalarType, never AD-active); on host it goes through SU2_TYPE::PassiveValue first, since
139 * ScalarType can be AD-active there (quantized_mode is only compiled out for reverse-mode
140 * AD, not forward-mode, see quantized_offdiag_needed in CSysMatrix.cpp) and PassiveValue is
141 * host-only (not SU2_CUDA_HOST_DEVICE).
142 */
143template <class F>
144SU2_CUDA_HOST_DEVICE FORCEINLINE void EncodeQuantRow(const F& f, uint8_t& qs, int8_t* __restrict qv, unsigned long nVar,
145 unsigned long r) noexcept {
146#ifdef __CUDA_ARCH__
147#define EQR_PASSIVE(ROW, COL) f(ROW, COL)
148#else
149#define EQR_PASSIVE(ROW, COL) SU2_TYPE::PassiveValue(f(ROW, COL))
150 using std::max;
151 using std::min;
152#endif
153 constexpr uint32_t eps_bits = 0x34000000u; // ~1.2e-7
154 uint32_t max_abs_bits = eps_bits;
155 for (auto c = 0ul; c < nVar; ++c) {
156 const auto fv = static_cast<float>(EQR_PASSIVE(r, c));
157#ifdef __CUDA_ARCH__
158 const uint32_t fb = __float_as_uint(fv);
159#else
160 uint32_t fb;
161 memcpy(&fb, &fv, sizeof(fb));
162#endif
163 /*--- Masking the mantissa as well as the sign leaves the exponent alone in place, which is
164 * all the scale needs (the max of the exponents is the exponent of the max). ---*/
165 max_abs_bits = max(max_abs_bits, fb & 0x7F800000u);
166 }
167 /*--- Add 1 (round up the exponent) and subtract 7 (to divide by 128. which is the int8 range
168 * for "qv") = -6. The 127 float offset is NOT removed, so the stored value is the biased
169 * exponent of the scale and DecodeQuantScale can use it as exponent bits directly. The
170 * eps_bits floor puts the result in [98, 249], so it always fits in uint8 without clamping. ---*/
171 qs = static_cast<uint8_t>((max_abs_bits >> 23) - 6);
172 /*--- 1/scale = 2^-(qs-127), whose biased exponent is 254 - qs. Because max_abs_bits holds the
173 * exponent already shifted into place with a zero mantissa, that whole expression collapses to
174 * one subtraction: (254 - ((max_abs_bits >> 23) - 6)) << 23 == (260 << 23) - max_abs_bits. ---*/
175 const uint32_t inv_bits = 0x82000000u /* 260 << 23 */ - max_abs_bits;
176#ifdef __CUDA_ARCH__
177 const float inv_rscale = __uint_as_float(inv_bits);
178#else
179 float inv_rscale;
180 memcpy(&inv_rscale, &inv_bits, sizeof(inv_rscale));
181#endif
182 for (auto c = 0ul; c < nVar; ++c) {
183 /*--- Truncate and add 0.5 away from 0, equivalent to roundf, but inline. ---*/
184 const float t = max(-128.f, min(127.f, static_cast<float>(EQR_PASSIVE(r, c) * inv_rscale)));
185 qv[c] = static_cast<int8_t>(t + copysignf(0.5f, t));
186 }
187#undef EQR_PASSIVE
188}
189
190/*!
191 * \brief Encode one nVar×nVar block into per-row int8 quantized storage, see EncodeQuantRow (each
192 * row's scale/quantization is independent, this just loops over all of them serially for
193 * the host path).
194 */
195template <class F>
196SU2_CUDA_HOST_DEVICE FORCEINLINE void EncodeQuantBlock(const F& f, uint8_t* __restrict qs, int8_t* __restrict qv,
197 unsigned long nVar) noexcept {
198 for (auto r = 0ul; r < nVar; ++r) EncodeQuantRow(f, qs[r], qv + r * nVar, nVar, r);
199}
200
201/*!
202 * \brief View of one matrix block, const-correct via the ScalarType template parameter.
203 * \c CBlockView<const ScalarType> is read-only; \c CBlockView<ScalarType> is mutable
204 * and exposes \c apply<Overwrite>(f) for writing with on-the-fly quantized encoding.
205 * Evaluates to \c false if the block is absent from the sparsity pattern.
206 */
207template <class ScalarType>
209 using QuantType = std::conditional_t<std::is_const_v<ScalarType>, const int8_t, int8_t>;
210 using QuantScaleType = std::conditional_t<std::is_const_v<ScalarType>, const uint8_t, uint8_t>;
211
212 ScalarType* ptr = nullptr; ///< Full-precision block; non-null iff not quantized.
213 QuantScaleType* qs = nullptr; ///< Per-row biased binary exponent; non-null iff quantized.
214 QuantType* qv = nullptr; ///< Quantized values (row-major); non-null iff quantized.
215 unsigned long nVar = 0;
216
217 /*! \brief False when the block is not present in the sparsity pattern. */
218 explicit operator bool() const { return ptr || qs; }
219
220 /*! \brief Return entry (row \p i, col \p j), decoding quantization if necessary. */
221 std::remove_const_t<ScalarType> operator()(unsigned long i, unsigned long j) const {
222 using T = std::remove_const_t<ScalarType>;
223 if (ptr) return ptr[i * nVar + j];
224 return static_cast<T>(qv[i * nVar + j] * DecodeQuantScale(qs[i]));
225 }
226
227 /*!
228 * \brief Write the block from callable \p f(i,j).
229 * \p Overwrite=true overwrites (or quantizes for Q_LU_SGS off-diagonal blocks);
230 * \p Overwrite=false accumulates into non-quantized storage only — accumulating into
231 * quantized storage would require decode-accumulate-encode and is a silent no-op.
232 * Only enabled for mutable (non-const ScalarType) views.
233 */
234 template <bool Overwrite, class F, class S = ScalarType, su2enable_if<!std::is_const_v<S>> = 0>
235 void apply(const F& f) const {
236 if (ptr) {
237 for (auto i = 0ul; i < nVar; ++i)
238 for (auto j = 0ul; j < nVar; ++j) {
239 if constexpr (Overwrite)
240 ptr[i * nVar + j] = f(i, j);
241 else
242 ptr[i * nVar + j] += f(i, j);
243 }
244 } else if constexpr (Overwrite) {
245 if (qs) EncodeQuantBlock(f, qs, qv, nVar);
246 }
247 }
248};
249
250/*!
251 * \class CSysMatrix
252 * \ingroup SpLinSys
253 * \brief Main class for defining block-compressed-row-storage sparse matrices.
254 */
255template <class ScalarType>
257 private:
258 friend struct CSysMatrixComms;
259
260 const int rank; /*!< \brief MPI Rank. */
261 const int size; /*!< \brief MPI Size. */
262
263 /*!< \brief Maximum number of variables the matrix can handle. The static
264 * size is needed for fast, per-thread, static memory allocation. */
265 enum : size_t { MAXNVAR = 20 };
266
267 enum { OMP_MAX_SIZE_L = 8192 }; /*!< \brief Max. chunk size used in light parallel for loops. */
268 enum { OMP_MAX_SIZE_H = 512 }; /*!< \brief Max. chunk size used in heavy parallel for loops. */
269 enum { OMP_MIN_SIZE = 32 }; /*!< \brief Chunk size for finer grain operations. */
270 unsigned long omp_light_size; /*!< \brief Actual chunk size used in light loops (e.g. over non zeros). */
271 unsigned long omp_heavy_size; /*!< \brief Actual chunk size used in heavy loops (e.g. over rows). */
272 unsigned long omp_num_parts; /*!< \brief Number of threads used in thread-parallel LU_SGS and ILU. */
273 unsigned long* omp_partitions; /*!< \brief Point indexes of LU_SGS and ILU thread-parallel sub partitioning. */
274
275 unsigned long nPoint; /*!< \brief Number of points in the grid. */
276 unsigned long nPointDomain; /*!< \brief Number of points in the grid (excluding halos). */
277 unsigned long nVar; /*!< \brief Number of variables (and rows of the blocks). */
278 unsigned long nEqn; /*!< \brief Number of equations (and columns of the blocks). */
279
280 /*!
281 * \brief Aggregates value arrays and sparse-structure pointers for an LDU-partitioned matrix.
282 * Each CSysMatrix holds three LDU<ScalarType> instances: the host matrix (mat), its
283 * device copy (gpu), and the ILU factorization (ilu). Ownership of the value arrays
284 * (d/l/u) and whether the pointers address host or device memory is managed by
285 * CSysMatrix. Also reused with T = QuantType to group the quantized scale/blocks
286 * storage (q_scale, q_blocks, d_q_scale, d_q_blocks) the same way; for those the pattern
287 * fields (row_ptr_l, col_ind_l, row_ptr_u, col_ind_u, nnz_l, nnz_u) are simply left
288 * unused, since the sparsity pattern is already available from mat/gpu.
289 */
290 template <class T>
291 struct LDU {
292 T* d = nullptr; /*!< \brief Diagonal block values. */
293 T* l = nullptr; /*!< \brief Strictly-lower block values. */
294 T* u = nullptr; /*!< \brief Strictly-upper block values. */
295 const su2uint* row_ptr_l = nullptr; /*!< \brief Row pointers for L (geometry-owned or GPU copy). */
296 const su2uint* col_ind_l = nullptr; /*!< \brief Column indices for L. */
297 const su2uint* row_ptr_u = nullptr; /*!< \brief Row pointers for U. */
298 const su2uint* col_ind_u = nullptr; /*!< \brief Column indices for U. */
299 unsigned long nnz_l = 0; /*!< \brief Number of L nonzeros. */
300 unsigned long nnz_u = 0; /*!< \brief Number of U nonzeros. */
301 };
302
303 LDU<ScalarType> mat; /*!< \brief Host matrix (values owned via aligned_alloc; pattern from geometry). */
304 LDU<ScalarType> gpu; /*!< \brief Device matrix (all pointers to GPU memory). */
305 LDU<ScalarType> ilu; /*!< \brief ILU factorization, host (values owned; pattern from geometry). */
306 LDU<ScalarType> gpu_ilu; /*!< \brief ILU factorization, device (values and pattern in GPU memory). */
307 ScalarType* d_invM = nullptr; /*!< \brief Device inverse diagonal blocks for the Jacobi or LU-SGS preconditioner. */
308
309 /*--- Quantized off-diagonal storage (used when quantized_mode == true). ---*/
310 using QuantType = int8_t;
311 /*!< \brief Row scales are stored as the biased float exponent, hence unsigned, see
312 * DecodeQuantScale. */
313 using QuantScaleType = uint8_t;
314
315 /*! \brief Set by Initialize() when preconditioner == Q_LU_SGS, Q_JACOBI or Q_IDENTITY.
316 * mat.l and mat.u are NOT allocated; off-diagonal blocks live in q_scale/q_blocks
317 * below. Only the matrix-vector product (used by the Krylov solver and, for Q_LU_SGS,
318 * by the sweeps) reads the quantized blocks; the Jacobi preconditioner never touches
319 * them since it only applies the (full precision) inverse diagonal, and the identity
320 * preconditioner does not touch the matrix at all. */
321#ifndef CODI_REVERSE_TYPE
322 bool quantized_mode = false;
323#else
324 static constexpr bool quantized_mode = false;
325#endif
326 /*!< \brief Per-row biased exponents; .l/.u sized [nnz_l/u * nVar], .d [nPoint * nVar]. .l/.u are
327 * populated during assembly (quantized on the fly); .d is populated by
328 * QuantizeDiagonalBlocks(). .l/.u are pinned (cudaMallocHost) rather than
329 * aligned_alloc when useCuda, so HtDTransfer()'s async uploads them. */
330 LDU<QuantScaleType> q_scale;
331 /*!< \brief Quantized block entries; .l/.u sized [nnz_l/u * nVar * nEqn], .d [nPoint * nVar * nEqn]. */
332 LDU<QuantType> q_blocks;
333
334 /*!< \brief Device mirrors of the quantized storage, only allocated when quantized_mode. */
335 LDU<QuantScaleType> d_q_scale;
336 LDU<QuantType> d_q_blocks;
337
338 bool useCuda = false; /*!< \brief Whether CUDA is enabled. */
339
340 /*!< \brief Whether the inverse diagonal blocks are only needed on the device. False for the
341 * Linelet preconditioner, which builds the Jacobi one but reads invM on the host. */
342 bool jacobi_on_device = false;
343
344 const su2uint* l_to_u_transp; /*!< \brief L-entry index -> U-entry index of its transpose. */
345 const su2uint* u_to_l_transp; /*!< \brief U-entry index -> L-entry index of its transpose. */
346
347 /*!
348 * \brief Lookup table from edges to the L-index in the LDU split.
349 * U-index == edge index by construction (edges are ordered 1:1 with the U pattern).
350 * Therefore, edge_ptr_l == u_to_l_transp, but we keep a separate member for clarity.
351 */
352 const su2uint* edge_ptr_l;
353
354 unsigned short ilu_fill_in; /*!< \brief Fill level for the ILU preconditioner. */
355
356 /*!< \brief Level structure of the ILU dependency graph: rows within a level are independent,
357 * rows in level k only depend on rows in levels < k. The same table drives the forward
358 * (increasing level) and backward (decreasing level) substitution, because the U pattern is
359 * the transpose of the L pattern. Used directly by the host/OMP substitution, and flattened
360 * into ilu_level_ptr / d_precond_level_idx below for the GPU triangular solves. */
362
363 /*!< \brief Coloring of the (domain-only) ILU dependency graph, used only by the GPU iterative
364 * factorization (see IluFactorColorKernel and ilu_color_ptr / d_ilu_color_idx below). The
365 * host/OMP path and the GPU triangular solves use levels_ilu instead. */
367
368 /*!< \brief Number of colored Gauss-Seidel sweeps used to build the ILU factorization on the
369 * device, see IluFactorColorKernel. Fixed (not adaptive) so the result is reproducible; set
370 * from config in Initialize(). The triangular solves have no equivalent sweep count: they are
371 * exact, one pass per level (see IluForwardKernel / IluBackwardKernel). */
372 unsigned short ilu_gpu_sweeps = 1;
373
374 vector<su2uint> ilu_color_ptr; /*!< \brief Start of each color in d_ilu_color_idx, size nColors+1. */
375 su2uint* d_ilu_color_idx = nullptr; /*!< \brief Row indices, grouped by color. */
376
377 vector<su2uint> precond_level_ptr; /*!< \brief Start of each level in d_precond_level_idx, size nLevels+1. */
378 su2uint* d_precond_level_idx = nullptr; /*!< \brief Row indices, grouped by level. */
379
380 /*--- The per-color (factorization) and per-level (triangular solves) kernel launch sequences
381 * are identical on every call: same grid/block sizes, same device pointers (all fixed members,
382 * allocated once). Each is captured once into a CUDA graph and replayed to remove
383 * host-side launch overhead without changing the parallelization. ---*/
384 mutable struct CUgraphExec_st* ilu_build_graph_exec = nullptr;
385
386 /*!< \brief Whether a build may refine the factors already on the device instead of computing
387 * them exactly. TransposeInPlace() clears it for good: from then on this matrix is used in
388 * both orientations, and the factors of one are a bad starting point for the other, which the
389 * ilu_gpu_sweeps colored sweeps cannot recover from. It is not restored after a build because
390 * the orientation flips again on the next one (and the solver refills the matrix in between
391 * without going through TransposeInPlace). Only the discrete adjoint transposes, so the primal
392 * keeps refining as before. */
393 mutable bool ilu_can_refine = true;
394 mutable struct CUgraphExec_st* precond_fwd_graph_exec = nullptr; // ILU or LU-SGS forward only
395 mutable struct CUgraphExec_st* precond_bwd_graph_exec = nullptr; // LU-SGS backward only
396 mutable const ScalarType* precond_fwd_graph_vec = nullptr; /*!< \brief Pointers the apply graph
397 * was captured with, to detect when
398 * it must be recaptured (the
399 * executable graph itself is then
400 * updated in place, not rebuilt,
401 * see InstantiateOrUpdateGraph). */
402 mutable ScalarType* precond_fwd_graph_prod = nullptr;
403 mutable ScalarType* precond_bwd_graph_prod = nullptr;
404
405 /*--- Non-default stream, needed for two uses: (1) the preconditioner build/apply CUDA graphs
406 * below, since the legacy default stream cannot be captured into a graph; (2) HtDTransfer's
407 * async H2D transfer of the quantized L/U blocks, so that transfer can run concurrently (copy
408 * engine) with kernels issued on the default stream (e.g. QuantizeDiagonalBlocksGPU, on the SM)
409 * instead of queueing behind them on the same stream. The two are mutually exclusive for ILU
410 * (never quantized) but not for Q_LU_SGS, which uses both; sharing one stream still needs no
411 * extra synchronization, and in fact gives the right answer for free: the apply graph is
412 * launched into aux_stream, hence ordered after the transfer of the quantized blocks its
413 * kernels read. htd_event marks the end of the H2D transfer specifically, so a *default*-stream
414 * kernel that reads the result (the quantized SpMV) can wait on it without a host-side
415 * block. ---*/
416 mutable struct CUstream_st* aux_stream = nullptr;
417 mutable struct CUevent_st* htd_event = nullptr;
418
419 ScalarType* invM; /*!< \brief Inverse of (Jacobi) preconditioner. */
420
421 /*--- Temporary (hence mutable) working memory used in the Linelet preconditioner, outer vector is for threads ---*/
422 mutable vector<vector<const ScalarType*>>
423 LineletUpper; /*!< \brief Pointers to the upper blocks of the tri-diag system (working memory). */
424 mutable vector<vector<ScalarType>>
425 LineletInvDiag; /*!< \brief Inverse of the diagonal blocks of the tri-diag system (working memory). */
426 mutable vector<vector<ScalarType>>
427 LineletVector; /*!< \brief Solution and RHS of the tri-diag system (working memory). */
428
429#ifdef USE_MKL
430 using gemm_t = typename mkl_jit_wrapper<ScalarType>::gemm_t;
431 void* MatrixMatrixProductJitter; /*!< \brief Jitter handle for MKL JIT based GEMM. */
432 gemm_t MatrixMatrixProductKernel; /*!< \brief MKL JIT based GEMM kernel. */
433 void* MatrixVectorProductJitterBetaZero; /*!< \brief Jitter handle for MKL JIT based GEMV. */
434 gemm_t MatrixVectorProductKernelBetaZero; /*!< \brief MKL JIT based GEMV kernel. */
435 void* MatrixVectorProductJitterBetaOne; /*!< \brief Jitter handle for MKL JIT based GEMV with BETA=1.0. */
436 gemm_t MatrixVectorProductKernelBetaOne; /*!< \brief MKL JIT based GEMV kernel with BETA=1.0. */
437 void* MatrixVectorProductJitterAlphaMinusOne; /*!< \brief Jitter handle for MKL JIT based GEMV with ALPHA=-1.0 and
438 BETA=1.0. */
439 gemm_t MatrixVectorProductKernelAlphaMinusOne; /*!< \brief MKL JIT based GEMV kernel with ALPHA=-1.0 and BETA=1.0. */
440#endif
441
442#ifdef HAVE_PASTIX
443 mutable CPastixWrapper<ScalarType> pastix_wrapper;
444#endif
445
446 /*!
447 * \brief Handle type conversion for when we Set, Add, etc. blocks, preserving derivative information (if supported by
448 * types).
449 */
450 template <class DstType, class SrcType, su2enable_if<std::is_arithmetic<DstType>::value> = 0>
451 FORCEINLINE static DstType ActiveAssign(const SrcType& val) {
452 return SU2_TYPE::GetValue(val);
453 }
454
455 template <class DstType, class SrcType, su2enable_if<!std::is_arithmetic<DstType>::value> = 0>
456 FORCEINLINE static DstType ActiveAssign(const SrcType& val) {
457 return val;
458 }
459
460 /*!
461 * \brief Handle type conversion for when we Set, Add, etc. blocks, discarding derivative information.
462 */
463 template <class SrcType>
464 FORCEINLINE static ScalarType PassiveAssign(const SrcType& val) {
465 return SU2_TYPE::GetValue(val);
466 }
467
468 /*!
469 * \brief Calculates the matrix-vector product: product = matrix*vector
470 * \param[in] matrix
471 * \param[in] vector
472 * \param[out] product
473 */
474 void MatrixVectorProduct(const ScalarType* matrix, const ScalarType* vector, ScalarType* product) const;
475
476 /*!
477 * \brief Calculates the matrix-vector product: product += matrix*vector
478 * \param[in] matrix
479 * \param[in] vector
480 * \param[in,out] product
481 */
482 void MatrixVectorProductAdd(const ScalarType* matrix, const ScalarType* vector, ScalarType* product) const;
483
484 /*!
485 * \brief Calculates the matrix-vector product: product -= matrix*vector
486 * \param[in] matrix
487 * \param[in] vector
488 * \param[in,out] product
489 */
490 void MatrixVectorProductSub(const ScalarType* matrix, const ScalarType* vector, ScalarType* product) const;
491
492 /*!
493 * \brief Calculates the matrix-matrix product
494 */
495 void MatrixMatrixProduct(const ScalarType* matrix_a, const ScalarType* matrix_b, ScalarType* product) const;
496
497 /*!
498 * \brief Subtract b from a and store the result in c.
499 */
500 FORCEINLINE void VectorSubtraction(const ScalarType* a, const ScalarType* b, ScalarType* c) const {
501 for (unsigned long iVar = 0; iVar < nVar; iVar++) c[iVar] = a[iVar] - b[iVar];
502 }
503
504 /*!
505 * \brief Subtract b from a and store the result in c.
506 */
507 FORCEINLINE void MatrixSubtraction(const ScalarType* a, const ScalarType* b, ScalarType* c) const {
509 for (unsigned long iVar = 0; iVar < nVar * nEqn; iVar++) c[iVar] = a[iVar] - b[iVar];
510 }
511
512 /*!
513 * \brief Copy matrix src into dst, transpose if required.
514 */
515 FORCEINLINE void MatrixCopy(const ScalarType* src, ScalarType* dst) const {
517 for (auto iVar = 0ul; iVar < nVar * nEqn; ++iVar) dst[iVar] = src[iVar];
518 }
519
520 /*!
521 * \brief Zero a matrix.
522 */
523 FORCEINLINE void ZeroMatrix(ScalarType* mat) const {
525 for (auto iVar = 0ul; iVar < nVar * nEqn; ++iVar) mat[iVar] = 0;
526 }
527
528 /*!
529 * \brief Solve a small (nVar x nVar) linear system using Gaussian elimination.
530 * \param[in,out] matrix - On entry the system matrix, on exit the factorized matrix.
531 * \param[in,out] vec - On entry the rhs, on exit the solution.
532 */
533 void GaussElimination(ScalarType* matrix, ScalarType* vec) const;
534
535 /*!
536 * \brief Invert a small dense matrix.
537 * \param[in,out] matrix - On entry the system matrix, on exit the factorized matrix.
538 * \param[out] inverse - the matrix inverse.
539 */
540 void MatrixInverse(ScalarType* matrix, ScalarType* inverse) const;
541
542 /*!
543 * \brief Performs the Gauss Elimination algorithm to solve the linear subsystem of the (i,i) subblock and rhs.
544 * \param[in] block_i - Index of the (i,i) diagonal block.
545 * \param[in] rhs - Right-hand-side of the linear system.
546 * \return Solution of the linear system (overwritten on rhs).
547 */
548 inline void GaussElimination(unsigned long block_i, ScalarType* rhs) const;
549
550 /*!
551 * \brief Inverse diagonal block.
552 * \param[in] block_i - Indexes of the block in the matrix-by-blocks structure.
553 * \param[out] invBlock - Inverse block.
554 */
555 inline void InverseDiagonalBlock(unsigned long block_i, ScalarType* invBlock) const;
556
557 /*!
558 * \brief Invert diagonal block (Uii) of the ILU matrix in place.
559 * \param[in] block_i - Index of the block to invert.
560 * \return Inverted block.
561 */
562 inline const ScalarType* InvertDiagonalBlockILUMatrix(unsigned long block_i);
563
564 /*!
565 * \brief Returns the start of the ILU block or nullptr if (i,j) is not a nonzero.
566 * \param[in] block_i/j - Indexes of the block in the matrix-by-blocks structure.
567 */
568 inline ScalarType* GetBlock_ILUMatrix(unsigned long block_i, unsigned long block_j);
569
570 /*!
571 * \brief Performs the product of i-th row of the upper part of a sparse matrix by a vector.
572 * \param[in] vec - Vector to be multiplied by the upper part of the sparse matrix A.
573 * \param[in] row_i - Row of the matrix to be multiplied by vector vec.
574 * \param[in] col_ub - Exclusive upper bound for column indices considered in multiplication.
575 * \param[out] prod - Result of the product U(A)*vec.
576 */
577 inline void UpperProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, unsigned long col_ub,
578 ScalarType* prod) const;
579
580 /*!
581 * \brief Performs the product of i-th row of the lower part of a sparse matrix by a vector.
582 * \param[in] vec - Vector to be multiplied by the lower part of the sparse matrix A.
583 * \param[in] row_i - Row of the matrix to be multiplied by vector vec.
584 * \param[in] col_lb - Inclusive lower bound for column indices considered in multiplication.
585 * \param[out] prod - Result of the product L(A)*vec.
586 */
587 inline void LowerProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, unsigned long col_lb,
588 ScalarType* prod) const;
589
590 /*!
591 * \brief Performs the product of i-th row of the diagonal part of a sparse matrix by a vector.
592 * \param[in] vec - Vector to be multiplied by the diagonal part of the sparse matrix A.
593 * \param[in] row_i - Row of the matrix to be multiplied by vector vec.
594 * \return prod Result of the product D(A)*vec (stored at *prod_row_vector).
595 */
596 inline void DiagonalProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, ScalarType* prod) const;
597
598 /*!
599 * \brief Performs the product of i-th row of a sparse matrix by a vector.
600 * \param[in] vec - Vector to be multiplied by the row of the sparse matrix A.
601 * \param[in] row_i - Row of the matrix to be multiplied by vector vec.
602 * \return Result of the product (stored at *prod_row_vector).
603 */
604 void RowProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, ScalarType* prod) const;
605
606 /*!
607 * \brief Computes product += A_k * vec using the quantized representation of block k.
608 * \note Only valid after QuantizeDiagonalBlocks() has been called.
609 * \param[in] k - Block index in the CSR flat storage.
610 * \param[in] vec - Input vector (nEqn entries).
611 * \param[in,out] prod - Accumulation output (nVar entries).
612 */
613 inline void QuantizedMatVecAdd(const QuantScaleType* qs, const QuantType* qv, const ScalarType* vec,
614 ScalarType* prod) const;
615
616 /*! \brief Quantize one nVar×nVar block (row-major) into the int8 scale+value arrays.
617 * Called on the hot assembly path (SetBlocks/UpdateBlocks in Q_LU_SGS mode). */
618 inline void QuantizeBlock(const ScalarType* blk, QuantScaleType* qs, QuantType* qv) const {
619 EncodeQuantBlock([&](unsigned long r, unsigned long c) { return blk[r * nVar + c]; }, qs, qv, nVar);
620 }
621
622 /*! \brief Full-row product using quantized L/D/U (Q_LU_SGS SpMV path). */
623 inline void QuantizedRowProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, ScalarType* prod) const;
624
625 /*! \brief Upper-triangle product using quantized U (Q_LU_SGS backward sweep). */
626 inline void QuantizedUpperProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, unsigned long col_ub,
627 ScalarType* prod) const;
628
629 /*! \brief Lower-triangle product using quantized L (Q_LU_SGS forward sweep). */
630 inline void QuantizedLowerProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, unsigned long col_lb,
631 ScalarType* prod) const;
632
633 /*! \brief Diagonal product using quantized D (Q_LU_SGS backward sweep). */
634 inline void QuantizedDiagonalProduct(const CSysVector<ScalarType>& vec, unsigned long row_i, ScalarType* prod) const;
635
636 /*! \brief Gauss elimination on the quantized diagonal block: decodes q_blocks.d into a local
637 * ScalarType buffer and delegates to the scalar GaussElimination overload. */
638 inline void QuantizedGaussElimination(unsigned long block_i, ScalarType* rhs) const;
639
640 /*--- Hooks for GPU versions (implemented is in CSysMatrixGPU.cu). ---*/
641
642 /*!
643 * \brief Performs the product of a sparse matrix by a CSysVector on the device.
644 */
645 void MatrixVectorProductGPU(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod, CGeometry* geometry,
646 const CConfig* config) const;
647
648 /*!
649 * \brief Quantize the diagonal blocks directly on the device.
650 * \note Requires the device matrix to be up to date, see HtDTransfer.
651 */
652 void QuantizeDiagonalBlocksGPU();
653
654 /*!
655 * \brief Build the Jacobi preconditioner on the device, from the device copy of the matrix.
656 * \note Requires the device matrix to be up to date, see HtDTransfer.
657 */
658 void BuildJacobiPreconditionerGPU();
659
660 /*!
661 * \brief Apply the Jacobi preconditioner on the GPU/device side.
662 */
663 void ComputeJacobiPreconditionerGPU(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod,
664 CGeometry* geometry, const CConfig* config) const;
665
666 /*!
667 * \brief Build the ILU preconditioner on the device, from the device copy of the matrix.
668 * \note Requires the device matrix to be up to date, see HtDTransfer.
669 */
670 void BuildILUPreconditionerGPU();
671
672 /*!
673 * \brief Apply the ILU preconditioner on the device.
674 */
675 void ComputeILUPreconditionerGPU(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod) const;
676
677 /*!
678 * \brief Build the LU-SGS preconditioner on the device
679 */
680 void BuildLU_SGSPreconditionerGPU();
681
682 /*!
683 * \brief Apply the LU-SGS preconditioner forward pass on the device
684 */
685 void ComputeLU_SGSForwardGPU(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod) const;
686
687 /*!
688 * \brief Apply the LU-SGS preconditioner backward pass on the device
689 */
690 void ComputeLU_SGSBackwardGPU(CSysVector<ScalarType>& prod) const;
691
692 /*!
693 * \brief Apply the forward pass of the LU-SGS preconditioner
694 */
695 void ComputeLU_SGSPreconditionerForward(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod) const;
696
697 /*!
698 * \brief Apply the backward pass of the LU-SGS preconditioner
699 */
700 void ComputeLU_SGSPreconditionerBackward(CSysVector<ScalarType>& prod) const;
701
702 public:
703 /*!
704 * \brief Constructor of the class.
705 */
706 CSysMatrix();
707
708 /*!
709 * \brief Destructor of the class.
710 */
711 ~CSysMatrix();
712
713 /*!
714 * \brief Initializes the sparse matrix.
715 * \note The preconditioners require nVar == nEqn (square blocks).
716 * \param[in] npoint - Number of points including halos.
717 * \param[in] npointdomain - Number of points excluding halos.
718 * \param[in] nvar - Number of variables (and rows of the blocks).
719 * \param[in] neqn - Number of equations (and columns of the blocks).
720 * \param[in] geometry - Geometrical definition of the problem.
721 * \param[in] config - Definition of the particular problem.
722 * \param[in] needTranspPtr - If the L/U transpose maps should be built, used for "SetDiagonalAsColumnSum".
723 * \param[in] allow_quant - Quantization is only possible with solvers that "set and forget" the off-diagonal
724 * blocks of the matrix. Solvers that perform multiple updates would lose too much information, so
725 * that pattern is not supported with quantization (the code will hit null pointers). It is up to
726 * the solver to declare whether it will "set and forget".
727 * \param[in] override_prec - Decide if, and with what argument to override the preconditioner.
728 */
729 void Initialize(unsigned long npoint, unsigned long npointdomain, unsigned short nvar, unsigned short neqn,
730 bool EdgeConnect, CGeometry* geometry, const CConfig* config, bool needTranspPtr = false,
731 bool allow_quant = false, std::optional<unsigned short> override_prec = std::nullopt);
732
733 /*!
734 * \brief Compresses off-diagonal blocks into quantized form for use with USE_QUANTIZATION.
735 */
737
738 /*!
739 * \brief Sets to zero all the entries of the sparse matrix.
740 */
741 void SetValZero();
742
743 /*!
744 * \brief Sets to zero all the block diagonal entries of the sparse matrix.
745 */
746 void SetValDiagonalZero();
747
748 /*!
749 * \brief Performs the memory copy from host to device.
750 * \param[in] trigger - boolean value that decides whether to conduct the transfer or not. True by default.
751 */
752 void HtDTransfer(bool trigger = true) const;
753
754 /*!
755 * \brief Get a pointer to the start of block "ij"
756 * \param[in] block_i - Row index.
757 * \param[in] block_j - Column index.
758 * \return Pointer to location in memory where the block starts.
759 */
760 FORCEINLINE const ScalarType* GetBlock(unsigned long block_i, unsigned long block_j) const {
761 if (block_i == block_j) return &mat.d[block_i * nVar * nEqn];
762 if (block_j < block_i) {
763 for (auto index = mat.row_ptr_l[block_i]; index < mat.row_ptr_l[block_i + 1]; ++index)
764 if (mat.col_ind_l[index] == block_j) return &mat.l[index * nVar * nEqn];
765 return nullptr;
766 }
767 for (auto index = mat.row_ptr_u[block_i]; index < mat.row_ptr_u[block_i + 1]; ++index)
768 if (mat.col_ind_u[index] == block_j) return &mat.u[index * nVar * nEqn];
769 return nullptr;
770 }
771
772 /*!
773 * \brief Get a pointer to the start of block "ij", non-const version.
774 */
775 FORCEINLINE ScalarType* GetBlock(unsigned long block_i, unsigned long block_j) {
776 const CSysMatrix& const_this = *this;
777 return const_cast<ScalarType*>(const_this.GetBlock(block_i, block_j));
778 }
779
780 /*!
781 * \brief Read-only view of block (block_i, block_j). In Q_LU_SGS mode values are decoded
782 * on access inside CBlockView::operator()(i,j); no temporary copy is made.
783 * \return A CBlockView<const ScalarType> that evaluates to false if the block is absent.
784 */
785 FORCEINLINE CBlockView<const ScalarType> GetBlockView(unsigned long block_i, unsigned long block_j) const {
786#define GET_BLOCK_VIEW_IMPL \
787 if (!quantized_mode || block_i == block_j) { \
788 return {GetBlock(block_i, block_j), nullptr, nullptr, nVar}; \
789 } \
790 if (block_j < block_i) { \
791 for (auto k = mat.row_ptr_l[block_i]; k < mat.row_ptr_l[block_i + 1]; ++k) \
792 if (mat.col_ind_l[k] == block_j) return {nullptr, &q_scale.l[k * nVar], &q_blocks.l[k * nVar * nVar], nVar}; \
793 } else { \
794 for (auto k = mat.row_ptr_u[block_i]; k < mat.row_ptr_u[block_i + 1]; ++k) \
795 if (mat.col_ind_u[k] == block_j) return {nullptr, &q_scale.u[k * nVar], &q_blocks.u[k * nVar * nVar], nVar}; \
796 } \
797 return {}
799 }
800
801 /*!
802 * \overload Non const version of GetBlockView.
803 */
804 FORCEINLINE CBlockView<ScalarType> GetBlockView(unsigned long block_i, unsigned long block_j) {
806#undef GET_BLOCK_VIEW_IMPL
807 }
808
809 /*!
810 * \brief Set the value of a scaled block in the sparse matrix.
811 * \note This is an templated overload for C2Dcontainer specialization su2matrix.
812 * It assumes that MatrixType supports a member type Scalar and access operator(i, j).
813 * If the template param Overwrite is false we add to the block (bij += alpha*b).
814 * \param[in] block_i - Row index.
815 * \param[in] block_j - Column index.
816 * \param[in] val_block - Block to set to A(i, j).
817 * \param[in] alpha - Scale factor.
818 */
819 template <bool Overwrite = true, class MatrixType>
820 inline void SetBlock(unsigned long block_i, unsigned long block_j, MatrixType& val_block,
821 std::decay_t<typename MatrixType::Scalar> alpha = 1.0) {
822 auto view = GetBlockView(block_i, block_j);
823 if (!view) return;
824 view.template apply<Overwrite>(
825 [&](unsigned long i, unsigned long j) { return PassiveAssign(alpha * val_block(i, j)); });
826 }
827
828 /*!
829 * \overload val_block is a pointer instead of a matrix type.
830 */
831 template <bool Overwrite = true, class OtherType, su2enable_if<!is_pointer<OtherType>::value> = 0>
832 inline void SetBlock(unsigned long block_i, unsigned long block_j, const OtherType* val_block,
833 std::decay_t<OtherType> alpha = 1.0) {
834 auto view = GetBlockView(block_i, block_j);
835 if (!view) return;
836 view.template apply<Overwrite>(
837 [&](unsigned long i, unsigned long j) { return PassiveAssign(alpha * val_block[i * nEqn + j]); });
838 }
839
840 /*!
841 * \overload val_block is a double pointer instead of matrix type.
842 */
843 template <bool Overwrite = true, class OtherType>
844 inline void SetBlock(unsigned long block_i, unsigned long block_j, const OtherType* const* val_block,
845 std::decay_t<OtherType> alpha = 1.0) {
846 auto view = GetBlockView(block_i, block_j);
847 if (!view) return;
848 view.template apply<Overwrite>(
849 [&](unsigned long i, unsigned long j) { return PassiveAssign(alpha * val_block[i][j]); });
850 }
851
852 /*!
853 * \brief Add a scaled block (in flat format) to the sparse matrix (see SetBlock).
854 * \param[in] block_i - Row index.
855 * \param[in] block_j - Column index.
856 * \param[in] val_block - Block to set to A(i, j).
857 * \param[in] alpha - Scale factor.
858 */
859 template <class T, class OtherType = ScalarType>
860 inline void AddBlock(unsigned long block_i, unsigned long block_j, const T& val_block, OtherType alpha = 1.0) {
861 SetBlock<false>(block_i, block_j, val_block, alpha);
862 }
863
864 /*!
865 * \brief Subtracts the specified block to the sparse matrix (see AddBlock).
866 * \param[in] block_i - Row index.
867 * \param[in] block_j - Column index.
868 * \param[in] val_block - Block to subtract to A(i, j).
869 */
870 template <class T>
871 inline void SubtractBlock(unsigned long block_i, unsigned long block_j, const T& val_block) {
872 AddBlock(block_i, block_j, val_block, -1);
873 }
874
875 /*!
876 * \brief Returns the 4 blocks ii, ij, ji, jj used by "UpdateBlocks".
877 * \note This method assumes an FVM-type sparse pattern.
878 * \param[in] edge - Index of edge that connects iPoint and jPoint.
879 * \param[in] iPoint - Row to which we add the blocks.
880 * \param[in] jPoint - Row from which we subtract the blocks.
881 * \param[out] bii, bij, bji, bjj - Blocks of the matrix.
882 */
883 inline void GetBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, ScalarType*& bii,
884 ScalarType*& bij, ScalarType*& bji, ScalarType*& bjj) {
885 const auto blkSz = nVar * nEqn;
886 bii = &mat.d[iPoint * blkSz];
887 bjj = &mat.d[jPoint * blkSz];
888 bij = &mat.u[iEdge * blkSz];
889 bji = &mat.l[edge_ptr_l[iEdge] * blkSz];
890 }
891
892 /*!
893 * \brief Update 4 blocks ii, ij, ji, jj (add to i* sub from j*).
894 * \note This method assumes an FVM-type sparse pattern.
895 * \param[in] edge - Index of edge that connects iPoint and jPoint.
896 * \param[in] iPoint - Row to which we add the blocks.
897 * \param[in] jPoint - Row from which we subtract the blocks.
898 * \param[in] block_i - Adds to ii, subs from ji.
899 * \param[in] block_j - Adds to ij, subs from jj.
900 * \param[in] scale - Scale blocks during update (axpy type op).
901 */
902 template <bool OverwriteOffDiag = false, class MatrixType, class OtherType = ScalarType>
903 inline void UpdateBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, const MatrixType& block_i,
904 const MatrixType& block_j, OtherType scale = 1) {
905 const auto blkSz = nVar * nEqn;
906 auto* bii = &mat.d[iPoint * blkSz];
907 auto* bjj = &mat.d[jPoint * blkSz];
908
909 unsigned long iVar, jVar, offset = 0;
910
911 if (quantized_mode) {
912 assert(OverwriteOffDiag);
913 /*--- Diagonal: full-precision accumulation. Off-diagonal: quantize on the fly. ---*/
914 ScalarType bij_buf[MAXNVAR * MAXNVAR], bji_buf[MAXNVAR * MAXNVAR];
915 for (iVar = 0; iVar < nVar; iVar++)
916 for (jVar = 0; jVar < nEqn; jVar++, ++offset) {
917 bii[offset] += PassiveAssign(block_i[iVar][jVar] * scale);
918 bjj[offset] -= PassiveAssign(block_j[iVar][jVar] * scale);
919 bij_buf[offset] = PassiveAssign(block_j[iVar][jVar] * scale);
920 bji_buf[offset] = -PassiveAssign(block_i[iVar][jVar] * scale);
921 }
922 QuantizeBlock(bij_buf, &q_scale.u[iEdge * nVar], &q_blocks.u[iEdge * blkSz]);
923 const auto k_l = edge_ptr_l[iEdge];
924 QuantizeBlock(bji_buf, &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz]);
925 return;
926 }
927
928 auto* bij = &mat.u[iEdge * blkSz];
929 auto* bji = &mat.l[edge_ptr_l[iEdge] * blkSz];
930 for (iVar = 0; iVar < nVar; iVar++) {
931 for (jVar = 0; jVar < nEqn; jVar++) {
932 bii[offset] += PassiveAssign(block_i[iVar][jVar] * scale);
933 bjj[offset] -= PassiveAssign(block_j[iVar][jVar] * scale);
934 if constexpr (OverwriteOffDiag) {
935 bij[offset] = PassiveAssign(block_j[iVar][jVar] * scale);
936 bji[offset] = -PassiveAssign(block_i[iVar][jVar] * scale);
937 } else {
938 bij[offset] += PassiveAssign(block_j[iVar][jVar] * scale);
939 bji[offset] -= PassiveAssign(block_i[iVar][jVar] * scale);
940 }
941 ++offset;
942 }
943 }
944 }
945
946 /*!
947 * \brief Short-hand for the "subtractive" version (sub from i* add to j*) of UpdateBlocks.
948 */
949 template <class MatrixType>
950 inline void UpdateBlocksSub(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint,
951 const MatrixType& block_i, const MatrixType& block_j) {
952 UpdateBlocks<false, MatrixType, ScalarType>(iEdge, iPoint, jPoint, block_i, block_j, -1);
953 }
954
955 /*!
956 * \brief SIMD version, does the update for multiple edges and points.
957 * \note Nothing is updated if the mask is 0.
958 */
959 template <class MatTypeSIMD, size_t N, class I, class F = ScalarType>
961 const MatTypeSIMD& block_i, const MatTypeSIMD& block_j, simd::Array<F, N> mask = 1) {
962 static_assert(MatTypeSIMD::StaticSize, "This method requires static size blocks.");
963 static_assert(MatTypeSIMD::IsRowMajor, "Block storage is not compatible with matrix.");
964 constexpr size_t blkSz = MatTypeSIMD::StaticSize;
965 assert(blkSz == nVar * nEqn);
966 constexpr size_t nVar = MatTypeSIMD::StaticNRows;
967
968 /*--- "Transpose" the blocks, scale, and possibly convert types,
969 * giving the compiler the chance to vectorize all of these. ---*/
970 ScalarType blk_i[N][blkSz], blk_j[N][blkSz];
971
972 for (size_t i = 0; i < blkSz; ++i) {
974 for (size_t k = 0; k < N; ++k) {
975 blk_i[k][i] = PassiveAssign(-mask[k] * block_i.data()[i][k]);
976 blk_j[k][i] = PassiveAssign(mask[k] * block_j.data()[i][k]);
977 }
978 }
979
980 /*--- Update one by one skipping if mask is 0. ---*/
981 for (size_t k = 0; k < N; ++k) {
982 if (mask[k] == 0) continue;
983
984 auto bii = &mat.d[iPoint[k] * blkSz];
985 auto bjj = &mat.d[jPoint[k] * blkSz];
986
987 if (quantized_mode) {
989 for (size_t i = 0; i < blkSz; ++i) {
990 bii[i] -= blk_i[k][i];
991 bjj[i] -= blk_j[k][i];
992 }
993 EncodeQuantBlock([&, k](unsigned long r, unsigned long c) { return blk_j[k][r * nVar + c]; },
994 &q_scale.u[iEdge[k] * nVar], &q_blocks.u[iEdge[k] * blkSz], nVar);
995 const auto k_l = edge_ptr_l[iEdge[k]];
996 EncodeQuantBlock([&, k](unsigned long r, unsigned long c) { return blk_i[k][r * nVar + c]; },
997 &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz], nVar);
998 } else {
999 auto bij = &mat.u[iEdge[k] * blkSz];
1000 auto bji = &mat.l[edge_ptr_l[iEdge[k]] * blkSz];
1001 /*--- Update, block i was negated during transpose in the
1002 * hope the assignments below become non-temporal stores. ---*/
1004 for (size_t i = 0; i < blkSz; ++i) {
1005 bii[i] -= blk_i[k][i];
1006 bjj[i] -= blk_j[k][i];
1007 bij[i] = blk_j[k][i];
1008 bji[i] = blk_i[k][i];
1009 }
1010 }
1011 }
1012 }
1013
1014 /*!
1015 * \brief Sets 2 blocks ij and ji (add to i* sub from j*) associated with
1016 * one edge of an FVM-type sparse pattern.
1017 * \note The parameter Overwrite allows completely writing over the
1018 * current values held by the matrix (true), or updating them (false).
1019 * \param[in] edge - Index of edge that connects iPoint and jPoint.
1020 * \param[in] block_i - Subs from ji.
1021 * \param[in] block_j - Adds to ij.
1022 * \param[in] scale - Scale blocks during update (axpy type op).
1023 */
1024 template <class MatrixType, class OtherType = ScalarType, bool Overwrite = true>
1025 inline void SetBlocks(unsigned long iEdge, const MatrixType& block_i, const MatrixType& block_j,
1026 OtherType scale = 1) {
1027 const auto blkSz = nVar * nEqn;
1028 unsigned long iVar, jVar, offset = 0;
1029
1030 if (quantized_mode) {
1031 assert(Overwrite);
1032 ScalarType bij_buf[MAXNVAR * MAXNVAR], bji_buf[MAXNVAR * MAXNVAR];
1033 for (iVar = 0; iVar < nVar; iVar++)
1034 for (jVar = 0; jVar < nEqn; jVar++, ++offset) {
1035 bij_buf[offset] = PassiveAssign(block_j[iVar][jVar] * scale);
1036 bji_buf[offset] = -PassiveAssign(block_i[iVar][jVar] * scale);
1037 }
1038 QuantizeBlock(bij_buf, &q_scale.u[iEdge * nVar], &q_blocks.u[iEdge * blkSz]);
1039 const auto k_l = edge_ptr_l[iEdge];
1040 QuantizeBlock(bji_buf, &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz]);
1041 return;
1042 }
1043
1044 ScalarType* bij = &mat.u[iEdge * blkSz];
1045 ScalarType* bji = &mat.l[edge_ptr_l[iEdge] * blkSz];
1046 for (iVar = 0; iVar < nVar; iVar++) {
1047 for (jVar = 0; jVar < nEqn; jVar++) {
1048 bij[offset] = (Overwrite ? ScalarType(0) : bij[offset]) + PassiveAssign(block_j[iVar][jVar] * scale);
1049 bji[offset] = (Overwrite ? ScalarType(0) : bji[offset]) - PassiveAssign(block_i[iVar][jVar] * scale);
1050 ++offset;
1051 }
1052 }
1053 }
1054
1055 /*!
1056 * \brief Short-hand for the "additive overwrite" version of SetBlocks.
1057 */
1058 template <class MatrixType, class OtherType = ScalarType>
1059 inline void UpdateBlocks(unsigned long iEdge, const MatrixType& block_i, const MatrixType& block_j,
1060 OtherType scale = 1) {
1061 SetBlocks<MatrixType, OtherType, false>(iEdge, block_i, block_j, scale);
1062 }
1063
1064 /*!
1065 * \brief Short-hand for the "subtractive" version (sub from i* add to j*) of SetBlocks.
1066 */
1067 template <class MatrixType>
1068 inline void UpdateBlocksSub(unsigned long iEdge, const MatrixType& block_i, const MatrixType& block_j) {
1069 SetBlocks<MatrixType, ScalarType, false>(iEdge, block_i, block_j, -1);
1070 }
1071
1072 /*!
1073 * \brief Set the four blocks of an edge, for fluxes whose i and j contributions are independent.
1074 * \note The diagonal blocks are accumulated, the off-diagonal blocks are set.
1075 */
1076 template <class MatrixType, class OtherType = ScalarType>
1077 inline void SetBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, const MatrixType& jac_ii,
1078 const MatrixType& jac_ij, const MatrixType& jac_ji, const MatrixType& jac_jj,
1079 OtherType mask = 1) {
1080 const auto blkSz = nVar * nEqn;
1081 auto* bii = &mat.d[iPoint * blkSz];
1082 auto* bjj = &mat.d[jPoint * blkSz];
1083 unsigned long iVar, jVar, offset = 0;
1084
1085 if (quantized_mode) {
1086 ScalarType bij_buf[MAXNVAR * MAXNVAR], bji_buf[MAXNVAR * MAXNVAR];
1087 for (iVar = 0; iVar < nVar; iVar++)
1088 for (jVar = 0; jVar < nEqn; jVar++, ++offset) {
1089 bii[offset] += PassiveAssign(jac_ii[iVar][jVar] * mask);
1090 bjj[offset] += PassiveAssign(jac_jj[iVar][jVar] * mask);
1091 bij_buf[offset] = PassiveAssign(jac_ij[iVar][jVar] * mask);
1092 bji_buf[offset] = PassiveAssign(jac_ji[iVar][jVar] * mask);
1093 }
1094 QuantizeBlock(bij_buf, &q_scale.u[iEdge * nVar], &q_blocks.u[iEdge * blkSz]);
1095 const auto k_l = edge_ptr_l[iEdge];
1096 QuantizeBlock(bji_buf, &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz]);
1097 return;
1098 }
1099
1100 auto* bij = &mat.u[iEdge * blkSz];
1101 auto* bji = &mat.l[edge_ptr_l[iEdge] * blkSz];
1102 for (iVar = 0; iVar < nVar; iVar++) {
1103 for (jVar = 0; jVar < nEqn; jVar++) {
1104 bii[offset] += PassiveAssign(jac_ii[iVar][jVar] * mask);
1105 bjj[offset] += PassiveAssign(jac_jj[iVar][jVar] * mask);
1106 bij[offset] = PassiveAssign(jac_ij[iVar][jVar] * mask);
1107 bji[offset] = PassiveAssign(jac_ji[iVar][jVar] * mask);
1108 ++offset;
1109 }
1110 }
1111 }
1112
1113 /*!
1114 * \brief Set the off-diagonal blocks of an edge, the diagonal being assembled elsewhere.
1115 */
1116 template <class MatrixType, class OtherType = ScalarType>
1117 inline void SetOffDiagBlocks(unsigned long iEdge, const MatrixType& jac_ij, const MatrixType& jac_ji,
1118 OtherType mask = 1) {
1119 const auto blkSz = nVar * nEqn;
1120 unsigned long iVar, jVar, offset = 0;
1121
1122 if (quantized_mode) {
1123 ScalarType bij_buf[MAXNVAR * MAXNVAR], bji_buf[MAXNVAR * MAXNVAR];
1124 for (iVar = 0; iVar < nVar; iVar++)
1125 for (jVar = 0; jVar < nEqn; jVar++, ++offset) {
1126 bij_buf[offset] = PassiveAssign(jac_ij[iVar][jVar] * mask);
1127 bji_buf[offset] = PassiveAssign(jac_ji[iVar][jVar] * mask);
1128 }
1129 QuantizeBlock(bij_buf, &q_scale.u[iEdge * nVar], &q_blocks.u[iEdge * blkSz]);
1130 const auto k_l = edge_ptr_l[iEdge];
1131 QuantizeBlock(bji_buf, &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz]);
1132 return;
1133 }
1134
1135 auto* bij = &mat.u[iEdge * blkSz];
1136 auto* bji = &mat.l[edge_ptr_l[iEdge] * blkSz];
1137 for (iVar = 0; iVar < nVar; iVar++) {
1138 for (jVar = 0; jVar < nEqn; jVar++) {
1139 bij[offset] = PassiveAssign(jac_ij[iVar][jVar] * mask);
1140 bji[offset] = PassiveAssign(jac_ji[iVar][jVar] * mask);
1141 ++offset;
1142 }
1143 }
1144 }
1145
1146 /*!
1147 * \brief SIMD version, does the update for multiple edges.
1148 * \note Nothing is updated if the mask is 0.
1149 */
1150 template <class MatTypeSIMD, size_t N, class I, class F = ScalarType>
1151 FORCEINLINE void SetBlocks(simd::Array<I, N> iEdge, const MatTypeSIMD& block_i, const MatTypeSIMD& block_j,
1152 simd::Array<F, N> mask = 1) {
1153 static_assert(MatTypeSIMD::StaticSize, "This method requires static size blocks.");
1154 static_assert(MatTypeSIMD::IsRowMajor, "Block storage is not compatible with matrix.");
1155 constexpr size_t blkSz = MatTypeSIMD::StaticSize;
1156 assert(blkSz == nVar * nEqn);
1157 constexpr size_t nVar = MatTypeSIMD::StaticNRows;
1158
1159 /*--- "Transpose" the blocks, scale, and possibly convert types,
1160 * giving the compiler the chance to vectorize all of these. ---*/
1161 ScalarType blk_i[N][blkSz], blk_j[N][blkSz];
1162
1163 for (size_t i = 0; i < blkSz; ++i) {
1165 for (size_t k = 0; k < N; ++k) {
1166 blk_i[k][i] = PassiveAssign(-mask[k] * block_i.data()[i][k]);
1167 blk_j[k][i] = PassiveAssign(mask[k] * block_j.data()[i][k]);
1168 }
1169 }
1170
1171 /*--- Update one by one skipping if mask is 0. ---*/
1172 for (size_t k = 0; k < N; ++k) {
1173 if (mask[k] == 0) continue;
1174
1175 if (quantized_mode) {
1176 EncodeQuantBlock([&, k](unsigned long r, unsigned long c) { return blk_j[k][r * nVar + c]; },
1177 &q_scale.u[iEdge[k] * nVar], &q_blocks.u[iEdge[k] * blkSz], nVar);
1178 const auto k_l = edge_ptr_l[iEdge[k]];
1179 EncodeQuantBlock([&, k](unsigned long r, unsigned long c) { return blk_i[k][r * nVar + c]; },
1180 &q_scale.l[k_l * nVar], &q_blocks.l[k_l * blkSz], nVar);
1181 } else {
1182 ScalarType* bij = &mat.u[iEdge[k] * blkSz];
1183 ScalarType* bji = &mat.l[edge_ptr_l[iEdge[k]] * blkSz];
1184 /*--- Update, block i was negated during transpose in the
1185 * hope the assignments below become non-temporal stores. ---*/
1187 for (size_t i = 0; i < blkSz; ++i) {
1188 bij[i] = blk_j[k][i];
1189 bji[i] = blk_i[k][i];
1190 }
1191 }
1192 }
1193 }
1194
1195 /*!
1196 * \brief Sets the specified block to the (i, i) subblock of the sparse matrix.
1197 * Scales the input block by factor alpha. If the Overwrite parameter is
1198 * false we update instead (bii += alpha*b).
1199 * \param[in] block_i - Diagonal index.
1200 * \param[in] val_block - Block to add to the diagonal of the matrix.
1201 * \param[in] alpha - Scale factor.
1202 */
1203 template <class OtherType, bool Overwrite = true, class T = ScalarType>
1204 inline void SetBlock2Diag(unsigned long block_i, const OtherType& val_block, T alpha = 1.0) {
1205 auto mat_ii = &mat.d[block_i * nVar * nEqn];
1206
1207 for (auto iVar = 0ul; iVar < nVar; iVar++)
1208 for (auto jVar = 0ul; jVar < nEqn; jVar++) {
1209 *mat_ii = (Overwrite ? ScalarType(0) : *mat_ii) + PassiveAssign(alpha * val_block[iVar][jVar]);
1210 ++mat_ii;
1211 }
1212 }
1213
1214 /*!
1215 * \brief Non overwrite version of SetBlock2Diag, also with scaling.
1216 */
1217 template <class OtherType, class T = ScalarType>
1218 inline void AddBlock2Diag(unsigned long block_i, const OtherType& val_block, T alpha = 1.0) {
1219 SetBlock2Diag<OtherType, false>(block_i, val_block, alpha);
1220 }
1221
1222 /*!
1223 * \brief Short-hand to AddBlock2Diag with alpha = -1, i.e. subtracts from the current diagonal.
1224 */
1225 template <class OtherType>
1226 inline void SubtractBlock2Diag(unsigned long block_i, const OtherType& val_block) {
1227 AddBlock2Diag(block_i, val_block, -1.0);
1228 }
1229
1230 /*!
1231 * \brief Adds the specified value to the diagonal of the (i, i) subblock
1232 * of the matrix-by-blocks structure.
1233 * \param[in] block_i - Diagonal index.
1234 * \param[in] val_matrix - Value to add to the diagonal elements of A(i, i).
1235 */
1236 template <class OtherType>
1237 inline void AddVal2Diag(unsigned long block_i, OtherType val_matrix) {
1238 auto d = &mat.d[block_i * nVar * nVar];
1239 for (auto iVar = 0ul; iVar < nVar; iVar++) d[iVar * (nVar + 1)] += PassiveAssign(val_matrix);
1240 }
1241
1242 /*!
1243 * \brief Adds the specified value to the diagonal of the (i, i) subblock
1244 * of the matrix-by-blocks structure.
1245 * \param[in] block_i - Diagonal index.
1246 * \param[in] iVar - Variable index.
1247 * \param[in] val - Value to add to the diagonal elements of A(i, i).
1248 */
1249 template <class OtherType>
1250 inline void AddVal2Diag(unsigned long block_i, unsigned long iVar, OtherType val) {
1251 mat.d[block_i * nVar * nVar + iVar * (nVar + 1)] += PassiveAssign(val);
1252 }
1253
1254 /*!
1255 * \brief Sets the specified value to the diagonal of the (i, i) subblock
1256 * of the matrix-by-blocks structure.
1257 * \param[in] block_i - Diagonal index.
1258 * \param[in] val_matrix - Value to add to the diagonal elements of A(i, i).
1259 */
1260 template <class OtherType>
1261 inline void SetVal2Diag(unsigned long block_i, OtherType val_matrix) {
1262 /*--- Clear entire block before setting its diagonal. ---*/
1264 for (auto iVar = 0ul; iVar < nVar * nVar; iVar++) mat.d[block_i * nVar * nVar + iVar] = 0.0;
1265
1266 AddVal2Diag(block_i, val_matrix);
1267 }
1268
1269 /*!
1270 * \brief Deletes the values of a row of the sparse matrix.
1271 * \param[in] block_i - Index of the block.
1272 * \param[in] row - Row within the block.
1273 */
1274 void DeleteValsRowi(unsigned long block_i, unsigned long row);
1275
1276 /*!
1277 * \brief Modifies this matrix (A) and a rhs vector (b) such that (A^-1 * b)_i = x_i.
1278 * \param[in] node_i - Index of the node for which to enforce the solution of all DOF's.
1279 * \param[in] x_i - Values to enforce (nVar sized).
1280 * \param[in,out] b - The rhs vector (b := b - A_{*,i} * x_i; b_i = x_i).
1281 */
1282 template <class OtherType>
1283 void EnforceSolutionAtNode(unsigned long node_i, const OtherType* x_i, CSysVector<OtherType>& b);
1284
1285 /*!
1286 * \brief Similar to EnforceSolutionAtNode, but for 0 projection in a given direction.
1287 */
1288 template <class OtherType>
1289 void EnforceZeroProjection(unsigned long node_i, const OtherType* n, CSysVector<OtherType>& b);
1290
1291 /*!
1292 * \brief Sets the diagonal entries of the matrix as the sum of the blocks in the corresponding column.
1293 */
1295
1296 /*!
1297 * \brief Transposes the matrix, any preconditioner that was computed may be invalid.
1298 */
1299 void TransposeInPlace();
1300
1301 /*!
1302 * \brief Add a scaled sparse matrix to "this" (axpy-type operation, A = A+alpha*B).
1303 * \note Matrices must have the same sparse pattern.
1304 * \param[in] alpha - The scaling constant.
1305 * \param[in] B - Matrix being.
1306 */
1307 void MatrixMatrixAddition(ScalarType alpha, const CSysMatrix& B);
1308
1309 /*!
1310 * \brief Performs the product of a sparse matrix by a CSysVector.
1311 * \param[in] vec - CSysVector to be multiplied by the sparse matrix A.
1312 * \param[in] geometry - Geometrical definition of the problem.
1313 * \param[in] config - Definition of the particular problem.
1314 * \param[out] prod - Result of the product.
1315 */
1316 void MatrixVectorProduct(const CSysVector<ScalarType>& vec, CSysVector<ScalarType>& prod, CGeometry* geometry,
1317 const CConfig* config) const;
1318
1319 /*!
1320 * \brief Build the Jacobi preconditioner.
1321 */
1323
1324 /*!
1325 * \brief Multiply CSysVector by the preconditioner
1326 * \param[in] vec - CSysVector to be multiplied by the preconditioner.
1327 * \param[out] prod - Result of the product A*vec.
1328 * \param[in] geometry - Geometrical definition of the problem.
1329 * \param[in] config - Definition of the particular problem.
1330 */
1332 const CConfig* config) const;
1333
1334 /*!
1335 * \brief Build the ILU preconditioner.
1336 */
1338
1339 /*!
1340 * \brief Multiply CSysVector by the preconditioner
1341 * \param[in] vec - CSysVector to be multiplied by the preconditioner.
1342 * \param[out] prod - Result of the product A*vec.
1343 * \param[in] geometry - Geometrical definition of the problem.
1344 * \param[in] config - Definition of the particular problem.
1345 */
1347 const CConfig* config) const;
1348
1349 /*!
1350 * \brief Build the LU-SGS preconditioner.
1351 */
1353
1354 /*!
1355 * \brief Multiply CSysVector by the preconditioner
1356 * \param[in] vec - CSysVector to be multiplied by the preconditioner.
1357 * \param[out] prod - Result of the product A*vec.
1358 */
1360 const CConfig* config) const;
1361
1362 /*!
1363 * \brief Build the Linelet preconditioner.
1364 * \param[in] geometry - Geometrical definition of the problem.
1365 * \param[in] config - Definition of the particular problem.
1366 */
1367 void BuildLineletPreconditioner(const CGeometry* geometry, const CConfig* config);
1368
1369 /*!
1370 * \brief Multiply CSysVector by the preconditioner
1371 * \param[in] vec - CSysVector to be multiplied by the preconditioner.
1372 * \param[out] prod - Result of the product A*vec.
1373 */
1375 CGeometry* geometry, const CConfig* config) const;
1376
1377 /*!
1378 * \brief Compute the linear residual.
1379 * \param[in] sol - Solution (x).
1380 * \param[in] f - Right hand side (b).
1381 * \param[out] res - Residual (Ax-b).
1382 */
1384 CSysVector<ScalarType>& res) const;
1385
1386 /*!
1387 * \brief Factorize matrix using PaStiX.
1388 * \param[in] geometry - Geometrical definition of the problem.
1389 * \param[in] config - Definition of the particular problem.
1390 * \param[in] kind_fact - Type of factorization.
1391 */
1392 void BuildPastixPreconditioner(CGeometry* geometry, const CConfig* config, unsigned short kind_fact);
1393
1394 /*!
1395 * \brief Apply the PaStiX factorization to CSysVec.
1396 * \param[in] vec - CSysVector to be multiplied by the preconditioner.
1397 * \param[out] prod - Result of the product M*vec.
1398 * \param[in] geometry - Geometrical definition of the problem.
1399 * \param[in] config - Definition of the particular problem.
1400 */
1402 const CConfig* config) const;
1403};
An interface to the INRIA solver PaStiX (http://pastix.gforge.inria.fr/files/README-txt....
#define GET_BLOCK_VIEW_IMPL
void EncodeQuantBlock(const F &f, uint8_t *__restrict qs, int8_t *__restrict qv, unsigned long nVar) noexcept
Encode one nVar×nVar block into per-row int8 quantized storage, see EncodeQuantRow (each row's scale/...
Definition CSysMatrix.hpp:196
float DecodeQuantScale(uint8_t e) noexcept
Reconstruct the float row-scale from a stored uint8 binary exponent. e is stored already biased,...
Definition CSysMatrix.hpp:121
#define EQR_PASSIVE(ROW, COL)
void EncodeQuantRow(const F &f, uint8_t &qs, int8_t *__restrict qv, unsigned long nVar, unsigned long r) noexcept
Encode one row of an nVar×nVar block into int8 quantized storage: qs receives the row's scale exponen...
Definition CSysMatrix.hpp:144
Declararion and inlines of the vector class used in the solution of large, distributed,...
#define B(i, j)
Definition blas_structure.cpp:132
Main class for defining the problem; basically this class reads the configuration file,...
Definition CConfig.hpp:64
Parent class for defining the geometry of the problem (complete geometry, multigrid agglomerated geom...
Definition CGeometry.hpp:75
Wrapper class that converts between SU2 sparse format and PaStiX format and simplifies calls to the e...
Definition CPastixWrapper.hpp:55
Main class for defining block-compressed-row-storage sparse matrices.
Definition CSysMatrix.hpp:256
void GetBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, ScalarType *&bii, ScalarType *&bij, ScalarType *&bji, ScalarType *&bjj)
Returns the 4 blocks ii, ij, ji, jj used by "UpdateBlocks".
Definition CSysMatrix.hpp:883
void SetVal2Diag(unsigned long block_i, OtherType val_matrix)
Sets the specified value to the diagonal of the (i, i) subblock of the matrix-by-blocks structure.
Definition CSysMatrix.hpp:1261
void ComputeLineletPreconditioner(const CSysVector< ScalarType > &vec, CSysVector< ScalarType > &prod, CGeometry *geometry, const CConfig *config) const
Multiply CSysVector by the preconditioner.
Definition CSysMatrix.cpp:1484
void SetBlocks(simd::Array< I, N > iEdge, const MatTypeSIMD &block_i, const MatTypeSIMD &block_j, simd::Array< F, N > mask=1)
SIMD version, does the update for multiple edges.
Definition CSysMatrix.hpp:1151
void EnforceSolutionAtNode(unsigned long node_i, const OtherType *x_i, CSysVector< OtherType > &b)
Modifies this matrix (A) and a rhs vector (b) such that (A^-1 * b)_i = x_i.
Definition CSysMatrix.cpp:1605
void UpdateBlocksSub(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, const MatrixType &block_i, const MatrixType &block_j)
Short-hand for the "subtractive" version (sub from i* add to j*) of UpdateBlocks.
Definition CSysMatrix.hpp:950
void BuildLineletPreconditioner(const CGeometry *geometry, const CConfig *config)
Build the Linelet preconditioner.
Definition CSysMatrix.cpp:1454
void BuildILUPreconditioner()
Build the ILU preconditioner.
Definition CSysMatrix.cpp:1064
void DeleteValsRowi(unsigned long block_i, unsigned long row)
Deletes the values of a row of the sparse matrix.
Definition CSysMatrix.cpp:922
void MatrixMatrixAddition(ScalarType alpha, const CSysMatrix &B)
Add a scaled sparse matrix to "this" (axpy-type operation, A = A+alpha*B).
Definition CSysMatrix.cpp:1795
~CSysMatrix()
Destructor of the class.
Definition CSysMatrix.cpp:120
void SetDiagonalAsColumnSum()
Sets the diagonal entries of the matrix as the sum of the blocks in the corresponding column.
Definition CSysMatrix.cpp:1677
void HtDTransfer(bool trigger=true) const
Performs the memory copy from host to device.
Definition CSysMatrixGPU.cu:1016
void SubtractBlock2Diag(unsigned long block_i, const OtherType &val_block)
Short-hand to AddBlock2Diag with alpha = -1, i.e. subtracts from the current diagonal.
Definition CSysMatrix.hpp:1226
void TransposeInPlace()
Transposes the matrix, any preconditioner that was computed may be invalid.
Definition CSysMatrix.cpp:1710
void SetValDiagonalZero()
Sets to zero all the block diagonal entries of the sparse matrix.
Definition CSysMatrix.cpp:836
void AddVal2Diag(unsigned long block_i, unsigned long iVar, OtherType val)
Adds the specified value to the diagonal of the (i, i) subblock of the matrix-by-blocks structure.
Definition CSysMatrix.hpp:1250
void SetValZero()
Sets to zero all the entries of the sparse matrix.
Definition CSysMatrix.cpp:810
void AddBlock2Diag(unsigned long block_i, const OtherType &val_block, T alpha=1.0)
Non overwrite version of SetBlock2Diag, also with scaling.
Definition CSysMatrix.hpp:1218
void EnforceZeroProjection(unsigned long node_i, const OtherType *n, CSysVector< OtherType > &b)
Similar to EnforceSolutionAtNode, but for 0 projection in a given direction.
Definition CSysMatrix.cpp:1640
void ComputeILUPreconditioner(const CSysVector< ScalarType > &vec, CSysVector< ScalarType > &prod, CGeometry *geometry, const CConfig *config) const
Multiply CSysVector by the preconditioner.
Definition CSysMatrix.cpp:1207
void SetBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, const MatrixType &jac_ii, const MatrixType &jac_ij, const MatrixType &jac_ji, const MatrixType &jac_jj, OtherType mask=1)
Set the four blocks of an edge, for fluxes whose i and j contributions are independent.
Definition CSysMatrix.hpp:1077
void ComputePastixPreconditioner(const CSysVector< ScalarType > &vec, CSysVector< ScalarType > &prod, CGeometry *geometry, const CConfig *config) const
Apply the PaStiX factorization to CSysVec.
Definition CSysMatrix.cpp:1831
void SetBlock2Diag(unsigned long block_i, const OtherType &val_block, T alpha=1.0)
Sets the specified block to the (i, i) subblock of the sparse matrix. Scales the input block by facto...
Definition CSysMatrix.hpp:1204
void SetBlock(unsigned long block_i, unsigned long block_j, MatrixType &val_block, std::decay_t< typename MatrixType::Scalar > alpha=1.0)
Set the value of a scaled block in the sparse matrix.
Definition CSysMatrix.hpp:820
const ScalarType * GetBlock(unsigned long block_i, unsigned long block_j) const
Get a pointer to the start of block "ij".
Definition CSysMatrix.hpp:760
ScalarType * GetBlock(unsigned long block_i, unsigned long block_j)
Get a pointer to the start of block "ij", non-const version.
Definition CSysMatrix.hpp:775
void UpdateBlocks(unsigned long iEdge, unsigned long iPoint, unsigned long jPoint, const MatrixType &block_i, const MatrixType &block_j, OtherType scale=1)
Update 4 blocks ii, ij, ji, jj (add to i* sub from j*).
Definition CSysMatrix.hpp:903
CBlockView< ScalarType > GetBlockView(unsigned long block_i, unsigned long block_j)
Definition CSysMatrix.hpp:804
void AddBlock(unsigned long block_i, unsigned long block_j, const T &val_block, OtherType alpha=1.0)
Add a scaled block (in flat format) to the sparse matrix (see SetBlock).
Definition CSysMatrix.hpp:860
CSysMatrix()
Constructor of the class.
Definition CSysMatrix.cpp:70
void BuildLU_SGSPreconditioner()
Build the LU-SGS preconditioner.
Definition CSysMatrix.cpp:1302
void ComputeResidual(const CSysVector< ScalarType > &sol, const CSysVector< ScalarType > &f, CSysVector< ScalarType > &res) const
Compute the linear residual.
Definition CSysMatrix.cpp:1590
void BuildJacobiPreconditioner()
Build the Jacobi preconditioner.
Definition CSysMatrix.cpp:1004
void BuildPastixPreconditioner(CGeometry *geometry, const CConfig *config, unsigned short kind_fact)
Factorize matrix using PaStiX.
Definition CSysMatrix.cpp:1815
void SubtractBlock(unsigned long block_i, unsigned long block_j, const T &val_block)
Subtracts the specified block to the sparse matrix (see AddBlock).
Definition CSysMatrix.hpp:871
void SetBlock(unsigned long block_i, unsigned long block_j, const OtherType *val_block, std::decay_t< OtherType > alpha=1.0)
Definition CSysMatrix.hpp:832
void SetOffDiagBlocks(unsigned long iEdge, const MatrixType &jac_ij, const MatrixType &jac_ji, OtherType mask=1)
Set the off-diagonal blocks of an edge, the diagonal being assembled elsewhere.
Definition CSysMatrix.hpp:1117
void UpdateBlocksSub(unsigned long iEdge, const MatrixType &block_i, const MatrixType &block_j)
Short-hand for the "subtractive" version (sub from i* add to j*) of SetBlocks.
Definition CSysMatrix.hpp:1068
void SetBlocks(simd::Array< I, N > iEdge, simd::Array< I, N > iPoint, simd::Array< I, N > jPoint, const MatTypeSIMD &block_i, const MatTypeSIMD &block_j, simd::Array< F, N > mask=1)
SIMD version, does the update for multiple edges and points.
Definition CSysMatrix.hpp:960
void SetBlocks(unsigned long iEdge, const MatrixType &block_i, const MatrixType &block_j, OtherType scale=1)
Sets 2 blocks ij and ji (add to i* sub from j*) associated with one edge of an FVM-type sparse patter...
Definition CSysMatrix.hpp:1025
void AddVal2Diag(unsigned long block_i, OtherType val_matrix)
Adds the specified value to the diagonal of the (i, i) subblock of the matrix-by-blocks structure.
Definition CSysMatrix.hpp:1237
void UpdateBlocks(unsigned long iEdge, const MatrixType &block_i, const MatrixType &block_j, OtherType scale=1)
Short-hand for the "additive overwrite" version of SetBlocks.
Definition CSysMatrix.hpp:1059
void Initialize(unsigned long npoint, unsigned long npointdomain, unsigned short nvar, unsigned short neqn, bool EdgeConnect, CGeometry *geometry, const CConfig *config, bool needTranspPtr=false, bool allow_quant=false, std::optional< unsigned short > override_prec=std::nullopt)
Initializes the sparse matrix.
Definition CSysMatrix.cpp:184
void SetBlock(unsigned long block_i, unsigned long block_j, const OtherType *const *val_block, std::decay_t< OtherType > alpha=1.0)
Definition CSysMatrix.hpp:844
void ComputeLU_SGSPreconditioner(const CSysVector< ScalarType > &vec, CSysVector< ScalarType > &prod, CGeometry *geometry, const CConfig *config) const
Multiply CSysVector by the preconditioner.
Definition CSysMatrix.cpp:1324
void QuantizeDiagonalBlocks()
Compresses off-diagonal blocks into quantized form for use with USE_QUANTIZATION.
Definition CSysMatrix.cpp:779
CBlockView< const ScalarType > GetBlockView(unsigned long block_i, unsigned long block_j) const
Read-only view of block (block_i, block_j). In Q_LU_SGS mode values are decoded on access inside CBlo...
Definition CSysMatrix.hpp:785
void ComputeJacobiPreconditioner(const CSysVector< ScalarType > &vec, CSysVector< ScalarType > &prod, CGeometry *geometry, const CConfig *config) const
Multiply CSysVector by the preconditioner.
Definition CSysMatrix.cpp:1033
Class for holding and manipulating vectors needed by linear solvers.
Definition CSysVector.hpp:195
A simple SIMD type relying on implicit vectorization, i.e. done by the compiler, explicitly vectorize...
Definition vectorization.hpp:89
#define SU2_CUDA_HOST_DEVICE
Definition code_config.hpp:57
uint32_t su2uint
Definition code_config.hpp:121
#define FORCEINLINE
Definition code_config.hpp:44
passivedouble GetValue(const su2double &data)
Get the (primitive) value of the datatype (needs to be specialized for active types).
Definition datatype_structure.hpp:113
#define SU2_OMP_SIMD
Definition omp_structure.hpp:125
#define SU2_OMP_SIMD_IF_NOT_AD
Definition omp_structure.hpp:129
MPI_QUANTITIES
Vertex-based quantities exchanged in MPI point-to-point communications.
Definition option_structure.hpp:2873
@ SOLUTION_MATRIX
Matrix solution communication.
View of one matrix block, const-correct via the ScalarType template parameter. CBlockView<const Scala...
Definition CSysMatrix.hpp:208
QuantType * qv
Quantized values (row-major); non-null iff quantized.
Definition CSysMatrix.hpp:214
unsigned long nVar
Definition CSysMatrix.hpp:215
std::conditional_t< std::is_const_v< ScalarType >, const int8_t, int8_t > QuantType
Definition CSysMatrix.hpp:209
std::remove_const_t< ScalarType > operator()(unsigned long i, unsigned long j) const
Return entry (row i, col j), decoding quantization if necessary.
Definition CSysMatrix.hpp:221
QuantScaleType * qs
Per-row biased binary exponent; non-null iff quantized.
Definition CSysMatrix.hpp:213
ScalarType * ptr
Full-precision block; non-null iff not quantized.
Definition CSysMatrix.hpp:212
std::conditional_t< std::is_const_v< ScalarType >, const uint8_t, uint8_t > QuantScaleType
Definition CSysMatrix.hpp:210
void apply(const F &f) const
Write the block from callable f(i,j). Overwrite=true overwrites (or quantizes for Q_LU_SGS off-diagon...
Definition CSysMatrix.hpp:235
Helper to communicate distributed vectors.
Definition CSysMatrix.hpp:88
static void Complete(CSysVector< T > &x, CGeometry *geometry, const CConfig *config, MPI_QUANTITIES commType=MPI_QUANTITIES::SOLUTION_MATRIX)
Routine to complete the set of non-blocking communications launched by Initiate() and unpacking of th...
Definition CSysMatrix.cpp:663
static void Initiate(const CSysVector< T > &x, CGeometry *geometry, const CConfig *config, MPI_QUANTITIES commType=MPI_QUANTITIES::SOLUTION_MATRIX)
Routine to load a vector quantity into the data structures for MPI point-to-point communication and t...
Definition CSysMatrix.cpp:547