Version: SMASH-2.0
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...
 
double cell_volume () const
 
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...
 
double cell_volume_
 The volume of a single cell. 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

◆ Grid() [1/2]

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() [2/2]

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
Exceptions
runtime_errorif your box length is smaller than the grid length.

Definition at line 101 of file grid.cc.

105  : length_(min_and_length.second) {
106  const auto min_position = min_and_length.first;
107  const SizeType particle_count = particles.size();
108 
109  // very simple setup for non-periodic boundaries and largest cellsize strategy
110  if (O == GridOptions::Normal && strategy == CellSizeStrategy::Largest) {
111  number_of_cells_ = {1, 1, 1};
112  cell_volume_ = length_[0] * length_[1] * length_[2];
113  cells_.clear();
114  cells_.reserve(1);
115  cells_.emplace_back(particles.copy_to_vector());
116  return;
117  }
118 
119  // The number of cells is determined by the min and max coordinates where
120  // particles are positioned and the maximal interaction length (which equals
121  // the length of a cell).
122  // But don't let the number of cells exceed the actual number of particles.
123  // That would be overkill. Let max_cells³ ≤ particle_count (conversion to
124  // int truncates).
125  // Consider that particle placement into cells uses half-open intervals. Thus
126  // a cell includes particles in [0, a[. The next cell [a, 2a[. And so on. This
127  // is important for calculating the number of cells. If length * index_factor
128  // (equivalent to length / max_interaction_length) is integral, then
129  // length * index_factor + 1 determines the number of required cells. That's
130  // because the last cell will then store particles in the interval
131  // [length, length + max_interaction_length[. The code below achieves this
132  // effect by rounding down (floor) and adding 1 afterwards.
133  const int max_cells =
134  (O == GridOptions::Normal)
135  ? std::cbrt(particle_count)
136  : std::max(2, static_cast<int>(std::cbrt(particle_count)));
137 
138  std::string error_box_too_small =
139  "Input error: Your box is too small for the grid.\n"
140  "The minimal length of the box is given by: " +
141  std::to_string(2 * max_interaction_length) +
142  " fm with the given timestep size.\n"
143  "If you have large timesteps please reduce them.\n"
144  "A larger box or the use of testparticles also helps.\n"
145  "Please take a look at your config.";
146 
147  // This normally equals 1/max_interaction_length, but if the number of cells
148  // is reduced (because of low density) then this value is smaller.
149  std::array<double, 3> index_factor = {1. / max_interaction_length,
150  1. / max_interaction_length,
151  1. / max_interaction_length};
152  for (std::size_t i = 0; i < number_of_cells_.size(); ++i) {
153  number_of_cells_[i] =
154  (strategy == CellSizeStrategy::Largest)
155  ? 2
156  : static_cast<int>(std::floor(length_[i] * index_factor[i])) +
157  // The last cell in each direction can be smaller than
158  // max_interaction_length. In that case periodic boundaries
159  // will not work correctly. Thus, we need to reduce the number
160  // of cells in that direction by one and make the last cell
161  // larger. This basically merges a smaller boundary cell into
162  // a full cell inside the grid. There's a ~0% chance that the
163  // given boundaries create an integral number of cells with
164  // length of max_interaction_length. Therefore, just make the
165  // default number of cells one less than for non-periodic
166  // boundaries.
167  (O == GridOptions::Normal ? 1 : 0);
168  // Only in the case of periodic boundaries (i.e. GridOptions != Normal) the
169  // number of cells can be zero.
170  if (number_of_cells_[i] == 0) {
171  // The minimal cell length exceeds the length of the box.
172  throw std::runtime_error(error_box_too_small);
173  }
174  // std::nextafter implements a safety margin so that no valid position
175  // inside the grid can reference an out-of-bounds cell
176  if (number_of_cells_[i] > max_cells) {
177  number_of_cells_[i] = max_cells;
178  index_factor[i] = number_of_cells_[i] / length_[i];
179  while (index_factor[i] * length_[i] >= number_of_cells_[i]) {
180  index_factor[i] = std::nextafter(index_factor[i], 0.);
181  }
182  assert(index_factor[i] * length_[i] < number_of_cells_[i]);
183  } else if (O == GridOptions::PeriodicBoundaries) {
184  if (number_of_cells_[i] == 1) {
185  number_of_cells_[i] = 2;
186  }
187  index_factor[i] = number_of_cells_[i] / length_[i];
188  while (index_factor[i] * length_[i] >= number_of_cells_[i]) {
189  index_factor[i] = std::nextafter(index_factor[i], 0.);
190  }
191  assert(index_factor[i] * length_[i] < number_of_cells_[i]);
192  // Verify that cell length did not become smaller than
193  // the max. interaction length by increasing the number of cells
194  if (1. / index_factor[i] <= std::nextafter(max_interaction_length, 0.)) {
195  // The minimal cell length exceeds
196  // the length of a grid cell in the box.
197  throw std::runtime_error(error_box_too_small);
198  }
199  }
200  }
201 
203  (length_[1] / number_of_cells_[1]) *
204  (length_[2] / number_of_cells_[2]);
205 
206  if (O == GridOptions::Normal &&
207  all_of(number_of_cells_, [](SizeType n) { return n <= 2; })) {
208  // dilute limit:
209  // the grid would have <= 2x2x2 cells, meaning every particle has to be
210  // compared with every other particle anyway. Then we can just as well
211  // fall back to not using the grid at all
212  // For a grid with periodic boundaries the situation is different and we
213  // never want to have a grid smaller than 2x2x2.
214  logg[LGrid].debug(
215  "There would only be ", number_of_cells_,
216  " cells. Therefore the Grid falls back to a single cell / "
217  "particle list.");
218  number_of_cells_ = {1, 1, 1};
219  cell_volume_ = length_[0] * length_[1] * length_[2];
220  cells_.resize(1);
221  cells_.front().reserve(particles.size());
222  std::copy_if(particles.begin(), particles.end(),
223  std::back_inserter(cells_.front()),
224  [&](const ParticleData &p) {
225  return p.xsec_scaling_factor(timestep_duration) > 0.0;
226  }); // filter out the particles that can not interact
227  } else {
228  // construct a normal grid
229  logg[LGrid].debug("min: ", min_position, "\nlength: ", length_,
230  "\ncell_volume: ", cell_volume_,
231  "\ncells: ", number_of_cells_,
232  "\nindex_factor: ", index_factor);
233 
234  // After the grid parameters are determined, we can start placing the
235  // particles in cells.
236  cells_.resize(number_of_cells_[0] * number_of_cells_[1] *
237  number_of_cells_[2]);
238 
239  // Returns the one-dimensional cell-index from the position vector inside
240  // the grid.
241  // This simply calculates the distance to min_position and multiplies it
242  // with index_factor to determine the 3 x,y,z indexes to pass to make_index.
243  auto &&cell_index_for = [&](const ParticleData &p) {
244  return make_index(
245  std::floor((p.position()[1] - min_position[0]) * index_factor[0]),
246  std::floor((p.position()[2] - min_position[1]) * index_factor[1]),
247  std::floor((p.position()[3] - min_position[2]) * index_factor[2]));
248  };
249 
250  for (const auto &p : particles) {
251  if (p.xsec_scaling_factor(timestep_duration) > 0.0) {
252  const auto idx = cell_index_for(p);
253 #ifndef NDEBUG
254  if (idx >= SizeType(cells_.size())) {
255  logg[LGrid].fatal(
257  "\nan out-of-bounds access would be necessary for the "
258  "particle ",
259  p, "\nfor a grid with the following parameters:\nmin: ",
260  min_position, "\nlength: ", length_,
261  "\ncells: ", number_of_cells_, "\nindex_factor: ", index_factor,
262  "\ncells_.size: ", cells_.size(), "\nrequested index: ", idx);
263  throw std::runtime_error("out-of-bounds grid access on construction");
264  }
265 #endif
266  cells_[idx].push_back(p);
267  }
268  }
269  }
270 
271  logg[LGrid].debug(cells_);
272 }

