NeoN
A framework for CFD software
Loading...
Searching...
No Matches
linearSystem.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2023 - 2026 NeoN authors
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
15#ifdef NF_WITH_MPI_SUPPORT
17#endif
18
19#include <string>
20#include <algorithm>
21#include <numeric>
22#include <vector>
23
24namespace NeoN::la
25{
26
34template<typename RHSValueType, typename MatrixViewType>
36{
37 LinearSystemView() = default;
38 ~LinearSystemView() = default;
39
41 MatrixViewType matrixView,
42 View<RHSValueType> rhsView,
43 MatrixViewType boundaryMatrixView,
44 View<RHSValueType> boundaryRhsView
45 )
46 : matrix(matrixView), rhs(rhsView), boundaryMatrix(boundaryMatrixView),
47 boundaryRhs(boundaryRhsView) {};
48
49 MatrixViewType matrix;
51
52 MatrixViewType boundaryMatrix;
54};
55
72template<
73 typename MatrixValueType,
74 typename RHSValueType = MatrixValueType,
75 typename SystemMatrixType = CSRMatrix<MatrixValueType, localIdx>,
76 typename BoundaryMatrixType = COOMatrix<MatrixValueType, localIdx>>
79 LinearSystem<MatrixValueType, RHSValueType, SystemMatrixType, BoundaryMatrixType>>
80{
81
82 void validate()
83 {
84 NF_ASSERT(matrix_.exec() == rhs_.exec(), "Executors are not the same");
85 NF_ASSERT(matrix_.nRows() == rhs_.size(), "Matrix and RHS size mismatch");
87 meshIteratorContext_ != nullptr,
88 "Mesh iterator context must be set before validating the linear system"
89 );
91 meshIteratorContext_->get() != nullptr,
92 "Mesh iterator strategy must be set before validating the linear system"
93 );
94 // NF_ASSERT(
95 // boundaryMatrix_.nRows() == boundaryRhs_.size(), "BMatrix.nRows() !=
96 // boundaryRHS.size()"
97 // );
98 }
99
100
101public:
102
103 using LinearSystemIndexType = typename SystemMatrixType::MatrixSparsityType::SparsityIndexType;
104
106 const SystemMatrixType& matrix,
108 const BoundaryMatrixType& offDiagonalMatrix,
109 const BoundaryMatrixType& boundaryMatrix,
111 std::shared_ptr<MeshIterationStrategy> strategy = std::make_shared<FaceBasedIterator>()
112 )
113 : matrix_(matrix), rhs_(rhs), boundaryMatrix_(boundaryMatrix),
114 offDiagonalMatrix_(offDiagonalMatrix), boundaryRhs_(boundaryRhs),
115 meshIteratorContext_(std::make_shared<MeshIteratorContext>())
116 {
117 meshIteratorContext_->setStrategy(strategy);
118 validate();
119 }
120
122 const SystemMatrixType& matrix,
124 const BoundaryMatrixType& boundaryMatrix,
126 std::shared_ptr<MeshIterationStrategy> strategy = std::make_shared<FaceBasedIterator>()
127 )
128 : LinearSystem(
129 matrix, rhs, emptyMatrix(matrix.exec()), boundaryMatrix, boundaryRhs, strategy
130 )
131 {}
132
134 : matrix_(ls.matrix_), rhs_(ls.rhs_), boundaryMatrix_(ls.boundaryMatrix_),
135 offDiagonalMatrix_(ls.offDiagonalMatrix_), boundaryRhs_(ls.boundaryRhs_),
136 // TODO move to a different location since this seems to be unrelated to linearSystem
137 faceFluxCorrection_(ls.faceFluxCorrection_),
138 keepFaceFluxCorrection_(ls.keepFaceFluxCorrection_),
139 diagCmpt_(ls.diagCmpt_ ? std::make_shared<Vector<RHSValueType>>(*ls.diagCmpt_) : nullptr),
140 meshIteratorContext_(ls.meshIteratorContext_)
141#ifdef NF_WITH_MPI_SUPPORT
142 ,
143 commPattern_(ls.commPattern_)
144#endif
145 {
146 validate();
147 }
148
149 ~LinearSystem() = default;
150
151 [[nodiscard]] SystemMatrixType& matrix() { return matrix_; }
152
153 [[nodiscard]] const SystemMatrixType& matrix() const { return matrix_; }
154
155 [[nodiscard]] BoundaryMatrixType& offDiagonalMatrix() { return offDiagonalMatrix_; }
156
157 [[nodiscard]] const BoundaryMatrixType& offDiagonalMatrix() const { return offDiagonalMatrix_; }
158
159 [[nodiscard]] BoundaryMatrixType& boundaryMatrix() { return boundaryMatrix_; }
160
161 [[nodiscard]] const BoundaryMatrixType& boundaryMatrix() const { return boundaryMatrix_; }
162
163 [[nodiscard]] Vector<RHSValueType>& rhs() { return rhs_; }
164
165 [[nodiscard]] const Vector<RHSValueType>& rhs() const { return rhs_; }
166
167 [[nodiscard]] Vector<RHSValueType>& boundaryRhs() { return boundaryRhs_; }
168
169 [[nodiscard]] const Vector<RHSValueType>& boundaryRhs() const { return boundaryRhs_; }
170
171 // Optional per-internal-face deferred flux correction — the OpenFOAM
172 // fvMatrix::faceFluxCorrectionPtr_ analogue. Populated by a corrected/limitedCorrected
173 // Laplacian assembly with the SAME per-face correction it deferred to the RHS, so the flux
174 // reconstruction phi = phiHbyA - pEqn.flux() (NeoFOAM updateFaceVelocity) can add it back and
175 // close div(phi) on non-orthogonal meshes. Null when no corrected Laplacian contributed
176 // (orthogonal / uncorrected schemes); never cleared by reset() since each corrected assembly
177 // overwrites every internal-face entry.
178 [[nodiscard]] std::shared_ptr<Vector<RHSValueType>>& faceFluxCorrection()
179 {
180 return faceFluxCorrection_;
181 }
182
183 [[nodiscard]] const std::shared_ptr<Vector<RHSValueType>>& faceFluxCorrection() const
184 {
185 return faceFluxCorrection_;
186 }
187
188 // Whether Laplacian assembly should populate faceFluxCorrection() for this system. Off by
189 // default; only the consumer that reconstructs the flux (the scalar pressure equation, via
190 // NeoFOAM updateFaceVelocity) opts in, so momentum / turbulence systems — which never
191 // reconstruct flux — allocate nothing for the correction.
192 [[nodiscard]] bool keepFaceFluxCorrection() const { return keepFaceFluxCorrection_; }
193
194 void keepFaceFluxCorrection(bool keep) { keepFaceFluxCorrection_ = keep; }
195
198 [[nodiscard]] const std::shared_ptr<Vector<RHSValueType>>& diagCmpt() const
199 {
200 return diagCmpt_;
201 }
202
207 {
208 if (!diagCmpt_)
209 {
210 diagCmpt_ =
211 std::make_shared<Vector<RHSValueType>>(exec(), rhs_.size(), zero<RHSValueType>());
212 }
213 return *diagCmpt_;
214 }
215
216
219 {
221 matrix_.copyToExecutor(exec),
222 rhs_.copyToExecutor(exec),
223 offDiagonalMatrix_.copyToExecutor(exec),
224 boundaryMatrix_.copyToExecutor(exec),
225 boundaryRhs_.copyToExecutor(exec)
226 };
227 if (diagCmpt_)
228 {
229 ls.diagCmpt_ = std::make_shared<Vector<RHSValueType>>(diagCmpt_->copyToExecutor(exec));
230 }
231#ifdef NF_WITH_MPI_SUPPORT
232 ls.commPattern_ = commPattern_;
233#endif
234 return ls;
235 }
236
237 void reset()
238 {
239 fill(matrix_.values(), zero<MatrixValueType>());
240 fill(rhs_, zero<RHSValueType>());
241 fill(boundaryMatrix_.values(), zero<MatrixValueType>());
242 fill(boundaryRhs_, zero<RHSValueType>());
243 fill(offDiagonalMatrix_.values(), zero<MatrixValueType>());
244 if (diagCmpt_) fill(*diagCmpt_, zero<RHSValueType>());
245 }
246
247 [[nodiscard]] LinearSystemView<
248 RHSValueType,
250 MatrixValueType,
252 view() && = delete;
253
254 [[nodiscard]] LinearSystemView<
255 RHSValueType,
257 MatrixValueType,
259 view() const&& = delete;
260
261 [[nodiscard]] LinearSystemView<
262 RHSValueType,
265 {
266 return {matrix_.view(), rhs_.view(), boundaryMatrix_.view(), boundaryRhs_.view()};
267 }
268
269 std::shared_ptr<const FaceToMatrixAddress> faceToMatrixAddress() const
270 {
271 return matrix_.faceToMatrixAddress();
272 }
273
274#ifdef NF_WITH_MPI_SUPPORT
275 [[nodiscard]] const CommunicationPattern& commPattern() const { return commPattern_; }
276 [[nodiscard]] CommunicationPattern& commPattern() { return commPattern_; }
277#endif
278
279 [[nodiscard]] LinearSystemView<
280 const RHSValueType,
281 const MatrixView<MatrixValueType, SparsityView<const LinearSystemIndexType>>>
282 view() const&
283 {
284 return {matrix_.view(), rhs_.view(), boundaryMatrix_.view(), boundaryRhs_.view()};
285 }
286
287 std::shared_ptr<MeshIteratorContext> getMeshIterator()
288 {
289 if (meshIteratorContext_ == nullptr)
290 {
291 NF_ERROR_EXIT(" meshIteratorContext_ == nullptr");
292 }
293 return meshIteratorContext_;
294 }
295
296 const Executor& exec() const { return matrix_.exec(); }
297
298private:
299
300 static BoundaryMatrixType emptyMatrix(const Executor& exec)
301 {
302 using IndexType = typename BoundaryMatrixType::MatrixSparsityType::SparsityIndexType;
303 auto sp = std::make_shared<const typename BoundaryMatrixType::MatrixSparsityType>(
305 );
306 return BoundaryMatrixType(Vector<MatrixValueType>(exec, 0, zero<MatrixValueType>()), sp);
307 }
308
309 // internal values
310 SystemMatrixType matrix_;
311
312 Vector<RHSValueType> rhs_;
313
314 // boundary values
315 BoundaryMatrixType boundaryMatrix_;
316
317 // store values on boundaries that are non local
318 // eg on processor boundaries
319 BoundaryMatrixType offDiagonalMatrix_;
320
321 Vector<RHSValueType> boundaryRhs_;
322
323 // see faceFluxCorrection(). shared_ptr so the
324 // (existing) member-wise copy ctor and the default-constructed empty state stay cheap.
325 std::shared_ptr<Vector<RHSValueType>> faceFluxCorrection_ = nullptr;
326
327 // Opt-in toggle for faceFluxCorrection_ storage; see keepFaceFluxCorrection().
328 bool keepFaceFluxCorrection_ = false;
329
330 Dictionary auxiliaryCoefficients_;
331
332 // Optional per-component diagonal correction for direction-dependent (transform) boundary
333 // conditions in implicit mode (slip/symmetry). One RHSValueType (e.g. Vec3) per cell; component
334 // c holds the diagonal contribution γ|S|·Δ·|n_c| applied for solve-component c on cells
335 // adjacent to an implicit transform patch. Lazily allocated by ensureDiagCmpt(); stays nullptr
336 // (and the shared scalar matrix / multi-RHS fast path is used) whenever no such patch exists.
337 std::shared_ptr<Vector<RHSValueType>> diagCmpt_ = nullptr;
338
339 std::shared_ptr<MeshIteratorContext> meshIteratorContext_ = nullptr;
340
341#ifdef NF_WITH_MPI_SUPPORT
342 CommunicationPattern commPattern_;
343#endif
344};
345
346/*@brief helper function that creates a zero initialised linear system based on a given mesh
347 */
348template<
349 typename ValueType,
350 typename RHSValueType = ValueType,
351 typename SystemMatrixType = CSRMatrix<ValueType, localIdx>,
352 typename BoundaryMatrixType = COOMatrix<ValueType, localIdx>>
354 const UnstructuredMesh& mesh,
355 std::shared_ptr<MeshIterationStrategy> strategy = std::make_shared<FaceBasedIterator>()
356)
357{
358 // Consume the per-mesh cached, immutable topology bundle (CSR system sparsity +
359 // FaceToMatrixAddress + boundary sparsity). These arrays depend only on mesh topology, so they
360 // are shared by every LinearSystem built on this mesh; only the per-system value/RHS vectors
361 // below are allocated fresh.
362 auto bundle = readOrCreateSparsityBundle<
363 typename SystemMatrixType::MatrixSparsityType,
364 typename BoundaryMatrixType::MatrixSparsityType>(mesh);
365 const auto& sp = bundle.systemSparsity;
366 const auto& mi = bundle.faceToMatrixAddress;
367 const auto& bSp = bundle.boundarySparsity;
368 const auto exec = sp->exec();
369 const auto nCells = static_cast<localIdx>(mesh.nCells());
370 const auto nProcFaces = static_cast<localIdx>(mesh.nProcBoundaryFaces());
371 using IndexType = typename BoundaryMatrixType::MatrixSparsityType::SparsityIndexType;
372
373 // Off-diagonal / proc-face sparsity stays per-system (comm-pattern-derived); not shared in v1.
374 Vector<IndexType> offDiagColIdxs(exec, nProcFaces, 0);
375 Vector<IndexType> offDiagRowIdxs(exec, nProcFaces, 0);
376
377#ifdef NF_WITH_MPI_SUPPORT
378 auto commPattern = computeCommunicationPattern(mesh);
379 if (nProcFaces > 0)
380 {
381 const localIdx nBoundaryFaces = static_cast<localIdx>(mesh.nBoundaryFaces());
382 const auto faceOwnersH = mesh.boundaryMesh().faceOwners().copyToHost();
383 const auto faceOwnersV = faceOwnersH.view();
384 Vector<IndexType> rowH(SerialExecutor {}, nProcFaces, 0);
385 Vector<IndexType> colH(SerialExecutor {}, nProcFaces, 0);
386 auto rowHV = rowH.view();
387 auto colHV = colH.view();
388 for (localIdx i = 0; i < nProcFaces; ++i)
389 {
390 // Store the local row index directly. The global offset used to be added here and
391 // subtracted again on the Ginkgo side; keeping the rows local avoids that round-trip.
392 // The column index stays global (it identifies a remote cell) and is consumed by
393 // Ginkgo's distributed index_map.
394 rowHV[i] = faceOwnersV[nBoundaryFaces + i];
395 colHV[i] = static_cast<IndexType>(commPattern.recvIdx[static_cast<std::size_t>(i)]);
396 }
397 // offDiagRowSortPerm[j] = proc-face index whose row/col belongs at sorted position j.
398 // Already computed (and stored) in BoundaryMesh; reuse it here to avoid re-sorting.
399 const auto& offDiagRowSortPerm = mesh.boundaryMesh().getRowSortPerm();
400 {
401 std::vector<IndexType> sortedRow(static_cast<std::size_t>(nProcFaces));
402 std::vector<IndexType> sortedCol(static_cast<std::size_t>(nProcFaces));
403 for (localIdx j = 0; j < nProcFaces; ++j)
404 {
405 auto src = offDiagRowSortPerm[static_cast<std::size_t>(j)];
406 sortedRow[static_cast<std::size_t>(j)] = rowHV[src];
407 sortedCol[static_cast<std::size_t>(j)] = colHV[src];
408 }
409 offDiagRowIdxs = Vector<IndexType>(exec, std::move(sortedRow));
410 offDiagColIdxs = Vector<IndexType>(exec, std::move(sortedCol));
411 }
412 commPattern.offDiagRowSortPerm = std::move(offDiagRowSortPerm);
413 }
414#endif
415
416 auto offDiagSp = std::make_shared<const typename BoundaryMatrixType::MatrixSparsityType>(
417 std::move(offDiagColIdxs), std::move(offDiagRowIdxs), Dimensions {nCells, nCells}
418 );
419
421 SystemMatrixType(Vector<ValueType>(sp->exec(), sp->nnz(), zero<ValueType>()), sp, mi),
422 Vector<RHSValueType>(sp->exec(), sp->rows(), zero<RHSValueType>()),
423 BoundaryMatrixType(Vector<ValueType>(exec, nProcFaces, zero<ValueType>()), offDiagSp),
424 BoundaryMatrixType(Vector<ValueType>(bSp->exec(), bSp->nnz(), zero<ValueType>()), bSp),
425 Vector<RHSValueType>(bSp->exec(), bSp->nnz(), zero<RHSValueType>()),
426 strategy
427 };
428
429#ifdef NF_WITH_MPI_SUPPORT
430 ls.commPattern() = std::move(commPattern);
431#endif
432
433 return ls;
434}
435
442template<
443 typename MatrixValueType,
444 typename RHSValueType,
445 typename SystemMatrixType,
446 typename BoundaryMatrixType>
450 lsIn
451)
452{
453 auto ls =
455 auto lsView = ls.view();
456 auto& matrix = lsView.matrix;
457 auto& rhs = lsView.rhs;
458 auto& bMatrix = lsView.boundaryMatrix;
459 auto& bRhs = lsView.boundaryRhs;
460
461 const auto ma = ls.faceToMatrixAddress()->view(ls.matrix().sparsity()->rowOffs().view());
462
464 ls.exec(),
465 {0, bMatrix.values.size()},
466 NEON_LAMBDA(const localIdx facei) {
467 const auto celli = bMatrix.sparsity.rowOffs[facei]; // cell index stored in rowOffs
468 Kokkos::atomic_add(&matrix.values[ma.diagIdx(celli)], bMatrix.values[facei]);
469 Kokkos::atomic_add(&rhs[celli], bRhs[facei]);
470 },
471 "removeBoundaryContributions"
472 );
473
474 return ls;
475}
476
477} // namespace NeoN::la
const labelVector & faceOwners() const
Get the list of labels of owner cells of boundary faces.
const std::vector< localIdx > & getRowSortPerm() const
Reference executor for serial CPU execution.
MixinClass signaling copyTo is supported.
Definition copyTo.hpp:20
Represents an unstructured mesh in NeoN.
localIdx nProcBoundaryFaces() const
Get the number of processor-boundary faces (inter-rank faces).
localIdx nCells() const
Get the number of cells in the mesh.
localIdx nBoundaryFaces() const
Get the number of boundary faces in the mesh.
const BoundaryMesh & boundaryMesh() const
Get the boundary mesh.
A class to contain the data and executors for a field and define some basic operations.
Definition vector.hpp:27
View< ValueType > view() &&=delete
Vector< ValueType > copyToHost() const
Returns a copy of the field back to the host.
A class representing a linear system of equations.
BoundaryMatrixType & offDiagonalMatrix()
LinearSystem(const SystemMatrixType &matrix, const Vector< RHSValueType > &rhs, const BoundaryMatrixType &boundaryMatrix, const Vector< RHSValueType > &boundaryRhs, std::shared_ptr< MeshIterationStrategy > strategy=std::make_shared< FaceBasedIterator >())
std::shared_ptr< MeshIteratorContext > getMeshIterator()
LinearSystem< MatrixValueType, RHSValueType, SystemMatrixType, BoundaryMatrixType > copyToExecutor(Executor exec) const override
void keepFaceFluxCorrection(bool keep)
Vector< RHSValueType > & rhs()
typename SystemMatrixType::MatrixSparsityType::SparsityIndexType LinearSystemIndexType
std::shared_ptr< Vector< RHSValueType > > & faceFluxCorrection()
LinearSystem(const LinearSystem &ls)
BoundaryMatrixType & boundaryMatrix()
Vector< RHSValueType > & boundaryRhs()
LinearSystem(const SystemMatrixType &matrix, const Vector< RHSValueType > &rhs, const BoundaryMatrixType &offDiagonalMatrix, const BoundaryMatrixType &boundaryMatrix, const Vector< RHSValueType > &boundaryRhs, std::shared_ptr< MeshIterationStrategy > strategy=std::make_shared< FaceBasedIterator >())
const std::shared_ptr< Vector< RHSValueType > > & faceFluxCorrection() const
const BoundaryMatrixType & boundaryMatrix() const
Vector< RHSValueType > & ensureDiagCmpt()
Lazily allocate (zero-initialised, one RHSValueType per cell) and return the per-component diagonal-c...
bool keepFaceFluxCorrection() const
LinearSystemView< RHSValueType, MatrixView< MatrixValueType, SparsityView< typename SystemMatrixType::MatrixSparsityType::SparsityIndexType > > > view() const &&=delete
const BoundaryMatrixType & offDiagonalMatrix() const
std::shared_ptr< const FaceToMatrixAddress > faceToMatrixAddress() const
LinearSystemView< RHSValueType, MatrixView< MatrixValueType, SparsityView< typename SystemMatrixType::MatrixSparsityType::SparsityIndexType > > > view() &&=delete
const Executor & exec() const
const SystemMatrixType & matrix() const
LinearSystemView< const RHSValueType, const MatrixView< MatrixValueType, SparsityView< const LinearSystemIndexType > > > view() const &
const std::shared_ptr< Vector< RHSValueType > > & diagCmpt() const
Per-component diagonal correction for implicit transform BCs (slip/symmetry). nullptr when no implici...
const Vector< RHSValueType > & boundaryRhs() const
const Vector< RHSValueType > & rhs() const
SystemMatrixType & matrix()
Sparse matrix class with compact storage by row (CSR) format.
Definition matrix.hpp:71
Holds and exposes the active MeshIterationStrategy.
#define NF_ERROR_EXIT(message)
Macro for printing an error message and aborting the program.
Definition error.hpp:90
#define NF_ASSERT(condition, message)
Macro for asserting a condition and printing an error message if the condition is false.
Definition error.hpp:118
la::LinearSystem< MatrixValueType, RHSValueType, SystemMatrixType, BoundaryMatrixType > removeBoundaryContributions(const la::LinearSystem< MatrixValueType, RHSValueType, SystemMatrixType, BoundaryMatrixType > &lsIn)
for testing purposes, this function reverses boundary contributions previously applied to the matrix ...
LinearSystem< ValueType, RHSValueType, SystemMatrixType, BoundaryMatrixType > createEmptyLinearSystem(const UnstructuredMesh &mesh, std::shared_ptr< MeshIterationStrategy > strategy=std::make_shared< FaceBasedIterator >())
SharedSparsityBundle< SystemSparsityType, BoundarySparsityType > readOrCreateSparsityBundle(const UnstructuredMesh &mesh)
int32_t localIdx
Definition label.hpp:50
std::variant< SerialExecutor, CPUExecutor, GPUExecutor > Executor
Definition executor.hpp:20
void parallelFor(const ExecutorType &, std::pair< localIdx, localIdx > range, const Kernel &kernel, std::string name)
void fill(ContType< ValueType > &cont, const std::type_identity_t< ValueType > value, std::pair< localIdx, localIdx > range={0, 0})
Fill the field with a vector value using a specific executor.
#define NEON_LAMBDA
hold the number of rows and columns of a matrix
A view linear into a linear system's data.
View< RHSValueType > rhs
View< RHSValueType > boundaryRhs
LinearSystemView(MatrixViewType matrixView, View< RHSValueType > rhsView, MatrixViewType boundaryMatrixView, View< RHSValueType > boundaryRhsView)
A view struct to allow easy read/write on all executors.
Definition matrix.hpp:24
A view struct to allow easy read/write on all executors.