PACMAN 0.1.0
Portable Algorithms for Coupling, Mapping, and Adaptive iNterpolation
Loading...
Searching...
No Matches
solver.hpp
Go to the documentation of this file.
1//
2// This file is subject to the terms and conditions defined in
3// file 'LICENSE', which is part of this source code package.
4//
5
6#pragma once
7
8#include <KokkosBatched_Copy_Decl.hpp>
9#include <Kokkos_Core.hpp>
10#include <Kokkos_Profiling_ScopedRegion.hpp>
11
12#include "common/concepts.hpp"
13#include "common/types.hpp"
14#include "interpolator.hxx"
19#include "utils/utils.hpp"
20
21#define VIEW_ALLOC(name) \
22 Kokkos::view_alloc(ExecSpace{}, Kokkos::WithoutInitializing, \
23 _region_name + "::" + name)
24
25namespace PACMAN {
26namespace RbfPum {
27
34FULL_TEMPLATE
35void TEMPLATED_CLASSNAME::Interpolate(void) {
36 const std::string _region_name = "RbfPumInterpolator::Interpolate";
37 const ExecSpace execspace{};
38
39 const index_t K = this->mClusters.extent(0);
40
41 Kokkos::Profiling::pushRegion(_region_name +
42 "::fetch source and target nodes");
43
44 // We get the source values in each cluster
45 VectorView<index_t> clusters_source_indices(
46 VIEW_ALLOC("clusters_source_indices"), 0);
47 VectorView<offset_t> clusters_source_offsets(
48 VIEW_ALLOC("clusters_source_offsets"), 0);
49 GetClustersPoints get_clusters_points_predicate{this->mClusters,
50 this->mRadius};
51 GetClustersPointsCallback get_clusters_points_callback;
52
53 // We get the target values in each cluster
54 VectorView<index_t> clusters_target_indices(
55 VIEW_ALLOC("clusters_target_indices"), 0);
56 VectorView<offset_t> clusters_target_offsets(
57 VIEW_ALLOC("clusters_target_offsets"), 0);
58
59 this->mSourceBvh.query(ExecSpace{}, get_clusters_points_predicate,
60 get_clusters_points_callback, clusters_source_indices,
61 clusters_source_offsets);
62 get_clusters_points_predicate.radius -= fp_consts::epsilon();
63 this->mTargetBvh.query(ExecSpace{}, get_clusters_points_predicate,
64 get_clusters_points_callback, clusters_target_indices,
65 clusters_target_offsets);
66 Kokkos::fence();
67 Kokkos::Profiling::popRegion(); // ! fetch source and target nodes
68
69 Kokkos::Profiling::pushRegion(_region_name +
70 "::compute offsets and allocate memory");
71
72 // We compute access offsets for our biggest matrices, to store them in a 1D
73 // array
74 VectorView<offset_t> rbf_mat_offsets(VIEW_ALLOC("rbf_mat_offsets"), K + 1);
75 VectorView<offset_t> eval_mat_offsets(VIEW_ALLOC("eval_mat_offsets"), K + 1);
76 offset_t rbf_mat_size;
77 offset_t eval_mat_size;
78 OffsetsScan<ExecSpace> offsets_scan{clusters_source_offsets,
79 clusters_target_offsets, rbf_mat_offsets,
80 eval_mat_offsets};
81 Kokkos::parallel_scan(_region_name + "::p_scan create access offsets",
82 Kokkos::RangePolicy(execspace, 0, K), offsets_scan);
83
84 // TODO: we must avoid this
85 auto sv1 = Kokkos::subview(rbf_mat_offsets, K);
86 auto sv2 = Kokkos::subview(rbf_mat_offsets, 0);
87 auto sv3 = Kokkos::subview(eval_mat_offsets, K);
88 auto sv4 = Kokkos::subview(eval_mat_offsets, 0);
89 Kokkos::deep_copy(rbf_mat_size, sv1);
90 Kokkos::deep_copy(sv2, 0);
91 Kokkos::deep_copy(eval_mat_size, sv3);
92 Kokkos::deep_copy(sv4, 0);
93
94 const int_t source_size = clusters_source_indices.extent_int(0);
95 const int_t target_size = clusters_target_indices.extent_int(0);
96 constexpr int_t poly_vals = Dim + 1;
97
98 // We allocate all of the space needed for the system solve process (N: nb
99 // source nodes, M: nb target nodes, poly_vals: nb active axis - 1) As: 1D
100 // vector which holds the RBF matrices (size N x N) Bs: 1D vector which holds
101 // the lhs vectors (the values we know at source points) (size 1 x N) Qs: 1D
102 // vector which holds the source polynomial augmentation (size N x poly_vals)
103 // Vs: 1D vector which holds the target polynomial augmentation (size M x
104 // poly_vals) Es: 1D vector which holds the RBF evaluation matrices (size M x
105 // N) Ts: 1D vector which holds temporary data for the QR factorization
106 // coefficients (size 1 x poly_vals) Ws: 1D vector which holds temporary
107 // workspace data for the QR factorization coefficients (size 1 x N), must be
108 // contiguous Xs: 1D vector which holds temporary solutions to compute the
109 // \beta coefficients (the source polynomial contribution) (size 1 x N)
110 // TempView: 1D vector which holds temporary local interpolant values (size 1
111 // x M) AxisView: 1D vector of `Kokkos::Array` which keeps track of the active
112 // axis for each system. Some axis can be deactivated for stable computations
113 // during the filling process of Q. (size K x (array<Dim>))
114 VectorView<fp_t> As(VIEW_ALLOC("As"), rbf_mat_size);
115 VectorView<fp_t> Bs(VIEW_ALLOC("Bs"), source_size);
116 VectorView<fp_t> Qs(VIEW_ALLOC("Qs"), source_size * poly_vals);
117 VectorView<fp_t> Vs(VIEW_ALLOC("Vs"), target_size * poly_vals);
118 VectorView<fp_t> Es(VIEW_ALLOC("Es"), eval_mat_size);
119 VectorView<fp_t> Ts(VIEW_ALLOC("Ts"), poly_vals * K);
120 VectorView<fp_t> Ws(VIEW_ALLOC("Ws"), source_size);
121 VectorView<fp_t> Xs(VIEW_ALLOC("Xs"), source_size);
122 VectorView<fp_t> TempView(VIEW_ALLOC("TempView"), target_size);
123 VectorView<Kokkos::Array<bool, Dim>> AxisView(VIEW_ALLOC("AxisView"), K);
124
125 Kokkos::fence();
126 Kokkos::Profiling::popRegion(); // ! compute offsets and allocate memory
127
128 // Computation of the per clusters weights using a Shepard kernel
129 Kokkos::Profiling::pushRegion(_region_name + "::compute clusters weights");
130 const auto centers = this->mClusters;
131 const auto source = this->mSource;
132 const auto target = this->mTarget;
133 const auto values = this->mValues;
134 const auto rbf_function = this->mRbfFunction;
135 const auto weighting_function = this->mWeightingFunction;
136
137 const auto center_bvh = ::ArborX::BoundingVolumeHierarchy{
138 execspace, ::ArborX::Experimental::attach_indices(centers)};
139 GetClustersPoints get_target_clusters{target, this->mRadius};
140 GetClustersPointsCallback get_target_clusters_callback;
141 VectorView<index_t> weights_indices(VIEW_ALLOC("weights_indices"), 0);
142 VectorView<offset_t> weights_offsets(VIEW_ALLOC("weights_offsets"), 0);
143 center_bvh.query(execspace, get_target_clusters, get_target_clusters_callback,
144 weights_indices, weights_offsets);
145
146 // weights: normalized weights per cluster per target point
147 // only non null weights are stored,
148 // so weights size if (M x (nb interacting regions per target point))
149 VectorView<fp_t> weights(VIEW_ALLOC("weights"), weights_indices.extent(0));
150
151 Kokkos::parallel_for(
152 _region_name + "::p_for compute weights",
153 Kokkos::RangePolicy(execspace, 0, target.extent(0)),
154 KOKKOS_LAMBDA(const index_t &i) {
155 const auto target_point = target(i);
156 fp_t sum_w = fp_consts::zero();
157 for (offset_t k = weights_offsets(i); k < weights_offsets(i + 1); ++k) {
158 const auto center_point = centers(weights_indices(k));
159 const auto w = weighting_function.Eval(
160 DistanceNoCheck(target_point, center_point));
161 sum_w += w;
162 weights(k) = w;
163 }
164 for (offset_t k = weights_offsets(i); k < weights_offsets(i + 1); ++k) {
165 weights(k) /= sum_w;
166 }
167 });
168 Kokkos::fence();
169 Kokkos::Profiling::popRegion(); // ! compute clusters weights
170
171 // output is transfer.targetValues to avoid a deepcopy
172 auto output = this->out;
173
174 using team_policy = Kokkos::TeamPolicy<ExecSpace>;
175 using member_type = team_policy::member_type;
176
177 // The RBF matrices can be quite big, so we use a team policy to fill them
178 // it takes more time with OpenMP backend but is a big speedup on device
179 Kokkos::Profiling::pushRegion(_region_name + "::fill and solve systems");
180 Kokkos::parallel_for(
181 _region_name + "::p_for team fill A, E, B",
182 team_policy(execspace, K, Kokkos::AUTO),
183 KOKKOS_LAMBDA(const member_type &team) {
184 const index_t k = team.league_rank();
185 const auto source_slice = Kokkos::make_pair(
186 clusters_source_offsets(k), clusters_source_offsets(k + 1));
187 const auto target_slice = Kokkos::make_pair(
188 clusters_target_offsets(k), clusters_target_offsets(k + 1));
189
190 const auto source_points =
191 Kokkos::subview(clusters_source_indices, source_slice);
192 const auto target_points =
193 Kokkos::subview(clusters_target_indices, target_slice);
194
195 const index_t n = source_slice.second - source_slice.first;
196 const index_t m = target_slice.second - target_slice.first;
197
198 // we fetch the right slice of As, Es, Bs to access to A, E, B
199 // and reshape them from a vector to their real shape
200 auto A = GetRbfMatrix(k, As, rbf_mat_offsets, n, n);
201 auto E = GetRbfMatrix(k, Es, eval_mat_offsets, m, n);
202 auto B = GetRbfVector(k, Bs, clusters_source_offsets, n);
203
204 // we fill these matrices
205 TeamFillA(team, A, source, source_points, rbf_function);
206 TeamFillE(team, E, source, source_points, target, target_points,
207 rbf_function);
208 TeamFillB(team, B, values, source_points);
209 });
210
211 // we want to fill Q and V, the polynomial augmentations
212 // and then solve $Q\beta = b$ & $A\alpha = b - \beta Q$
213 // after that, we compute the interpolated values with:
214 // $t_{local} = E\alpha + V\beta$ <- this represent local approximations per
215 // regions finally, we aggregate these local approximations with the weights:
216 // $t_{values}(x) = \sum_{k=0}^{K} w_k(x) \times t_{local}(x)
217 Kokkos::parallel_for(
218 _region_name + "::p_for fill and solve remaining systems",
219 Kokkos::RangePolicy(execspace, 0, K), KOKKOS_LAMBDA(const index_t &k) {
220 const auto source_slice = Kokkos::make_pair(
221 clusters_source_offsets(k), clusters_source_offsets(k + 1));
222 const auto target_slice = Kokkos::make_pair(
223 clusters_target_offsets(k), clusters_target_offsets(k + 1));
224
225 const auto source_points =
226 Kokkos::subview(clusters_source_indices, source_slice);
227 const auto target_points =
228 Kokkos::subview(clusters_target_indices, target_slice);
229
230 const index_t n = source_slice.second - source_slice.first;
231 const index_t m = target_slice.second - target_slice.first;
232
233 auto A = GetRbfMatrix(k, As, rbf_mat_offsets, n, n);
234 auto E = GetRbfMatrix(k, Es, eval_mat_offsets, m, n);
235 auto B = GetRbfMatrix(k, Bs, clusters_source_offsets, n, 1);
236 auto b = Kokkos::subview(B, Kokkos::ALL(), 0);
237
238 auto active_axis = AxisView(k);
239 for (int_t d = 0; d < active_axis.size(); ++d) {
240 active_axis[d] = true;
241 }
242 auto tQ = GetPolyMatrix(k, Qs, clusters_source_offsets, n, active_axis);
243 auto W = GetRbfVector(k, Ws, clusters_source_offsets, n);
244 // this function returns the number of active axis, pv, after the
245 // condition number check loop on Q
246 int_t pv = SerialFillQ(tQ, source, source_points, W, active_axis);
247
248 auto V = GetPolyMatrix(k, Vs, clusters_target_offsets, m, active_axis);
249 SerialFillPoly(V, target, target_points, active_axis);
250
251 auto X = GetRbfMatrix(k, Xs, clusters_source_offsets, n, 1);
252 auto T = Kokkos::subview(
253 Ts, Kokkos::make_pair(k * poly_vals, k * poly_vals + pv));
254
255 auto Q = GetPolyMatrix(k, Qs, clusters_source_offsets, n, active_axis);
256 SerialSolveQR(Q, B, T, X, W);
257
258 SerialFillPoly(Q, source, source_points, active_axis);
259 const auto polynomial_contrib =
260 Kokkos::subview(X, Kokkos::make_pair(0, pv), 0);
261 SerialMulMatVec(Q, polynomial_contrib, W);
262
263 SerialVecVecSub(b, W);
264
265 auto temp_out = GetRbfVector(k, TempView, clusters_target_offsets, m);
266
267 auto x = Kokkos::subview(X, Kokkos::make_pair(0, pv), 0);
268
269 SerialSolveLU(A, b);
270 SerialMulMatVec(E, b, temp_out);
271 auto eval_mat_slice = Kokkos::subview(E, Kokkos::ALL(), 0);
272 SerialMulMatVec(V, x, eval_mat_slice);
273 SerialVecVecAdd(temp_out, eval_mat_slice);
274
275 for (index_t i = 0; i < m; ++i) {
276 const auto target_indice = target_points(i);
277 const auto local_contribution = temp_out(i);
278 const auto output_addr = &(output(target_indice));
279 for (offset_t t = weights_offsets(target_indice);
280 t < weights_offsets(target_indice + 1); ++t) {
281 if (weights_indices(t) == k) {
282 Kokkos::atomic_add(output_addr, weights(t) * local_contribution);
283 }
284 }
285 }
286 });
287 Kokkos::fence();
288 Kokkos::Profiling::popRegion(); // ! fill and solve systems
289}
290} // namespace RbfPum
291} // namespace PACMAN
KOKKOS_FORCEINLINE_FUNCTION void SerialVecVecSub(UViewType &U, const VViewType &V)
Perform an inplace difference of 2 vectors such as U = U - V.
KOKKOS_FORCEINLINE_FUNCTION auto GetPolyMatrix(const index_t &k, const PsViewType &Ps, const OffsViewType &offs, const int_t &n, const AxisType &activeAxis)
Returns a matrix of rank 2, shaped as (n, m), using the given data view, team rank,...
Definition getters.hpp:59
KOKKOS_FORCEINLINE_FUNCTION int_t SerialFillQ(QViewType &Q, const XViewType &X, const XOffsType &XOffs, WViewType &W, AxisType &activeAxis)
Fill the polynomial augmentation matrix Q and sets activeAxis accordingly to the deactivated axis in ...
KOKKOS_FORCEINLINE_FUNCTION auto GetRbfVector(const index_t &k, const BsViewType &Bs, const OffsViewType &offs, const int_t &n)
Returns a vector view of rank 1, of length n, using the given data view, team rank,...
Definition getters.hpp:92
KOKKOS_FORCEINLINE_FUNCTION void SerialSolveLU(AViewType &A, BViewType &B)
Solves AX=B for X using a LU factorization on A.
KOKKOS_FUNCTION void SerialFillPoly(PViewType &P, const XViewType &X, const OffsViewType &XOffs, AxisType &activeAxis)
Fill the polynomial augmentation matrix accordingly to the activated axis in activeAxis
KOKKOS_FORCEINLINE_FUNCTION void TeamFillA(const TeamHandleType &teamHandle, const AViewType &A, const XType &X, const XOffsType &XOffs, const RbfFuncType &func)
Fills the RBF matrix A in parallel.
Definition team_ops.hpp:39
KOKKOS_FORCEINLINE_FUNCTION void TeamFillB(const TeamHandleType &teamHandle, BViewType &B, const XType &X, const XOffsType &XOffs)
Fills the vector of known source values B in parallel.
Definition team_ops.hpp:115
KOKKOS_FORCEINLINE_FUNCTION auto GetRbfMatrix(const index_t &k, const AsViewType &As, const OffsViewType &offs, const int_t &n, const int_t &m)
Returns a matrix of rank 2, shaped as (n, m), using the given data view, team rank,...
Definition getters.hpp:31
KOKKOS_FORCEINLINE_FUNCTION void SerialMulMatVec(const AViewType &A, const BViewType &B, OutViewType &out)
Perform: out = Ab.
KOKKOS_INLINE_FUNCTION fp_t DistanceNoCheck(const ::ArborX::Point< Dim, coordinates_t > &lhs, const ::ArborX::Point< Dim, coordinates_t > &rhs)
Returns the euclidian norm between two points by performing a square root operation on the SquaredDif...
KOKKOS_FORCEINLINE_FUNCTION void SerialVecVecAdd(UViewType &U, const VViewType &V)
Perform an inplace addition of 2 vectors such as U = U + V.
KOKKOS_FORCEINLINE_FUNCTION void TeamFillE(const TeamHandleType &teamHandle, AViewType &A, const XType &X, const XOffsType &XOffs, const YType &Y, const YOffsType &YOffs, const RbfFuncType &func)
Fills the RBF evaluation matrix E in parallel.
Definition team_ops.hpp:83
KOKKOS_FORCEINLINE_FUNCTION auto SerialSolveQR(AViewType &A, const BViewType &B, TViewType &T, XViewType &X, WViewType &W)
Solves AX=B for X using a QR factorization on A.
KOKKOS_INLINE_FUNCTION consteval fp_t zero(void)
Definition types.hpp:92
KOKKOS_INLINE_FUNCTION consteval fp_t epsilon(void)
Definition types.hpp:98
double fp_t
Definition types.hpp:15
uint32_t index_t
Definition types.hpp:20
int32_t offset_t
Definition types.hpp:21
int32_t int_t
Definition types.hpp:18
#define VIEW_ALLOC(name)
Definition solver.hpp:21