Version: SMASH-1.6
smash::Grid< Options > Class Template Reference

#include <grid.h>

template<GridOptions Options = GridOptions::Normal>
class smash::Grid< Options >

Abstracts a list of cells that partition the particles in the experiment into regions of space that can interact / cannot interact.

This class is used to construct a helper data structure to reduce the combinatorics of finding particle pairs that could interact (scatter). It takes a list of ParticleData objects and sorts them in such a way that it is easy to look only at lists of particles that have a chance of interacting.

Template Parameters
OptionsThis policy parameter determines whether ghost cells are created to support periodic boundaries, or not.

Definition at line 79 of file grid.h.

Inheritance diagram for smash::Grid< Options >:
[legend]
Collaboration diagram for smash::Grid< Options >:
[legend]

Public Member Functions

 Grid (const Particles &particles, double min_cell_length, double timestep_duration, CellSizeStrategy strategy=CellSizeStrategy::Optimal)
 Constructs a grid from the given particle list particles. More...
 
 Grid (const std::pair< std::array< double, 3 >, std::array< double, 3 >> &min_and_length, const Particles &particles, double min_cell_length, double timestep_duration, CellSizeStrategy strategy=CellSizeStrategy::Optimal)
 Constructs a grid with the given minimum grid coordinates and grid length. More...
 
void iterate_cells (const std::function< void(const ParticleList &)> &search_cell_callback, const std::function< void(const ParticleList &, const ParticleList &)> &neighbor_cell_callback) const
 Iterates over all cells in the grid and calls the callback arguments with a search cell and 0 to 13 neighbor cells. More...
 
template<>
void iterate_cells (const std::function< void(const ParticleList &)> &search_cell_callback, const std::function< void(const ParticleList &, const ParticleList &)> &neighbor_cell_callback) const
 Specialization of iterate_cells. More...
 
template<>
void iterate_cells (const std::function< void(const ParticleList &)> &search_cell_callback, const std::function< void(const ParticleList &, const ParticleList &)> &neighbor_cell_callback) const
 Specialization of iterate_cells. More...
 

Private Member Functions

SizeType make_index (SizeType x, SizeType y, SizeType z) const
 
SizeType make_index (std::array< SizeType, 3 > idx) const
 

Private Attributes

const std::array< double, 3 > length_
 The 3 lengths of the complete grid. Used for periodic boundary wrapping. More...
 
std::array< int, 3 > number_of_cells_
 The number of cells in x, y, and z direction. More...
 
std::vector< ParticleList > cells_
 The cell storage. More...
 

Additional Inherited Members

- Public Types inherited from smash::GridBase
typedef int SizeType
 A type to store the sizes. More...
 
- Static Protected Member Functions inherited from smash::GridBase
static std::pair< std::array< double, 3 >, std::array< double, 3 > > find_min_and_length (const Particles &particles)
 

Constructor & Destructor Documentation

template<GridOptions Options = GridOptions::Normal>
smash::Grid< Options >::Grid ( const Particles particles,
double  min_cell_length,
double  timestep_duration,
CellSizeStrategy  strategy = CellSizeStrategy::Optimal 
)
inline

Constructs a grid from the given particle list particles.

It automatically determines the necessary size for the grid from the positions of the particles.

Parameters
[in]particlesThe particles to place onto the grid.
[in]min_cell_lengthThe minimal length a cell must have.
[in]timestep_durationDuration of the timestep. It is necessary for formation times treatment: if particle is fully or partially formed before the end of the timestep, it has to be on the grid.
[in]strategyThe strategy for determining the cell size

Definition at line 93 of file grid.h.

96  : Grid{find_min_and_length(particles), std::move(particles),
97  min_cell_length, timestep_duration, strategy} {}
Grid(const Particles &particles, double min_cell_length, double timestep_duration, CellSizeStrategy strategy=CellSizeStrategy::Optimal)
Constructs a grid from the given particle list particles.
Definition: grid.h:93
static std::pair< std::array< double, 3 >, std::array< double, 3 > > find_min_and_length(const Particles &particles)
Definition: grid.cc:71

