MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
cudss.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "cudss.hpp"
14#include <string>
15
16#ifdef MFEM_USE_CUDSS
17
18#if CUDSS_VERSION >= 800
19#ifdef MFEM_USE_SINGLE
20#define CUDSS_REAL_T CUDSS_R_32F
21#else
22#define CUDSS_REAL_T CUDSS_R_64F
23#endif
24#define CUDSS_INT_T CUDSS_R_32I
25#else
26#ifdef MFEM_USE_SINGLE
27#define CUDSS_REAL_T CUDA_R_32F
28#else
29#define CUDSS_REAL_T CUDA_R_64F
30#endif
31#define CUDSS_INT_T CUDA_R_32I
32#endif
33
34// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
35// type 'cudssStatus_t'. This macro evaluates 'x' and raises an error if the
36// result is not CUDSS_STATUS_SUCCESS.
37#define MFEM_CUDSS_CHECK(x) \
38 do { \
39 cudssStatus_t mfem_err_internal_var_name = (x); \
40 if (mfem_err_internal_var_name != CUDSS_STATUS_SUCCESS) { \
41 ::mfem::mfem_cudss_error(mfem_err_internal_var_name, #x, \
42 _MFEM_FUNC_NAME, __FILE__, __LINE__); \
43 } \
44 } while (0)
45
46namespace mfem
47{
48// Function used by the macro MFEM_CUDSS_CHECK.
49void mfem_cudss_error(cudssStatus_t status, const char *expr, const char *func,
50 const char *file, int line)
51{
52 mfem::err << "\n\nCUDSS error: (" << expr << ") failed with error:\n --> "
53 << "CUDSS call ended unsuccessfully"
54 << " [code: " << static_cast<int>(status) << ']'
55 << "\n ... in function: " << func << "\n ... in file: " << file
56 << ':' << line << '\n';
57 mfem_error();
58}
59
60CuDSSSolver::CuDSSSolver() { InitCuDSS(); }
61
62#ifdef MFEM_USE_MPI
63CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
64{
65 InitCuDSS();
66
67 // NOTE: Set the communication layer to NULL so that cuDSS picks it
68 // from the environment variable "CUDSS_COMM_LIB"
69 const char* comm_lib = GetEnv("CUDSS_COMM_LIB");
70#ifdef MFEM_CUDSS_COMM_LIB
71 if (comm_lib == nullptr)
72 {
73 comm_lib = MFEM_CUDSS_COMM_LIB;
74 }
75#endif
76 MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
77
78#if CUDSS_VERSION >= 800
79 MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM_HOST,
80 &mpi_comm, sizeof(MPI_Comm *)));
81#else
82 MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
83 &mpi_comm, sizeof(MPI_Comm *)));
84#endif
85}
86#endif // MFEM_USE_MPI
87
89{
90 // Sync the stream to make sure any pending asynchronous operations have
91 // completed.
92 MFEM_STREAM_SYNC;
93 // Destroy the system Matrix, RHS vector and solution vector
94 if (Ac)
95 {
96 MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
97 MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
98 MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
99 }
100
101 // Destroy the cuDSS handle, solver config and solver data
102 MFEM_CUDSS_CHECK(cudssDataDestroy(handle, solverData));
103 MFEM_CUDSS_CHECK(cudssConfigDestroy(solverConfig));
104
105 MFEM_CUDSS_CHECK(cudssDestroy(handle));
106 handle = nullptr;
107
108
109 if (csr_offsets_d != NULL)
110 {
111 CuMemFree(csr_offsets_d);
112 }
113
114 if (csr_columns_d != NULL)
115 {
116 CuMemFree(csr_columns_d);
117 }
118
119 if (csr_values_d != NULL)
120 {
121 CuMemFree(csr_values_d);
122 }
123}
124
125void CuDSSSolver::InitCuDSS()
126{
127 // Create the cuDSS handle
128 MFEM_CUDSS_CHECK(cudssCreate(&handle));
129
130 // Set CuDSS to use MFEM's default stream of 0.
131 MFEM_CUDSS_CHECK(cudssSetStream(handle, 0));
132
133#ifdef MFEM_USE_OPENMP
134 // NOTE: Set the threading layer library name to NULL so that cuDSS picks
135 // it from the environment variable "CUDSS_THREADING_LIB"
136 const char* threading_lib = GetEnv("CUDSS_THREADING_LIB");
137#ifdef MFEM_CUDSS_THREADING_LIB
138 if (threading_lib == nullptr)
139 {
140 threading_lib = MFEM_CUDSS_THREADING_LIB;
141 }
142#endif
143 MFEM_CUDSS_CHECK(cudssSetThreadingLayer(handle, threading_lib));
144#endif // MFEM_USE_OPENMP
145
146 // Create the solver configuration and data objects
147 MFEM_CUDSS_CHECK(cudssConfigCreate(&solverConfig));
148 MFEM_CUDSS_CHECK(cudssDataCreate(handle, &solverData));
149}
150
152{
153 switch (mtype_)
154 {
156 mat_type = CUDSS_MTYPE_SYMMETRIC;
157 break;
159 mat_type = CUDSS_MTYPE_SPD;
160 break;
161 default:
162 mat_type = CUDSS_MTYPE_GENERAL;
163 break;
164 }
165}
166
168{
169 // If the MatType is NONSYMMETRIC, the matrix view type must be FULL.
170 if (mat_type == CUDSS_MTYPE_GENERAL)
171 {
172 mview = CUDSS_MVIEW_FULL;
173 return;
174 }
175
176 // If the matrix is symmetric, the following view type will be optional.
177 switch (mvtype_)
178 {
180 mview = CUDSS_MVIEW_LOWER;
181 break;
183 mview = CUDSS_MVIEW_UPPER;
184 break;
185 default:
186 mview = CUDSS_MVIEW_FULL;
187 break;
188 }
189}
190
192{
193 MFEM_VERIFY(Ac == nullptr,
194 "Set reordering reuse before setting the operator!");
195 reorder_reuse = reuse;
196}
197
198#ifdef MFEM_USE_MPI
199void CuDSSSolver::SetMatrix(const HypreParMatrix &op)
200{
201 bool cuDSSObjectInitialized = (Ac != nullptr);
202
203 hypre_ParCSRMatrix *parcsr_op = op;
204 op.HypreRead();
205 hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
206 op.HypreRead();
207#if MFEM_HYPRE_VERSION >= 21600
208 hypre_CSRMatrixBigJtoJ(csr_op);
209#endif
210
211 // Parameters of the Operator
212 n_loc = height; // Equal to the csr_op->num_rows
213 n_global = internal::to_int(parcsr_op->global_num_rows);
214 row_start = parcsr_op->first_row_index;
215 row_end = row_start + n_loc - 1;
216 MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
217 (reorder_reuse && (nnz == csr_op->num_nonzeros)),
218 "Inconsistent new matrix pattern!");
219 nnz = csr_op->num_nonzeros;
220
221 SetMatrixCuDSS(csr_op->i, csr_op->j, csr_op->data);
222 hypre_CSRMatrixDestroy(csr_op);
223}
224#endif // MFEM_USE_MPI
225
226void CuDSSSolver::SetMatrix(const SparseMatrix &op)
227{
228 bool cuDSSObjectInitialized = (Ac != nullptr);
229
230 // Parameters of the Operator
231 MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
232 (reorder_reuse && (nnz == op.NumNonZeroElems())),
233 "Inconsistent new matrix pattern!");
234
235 SparseMatrix *A = const_cast<SparseMatrix *>(&op);
236
237 nnz = A->NumNonZeroElems();
238 n_global = height; // Equal to the height in serial
239 n_loc = height; // Equal to the height in serial
240
241 int *csr_offsets = const_cast<int *>(A->ReadI());
242 int *csr_columns = const_cast<int *>(A->ReadJ());
243 real_t *csr_values = const_cast<real_t *>(A->ReadData());
244
245 SetMatrixCuDSS(csr_offsets, csr_columns, csr_values);
246}
247
248void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
249 real_t *csr_values)
250{
251 bool cuDSSObjectInitialized = (Ac != nullptr);
252 // Initial the cudssMatrix objects
253 if (!cuDSSObjectInitialized)
254 {
255 // Set the cudssMatrix object of csr operator
256 Ac = std::make_unique<cudssMatrix_t>();
257 // Create empty RHS and solution vectors
258 SetNumRHS(1);
259 }
260
261 if (cuDSSObjectInitialized && !reorder_reuse)
262 {
263 MFEM_STREAM_SYNC;
264 MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
265 }
266
267 // Allocate device memory for csr values. Unless reuse is specified, the
268 // nnz may be different, so we will free and reallocate.
269 if (csr_values_d == NULL || !reorder_reuse)
270 {
271 if (csr_values_d != NULL) { CuMemFree(csr_values_d); }
272 CuMemAlloc(&csr_values_d, nnz * sizeof(real_t));
273 }
274 CuMemcpyDtoD(csr_values_d, csr_values, nnz * sizeof(real_t));
275
276 // We copy and store the I and J arrays, since the CuDSS matrix object
277 // technically needs these to be valid, so we protect against the caller
278 // destroying the original matrix.
279 if (!cuDSSObjectInitialized || !reorder_reuse)
280 {
281 if (csr_offsets_d != NULL) { CuMemFree(csr_offsets_d); }
282 CuMemAlloc(&csr_offsets_d, (n_loc + 1) * sizeof(int));
283 if (csr_columns_d != NULL) { CuMemFree(csr_columns_d); }
284 CuMemAlloc(&csr_columns_d, nnz * sizeof(int));
285 CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
286 CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
287 }
288
289 // New cuDSS CSR matrix object and analysis or reuse the one from a previous
290 // matrix
291 if (!cuDSSObjectInitialized || !reorder_reuse)
292 {
293 if (reorder_reuse) // !cuDSSObjectInitialized && reorder_reuse
294 {
295#if CUDSS_VERSION >= 800
296 MFEM_CUDSS_CHECK(
297 cudssMatrixCreateCsr(
298 Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
299 csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
300 mat_type, mview, CUDSS_BASE_ZERO));
301#else
302 MFEM_CUDSS_CHECK(
303 cudssMatrixCreateCsr(
304 Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
305 csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
306 mat_type, mview, CUDSS_BASE_ZERO));
307#endif
308 }
309 else // !reorder_reuse
310 {
311#if CUDSS_VERSION >= 800
312 MFEM_CUDSS_CHECK(
313 cudssMatrixCreateCsr(
314 Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
315 csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
316 mat_type, mview, CUDSS_BASE_ZERO));
317#else
318 MFEM_CUDSS_CHECK(
319 cudssMatrixCreateCsr(
320 Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
321 csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
322 mat_type, mview, CUDSS_BASE_ZERO));
323#endif
324 }
325#ifdef MFEM_USE_MPI
326 if (Mpi::IsInitialized())
327 {
328 MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(*Ac, row_start, row_end));
329 }
330#endif
331 // Analysis
332 MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_ANALYSIS, solverConfig,
333 solverData, *Ac, yc, xc));
334 }
335 else // cuDSSObjectInitialized && reorder_reuse
336 {
337 // NOTE: When reusing analysis result, we only update the Data array,
338 // without changing the I and J arrays.
339 MFEM_CUDSS_CHECK(cudssMatrixSetValues(*Ac, csr_values_d));
340 }
341
342 // Factorization
343 MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_FACTORIZATION, solverConfig,
344 solverData, *Ac, yc, xc));
345
346 // In serial, the factorization can execute asynchronously.
347 MFEM_STREAM_SYNC;
348}
349
351{
352 bool cuDSSObjectInitialized = (Ac != nullptr);
353 MFEM_VERIFY(
354 !cuDSSObjectInitialized || (height == op.Height() && width == op.Width()),
355 "Inconsistent new matrix size!");
356 height = op.Height();
357 width = op.Width();
358 if (const SparseMatrix *A = dynamic_cast<const SparseMatrix *>(&op))
359 {
360 SetMatrix(*A);
361 }
362#ifdef MFEM_USE_MPI
363 else if (const HypreParMatrix *A =
364 dynamic_cast<const HypreParMatrix *>(&op))
365 {
366 SetMatrix(*A);
367 }
368#endif // MFEM_USE_MPI
369 else
370 {
371 MFEM_ABORT("Unsupported Operator Type \n");
372 }
373}
374
375void CuDSSSolver::SetNumRHS(int nrhs_) const
376{
377 if (nrhs != nrhs_)
378 {
379 if (nrhs > 0)
380 {
381 // Destroy the previous RHS vector and solution vector
382 MFEM_STREAM_SYNC;
383 MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
384 MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
385 }
386 // Create empty RHS and solution vectors
387 MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
388 CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
389
390 MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
391 CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
392
393#ifdef MFEM_USE_MPI
394 MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
395 MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(yc, row_start, row_end));
396#endif // MFEM_USE_MPI
397 }
398 nrhs = nrhs_;
399}
400
401void CuDSSSolver::Mult(const Vector &x, Vector &y) const
402{
404 Array<Vector *> Y(1);
405 X[0] = &x;
406 Y[0] = &y;
407 ArrayMult(X, Y);
408}
409
411 Array<Vector *> &Y) const
412{
413 SetNumRHS(X.Size());
414
415 Vector RHS, SOL;
416
417 if (nrhs == 1)
418 {
419 RHS.MakeRef(*(const_cast<Vector *>(X[0])), 0, X[0]->Size());
420 SOL.MakeRef(*Y[0], 0, Y[0]->Size());
421 }
422 else
423 {
424 // NOTE: RHS must have **global** num_rows and nrhs columns
425 RHS.SetSize(nrhs * n_global, *X[0]);
426 for (int i = 0; i < nrhs; i++)
427 {
428 Vector s(RHS, i * n_global, n_loc);
429 s = *X[i];
430 }
431
432 // NOTE: SOL must have **global** num_rows and nrhs columns
433 SOL.SetSize(nrhs * n_global, *Y[0]);
434 }
435
436 MFEM_CUDSS_CHECK(cudssMatrixSetValues(xc, const_cast<real_t *>(RHS.Read())));
437 MFEM_CUDSS_CHECK(cudssMatrixSetValues(yc, SOL.Write()));
438
439 // Solve
440 MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_SOLVE, solverConfig,
441 solverData, *Ac, yc, xc));
442
443 if (nrhs == 1)
444 {
445 SOL.SyncAliasMemory(*Y[0]);
446 }
447
448 if (nrhs > 1)
449 {
450 // Get solution for each right-hand side
451 for (int i = 0; i < nrhs; i++)
452 {
453 Vector s(SOL, i * n_global, n_loc);
454 *Y[i] = s;
455 }
456 }
457}
458
459} // namespace mfem
460#endif // MFEM_USE_CUDSS
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void Mult(const Vector &x, Vector &y) const override
Solve .
Definition cudss.cpp:401
void SetOperator(const Operator &op) override
Set/update the solver for the given operator.
Definition cudss.cpp:350
MatType
Specify the type of matrix we are applying the solver to.
Definition cudss.hpp:42
@ SYMMETRIC_INDEFINITE
CUDSS_MTYPE_SYMMETRIC: Real symmetric matrix.
Definition cudss.hpp:46
@ SYMMETRIC_POSITIVE_DEFINITE
CUDSS_MTYPE_SPD: Symmetric positive-definite matrix.
Definition cudss.hpp:48
void SetReorderingReuse(bool reuse)
Set the flag controlling reuse of the symbolic factorization for multiple operators.
Definition cudss.cpp:191
MatViewType
Specify the view type of matrix we are applying the solver to.
Definition cudss.hpp:53
@ UPPER
CUDSS_MVIEW_UPPER: Upper-triangular matrix (including the diagonal).
Definition cudss.hpp:59
@ LOWER
CUDSS_MVIEW_LOWER: Lower-triangular matrix (including the diagonal).
Definition cudss.hpp:57
CuDSSSolver()
Constructor.
Definition cudss.cpp:60
void SetMatrixViewType(MatViewType mvtype)
Set the matrix view type.
Definition cudss.cpp:167
void SetMatrixSymType(MatType mtype_)
Set the matrix type.
Definition cudss.cpp:151
void ArrayMult(const Array< const Vector * > &X, Array< Vector * > &Y) const override
Solve .
Definition cudss.cpp:410
Wrapper for hypre's ParCSR matrix class.
Definition hypre.hpp:419
void HypreRead() const
Update the internal hypre_ParCSRMatrix object, A, to be in hypre memory space.
Definition hypre.hpp:941
static bool IsInitialized()
Return true if MPI has been initialized.
Abstract operator.
Definition operator.hpp:27
int width
Dimension of the input / number of columns in the matrix.
Definition operator.hpp:30
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
int height
Dimension of the output / number of rows in the matrix.
Definition operator.hpp:29
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition operator.hpp:74
Data type sparse matrix.
Definition sparsemat.hpp:51
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
void SyncAliasMemory(const Vector &v) const
Update the alias memory location of the vector to match v.
Definition vector.hpp:275
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition vector.hpp:709
mfem::real_t real_t
void * CuMemAlloc(void **dptr, size_t bytes)
Allocates device memory and returns destination ptr.
Definition cuda.cpp:34
void * CuMemFree(void *dptr)
Frees device memory and returns destination ptr.
Definition cuda.cpp:79
void mfem_error(const char *msg)
Definition error.cpp:154
const char * GetEnv(const char *name)
Wrapper for std::getenv.
Definition globals.cpp:79
OutStream err(std::cerr)
Global stream used by the library for standard error output. Initially it uses the same std::streambu...
Definition globals.hpp:71
float real_t
Definition config.hpp:46
void * CuMemcpyDtoD(void *dst, const void *src, size_t bytes)
Copies memory from Device to Device.
Definition cuda.cpp:132
void mfem_cudss_error(cudssStatus_t status, const char *expr, const char *func, const char *file, int line)
Definition cudss.cpp:49