Line data Source code
1 : #ifndef _MOLECULARDYNAMICS_MOLECULARCONTAINER_H_
2 : #define _MOLECULARDYNAMICS_MOLECULARCONTAINER_H_
3 :
4 : #include <Kokkos_Core.hpp>
5 :
6 : #include <simplemd/MolecularDynamicsDefinitions.h>
7 : #include <simplemd/Molecule.h>
8 : #include <simplemd/LinkedCell.h>
9 : #include <simplemd/services/ParallelTopologyService.h>
10 : #include <simplemd/services/MolecularPropertiesService.h>
11 :
12 : namespace simplemd {
13 : namespace services {
14 : // forward declarations to remove circular dependencies
15 : class ParallelTopologyService;
16 : } // namespace services
17 :
18 : class MoleculeContainer;
19 : } // namespace simplemd
20 :
21 : /**
22 : * @brief Contains molecules, creates linked cells and manages its own memory.
23 : *
24 : * The MoleculeContainer class serves as the sole container responsible for all molecule-related work.
25 : * Molecules are stored in a 2D Kokkos View, where each row corresponds to a linked cell (ghost included).
26 : * While creating a container, the maximum possible capacity (in molecules) of any cell is defined by cellCapacity.
27 : * Thus, each linked cell has a numMolecules() <= capacity.
28 : * All data with an index between numMolecules and capacity is treated as garbage data.
29 : */
30 : class simplemd::MoleculeContainer {
31 : public:
32 : /**
33 : * @brief Construct a new MoleculeContainer object
34 : *
35 : * Uses parallelTopologyService to store relevant information related to spatial layout,
36 : * so that 3D coordinated can be converted to 3D and 1D linked cell indices.
37 : *
38 : * @param parallelTopologyService Used to extract and store local number of cells, domain offset etc.
39 : * @param cellCapacity The maximum capacity of any cell. Cannot be changed, must provide ample room at compiletime.
40 : */
41 : MoleculeContainer(simplemd::services::ParallelTopologyService& parallelTopologyService, int cellCapacity);
42 :
43 : /**
44 : * @brief Inserts a molecule into a specific linked cell.
45 : *
46 : * @param cellIdx The one-dimensional index of the linked cell to insert the molecule into (ghost included).
47 : * @param molecule The molecule to be inserted.
48 : */
49 : void insert(unsigned int cellIdx, const simplemd::Molecule& molecule);
50 :
51 : /**
52 : * @brief Inserts a molecule into a specific linked cell.
53 : *
54 : * @param cellIdx The vector index of the linked cell to insert the molecule into (ghost included).
55 : * @param molecule The molecule to be inserted.
56 : */
57 : void insert(tarch::la::Vector<MD_DIM, unsigned int> cellIdx, const simplemd::Molecule& molecule) { insert(vectorIndexToLinear(cellIdx), molecule); }
58 :
59 : /**
60 : * @brief Inserts a molecule into the container.
61 : *
62 : * The function calculates the appropriate linked cell to insert the molecule into.
63 : *
64 : * @param molecule The molecule to be inserted.
65 : */
66 : void insert(const simplemd::Molecule& molecule);
67 :
68 : /**
69 : * @brief Removes a molecule, given its ID and linked cell index.
70 : *
71 : * This is done by swapping the last molecule of the linked cell into position moleculeIdx,
72 : * and decrementing the number of molecules in this cell.
73 : *
74 : * @param cellIdx 1D linked cell index of the molecule (ghost included).
75 : * @param moleculeIdx Index of the molecule within the linked cell.
76 : */
77 : void remove(unsigned int cellIdx, unsigned int moleculeIdx);
78 :
79 : /**
80 : * @brief Clears a linked cell.
81 : *
82 : * This function simply sets the number of molecules in the linked cell as 0.
83 : * All data in the linked cell is treated as garbage data, and insertion starts from index 0
84 : * within the linked cell again, overwriting previous data.
85 : *
86 : * @param cellIdx 1D index of the linked cell to clear (ghost included).
87 : */
88 : void clearLinkedCell(unsigned int cellIdx);
89 :
90 : /**
91 : * @brief Removes all outgoing molecules from a linked cell and moves them to the appropriate destination.
92 : *
93 : * @param cellIdx 1D index of the linked cell to sort (ghost included).
94 : */
95 : void sort(unsigned int cellIdx);
96 :
97 : /**
98 : * @brief Puts all molecules into their appropriate linked cells.
99 : *
100 : * Since a molecule can only travel to its immediate neighbours per timestep, sorting is done with a red-black
101 : * traversal of the whole domain (including ghost cells). This ensures that there are no concurrency issues since
102 : * no two linked cells should try to write to the same linked cell. Concurrency is implemented in quarter shells.
103 : * Since this includes ghost shells, they should be empty before sort() is called.
104 : */
105 : void sort();
106 :
107 : /**
108 : * @brief Get the Molecule at linked cell index i (ghost included), position j
109 : *
110 : * @param i
111 : * @param j
112 : * @return simplemd::Molecule&
113 : */
114 : KOKKOS_FUNCTION simplemd::Molecule& getMoleculeAt(size_t i, size_t j) const;
115 :
116 : /**
117 : * @brief Returns the linked cell at 1D index idx (ghost included)
118 : *
119 : * @param idx
120 : * @return simplemd::LinkedCell
121 : */
122 : KOKKOS_FUNCTION simplemd::LinkedCell operator[](const size_t idx) const;
123 :
124 : /**
125 : * @brief Returns the linked cell at 3D index idx (ghost included)
126 : *
127 : * @param idx
128 : * @return simplemd::LinkedCell
129 : */
130 : KOKKOS_FUNCTION simplemd::LinkedCell operator[](const tarch::la::Vector<MD_DIM, unsigned int> cellIdx) const;
131 :
132 : /**
133 : * @brief Get the total number of cells in the container
134 : *
135 : * @return size_t
136 : */
137 : KOKKOS_FUNCTION size_t getLocalNumberOfCellsScalarWithGhost() const;
138 :
139 : /**
140 : * @brief Returns the number of molecules in all cells
141 : *
142 : * @return const size_t
143 : */
144 : size_t getLocalNumberOfMoleculesWithGhost() const;
145 :
146 : /**
147 : * @brief returns the index of the first (non-ghost) cell along each dimension
148 : *
149 : * @return tarch::la::Vector
150 : */
151 : const tarch::la::Vector<MD_DIM, unsigned int>& getLocalIndexOfFirstCell() const;
152 :
153 : /**
154 : * @brief returns the number of (non-ghost) cells along each dimension
155 : *
156 : * @return tarch::la::Vector
157 : */
158 : const tarch::la::Vector<MD_DIM, unsigned int> getLocalNumberOfCells() const;
159 :
160 : /**
161 : * @brief returns the local cell index vector for the local cell index cellIndex
162 : *
163 : * @return tarch::la::Vector
164 : */
165 : KOKKOS_FUNCTION tarch::la::Vector<MD_DIM, unsigned int> getLocalCellIndexVector(const size_t cellIndex) const;
166 :
167 : /**
168 : * @brief can be used to apply a molecule-mapping which is iterated over all molecules of this process
169 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called
170 : */
171 : template <class A> void iterateMolecules(A& a);
172 :
173 : /**
174 : * @brief can be used to apply a molecule-with-cell-mapping which is iterated over all molecules of this process
175 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called
176 : */
177 : template <class A> void iterateMoleculesWithCell(A& a);
178 :
179 : /**
180 : * @brief iterates over all cells in the range defined by the lower left front
181 : * corner cell lowerLeftFrontCell and the size of the domain cellRange.
182 : * cellRange defines a number of cells in each spatial direction that the
183 : * class A shall be applied to. lowerLeftFrontCell needs to be given in local
184 : * coordinates.
185 : *
186 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called.
187 : */
188 : template <class A>
189 : void iterateCells(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell, const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
190 :
191 : /**
192 : * @brief iterates over all cells in the inner part (i.e. does not consider the
193 : * ghost layer)
194 : *
195 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called
196 : */
197 : template <class A> void iterateCells(A& a);
198 :
199 : /**
200 : * @brief iterates over all cell pairs for the cells in the inner region of each
201 : * local process
202 : *
203 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called
204 : */
205 : template <class A> void iterateCellPairs(A& a);
206 :
207 : /**
208 : * @brief iterates over all cell pairs cell1 and cell2 with cell1 in the range
209 : * described by lowerLeftFrontCell and cellRange; cell2 does not need to lie
210 : * within the range (example: iterate only over lowerLeftFrontCell=(1,1,1) and
211 : * cellRange=(1,1,1). Then, we will consider amongst others the pair
212 : * (0,0,0),(1,1,1)).
213 : *
214 : * uses static member in mapping class (A::IsParallel) to determine whether the parallel or serial iterator will be called
215 : */
216 : template <class A>
217 : void iterateCellPairs(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell, const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
218 :
219 : /**
220 : * @brief applies molecule mapping without any node-level parallelisation
221 : */
222 : template <class A> void iterateMoleculesSerial(A& a);
223 :
224 : /**
225 : * @brief applies molecule mapping while parallelising using Kokkos
226 : */
227 : template <class A> void iterateMoleculesParallel(A& a);
228 :
229 : /**
230 : * @brief applies molecule-with-cell mapping to all neighbors of cell
231 : */
232 : template <class A> KOKKOS_FUNCTION void handleCellNeighbors(A& a, Molecule& m, const LinkedCell& cell) const;
233 :
234 : /**
235 : * @brief applies molecule-with-cell mapping without any node-level parallelisation
236 : */
237 : template <class A> void iterateMoleculesWithCellSerial(A& a);
238 :
239 : /**
240 : * @brief applies molecule-with-cell mapping while parallelising using Kokkos
241 : */
242 : template <class A> void iterateMoleculesWithCellParallel(A& a);
243 :
244 : /**
245 : * @brief iterates over cells in parallel using Kokkos
246 : */
247 : template <class A>
248 : void iterateCellsParallel(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell, const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
249 :
250 : /**
251 : * @brief iterates over all cells in the inner part (i.e. does not consider the
252 : * ghost layer) in parallel using Kokkos
253 : */
254 : template <class A> void iterateCellsParallel(A& a);
255 :
256 : /**
257 : * @brief iterates over all cell pairs for the cells in the inner region of each
258 : * local process in parallel using Kokkos
259 : */
260 : template <class A> void iterateCellPairsParallel(A& a);
261 :
262 : /**
263 : * @brief iterates over all cell pairs cell1 and cell2 with cell1 in the range
264 : * described by lowerLeftFrontCell and cellRange in parallel using Kokkos; cell2 does not need to lie
265 : * within the range (example: iterate only over lowerLeftFrontCell=(1,1,1) and
266 : * cellRange=(1,1,1). Then, we will consider amongst others the pair
267 : * (0,0,0),(1,1,1)).
268 : */
269 : template <class A>
270 : void iterateCellPairsParallel(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
271 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
272 :
273 : /**
274 : * @brief iterates over cells without parallelization
275 : */
276 : template <class A>
277 : void iterateCellsSerial(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell, const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
278 :
279 : /**
280 : * @brief iterates over all cells in the inner part (i.e. does not consider the
281 : * ghost layer) without parallelisation */
282 : template <class A> void iterateCellsSerial(A& a);
283 :
284 : /**
285 : * @brief iterates over all cell pairs for the cells in the inner region of each
286 : * local process without parallelisation */
287 : template <class A> void iterateCellPairsSerial(A& a);
288 :
289 : /**
290 : * @brief iterates over all cell pairs cell1 and cell2 with cell1 in the range
291 : * described by lowerLeftFrontCell and cellRange without parallelisation; cell2 does not need to lie
292 : * within the range (example: iterate only over lowerLeftFrontCell=(1,1,1) and
293 : * cellRange=(1,1,1). Then, we will consider amongst others the pair
294 : * (0,0,0),(1,1,1)).
295 : */
296 : template <class A>
297 : void iterateCellPairsSerial(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
298 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange);
299 :
300 : /**
301 : * @brief Converts a global 3D spatial coordinate to a local 1D linked cell index.
302 : *
303 : * This can be used to find the linked cell that a molecule belongs to.
304 : *
305 : * @param position 3D spatial coordinate.
306 : * @return unsigned int
307 : */
308 : KOKKOS_FUNCTION unsigned int positionToCellIndex(const tarch::la::Vector<MD_DIM, double>& position) const;
309 :
310 : private:
311 : /**
312 : * @brief Converts a 3D local linked cell index into a 1D local linked cell index.
313 : *
314 : * @param vectorIndex 3D local index of the linked cell.
315 : * @return const unsigned int
316 : */
317 : KOKKOS_FUNCTION size_t vectorIndexToLinear(const tarch::la::Vector<MD_DIM, unsigned int>& vectorIndex) const;
318 :
319 : /**
320 : * @brief returns true if the local cell index cellIndex describes a linked cell
321 : * within the ghost layer
322 : *
323 : * @param cellIndex The linear cell index
324 : * @return bool
325 : */
326 : KOKKOS_INLINE_FUNCTION bool isGhostCell(const size_t cellIndex) const;
327 :
328 : KOKKOS_FUNCTION void printCellMolecules(size_t cellIndex) const;
329 : KOKKOS_FUNCTION void printNonGhostCells(bool shouldPrintCells, const char* const label) const;
330 :
331 : /** number of cells per direction in the local domain */
332 : const tarch::la::Vector<MD_DIM, unsigned int> _numCells;
333 : /** The number of ghost cells around the local domain along each axis on each side*/
334 : const tarch::la::Vector<MD_DIM, unsigned int> _ghostCellLayerThickness;
335 : const tarch::la::Vector<MD_DIM, unsigned int> _numLocalCellsNoGhost;
336 :
337 : /** maximum number of particles a cell (a row of the view) can contain
338 : * if this is exceeded when writing to cell, the simulation behaviour is undefined
339 : */
340 : size_t _cellCapacity;
341 :
342 : /** domain size */
343 : const tarch::la::Vector<MD_DIM, double> _domainSize;
344 :
345 : /** global domain offset */
346 : const tarch::la::Vector<MD_DIM, double> _domainOffset;
347 :
348 : /** mesh width of the linked cells */
349 : const tarch::la::Vector<MD_DIM, double> _meshWidth;
350 :
351 : /** global index of the first cell of this domain */
352 : const tarch::la::Vector<MD_DIM, unsigned int> _globalIndexOfFirstCell;
353 :
354 : /** local index of the first cell within this domain */
355 : const tarch::la::Vector<MD_DIM, unsigned int> _localIndexOfFirstCell;
356 :
357 : Kokkos::View<simplemd::Molecule**, Kokkos::LayoutRight, Kokkos::SharedSpace> _moleculeData;
358 : Kokkos::View<size_t*, Kokkos::LayoutRight, Kokkos::SharedSpace> _linkedCellNumMolecules;
359 : Kokkos::View<bool*> _linkedCellIsGhostCell;
360 : /** index offsets of all 26 neighbor cell directions */
361 : Kokkos::View<int*> _neighborOffsets;
362 : };
363 :
364 280 : template <class A> void simplemd::MoleculeContainer::iterateMolecules(A& a) {
365 : if constexpr (A::IsParallel) {
366 144 : iterateMoleculesParallel(a);
367 : } else {
368 136 : iterateMoleculesSerial(a);
369 : }
370 280 : }
371 :
372 136 : template <class A> void simplemd::MoleculeContainer::iterateMoleculesSerial(A& a) {
373 124 : a.beginMoleculeIteration();
374 1378728 : for (unsigned int i = 0; i < _linkedCellNumMolecules.size(); i++) {
375 1394692 : for (unsigned int j = 0; j < _linkedCellNumMolecules(i); j++) {
376 : #if (MD_DEBUG == MD_YES)
377 : std::cout << "Handle molecule " << j << " in cell #" << i << std::endl;
378 : #endif
379 16700 : a.handleMolecule(getMoleculeAt(i, j));
380 : }
381 : }
382 124 : a.endMoleculeIteration();
383 136 : }
384 :
385 144 : template <class A> void simplemd::MoleculeContainer::iterateMoleculesParallel(A& a) {
386 : #if (MD_DEBUG == MD_YES)
387 : iterateMoleculesSerial(a);
388 : #else
389 : a.beginMoleculeIteration();
390 144 : printNonGhostCells(true, "host start iterateMoleculesParallel");
391 144 : Kokkos::parallel_for(
392 432 : "simplemd::MoleculeContainer::iterateMoleculesParallel", Kokkos::RangePolicy<MainExecSpace>(0, _linkedCellIsGhostCell.size()),
393 724992 : KOKKOS_CLASS_LAMBDA(const unsigned int i) {
394 1400544 : printNonGhostCells(i == 0, "device start iterateMoleculesParallel");
395 1417644 : for (unsigned int j = 0; j < _linkedCellNumMolecules(i); j++) {
396 34200 : a.handleMolecule(getMoleculeAt(i, j));
397 : }
398 1400544 : printNonGhostCells(i == 0, "device end iterateMoleculesParallel");
399 : });
400 144 : Kokkos::fence(); // Ensure results are available on the host
401 144 : printNonGhostCells(true, "host end iterateMoleculesParallel");
402 : a.endMoleculeIteration();
403 : #endif
404 144 : }
405 :
406 : template <class A> void simplemd::MoleculeContainer::iterateMoleculesWithCell(A& a) {
407 : if constexpr (A::IsParallel) {
408 : iterateMoleculesWithCellParallel(a);
409 : } else {
410 : iterateMoleculesWithCellSerial(a);
411 : }
412 : }
413 :
414 : template <class A> void simplemd::MoleculeContainer::handleCellNeighbors(A& a, Molecule& m, const LinkedCell& cell) const {
415 : unsigned int index = cell.getIndex();
416 : for (unsigned int i = 0; i < 26; i++) {
417 : auto cell2 = (*this)[index + _neighborOffsets(i)];
418 : a.handleMolecule(m, cell2);
419 : }
420 : }
421 :
422 : template <class A> void simplemd::MoleculeContainer::iterateMoleculesWithCellSerial(A& a) {
423 : a.beginMoleculeIteration();
424 : for (unsigned int i = 0; i < _linkedCellNumMolecules.size(); i++) {
425 : for (unsigned int j = 0; j < _linkedCellNumMolecules(i); j++) {
426 : simplemd::LinkedCell cell = (*this)[i];
427 : Molecule& m = getMoleculeAt(i, j);
428 : a.handleMolecule(m, cell);
429 : handleCellNeighbors(a, m, cell);
430 : }
431 : }
432 : a.endMoleculeIteration();
433 : }
434 :
435 : template <class A> void simplemd::MoleculeContainer::iterateMoleculesWithCellParallel(A& a) {
436 : a.beginMoleculeIteration();
437 : const unsigned int threads_per_cell = 5;
438 : const unsigned int length = _linkedCellNumMolecules.size() * threads_per_cell;
439 : Kokkos::parallel_for(
440 : "simplemd::MoleculeContainer::iterateMoleculesWithCellParallel", Kokkos::RangePolicy<MainExecSpace>(0, length),
441 : KOKKOS_CLASS_LAMBDA(const unsigned int i) {
442 : const unsigned int cellIndex = i / threads_per_cell;
443 : simplemd::LinkedCell cell = (*this)[cellIndex];
444 :
445 : for (unsigned int j = i % threads_per_cell; j < _linkedCellNumMolecules(cellIndex); j += threads_per_cell) {
446 : Molecule& m = getMoleculeAt(cellIndex, j);
447 : a.handleMolecule(m, cell);
448 : handleCellNeighbors(a, m, cell);
449 : }
450 : });
451 : Kokkos::fence(); // Ensure results are available on the host
452 :
453 : a.endMoleculeIteration();
454 : }
455 :
456 : template <class A>
457 4 : void simplemd::MoleculeContainer::iterateCellsSerial(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
458 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
459 4 : unsigned int index = 0;
460 : #if (MD_ERROR == MD_YES)
461 16 : for (unsigned int d = 0; d < MD_DIM; d++) {
462 12 : if (cellRange[d] == 0) {
463 0 : Kokkos::printf("cellRange(%d)==0!\n", d);
464 : Kokkos::abort("ERROR simplemd::MoleculeContainer::iterateCells: zero in cell range\n");
465 : }
466 12 : if (lowerLeftFrontCell[d] + cellRange[d] > 2 * _ghostCellLayerThickness[d] + _numLocalCellsNoGhost[d]) {
467 : Kokkos::abort("ERROR simplemd::MoleculeContainer::iterateCells(): "
468 : "defined Range does not fit into local sub-domain!\n");
469 : }
470 : }
471 : #endif
472 :
473 : // start iteration();
474 : a.beginCellIteration();
475 4 : tarch::la::Vector<MD_DIM, unsigned int> coords(0);
476 : // loop over domain
477 : #if (MD_DIM > 2)
478 204 : for (coords[2] = lowerLeftFrontCell[2]; coords[2] < lowerLeftFrontCell[2] + cellRange[2]; coords[2]++) {
479 : #endif
480 : #if (MD_DIM > 1)
481 6200 : for (coords[1] = lowerLeftFrontCell[1]; coords[1] < lowerLeftFrontCell[1] + cellRange[1]; coords[1]++) {
482 : #endif
483 306000 : for (coords[0] = lowerLeftFrontCell[0]; coords[0] < lowerLeftFrontCell[0] + cellRange[0]; coords[0]++) {
484 : #if (MD_DEBUG == MD_YES)
485 : std::cout << "Handle cell " << coords << std::endl;
486 : #endif
487 300000 : index = vectorIndexToLinear(coords);
488 300000 : simplemd::LinkedCell cell = (*this)[index];
489 300000 : a.handleCell(cell);
490 : }
491 : #if (MD_DIM > 1)
492 : }
493 : #endif
494 : #if (MD_DIM > 2)
495 : }
496 : #endif
497 :
498 : // end iteration();
499 : a.endCellIteration();
500 4 : }
501 :
502 : template <class A> void simplemd::MoleculeContainer::iterateCellsSerial(A& a) { iterateCellsSerial(a, _ghostCellLayerThickness, _numLocalCellsNoGhost); }
503 :
504 : template <class A>
505 4 : void simplemd::MoleculeContainer::iterateCellPairsSerial(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
506 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
507 4 : tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS / 2, unsigned int> neighbourOffset;
508 4 : tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS / 2, unsigned int> indexOffset;
509 : #if (MD_DIM == 1)
510 : neighbourOffset[0] = 1;
511 : indexOffset[0] = 0;
512 : #elif (MD_DIM == 2)
513 : indexOffset[0] = 0;
514 : neighbourOffset[0] = 1;
515 : indexOffset[1] = 0;
516 : neighbourOffset[1] = _numLocalCellsNoGhost[0] + 2;
517 : indexOffset[2] = 0;
518 : neighbourOffset[2] = _numLocalCellsNoGhost[0] + 3;
519 : indexOffset[3] = 1;
520 : neighbourOffset[3] = _numLocalCellsNoGhost[0] + 2;
521 : #elif (MD_DIM == 3)
522 4 : indexOffset[0] = 0;
523 4 : neighbourOffset[0] = 1;
524 4 : indexOffset[1] = 0;
525 4 : neighbourOffset[1] = _numLocalCellsNoGhost[0] + 2;
526 4 : indexOffset[2] = 0;
527 4 : neighbourOffset[2] = _numLocalCellsNoGhost[0] + 3;
528 4 : indexOffset[3] = 0;
529 4 : neighbourOffset[3] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
530 4 : indexOffset[4] = 0;
531 4 : neighbourOffset[4] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + 1;
532 4 : indexOffset[5] = 0;
533 4 : neighbourOffset[5] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2);
534 4 : indexOffset[6] = 0;
535 4 : neighbourOffset[6] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2) + 1;
536 :
537 4 : indexOffset[7] = 1;
538 4 : neighbourOffset[7] = _numLocalCellsNoGhost[0] + 2;
539 4 : indexOffset[8] = 1;
540 4 : neighbourOffset[8] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
541 4 : indexOffset[9] = 1;
542 4 : neighbourOffset[9] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2);
543 :
544 4 : indexOffset[10] = _numLocalCellsNoGhost[0] + 2;
545 4 : neighbourOffset[10] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
546 4 : indexOffset[11] = _numLocalCellsNoGhost[0] + 2;
547 4 : neighbourOffset[11] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + 1;
548 :
549 4 : indexOffset[12] = (_numLocalCellsNoGhost[0] + 2) + 1;
550 4 : neighbourOffset[12] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
551 : #endif
552 :
553 : #if (MD_ERROR == MD_YES)
554 16 : for (unsigned int d = 0; d < MD_DIM; d++) {
555 12 : if (cellRange[d] > 2 * _ghostCellLayerThickness[d] + _numLocalCellsNoGhost[d] - 1) {
556 0 : std::cout << "ERROR simplemd::MoleculeContainer::iterateCellPairs(): "
557 : "defined Range does not fit into local sub-domain!"
558 0 : << std::endl;
559 0 : exit(EXIT_FAILURE);
560 : }
561 : }
562 : #endif
563 :
564 : // start iteration();
565 : a.beginCellIteration();
566 4 : tarch::la::Vector<MD_DIM, unsigned int> coords(0);
567 4 : unsigned int coordsCell1Buffer(0);
568 4 : unsigned int coordsCell2Buffer(0);
569 : unsigned int index;
570 :
571 : // loop over domain
572 : #if (MD_DIM > 2)
573 208 : for (coords[2] = lowerLeftFrontCell[2]; coords[2] < lowerLeftFrontCell[2] + cellRange[2]; coords[2]++) {
574 : #endif
575 : #if (MD_DIM > 1)
576 6528 : for (coords[1] = lowerLeftFrontCell[1]; coords[1] < lowerLeftFrontCell[1] + cellRange[1]; coords[1]++) {
577 : #endif
578 328848 : for (coords[0] = lowerLeftFrontCell[0]; coords[0] < lowerLeftFrontCell[0] + cellRange[0]; coords[0]++) {
579 : // handle cell itself
580 322524 : index = vectorIndexToLinear(coords);
581 : #if (MD_DEBUG == MD_YES)
582 : std::cout << "iterateCellPairs: Single index " << index << std::endl;
583 : #endif
584 322524 : simplemd::LinkedCell cell = (*this)[index];
585 : a.handleCell(cell);
586 : // handle pairs (lower,left,back-oriented cells)
587 4515336 : for (unsigned int i = 0; i < MD_LINKED_CELL_NEIGHBOURS / 2; i++) {
588 : #if (MD_DEBUG == MD_YES)
589 : std::cout << "iterateCellPairs: Pair index " << index + indexOffset[i] << "," << index + neighbourOffset[i] << std::endl;
590 : #endif
591 :
592 4192812 : coordsCell1Buffer = index + indexOffset[i];
593 4192812 : coordsCell2Buffer = index + neighbourOffset[i];
594 4192812 : simplemd::LinkedCell cell1 = (*this)[coordsCell1Buffer];
595 4192812 : simplemd::LinkedCell cell2 = (*this)[coordsCell2Buffer];
596 4192812 : a.handleCellPair(cell1, cell2, coordsCell1Buffer, coordsCell2Buffer);
597 : }
598 : } // coords(0)
599 : #if (MD_DIM > 1)
600 : }
601 : #endif
602 : #if (MD_DIM > 2)
603 : }
604 : #endif
605 :
606 : // end iteration();
607 : a.endCellIteration();
608 4 : }
609 :
610 : template <class A> void simplemd::MoleculeContainer::iterateCellPairsSerial(A& a) {
611 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationStart(0);
612 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationLength(getLocalNumberOfCells() + getLocalIndexOfFirstCell());
613 : iterateCellPairsSerial(a, pairIterationStart, pairIterationLength);
614 : }
615 :
616 : template <class A>
617 4 : void simplemd::MoleculeContainer::iterateCellsParallel(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
618 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
619 : #if (MD_ERROR == MD_YES)
620 16 : for (unsigned int d = 0; d < MD_DIM; d++) {
621 12 : if (cellRange[d] == 0) {
622 0 : Kokkos::printf("cellRange(%d)==0!\n", d);
623 : Kokkos::abort("ERROR simplemd::MoleculeContainer::iterateCells: zero in cell range\n");
624 : }
625 12 : if (lowerLeftFrontCell[d] + cellRange[d] > 2 * _ghostCellLayerThickness[d] + _numLocalCellsNoGhost[d]) {
626 : Kokkos::abort("ERROR simplemd::MoleculeContainer::iterateCells(): "
627 : "defined Range does not fit into local sub-domain!\n");
628 : }
629 : }
630 : #endif
631 :
632 : // start iteration();
633 : a.beginCellIteration();
634 : /**
635 : * The size<> Vector stores the number of cells, plus ghost layer.
636 : * If there are (1,1,1) ghost cells per dimension, getLocalIndexOfFirstCell will return (1,1,1)
637 : * Thus this is multiplied by 2 to account for ghost cells in both locations (begin, end) per axis
638 : * and then added to local number of cells
639 : */
640 4 : const tarch::la::Vector<MD_DIM, unsigned int> size(getLocalNumberOfCells() + 2u * getLocalIndexOfFirstCell());
641 4 : const int length = cellRange[0]
642 : #if (MD_DIM > 1)
643 4 : * cellRange[1]
644 : #endif
645 : #if (MD_DIM > 2)
646 4 : * cellRange[2]
647 : #endif
648 : ;
649 : // loop over domain, but with a single loop
650 4 : printNonGhostCells(true, "host start iterateCellsParallel");
651 4 : Kokkos::parallel_for(
652 24 : "simplemd::MoleculeContainer::iterateCellsParallel", Kokkos::RangePolicy<MainExecSpace>(0, length), KOKKOS_CLASS_LAMBDA(const unsigned int i) {
653 300000 : printNonGhostCells(i == 0, "device start iterateCellsParallel");
654 : // compute index of the current cell
655 : #if (MD_DIM > 1)
656 300000 : int helpIndex1 = i;
657 300000 : int helpIndex2 = 0;
658 : #endif
659 300000 : unsigned int index = 0;
660 :
661 : #if (MD_DIM > 2)
662 : // determine plane within traversed block
663 300000 : helpIndex2 = helpIndex1 / (cellRange[0] * cellRange[1]);
664 : // save rest of index in helpIndex1
665 300000 : helpIndex1 = helpIndex1 - helpIndex2 * (cellRange[0] * cellRange[1]);
666 : // compute contribution to index
667 300000 : index += (lowerLeftFrontCell[2] + helpIndex2) * size[0] * size[1];
668 : #endif
669 : #if (MD_DIM > 1)
670 : // determine plane within traversed block
671 300000 : helpIndex2 = helpIndex1 / cellRange[0];
672 : // save rest of index in helpIndex1
673 300000 : helpIndex1 = helpIndex1 - helpIndex2 * cellRange[0];
674 : // compute contribution to index
675 300000 : index += (lowerLeftFrontCell[1] + helpIndex2) * size[0];
676 : // compute contribution for last dimension
677 300000 : index += (lowerLeftFrontCell[0] + helpIndex1);
678 : #else
679 : index = lowerLeftFrontCell[0] + i;
680 : #endif
681 : #if (MD_DEBUG == MD_YES)
682 : Kokkos::printf("Handle cell %u\n", index);
683 : #endif
684 :
685 : // handle cell
686 300000 : auto cell = (*this)[index];
687 300000 : a.handleCell(cell);
688 300000 : printNonGhostCells(i == 0, "device end iterateCellsParallel");
689 : }); // Kokkos::parallel_for
690 4 : Kokkos::fence(); // Ensure results are available on the host
691 4 : printNonGhostCells(true, "host end iterateCellsParallel");
692 : // end iteration();
693 : a.endCellIteration();
694 4 : }
695 :
696 : template <class A> void simplemd::MoleculeContainer::iterateCellsParallel(A& a) { iterateCellsParallel(a, _ghostCellLayerThickness, _numLocalCellsNoGhost); }
697 :
698 : template <class A>
699 136 : void simplemd::MoleculeContainer::iterateCellPairsParallel(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
700 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
701 136 : tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS / 2, unsigned int> neighbourOffset;
702 136 : tarch::la::Vector<MD_LINKED_CELL_NEIGHBOURS / 2, unsigned int> indexOffset;
703 : #if (MD_DIM == 1)
704 : neighbourOffset[0] = 1;
705 : indexOffset[0] = 0;
706 : #elif (MD_DIM == 2)
707 : indexOffset[0] = 0;
708 : neighbourOffset[0] = 1;
709 : indexOffset[1] = 0;
710 : neighbourOffset[1] = _numLocalCellsNoGhost[0] + 2;
711 : indexOffset[2] = 0;
712 : neighbourOffset[2] = _numLocalCellsNoGhost[0] + 3;
713 : indexOffset[3] = 1;
714 : neighbourOffset[3] = _numLocalCellsNoGhost[0] + 2;
715 : #elif (MD_DIM == 3)
716 136 : indexOffset[0] = 0;
717 136 : neighbourOffset[0] = 1;
718 136 : indexOffset[1] = 0;
719 136 : neighbourOffset[1] = _numLocalCellsNoGhost[0] + 2;
720 136 : indexOffset[2] = 0;
721 136 : neighbourOffset[2] = _numLocalCellsNoGhost[0] + 3;
722 136 : indexOffset[3] = 0;
723 136 : neighbourOffset[3] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
724 136 : indexOffset[4] = 0;
725 136 : neighbourOffset[4] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + 1;
726 136 : indexOffset[5] = 0;
727 136 : neighbourOffset[5] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2);
728 136 : indexOffset[6] = 0;
729 136 : neighbourOffset[6] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2) + 1;
730 :
731 136 : indexOffset[7] = 1;
732 136 : neighbourOffset[7] = _numLocalCellsNoGhost[0] + 2;
733 136 : indexOffset[8] = 1;
734 136 : neighbourOffset[8] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
735 136 : indexOffset[9] = 1;
736 136 : neighbourOffset[9] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + (_numLocalCellsNoGhost[0] + 2);
737 :
738 136 : indexOffset[10] = _numLocalCellsNoGhost[0] + 2;
739 136 : neighbourOffset[10] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
740 136 : indexOffset[11] = _numLocalCellsNoGhost[0] + 2;
741 136 : neighbourOffset[11] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2) + 1;
742 :
743 136 : indexOffset[12] = (_numLocalCellsNoGhost[0] + 2) + 1;
744 136 : neighbourOffset[12] = (_numLocalCellsNoGhost[0] + 2) * (_numLocalCellsNoGhost[1] + 2);
745 : #endif
746 :
747 : #if (MD_ERROR == MD_YES)
748 544 : for (unsigned int d = 0; d < MD_DIM; d++) {
749 408 : if (cellRange[d] > 2 * _ghostCellLayerThickness[d] + _numLocalCellsNoGhost[d] - 1) {
750 : Kokkos::abort("ERROR simplemd::MoleculeContainer::iterateCellPairs(): "
751 : "defined Range does not fit into local sub-domain!\n");
752 : }
753 : }
754 : #endif
755 :
756 : // start iteration();
757 132 : a.beginCellIteration();
758 : /**
759 : * The size<> Vector stores the number of cells, plus ghost layer.
760 : * If there are (1,1,1) ghost cells per dimension, getLocalIndexOfFirstCell will return (1,1,1)
761 : * Thus this is multiplied by 2 to account for ghost cells in both locations (begin, end) per axis
762 : * and then added to local number of cells
763 : */
764 136 : const tarch::la::Vector<MD_DIM, unsigned int> size(getLocalNumberOfCells() + 2u * getLocalIndexOfFirstCell());
765 :
766 : // iterate over the domain in a red-black manner
767 : #if (MD_DIM > 2)
768 408 : for (unsigned int z = 0; z < 2; z++) {
769 : #endif
770 : #if (MD_DIM > 1)
771 816 : for (unsigned int y = 0; y < 2; y++) {
772 : #endif
773 1632 : for (unsigned int x = 0; x < 2; x++) {
774 : // determine range/ length of blocks for red-black traversal.
775 : // For odd block sizes, we need to do some more work in the
776 : // x/y/z==0-traversals. The second x/y/z==1-traversals are reduced by
777 : // the normal integer-rounding in this case.
778 1088 : const tarch::la::Vector<MD_DIM, unsigned int> lengthVector((cellRange[0] + (cellRange[0] % 2) * (x == 0)) / 2
779 : #if (MD_DIM > 1)
780 : ,
781 1088 : (cellRange[1] + (cellRange[1] % 2) * (y == 0)) / 2
782 : #endif
783 : #if (MD_DIM > 2)
784 : ,
785 1088 : (cellRange[2] + (cellRange[2] % 2) * (z == 0)) / 2
786 : #endif
787 : );
788 1088 : const int length = lengthVector[0]
789 : #if (MD_DIM > 1)
790 1088 : * lengthVector[1]
791 : #endif
792 : #if (MD_DIM > 2)
793 1088 : * lengthVector[2]
794 : #endif
795 : ;
796 :
797 : // parallelise loop for all cells that are to be traversed in this way
798 1088 : printNonGhostCells(true, "host start iterateCellPairsParallel");
799 1088 : Kokkos::parallel_for(
800 587656 : "simplemd::MoleculeContainer::iterateCellPairsParallel", Kokkos::RangePolicy<MainExecSpace>(0, length), KOKKOS_CLASS_LAMBDA(const unsigned int j) {
801 612528 : printNonGhostCells(j == 0, "device start iterateCellPairsParallel");
802 : // compute index of the current cell
803 612528 : unsigned int index = 0;
804 : #if (MD_DIM > 1)
805 612528 : int helpIndex1 = j;
806 612528 : int helpIndex2 = 0;
807 : #endif
808 : unsigned int coordsCell1Buffer;
809 : unsigned int coordsCell2Buffer;
810 :
811 : #if (MD_DIM > 2)
812 : // determine plane within traversed block
813 612528 : helpIndex2 = helpIndex1 / (lengthVector[0] * lengthVector[1]);
814 : // save rest of index in helpIndex1
815 612528 : helpIndex1 = helpIndex1 - helpIndex2 * (lengthVector[0] * lengthVector[1]);
816 : // compute contribution to index
817 612528 : index += (lowerLeftFrontCell[2] + 2 * helpIndex2 + z) * size[0] * size[1];
818 : #endif
819 : #if (MD_DIM > 1)
820 : // determine plane within traversed block
821 612528 : helpIndex2 = helpIndex1 / lengthVector[0];
822 : // save rest of index in helpIndex1
823 612528 : helpIndex1 = helpIndex1 - helpIndex2 * lengthVector[0];
824 : // compute contribution to index
825 612528 : index += (lowerLeftFrontCell[1] + 2 * helpIndex2 + y) * size[0];
826 : // compute contribution for last dimension
827 612528 : index += (lowerLeftFrontCell[0] + 2 * helpIndex1 + x);
828 : #else
829 : index = lowerLeftFrontCell[0] + 2 * j + x;
830 : #endif
831 : #if (MD_DEBUG == MD_YES)
832 : Kokkos::printf("Handle cell %d\n", index);
833 : #endif
834 :
835 612528 : simplemd::LinkedCell cell = (*this)[index];
836 290004 : a.handleCell(cell);
837 : // handle pairs (lower,left,back-oriented cells)
838 8575392 : for (unsigned int i = 0; i < MD_LINKED_CELL_NEIGHBOURS / 2; i++) {
839 : #if (MD_DEBUG == MD_YES)
840 : Kokkos::printf("iterateCellPairs: Pair index %d, %d\n", index + indexOffset[i], index + neighbourOffset[i]);
841 : #endif
842 7962864 : coordsCell1Buffer = index + indexOffset[i];
843 7962864 : coordsCell2Buffer = index + neighbourOffset[i];
844 7962864 : auto cell1 = (*this)[coordsCell1Buffer];
845 7962864 : auto cell2 = (*this)[coordsCell2Buffer];
846 7962864 : a.handleCellPair(cell1, cell2, coordsCell1Buffer, coordsCell2Buffer);
847 : }
848 612528 : printNonGhostCells(j == 0, "device end iterateCellPairsParallel");
849 : }); // j, Kokkos::parallel_for
850 1088 : Kokkos::fence(); // Ensure results are available on the host
851 1088 : printNonGhostCells(true, "host end iterateCellPairsParallel");
852 : } // x
853 : #if (MD_DIM > 1)
854 : } // y
855 : #endif
856 : #if (MD_DIM > 2)
857 : } // z
858 : #endif
859 : // end iteration();
860 : a.endCellIteration();
861 136 : }
862 :
863 : template <class A> void simplemd::MoleculeContainer::iterateCellPairsParallel(A& a) {
864 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationStart(0);
865 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationLength(getLocalNumberOfCells() + getLocalIndexOfFirstCell());
866 : iterateCellPairsParallel(a, pairIterationStart, pairIterationLength);
867 : }
868 :
869 : template <class A>
870 140 : void simplemd::MoleculeContainer::iterateCellPairs(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
871 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
872 : if constexpr (A::IsParallel) {
873 136 : iterateCellPairsParallel(a, lowerLeftFrontCell, cellRange);
874 : } else {
875 4 : iterateCellPairsSerial(a, lowerLeftFrontCell, cellRange);
876 : }
877 140 : }
878 :
879 140 : template <class A> void simplemd::MoleculeContainer::iterateCellPairs(A& a) {
880 140 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationStart(0);
881 140 : const tarch::la::Vector<MD_DIM, unsigned int> pairIterationLength(getLocalNumberOfCells() + getLocalIndexOfFirstCell());
882 140 : iterateCellPairs(a, pairIterationStart, pairIterationLength);
883 140 : }
884 :
885 : template <class A>
886 8 : void simplemd::MoleculeContainer::iterateCells(A& a, const tarch::la::Vector<MD_DIM, unsigned int>& lowerLeftFrontCell,
887 : const tarch::la::Vector<MD_DIM, unsigned int>& cellRange) {
888 : if constexpr (A::IsParallel) {
889 4 : iterateCellsParallel(a, lowerLeftFrontCell, cellRange);
890 : } else {
891 4 : iterateCellsSerial(a, lowerLeftFrontCell, cellRange);
892 : }
893 8 : }
894 :
895 8 : template <class A> void simplemd::MoleculeContainer::iterateCells(A& a) { iterateCells(a, _ghostCellLayerThickness, _numLocalCellsNoGhost); }
896 :
897 : #endif // _MOLECULARDYNAMICS_MOLECULARCONTAINER_H_
|