Here is the caller graph for this function:

template<GridOptions O>
template smash::Grid< Options >::Grid ( const std::pair< std::array< double, 3 >, std::array< double, 3 >> &  min_and_length,
const Particles particles,
double  min_cell_length,
double  timestep_duration,
CellSizeStrategy  strategy = CellSizeStrategy::Optimal 
)

Constructs a grid with the given minimum grid coordinates and grid length.

If you need periodic boundaries you have to use this constructor to set the correct length to use for wrapping particles around the borders.

Parameters
[in]min_and_lengthA pair consisting of the three min coordinates and the three lengths.
[in]particlesThe particles to place onto the grid.
[in]min_cell_lengthThe minimal length a cell must have.
[in]timestep_durationduration of the timestep in fm/c
[in]strategyThe strategy for determining the cell size

Definition at line 100 of file grid.cc.

104  : length_(min_and_length.second) {
105  const auto min_position = min_and_length.first;
106  const SizeType particle_count = particles.size();
107 
108  // very simple setup for non-periodic boundaries and largest cellsize strategy
109  if (O == GridOptions::Normal && strategy == CellSizeStrategy::Largest) {
110  number_of_cells_ = {1, 1, 1};
111  cells_.clear();
112  cells_.reserve(1);
113  cells_.emplace_back(particles.copy_to_vector());
114  return;
115  }
116 
117  // The number of cells is determined by the min and max coordinates where
118  // particles are positioned and the maximal interaction length (which equals
119  // the length of a cell).
120  // But don't let the number of cells exceed the actual number of particles.
121  // That would be overkill. Let max_cells³ ≤ particle_count (conversion to
122  // int truncates).
123  // Consider that particle placement into cells uses half-open intervals. Thus
124  // a cell includes particles in [0, a[. The next cell [a, 2a[. And so on. This
125  // is important for calculating the number of cells. If length * index_factor
126  // (equivalent to length / max_interaction_length) is integral, then
127  // length * index_factor + 1 determines the number of required cells. That's
128  // because the last cell will then store particles in the interval
129  // [length, length + max_interaction_length[. The code below achieves this
130  // effect by rounding down (floor) and adding 1 afterwards.
131  const int max_cells =
132  (O == GridOptions::Normal)
133  ? std::cbrt(particle_count)
134  : std::max(2, static_cast<int>(std::cbrt(particle_count)));
135 
136  // This normally equals 1/max_interaction_length, but if the number of cells
137  // is reduced (because of low density) then this value is smaller.
138  std::array<double, 3> index_factor = {1. / max_interaction_length,
139  1. / max_interaction_length,
140  1. / max_interaction_length};
141  for (std::size_t i = 0; i < number_of_cells_.size(); ++i) {
142  number_of_cells_[i] =
143  (strategy == CellSizeStrategy::Largest)
144  ? 2
145  : static_cast<int>(std::floor(length_[i] * index_factor[i])) +
146  // The last cell in each direction can be smaller than
147  // max_interaction_length. In that case periodic boundaries
148  // will not work correctly. Thus, we need to reduce the number
149  // of cells in that direction by one and make the last cell
150  // larger. This basically merges a smaller boundary cell into
151  // a full cell inside the grid. There's a ~0% chance that the
152  // given boundaries create an integral number of cells with
153  // length of max_interaction_length. Therefore, just make the
154  // default number of cells one less than for non-periodic
155  // boundaries.
156  (O == GridOptions::Normal ? 1 : 0);
157 
158  // std::nextafter implements a safety margin so that no valid position
159  // inside the grid can reference an out-of-bounds cell
160  if (number_of_cells_[i] > max_cells) {
161  number_of_cells_[i] = max_cells;
162  index_factor[i] = number_of_cells_[i] / length_[i];
163  while (index_factor[i] * length_[i] >= number_of_cells_[i]) {
164  index_factor[i] = std::nextafter(index_factor[i], 0.);
165  }
166  assert(index_factor[i] * length_[i] < number_of_cells_[i]);
167  } else if (O == GridOptions::PeriodicBoundaries) {
168  if (number_of_cells_[i] == 1) {
169  number_of_cells_[i] = 2;
170  }
171  index_factor[i] = number_of_cells_[i] / length_[i];
172  while (index_factor[i] * length_[i] >= number_of_cells_[i]) {
173  index_factor[i] = std::nextafter(index_factor[i], 0.);
174  }
175  assert(index_factor[i] * length_[i] < number_of_cells_[i]);
176  }
177  }
178 
179  const auto &log = logger<LogArea::Grid>();
180  if (O == GridOptions::Normal &&
181  all_of(number_of_cells_, [](SizeType n) { return n <= 2; })) {
182  // dilute limit:
183  // the grid would have <= 2x2x2 cells, meaning every particle has to be
184  // compared with every other particle anyway. Then we can just as well
185  // fall back to not using the grid at all
186  // For a grid with periodic boundaries the situation is different and we
187  // never want to have a grid smaller than 2x2x2.
188  log.debug("There would only be ", number_of_cells_,
189  " cells. Therefore the Grid falls back to a single cell / "
190  "particle list.");
191  number_of_cells_ = {1, 1, 1};
192  cells_.resize(1);
193  cells_.front().reserve(particles.size());
194  std::copy_if(particles.begin(), particles.end(),
195  std::back_inserter(cells_.front()),
196  [&](const ParticleData &p) {
197  return p.xsec_scaling_factor(timestep_duration) > 0.0;
198  }); // filter out the particles that can not interact
199  } else {
200  // construct a normal grid
201  log.debug("min: ", min_position, "\nlength: ", length_,
202  "\ncells: ", number_of_cells_, "\nindex_factor: ", index_factor);
203 
204  // After the grid parameters are determined, we can start placing the
205  // particles in cells.
206  cells_.resize(number_of_cells_[0] * number_of_cells_[1] *
207  number_of_cells_[2]);
208 
209  // Returns the one-dimensional cell-index from the position vector inside
210  // the grid.
211  // This simply calculates the distance to min_position and multiplies it
212  // with index_factor to determine the 3 x,y,z indexes to pass to make_index.
213  auto &&cell_index_for = [&](const ParticleData &p) {
214  return make_index(
215  std::floor((p.position()[1] - min_position[0]) * index_factor[0]),
216  std::floor((p.position()[2] - min_position[1]) * index_factor[1]),
217  std::floor((p.position()[3] - min_position[2]) * index_factor[2]));
218  };
219 
220  for (const auto &p : particles) {
221  if (p.xsec_scaling_factor(timestep_duration) > 0.0) {
222  const auto idx = cell_index_for(p);
223 #ifndef NDEBUG
224  if (idx >= SizeType(cells_.size())) {
225  log.fatal(
227  "\nan out-of-bounds access would be necessary for the "
228  "particle ",
229  p, "\nfor a grid with the following parameters:\nmin: ",
230  min_position, "\nlength: ", length_,
231  "\ncells: ", number_of_cells_, "\nindex_factor: ", index_factor,
232  "\ncells_.size: ", cells_.size(), "\nrequested index: ", idx);
233  throw std::runtime_error("out-of-bounds grid access on construction");
234  }
235 #endif
236  cells_[idx].push_back(p);
237  }
238  }
239  }
240 
241  log.debug(cells_);
242 }
With ghost cells for periodic boundaries.
Without ghost cells.
std::vector< ParticleList > cells_
The cell storage.
Definition: grid.h:160
#define source_location
Hackery that is required to output the location in the source code where the log statement occurs...
Definition: logging.h:246
std::array< int, 3 > number_of_cells_
The number of cells in x, y, and z direction.
Definition: grid.h:157
int SizeType
A type to store the sizes.
Definition: grid.h:53
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
Definition: algorithms.h:80
constexpr int p
Proton.
constexpr int n
Neutron.
Make cells as large as possible.
SizeType make_index(SizeType x, SizeType y, SizeType z) const
Definition: grid.cc:245
const std::array< double, 3 > length_
The 3 lengths of the complete grid. Used for periodic boundary wrapping.
Definition: grid.h:154