Member Function Documentation

◆ iterate_cells() [1/3]

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.

◆ cell_volume()

template<GridOptions Options = GridOptions::Normal>
double smash::Grid< Options >::cell_volume ( ) const
inline
Returns
the volume of a single grid cell

Definition at line 142 of file grid.h.

142 { return cell_volume_; }

◆ make_index() [1/2]

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 275 of file grid.cc.

276  {
277  return (z * number_of_cells_[1] + y) * number_of_cells_[0] + x;
278 }
Here is the caller graph for this function:

◆ make_index() [2/2]

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 155 of file grid.h.

155  {
156  return make_index(idx[0], idx[1], idx[2]);
157  }

◆ iterate_cells() [2/3]

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 288 of file grid.cc.

291  {
292  std::array<SizeType, 3> search_index;
293  SizeType &x = search_index[0];
294  SizeType &y = search_index[1];
295  SizeType &z = search_index[2];
296  SizeType search_cell_index = 0;
297  for (z = 0; z < number_of_cells_[2]; ++z) {
298  for (y = 0; y < number_of_cells_[1]; ++y) {
299  for (x = 0; x < number_of_cells_[0]; ++x, ++search_cell_index) {
300  assert(search_cell_index == make_index(search_index));
301  assert(search_cell_index >= 0);
302  assert(search_cell_index < SizeType(cells_.size()));
303  const ParticleList &search = cells_[search_cell_index];
304  search_cell_callback(search);
305 
306  const auto &dz_list = z == number_of_cells_[2] - 1 ? ZERO : ZERO_ONE;
307  const auto &dy_list = number_of_cells_[1] == 1
308  ? ZERO
309  : y == 0 ? ZERO_ONE
310  : y == number_of_cells_[1] - 1
313  const auto &dx_list = number_of_cells_[0] == 1
314  ? ZERO
315  : x == 0 ? ZERO_ONE
316  : x == number_of_cells_[0] - 1
319  for (SizeType dz : dz_list) {
320  for (SizeType dy : dy_list) {
321  for (SizeType dx : dx_list) {
322  const auto di = make_index(dx, dy, dz);
323  if (di > 0) {
324  neighbor_cell_callback(search, cells_[search_cell_index + di]);
325  }
326  }
327  }
328  }
329  }
330  }
331  }
332 }

