Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
gpu_acc_map.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Vlasiator.
3 * Copyright 2010-2025 Finnish Meteorological Institute and University of Helsinki
4 *
5 * For details of usage, see the COPYING file and read the "Rules of the Road"
6 * at http://www.physics.helsinki.fi/vlasiator/
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23#include "gpu_acc_map.hpp"
25
26
27/* These macros are used for bank collision reduction on CUDA hardware in the
28 scan_probe kernel.
29 NUM_BANKS and LOG_NUM_BANKS are defined in splitvector headers (32 and 5)
30 TODO: Which one provides best bank conflict avoidance? Depends on hardware?
31 On some hardware this gives the warning #63-D: shift count is too large, yet works.
32*/
33#define LOG_BANKS 4
34#ifdef USE_CUDA // Nvidia hardware
35#define BANK_OFFSET(n) \
36 ((n) >> (LOG_BANKS) + (n) >> (2 * LOG_BANKS))
37//#define BANK_OFFSET(n) ((n) >> LOG_BANKS) // segfaults, do not use
38#else // AMD hardware
39#define BANK_OFFSET(n) 0 // Reduces to no bank conflict elimination
40#endif
41
64__global__ void prefill_probe_kernel(
65 vmesh::VelocityMesh** __restrict__ vmeshes,
66 vmesh::LocalID *dev_probeCubeData,
67 const uint flatExtent,
68 const size_t Dacc,
69 const size_t Dother,
70 const vmesh::LocalID invalidLID,
71 // Pass these for emptying
72 split::SplitVector<vmesh::GlobalID>* *lists_with_replace_new,
73 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* *lists_delete,
74 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* *lists_to_replace,
75 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* *lists_with_replace_old,
76 // This one is resized and re-used as a LIDlist
77 split::SplitVector<vmesh::GlobalID> ** dev_vbwcl_vec,
78 const uint cumulativeOffset,
79 const size_t gpu_probeStride
80 ) {
81 const size_t ind = blockIdx.x * blockDim.x + threadIdx.x;
82 const uint parallelOffsetIndex = blockIdx.y;
84 const size_t nTot = Dacc*Dother;
85 vmesh::LocalID* probeFlattened = dev_probeCubeData + parallelOffsetIndex * gpu_probeStride;
86 //vmesh::LocalID* probeCube = probeFlattened + flatExtent*GPU_PROBEFLAT_N;
87
88 if (ind < flatExtent*GPU_PROBEFLAT_N) {
89 // Flattened probe region
90 probeFlattened[ind] = 0;
91 } else if (ind < flatExtent*GPU_PROBEFLAT_N + nTot) {
92 // Probe cube region
93 probeFlattened[ind] = invalidLID;
94 }
95 // Device clears from single thread per cell
96 if (ind==0) {
98 lists_delete[cellOffset]->clear();
99 lists_to_replace[cellOffset]->clear();
100 lists_with_replace_old[cellOffset]->clear();
101 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellOffset];
102 const vmesh::LocalID nBlocks = vmesh->size();
103 dev_vbwcl_vec[cellOffset]->device_resize(nBlocks,false); // false: do not construct / reset new entries
104 }
105}
106
114__global__ void fill_VBC_zero_kernel(
115 vmesh::VelocityBlockContainer** blockContainers,
116 const uint cumulativeOffset
117 ) {
118 const uint parallelOffsetIndex = blockIdx.y;
120 vmesh::VelocityBlockContainer* blockContainer = blockContainers[cellOffset];
121 const size_t VBC_size = blockContainer->size() * WID3;
122 Realf *blockData = blockContainer->getData();
123
124 const size_t ind = blockIdx.x * blockDim.x + threadIdx.x;
125 for (size_t i = ind; i < VBC_size; i += gridDim.x * blockDim.x) {
126 blockData[i] = 0;
127 }
128}
129
163__global__ void fill_probe_ordered(
164 vmesh::VelocityMesh** __restrict__ vmeshes,
165 vmesh::LocalID *dev_probeCubeData, // recast to vmesh::LocalID *probeCube
166 const uint flatExtent,
167 const uint* __restrict__ gpu_block_indices_to_probe,
168 const uint cumulativeOffset,
169 const size_t gpu_probeStride
170 ) {
171 const int ti = threadIdx.x; // [0,Hashinator::defaults::MAX_BLOCKSIZE)
172 const vmesh::LocalID LID = blockDim.x * blockIdx.x + ti;
173 const uint parallelOffsetIndex = blockIdx.y;
175 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellOffset];
176 const vmesh::LocalID nBlocks = vmesh->size();
177
178 if (LID >= nBlocks) {
179 return;
180 }
181 vmesh::LocalID* probeFlattened = dev_probeCubeData + parallelOffsetIndex * gpu_probeStride;
182 vmesh::LocalID* probeCube = probeFlattened + flatExtent*GPU_PROBEFLAT_N;
183 // Store in probe cube with ordering so that reading will be fast
184 const vmesh::GlobalID GID = vmesh->getGlobalID(LID);
185 vmesh::LocalID indices[3];
186 vmesh->getIndices(GID,indices[0],indices[1],indices[2]);
187
188 // Use pre-calculated probe indices
189 const int target = indices[0] * gpu_block_indices_to_probe[0]
190 + indices[1] * gpu_block_indices_to_probe[1]
191 + indices[2] * gpu_block_indices_to_probe[2];
192
193 probeCube[target] = LID;
194}
195
218__global__ void flatten_probe_cube(
219 vmesh::LocalID *dev_probeCubeData, // recast to vmesh::LocalID *probeCube, *probeFlattened
220 const vmesh::LocalID Dacc,
221 const vmesh::LocalID Dother,
222 const size_t flatExtent,
223 const vmesh::LocalID invalidLID,
224 const size_t gpu_probeStride
225 ) {
226 // Probe cube contents have been ordered based on acceleration dimesion
227 // so this kernel always reads in the same way.
228
229 const int ti = threadIdx.x; // [0,Hashinator::defaults::MAX_BLOCKSIZE)
230 const vmesh::LocalID ind = blockDim.x * blockIdx.x + ti;
231 const uint parallelOffsetIndex = blockIdx.y;
232
233 vmesh::LocalID* probeFlattened = dev_probeCubeData + parallelOffsetIndex * gpu_probeStride;
234 vmesh::LocalID* probeCube = probeFlattened + flatExtent*GPU_PROBEFLAT_N;
235
236 if (ind < Dother) {
237 // Per-thread counters
238 vmesh::LocalID foundBlocks = 0;
239 vmesh::LocalID foundCols = 0;
240 bool inCol = false;
241
242 for (vmesh::LocalID j = 0; j < Dacc; j++) {
243 if (probeCube[j*Dother + ind] == invalidLID) {
244 // No block at this index.
245 if (inCol) {
246 // finish current column
247 foundCols++;
248 inCol = false;
249 }
250 } else {
251 // Valid block found at this index
252 foundBlocks++;
253 if (!inCol) {
254 // start new column
255 inCol = true;
256 }
257 }
258 }
259 // Finished loop. If we are "still in a colum", count that.
260 if (inCol) {
261 foundCols++;
262 }
263 // Store values in global memory array
264 probeFlattened[ind] = foundCols;
265 probeFlattened[flatExtent + ind] = foundBlocks;
266 }
267}
268
305
306__global__ void scan_probe(
307 vmesh::VelocityMesh** __restrict__ vmeshes,
308 vmesh::LocalID *dev_probeCubeData, // recast to vmesh::LocalID *probeFlattened
309 const vmesh::LocalID Dacc,
310 const vmesh::LocalID Dother,
311 const size_t flatExtent,
312 vmesh::LocalID *dev_numCols,
313 vmesh::LocalID *dev_numColSets,
316 const uint cumulativeOffset,
317 const size_t gpu_probeStride
318 ) {
319 const uint parallelOffsetIndex = blockIdx.y;
321 vmesh::LocalID* probeFlattened = dev_probeCubeData + parallelOffsetIndex * gpu_probeStride;
322 //vmesh::LocalID* probeCube = probeFlattened + flatExtent*GPU_PROBEFLAT_N;
323
324 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellOffset];
325 const vmesh::LocalID nBlocks = vmesh->size(); // For early exit
326
327 // Per-thread counters in shared memory for reduction. Double size buffer for better bank conflict avoidance.
328 const int n = 2*Hashinator::defaults::MAX_BLOCKSIZE;
329 __shared__ vmesh::LocalID reductionA[2*Hashinator::defaults::MAX_BLOCKSIZE]; // columns
330 __shared__ vmesh::LocalID reductionB[2*Hashinator::defaults::MAX_BLOCKSIZE]; // columnsets
331 __shared__ vmesh::LocalID reductionC[2*Hashinator::defaults::MAX_BLOCKSIZE]; // blocks
332 __shared__ vmesh::LocalID offsetA;
333 __shared__ vmesh::LocalID offsetB;
334 __shared__ vmesh::LocalID offsetC;
335
336 const int ti = threadIdx.x;
337 if (ti==0) { // Cumulative result gathered per cycle
338 offsetA = 0;
339 offsetB = 0;
340 offsetC = 0;
341 }
344 size_t majorOffset = 0;
345 // Utilizes bank conflict avoidance scheme. To simplify handling, the input buffer
346 // is enforced to be a multiple of 2*Hashinator::defaults::MAX_BLOCKSIZE in size.
347 while ((majorOffset < flatExtent) && (offsetC<nBlocks)) {
348 int offset = 1;
349 // Load input into shared memory
350 int ai = ti;
351 int bi = ti + (n/2);
352 int bankOffsetA = BANK_OFFSET(ai);
353 int bankOffsetB = BANK_OFFSET(bi);
354 reductionA[ai + bankOffsetA] = probeFlattened[majorOffset + ai];
355 reductionA[bi + bankOffsetB] = probeFlattened[majorOffset + bi];
356 reductionB[ai + bankOffsetA] = (probeFlattened[majorOffset + ai] != 0 ? 1 : 0);
357 reductionB[bi + bankOffsetB] = (probeFlattened[majorOffset + bi] != 0 ? 1 : 0);
358 reductionC[ai + bankOffsetA] = probeFlattened[flatExtent + majorOffset + ai];
359 reductionC[bi + bankOffsetB] = probeFlattened[flatExtent + majorOffset + bi];
360
361 // build sum in place up the tree
362 for (int d = n>>1; d > 0; d >>= 1) {
364 if (ti < d) {
365 int ai = offset*(2*ti+1)-1;
366 int bi = offset*(2*ti+2)-1;
367 ai += BANK_OFFSET(ai);
368 bi += BANK_OFFSET(bi);
369 reductionA[bi] += reductionA[ai];
370 reductionB[bi] += reductionB[ai];
371 reductionC[bi] += reductionC[ai];
372 }
373 offset *= 2;
374 }
375 // Clear the last element
376 if (ti==0) {
377 reductionA[n - 1 + BANK_OFFSET(n - 1)] = 0;
378 reductionB[n - 1 + BANK_OFFSET(n - 1)] = 0;
379 reductionC[n - 1 + BANK_OFFSET(n - 1)] = 0;
380 }
381
382 // traverse down tree & build scan
383 for (int d = 1; d < n; d *= 2) {
384 offset >>= 1;
386 if (ti < d) {
387 int ai = offset*(2*ti+1)-1;
388 int bi = offset*(2*ti+2)-1;
389 ai += BANK_OFFSET(ai);
390 bi += BANK_OFFSET(bi);
391
392 vmesh::LocalID t = reductionA[ai];
393 reductionA[ai] = reductionA[bi];
394 reductionA[bi] += t;
395 t = reductionB[ai];
396 reductionB[ai] = reductionB[bi];
397 reductionB[bi] += t;
398 t = reductionC[ai];
399 reductionC[ai] = reductionC[bi];
400 reductionC[bi] += t;
401 }
402 }
404
405 // write results to device memory, increment majorOffset and offsetA/B/C.
406 // Remember:
407 // The flattened version must store:
408 // 1) how many columns per potential column position (potColumn) (input for this kernel)
409 // 2) how many blocks per potColumn (input for this kernel)
410 // 3) cumulative offset into columns per potColumn (output for this kernel)
411 // 4) cumulative offset into columnSets per potColumn (output for this kernel)
412 // 5) cumulative offset into blocks per potColumn (output for this kernel)
413
414 probeFlattened[2*flatExtent + majorOffset + ai] = reductionA[ai + bankOffsetA] + offsetA;
415 probeFlattened[2*flatExtent + majorOffset + bi] = reductionA[bi + bankOffsetB] + offsetA;
416 probeFlattened[3*flatExtent + majorOffset + ai] = reductionB[ai + bankOffsetA] + offsetB;
417 probeFlattened[3*flatExtent + majorOffset + bi] = reductionB[bi + bankOffsetB] + offsetB;
418 probeFlattened[4*flatExtent + majorOffset + ai] = reductionC[ai + bankOffsetA] + offsetC;
419 probeFlattened[4*flatExtent + majorOffset + bi] = reductionC[bi + bankOffsetB] + offsetC;
420 // Advance to reading next section of input buffer
421 majorOffset += n;
422 // Increment cumulative offset (exclusive sum result of last bin + contents of that one)
424 if (ti==0) {
425 offsetA += reductionA[n-1] + probeFlattened[majorOffset-1];
426 offsetB += reductionB[n-1] + (probeFlattened[majorOffset-1] != 0 ? 1 : 0);
427 offsetC += reductionC[n-1] + probeFlattened[flatExtent + majorOffset-1];
428 }
430 }
432 if (ti == 0) {
433 // Store reduction results
434 const vmesh::LocalID numCols = offsetA;
435 const vmesh::LocalID numColSets = offsetB;
436 //printf("found columns %u and columnsets %u, %u blocks vs %d\n",numCols,numColSets,offsetC,nBlocks);
437 dev_numCols[parallelOffsetIndex] = numCols; // Total number of columns
438 dev_numColSets[parallelOffsetIndex] = numColSets; // Total number of column sets
439 // Resize device-side column offset container vectors. First verify capacity.
440 // set dev_resizeSuccess to unity to indicate if re-capacitate on host is needed.
441 if ( (columnData->dev_capacityCols() < numCols) ||
442 (columnData->dev_capacityColSets() < numColSets) ) {
444 return;
445 } else {
447 }
448 columnData->device_setSizes(numCols,numColSets);
449 }
450
451 // Todo: unrolling of reduction loops to get even more performance.
452 // Memos:
453 // Perform all-prefix-sum to gather offsets
454 // Look at e.g.
455 // https://developer.nvidia.com/gpugems/gpugems3/part-vi-gpu-computing/chapter-39-parallel-prefix-sum-scan-cuda
456 // Example 39-2 onwards
457 // See also splitvector's stream compaction mechanism and
458 // Credits to https://www.eecs.umich.edu/courses/eecs570/hw/parprefix.pdf
459 // Should also be made to work with arbitrary size buffers, not just powers-of-two
460}
461
486__global__ void build_column_offsets(
487 vmesh::VelocityMesh** __restrict__ vmeshes,
488 vmesh::LocalID* dev_probeCubeData, // recast to vmesh::LocalID *probeCube, *probeFlattened
489 const vmesh::LocalID D0,
490 const vmesh::LocalID D1,
491 const vmesh::LocalID D2,
492 const int dimension,
493 const size_t flatExtent,
494 const vmesh::LocalID invalidLID,
496 split::SplitVector<vmesh::GlobalID> ** dev_vbwcl_vec, // use as LIDlist
497 const uint cumulativeOffset,
498 const size_t gpu_probeStride
499 ) {
500 // Probe cube contents have been ordered based on acceleration dimesion
501 // so this kernel always reads in the same way.
502
503 const int ti = threadIdx.x; // [0,Hashinator::defaults::MAX_BLOCKSIZE)
504 const vmesh::LocalID ind = blockDim.x * blockIdx.x + ti;
505
506 const uint parallelOffsetIndex = blockIdx.y;
508
509 // Caller function verified this cast is safe
510 vmesh::LocalID* LIDlist = reinterpret_cast<vmesh::LocalID*>(dev_vbwcl_vec[cellOffset]->data());
512
513 vmesh::LocalID* probeFlattened = dev_probeCubeData + parallelOffsetIndex * gpu_probeStride;
514 vmesh::LocalID* probeCube = probeFlattened + flatExtent*GPU_PROBEFLAT_N;
515
516 // definition: potColumn is a potential column(set), i.e. a stack from the probe cube.
517 // potColumn indexes/offsets into columnData and LIDlist
518 const vmesh::LocalID N_cols = probeFlattened[ind];
519 const vmesh::LocalID offset_cols = probeFlattened[2*flatExtent + ind];
520 const vmesh::LocalID offset_colsets = probeFlattened[3*flatExtent + ind];
521 const vmesh::LocalID offset_blocks = probeFlattened[4*flatExtent + ind];
522
523 // Here we use ind to back-calculate the transverse "x" and "y" indices (i,j) of the column(set).
524 // which is by agreement propagated in the "z"-direction. TODO: This could probably be done through
525 // multiplication of indices and pre-computed multipliers as is done in many other kernels, but this
526 // works well enough.
528 vmesh::LocalID Dacc, Dother;
529 switch (dimension) {
530 case 0:
531 // propagate along x
532 Dacc = D0;
533 Dother = D1*D2;
534 i = ind % D1; // Z (last dimension)
535 j = ind / D1; // Y
536 break;
537 case 1:
538 // propagate along y
539 Dacc = D1;
540 Dother = D0*D2;
541 i = ind / D2; // X
542 j = ind % D2; // Z (last dimension)
543 break;
544 case 2:
545 // propagate along z
546 Dacc = D2;
547 Dother = D0*D1;
548 i = ind / D1; // X
549 j = ind % D1; // Y (last dimension)
550 break;
551 default:
552 assert("ERROR! incorrect dimension!\n");
553 return;
554 }
555 if (ind < Dother) {
556 if (N_cols != 0) {
557 // Update values in columnSets vector
558 columnData->setColumnOffsets[offset_colsets] = offset_cols;
559 columnData->setNumColumns[offset_colsets] = N_cols;
560 }
561 // Per-thread counters
562 vmesh::LocalID foundBlocks = 0;
563 vmesh::LocalID foundBlocksThisCol = 0;
564 vmesh::LocalID foundCols = 0;
565 bool inCol = false;
566
567 // Loop through acceleration dimension of cube
568 for (vmesh::LocalID k = 0; k < Dacc; k++) {
569 // Early return when all columns have been completed
570 if (foundCols >= N_cols) {
571 return;
572 }
573 const vmesh::LocalID LID = probeCube[k*Dother + ind];
574 if (LID == invalidLID) {
575 // No block at this index.
576 if (inCol) {
577 // finish current column
578 columnData->columnNumBlocks[offset_cols + foundCols] = foundBlocksThisCol;
579 foundCols++;
580 inCol = false;
581 }
582 } else {
583 // Valid block found at this index!
584 // Store LID into buffer
585 LIDlist[offset_blocks + foundBlocks] = LID;
586 if (!inCol) {
587 // start new column
588 inCol = true;
589 foundBlocksThisCol = 0;
590 columnData->columnBlockOffsets[offset_cols + foundCols] = offset_blocks + foundBlocks;
591 columnData->i[offset_cols + foundCols] = i;
592 columnData->j[offset_cols + foundCols] = j;
593 columnData->kBegin[offset_cols + foundCols] = k;
594 }
595 foundBlocks++;
596 foundBlocksThisCol++;
597 }
598 }
599 // Finished loop. If we are "still in a colum", count that.
600 if (inCol) {
601 columnData->columnNumBlocks[offset_cols + foundCols] = foundBlocksThisCol;
602 }
603 }
604}
605
665__global__ void __launch_bounds__(WID3) reorder_blocks_by_dimension_kernel(
666 vmesh::VelocityBlockContainer** __restrict__ blockContainers,
668 const uint* __restrict__ gpu_cell_indices_to_id,
669 split::SplitVector<vmesh::GlobalID> ** dev_vbwcl_vec, // use as LIDlist
671 vmesh::LocalID* dev_nColumns,
672 const uint cumulativeOffset
673 ) {
674 // This is launched with block size (WID,WID,WID)
675 const uint ti = threadIdx.z*blockDim.x*blockDim.y + threadIdx.y*blockDim.x + threadIdx.x;
676 // Acceleration direction becomes "z"
677 const uint sourcei = threadIdx.x*gpu_cell_indices_to_id[0]
678 + threadIdx.y*gpu_cell_indices_to_id[1]
679 + threadIdx.z*gpu_cell_indices_to_id[2];
680
681 const uint iColumn = blockIdx.x;
682 const uint parallelOffsetIndex = blockIdx.y;
684 // Early return if already dealt with all columns
685 if (iColumn >= dev_nColumns[parallelOffsetIndex]) {
686 return;
687 }
688 const vmesh::VelocityBlockContainer* __restrict__ blockContainer = blockContainers[cellOffset];
690 Realf *gpu_blockDataOrdered = dev_blockDataOrdered[parallelOffsetIndex];
691
692 // Caller function verified this cast is safe
693 vmesh::LocalID* LIDlist = reinterpret_cast<vmesh::LocalID*>(dev_vbwcl_vec[cellOffset]->data());
694
695 // Each gpuBlock deals with one column.
696 const uint inputOffset = columnData->columnBlockOffsets[iColumn];
697 const uint outputOffset = (inputOffset + 2 * iColumn) * WID3;
698 const uint columnLength = columnData->columnNumBlocks[iColumn];
699
700 // Loop over column blocks
701 for (uint b = 0; b < columnLength; b++) {
702 #ifdef DEBUG_ACC
703 assert((inputOffset + b) < blockContainer->size() && "reorder_blocks_by_dimension_kernel too large LID");
704 #endif
705 const vmesh::LocalID LID = LIDlist[inputOffset + b];
706 const Realf* __restrict__ gpu_blockData = blockContainer->getData(LID);
707 // Transpose block so that propagation direction becomes last dimension (z)
708 gpu_blockDataOrdered[outputOffset + (1 + b) * WID3 + ti] = gpu_blockData[sourcei];
709 }
710 // Set first and last blocks to zero
711 gpu_blockDataOrdered[outputOffset + ti] = 0.0;
712 gpu_blockDataOrdered[outputOffset + (columnLength + 1) * WID3 + ti] = 0.0;
713 // Note: this kernel does not memset gpu_blockData to zero, there is a separate kernel for that.
714}
715
746 __global__ void __launch_bounds__(GPUTHREADS,4) evaluate_column_extents_kernel(
747 const uint dimension,
748 vmesh::VelocityMesh** __restrict__ vmeshes,
750 split::SplitVector<vmesh::GlobalID>* *lists_with_replace_new,
751 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* *allMaps,
752 const uint* __restrict__ gpu_block_indices_to_id,
755 const int max_v_length,
757 const Realf dv,
758 vmesh::LocalID *dev_resizeSuccess, // bailout flag: splitvector list_with_replace_new capacity error
759 vmesh::LocalID *dev_overflownElements, // bailout flag: touching velspace wall
761 ) {
762 const uint warpSize = blockDim.x;
763 const uint setIndex = blockIdx.x;
764 const uint ti = threadIdx.x;
765
766 const uint parallelOffsetIndex = blockIdx.y;
768
774
775 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *dev_map_require = allMaps[2*cellOffset];
776 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID> *dev_map_remove = allMaps[2*cellOffset+1];
777 split::SplitVector<vmesh::GlobalID> *list_with_replace_new = lists_with_replace_new[cellOffset];
779 // Shared within all threads in one block (one columnSet)
782
783 if (setIndex < columnData->setColumnOffsets.size()) {
784
785 // Clear flags used for this columnSet
786 for(uint tti = 0; tti < MAX_BLOCKS_PER_DIM; tti += warpSize ) {
787 const uint index = tti + ti;
788 if (index < MAX_BLOCKS_PER_DIM) {
789 isTargetBlock[index] = 0;
790 isSourceBlock[index] = 0;
791 }
792 }
794
795 /*need x,y coordinate of this column set */
796 const vmesh::LocalID set_i = columnData->i[columnData->setColumnOffsets[setIndex]];
797 const vmesh::LocalID set_j = columnData->j[columnData->setColumnOffsets[setIndex]];
798
799 /* Compute the maximum starting point of the lagrangian (target) grid
800 within the 4 corner cells in this block. Needed for computing
801 maximum extent of target column.
802 */
803
804 Realf intersectionMins[4];
805 intersectionMins[0] = intersection + (set_i * WID + 0) * intersection_di +
806 (set_j * WID + 0) * intersection_dj;
807 intersectionMins[1] = intersection + (set_i * WID + 0) * intersection_di +
808 (set_j * WID + WID - 1) * intersection_dj;
809 intersectionMins[2] = intersection + (set_i * WID + WID - 1) * intersection_di +
810 (set_j * WID + 0) * intersection_dj;
811 intersectionMins[3] = intersection + (set_i * WID + WID - 1) * intersection_di +
812 (set_j * WID + WID - 1) * intersection_dj;
813
814 Realf min_intersectionMin = std::min(std::min(intersectionMins[0],intersectionMins[1]),
815 std::min(intersectionMins[2],intersectionMins[3]));
816 Realf max_intersectionMin = std::max(std::max(intersectionMins[0],intersectionMins[1]),
817 std::max(intersectionMins[2],intersectionMins[3]));
818
819 // Now record which blocks are target blocks
820 for (uint columnIndex = columnData->setColumnOffsets[setIndex];
821 columnIndex < columnData->setColumnOffsets[setIndex] + columnData->setNumColumns[setIndex] ;
822 ++columnIndex) {
823 // Not parallelizing this at this level; not going to be many columns within a set
824 // (and we want to manage each columnSet within one block)
825
826 const vmesh::LocalID n_cblocks = columnData->columnNumBlocks[columnIndex];
827 const vmesh::LocalID kBegin = columnData->kBegin[columnIndex];
828 const vmesh::LocalID kEnd = kBegin + n_cblocks -1;
829
830 /* firstBlockV is in z the minimum velocity value of the lower
831 * edge in source grid.
832 * lastBlockV is in z the maximum velocity value of the upper
833 * edge in source grid. */
834 const Realf firstBlockMinV = (WID * kBegin) * dv + v_min;
835 const Realf lastBlockMaxV = (WID * (kEnd + 1)) * dv + v_min;
836
837 /* gk is now the k value in terms of cells in target
838 grid. This distance between max_intersectionMin (so lagrangian
839 plan, well max value here) and V of source grid, divided by
840 intersection_dk to find out how many grid cells that is*/
841 const int firstBlock_gk = (int)((firstBlockMinV - max_intersectionMin)/intersection_dk);
842 const int lastBlock_gk = (int)((lastBlockMaxV - min_intersectionMin)/intersection_dk);
843
844 int firstBlockIndexK = firstBlock_gk/WID;
845 int lastBlockIndexK = lastBlock_gk/WID;
846
847 // now enforce mesh limits for target column blocks (and check if we are
848 // too close to the velocity space boundaries)
849 firstBlockIndexK = (firstBlockIndexK >= 0) ? firstBlockIndexK : 0;
850 firstBlockIndexK = (firstBlockIndexK < max_v_length ) ? firstBlockIndexK : max_v_length - 1;
851 lastBlockIndexK = (lastBlockIndexK >= 0) ? lastBlockIndexK : 0;
852 lastBlockIndexK = (lastBlockIndexK < max_v_length ) ? lastBlockIndexK : max_v_length - 1;
853 if(firstBlockIndexK < bailout_velocity_space_wall_margin
854 || firstBlockIndexK >= max_v_length - bailout_velocity_space_wall_margin
855 || lastBlockIndexK < bailout_velocity_space_wall_margin
857 ) {
858 // Pass bailout (hitting the wall) flag back to host
859 if (ti==0) {
861 }
862 }
863
864 //store source blocks
865 for (uint blockK = kBegin; blockK <= kEnd; blockK +=warpSize){
866 if ((blockK+ti) <= kEnd) {
867 isSourceBlock[blockK+ti] = 1; // Does not need to be atomic, as long as it's no longer zero
868 }
869 }
871
872 //store target blocks
873 for (uint blockK = (uint)firstBlockIndexK; blockK <= (uint)lastBlockIndexK; blockK+=warpSize){
874 if ((blockK+ti) <= (uint)lastBlockIndexK) {
875 isTargetBlock[blockK+ti] = 1; // Does not need to be atomic, as long as it's no longer zero
876 }
877 }
879
880 if (ti==0) {
881 // Store for each column firstBlockIndexK, and lastBlockIndexK
882 columnData->minBlockK[columnIndex] = firstBlockIndexK;
883 columnData->maxBlockK[columnIndex] = lastBlockIndexK;
884 }
885 } // end loop over columns in set
887
888 for (uint blockT = 0; blockT < MAX_BLOCKS_PER_DIM; blockT +=warpSize) {
889 const uint blockK = blockT + ti;
890 // Not using warp accessors, as each thread has different block
891 if (blockK < MAX_BLOCKS_PER_DIM) {
892 if (isTargetBlock[blockK] != 0) {
893 const int targetBlock =
894 set_i * gpu_block_indices_to_id[0] +
895 set_j * gpu_block_indices_to_id[1] +
896 blockK * gpu_block_indices_to_id[2];
897 // Templated parameter: do not overwrite existing values
898 dev_map_require->set_element<true>(targetBlock, vmesh->getLocalID(targetBlock));
899 }
900 if (isTargetBlock[blockK] !=0 && isSourceBlock[blockK] == 0 ) {
901 const int targetBlock =
902 set_i * gpu_block_indices_to_id[0] +
903 set_j * gpu_block_indices_to_id[1] +
904 blockK * gpu_block_indices_to_id[2];
905 if (!list_with_replace_new->device_push_back(targetBlock)) {
906 // out of capacity, bailout and gather how much capacity needs to grow
907 atomicAdd(&dev_resizeSuccess[cellOffset],1);
908 }
909 }
910 if (isTargetBlock[blockK] == 0 && isSourceBlock[blockK] != 0 ) {
911 const int targetBlock =
912 set_i * gpu_block_indices_to_id[0] +
913 set_j * gpu_block_indices_to_id[1] +
914 blockK * gpu_block_indices_to_id[2];
915 // Templated parameter: do not overwrite existing values
916 dev_map_remove->set_element<true>(targetBlock, vmesh->getLocalID(targetBlock));
917 }
918 } // block within MAX_BLOCKS_PER_DIM
919 } // loop over all potential blocks
920 } // if valid setIndex
921}
922
923// Use max 2048 per MP threads due to register usage limitations
924#if THREADS_PER_MP < (REGISTERS_PER_MP/64 + 1)
925 #define ACCELERATION_KERNEl_MIN_BLOCKS THREADS_PER_MP/(WID3)
926#else
927 #define ACCELERATION_KERNEl_MIN_BLOCKS (REGISTERS_PER_MP/64)/(WID3)
928#endif
953__global__ void __launch_bounds__(WID3, ACCELERATION_KERNEl_MIN_BLOCKS) acceleration_kernel(
954 vmesh::VelocityMesh** __restrict__ vmeshes, // indexing: cellOffset
955 vmesh::VelocityBlockContainer **blockContainers, // indexing: cellOffset
956 Realf** __restrict__ dev_blockDataOrdered, //indexing: blockIdx.y
957 const uint* __restrict__ gpu_cell_indices_to_id,
958 const uint* __restrict__ gpu_block_indices_to_id,
959 ColumnOffsets* __restrict__ dev_columnOffsetData, //indexing: blockIdx.y
960 const Realf *dev_intersections, // indexing: cellOffset
961 const Realf v_min,
962 const Realf i_dv,
963 const Realf dv,
964 const Real *dev_minValues, // indexing: cellOffset
965 const size_t invalidLID,
966 const uint cumulativeOffset
967) {
968 const uint parallelOffsetIndex = blockIdx.y; // which vlasov buffer allocation to access
970
971 // This is launched with block size (WID,WID,WID)
972 // Indexes into transposed data blocks
973 const int i = threadIdx.x;
974 const int j = threadIdx.y;
975 const int k = threadIdx.z; // Acceleration direction
976 const int ij = threadIdx.x + threadIdx.y * blockDim.x; // transverse index
977 const int ti = ij + k*blockDim.x*blockDim.y;
978
979 const Realf* __restrict__ gpu_blockDataOrdered = dev_blockDataOrdered[parallelOffsetIndex];
981
986
987 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellOffset];
988 vmesh::VelocityBlockContainer *blockContainer = blockContainers[cellOffset];
989 Realf *gpu_blockData = blockContainer->getData();
990
991 // Load minvalues to shared memory
992 __shared__ Realf minValue;
993 __shared__ uint setColumnOffset;
994 __shared__ uint numColumns;
995
996 // shared memory buffer for reducing looping count per block
997 __shared__ int loopN[WID3/GPUTHREADS];
998
999 {
1000 const uint setIndex = blockIdx.x;
1001
1002 if (setIndex >= columnData->dev_sizeColSets()) {
1003 return;
1004 }
1005
1006 if (ti == 0) {
1007 minValue = (Realf)dev_minValues[cellOffset];
1008 setColumnOffset = columnData->setColumnOffsets[setIndex];
1009 numColumns = columnData->setNumColumns[setIndex];
1010 }
1011 }
1012
1013 __syncthreads();
1014
1015 // Kernel must loop over all columns in set to ensure correct writes
1016 for (uint columnIndex = 0;
1017 columnIndex < numColumns;
1018 ++columnIndex) {
1019
1020 const uint column = setColumnOffset + columnIndex;
1021
1022 const Realf v_r0 = ( (Realf)(WID * columnData->kBegin[column]) * dv + v_min);
1023 const int nBlocks = columnData->columnNumBlocks[column];
1024 const int col_i = columnData->i[column];
1025 const int col_j = columnData->j[column];
1026 // Target block-k values for column
1027 const int col_mink = columnData->minBlockK[column];
1028 const int col_maxk = columnData->maxBlockK[column];
1029 const size_t stencilDataOffset = (columnData->columnBlockOffsets[column] + 2*column) * WID3;
1030
1031 // Column index contribution for adjusted velocity block container at correct target GID/LID
1032 const vmesh::GlobalID columnGID = col_i * gpu_block_indices_to_id[0] + col_j * gpu_block_indices_to_id[1];
1033
1034 // Intersection for this cell
1035 const Realf intersection_min =
1037 + intersection_di * (Realf)(col_i * WID + i)
1038 + intersection_dj * (Realf)(col_j * WID + j);
1039 // Pre-computed constant target offset contribution
1040 const int target_cell_index_common = i * gpu_cell_indices_to_id[0]
1041 + j * gpu_cell_indices_to_id[1];
1042
1043 // Loop over blocks in column
1044 for (int b = 0; b < nBlocks; b++) {
1045 const int blockOffset = WID * b; // in units k
1046
1047 int minGk;
1048
1049 {
1050 // Min/max Velocity coordinates in acceleration direction for this block
1051 const Realf min_lagrangian_v_l = v_r0 + blockOffset * dv;
1052 const Realf max_lagrangian_v_r = v_r0 + (blockOffset + WID) * dv;
1053
1054 // Sub-column (single i and j) target k-index extent
1055 const int subcolumnMinGk = int(trunc((min_lagrangian_v_l - intersection_min)/intersection_dk));
1056 const int subcolumnMaxGk = int(trunc((max_lagrangian_v_r - intersection_min)/intersection_dk));
1057
1058 // Truncate to possible output block values
1059 // min-value decreased by (WID-1) so even last slice in sub-column gets to calculate from first gk-index
1060 minGk = std::max(subcolumnMinGk, col_mink * WID) - (WID-1);
1061 const int maxGk = std::min(subcolumnMaxGk, (col_maxk + 1) * WID - 1);
1062
1063 // Reduce Gk loop count
1064 int indexInsideWarp = ti % GPUTHREADS;
1065 int warpIndex = ti / GPUTHREADS;
1066
1067 int val = maxGk - minGk + 1;
1068
1069 for (int offset = GPUTHREADS/2; offset > 0; offset /= 2) {
1070 val = max(val, gpuKernelShflDown(val, offset));
1071 }
1072
1073 if (indexInsideWarp == 0) {
1074 loopN[warpIndex] = val;
1075 }
1076
1077 __syncthreads();
1078
1079 if (warpIndex == 0) {
1080 val = (indexInsideWarp < WID3/GPUTHREADS) ? loopN[indexInsideWarp] : INT_MIN;
1081 for (int offset = GPUTHREADS/2; offset > 0; offset /= 2) {
1082 val = max(val, gpuKernelShflDown(val, offset));
1083 }
1084
1085 if (indexInsideWarp == 0) {
1086 loopN[0] = val; // Store final result
1087 }
1088 }
1089 }
1090
1091 __syncthreads();
1092
1093 // Velocity coordinate in acceleration direction for this cell
1094 const Realf v_l = v_r0 + (blockOffset + k) * dv;
1095 const Realf v_r = v_r0 + (blockOffset + k + 1) * dv;
1096 // Target k-indexing for this cell
1097 const int lagrangian_gk_l = std::trunc((v_l-intersection_min)/intersection_dk);
1098 const int lagrangian_gk_r = std::trunc((v_r-intersection_min)/intersection_dk);
1099
1100 // Compute reconstruction coefficients using WID2 as stride per slice
1101 // read from the offset for this column + the count of source blocks + 1 for an empty source block to begin with
1102 const size_t valuesOffset = stencilDataOffset + (b + 1) * WID3;
1103#ifdef ACC_SEMILAG_PLM
1104 Realf a[2];
1105 compute_plm_coeff(gpu_blockDataOrdered + valuesOffset, k, a, minValue, ij, WID2);
1106#endif
1107#ifdef ACC_SEMILAG_PPM
1108 Realf a[3];
1109 compute_ppm_coeff(gpu_blockDataOrdered + valuesOffset, h4, k, a, minValue, ij, WID2);
1110#endif
1111#ifdef ACC_SEMILAG_PQM
1112 Realf a[5];
1113 compute_pqm_coeff(gpu_blockDataOrdered + valuesOffset, h8, k, a, minValue, ij, WID2);
1114#endif
1115
1116 // set the initial value for the integrand at the boundary at v = 0
1117 // (in reduced cell units), this will be shifted to target_density_1, see below.
1118 Realf target_density_r = (Realf)(0.0);
1119
1120 // Perform the polynomial reconstruction for all cells the mapping streches into
1121 for(int loopgk = 0; loopgk < loopN[0]; loopgk++) {
1122 // Each cell within the subcolumn needs to consider a different gk value so writes don't overlap
1123 const int gk = minGk + loopgk + k;
1124 // // Does this cell need to consider this target gk?
1125 if (gk >= lagrangian_gk_l && gk <= lagrangian_gk_r) {
1126 const int blockK = gk/WID;
1127 const int gk_mod_WID = (gk - blockK * WID);
1128 // the velocities between which we will integrate, in order to put mass
1129 // into the target cell. If both v_r and v_l are in same cell
1130 // then v_1,v_2 should be between v_l and v_r.
1131 // v_1 and v_2 normalized to be between 0 and 1 in the cell.
1132 const Realf v_norm_r = ( std::min( std::max( (gk + 1) * intersection_dk + intersection_min, v_l), v_r) - v_l) * i_dv;
1133
1134 // shift, old right integrand is new left integrand
1135 const Realf target_density_l = target_density_r;
1136
1137 // compute right integrand using FMA
1138 #ifdef ACC_SEMILAG_PLM
1139 target_density_r = a[1];
1140 target_density_r = a[0] + v_norm_r * target_density_r;
1141 target_density_r = v_norm_r * target_density_r;
1142 #endif
1143 #ifdef ACC_SEMILAG_PPM
1144 target_density_r = a[2];
1145 target_density_r = a[1] + v_norm_r * target_density_r;
1146 target_density_r = a[0] + v_norm_r * target_density_r;
1147 target_density_r = v_norm_r * target_density_r;
1148 #endif
1149 #ifdef ACC_SEMILAG_PQM
1150 target_density_r = a[4];
1151 target_density_r = a[3] + v_norm_r * target_density_r;
1152 target_density_r = a[2] + v_norm_r * target_density_r;
1153 target_density_r = a[1] + v_norm_r * target_density_r;
1154 target_density_r = a[0] + v_norm_r * target_density_r;
1155 target_density_r = v_norm_r * target_density_r;
1156
1157 //target_density_r = v_norm_r * ( a[0] + v_norm_r * ( a[1] + v_norm_r * ( a[2] + v_norm_r * ( a[3] + v_norm_r * a[4] ) ) ) );
1158 #endif
1159
1160 // integral area between the two integrands
1161 Realf tval = target_density_r - target_density_l;
1162
1163 // Store directly into adjusted velocity block container at correct target GID/LID
1164 const vmesh::GlobalID targetGID = columnGID + blockK * gpu_block_indices_to_id[2];
1165 const vmesh::LocalID targetLID = vmesh->getLocalID(targetGID);
1166 // The target velocity cell within the target bloxk
1167 const int tcell = target_cell_index_common
1168 + gk_mod_WID * gpu_cell_indices_to_id[2];
1169 // Write values into block data
1170 if (isfinite(tval) && (tval>(Realf)(0.0)) && (targetLID != invalidLID) ) {
1171 // gpu_blockData[targetLID * WID3 + tcell] += tval;
1172
1173 // We use atomicAdd to avoid the need for sync
1174 // It shouldn't be any slower if there is no competition
1175 atomicAdd(&gpu_blockData[targetLID*WID3+tcell],tval);
1176 }
1177 } // end check if gk valid for this thread
1178 } // for loop over target k-indices
1179 } // for-loop over source blocks
1180 } // End this column
1181} // end semilag acc kernel
1182
1183
1212__host__ bool gpu_acc_map_1d(
1213 dccrg::Dccrg<spatial_cell::SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
1214 vector<CellID> &launchCells,
1215 const uint popID,
1216 const uint dimension,
1217 const int Dacc, // velocity block max dimension, direction of acceleration
1218 const int Dother, // Product of other two dimensions (max blocks)
1219 const size_t cumulativeOffset
1220 ) {
1221
1222 phiprof::Timer prepTimer {"preparation"};
1223
1224 // Empty meshes are already excluded in gpu_acc_semilag.cpp
1225 // Sample vmesh from first cell
1226 vmesh::VelocityMesh* sampleVmesh = mpiGrid[launchCells[0]]->get_velocity_mesh(popID);
1227 // These are constant for all cells included in this launch
1228 const vmesh::LocalID D0 = sampleVmesh->getGridLength()[0];
1229 const vmesh::LocalID D1 = sampleVmesh->getGridLength()[1];
1230 const vmesh::LocalID D2 = sampleVmesh->getGridLength()[2];
1231 const Realf dv = sampleVmesh->getCellSize()[dimension];
1232 const Realf v_min = sampleVmesh->getMeshMinLimits()[dimension];
1233 const int max_v_length = (int)sampleVmesh->getGridLength()[dimension];
1234 const Realf i_dv = 1.0/dv;
1235 const vmesh::LocalID invalidLocalID = sampleVmesh->invalidLocalID();
1236
1237 const gpuStream_t baseStream = gpu_getStream();
1238
1257 //const size_t flatExtent = 2*Hashinator::defaults::MAX_BLOCKSIZE * (1 + ((Dother - 1) / (2*Hashinator::defaults::MAX_BLOCKSIZE)));
1258 // This is now pre-computed in gpu_base.cpp
1259 const size_t flatExtent = gpu_probeFlattenedSize;
1260
1266 if constexpr (sizeof(vmesh::LocalID) != sizeof(vmesh::GlobalID)) {
1267 string message = " ERROR! vmesh::LocalID and vmesh::GlobalID are of different sizes, and thus";
1268 message += " the acceleration solver cannot safely use the spatial_cell->dev_list_delete";
1269 message += " Hashinator::splitVector object for storing a list of LIDs.";
1270 bailout(true, message, __FILE__, __LINE__);
1271 }
1272
1273 const uint nLaunchCells = launchCells.size();
1274 size_t largestSizePower = 0;
1275 size_t largestNBefore = 0;
1276 vmesh::LocalID largest_totalColumns = 0;
1277 vmesh::LocalID largest_totalColumnSets = 0;
1278 vmesh::LocalID largest_nAfter = 0;
1279
1280 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1281 const CellID cid = launchCells[cellIndex];
1282 const SpatialCell* SC = mpiGrid[cid];
1283 largestSizePower = std::max(largestSizePower, (size_t)SC->vbwcl_sizePower);
1284 largestSizePower = std::max(largestSizePower, (size_t)SC->vbwncl_sizePower);
1285 largestNBefore = std::max(largestNBefore, (size_t)SC->get_number_of_velocity_blocks(popID));
1286 }
1287 prepTimer.stop();
1288
1289 phiprof::Timer clearTimer {"clear and prepare probe buffers"};
1290 // Clear hash maps used to evaluate block updates
1291 clear_maps_caller(nLaunchCells,largestSizePower,0,cumulativeOffset);
1292
1293 gpu_calculateProbeAllocation(nLaunchCells);
1294 gpuMemoryManager.startSession(0,0);
1295
1297
1298 // probe cube and flattened version now re-use gpu_probeCubeData[cpuThreadID].
1299 // Due to alignment, Flattened version is at start of buffer, followed by the cube.
1300 // Required allocation sizes are calculated in gpu_base.cpp
1301 // Solve launch grid
1302 const size_t probeCombinedSize = gpu_probeFullSize + gpu_probeFlattenedSize * GPU_PROBEFLAT_N;
1303 const size_t n_prefill = 1 + ((probeCombinedSize - 1) / Hashinator::defaults::MAX_BLOCKSIZE);
1304 // This kernel fills the probe cube with invalid values and the flattened one with zeroes.
1305 const dim3 grid_prefill_probe(n_prefill,nLaunchCells,1);
1308 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_probeCubeData), // recast to vmesh::LocalID *probeCube
1309 flatExtent,
1310 Dacc,
1311 Dother,
1312 invalidLocalID,
1313 // Pass vectors for clearing
1314 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new),
1315 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete),
1316 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace),
1317 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old),
1318 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), // dev_velocity_block_with_content_list, // Resize to use as LIDlist
1321 );
1323 CHK_ERR( gpuStreamSynchronize(baseStream) );
1324 clearTimer.stop();
1325
1326 phiprof::Timer fillTimer {"fill probe cube"};
1327 // Read in GID list from vmesh, store LID values into probe cube in correct order
1328 // Launch params, fast ceil for positive ints
1329 const size_t n_fill_ord = 1 + ((largestNBefore - 1) / Hashinator::defaults::MAX_BLOCKSIZE);
1330 const dim3 grid_fill_ord(n_fill_ord,nLaunchCells,1);
1333 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_probeCubeData), // recast to vmesh::LocalID *probeCube
1334 flatExtent,
1335 GET_POINTER(gpuMemoryManager, uint, gpu_block_indices_to_probe),
1338 );
1340 CHK_ERR( gpuStreamSynchronize(baseStream) );
1341 fillTimer.stop();
1342
1343 // Now we perform reductions / flattenings / scans of the probe cube.
1344 // The kernel loops over the acceleration direction (Dacc).
1345 phiprof::Timer flattenTimer {"flatten probe cube"};
1346 const size_t n_grid_cube = 1 + ((Dother - 1) / Hashinator::defaults::MAX_BLOCKSIZE);
1347 const dim3 grid_cube(n_grid_cube,nLaunchCells,1);
1349 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_probeCubeData), // recast to vmesh::LocalID *probeCube, *probeFlattened
1350 Dacc,
1351 Dother,
1352 flatExtent,
1353 invalidLocalID,
1355 );
1357 CHK_ERR( gpuStreamSynchronize(baseStream) );
1358 flattenTimer.stop();
1359
1360 /*
1361 This kernel performs an exclusive prefix scan to get offsets for storing
1362 data from potential columns into the columnData container. Also gives us the total
1363 counts of columns, columnsets, and blocks, and uses the first two to resize
1364 our splitvector containers inside columnData.
1365
1366 A proper prefix scan needs to be a two-phase process, thus two kernels,
1367 but here we do an iterative loop processing MAX_BLOCKSIZE elements at once.
1368 Not as efficient but simpler, and will be parallelized over spatial cells.
1369 */
1370 SESSION_HOST_ALLOCATE(gpuMemoryManager, vmesh::LocalID, host_nColumns, nLaunchCells*sizeof(vmesh::LocalID));
1371 SESSION_HOST_ALLOCATE(gpuMemoryManager, vmesh::LocalID, host_nColumnSets, nLaunchCells*sizeof(vmesh::LocalID));
1372 SESSION_ALLOCATE(gpuMemoryManager, vmesh::LocalID, dev_nColumns, nLaunchCells*sizeof(vmesh::LocalID));
1373 SESSION_ALLOCATE(gpuMemoryManager, vmesh::LocalID, dev_nColumnSets, nLaunchCells*sizeof(vmesh::LocalID));
1374
1375 phiprof::Timer scanTimer {"scan probe cube"};
1376 const dim3 grid_scan(1,nLaunchCells,1);
1379 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_probeCubeData), // recast to vmesh::LocalID *probeFlattened
1380 Dacc,
1381 Dother,
1382 flatExtent,
1389 );
1391
1392 // Copy back to host sizes of found columns etc
1396 CHK_ERR( gpuStreamSynchronize(baseStream) );
1397 scanTimer.stop();
1398
1399 phiprof::Timer allocTimer {"ensure allocations"};
1400 // Ensure allocations (faster without threading)
1401 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1403 // Read count of columns and columnsets, calculate required size of buffers
1405 vmesh::LocalID host_totalColumnSets = (GET_SESSION_HOST_POINTER(gpuMemoryManager, vmesh::LocalID, host_nColumnSets))[cellIndex];
1406 vmesh::LocalID host_recapacitateVectors = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_resizeSuccess))[cellOffset]; // resize of columnData vectors
1407 largest_totalColumns = std::max(largest_totalColumns,host_totalColumns);
1408 largest_totalColumnSets = std::max(largest_totalColumnSets,host_totalColumnSets);
1409 if (host_recapacitateVectors) {
1410 // Can't call CPU reallocation directly as then copies go out of sync.
1411 // This function updates both CPU and GPU copies correctly.
1412 gpu_acc_allocate_perthread(cellIndex, host_totalColumns, host_totalColumnSets);
1413 }
1414 } // end parallel region
1415 allocTimer.stop();
1416
1417 // Now we have gathered all the required offsets into probeFlattened, and can
1418 // now launch a kernel which constructs the columns offsets in parallel.
1419 phiprof::Timer columnsTimer {"build columns"};
1422 GET_SESSION_POINTER(gpuMemoryManager, vmesh::LocalID, dev_probeCubeData), // recast to vmesh::LocalID *probeCube, *probeFlattened
1423 D0,D1,D2,
1424 dimension,
1425 flatExtent,
1426 invalidLocalID,
1428 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), //dev_velocity_block_with_content_list, // use as LIDlist
1431 );
1433 CHK_ERR( gpuStreamSynchronize(baseStream) );
1434 columnsTimer.stop();
1435
1436 phiprof::Timer allocTimer2 {"ensure vlasov allocations"};
1437 // Ensure allocations
1438 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1440 // Read count of columns and columnsets, calculate required size of buffers
1442
1443 const CellID cid = launchCells[cellIndex];
1444 SpatialCell *SC = mpiGrid[cid];
1445 const vmesh::VelocityMesh *thisVmesh = SC->get_velocity_mesh(popID);
1446 const vmesh::LocalID nBlocks = thisVmesh->size();
1447 gpu_vlasov_allocate_perthread(cellIndex, 2*host_totalColumns+nBlocks);
1448 } // end parallel region
1449
1451
1452 allocTimer2.stop();
1453
1454
1455 // Launch kernels for transposing and ordering velocity space data into columns
1456 phiprof::Timer reorderTimer {"reorder blocks"};
1457 const dim3 grid_reorder(largest_totalColumns,nLaunchCells,1);
1458 const dim3 block_reorder(WID,WID,WID);
1459 reorder_blocks_by_dimension_kernel<<<grid_reorder, block_reorder, 0, baseStream>>> (
1462 GET_POINTER(gpuMemoryManager, uint, gpu_cell_indices_to_id),
1463 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), //dev_velocity_block_with_content_list, // use as LIDlist
1467 );
1469 CHK_ERR( gpuStreamSynchronize(baseStream) );
1470 reorderTimer.stop();
1471
1472 gpuMemoryManager.endSession();
1473
1474 phiprof::Timer extentsTimer {"column extents"};
1475 // Reset counters used for verifying sufficient vector capacities and not overflowing v-space
1478
1479 // Calculate target column extents
1480 const dim3 grid_column_extents(largest_totalColumnSets,nLaunchCells,1);
1481 evaluate_column_extents_kernel<<<grid_column_extents, GPUTHREADS, 0, baseStream>>> (
1482 dimension,
1485 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new),
1486 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps),
1491 v_min,
1492 dv,
1493 GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_resizeSuccess), // bailout flag: splitvector list_with_replace_new capacity error
1494 GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_overflownElements), // bailout flag: touching velspace wall
1496 );
1498 // Check whether we exceeded the column data splitVectors on the way or if we need to bailout due to hitting v-space edge
1501 CHK_ERR( gpuStreamSynchronize(baseStream) );
1502 extentsTimer.stop();
1503
1504 phiprof::Timer extents2Timer {"column extents 2"};
1505 bool needSecondLaunchColumnExtents = false;
1506 // Faster without threading
1507 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1508 SpatialCell* SC = mpiGrid[launchCells[cellIndex]];
1509 const uint cellOffset = cellIndex + cumulativeOffset;
1510 if ((GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_resizeSuccess))[cellOffset] != 0) {
1511 needSecondLaunchColumnExtents = true;
1512 // counter indicates how many vector additions failed due to out-of-capacity.
1513 // Recapacitate with added safety factor and gather extents again.
1514 size_t newCapacity = (size_t)((SC->getReservation(popID)+(GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_resizeSuccess))[cellOffset])*BLOCK_ALLOCATION_FACTOR);
1515 SC->setReservation(popID, newCapacity);
1516 SC->applyReservation(popID);
1517 // Clear the vector which receives push_backs. The maps do not need to be cleared.
1518 SC->list_with_replace_new->clear();
1519 }
1520 } // end parallel for
1521
1522 if (needSecondLaunchColumnExtents) {
1523 // Reset counters, upload new pointers to splitvectors
1526 // Think this might not be actually needed, but let's play safe
1527 CHK_ERR( gpuMemcpyAsync(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cumulativeOffset, GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new)+cumulativeOffset, nLaunchCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice, baseStream) );
1528 // Launch kernel a second time (now capacity should be sufficient)
1529 evaluate_column_extents_kernel<<<grid_column_extents, GPUTHREADS, 0, baseStream>>> (
1530 dimension,
1533 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new),
1534 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps),
1539 v_min,
1540 dv,
1541 GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_resizeSuccess), // bailout flag: splitvector list_with_replace_new capacity error
1542 GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_overflownElements), // bailout flag: touching velspace wall
1544 );
1546 // Check whether we exceeded the column data splitVectors on the way and ensure capacity was now sufficient.
1549 CHK_ERR( gpuStreamSynchronize(baseStream) );
1550 }
1551 extents2Timer.stop();
1552
1553 // Bailout checks (faster without threading)
1554 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1555 SpatialCell* SC = mpiGrid[launchCells[cellIndex]];
1556 const uint cellOffset = cellIndex + cumulativeOffset;
1557 // Check if we need to bailout due to hitting v-space edge
1558 if ((GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_overflownElements))[cellOffset] != 0) { //host_wallspace_margin_bailout_flag
1559 string message = "Some target blocks in acceleration are going to be less than ";
1560 message += std::to_string(Parameters::bailout_velocity_space_wall_margin);
1561 message += " blocks away from the current velocity space walls for population ";
1562 message += getObjectWrapper().particleSpecies[popID].name;
1563 message += " at CellID ";
1564 message += std::to_string((uint)SC->parameters[CellParams::CELLID]);
1565 message += ". Consider expanding velocity space for that population.";
1566 bailout(true, message, __FILE__, __LINE__);
1567 }
1568 // Also bail out if recapacitation was insufficient.
1569 if ((GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_resizeSuccess))[cellOffset] != 0) {
1570 string message = "Recapacitation of added velocity blocks vector for population ";
1571 message += " blocks away from the current velocity space walls for population ";
1572 message += getObjectWrapper().particleSpecies[popID].name;
1573 message += " at CellID ";
1574 message += std::to_string((uint)SC->parameters[CellParams::CELLID]);
1575 message += " failed. This should not happen.";
1576 bailout(true, message, __FILE__, __LINE__);
1577 }
1578 } // end parallel for
1579
1583
1584 phiprof::Timer extractTimer {"extract block adjust vectors"};
1585 // TODO: Launch these three extracts in parallel from different streams?
1586 // Finds Blocks (GID,LID) to be rescued from end of v-space
1588 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset, //dev_has_content_maps, // input maps
1589 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old)+cumulativeOffset, // output vecs
1590 NULL, // pass null to not store vector lengths
1592 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset+1, //dev_has_no_content_maps// rule_maps
1593 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cumulativeOffset, // rule_vectors
1594 nLaunchCells,
1595 baseStream
1596 );
1597 // Find Blocks (GID,LID) to be outright deleted
1599 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset+1,//dev_has_no_content_maps, // input maps
1600 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete)+cumulativeOffset, // output vecs
1601 NULL, // pass null to not store vector lengths
1603 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset+1, //dev_has_no_content_maps, // rule_maps
1604 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cumulativeOffset, // rule_vectors
1605 nLaunchCells,
1606 baseStream
1607 );
1608 // Find Blocks (GID,LID) to be replaced with new ones
1610 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset+1,//dev_has_no_content_maps, // input maps
1611 GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace)+cumulativeOffset, // output vecs
1612 NULL, // pass null to not store vector lengths
1614 GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps)+2*cumulativeOffset+1,//dev_has_no_content_maps, // rule_maps
1615 GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new)+cumulativeOffset, // rule_vectors
1616 nLaunchCells,
1617 baseStream
1618 );
1619 CHK_ERR( gpuStreamSynchronize(baseStream) );
1620 extractTimer.stop();
1621
1622 // Note: in this call, unless hitting v-space walls, we only grow the vspace size
1623 // and thus do not delete blocks or replace with old blocks.
1624 // The call now uses the batch block adjust interface.
1625 phiprof::Timer adjustTimer {"block adjust caller"};
1626 uint largestBlocksToChange; // Not needed
1627 uint largestBlocksBeforeOrAfter; // Not needed
1629 mpiGrid,
1630 launchCells,
1632 largestBlocksToChange,
1633 largestBlocksBeforeOrAfter,
1634 popID);
1635 // This caller function updates values in host_nAfter
1636 // Velocity space has now all extra blocks added and/or removed for the transform target
1637 // and will not change shape anymore.
1638 adjustTimer.stop();
1639
1640 // Track of largest vmesh size, evaluate launch parameters for zeroing kernel
1641 phiprof::Timer alloc2Timer {"ensure allocations 2"};
1642 for (size_t cellIndex = 0; cellIndex < nLaunchCells; cellIndex++) {
1643 SpatialCell* SC = mpiGrid[launchCells[cellIndex]];
1644 const uint cellOffset = cellIndex + cumulativeOffset;
1645 // The function batch_adjust_blocks_caller updates host_nAfter
1646 const vmesh::LocalID nBlocksAfterAdjust = (GET_POINTER(gpuMemoryManager, vmesh::LocalID, host_nAfter))[cellOffset];
1647 SC->largestvmesh = SC->largestvmesh > nBlocksAfterAdjust ? SC->largestvmesh : nBlocksAfterAdjust;
1648 largest_nAfter = std::max(largest_nAfter,nBlocksAfterAdjust);
1649 } // end parallel region
1650 alloc2Timer.stop();
1651
1652 /* Zero out target data on device (unified). We could call a separate gpuMemSet for each
1653 blockContainer, but gpuMemSets will just end up calling a kernel under the hood anyway, and
1654 with this kernel of our own we can use one call for all spatial cells at once, and the pointers
1655 to the velocity block containers already reside on-device.
1656 */
1657 phiprof::Timer zeroTimer {"zero target data"};
1658 const size_t n_fill_VBC_zero = 1 + ((largest_nAfter*WID3 - 1) / Hashinator::defaults::MAX_BLOCKSIZE);
1659 const dim3 grid_fill_VBC_zero(n_fill_VBC_zero,nLaunchCells,1);
1661 GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, dev_VBCs), // indexing: cellOffset
1663 );
1665 CHK_ERR( gpuStreamSynchronize(baseStream) );
1666 zeroTimer.stop();
1667
1668 // Launch actual acceleration kernel performing Semi-Lagrangian re-mapping
1669 phiprof::Timer accTimer {"acceleration kernel"};
1670 const dim3 grid_acc(largest_totalColumnSets,nLaunchCells,1);
1671 const dim3 block_acc(WID,WID,WID); // Calculates a whole block at a time
1672 acceleration_kernel<<<grid_acc, block_acc, 0, baseStream>>> (
1673 GET_POINTER(gpuMemoryManager, vmesh::VelocityMesh*, dev_vmeshes), // indexing: cellOffset
1674 GET_POINTER(gpuMemoryManager, vmesh::VelocityBlockContainer*, dev_VBCs), // indexing: cellOffset
1675 GET_POINTER(gpuMemoryManager, Realf*, dev_blockDataOrdered), //indexing: blockIdx.y
1676 GET_POINTER(gpuMemoryManager, uint, gpu_cell_indices_to_id),
1679 GET_POINTER(gpuMemoryManager, Realf, dev_intersections), // indexing: cellOffset
1680 v_min,
1681 i_dv,
1682 dv,
1683 GET_POINTER(gpuMemoryManager, Real, dev_minValues), // indexing: cellOffset, used by slope limiters
1684 invalidLocalID,
1686 );
1688 CHK_ERR( gpuStreamSynchronize(baseStream) );
1689 accTimer.stop();
1690
1691 return true;
1692}
for i
Definition Dispersion.m:24
#define gpuPeekAtLastError
#define gpuStream_t
#define gpuStreamSynchronize
#define gpuMemcpyHostToDevice
#define CHK_ERR(err)
#define gpuMemcpy
#define gpuMemcpyDeviceToHost
#define gpuKernelShflDown(val, offset)
#define gpuMemcpyAsync
#define gpuMemset
#define gpuMemsetAsync
#define GPUTHREADS
vmesh::LocalID get_number_of_velocity_blocks(const uint popID) const
vmesh::VelocityMesh * get_velocity_mesh(const size_t &popID)
split::SplitVector< vmesh::GlobalID > * list_with_replace_new
vmesh::LocalID getReservation(const uint popID) const
void applyReservation(const uint popID)
std::array< Real, CellParams::N_SPATIAL_CELL_PARAMS > parameters
void setReservation(const uint popID, const vmesh::LocalID reservationsize, bool force=false)
ARCH_HOSTDEV vmesh::LocalID size() const
const vmesh::LocalID * getGridLength() const
static vmesh::LocalID invalidLocalID()
const Real * getMeshMinLimits() const
const Real * getCellSize() const
size_t size(bool dummy=0) const
void bailout(const bool condition, const std::string &message, const char *const file, const int line)
A function to stop the simulation if the boolean condition is true. Raises a flag which gets MPI_Redu...
Definition common.cpp:36
#define WID
Definition common.h:514
#define MAX_BLOCKS_PER_DIM
Definition common.h:73
const int WID3
Definition common.h:517
const int WID2
Definition common.h:516
static void compute_plm_coeff(const Vec *const values, const uint k, Vec a[2], const Realf threshold)
static void compute_ppm_coeff(const Vec *const values, const face_estimate_order order, const uint k, Vec a[3], const Realf threshold)
static void compute_pqm_coeff(const Vec *__restrict__ values, face_estimate_order order, uint k, Vec a[5], const Realf threshold)
float Real
Definition definitions.h:41
uint64_t CellID
Definition definitions.h:54
float Realf
Definition definitions.h:33
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets * dev_columnOffsetData
__global__ void fill_VBC_zero_kernel(vmesh::VelocityBlockContainer **blockContainers, const uint cumulativeOffset)
const Realf intersection
__global__ void prefill_probe_kernel(vmesh::VelocityMesh **__restrict__ vmeshes, vmesh::LocalID *dev_probeCubeData, const uint flatExtent, const size_t Dacc, const size_t Dother, const vmesh::LocalID invalidLID, split::SplitVector< vmesh::GlobalID > **lists_with_replace_new, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **lists_delete, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **lists_to_replace, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **lists_with_replace_old, split::SplitVector< vmesh::GlobalID > **dev_vbwcl_vec, const uint cumulativeOffset, const size_t gpu_probeStride)
GPU kernel which fills the target probe cube with the invalid value for vmesh::LocalID.
#define BANK_OFFSET(n)
const uint ti
const Realf intersection_dk
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > ** lists_with_replace_new
const uint cellOffset
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_map_remove
__global__ void vmesh::VelocityMesh **__restrict__ vmeshes
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ gpu_block_indices_to_id
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf vmesh::LocalID vmesh::LocalID const uint cumulativeOffset
__shared__ int isTargetBlock[MAX_BLOCKS_PER_DIM]
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf vmesh::LocalID vmesh::LocalID * dev_overflownElements
__global__ void build_column_offsets(vmesh::VelocityMesh **__restrict__ vmeshes, vmesh::LocalID *dev_probeCubeData, const vmesh::LocalID D0, const vmesh::LocalID D1, const vmesh::LocalID D2, const int dimension, const size_t flatExtent, const vmesh::LocalID invalidLID, ColumnOffsets *dev_columnOffsetData, split::SplitVector< vmesh::GlobalID > **dev_vbwcl_vec, const uint cumulativeOffset, const size_t gpu_probeStride)
GPU kernel for building the columns and columnSets for each spatial cell, and storing offsets and len...
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf v_min
const Realf intersection_di
__global__ void fill_probe_ordered(vmesh::VelocityMesh **__restrict__ vmeshes, vmesh::LocalID *dev_probeCubeData, const uint flatExtent, const uint *__restrict__ gpu_block_indices_to_probe, const uint cumulativeOffset, const size_t gpu_probeStride)
GPU kernel for taking the contents of a vmesh and placign the existing velocity blocks in a probe cub...
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf dv
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > ** allMaps
__global__ void scan_probe(vmesh::VelocityMesh **__restrict__ vmeshes, vmesh::LocalID *dev_probeCubeData, const vmesh::LocalID Dacc, const vmesh::LocalID Dother, const size_t flatExtent, vmesh::LocalID *dev_numCols, vmesh::LocalID *dev_numColSets, vmesh::LocalID *dev_resizeSuccess, ColumnOffsets *dev_columnOffsetData, const uint cumulativeOffset, const size_t gpu_probeStride)
GPU kernel which performs exclusive prefix scans of the flattened probe cube, providing cumulative su...
const uint setIndex
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int max_v_length
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf * dev_intersections
split::SplitVector< vmesh::GlobalID > * list_with_replace_new
__host__ bool gpu_acc_map_1d(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, vector< CellID > &launchCells, const uint popID, const uint dimension, const int Dacc, const int Dother, const size_t cumulativeOffset)
This function performs the semi-Lagrangian acceleration for a provided list of spatial cells,...
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int bailout_velocity_space_wall_margin
__global__ void flatten_probe_cube(vmesh::LocalID *dev_probeCubeData, const vmesh::LocalID Dacc, const vmesh::LocalID Dother, const size_t flatExtent, const vmesh::LocalID invalidLID, const size_t gpu_probeStride)
GPU kernel which flattens the probe cube into two reduction results (counters): how many columns and ...
const Realf intersection_dj
__shared__ int isSourceBlock[MAX_BLOCKS_PER_DIM]
ColumnOffsets * columnData
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf const int const int const Realf const Realf vmesh::LocalID * dev_resizeSuccess
#define ACCELERATION_KERNEl_MIN_BLOCKS
const uint parallelOffsetIndex
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_map_require
__host__ void gpu_acc_allocate_perthread(uint allocID, uint firstAllocationCount, uint columnSetAllocationCount)
Definition gpu_base.cpp:567
GPUMemoryManager gpuMemoryManager
Definition gpu_base.cpp:64
__host__ void gpu_vlasov_allocate_perthread(uint allocID, uint blockAllocationCount)
Definition gpu_base.cpp:421
size_t gpu_probeStride
Definition gpu_base.cpp:57
size_t gpu_probeFullSize
Definition gpu_base.cpp:57
__host__ gpuStream_t gpu_getStream()
Definition gpu_base.cpp:244
size_t gpu_probeFlattenedSize
Definition gpu_base.cpp:57
__host__ void gpu_calculateProbeAllocation(const uint maxBlockCount)
Definition gpu_base.cpp:359
__host__ uint gpu_getAllocationCount()
Definition gpu_base.cpp:259
#define SESSION_HOST_ALLOCATE(object, type, member, bytes)
Definition gpu_base.hpp:600
static const int GPU_PROBEFLAT_N
Definition gpu_base.hpp:65
#define SESSION_ALLOCATE(object, type, member, bytes)
Definition gpu_base.hpp:557
#define GET_SESSION_POINTER(object, type, member)
Definition gpu_base.hpp:833
#define GET_SESSION_HOST_POINTER(object, type, member)
Definition gpu_base.hpp:853
#define GET_POINTER(object, type, member)
Definition gpu_base.hpp:809
static const double BLOCK_ALLOCATION_FACTOR
Definition gpu_base.hpp:61
#define SINGLE_ARG(...)
Definition gpu_base.hpp:280
const int j
__syncthreads()
const int k
__global__ void const Realf const uint *__restrict__ const uint *__restrict__ const vmesh::GlobalID *__restrict__ const uint const uint const uint const Realf const vmesh::VelocityMesh *__restrict__ const vmesh::VelocityBlockContainer Realf Realf ** dev_blockDataOrdered
ObjectWrapper & getObjectWrapper()
Definition main.cpp:33
uint32_t uint
void extract_to_delete_or_move_caller(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **output_vecs, vmesh::LocalID *output_sizes, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
static __global__ void __launch_bounds__(WID3, 4) population_scale_kernel(vmesh
void batch_adjust_blocks_caller(dccrg::Dccrg< spatial_cell::SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const vector< CellID > &cellsToAdjust, const uint cellOffset, uint &out_largestBlocksToChange, uint &out_largestBlocksBeforeOrAfter, const uint popID)
void clear_maps_caller(const uint nCells, const size_t largestSizePower, gpuStream_t stream, const size_t offset)
void extract_to_replace_caller(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **output_vecs, vmesh::LocalID *output_sizes, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
uint32_t LocalID
Definition definitions.h:60
uint32_t GlobalID
Definition definitions.h:59
std::vector< species::Species > particleSpecies
static uint bailout_velocity_space_wall_margin
Definition parameters.h:188
static ARCH_HOSTDEV VecSimple< T > max(VecSimple< T > const &l, VecSimple< T > const &r)