Here is the call graph for this function:

Member Function Documentation

template<GridOptions Options = GridOptions::Normal>
void smash::Grid< Options >::iterate_cells ( const std::function< void(const ParticleList &)> &  search_cell_callback,
const std::function< void(const ParticleList &, const ParticleList &)> &  neighbor_cell_callback 
) const

Iterates over all cells in the grid and calls the callback arguments with a search cell and 0 to 13 neighbor cells.

The neighbor cells are constructed like this:

  • one cell at x+1
  • three cells (x-1,x,x+1) at y+1
  • nine cells (x-1, y-1)...(x+1, y+1) at z+1
Parameters
[in]search_cell_callbackA callable called for/with every non-empty cell in the grid.
[in]neighbor_cell_callbackA callable called for/with every non-empty cell and adjacent cell combination. For a periodic grid, the first argument will be adjusted to wrap around the grid.
template<GridOptions Options>
Grid< Options >::SizeType smash::Grid< Options >::make_index ( SizeType  x,
SizeType  y,
SizeType  z 
) const
inlineprivate
Returns
the one-dimensional cell-index from the 3-dim index x, y, z.

Definition at line 245 of file grid.cc.

246  {
247  return (z * number_of_cells_[1] + y) * number_of_cells_[0] + x;
248 }
std::array< int, 3 > number_of_cells_
The number of cells in x, y, and z direction.
Definition: grid.h:157