◆ iterate_cells() [3/3]

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 354 of file grid.cc.

357  {
358  std::array<SizeType, 3> search_index;
359  SizeType &x = search_index[0];
360  SizeType &y = search_index[1];
361  SizeType &z = search_index[2];
362  SizeType search_cell_index = 0;
363 
364  // defaults:
365  std::array<NeighborLookup, 2> dz_list;
366  std::array<NeighborLookup, 3> dy_list;
367  std::array<NeighborLookup, 3> dx_list;
368 
369  assert(number_of_cells_[2] >= 2);
370  assert(number_of_cells_[1] >= 2);
371  assert(number_of_cells_[0] >= 2);
372 
373  for (z = 0; z < number_of_cells_[2]; ++z) {
374  dz_list[0].index = z;
375  dz_list[1].index = z + 1;
376  if (dz_list[1].index == number_of_cells_[2]) {
377  dz_list[1].index = 0;
378  // last z in the loop, so no need to reset wrap again
379  dz_list[1].wrap = NeedsToWrap::MinusLength;
380  }
381  for (y = 0; y < number_of_cells_[1]; ++y) {
382  dy_list[0].index = y;
383  dy_list[1].index = y - 1;
384  dy_list[2].index = y + 1;
385  dy_list[2].wrap = NeedsToWrap::No;
386  if (y == 0) {
387  dy_list[1] = dy_list[2];
388  dy_list[2].index = number_of_cells_[1] - 1;
389  dy_list[2].wrap = NeedsToWrap::PlusLength;
390  } else if (dy_list[2].index == number_of_cells_[1]) {
391  dy_list[2].index = 0;
392  dy_list[2].wrap = NeedsToWrap::MinusLength;
393  }
394  for (x = 0; x < number_of_cells_[0]; ++x, ++search_cell_index) {
395  dx_list[0].index = x;
396  dx_list[1].index = x - 1;
397  dx_list[2].index = x + 1;
398  dx_list[2].wrap = NeedsToWrap::No;
399  if (x == 0) {
400  dx_list[1] = dx_list[2];
401  dx_list[2].index = number_of_cells_[0] - 1;
402  dx_list[2].wrap = NeedsToWrap::PlusLength;
403  } else if (dx_list[2].index == number_of_cells_[0]) {
404  dx_list[2].index = 0;
405  dx_list[2].wrap = NeedsToWrap::MinusLength;
406  }
407 
408  assert(search_cell_index == make_index(search_index));
409  assert(search_cell_index >= 0);
410  assert(search_cell_index < SizeType(cells_.size()));
411  ParticleList search = cells_[search_cell_index];
412  search_cell_callback(search);
413 
414  auto virtual_search_index = search_index;
415  ThreeVector wrap_vector = {}; // no change
416  auto current_wrap_vector = wrap_vector;
417 
418  for (const auto &dz : dz_list) {
419  if (dz.wrap == NeedsToWrap::MinusLength) {
420  // last dz in the loop, so no need to undo the wrap
421  wrap_vector[2] = -length_[2];
422  virtual_search_index[2] = -1;
423  }
424  for (const auto &dy : dy_list) {
425  // only the last dy in dy_list can wrap
426  if (dy.wrap == NeedsToWrap::MinusLength) {
427  wrap_vector[1] = -length_[1];
428  virtual_search_index[1] = -1;
429  } else if (dy.wrap == NeedsToWrap::PlusLength) {
430  wrap_vector[1] = length_[1];
431  virtual_search_index[1] = number_of_cells_[1];
432  }
433  for (const auto &dx : dx_list) {
434  // only the last dx in dx_list can wrap
435  if (dx.wrap == NeedsToWrap::MinusLength) {
436  wrap_vector[0] = -length_[0];
437  virtual_search_index[0] = -1;
438  } else if (dx.wrap == NeedsToWrap::PlusLength) {
439  wrap_vector[0] = length_[0];
440  virtual_search_index[0] = number_of_cells_[0];
441  }
442  assert(dx.index >= 0);
443  assert(dx.index < number_of_cells_[0]);
444  assert(dy.index >= 0);
445  assert(dy.index < number_of_cells_[1]);
446  assert(dz.index >= 0);
447  assert(dz.index < number_of_cells_[2]);
448  const auto neighbor_cell_index =
449  make_index(dx.index, dy.index, dz.index);
450  assert(neighbor_cell_index >= 0);
451  assert(neighbor_cell_index < SizeType(cells_.size()));
452  if (neighbor_cell_index <= make_index(virtual_search_index)) {
453  continue;
454  }
455 
456  if (wrap_vector != current_wrap_vector) {
457  logg[LGrid].debug("translating search cell by ",
458  wrap_vector - current_wrap_vector);
459  for_each(search, [&](ParticleData &p) {
460  p = p.translated(wrap_vector - current_wrap_vector);
461  });
462  current_wrap_vector = wrap_vector;
463  }
464  neighbor_cell_callback(search, cells_[neighbor_cell_index]);
465  }
466  virtual_search_index[0] = search_index[0];
467  wrap_vector[0] = 0;
468  }
469  virtual_search_index[1] = search_index[1];
470  wrap_vector[1] = 0;
471  }
472  }
473  }
474  }
475 }

