Version: SMASH-3.4
lattice.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2015-2021,2023-2024,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_LATTICE_H_
11 #define SRC_INCLUDE_SMASH_LATTICE_H_
12 
13 #include <array>
14 #include <cstring>
15 #include <functional>
16 #include <optional>
17 #include <utility>
18 #include <vector>
19 
20 #include "forwarddeclarations.h"
21 #include "fourvector.h"
22 #include "logging.h"
23 #include "numeric_cast.h"
24 #include "numerics.h"
25 
26 namespace smash {
27 static constexpr int LLattice = LogArea::Lattice::id;
28 
29 /**
30  * Enumerator option for lattice updates.
31  *
32  * Updating the lattice is a costly operation and should be performed only if
33  * necessary. Possible needs are:
34  *
35  * - output: then it is enough to update lattice just before output,
36  * - physics: update every time step is unavoidable.
37  */
38 enum class LatticeUpdate {
39  AtOutput = 0,
40  EveryTimestep = 1,
42 };
43 
44 /**
45  * A container class to hold all the arrays on the lattice and access them.
46  * \tparam T The type of the contained values.
47  */
48 template <typename T>
50  public:
51  /**
52  * Rectangular lattice constructor.
53  *
54  * \param[in] l 3-dimensional array (lx,ly,lz) indicates the size of
55  * the lattice in x, y, z directions respectively [fm].
56  * \param[in] n 3-dimensional array (nx,ny,nz) indicates the number of
57  * cells of the lattice in x, y, z directions respectively.
58  * Each cell in the lattice is labeled by three integers i, j, k
59  * where \f$i\in[0, nx-1]\f$, \f$j\in[0, ny-1]\f$,
60  * \f$k\in[0, nz-1]\f$. The sizes of each cell are given by
61  * lx/nx, ly/ny, lz/nz in x,y,z directions respectively.
62  * \param[in] orig A 3-dimensional array indicating the coordinates of the
63  * origin [fm].
64  * \param[in] per Boolean indicating whether a periodic boundary condition
65  * is applied.
66  * \param[in] upd Enum indicating how frequently the lattice is updated.
67  */
68  RectangularLattice(const std::array<double, 3>& l,
69  const std::array<int, 3>& n,
70  const std::array<double, 3>& orig, bool per,
71  const LatticeUpdate upd)
72  : lattice_sizes_(l),
73  n_cells_(n),
74  cell_sizes_{l[0] / n[0], l[1] / n[1], l[2] / n[2]},
76  origin_(orig),
77  periodic_(per),
78  when_update_(upd) {
79  lattice_.resize(n_cells_[0] * n_cells_[1] * n_cells_[2]);
80  logg[LLattice].debug(
81  "Rectangular lattice created: sizes[fm] = (", lattice_sizes_[0], ",",
82  lattice_sizes_[1], ",", lattice_sizes_[2], "), dims = (", n_cells_[0],
83  ",", n_cells_[1], ",", n_cells_[2], "), origin = (", origin_[0], ",",
84  origin_[1], ",", origin_[2], "), periodic: ", periodic_);
85  if (n_cells_[0] < 1 || n_cells_[1] < 1 || n_cells_[2] < 1 ||
86  lattice_sizes_[0] < 0.0 || lattice_sizes_[1] < 0.0 ||
87  lattice_sizes_[2] < 0.0) {
88  throw std::invalid_argument(
89  "Lattice sizes should be positive, "
90  "number of lattice cells should be > 0.");
91  }
92  }
93 
94  /// Copy-constructor
96  : lattice_(rl.lattice_),
98  n_cells_(rl.n_cells_),
101  origin_(rl.origin_),
102  periodic_(rl.periodic_),
104 
105  /// Sets all values on lattice to zeros.
106  void reset() { std::fill(lattice_.begin(), lattice_.end(), T()); }
107 
108  /**
109  * Checks if 3D index is out of lattice bounds.
110  *
111  * \param[in] ix The index of the cell in x direction.
112  * \param[in] iy The index of the cell in y direction.
113  * \param[in] iz The index of the cell in z direction.
114  * \return Whether the cell is out of the lattice.
115  */
116  inline bool out_of_bounds(int ix, int iy, int iz) const {
117  // clang-format off
118  return !periodic_ &&
119  (ix < 0 || ix >= n_cells_[0] ||
120  iy < 0 || iy >= n_cells_[1] ||
121  iz < 0 || iz >= n_cells_[2]);
122  // clang-format on
123  }
124 
125  /**
126  * Find the coordinates of a given cell.
127  *
128  * \param[in] ix The index of the cell in x direction.
129  * \param[in] iy The index of the cell in y direction.
130  * \param[in] iz The index of the cell in z direction.
131  * \return Coordinates of the center of the given cell [fm].
132  */
133  inline ThreeVector cell_center(int ix, int iy, int iz) const {
134  return ThreeVector(origin_[0] + cell_sizes_[0] * (ix + 0.5),
135  origin_[1] + cell_sizes_[1] * (iy + 0.5),
136  origin_[2] + cell_sizes_[2] * (iz + 0.5));
137  }
138 
139  /**
140  * Find the coordinate of cell center given the 1d index of the cell.
141  *
142  * \param[in] index 1-dimensional index of the given cell. It can be
143  * related to a 3-dimensional one by
144  * index = ix + nx (iy + iz * ny).
145  * \return Coordinates of the center of the given cell [fm].
146  */
147  inline ThreeVector cell_center(int index) const {
148  const int ix = index % n_cells_[0];
149  index = index / n_cells_[0];
150  const int iy = index % n_cells_[1];
151  const int iz = index / n_cells_[1];
152  return cell_center(ix, iy, iz);
153  }
154 
155  /// \return Lengths of the lattice in x, y, z directions.
156  const std::array<double, 3>& lattice_sizes() const { return lattice_sizes_; }
157 
158  /// \return Number of cells in x, y, z directions.
159  const std::array<int, 3>& n_cells() const { return n_cells_; }
160 
161  /// \return Lengths of one cell in x, y, z directions.
162  const std::array<double, 3>& cell_sizes() const { return cell_sizes_; }
163 
164  /// \return Lattice origin: left, down, near corner coordinates.
165  const std::array<double, 3>& origin() const { return origin_; }
166 
167  /// \return If lattice is periodic or not.
168  bool periodic() const { return periodic_; }
169 
170  /// \return The enum, which tells at which time lattice needs to be updated.
172 
173  /// Iterator of lattice.
174  using iterator = typename std::vector<T>::iterator;
175  /// Const interator of lattice.
176  using const_iterator = typename std::vector<T>::const_iterator;
177  /// \return First element of lattice.
178  iterator begin() { return lattice_.begin(); }
179  /// \return First element of lattice (const).
180  const_iterator begin() const { return lattice_.begin(); }
181  /// \return Last element of lattice.
182  iterator end() { return lattice_.end(); }
183  /// \return Last element of lattice (const).
184  const_iterator end() const { return lattice_.end(); }
185  /// \return ith element of lattice.
186  T& operator[](std::size_t i) { return lattice_[i]; }
187  /// \return ith element of lattice (const).
188  const T& operator[](std::size_t i) const { return lattice_[i]; }
189  /// \return Size of lattice.
190  std::size_t size() const { return lattice_.size(); }
191 
192  /**
193  * Overwrite with a template value T at a given node
194  */
195  void assign_value(int lattice_index, T value) {
196  lattice_[lattice_index] = value;
197  }
198 
199  /**
200  * Return the index of a given cell.
201  *
202  * \param[in] ix index of a cell in the x-direction
203  * \param[in] iy index of a cell in the y-direction
204  * \param[in] iz index of a cell in the z-direction
205  * \return index of cell
206  */
207  int index1d(int ix, int iy, int iz) {
208  assert(ix < n_cells_[0]);
209  assert(iy < n_cells_[1]);
210  assert(iz < n_cells_[2]);
211  return (ix + n_cells_[0] * (iy + iz * n_cells_[1]));
212  }
213  /**
214  * Given the indices of a cell in the x, y, and z directions, return index of
215  * the nearest cell in the -x direction ("left").
216  *
217  * \param[in] ix index of a cell in the x-direction
218  * \param[in] iy index of a cell in the y-direction
219  * \param[in] iz index of a cell in the z-direction
220  * \return index of the nearest cell in the "left" direction (ix-1) with
221  * respect to the current cell
222  */
223  int index_left(int ix, int iy, int iz) {
224  if (unlikely(ix == 0)) {
225  return periodic_ ? index1d(n_cells_[0] - 1, iy, iz) : index1d(ix, iy, iz);
226  } else {
227  return index1d(ix - 1, iy, iz);
228  }
229  }
230  /**
231  * Given the indices of a cell in the x, y, and z directions, return index of
232  * the nearest cell in the +x direction ("right").
233  *
234  * \param[in] ix index of a cell in the x-direction
235  * \param[in] iy index of a cell in the y-direction
236  * \param[in] iz index of a cell in the z-direction
237  * \return index of the nearest cell in the "right" direction (ix+1) with
238  * respect to the current cell
239  */
240  int index_right(int ix, int iy, int iz) {
241  if (unlikely(ix == (n_cells_[0] - 1))) {
242  return periodic_ ? index1d(0, iy, iz) : index1d(ix, iy, iz);
243  } else {
244  return index1d(ix + 1, iy, iz);
245  }
246  }
247  /**
248  * Given the indices of a cell in the x, y, and z directions, return index of
249  * the nearest cell in the -y direction ("down").
250  *
251  * \param[in] ix index of a cell in the x-direction
252  * \param[in] iy index of a cell in the y-direction
253  * \param[in] iz index of a cell in the z-direction
254  * \return index of the nearest cell in the "down" direction (iy-1) with
255  * respect to the current cell
256  */
257  int index_down(int ix, int iy, int iz) {
258  if (unlikely(iy == 0)) {
259  return periodic_ ? index1d(ix, n_cells_[1] - 1, iz) : index1d(ix, iy, iz);
260  } else {
261  return index1d(ix, iy - 1, iz);
262  }
263  }
264  /**
265  * Given the indices of a cell in the x, y, and z directions, return index of
266  * the nearest cell in the +y direction ("up").
267  *
268  * \param[in] ix index of a cell in the x-direction
269  * \param[in] iy index of a cell in the y-direction
270  * \param[in] iz index of a cell in the z-direction
271  * \return index of the nearest cell in the "up" direction (iy+1) with
272  * respect to the current cell
273  */
274  int index_up(int ix, int iy, int iz) {
275  if (unlikely(iy == (n_cells_[1] - 1))) {
276  return periodic_ ? index1d(ix, 0, iz) : index1d(ix, iy, iz);
277  } else {
278  return index1d(ix, iy + 1, iz);
279  }
280  }
281  /**
282  * Given the indices of a cell in the x, y, and z directions, return index of
283  * the nearest cell in the -z direction ("backward").
284  *
285  * \param[in] ix index of a cell in the x-direction
286  * \param[in] iy index of a cell in the y-direction
287  * \param[in] iz index of a cell in the z-direction
288  * \return index of the nearest cell in the "backward" direction (iz-1) with
289  * respect to the current cell
290  */
291  int index_backward(int ix, int iy, int iz) {
292  if (unlikely(iz == 0)) {
293  return periodic_ ? index1d(ix, iy, n_cells_[2] - 1) : index1d(ix, iy, iz);
294  } else {
295  return index1d(ix, iy, iz - 1);
296  }
297  }
298  /**
299  * Given the indices of a cell in the x, y, and z directions, return index of
300  * the nearest cell in the +z direction ("forward").
301  *
302  * \param[in] ix index of a cell in the x-direction
303  * \param[in] iy index of a cell in the y-direction
304  * \param[in] iz index of a cell in the z-direction
305  * \return index of the nearest cell in the "forward" direction (iz+1) with
306  * respect to the current cell
307  */
308  int index_forward(int ix, int iy, int iz) {
309  if (unlikely(iz == (n_cells_[2] - 1))) {
310  return periodic_ ? index1d(ix, iy, 0) : index1d(ix, iy, iz);
311  } else {
312  return index1d(ix, iy, iz + 1);
313  }
314  }
315 
316  /**
317  * Compute a gradient on a lattice of doubles via the finite difference method
318  *
319  * return a lattice of ThreeVectors which are gradients of the values on the
320  * original lattice
321  */
323  RectangularLattice<ThreeVector>& grad_lat) const {
324  if (n_cells_[0] < 2 || n_cells_[1] < 2 || n_cells_[2] < 2) {
325  // Gradient calculation is impossible
326  throw std::runtime_error(
327  "Lattice is too small for gradient calculation"
328  " (should be at least 2x2x2)");
329  }
330  if (!identical_to_lattice(&grad_lat)) {
331  // Lattice for gradient should have identical origin/dims/periodicity
332  throw std::invalid_argument(
333  "Lattice for gradient should have the"
334  " same origin/dims/periodicity as the original one.");
335  }
336  const double inv_2dx = 0.5 / cell_sizes_[0];
337  const double inv_2dy = 0.5 / cell_sizes_[1];
338  const double inv_2dz = 0.5 / cell_sizes_[2];
339  const int dix = 1;
340  const int diy = n_cells_[0];
341  const int diz = n_cells_[0] * n_cells_[1];
342  const int d = diz * n_cells_[2];
343 
344  for (int iz = 0; iz < n_cells_[2]; iz++) {
345  const int z_offset = diz * iz;
346  for (int iy = 0; iy < n_cells_[1]; iy++) {
347  const int y_offset = diy * iy + z_offset;
348  for (int ix = 0; ix < n_cells_[0]; ix++) {
349  const int index = ix + y_offset;
350  if (unlikely(ix == 0)) {
351  (grad_lat)[index].set_x1(
352  periodic_
353  ? (lattice_[index + dix] - lattice_[index + diy - dix]) *
354  inv_2dx
355  : (lattice_[index + dix] - lattice_[index]) * 2.0 *
356  inv_2dx);
357  } else if (unlikely(ix == n_cells_[0] - 1)) {
358  (grad_lat)[index].set_x1(
359  periodic_
360  ? (lattice_[index - diy + dix] - lattice_[index - dix]) *
361  inv_2dx
362  : (lattice_[index] - lattice_[index - dix]) * 2.0 *
363  inv_2dx);
364  } else {
365  (grad_lat)[index].set_x1(
366  (lattice_[index + dix] - lattice_[index - dix]) * inv_2dx);
367  }
368 
369  if (unlikely(iy == 0)) {
370  (grad_lat)[index].set_x2(
371  periodic_
372  ? (lattice_[index + diy] - lattice_[index + diz - diy]) *
373  inv_2dy
374  : (lattice_[index + diy] - lattice_[index]) * 2.0 *
375  inv_2dy);
376  } else if (unlikely(iy == n_cells_[1] - 1)) {
377  (grad_lat)[index].set_x2(
378  periodic_
379  ? (lattice_[index - diz + diy] - lattice_[index - diy]) *
380  inv_2dy
381  : (lattice_[index] - lattice_[index - diy]) * 2.0 *
382  inv_2dy);
383  } else {
384  (grad_lat)[index].set_x2(
385  (lattice_[index + diy] - lattice_[index - diy]) * inv_2dy);
386  }
387 
388  if (unlikely(iz == 0)) {
389  (grad_lat)[index].set_x3(
390  periodic_
391  ? (lattice_[index + diz] - lattice_[index + d - diz]) *
392  inv_2dz
393  : (lattice_[index + diz] - lattice_[index]) * 2.0 *
394  inv_2dz);
395  } else if (unlikely(iz == n_cells_[2] - 1)) {
396  (grad_lat)[index].set_x3(
397  periodic_
398  ? (lattice_[index - d + diz] - lattice_[index - diz]) *
399  inv_2dz
400  : (lattice_[index] - lattice_[index - diz]) * 2.0 *
401  inv_2dz);
402  } else {
403  (grad_lat)[index].set_x3(
404  (lattice_[index + diz] - lattice_[index - diz]) * inv_2dz);
405  }
406  }
407  }
408  }
409  }
410 
411  /**
412  * Compute a fourgradient on a lattice of FourVectors jmu via the finite
413  * difference method.
414  *
415  * \param[in] old_lat the lattice of FourVectors jmu at a previous time step
416  * \param[in] time_step the used time step, needed for the time derivative
417  * \param[out] grad_lat a lattice of 4-arrays of 4-vectors with the following
418  * structure: [djmu_dt, djmu_dx, djmu_dy, djmu_dz]
419  */
421  RectangularLattice<FourVector>& old_lat, double time_step,
422  RectangularLattice<std::array<FourVector, 4>>& grad_lat) const {
423  if (n_cells_[0] < 2 || n_cells_[1] < 2 || n_cells_[2] < 2) {
424  // Gradient calculation is impossible
425  throw std::runtime_error(
426  "Lattice is too small for gradient calculation"
427  " (should be at least 2x2x2)");
428  }
429  if (!identical_to_lattice(&grad_lat)) {
430  // Lattice for gradient should have identical origin/dims/periodicity
431  throw std::invalid_argument(
432  "Lattice for gradient should have the"
433  " same origin/dims/periodicity as the original one.");
434  }
435  const double inv_2dx = 0.5 / cell_sizes_[0];
436  const double inv_2dy = 0.5 / cell_sizes_[1];
437  const double inv_2dz = 0.5 / cell_sizes_[2];
438  const int dix = 1;
439  const int diy = n_cells_[0];
440  const int diz = n_cells_[0] * n_cells_[1];
441  const int d = diz * n_cells_[2];
442 
443  for (int iz = 0; iz < n_cells_[2]; iz++) {
444  const int z_offset = diz * iz;
445  for (int iy = 0; iy < n_cells_[1]; iy++) {
446  const int y_offset = diy * iy + z_offset;
447  for (int ix = 0; ix < n_cells_[0]; ix++) {
448  const int index = ix + y_offset;
449 
450  // auxiliary vectors used for the calculation of gradients
451  FourVector grad_t_jmu(0.0, 0.0, 0.0, 0.0);
452  FourVector grad_x_jmu(0.0, 0.0, 0.0, 0.0);
453  FourVector grad_y_jmu(0.0, 0.0, 0.0, 0.0);
454  FourVector grad_z_jmu(0.0, 0.0, 0.0, 0.0);
455  // t direction
456  grad_t_jmu = (lattice_[index] - (old_lat)[index]) * (1.0 / time_step);
457  // x direction
458  if (unlikely(ix == 0)) {
459  grad_x_jmu =
460  periodic_
461  ? (lattice_[index + dix] - lattice_[index + diy - dix]) *
462  inv_2dx
463  : (lattice_[index + dix] - lattice_[index]) * 2.0 * inv_2dx;
464  } else if (unlikely(ix == n_cells_[0] - 1)) {
465  grad_x_jmu =
466  periodic_
467  ? (lattice_[index - diy + dix] - lattice_[index - dix]) *
468  inv_2dx
469  : (lattice_[index] - lattice_[index - dix]) * 2.0 * inv_2dx;
470  } else {
471  grad_x_jmu =
472  (lattice_[index + dix] - lattice_[index - dix]) * inv_2dx;
473  }
474  // y direction
475  if (unlikely(iy == 0)) {
476  grad_y_jmu =
477  periodic_
478  ? (lattice_[index + diy] - lattice_[index + diz - diy]) *
479  inv_2dy
480  : (lattice_[index + diy] - lattice_[index]) * 2.0 * inv_2dy;
481  } else if (unlikely(iy == n_cells_[1] - 1)) {
482  grad_y_jmu =
483  periodic_
484  ? (lattice_[index - diz + diy] - lattice_[index - diy]) *
485  inv_2dy
486  : (lattice_[index] - lattice_[index - diy]) * 2.0 * inv_2dy;
487  } else {
488  grad_y_jmu =
489  (lattice_[index + diy] - lattice_[index - diy]) * inv_2dy;
490  }
491  // z direction
492  if (unlikely(iz == 0)) {
493  grad_z_jmu =
494  periodic_
495  ? (lattice_[index + diz] - lattice_[index + d - diz]) *
496  inv_2dz
497  : (lattice_[index + diz] - lattice_[index]) * 2.0 * inv_2dz;
498  } else if (unlikely(iz == n_cells_[2] - 1)) {
499  grad_z_jmu =
500  periodic_
501  ? (lattice_[index - d + diz] - lattice_[index - diz]) *
502  inv_2dz
503  : (lattice_[index] - lattice_[index - diz]) * 2.0 * inv_2dz;
504  } else {
505  grad_z_jmu =
506  (lattice_[index + diz] - lattice_[index - diz]) * inv_2dz;
507  }
508  // fill
509  (grad_lat)[index][0] = grad_t_jmu;
510  (grad_lat)[index][1] = grad_x_jmu;
511  (grad_lat)[index][2] = grad_y_jmu;
512  (grad_lat)[index][3] = grad_z_jmu;
513  }
514  }
515  }
516  }
517 
518  /**
519  * Take the value of a cell given its 3-D indices.
520  *
521  * \param[in] ix The index of the cell in x direction.
522  * \param[in] iy The index of the cell in y direction.
523  * \param[in] iz The index of the cell in z direction.
524  * \return Physical quantity evaluated at the cell center.
525  *
526  * \note This serves as an accessor to the lattice node, since (prios to
527  * C++23) the overload of \c operator[] can only receive one argument. Because
528  * we do not want to allow for localized changes from outside the class, only
529  * the const version is implemented.
530  */
531  const T& node(int ix, int iy, int iz) const {
532  return periodic_
533  ? lattice_[positive_modulo(ix, n_cells_[0]) +
534  n_cells_[0] *
535  (positive_modulo(iy, n_cells_[1]) +
536  n_cells_[1] * positive_modulo(iz, n_cells_[2]))]
537  : lattice_[ix + n_cells_[0] * (iy + n_cells_[1] * iz)];
538  }
539 
540  /**
541  * Interpolates lattice quantity to coordinate r. Result is stored
542  * in the value variable. Returns true if coordinate r is on the
543  * lattice, false if out of the lattice. In the latter case, the
544  * value is set to the default value (usually 0).
545  *
546  * \param[in] r Position where the physical quantity would be evaluated.
547  * \param[out] value Physical quantity evaluated at the nearest cell
548  * to the given position.
549  * \return Boolean indicates whether the position r is located inside
550  * the lattice.
551  *
552  * \todo (oliiny): maybe 1-order interpolation instead of 0-order?
553  */
554  bool value_at(const ThreeVector& r, T& value) const {
555  const int ix =
556  numeric_cast<int>(std::floor((r.x1() - origin_[0]) / cell_sizes_[0]));
557  const int iy =
558  numeric_cast<int>(std::floor((r.x2() - origin_[1]) / cell_sizes_[1]));
559  const int iz =
560  numeric_cast<int>(std::floor((r.x3() - origin_[2]) / cell_sizes_[2]));
561  if (out_of_bounds(ix, iy, iz)) {
562  value = T();
563  return false;
564  } else {
565  value = node(ix, iy, iz);
566  return true;
567  }
568  }
569 
570  /**
571  * A sub-lattice iterator, which iterates in a 3D-structured manner and
572  * calls a function on every cell.
573  *
574  * \tparam F Type of the function. Arguments are the current node and the 3
575  * integer indices of the cell.
576  * \param[in] lower_bounds Starting numbers for iterating ix, iy, iz.
577  * \param[in] upper_bounds Ending numbers for iterating ix, iy, iz.
578  * \param[in] func Function acting on the cells (such as taking value).
579  */
580  template <typename F>
581  void iterate_sublattice(const std::array<int, 3>& lower_bounds,
582  const std::array<int, 3>& upper_bounds, F&& func) {
583  logg[LLattice].debug(
584  "Iterating sublattice with lower bound index (", lower_bounds[0], ",",
585  lower_bounds[1], ",", lower_bounds[2], "), upper bound index (",
586  upper_bounds[0], ",", upper_bounds[1], ",", upper_bounds[2], ")");
587 
588  if (periodic_) {
589  for (int iz = lower_bounds[2]; iz < upper_bounds[2]; iz++) {
590  const int z_offset = positive_modulo(iz, n_cells_[2]) * n_cells_[1];
591  for (int iy = lower_bounds[1]; iy < upper_bounds[1]; iy++) {
592  const int y_offset =
593  n_cells_[0] * (positive_modulo(iy, n_cells_[1]) + z_offset);
594  for (int ix = lower_bounds[0]; ix < upper_bounds[0]; ix++) {
595  const int index = positive_modulo(ix, n_cells_[0]) + y_offset;
596  func(lattice_[index], ix, iy, iz);
597  }
598  }
599  }
600  } else {
601  for (int iz = lower_bounds[2]; iz < upper_bounds[2]; iz++) {
602  const int z_offset = iz * n_cells_[1];
603  for (int iy = lower_bounds[1]; iy < upper_bounds[1]; iy++) {
604  const int y_offset = n_cells_[0] * (iy + z_offset);
605  for (int ix = lower_bounds[0]; ix < upper_bounds[0]; ix++) {
606  func(lattice_[ix + y_offset], ix, iy, iz);
607  }
608  }
609  }
610  }
611  }
612 
613  /**
614  * Iterates only nodes whose cell centers lie not further than r_cut in x, y,
615  * z directions from the given point, that is iterates within a cube of side
616  * length 2*r_cut, and applies a function to each node.
617  * Useful for adding quantities from one particle to the lattice.
618  *
619  * \tparam F Type of the function. Arguments are the current node and the 3
620  * integer indices of the cell.
621  * \param[in] point Position, usually the position of particle [fm].
622  * \param[in] r_cut Maximum distance from the cell center to the
623  * given position. [fm]
624  * \param[in] func Function acting on the cells (such as taking value).
625  */
626  template <typename F>
627  void iterate_in_cube(const ThreeVector& point, const double r_cut, F&& func) {
628  std::array<int, 3> l_bounds, u_bounds;
629 
630  /* Array holds value at the cell center: r_center = r_0 + (i+0.5)cell_size,
631  * where i is index in any direction. Therefore we want cells with condition
632  * (r-r_cut)/csize - 0.5 < i < (r+r_cut)/csize - 0.5, r = r_center - r_0 */
633  for (int i = 0; i < 3; i++) {
634  l_bounds[i] = numeric_cast<int>(
635  std::ceil((point[i] - origin_[i] - r_cut) / cell_sizes_[i] - 0.5));
636  u_bounds[i] = numeric_cast<int>(
637  std::ceil((point[i] - origin_[i] + r_cut) / cell_sizes_[i] - 0.5));
638  }
639 
640  if (!periodic_) {
641  for (int i = 0; i < 3; i++) {
642  if (l_bounds[i] < 0) {
643  l_bounds[i] = 0;
644  }
645  if (u_bounds[i] > n_cells_[i]) {
646  u_bounds[i] = n_cells_[i];
647  }
648  if (l_bounds[i] > n_cells_[i] || u_bounds[i] < 0) {
649  return;
650  }
651  }
652  }
653  iterate_sublattice(l_bounds, u_bounds, std::forward<F>(func));
654  }
655 
656  /**
657  * Calculate a volume integral with given integrand
658  *
659  * \tparam F return type of the integrand
660  * \param[out] integral variable to store rsult in.
661  * Input should be 0.
662  * \param[in] integrand Function to be integrated
663  * \param[in] rcut size of the integration volume. In total the intgration
664  * volume will be a cube with edge length 2*rcut
665  * \param[in] point center of the integration volume
666  */
667  template <typename F>
668  void integrate_volume(F& integral,
669  F (*integrand)(ThreeVector, T&, ThreeVector),
670  const double rcut, const ThreeVector& point) {
672  point, {rcut, rcut, rcut},
673  [&point, &integral, &integrand, this](T value, int ix, int iy, int iz) {
674  ThreeVector pos = this->cell_center(ix, iy, iz);
675  integral += integrand(pos, value, point) * this->cell_volume_;
676  });
677  }
678  /**
679  * Iterates only nodes whose cell centers lie not further than d_x in x-,
680  * d_y in y-, and d_z in z-direction from the given point, that is iterates
681  * within a rectangle of side lengths (2*dx, 2*dy, 2*dz), and applies a
682  * function to each node.
683  * Useful for adding quantities from one particle to the lattice.
684  *
685  * \tparam F Type of the function. Arguments are the current node and the 3
686  * integer indices of the cell.
687  * \param[in] point Position, usually the position of particle [fm].
688  * \param[in] rectangle Maximum distances in the x-, y-, and z-directions
689  * from the cell center to the given position. [fm]
690  * \param[in] func Function acting on the cells (such as taking value).
691  */
692  template <typename F>
694  const std::array<double, 3>& rectangle, F&& func) {
695  std::array<int, 3> l_bounds, u_bounds;
696 
697  /* Array holds value at the cell center: r_center = r_0 + (i+0.5)cell_size,
698  * where i is index in any direction. Therefore we want cells with condition
699  * (r[i]-rectangle[i])/csize - 0.5 < i < (r[i]+rectangle[i])/csize - 0.5,
700  * r[i] = r_center[i] - r_0[i]
701  */
702  for (int i = 0; i < 3; i++) {
703  l_bounds[i] = numeric_cast<int>(std::ceil(
704  (point[i] - origin_[i] - rectangle[i]) / cell_sizes_[i] - 0.5));
705  u_bounds[i] = numeric_cast<int>(std::ceil(
706  (point[i] - origin_[i] + rectangle[i]) / cell_sizes_[i] - 0.5));
707  }
708 
709  if (!periodic_) {
710  for (int i = 0; i < 3; i++) {
711  if (l_bounds[i] < 0) {
712  l_bounds[i] = 0;
713  }
714  if (u_bounds[i] > n_cells_[i]) {
715  u_bounds[i] = n_cells_[i];
716  }
717  if (l_bounds[i] > n_cells_[i] || u_bounds[i] < 0) {
718  return;
719  }
720  }
721  }
722  iterate_sublattice(l_bounds, u_bounds, std::forward<F>(func));
723  }
724 
725  /**
726  * Iterates only over nodes corresponding to the center cell (the cell
727  * containing the given point) and its nearest neighbors in the -x, +x, -y,
728  * +y, -z, +z directions, and applies a function to each node.
729  * Useful for adding quantities from one particle to the lattice.
730  *
731  * \tparam F Type of the function. Arguments are the current node and the 3
732  * integer indices of the cell.
733  * \param[in] point Position, usually the position of particle [fm].
734  * \param[in] func Function acting on the cells (such as taking value).
735  */
736  template <typename F>
737  void iterate_nearest_neighbors(const ThreeVector& point, F&& func) {
738  // get the 3D indices of the cell containing the given point
739  const int ix = numeric_cast<int>(
740  std::floor((point.x1() - origin_[0]) / cell_sizes_[0]));
741  const int iy = numeric_cast<int>(
742  std::floor((point.x2() - origin_[1]) / cell_sizes_[1]));
743  const int iz = numeric_cast<int>(
744  std::floor((point.x3() - origin_[2]) / cell_sizes_[2]));
745 
746  logg[LLattice].debug(
747  "Iterating over nearest neighbors of the cell at ix = ", ix,
748  ", iy = ", iy, ", iz = ", iz);
749 
750  // determine the 1D index of the center cell
751  const int i = index1d(ix, iy, iz);
752  func(lattice_[i], i, i);
753 
754  // determine the indeces of nearby cells, perform function on them
755  int ileft = index_left(ix, iy, iz);
756  func(lattice_[ileft], ileft, i);
757 
758  int iright = index_right(ix, iy, iz);
759  func(lattice_[iright], iright, i);
760 
761  int idown = index_down(ix, iy, iz);
762  func(lattice_[idown], idown, i);
763 
764  int iup = index_up(ix, iy, iz);
765  func(lattice_[iup], iup, i);
766 
767  int ibackward = index_backward(ix, iy, iz);
768  func(lattice_[ibackward], ibackward, i);
769 
770  int iforward = index_forward(ix, iy, iz);
771  func(lattice_[iforward], iforward, i);
772  }
773 
774  /**
775  * Checks if lattices of possibly different types have identical structure.
776  *
777  * \tparam L Type of the other lattice.
778  * \param[in] lat The other lattice being compared with the current one
779  * \return Whether the two lattices have the same sizes, cell numbers,
780  * origins, and boundary conditions.
781  */
782  template <typename L>
783  bool identical_to_lattice(const L* lat) const {
784  return n_cells_[0] == lat->n_cells()[0] &&
785  n_cells_[1] == lat->n_cells()[1] &&
786  n_cells_[2] == lat->n_cells()[2] &&
787  std::abs(lattice_sizes_[0] - lat->lattice_sizes()[0]) <
788  really_small &&
789  std::abs(lattice_sizes_[1] - lat->lattice_sizes()[1]) <
790  really_small &&
791  std::abs(lattice_sizes_[2] - lat->lattice_sizes()[2]) <
792  really_small &&
793  std::abs(origin_[0] - lat->origin()[0]) < really_small &&
794  std::abs(origin_[1] - lat->origin()[1]) < really_small &&
795  std::abs(origin_[2] - lat->origin()[2]) < really_small &&
796  periodic_ == lat->periodic();
797  }
798 
799  /**
800  * Rebuilds the lattice with a different size, reseting it to zero values. The
801  * parameters are all optional with different types, if none are passed the
802  * function throws.
803  *
804  * \param[in] new_length 3-dimensional array indicates the new size of
805  * the lattice [fm].
806  * \param[in] new_origin 3-dimensional array (nx,ny,nz) indicates the origin
807  * of the lattice.
808  * \param[in] new_cells 3-dimensional array with the new
809  * number of lattice cells.
810  * \throw std::invalid_argument if no arguments are given.
811  */
812  void reset_and_resize(std::optional<std::array<double, 3>> new_length,
813  std::optional<std::array<double, 3>> new_origin,
814  std::optional<std::array<int, 3>> new_cells) {
815  if (!new_length && !new_origin && !new_cells) {
816  throw std::invalid_argument(
817  "RectangularLattice::reset_and_resize called "
818  "without arguments, lattice was not changed.");
819  }
820  reset();
821  if (new_length)
822  lattice_sizes_ = *new_length;
823  if (new_origin)
824  origin_ = *new_origin;
825  if (new_cells)
826  n_cells_ = *new_cells;
828  lattice_sizes_[1] / n_cells_[1],
829  lattice_sizes_[2] / n_cells_[2]};
831  }
832 
833  protected:
834  /// The lattice itself, array containing physical quantities.
835  std::vector<T> lattice_;
836  /// Lattice sizes in x, y, z directions.
837  std::array<double, 3> lattice_sizes_;
838  /// Number of cells in x,y,z directions.
839  std::array<int, 3> n_cells_;
840  /// Cell sizes in x, y, z directions.
841  std::array<double, 3> cell_sizes_;
842  /// Volume of a cell.
843  double cell_volume_;
844  /// Coordinates of the left down nearer corner.
845  std::array<double, 3> origin_;
846  /// Whether the lattice is periodic.
847  const bool periodic_;
848  /// When the lattice should be recalculated.
850 
851  private:
852  /**
853  * Returns division modulo, which is always between 0 and n-1
854  * i%n is not suitable, because it returns results from -(n-1) to n-1
855  *
856  * \param[in] i Dividend.
857  * \param[in] n Divisor.
858  * \return Positive remainder.
859  */
860  inline int positive_modulo(int i, int n) const {
861  /* (i % n + n) % n would be correct, but slow.
862  * Instead I rely on the fact that i should never go too far
863  * in negative region and replace i%n + n by i + 256 * n = i + (n << 8) */
864  // FIXME: This should use asserts, also checking for under- or overflows.
865  return (i + (n << 8)) % n;
866  }
867 };
868 
869 } // namespace smash
870 
871 #endif // SRC_INCLUDE_SMASH_LATTICE_H_
The FourVector class holds relevant values in Minkowski spacetime with (+, −, −, −) metric signature.
Definition: fourvector.h:33
A container class to hold all the arrays on the lattice and access them.
Definition: lattice.h:49
bool out_of_bounds(int ix, int iy, int iz) const
Checks if 3D index is out of lattice bounds.
Definition: lattice.h:116
const std::array< double, 3 > & origin() const
Definition: lattice.h:165
const_iterator end() const
Definition: lattice.h:184
int index_right(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the +x...
Definition: lattice.h:240
int index_left(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the -x...
Definition: lattice.h:223
const bool periodic_
Whether the lattice is periodic.
Definition: lattice.h:847
int index1d(int ix, int iy, int iz)
Return the index of a given cell.
Definition: lattice.h:207
const std::array< double, 3 > & lattice_sizes() const
Definition: lattice.h:156
bool value_at(const ThreeVector &r, T &value) const
Interpolates lattice quantity to coordinate r.
Definition: lattice.h:554
T & operator[](std::size_t i)
Definition: lattice.h:186
const T & node(int ix, int iy, int iz) const
Take the value of a cell given its 3-D indices.
Definition: lattice.h:531
const T & operator[](std::size_t i) const
Definition: lattice.h:188
std::array< double, 3 > cell_sizes_
Cell sizes in x, y, z directions.
Definition: lattice.h:841
bool periodic() const
Definition: lattice.h:168
int index_up(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the +y...
Definition: lattice.h:274
bool identical_to_lattice(const L *lat) const
Checks if lattices of possibly different types have identical structure.
Definition: lattice.h:783
int index_backward(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the -z...
Definition: lattice.h:291
void reset()
Sets all values on lattice to zeros.
Definition: lattice.h:106
LatticeUpdate when_update() const
Definition: lattice.h:171
void compute_four_gradient_lattice(RectangularLattice< FourVector > &old_lat, double time_step, RectangularLattice< std::array< FourVector, 4 >> &grad_lat) const
Compute a fourgradient on a lattice of FourVectors jmu via the finite difference method.
Definition: lattice.h:420
const LatticeUpdate when_update_
When the lattice should be recalculated.
Definition: lattice.h:849
void reset_and_resize(std::optional< std::array< double, 3 >> new_length, std::optional< std::array< double, 3 >> new_origin, std::optional< std::array< int, 3 >> new_cells)
Rebuilds the lattice with a different size, reseting it to zero values.
Definition: lattice.h:812
const std::array< int, 3 > & n_cells() const
Definition: lattice.h:159
RectangularLattice(const std::array< double, 3 > &l, const std::array< int, 3 > &n, const std::array< double, 3 > &orig, bool per, const LatticeUpdate upd)
Rectangular lattice constructor.
Definition: lattice.h:68
std::array< int, 3 > n_cells_
Number of cells in x,y,z directions.
Definition: lattice.h:839
void iterate_sublattice(const std::array< int, 3 > &lower_bounds, const std::array< int, 3 > &upper_bounds, F &&func)
A sub-lattice iterator, which iterates in a 3D-structured manner and calls a function on every cell.
Definition: lattice.h:581
void compute_gradient_lattice(RectangularLattice< ThreeVector > &grad_lat) const
Compute a gradient on a lattice of doubles via the finite difference method.
Definition: lattice.h:322
ThreeVector cell_center(int ix, int iy, int iz) const
Find the coordinates of a given cell.
Definition: lattice.h:133
int positive_modulo(int i, int n) const
Returns division modulo, which is always between 0 and n-1 in is not suitable, because it returns res...
Definition: lattice.h:860
int index_forward(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the +z...
Definition: lattice.h:308
typename std::vector< T >::const_iterator const_iterator
Const interator of lattice.
Definition: lattice.h:176
void assign_value(int lattice_index, T value)
Overwrite with a template value T at a given node.
Definition: lattice.h:195
void iterate_in_cube(const ThreeVector &point, const double r_cut, F &&func)
Iterates only nodes whose cell centers lie not further than r_cut in x, y, z directions from the give...
Definition: lattice.h:627
RectangularLattice(RectangularLattice< T > const &rl)
Copy-constructor.
Definition: lattice.h:95
std::array< double, 3 > origin_
Coordinates of the left down nearer corner.
Definition: lattice.h:845
const_iterator begin() const
Definition: lattice.h:180
void integrate_volume(F &integral, F(*integrand)(ThreeVector, T &, ThreeVector), const double rcut, const ThreeVector &point)
Calculate a volume integral with given integrand.
Definition: lattice.h:668
void iterate_nearest_neighbors(const ThreeVector &point, F &&func)
Iterates only over nodes corresponding to the center cell (the cell containing the given point) and i...
Definition: lattice.h:737
typename std::vector< T >::iterator iterator
Iterator of lattice.
Definition: lattice.h:174
void iterate_in_rectangle(const ThreeVector &point, const std::array< double, 3 > &rectangle, F &&func)
Iterates only nodes whose cell centers lie not further than d_x in x-, d_y in y-, and d_z in z-direct...
Definition: lattice.h:693
std::array< double, 3 > lattice_sizes_
Lattice sizes in x, y, z directions.
Definition: lattice.h:837
double cell_volume_
Volume of a cell.
Definition: lattice.h:843
std::vector< T > lattice_
The lattice itself, array containing physical quantities.
Definition: lattice.h:835
ThreeVector cell_center(int index) const
Find the coordinate of cell center given the 1d index of the cell.
Definition: lattice.h:147
std::size_t size() const
Definition: lattice.h:190
int index_down(int ix, int iy, int iz)
Given the indices of a cell in the x, y, and z directions, return index of the nearest cell in the -y...
Definition: lattice.h:257
const std::array< double, 3 > & cell_sizes() const
Definition: lattice.h:162
The ThreeVector class represents a physical three-vector with the components .
Definition: threevector.h:31
double x3() const
Definition: threevector.h:194
double x2() const
Definition: threevector.h:190
double x1() const
Definition: threevector.h:186
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
#define unlikely(x)
Tell the branch predictor that this expression is likely false.
Definition: macros.h:16
constexpr int n
Neutron.
Definition: action.h:24
LatticeUpdate
Enumerator option for lattice updates.
Definition: lattice.h:38
constexpr double really_small
Numerical error tolerance.
Definition: constants.h:41
static constexpr int LLattice
Definition: lattice.h:27
Generic numerical functions.