Here is the caller graph for this function:

template<GridOptions Options = GridOptions::Normal>
SizeType smash::Grid< Options >::make_index ( std::array< SizeType, 3 >  idx) const
inlineprivate
Returns
the one-dimensional cell-index from the 3-dim index idx. This is a convenience overload for the above function.

Definition at line 149 of file grid.h.

149  {
150  return make_index(idx[0], idx[1], idx[2]);
151  }
SizeType make_index(SizeType x, SizeType y, SizeType z) const
Definition: grid.cc:245
template<>
void smash::Grid< GridOptions::Normal >::iterate_cells ( const std::function< void(const ParticleList &)> &  search_cell_callback,
const std::function< void(const ParticleList &, const ParticleList &)> &  neighbor_cell_callback 
) const

Specialization of iterate_cells.

Definition at line 258 of file grid.cc.

261  {
262  std::array<SizeType, 3> search_index;
263  SizeType &x = search_index[0];
264  SizeType &y = search_index[1];
265  SizeType &z = search_index[2];
266  SizeType search_cell_index = 0;
267  for (z = 0; z < number_of_cells_[2]; ++z) {
268  for (y = 0; y < number_of_cells_[1]; ++y) {
269  for (x = 0; x < number_of_cells_[0]; ++x, ++search_cell_index) {
270  assert(search_cell_index == make_index(search_index));
271  assert(search_cell_index >= 0);
272  assert(search_cell_index < SizeType(cells_.size()));
273  const ParticleList &search = cells_[search_cell_index];
274  search_cell_callback(search);
275 
276  const auto &dz_list = z == number_of_cells_[2] - 1 ? ZERO : ZERO_ONE;
277  const auto &dy_list = number_of_cells_[1] == 1
278  ? ZERO
279  : y == 0 ? ZERO_ONE
280  : y == number_of_cells_[1] - 1
283  const auto &dx_list = number_of_cells_[0] == 1
284  ? ZERO
285  : x == 0 ? ZERO_ONE
286  : x == number_of_cells_[0] - 1
289  for (SizeType dz : dz_list) {
290  for (SizeType dy : dy_list) {
291  for (SizeType dx : dx_list) {
292  const auto di = make_index(dx, dy, dz);
293  if (di > 0) {
294  neighbor_cell_callback(search, cells_[search_cell_index + di]);
295  }
296  }
297  }
298  }
299  }
300  }
301  }
302 }
static const std::initializer_list< GridBase::SizeType > ZERO_ONE
Definition: grid.cc:251
std::vector< ParticleList > cells_
The cell storage.
Definition: grid.h:160
std::array< int, 3 > number_of_cells_
The number of cells in x, y, and z direction.
Definition: grid.h:157
int SizeType
A type to store the sizes.
Definition: grid.h:53
static const std::initializer_list< GridBase::SizeType > MINUS_ONE_ZERO
Definition: grid.cc:252
static const std::initializer_list< GridBase::SizeType > MINUS_ONE_ZERO_ONE
Definition: grid.cc:253
static const std::initializer_list< GridBase::SizeType > ZERO
Definition: grid.cc:250
SizeType make_index(SizeType x, SizeType y, SizeType z) const
Definition: grid.cc:245