Member Data Documentation

◆ length_

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 160 of file grid.h.

◆ cell_volume_

template<GridOptions Options = GridOptions::Normal>
double smash::Grid< Options >::cell_volume_
private

The volume of a single cell.

Definition at line 163 of file grid.h.

◆ number_of_cells_

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 166 of file grid.h.

◆ cells_

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

The cell storage.

Definition at line 169 of file grid.h.


The documentation for this class was generated from the following files:
smash::MINUS_ONE_ZERO_ONE
static const std::initializer_list< GridBase::SizeType > MINUS_ONE_ZERO_ONE
Definition: grid.cc:283
smash::for_each
UnaryFunction for_each(Container &&c, UnaryFunction &&f)
Convenience wrapper for std::for_each that operates on a complete container.
Definition: algorithms.h:96
smash::MINUS_ONE_ZERO
static const std::initializer_list< GridBase::SizeType > MINUS_ONE_ZERO
Definition: grid.cc:282
smash::logg
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:39
smash::all_of
bool all_of(Container &&c, UnaryPredicate &&p)
Convenience wrapper for std::all_of that operates on a complete container.
Definition: algorithms.h:80
smash::GridOptions::Normal
Without ghost cells.
source_location
#define source_location
Hackery that is required to output the location in the source code where the log statement occurs.
Definition: logging.h:243
smash::Grid::number_of_cells_
std::array< int, 3 > number_of_cells_
The number of cells in x, y, and z direction.
Definition: grid.h:166
smash::LGrid
static constexpr int LGrid
Definition: grid.cc:66
smash::ZERO
static const std::initializer_list< GridBase::SizeType > ZERO
Definition: grid.cc:280
smash::NeedsToWrap::PlusLength
smash::Grid::cells_
std::vector< ParticleList > cells_
The cell storage.
Definition: grid.h:169
smash::CellSizeStrategy::Largest
Make cells as large as possible.
smash::Grid::length_
const std::array< double, 3 > length_
The 3 lengths of the complete grid. Used for periodic boundary wrapping.
Definition: grid.h:160
smash::GridBase::SizeType
int SizeType
A type to store the sizes.
Definition: grid.h:53
smash::Grid::cell_volume_
double cell_volume_
The volume of a single cell.
Definition: grid.h:163
smash::ZERO_ONE
static const std::initializer_list< GridBase::SizeType > ZERO_ONE
Definition: grid.cc:281
smash::Grid::Grid
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
smash::GridOptions::PeriodicBoundaries
With ghost cells for periodic boundaries.
smash::pdg::p
constexpr int p
Proton.
Definition: pdgcode_constants.h:28
smash::pdg::n
constexpr int n
Neutron.
Definition: pdgcode_constants.h:30
smash::GridBase::find_min_and_length
static std::pair< std::array< double, 3 >, std::array< double, 3 > > find_min_and_length(const Particles &particles)
Definition: grid.cc:72
smash::NeedsToWrap::No
smash::Grid::make_index
SizeType make_index(SizeType x, SizeType y, SizeType z) const
Definition: grid.cc:275
smash::NeedsToWrap::MinusLength