DDC 0.16.0
Loading...
Searching...
No Matches
splines_linear_problem_2x2_blocks.cpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#include <cassert>
6#include <cstddef>
7#include <memory>
8#include <utility>
9
10#include <ddc/ddc.hpp>
11
12#include <Kokkos_Core.hpp>
13
17
18namespace ddc::detail {
19
20template <class ExecSpace>
21struct SplinesLinearProblem2x2Blocks<ExecSpace>::Coo
22{
23 std::size_t m_nrows;
24 std::size_t m_ncols;
25 Kokkos::View<int*, Kokkos::LayoutRight, memory_space> m_rows_idx;
26 Kokkos::View<int*, Kokkos::LayoutRight, memory_space> m_cols_idx;
27 Kokkos::View<Real*, Kokkos::LayoutRight, memory_space> m_values;
28
29 Coo() : m_nrows(0), m_ncols(0) {}
30
31 Coo(std::size_t const nrows_,
32 std::size_t const ncols_,
33 Kokkos::View<int*, Kokkos::LayoutRight, memory_space> rows_idx_,
34 Kokkos::View<int*, Kokkos::LayoutRight, memory_space> cols_idx_,
35 Kokkos::View<Real*, Kokkos::LayoutRight, memory_space> values_)
36 : m_nrows(nrows_)
37 , m_ncols(ncols_)
38 , m_rows_idx(std::move(rows_idx_))
39 , m_cols_idx(std::move(cols_idx_))
40 , m_values(std::move(values_))
41 {
42 assert(m_rows_idx.extent(0) == m_cols_idx.extent(0));
43 assert(m_rows_idx.extent(0) == m_values.extent(0));
44 }
45
46 KOKKOS_FUNCTION std::size_t nnz() const
47 {
48 return m_values.extent(0);
49 }
50
51 KOKKOS_FUNCTION std::size_t nrows() const
52 {
53 return m_nrows;
54 }
55
56 KOKKOS_FUNCTION std::size_t ncols() const
57 {
58 return m_ncols;
59 }
60
61 KOKKOS_FUNCTION Kokkos::View<int*, Kokkos::LayoutRight, memory_space> rows_idx() const
62 {
63 return m_rows_idx;
64 }
65
66 KOKKOS_FUNCTION Kokkos::View<int*, Kokkos::LayoutRight, memory_space> cols_idx() const
67 {
68 return m_cols_idx;
69 }
70
71 KOKKOS_FUNCTION Kokkos::View<Real*, Kokkos::LayoutRight, memory_space> values() const
72 {
73 return m_values;
74 }
75};
76
77template <class ExecSpace>
78SplinesLinearProblem2x2Blocks<ExecSpace>::SplinesLinearProblem2x2Blocks(
79 std::size_t const mat_size,
80 std::unique_ptr<SplinesLinearProblem<ExecSpace>> top_left_block)
81 : SplinesLinearProblem<ExecSpace>(mat_size)
82 , m_top_left_block(std::move(top_left_block))
83 , m_top_right_block(
84 "top_right_block",
85 m_top_left_block->size(),
86 mat_size - m_top_left_block->size())
87 , m_bottom_left_block(
88 "bottom_left_block",
89 mat_size - m_top_left_block->size(),
90 m_top_left_block->size())
91 , m_bottom_right_block(
92 new SplinesLinearProblemDense<ExecSpace>(mat_size - m_top_left_block->size()))
93{
94 assert(m_top_left_block->size() <= mat_size);
95
96 Kokkos::deep_copy(m_top_right_block.view_host(), 0.);
97 Kokkos::deep_copy(m_bottom_left_block.view_host(), 0.);
98}
99
100template <class ExecSpace>
101SplinesLinearProblem2x2Blocks<ExecSpace>::~SplinesLinearProblem2x2Blocks() = default;
102
103template <class ExecSpace>
104Real SplinesLinearProblem2x2Blocks<ExecSpace>::get_element(std::size_t const i, std::size_t const j)
105 const
106{
107 assert(i < size());
108 assert(j < size());
109
110 std::size_t const nq = m_top_left_block->size();
111 if (i < nq && j < nq) {
112 return m_top_left_block->get_element(i, j);
113 }
114
115 if (i >= nq && j >= nq) {
116 return m_bottom_right_block->get_element(i - nq, j - nq);
117 }
118
119 if (j >= nq) {
120 return m_top_right_block.view_host()(i, j - nq);
121 }
122
123 return m_bottom_left_block.view_host()(i - nq, j);
124}
125
126template <class ExecSpace>
127void SplinesLinearProblem2x2Blocks<ExecSpace>::set_element(
128 std::size_t const i,
129 std::size_t const j,
130 Real const aij)
131{
132 assert(i < size());
133 assert(j < size());
134
135 std::size_t const nq = m_top_left_block->size();
136 if (i < nq && j < nq) {
137 m_top_left_block->set_element(i, j, aij);
138 } else if (i >= nq && j >= nq) {
139 m_bottom_right_block->set_element(i - nq, j - nq, aij);
140 } else if (j >= nq) {
141 m_top_right_block.view_host()(i, j - nq) = aij;
142 } else {
143 m_bottom_left_block.view_host()(i - nq, j) = aij;
144 }
145}
146
147template <class ExecSpace>
148std::unique_ptr<typename SplinesLinearProblem2x2Blocks<ExecSpace>::Coo>
149SplinesLinearProblem2x2Blocks<ExecSpace>::dense2coo(
150 Kokkos::View<Real const**, Kokkos::LayoutRight, memory_space> dense_matrix,
151 Real const tol)
152{
153 Kokkos::View<int*, Kokkos::LayoutRight, memory_space>
154 rows_idx("ddc_splines_coo_rows_idx", dense_matrix.extent(0) * dense_matrix.extent(1));
155 Kokkos::View<int*, Kokkos::LayoutRight, memory_space>
156 cols_idx("ddc_splines_coo_cols_idx", dense_matrix.extent(0) * dense_matrix.extent(1));
157 Kokkos::View<Real*, Kokkos::LayoutRight, memory_space>
158 values("ddc_splines_coo_values", dense_matrix.extent(0) * dense_matrix.extent(1));
159
160 Kokkos::DualView<std::size_t, Kokkos::LayoutRight, memory_space> n_nonzeros(
161 "ddc_splines_n_nonzeros");
162 n_nonzeros.view_host()() = 0;
163 n_nonzeros.modify_host();
164 n_nonzeros.sync_device();
165
166 auto const n_nonzeros_device = n_nonzeros.view_device();
167 Kokkos::parallel_for(
168 "dense2coo",
169 Kokkos::RangePolicy(ExecSpace(), 0, 1),
170 KOKKOS_LAMBDA(int const) {
171 for (int i = 0; i < dense_matrix.extent(0); ++i) {
172 for (int j = 0; j < dense_matrix.extent(1); ++j) {
173 Real const aij = dense_matrix(i, j);
174 if (Kokkos::abs(aij) >= tol) {
175 rows_idx(n_nonzeros_device()) = i;
176 cols_idx(n_nonzeros_device()) = j;
177 values(n_nonzeros_device()) = aij;
178 n_nonzeros_device()++;
179 }
180 }
181 }
182 });
183 n_nonzeros.modify_device();
184 n_nonzeros.sync_host();
185 Kokkos::resize(rows_idx, n_nonzeros.view_host()());
186 Kokkos::resize(cols_idx, n_nonzeros.view_host()());
187 Kokkos::resize(values, n_nonzeros.view_host()());
188
189 return std::make_unique<
190 Coo>(dense_matrix.extent(0), dense_matrix.extent(1), rows_idx, cols_idx, values);
191}
192
193template <class ExecSpace>
194void SplinesLinearProblem2x2Blocks<ExecSpace>::compute_schur_complement()
195{
196 auto const bottom_left_block = m_bottom_left_block.view_host();
197 auto const top_right_block = m_top_right_block.view_host();
198 Kokkos::parallel_for(
199 "compute_schur_complement",
200 Kokkos::MDRangePolicy<Kokkos::DefaultHostExecutionSpace, Kokkos::Rank<2>>(
201 {0, 0},
202 {m_bottom_right_block->size(), m_bottom_right_block->size()}),
203 [&](int const i, int const j) {
204 Real val = 0.0;
205 for (int l = 0; l < m_top_left_block->size(); ++l) {
206 val += bottom_left_block(i, l) * top_right_block(l, j);
207 }
208 m_bottom_right_block
209 ->set_element(i, j, m_bottom_right_block->get_element(i, j) - val);
210 });
211}
212
213template <class ExecSpace>
214void SplinesLinearProblem2x2Blocks<ExecSpace>::setup_solver()
215{
216 // Setup the top-left solver
217 m_top_left_block->setup_solver();
218
219 // Compute Q^-1*gamma in top-right block
220 m_top_right_block.modify_host();
221 m_top_right_block.sync_device();
222 m_top_left_block->solve(m_top_right_block.view_device(), false);
223 m_top_right_block_coo = dense2coo(m_top_right_block.view_device());
224 m_top_right_block.modify_device();
225 m_top_right_block.sync_host();
226
227 // Push lambda on device in bottom-left block
228 m_bottom_left_block.modify_host();
229 m_bottom_left_block.sync_device();
230 m_bottom_left_block_coo = dense2coo(m_bottom_left_block.view_device());
231
232 // Compute delta - lambda*Q^-1*gamma in bottom-right block & setup the bottom-right solver
233 compute_schur_complement();
234 m_bottom_right_block->setup_solver();
235}
236
237template <class ExecSpace>
238void SplinesLinearProblem2x2Blocks<ExecSpace>::spdm_minus1_1(
239 Coo const& LinOp,
240 MultiRHS const x,
241 MultiRHS const y,
242 bool const transpose) const
243{
244 assert((!transpose && LinOp.nrows() == y.extent(0))
245 || (transpose && LinOp.ncols() == y.extent(0)));
246 assert((!transpose && LinOp.ncols() == x.extent(0))
247 || (transpose && LinOp.nrows() == x.extent(0)));
248 assert(x.extent(1) == y.extent(1));
249
250 if (!transpose) {
251 Kokkos::parallel_for(
252 "ddc_splines_spdm_minus1_1",
253 Kokkos::RangePolicy(ExecSpace(), 0, y.extent(1)),
254 KOKKOS_LAMBDA(int const j) {
255 for (int nz_idx = 0; nz_idx < LinOp.nnz(); ++nz_idx) {
256 int const i = LinOp.rows_idx()(nz_idx);
257 int const k = LinOp.cols_idx()(nz_idx);
258 y(i, j) -= LinOp.values()(nz_idx) * x(k, j);
259 }
260 });
261 } else {
262 Kokkos::parallel_for(
263 "ddc_splines_spdm_minus1_1_tr",
264 Kokkos::RangePolicy(ExecSpace(), 0, y.extent(1)),
265 KOKKOS_LAMBDA(int const j) {
266 for (int nz_idx = 0; nz_idx < LinOp.nnz(); ++nz_idx) {
267 int const i = LinOp.rows_idx()(nz_idx);
268 int const k = LinOp.cols_idx()(nz_idx);
269 y(k, j) -= LinOp.values()(nz_idx) * x(i, j);
270 }
271 });
272 }
273}
274
275template <class ExecSpace>
276void SplinesLinearProblem2x2Blocks<ExecSpace>::solve(MultiRHS const b, bool const transpose) const
277{
278 assert(b.extent(0) == size());
279
280 MultiRHS const b1 = Kokkos::
281 subview(b,
282 std::pair<std::size_t, std::size_t>(0, m_top_left_block->size()),
283 Kokkos::ALL);
284 MultiRHS const b2 = Kokkos::
285 subview(b,
286 std::pair<std::size_t, std::size_t>(m_top_left_block->size(), b.extent(0)),
287 Kokkos::ALL);
288 if (!transpose) {
289 m_top_left_block->solve(b1, false);
290 spdm_minus1_1(*m_bottom_left_block_coo, b1, b2);
291 m_bottom_right_block->solve(b2, false);
292 spdm_minus1_1(*m_top_right_block_coo, b2, b1);
293 } else {
294 spdm_minus1_1(*m_top_right_block_coo, b1, b2, true);
295 m_bottom_right_block->solve(b2, true);
296 spdm_minus1_1(*m_bottom_left_block_coo, b2, b1, true);
297 m_top_left_block->solve(b1, true);
298 }
299}
300
301#if defined(KOKKOS_ENABLE_SERIAL)
302template class SplinesLinearProblem2x2Blocks<Kokkos::Serial>;
303#endif
304#if defined(KOKKOS_ENABLE_OPENMP)
305template class SplinesLinearProblem2x2Blocks<Kokkos::OpenMP>;
306#endif
307#if defined(KOKKOS_ENABLE_CUDA)
308template class SplinesLinearProblem2x2Blocks<Kokkos::Cuda>;
309#endif
310#if defined(KOKKOS_ENABLE_HIP)
311template class SplinesLinearProblem2x2Blocks<Kokkos::HIP>;
312#endif
313#if defined(KOKKOS_ENABLE_SYCL)
314template class SplinesLinearProblem2x2Blocks<Kokkos::SYCL>;
315#endif
316
317} // namespace ddc::detail
The top-level namespace of DDC.