Here is the call graph for this function:

template<>
void smash::Grid< GridOptions::PeriodicBoundaries >::iterate_cells ( const std::function< void(const ParticleList &)> &  search_cell_callback,
const std::function< void(const ParticleList &, const ParticleList &)> &  neighbor_cell_callback 
) const

Specialization of iterate_cells.

Definition at line 324 of file grid.cc.

327  {
328  const auto &log = logger<LogArea::Grid>();
329 
330  std::array<SizeType, 3> search_index;
331  SizeType &x = search_index[0];
332  SizeType &y = search_index[1];
333  SizeType &z = search_index[2];
334  SizeType search_cell_index = 0;
335 
336  // defaults:
337  std::array<NeighborLookup, 2> dz_list;
338  std::array<NeighborLookup, 3> dy_list;
339  std::array<NeighborLookup, 3> dx_list;
340 
341  assert(number_of_cells_[2] >= 2);
342  assert(number_of_cells_[1] >= 2);
343  assert(number_of_cells_[0] >= 2);
344 
345  for (z = 0; z < number_of_cells_[2]; ++z) {
346  dz_list[0].index = z;
347  dz_list[1].index = z + 1;
348  if (dz_list[1].index == number_of_cells_[2]) {
349  dz_list[1].index = 0;
350  // last z in the loop, so no need to reset wrap again
351  dz_list[1].wrap = NeedsToWrap::MinusLength;
352  }
353  for (y = 0; y < number_of_cells_[1]; ++y) {
354  dy_list[0].index = y;
355  dy_list[1].index = y - 1;
356  dy_list[2].index = y + 1;
357  dy_list[2].wrap = NeedsToWrap::No;
358  if (y == 0) {
359  dy_list[1] = dy_list[2];
360  dy_list[2].index = number_of_cells_[1] - 1;
361  dy_list[2].wrap = NeedsToWrap::PlusLength;
362  } else if (dy_list[2].index == number_of_cells_[1]) {
363  dy_list[2].index = 0;
364  dy_list[2].wrap = NeedsToWrap::MinusLength;
365  }
366  for (x = 0; x < number_of_cells_[0]; ++x, ++search_cell_index) {
367  dx_list[0].index = x;
368  dx_list[1].index = x - 1;
369  dx_list[2].index = x + 1;
370  dx_list[2].wrap = NeedsToWrap::No;
371  if (x == 0) {
372  dx_list[1] = dx_list[2];
373  dx_list[2].index = number_of_cells_[0] - 1;
374  dx_list[2].wrap = NeedsToWrap::PlusLength;
375  } else if (dx_list[2].index == number_of_cells_[0]) {
376  dx_list[2].index = 0;
377  dx_list[2].wrap = NeedsToWrap::MinusLength;
378  }
379 
380  assert(search_cell_index == make_index(search_index));
381  assert(search_cell_index >= 0);
382  assert(search_cell_index < SizeType(cells_.size()));
383  ParticleList search = cells_[search_cell_index];
384  search_cell_callback(search);
385 
386  auto virtual_search_index = search_index;
387  ThreeVector wrap_vector = {}; // no change
388  auto current_wrap_vector = wrap_vector;
389 
390  for (const auto &dz : dz_list) {
391  if (dz.wrap == NeedsToWrap::MinusLength) {
392  // last dz in the loop, so no need to undo the wrap
393  wrap_vector[2] = -length_[2];
394  virtual_search_index[2] = -1;
395  }
396  for (const auto &dy : dy_list) {
397  // only the last dy in dy_list can wrap
398  if (dy.wrap == NeedsToWrap::MinusLength) {
399  wrap_vector[1] = -length_[1];
400  virtual_search_index[1] = -1;
401  } else if (dy.wrap == NeedsToWrap::PlusLength) {
402  wrap_vector[1] = length_[1];
403  virtual_search_index[1] = number_of_cells_[1];
404  }
405  for (const auto &dx : dx_list) {
406  // only the last dx in dx_list can wrap
407  if (dx.wrap == NeedsToWrap::MinusLength) {
408  wrap_vector[0] = -length_[0];
409  virtual_search_index[0] = -1;
410  } else if (dx.wrap == NeedsToWrap::PlusLength) {
411  wrap_vector[0] = length_[0];
412  virtual_search_index[0] = number_of_cells_[0];
413  }
414  assert(dx.index >= 0);
415  assert(dx.index < number_of_cells_[0]);
416  assert(dy.index >= 0);
417  assert(dy.index < number_of_cells_[1]);
418  assert(dz.index >= 0);
419  assert(dz.index < number_of_cells_[2]);
420  const auto neighbor_cell_index =
421  make_index(dx.index, dy.index, dz.index);
422  assert(neighbor_cell_index >= 0);
423  assert(neighbor_cell_index < SizeType(cells_.size()));
424  if (neighbor_cell_index <= make_index(virtual_search_index)) {
425  continue;
426  }
427 
428  if (wrap_vector != current_wrap_vector) {
429  log.debug("translating search cell by ",
430  wrap_vector - current_wrap_vector);
431  for_each(search, [&](ParticleData &p) {
432  p = p.translated(wrap_vector - current_wrap_vector);
433  });
434  current_wrap_vector = wrap_vector;
435  }
436  neighbor_cell_callback(search, cells_[neighbor_cell_index]);
437  }
438  virtual_search_index[0] = search_index[0];
439  wrap_vector[0] = 0;
440  }
441  virtual_search_index[1] = search_index[1];
442  wrap_vector[1] = 0;
443  }
444  }
445  }
446  }
447 }
std::vector< ParticleList > cells_
The cell storage.
Definition: grid.h:160
std::array< int, 3 > number_of_cells_
The number of cells in x, y, and z direction.
Definition: grid.h:157
int SizeType
A type to store the sizes.
Definition: grid.h:53
UnaryFunction for_each(Container &&c, UnaryFunction &&f)
Convenience wrapper for std::for_each that operates on a complete container.
Definition: algorithms.h:96
constexpr int p
Proton.
SizeType make_index(SizeType x, SizeType y, SizeType z) const
Definition: grid.cc:245
const std::array< double, 3 > length_
The 3 lengths of the complete grid. Used for periodic boundary wrapping.
Definition: grid.h:154

Here is the call graph for this function:

Member Data Documentation

template<GridOptions Options = GridOptions::Normal>
const std::array<double, 3> smash::Grid< Options >::length_
private

The 3 lengths of the complete grid. Used for periodic boundary wrapping.

Definition at line 154 of file grid.h.

template<GridOptions Options = GridOptions::Normal>
std::array<int, 3> smash::Grid< Options >::number_of_cells_
private

The number of cells in x, y, and z direction.

Definition at line 157 of file grid.h.

template<GridOptions Options = GridOptions::Normal>
std::vector<ParticleList> smash::Grid< Options >::cells_
private

The cell storage.

Definition at line 160 of file grid.h.


The documentation for this class was generated from the following files: