Compadre  1.5.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Compadre_NeighborLists.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Compadre: COMpatible PArticle Discretization and REmap Toolkit
4 //
5 // Copyright 2018 NTESS and the Compadre contributors.
6 // SPDX-License-Identifier: BSD-2-Clause
7 // *****************************************************************************
8 // @HEADER
9 #ifndef _COMPADRE_NEIGHBORLISTS_HPP_
10 #define _COMPADRE_NEIGHBORLISTS_HPP_
11 
12 #include "Compadre_Typedefs.hpp"
13 #include <Kokkos_Core.hpp>
14 
15 namespace Compadre {
16 
17 //! NeighborLists assists in accessing entries of compressed row neighborhood lists
18 template <typename view_type>
19 struct NeighborLists {
20 
21  typedef view_type internal_view_type;
22  typedef Kokkos::View<global_index_type*, typename view_type::array_layout,
23  typename view_type::memory_space, typename view_type::memory_traits> internal_row_offsets_view_type;
24 
29 
31  view_type _cr_neighbor_lists;
33 
34  typename internal_row_offsets_view_type::HostMirror _host_row_offsets;
35  typename view_type::HostMirror _host_cr_neighbor_lists;
36  typename view_type::HostMirror _host_number_of_neighbors_list;
37 
38 /** @name Constructors
39  * Ways to initialize a NeighborLists object
40  */
41 ///@{
42 
43  //! \brief Constructor for the purpose of classes who have NeighborLists as a member object
47  _needs_sync_to_host = true;
49  }
50 
51  /*! \brief Constructor for when compressed row `cr_neighbor_lists` is preallocated/populated,
52  * `number_of_neighbors_list` and `neighbor_lists_row_offsets` have already been populated.
53  */
54  NeighborLists(view_type cr_neighbor_lists, view_type number_of_neighbors_list,
55  internal_row_offsets_view_type neighbor_lists_row_offsets, bool compute_max = true) {
56  compadre_assert_release((view_type::rank==1) &&
57  "cr_neighbor_lists and number_neighbors_list and neighbor_lists_row_offsets must be a 1D Kokkos view.");
58 
59  _number_of_targets = number_of_neighbors_list.extent(0);
60  _number_of_neighbors_list = number_of_neighbors_list;
61  _cr_neighbor_lists = cr_neighbor_lists;
62  _row_offsets = neighbor_lists_row_offsets;
63 
64  _host_cr_neighbor_lists = Kokkos::create_mirror_view(_cr_neighbor_lists);
65  _host_number_of_neighbors_list = Kokkos::create_mirror_view(_number_of_neighbors_list);
66  _host_row_offsets = Kokkos::create_mirror_view(_row_offsets);
67 
68  Kokkos::deep_copy(_host_cr_neighbor_lists, _cr_neighbor_lists);
70  Kokkos::deep_copy(_host_row_offsets, _row_offsets);
71  Kokkos::fence();
72 
73  if (compute_max) {
75  } else {
77  }
79 
80  //check neighbor_lists is large enough
81  compadre_assert_release(((size_t)(this->getTotalNeighborsOverAllListsHost())<=cr_neighbor_lists.extent(0))
82  && "neighbor_lists is not large enough to store all neighbors.");
83 
84  _needs_sync_to_host = false;
85  }
86 
87  /*! \brief Constructor for when compressed row `cr_neighbor_lists` is preallocated/populated,
88  * and `number_of_neighbors_list` is already populated, and row offsets still need to be computed.
89  */
90  NeighborLists(view_type cr_neighbor_lists, view_type number_of_neighbors_list) {
91  compadre_assert_release((view_type::rank==1)
92  && "cr_neighbor_lists and number_neighbors_list must be a 1D Kokkos view.");
93 
94  _number_of_targets = number_of_neighbors_list.extent(0);
95 
96  _row_offsets = internal_row_offsets_view_type("row offsets", number_of_neighbors_list.extent(0));
97  _number_of_neighbors_list = number_of_neighbors_list;
98  _cr_neighbor_lists = cr_neighbor_lists;
99 
100  _host_cr_neighbor_lists = Kokkos::create_mirror_view(_cr_neighbor_lists);
101  _host_number_of_neighbors_list = Kokkos::create_mirror_view(_number_of_neighbors_list);
102  _host_row_offsets = Kokkos::create_mirror_view(_row_offsets);
103 
104  Kokkos::deep_copy(_host_cr_neighbor_lists, _cr_neighbor_lists);
105  Kokkos::deep_copy(_host_number_of_neighbors_list, _number_of_neighbors_list);
106  Kokkos::deep_copy(_host_row_offsets, _row_offsets);
107  Kokkos::fence();
108 
111 
112  //check neighbor_lists is large enough
113  compadre_assert_release(((size_t)(this->getTotalNeighborsOverAllListsHost())<=cr_neighbor_lists.extent(0))
114  && "neighbor_lists is not large enough to store all neighbors.");
115 
116  _needs_sync_to_host = false;
117  }
118 
119  /*! \brief Constructor for when `number_of_neighbors_list` is already populated.
120  * Will allocate space for compressed row neighbor lists data, and will allocate then
121  * populate information for row offsets.
122  */
123  NeighborLists(view_type number_of_neighbors_list) {
124  compadre_assert_release((view_type::rank==1)
125  && "cr_neighbor_lists and number_neighbors_list must be a 1D Kokkos view.");
126 
127  _number_of_targets = number_of_neighbors_list.extent(0);
128 
129  _row_offsets = internal_row_offsets_view_type("row offsets", number_of_neighbors_list.extent(0));
130  _number_of_neighbors_list = number_of_neighbors_list;
131 
132  _host_number_of_neighbors_list = Kokkos::create_mirror_view(_number_of_neighbors_list);
133  _host_row_offsets = Kokkos::create_mirror_view(_row_offsets);
134 
135  Kokkos::deep_copy(_host_number_of_neighbors_list, _number_of_neighbors_list);
136  Kokkos::deep_copy(_host_row_offsets, _row_offsets);
137  Kokkos::fence();
138 
141 
142  _cr_neighbor_lists = view_type("compressed row neighbor lists data", this->getTotalNeighborsOverAllListsHost());
143  _host_cr_neighbor_lists = Kokkos::create_mirror_view(_cr_neighbor_lists);
144  Kokkos::deep_copy(_host_cr_neighbor_lists, _cr_neighbor_lists);
145  Kokkos::fence();
146 
147  _needs_sync_to_host = false;
148  }
149 ///@}
150 
151 /** @name Public modifiers
152  */
153 ///@{
154 
155  //! Setter function for N(i,j) indexing where N(i,j) is the index of the jth neighbor of i
156  KOKKOS_INLINE_FUNCTION
157  void setNeighborDevice(int target_index, int neighbor_num, int new_value) {
158  _cr_neighbor_lists(_row_offsets(target_index)+neighbor_num) = new_value;
159  // indicate that host view is now out of sync with device
160  // but only in debug mode (notice the next line is both setting the variable and checking it was set)
162  }
163 
164  //! Calculate the maximum number of neighbors of all targets' neighborhoods (host)
166  if (_number_of_neighbors_list.extent(0)==0) {
168  } else {
169  auto number_of_neighbors_list = _number_of_neighbors_list;
170  Kokkos::parallel_reduce("max number of neighbors",
171  Kokkos::RangePolicy<typename view_type::execution_space>(0, _number_of_neighbors_list.extent(0)),
172  KOKKOS_LAMBDA(const int i, int& t_max_num_neighbors) {
173  t_max_num_neighbors = (number_of_neighbors_list(i) > t_max_num_neighbors) ? number_of_neighbors_list(i) : t_max_num_neighbors;
174  }, Kokkos::Max<int>(_max_neighbor_list_row_storage_size));
175  Kokkos::fence();
176  }
177  }
178 
179  //! Calculate the minimum number of neighbors of all targets' neighborhoods (host)
181  if (_number_of_neighbors_list.extent(0)==0) {
183  } else {
184  auto number_of_neighbors_list = _number_of_neighbors_list;
185  Kokkos::parallel_reduce("min number of neighbors",
186  Kokkos::RangePolicy<typename view_type::execution_space>(0, _number_of_neighbors_list.extent(0)),
187  KOKKOS_LAMBDA(const int i, int& t_min_num_neighbors) {
188  t_min_num_neighbors = (number_of_neighbors_list(i) < t_min_num_neighbors) ? number_of_neighbors_list(i) : t_min_num_neighbors;
189  }, Kokkos::Min<int>(_min_neighbor_list_row_storage_size));
190  Kokkos::fence();
191  }
192  }
193 
194  //! Calculate the row offsets for each target's neighborhood (host)
196  auto number_of_neighbors_list = _number_of_neighbors_list;
197  auto row_offsets = _row_offsets;
198  Kokkos::parallel_scan("number of neighbors offsets",
199  Kokkos::RangePolicy<typename view_type::execution_space>(0, _number_of_neighbors_list.extent(0)),
200  KOKKOS_LAMBDA(const int i, global_index_type& lsum, bool final) {
201  row_offsets(i) = lsum;
202  lsum += number_of_neighbors_list(i);
203  });
204  Kokkos::deep_copy(_host_row_offsets, _row_offsets);
205  Kokkos::fence();
206  }
207 
208  //! Sync the host from the device (copy device data to host)
210  Kokkos::deep_copy(_host_cr_neighbor_lists, _cr_neighbor_lists);
211  Kokkos::fence();
212  _needs_sync_to_host = false;
213  }
214 
215  //! Device view into neighbor lists data (use with caution)
216  view_type getNeighborLists() const {
217  return _cr_neighbor_lists;
218  }
219 
220  //! Device view into number of neighbors list (use with caution)
221  view_type getNumberOfNeighborsList() const {
223  }
224 
225 ///@}
226 /** @name Public accessors
227  */
228 ///@{
229  //! Get number of total targets having neighborhoods (host/device).
230  KOKKOS_INLINE_FUNCTION
231  int getNumberOfTargets() const {
232  return _number_of_targets;
233  }
234 
235  //! Get number of neighbors for a given target (host)
236  int getNumberOfNeighborsHost(int target_index) const {
237  compadre_assert_extreme_debug(target_index < this->getNumberOfTargets());
238  return _host_number_of_neighbors_list(target_index);
239  }
240 
241  //! Get number of neighbors for a given target (device)
242  KOKKOS_INLINE_FUNCTION
243  int getNumberOfNeighborsDevice(int target_index) const {
245  return _number_of_neighbors_list(target_index);
246  }
247 
248  //! Get offset into compressed row neighbor lists (host)
249  global_index_type getRowOffsetHost(int target_index) const {
250  compadre_assert_extreme_debug(target_index < this->getNumberOfTargets());
251  return _host_row_offsets(target_index);
252  }
253 
254  //! Get offset into compressed row neighbor lists (device)
255  KOKKOS_INLINE_FUNCTION
256  global_index_type getRowOffsetDevice(int target_index) const {
258  return _row_offsets(target_index);
259  }
260 
261  //! Offers N(i,j) indexing where N(i,j) is the index of the jth neighbor of i (host)
262  int getNeighborHost(int target_index, int neighbor_num) const {
263  compadre_assert_extreme_debug(target_index < this->getNumberOfTargets());
265  && "Stale information in host_cr_neighbor_lists. Call CopyDeviceDataToHost() to refresh.");
266  compadre_assert_debug((neighbor_num<_host_number_of_neighbors_list(target_index))
267  && "neighor_num exceeds number of neighbors for this target_index.");
268  return _host_cr_neighbor_lists(_host_row_offsets(target_index)+neighbor_num);
269  }
270 
271  //! Offers N(i,j) indexing where N(i,j) is the index of the jth neighbor of i (device)
272  KOKKOS_INLINE_FUNCTION
273  int getNeighborDevice(int target_index, int neighbor_num) const {
275  && "neighor_num exceeds number of neighbors for this target_index.");
277  return _cr_neighbor_lists(_row_offsets(target_index)+neighbor_num);
278  }
279 
280  //! Get the maximum number of neighbors of all targets' neighborhoods (host/device)
281  KOKKOS_INLINE_FUNCTION
282  int getMaxNumNeighbors() const {
283  compadre_kernel_assert_debug((_max_neighbor_list_row_storage_size > -1) && "getMaxNumNeighbors() called but maximum never calculated.");
285  }
286 
287  //! Get the minimum number of neighbors of all targets' neighborhoods (host/device)
288  KOKKOS_INLINE_FUNCTION
289  int getMinNumNeighbors() const {
290  compadre_kernel_assert_debug((_min_neighbor_list_row_storage_size > -1) && "getMinNumNeighbors() called but minimum never calculated.");
292  }
293 
294  //! Get the sum of the number of neighbors of all targets' neighborhoods (host)
296  if (this->getNumberOfTargets()==0) {
297  return 0;
298  } else {
300  }
301  }
302 
303  //! Get the sum of the number of neighbors of all targets' neighborhoods (device)
304  KOKKOS_INLINE_FUNCTION
307  }
308 ///@}
309 
310 }; // NeighborLists
311 
312 //! CreateNeighborLists allows for the construction of an object of type NeighborLists with template deduction
313 template <typename view_type>
314 NeighborLists<view_type> CreateNeighborLists(view_type number_of_neighbors_list) {
315  return NeighborLists<view_type>(number_of_neighbors_list);
316 }
317 
318 //! CreateNeighborLists allows for the construction of an object of type NeighborLists with template deduction
319 template <typename view_type>
320 NeighborLists<view_type> CreateNeighborLists(view_type neighbor_lists, view_type number_of_neighbors_list) {
321  return NeighborLists<view_type>(neighbor_lists, number_of_neighbors_list);
322 }
323 
324 //! CreateNeighborLists allows for the construction of an object of type NeighborLists with template deduction
325 template <typename view_type>
326 NeighborLists<view_type> CreateNeighborLists(view_type neighbor_lists, view_type number_of_neighbors_list, view_type neighbor_lists_row_offsets) {
327  return NeighborLists<view_type>(neighbor_lists, number_of_neighbors_list, neighbor_lists_row_offsets);
328 }
329 
330 //! Converts 2D neighbor lists to compressed row neighbor lists
331 template <typename view_type_2d, typename view_type_1d = Kokkos::View<int*, typename view_type_2d::memory_space, typename view_type_2d::memory_traits> >
333 
334  // gets total number of neighbors over all lists
335  // computes calculation where the data resides (device/host)
336  global_index_type total_storage_size = 0;
337  Kokkos::parallel_reduce("total number of neighbors over all lists", Kokkos::RangePolicy<typename view_type_2d::execution_space>(0, neighbor_lists.extent(0)),
338  KOKKOS_LAMBDA(const int i, global_index_type& t_total_num_neighbors) {
339  t_total_num_neighbors += neighbor_lists(i,0);
340  }, Kokkos::Sum<global_index_type>(total_storage_size));
341  Kokkos::fence();
342 
343  // view_type_1d may be on host or device, and view_type_2d may be either as well (could even be opposite)
344  view_type_1d new_cr_neighbor_lists("compressed row neighbor lists", total_storage_size);
345  view_type_1d new_number_of_neighbors_list("number of neighbors list", neighbor_lists.extent(0));
346 
347  // copy number of neighbors list over to view_type_1d
348  // d_neighbor_lists will be accessible from view_type_1d's execution space
349  auto d_neighbor_lists = create_mirror_view(typename view_type_1d::execution_space(), neighbor_lists);
350  Kokkos::deep_copy(d_neighbor_lists, neighbor_lists);
351  Kokkos::fence();
352  Kokkos::parallel_for("copy number of neighbors to compressed row",
353  Kokkos::RangePolicy<typename view_type_1d::execution_space>(0, neighbor_lists.extent(0)),
354  KOKKOS_LAMBDA(const int i) {
355  new_number_of_neighbors_list(i) = d_neighbor_lists(i,0);
356  });
357  Kokkos::fence();
358 
359 
360  // this will calculate row offsets
361  auto nla = CreateNeighborLists(new_cr_neighbor_lists, new_number_of_neighbors_list);
362  auto cr_data = nla.getNeighborLists();
363 
364  // if device_execution_space can access this view, then write directly into the view
365  if (Kokkos::SpaceAccessibility<device_execution_space, typename view_type_1d::memory_space>::accessible==1) {
366  Kokkos::parallel_for("copy neighbor lists to compressed row", Kokkos::RangePolicy<typename view_type_1d::execution_space>(0, neighbor_lists.extent(0)),
367  KOKKOS_LAMBDA(const int i) {
368  for (int j=0; j<d_neighbor_lists(i,0); ++j) {
369  cr_data(nla.getRowOffsetDevice(i)+j) = d_neighbor_lists(i,j+1);
370  }
371  });
372  Kokkos::fence();
373  nla.copyDeviceDataToHost(); // has a fence at the end
374  }
375  // otherwise we are writing to a view that can't be seen from device (must be host space),
376  // and d_neighbor_lists was already made to be a view_type that is accessible from view_type_1d's execution_space
377  // (which we know is host) so we can do a parallel_for over the host_execution_space
378  else {
379  Kokkos::parallel_for("copy neighbor lists to compressed row", Kokkos::RangePolicy<host_execution_space>(0, neighbor_lists.extent(0)),
380  KOKKOS_LAMBDA(const int i) {
381  for (int j=0; j<neighbor_lists(i,0); ++j) {
382  cr_data(nla.getRowOffsetHost(i)+j) = d_neighbor_lists(i,j+1);
383  }
384  });
385  Kokkos::fence();
386  }
387 
388  return nla;
389 }
390 
391 } // Compadre namespace
392 
393 #endif
394 
void computeRowOffsets()
Calculate the row offsets for each target&#39;s neighborhood (host)
int getNumberOfNeighborsHost(int target_index) const
Get number of neighbors for a given target (host)
NeighborLists(view_type number_of_neighbors_list)
Constructor for when number_of_neighbors_list is already populated. Will allocate space for compresse...
std::size_t global_index_type
KOKKOS_INLINE_FUNCTION int getNumberOfNeighborsDevice(int target_index) const
Get number of neighbors for a given target (device)
#define compadre_assert_extreme_debug(condition)
compadre_kernel_assert_debug is similar to compadre_assert_debug, but is a call on the device...
KOKKOS_INLINE_FUNCTION int getNumberOfTargets() const
Get number of total targets having neighborhoods (host/device).
#define compadre_kernel_assert_extreme_debug(condition)
internal_row_offsets_view_type _row_offsets
global_index_type getRowOffsetHost(int target_index) const
Get offset into compressed row neighbor lists (host)
KOKKOS_INLINE_FUNCTION int getMinNumNeighbors() const
Get the minimum number of neighbors of all targets&#39; neighborhoods (host/device)
KOKKOS_INLINE_FUNCTION void setNeighborDevice(int target_index, int neighbor_num, int new_value)
Setter function for N(i,j) indexing where N(i,j) is the index of the jth neighbor of i...
NeighborLists(view_type cr_neighbor_lists, view_type number_of_neighbors_list, internal_row_offsets_view_type neighbor_lists_row_offsets, bool compute_max=true)
Constructor for when compressed row cr_neighbor_lists is preallocated/populated, number_of_neighbors_...
NeighborLists< view_type > CreateNeighborLists(view_type number_of_neighbors_list)
CreateNeighborLists allows for the construction of an object of type NeighborLists with template dedu...
view_type getNeighborLists() const
Device view into neighbor lists data (use with caution)
NeighborLists(view_type cr_neighbor_lists, view_type number_of_neighbors_list)
Constructor for when compressed row cr_neighbor_lists is preallocated/populated, and number_of_neighb...
void computeMinNumNeighbors()
Calculate the minimum number of neighbors of all targets&#39; neighborhoods (host)
internal_row_offsets_view_type::HostMirror _host_row_offsets
void computeMaxNumNeighbors()
Calculate the maximum number of neighbors of all targets&#39; neighborhoods (host)
global_index_type getTotalNeighborsOverAllListsHost() const
Get the sum of the number of neighbors of all targets&#39; neighborhoods (host)
#define TO_GLOBAL(variable)
Kokkos::View< global_index_type *, typename view_type::array_layout, typename view_type::memory_space, typename view_type::memory_traits > internal_row_offsets_view_type
KOKKOS_INLINE_FUNCTION global_index_type getTotalNeighborsOverAllListsDevice() const
Get the sum of the number of neighbors of all targets&#39; neighborhoods (device)
KOKKOS_INLINE_FUNCTION int getMaxNumNeighbors() const
Get the maximum number of neighbors of all targets&#39; neighborhoods (host/device)
void copyDeviceDataToHost()
Sync the host from the device (copy device data to host)
NeighborLists()
Constructor for the purpose of classes who have NeighborLists as a member object. ...
int getNeighborHost(int target_index, int neighbor_num) const
Offers N(i,j) indexing where N(i,j) is the index of the jth neighbor of i (host)
view_type::HostMirror _host_cr_neighbor_lists
#define compadre_assert_debug(condition)
compadre_assert_debug is used for assertions that are checked in loops, as these significantly impact...
view_type getNumberOfNeighborsList() const
Device view into number of neighbors list (use with caution)
KOKKOS_INLINE_FUNCTION int getNeighborDevice(int target_index, int neighbor_num) const
Offers N(i,j) indexing where N(i,j) is the index of the jth neighbor of i (device) ...
NeighborLists< view_type_1d > Convert2DToCompressedRowNeighborLists(view_type_2d neighbor_lists)
Converts 2D neighbor lists to compressed row neighbor lists.
NeighborLists assists in accessing entries of compressed row neighborhood lists.
KOKKOS_INLINE_FUNCTION global_index_type getRowOffsetDevice(int target_index) const
Get offset into compressed row neighbor lists (device)
view_type::HostMirror _host_number_of_neighbors_list
#define compadre_assert_release(condition)
compadre_assert_release is used for assertions that should always be checked, but generally are not e...
#define compadre_kernel_assert_debug(condition)