Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
block_adjust_gpu_kernels.hpp
Go to the documentation of this file.
1/*
2 * This file is part of Vlasiator.
3 * Copyright 2010-2024 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#ifndef VLASIATOR_BLOCK_ADJUST_KERNELS_HPP
24#define VLASIATOR_BLOCK_ADJUST_KERNELS_HPP
25
26#ifdef USE_WARPACCESSORS
27 #define USE_BATCH_WARPACCESSORS
28#endif
29
30// __launch_bounds__(MAX_THREADS_PER_BLOCK, MIN_BLOCKS_PER_MP)
31#ifdef __CUDACC__
32#define WARPS_PER_MP 64
33#define FULLBLOCKS_PER_MP THREADS_PER_MP/Hashinator::defaults::MAX_BLOCKSIZE
34#define WID3S_PER_MP (2048/WID3)
35#endif
36#ifdef __HIP_PLATFORM_HCC___
37#define WARPS_PER_MP 8
38#define FULLBLOCKS_PER_MP 1
39#define WID3S_PER_MP 7 // because of batch_update_velocity_blocks_kernel
40#endif
41
43__global__ void __launch_bounds__(WID3,WID3S_PER_MP) batch_update_velocity_block_content_lists_kernel (
44 const vmesh::VelocityMesh* __restrict__ const *vmeshes,
45 const vmesh::VelocityBlockContainer* __restrict__ const *blockContainers,
46 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* *allMaps,
47 const Real* __restrict__ velocity_block_min_values,
48 const bool gatherMass,
49 Real* dev_mass
50 ) {
51 //const uint nCells = gridDim.y;
52 const int cellIndex = blockIdx.y;
53 const int blockiStart = blockIdx.x;
54
55 const uint ti = threadIdx.x;
56
57 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellIndex];
58 const vmesh::VelocityBlockContainer* __restrict__ blockContainer = blockContainers[cellIndex];
59 const Real velocity_block_min_value = velocity_block_min_values[cellIndex];
60 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwcl_map = allMaps[2*cellIndex];
61 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwncl_map = allMaps[2*cellIndex+1];
62
63 #define warpsPerBlockBatchContent WID3/GPUTHREADS
64
65 __shared__ int has_content[warpsPerBlockBatchContent];
66 __shared__ Real gathered_mass[WID3];
67 const uint nBlocks = vmesh->size();
68 #ifdef DEBUG_SPATIAL_CELL
69 if (nBlocks != blockContainer->size()) {
70 if (ti==0) {
71 printf("VBC and vmesh size mismatch in batch_update_velocity_block_content_lists_kernel!\n");
72 }
73 assert(0);
74 }
75 #endif
76 const uint blockLID = blockiStart;
77 {
78 if (blockLID >= nBlocks) {
79 return;
80 }
81 // Check each velocity cell if it is above the threshold
82 const Realf* __restrict__ avgs = blockContainer->getData(blockLID);
83
84 const vmesh::GlobalID blockGID = vmesh->getGlobalID(blockLID);
85 #ifdef DEBUG_SPATIAL_CELL
86 if (blockGID == vmesh->invalidGlobalID()) {
87 if (ti==0) {
88 printf("Invalid GID encountered in batch_update_velocity_block_content_lists_kernel!\n");
89 }
90 assert(0);
91 }
92 if (blockLID == vmesh->invalidLocalID()) {
93 if (ti==0) {
94 printf("Invalid LID encountered in batch_update_velocity_block_content_lists_kernel!\n");
95 }
96 assert(0);
97 }
98 #endif
99
100 bool hasContentThread = (avgs[ti] >= velocity_block_min_value);
101
102 if (gatherMass) {
103 gathered_mass[ti] = avgs[ti];
105 // Perform loop over all elements to gather total mass
106 for (unsigned int s=WID3/2; s>0; s>>=1) {
107 if (ti < s) {
108 gathered_mass[ti] += gathered_mass[ti + s];
109 }
111 }
112 }
113
114 const int indexInsideWarp = ti % GPUTHREADS;
115 const int warpIndex = ti / GPUTHREADS;
116
117 // Check for content with two consecutive warp votes
118 hasContentThread = gpuKernelAny(0xFFFFFFFF, hasContentThread);
119
120 if (WID3 > GPUTHREADS) {
121 if (indexInsideWarp == 0) {
122 has_content[warpIndex] = hasContentThread;
123 }
125
126 if (warpIndex == 0) {
127 hasContentThread = (indexInsideWarp < warpsPerBlockBatchContent) ? has_content[indexInsideWarp] : false;
128 hasContentThread = gpuKernelAny(0xFFFFFFFF, hasContentThread);
129 }
130 }
131
132 #ifdef USE_BATCH_WARPACCESSORS
133 // Insert into map only from threads 0...WARPSIZE
134 if (ti < GPUTHREADS) {
135 if (ti == 0) {
136 has_content[0] = hasContentThread;
137 }
139 if (hasContentThread) {
140 vbwcl_map->warpInsert(blockGID,blockLID,ti);
141 } else {
142 vbwncl_map->warpInsert(blockGID,blockLID,ti);
143 }
144 }
145 #else
146 // Insert into map only from thread 0
147 if (ti == 0) {
148 if (hasContentThread) {
149 vbwcl_map->set_element(blockGID,blockLID);
150 } else {
151 vbwncl_map->set_element(blockGID,blockLID);
152 }
153 }
154 #endif
155 // Store gathered mass as atomic from one thread per block
156 if (gatherMass && (ti == 0)) {
157 Real old = atomicAdd(&dev_mass[cellIndex], gathered_mass[0]);
158 }
159 }
160}
161
162/*
163 * Resets all elements in all provided hashmaps to EMPTY, VAL_TYPE()
164 */
165__global__ void __launch_bounds__(Hashinator::defaults::MAX_BLOCKSIZE, FULLBLOCKS_PER_MP) batch_reset_all_to_empty(
166 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>**maps
167 ) {
168 //launch parameters: dim3 grid(blocksNeeded,nCells,2);
169 const size_t hashmapIndex = blockIdx.y * 2 + blockIdx.z;
170 const size_t tid = threadIdx.x + blockIdx.x * blockDim.x;
171 const size_t stride = gridDim.x * blockDim.x;
172 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* thisMap = maps[hashmapIndex];
173 const size_t len = thisMap->bucket_count();
174 const vmesh::GlobalID emptybucket = thisMap->get_emptybucket();
175 Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>* dst = thisMap->expose_bucketdata<false>();
176
177 for (size_t bucketIndex = tid; bucketIndex < len; bucketIndex += stride) {
178 dst[bucketIndex].first = emptybucket;
179 }
180
181 //Thread 0 resets fill
182 if (tid==0) {
183 Hashinator::Info *info = thisMap->expose_mapinfo<false>();
184 info->fill=0;
185 }
186}
187
188/*
189 * Reads sizes of hashmaps, compares with capacities of provided vectors, and sets the provided buffer
190 * to indicate if the vector needs recapacitating. Assumes the required_capacities buffer has been
191 * memset to zero before this kernel is called.
192 */
193__global__ void __launch_bounds__(Hashinator::defaults::MAX_BLOCKSIZE, FULLBLOCKS_PER_MP) check_vector_capacities(
194 const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* __restrict__ const *maps,
195 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *vecs,
196 vmesh::LocalID *required_capacities
197 ) {
198 const size_t index = threadIdx.x + blockIdx.x * blockDim.x;
199 const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* __restrict__ thisMap = maps[2*index];
200 const split::SplitVector<vmesh::GlobalID>* __restrict__ thisVec = vecs[index];
201 const size_t mapSize = thisMap->size();
202 if (mapSize > thisVec->capacity()) {
203 required_capacities[index] = mapSize;
204 }
205}
206
207/*
208 * Extracts keys (GIDs, if firstonly is true) or key-value pairs (GID-LID pairs)
209 * from all provided hashmaps to provided splitvectors, and stores the vector size in an array.
210 */
211template <typename Rule, typename ELEMENT, bool FIRSTONLY=false>
212__global__ void __launch_bounds__(Hashinator::defaults::MAX_BLOCKSIZE, FULLBLOCKS_PER_MP) extract_GIDs_kernel(
213 const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* __restrict__ const *input_maps, // buffer of pointers to source maps
214 split::SplitVector<ELEMENT> **output_vecs,
215 vmesh::LocalID* output_sizes,
216 Rule rule,
217 const vmesh::VelocityMesh* __restrict__ const *rule_meshes, // buffer of pointers to vmeshes, sizes used by rules
218 const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* __restrict__ const *rule_maps,
219 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *rule_vectors
220 ) {
221 //launch parameters: dim3 grid(nMaps,1,1); // As this is a looping reduction
222 const size_t cellIndex = blockIdx.x;
223 const size_t hashmapIndex = 2*blockIdx.x; // Assumes maps are with a stride of two due to allMaps buffer holding two for each cell
224 if (input_maps[hashmapIndex]==0) {
225 return; // Early return for invalid cells
226 }
227 const Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* __restrict__ thisMap = input_maps[hashmapIndex];
228 split::SplitVector<ELEMENT> *outputVec = output_vecs[cellIndex];
229
230 // Threshold value used by some rules
231 const vmesh::LocalID threshold = rule_meshes[cellIndex]->size()
232 + rule_vectors[cellIndex]->size() - rule_maps[hashmapIndex]->size();
233
234 const vmesh::LocalID invalidLID = rule_meshes[cellIndex]->invalidLocalID();
235 const vmesh::GlobalID invalidGID = rule_meshes[cellIndex]->invalidGlobalID();
236
237 // This must be equal to at least both WARPLENGTH and MAX_BLOCKSIZE/WARPLENGTH
238 __shared__ uint32_t warpSums[WARPLENGTH];
239 __shared__ uint32_t outputCount;
240 const int tid = threadIdx.x;
241 const int wid = tid / WARPLENGTH;
242 const int w_tid = tid % WARPLENGTH;
243 //const int warpsPerBlock = BLOCKSIZE / WARPLENGTH;
244 const size_t warpsPerBlock = blockDim.x / WARPLENGTH;
245 // zero init shared buffer
246 if (wid == 0) {
247 warpSums[w_tid] = 0;
248 }
250 // full warp votes for rule-> mask = [01010101010101010101010101010101]
251 int64_t remaining = thisMap->bucket_count();
252 const uint capacity = outputVec->capacity();
253 uint32_t outputSize = 0;
254 uint32_t inputOffset = 0;
255 // Initial pointers into data
256 //Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID> *input = thisMap->expose_bucketdata<false>();
257 ELEMENT* output = outputVec->data();
258 // Start loop
259 while (remaining > 0) {
260 const Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID>* __restrict__ input = thisMap->expose_bucketdata<false>();
261 const int current = remaining > blockDim.x ? blockDim.x : remaining;
263 const int active = (tid < current) ? rule(thisMap, input[inputOffset + tid], threshold, invalidLID, invalidGID) : false;
264 const auto mask = split::s_warpVote(active == 1, SPLIT_VOTING_MASK);
265 const auto warpCount = split::s_pop_count(mask);
266 if (w_tid == 0) {
267 warpSums[wid] = warpCount;
268 }
270 // Figure out the total here because we overwrite shared mem later
271 if (wid == 0) {
272 // ceil int division
273 int activeWARPS = nextPow2(1 + ((current - 1) / WARPLENGTH));
274 auto reduceCounts = [activeWARPS](int localCount) -> int {
275 for (int i = activeWARPS / 2; i > 0; i = i / 2) {
276 localCount += split::s_shuffle_down(localCount, i, SPLIT_VOTING_MASK);
277 }
278 return localCount;
279 };
280 auto localCount = warpSums[w_tid];
281 const int totalCount = reduceCounts(localCount);
282 if (w_tid == 0) {
283 outputCount = totalCount;
284 outputSize += totalCount;
285 assert((outputSize <= capacity) && "extract_GIDs_kernel ran out of capacity!");
286 outputVec->device_resize(outputSize);
287 }
288 }
289 // Prefix scan WarpSums on the first warp
290 if (wid == 0) {
291 auto value = warpSums[w_tid];
292 for (uint d = 1; d < warpsPerBlock; d = 2 * d) {
293 int res = split::s_shuffle_up(value, (int)d, SPLIT_VOTING_MASK);
294 if (tid % warpsPerBlock >= d) {
295 value += res;
296 }
297 }
298 warpSums[w_tid] = value;
299 }
301 auto offset = (wid == 0) ? 0 : warpSums[wid - 1];
302 auto pp = split::s_pop_count(mask & ((ONE << w_tid) - ONE));
303 const auto warpTidWriteIndex = offset + pp;
304 if (active) {
305 if constexpr (FIRSTONLY) {
306 output[warpTidWriteIndex] = input[inputOffset + tid].first;
307 } else {
308 output[warpTidWriteIndex] = input[inputOffset + tid];
309 }
310 }
311 // Next loop iteration:
312 //input += current;
313 inputOffset += current;
314 output += outputCount;
315 remaining -= current;
316 }
318 if (tid == 0) {
319 // Resize to final correct output size.
320 outputVec->device_resize(outputSize);
321 if (output_sizes) {// Only store lengths if output buffer is not null
322 output_sizes[cellIndex] = outputSize;
323 }
324 }
325}
326
327template <typename Rule, typename ELEMENT, bool FIRSTONLY=false>
329 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** input_maps,
330 split::SplitVector<ELEMENT> **output_vecs,
331 vmesh::LocalID* output_sizes,
332 Rule rule,
333 vmesh::VelocityMesh** rule_meshes,
334 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** rule_maps,
335 split::SplitVector<vmesh::GlobalID>** rule_vectors,
336 const uint nCells,
337 gpuStream_t stream
338 ) {
339 extract_GIDs_kernel<Rule,ELEMENT,FIRSTONLY><<<nCells, Hashinator::defaults::MAX_BLOCKSIZE, 0, stream>>>(
340 input_maps,
341 output_vecs,
342 output_sizes,
343 rule,
344 rule_meshes,
345 rule_maps,
346 rule_vectors
347 );
349}
350
351/*
352 * Extracts key-value (GID-LID) pairs matching the given rule
353 * from the hashmaps of all provided velocity meshes,
354 * stores them in provided splitvectors, and
355 * clears all tombstones and matched elements.
356 */
357template <typename Rule>
358__global__ void __launch_bounds__(Hashinator::defaults::MAX_BLOCKSIZE, FULLBLOCKS_PER_MP) extract_overflown_kernel(
359 vmesh::VelocityMesh **vmeshes, // buffer of pointers to vmeshes, contain hashmaps
360 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>> **output_vecs,
361 vmesh::LocalID* output_sizes,
362 Rule rule
363 ) {
364 //launch parameters: dim3 grid(nMaps,1,1); // As this is a looping reduction
365 const size_t vmeshIndex = blockIdx.x;
366 if (vmeshes[vmeshIndex]==0) {
367 return; // Early return for invalid cells
368 }
369 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* thisMap = vmeshes[vmeshIndex]->gpu_expose_map();
370 Hashinator::Info *info = thisMap->expose_mapinfo<false>();
371 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>> *outputVec = output_vecs[vmeshIndex];
372
373 if (info->tombstoneCounter == 0) {
374 // If there are no tombstones, then also any overflown elements will be minimally overflown.
375 outputVec->device_resize(0);
376 return;
377 }
378 // This must be equal to at least both WARPLENGTH and MAX_BLOCKSIZE/WARPLENGTH
379 __shared__ uint32_t warpSums[WARPLENGTH];
380 __shared__ uint32_t outputCount;
381 const int tid = threadIdx.x;
382 const int wid = tid / WARPLENGTH;
383 const int w_tid = tid % WARPLENGTH;
384 //const int warpsPerBlock = BLOCKSIZE / WARPLENGTH;
385 const uint warpsPerBlock = blockDim.x / WARPLENGTH;
386 // zero init shared buffer
387 if (wid == 0) {
388 warpSums[w_tid] = 0;
389 }
391 // full warp votes for rule-> mask = [01010101010101010101010101010101]
392 int64_t remaining = thisMap->bucket_count();
393 const uint capacity = outputVec->capacity();
394 uint32_t outputSize = 0;
395 // Initial pointers into data
396 Hashinator::hash_pair<vmesh::GlobalID, vmesh::LocalID> *input = thisMap->expose_bucketdata<false>();
397 Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>* output = outputVec->data();
398 const vmesh::GlobalID emptybucket = thisMap->get_emptybucket();
399 // Start loop
400 while (remaining > 0) {
401 int current = remaining > blockDim.x ? blockDim.x : remaining;
403 const int active = (tid < current) ? rule(thisMap, input[tid]) : false;
404 const auto mask = split::s_warpVote(active == 1, SPLIT_VOTING_MASK);
405 const auto warpCount = split::s_pop_count(mask);
406 if (w_tid == 0) {
407 warpSums[wid] = warpCount;
408 }
410 // Figure out the total here because we overwrite shared mem later
411 if (wid == 0) {
412 // ceil int division
413 int activeWARPS = nextPow2(1 + ((current - 1) / WARPLENGTH));
414 auto reduceCounts = [activeWARPS](int localCount) -> int {
415 for (int i = activeWARPS / 2; i > 0; i = i / 2) {
416 localCount += split::s_shuffle_down(localCount, i, SPLIT_VOTING_MASK);
417 }
418 return localCount;
419 };
420 auto localCount = warpSums[w_tid];
421 int totalCount = reduceCounts(localCount);
422 if (w_tid == 0) {
423 outputCount = totalCount;
424 outputSize += totalCount;
425 assert((outputSize <= capacity) && "extract_overflown_kernel ran out of capacity!");
426 outputVec->device_resize(outputSize);
427 }
428 }
429 // Prefix scan WarpSums on the first warp
430 if (wid == 0) {
431 auto value = warpSums[w_tid];
432 for (uint d = 1; d < warpsPerBlock; d = 2 * d) {
433 int res = split::s_shuffle_up(value, (int)d, SPLIT_VOTING_MASK);
434 if (tid % warpsPerBlock >= d) {
435 value += res;
436 }
437 }
438 warpSums[w_tid] = value;
439 }
441 auto offset = (wid == 0) ? 0 : warpSums[wid - 1];
442 auto pp = split::s_pop_count(mask & ((ONE << w_tid) - ONE));
443 const auto warpTidWriteIndex = offset + pp;
444 if (active) {
445 output[warpTidWriteIndex] = input[tid];
446 // Now also delete this entry. Must edit fill count at end of kernel.
447 input[tid].first = emptybucket;
448 }
449 // Next loop iteration:
450 input += current;
451 output += outputCount;
452 remaining -= current;
453 }
455 if (tid == 0) {
456 // Resize to final correct output size.
457 outputVec->device_resize(outputSize);
458 output_sizes[vmeshIndex] = outputSize;
459 // Update mapInfo
460 info->currentMaxBucketOverflow = Hashinator::defaults::BUCKET_OVERFLOW;
461 info->fill -= outputSize; // subtract deleted (overflown) elements
462 info->tombstoneCounter = 0;
463 }
464}
465
466template <typename Rule>
469 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>> **overflown_elements,
470 vmesh::LocalID* output_sizes,
471 Rule rule,
472 const uint nCells,
473 gpuStream_t stream
474 ) {
475 // Extract overflown elements into temporary vector
476 extract_overflown_kernel<Rule><<<nCells, Hashinator::defaults::MAX_BLOCKSIZE, 0, stream>>>(
477 vmeshes,
478 overflown_elements,
479 output_sizes,
480 rule
481 );
483}
484
485/*
486 * Mini-kernel for inserting previously extracted overflown elements
487 */
488__global__ void __launch_bounds__(GPUTHREADS, WARPS_PER_MP) batch_insert_kernel(
489 vmesh::VelocityMesh **vmeshes, // buffer of pointers to vmeshes, contain hashmaps
490 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ const *input_vecs
491 ) {
492 //launch parameters: dim3 grid(largestOverflow,nCells,1);
493 const uint ti = threadIdx.x; // [0,blockSize)
494 const int b_tid = ti % GPUTHREADS; // [0,GPUTHREADS)
495 // GPUTODO: several entries in parallel per block
496 const size_t vmeshIndex = blockIdx.y;
497 const size_t blockIndex = blockIdx.x;
498 if (vmeshes[vmeshIndex]==0) {
499 return; // Early return for invalid cells
500 }
501 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* thisMap = vmeshes[vmeshIndex]->gpu_expose_map();
502 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ inputVec = input_vecs[vmeshIndex];
503
504 const size_t inputVecSize = inputVec->size();
505 if (inputVecSize == 0 || blockIndex >= inputVecSize) {
506 // No elements to insert
507 return;
508 }
509
510 #ifdef USE_BATCH_WARPACCESSORS
511 // Insert into map only from threads 0...WARPSIZE
512 if (b_tid < GPUTHREADS) {
513 #ifdef DEBUG_SPATIAL_CELL
514 thisMap->warpInsert((inputVec->at(blockIndex)).first,(inputVec->at(blockIndex)).second,b_tid);
515 #else
516 thisMap->warpInsert(((*inputVec)[blockIndex]).first,((*inputVec)[blockIndex]).second,b_tid);
517 #endif
518
519 }
520 #else
521 // Insert into map only from thread 0
522 if (b_tid == 0) {
523 #ifdef DEBUG_SPATIAL_CELL
524 thisMap->set_element((inputVec->at(blockIndex)).first,(inputVec->at(blockIndex)).second);
525 #else
526 thisMap->set_element(((*inputVec)[blockIndex]).first,((*inputVec)[blockIndex]).second);
527 #endif
528 }
529 #endif
530}
531
532#ifdef USE_BATCH_WARPACCESSORS
539__global__ void __launch_bounds__(26*32, FULLBLOCKS_PER_MP) batch_update_velocity_halo_kernel (
540 const vmesh::VelocityMesh* __restrict__ const *vmeshes,
541 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *velocity_block_with_content_lists,
542 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** allMaps
543 ) {
544 // launch grid dim3 grid(launchBlocks,nCells,1);
545 // Each block manages a single GID at a time, all velocity neighbours
546 const uint nCells = gridDim.y;
547 const uint cellIndex = blockIdx.y;
548 const uint blockiStart = blockIdx.x;
549 //const int blockSize = blockDim.x; // should be 26*32 or 13*64
550 const uint ti = threadIdx.x;
551
552 // Cells such as DO_NOT_COMPUTE are identified with a zero in the vmeshes pointer buffer
553 if (vmeshes[cellIndex] == 0) {
554 return;
555 }
556 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellIndex];
557 const split::SplitVector<vmesh::GlobalID>* __restrict__ velocity_block_with_content_list = velocity_block_with_content_lists[cellIndex];
558 const vmesh::GlobalID* __restrict__ velocity_block_with_content_list_data = velocity_block_with_content_list->data();
559 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwcl_map = allMaps[2*cellIndex];
560 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwncl_map = allMaps[2*cellIndex+1];
561 const vmesh::LocalID nBlocks = velocity_block_with_content_list->size();
562
563 const vmesh::LocalID blocki = blockiStart;
564 {
565 // Return if we are beyond the size of the list for this cell
566 if (blocki >= nBlocks) {
567 return;
568 }
569 // Which spatial neighbour to consider out of the 26 face, edge, or corner neighbors
570 const int offsetIndex1 = ti / GPUTHREADS; // [0,26) (NVIDIA) or [0,13) (AMD)
571 const int w_tid = ti % GPUTHREADS; // [0,WARPSIZE)
572
573 // Assumes addWidthV = 1
574 #ifdef __CUDACC__
575 const int max_i=1;
576 #endif
577 #ifdef __HIP_PLATFORM_HCC___
578 const int max_i=2;
579 #endif
580 for (int i=0; i<max_i; i++) {
581 int offsetIndex = offsetIndex1 + 13*i;
582 // nudge latter half in order to exclude self
583 if (offsetIndex > 12) {
584 offsetIndex++;
585 }
586 const int offset_vx = (offsetIndex % 3) - 1;
587 const int offset_vy = ((offsetIndex / 3) % 3) - 1;
588 const int offset_vz = (offsetIndex / 9) - 1;
589 // Offsets verified in python
590 #ifdef DEBUG_SPATIAL_CELL
591 const vmesh::GlobalID GID = velocity_block_with_content_list->at(blocki);
592 #else
593 const vmesh::GlobalID GID = velocity_block_with_content_list_data[blocki];
594 #endif
595 vmesh::LocalID ind0,ind1,ind2;
596 vmesh->getIndices(GID,ind0,ind1,ind2);
597 const int nind0 = ind0 + offset_vx;
598 const int nind1 = ind1 + offset_vy;
599 const int nind2 = ind2 + offset_vz;
600 const vmesh::GlobalID nGID
601 = vmesh->getGlobalID(nind0,nind1,nind2);
602 if (nGID != vmesh->invalidGlobalID()) {
603 // Does block already exist in mesh?
604 const vmesh::LocalID LID = vmesh->warpGetLocalID(nGID, w_tid);
605 // Try adding this nGID to velocity_block_with_content_map. If it exists, do not overwrite.
606 const bool newlyadded = vbwcl_map->warpInsert_V<true>(nGID,LID, w_tid);
607 if (newlyadded) {
608 // Block did not previously exist in velocity_block_with_content_map
609 if ( LID != vmesh->invalidLocalID()) {
610 // Block exists in mesh, ensure it won't get deleted:
611 vbwncl_map->warpErase(nGID, w_tid);
612 }
613 // else:
614 // Block does not yet exist in mesh at all. Needs adding!
615 // Identified as invalidLID entries in velocity_block_with_content_map.
616 }
617 }
618 }
620 }
621}
622#else // if not using warp accessors
628 const vmesh::VelocityMesh* __restrict__ const *vmeshes,
629 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *velocity_block_with_content_lists,
630 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** allMaps,
631 const uint warpsPerBlockBatchHalo
632 ) {
633 // launch grid dim3 grid(launchBlocks,nCells,1);
634 // Each block manages a single GID at a time, all velocity neighbours
635 //const uint nCells = gridDim.y;
636 const uint cellIndex = blockIdx.y;
637 const uint blockiStart = blockIdx.x*warpsPerBlockBatchHalo+threadIdx.y; // launch grid block index inside number of velocity blocks
638 const uint ti = threadIdx.x; // Thread index inside warp / wavefront acting on single LID
639
640 // Cells such as DO_NOT_COMPUTE are identified with a zero in the vmeshes pointer buffer
641 if (vmeshes[cellIndex] == 0) {
642 return;
643 }
644 // Only act on first 26 threads of each warp / wavefront
645 if (ti >= 26) {
646 return; // Note: this prevents use of syncthreads!
647 }
648
649 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellIndex];
650 const split::SplitVector<vmesh::GlobalID>* __restrict__ velocity_block_with_content_list = velocity_block_with_content_lists[cellIndex];
651 const vmesh::GlobalID* __restrict__ velocity_block_with_content_list_data = velocity_block_with_content_list->data();
652 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwcl_map = allMaps[2*cellIndex];
653 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwncl_map = allMaps[2*cellIndex+1];
654 const vmesh::LocalID nBlocks = velocity_block_with_content_list->size();
655
656 const vmesh::LocalID blocki = blockiStart;
657 {
658 // Return if we are beyond the size of the list for this cell
659 if (blocki >= nBlocks) {
660 return; // Disallows use of __syncthreads() in this kernel
661 }
662 int offsetIndex = ti;
663 // nudge latter half in order to exclude self
664 if (offsetIndex > 12) {
665 offsetIndex++;
666 }
667 const int offset_vx = (offsetIndex % 3) - 1;
668 const int offset_vy = ((offsetIndex / 3) % 3) - 1;
669 const int offset_vz = (offsetIndex / 9) - 1;
670 // Offsets verified in python
671 #ifdef DEBUG_SPATIAL_CELL
672 const vmesh::GlobalID GID = velocity_block_with_content_list->at(blocki);
673 #else
674 const vmesh::GlobalID GID = velocity_block_with_content_list_data[blocki];
675 #endif
676 vmesh::LocalID ind0,ind1,ind2;
677 vmesh->getIndices(GID,ind0,ind1,ind2);
678 const int nind0 = ind0 + offset_vx;
679 const int nind1 = ind1 + offset_vy;
680 const int nind2 = ind2 + offset_vz;
681 const vmesh::GlobalID nGID
682 = vmesh->getGlobalID(nind0,nind1,nind2);
683 if (nGID != vmesh->invalidGlobalID()) {
684 // Does block already exist in mesh?
685 const vmesh::LocalID LID = vmesh->getLocalID(nGID);
686 // Add this nGID to velocity_block_with_content_map.
687 const bool newlyadded = vbwcl_map->set_element<true>(nGID,LID);
688 if (newlyadded) {
689 // Block did not previously exist in velocity_block_with_content_map
690 if ( LID != vmesh->invalidLocalID()) {
691 // Block exists in mesh, ensure it won't get deleted:
692 vbwncl_map->device_erase(nGID);
693 }
694 }
695 }
696 //__syncthreads(); // Not allowed due to early thread returns
697 }
698}
699#endif // end if warp accessors
700
701
702#ifdef USE_BATCH_WARPACCESSORS
705__global__ void __launch_bounds__(GPUTHREADS*WARPSPERBLOCK, FULLBLOCKS_PER_MP) batch_update_neighbour_halo_kernel (
706 const vmesh::VelocityMesh* __restrict__ const *vmeshes,
707 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** allMaps,
708 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *neigh_velocity_block_with_content_lists
709 ) {
710 const uint nCells = gridDim.y;
711 const uint maxNeighbours = gridDim.z;
712 const uint cellIndex = blockIdx.y;
713 const uint neighIndex = blockIdx.y * maxNeighbours + blockIdx.z;
714
715 // Cells such as DO_NOT_COMPUTE are identified with a zero in the vmeshes pointer buffer
716 if (vmeshes[cellIndex] == 0) {
717 return;
718 }
719 // Early return for non-existing neighbour indexes
720 if (neigh_velocity_block_with_content_lists[neighIndex] == 0) {
721 return;
722 }
723
724 const int ti = threadIdx.x; // [0,blockSize)
725 const int w_tid = ti % GPUTHREADS; // [0,WARPSIZE)
726 const int w_id = ti / GPUTHREADS; // [0,WARPSPERBLOCK)
727
728 const int blockWidth = WARPSPERBLOCK; // how many GIDs each GPU block manages at once (in parallel)
729 const int blockiStart = blockIdx.x * blockWidth;
730
731 const split::SplitVector<vmesh::GlobalID>* __restrict__ velocity_block_with_content_list = neigh_velocity_block_with_content_lists[neighIndex];
732 const int nBlocks = velocity_block_with_content_list->size();
733
734 for (int blocki = blockiStart + w_id; blocki < blockiStart+blockWidth; blocki += blockWidth) {
735 // Skip to sync if we are beyond the size of the list for this cell
736 if (blocki < nBlocks) {
737 const vmesh::VelocityMesh* __restrict__ vmesh = vmeshes[cellIndex];
738 const vmesh::GlobalID* __restrict__ velocity_block_with_content_list_data = velocity_block_with_content_list->data();
739 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwcl_map = allMaps[2*cellIndex];
740 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwncl_map = allMaps[2*cellIndex+1];
741
742 #ifdef DEBUG_SPATIAL_CELL
743 const vmesh::GlobalID nGID = velocity_block_with_content_list->at(blocki);
744 #else
745 const vmesh::GlobalID nGID = velocity_block_with_content_list_data[blocki];
746 #endif
747 // Does block already exist in mesh?
748 const vmesh::LocalID LID = vmesh->warpGetLocalID(nGID, w_tid);
749 // Try adding this nGID to velocity_block_with_content_map. If it exists, do not overwrite.
750 const bool newlyadded = vbwcl_map->warpInsert_V<true>(nGID,LID, w_tid);
751 if (newlyadded) {
752 // Block did not previously exist in velocity_block_with_content_map
753 if ( LID != vmesh->invalidLocalID()) {
754 // Block exists in mesh, ensure it won't get deleted:
755 vbwncl_map->warpErase(nGID, w_tid);
756 }
757 // else:
758 // Block does not yet exist in mesh at all. Needs adding!
759 // Identified as invalidLID entries in velocity_block_with_content_map.
760 }
761 }
763 }
764}
765#else // if not using warp accessors
768__global__ void __launch_bounds__(GPUTHREADS*WARPSPERBLOCK, FULLBLOCKS_PER_MP) batch_update_neighbour_halo_kernel (
769 const vmesh::VelocityMesh* __restrict__ const *vmeshes,
770 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>** allMaps,
771 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *neigh_velocity_block_with_content_lists
772 ) {
773
774 //const uint nCells = gridDim.y;
775 const uint maxNeighbours = gridDim.z;
776 const uint cellIndex = blockIdx.y;
777 const uint neighIndex = blockIdx.y * maxNeighbours + blockIdx.z;
778
779 const vmesh::VelocityMesh* __restrict__ vmeshCellIndex = vmeshes[cellIndex];
780 const split::SplitVector<vmesh::GlobalID>* __restrict__ velocity_block_with_content_list = neigh_velocity_block_with_content_lists[neighIndex];
781
782 // Cells such as DO_NOT_COMPUTE are identified with a zero in the vmeshes pointer buffer
783 if (vmeshCellIndex == 0) {
784 return;
785 }
786 // Early return for non-existing neighbour indexes
787 if (velocity_block_with_content_list == 0) {
788 return;
789 }
790
791 const int blockWidth = blockDim.x; // how many GIDs each GPU block manages at once (in parallel)
792 const int ti = threadIdx.x; // [0,blockSize)
793
794 const int nBlocks = velocity_block_with_content_list->size();
795
796 {
797 const int blocki = blockIdx.x * blockWidth + ti;
798 // Return if we are beyond the size of the list for this cell
799 if (blocki >= nBlocks) {
800 return; // Disallows use of __syncthreads() in this kernel
801 }
802 const vmesh::GlobalID* __restrict__ velocity_block_with_content_list_data = velocity_block_with_content_list->data();
803 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwcl_map = allMaps[2*cellIndex];
804 Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>* vbwncl_map = allMaps[2*cellIndex+1];
805
806 #ifdef DEBUG_SPATIAL_CELL
807 const vmesh::GlobalID nGID = velocity_block_with_content_list->at(blocki);
808 #else
809 const vmesh::GlobalID nGID = velocity_block_with_content_list_data[blocki];
810 #endif
811 // Does block already exist in mesh?
812 const vmesh::LocalID LID = vmeshCellIndex->getLocalID(nGID);
813 // Add this nGID to velocity_block_with_content_map.
814 const bool newlyadded = vbwcl_map->set_element<true>(nGID,LID);
815 if (newlyadded) {
816 // Block did not previously exist in velocity_block_with_content_map
817 if ( LID != vmeshCellIndex->invalidLocalID()) {
818 // Block exists in mesh, ensure it won't get deleted:
819 vbwncl_map->device_erase(nGID);
820 }
821 }
822 //__syncthreads(); // Not allowed due to early thread returns
823 }
824}
825#endif
826
830 vmesh::VelocityBlockContainer **blockContainers,
831 split::SplitVector<vmesh::GlobalID>** dev_list_with_replace_new,
832 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>** dev_list_delete,
833 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>** dev_list_to_replace,
834 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>** dev_list_with_replace_old,
835 // return values: nbefore, nafter, nblockstochange, resize success
836 vmesh::LocalID* dev_nBefore,
837 vmesh::LocalID* dev_nAfter,
838 vmesh::LocalID* dev_nBlocksToChange,
840 Real* dev_rhoLossAdjust // mass loss, set to zero
841 ) {
842 const size_t cellIndex = blockIdx.x;
843 if (vmeshes[cellIndex]==0) {
844 return; // Early return for invalid cells
845 }
847 vmesh::VelocityBlockContainer *blockContainer = blockContainers[cellIndex];
848 split::SplitVector<vmesh::GlobalID>* list_with_replace_new = dev_list_with_replace_new[cellIndex];
849 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* list_delete = dev_list_delete[cellIndex];
850 split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* list_to_replace = dev_list_to_replace[cellIndex];
851 //split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* list_with_replace_old = dev_list_with_replace_old[cellIndex];
852
853 const vmesh::LocalID nBlocksBeforeAdjust = vmesh->size();
854 const vmesh::LocalID nToAdd = list_with_replace_new->size();
855 const vmesh::LocalID nToRemove = list_delete->size() + list_to_replace->size();
856 const vmesh::LocalID nBlocksAfterAdjust = nBlocksBeforeAdjust + nToAdd - nToRemove;
857 const vmesh::LocalID nBlocksToChange = nToAdd > nToRemove ? nToAdd : nToRemove;
858
859 dev_rhoLossAdjust[cellIndex] = 0.0;
860 dev_nBefore[cellIndex] = nBlocksBeforeAdjust;
861 dev_nAfter[cellIndex] = nBlocksAfterAdjust;
862 dev_nBlocksToChange[cellIndex] = nBlocksToChange;
863 // Should we grow the size?
864 if (nBlocksAfterAdjust > nBlocksBeforeAdjust) {
865 if ((nBlocksAfterAdjust <= vmesh->capacity()) && (nBlocksAfterAdjust <= blockContainer->capacity())) {
866 dev_resizeSuccess[cellIndex] = 1; // Resize on-device will work.
867 vmesh->device_setNewSize(nBlocksAfterAdjust);
868 blockContainer->setNewSize(nBlocksAfterAdjust);
869 } else {
870 dev_resizeSuccess[cellIndex] = 0; // Need to recapacitate and resize from host
871 }
872 } else {
873 // No error as no resize.
875 }
876}
877
881 vmesh::VelocityBlockContainer **blockContainers,
882 vmesh::LocalID* dev_nAfter
883 ) {
884 const size_t cellIndex = blockIdx.x;
885 if (vmeshes[cellIndex]==0) {
886 return; // Early return for invalid cells
887 }
889 vmesh::VelocityBlockContainer *blockContainer = blockContainers[cellIndex];
890 const vmesh::LocalID nBlocksAfterAdjust = dev_nAfter[cellIndex];
891 vmesh->device_setNewSize(nBlocksAfterAdjust);
892 blockContainer->setNewSize(nBlocksAfterAdjust);
893}
894
895
897__global__ void __launch_bounds__(WID3, WID3S_PER_MP) batch_update_velocity_blocks_kernel(
899 vmesh::VelocityBlockContainer **blockContainers,
900 const split::SplitVector<vmesh::GlobalID>* __restrict__ const *dev_list_with_replace_new,
901 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ const *dev_list_delete,
902 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ const *dev_list_to_replace,
903 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ const *dev_list_with_replace_old,
904 vmesh::LocalID* dev_nBefore,
905 vmesh::LocalID* dev_nAfter,
906 vmesh::LocalID* dev_nBlocksToChange,
907 Real* dev_rhoLossAdjust // mass loss, gather from deleted blocks
908 ) {
909 // launch griddim3 grid(launchBlocks,nCells,1);
910 const size_t cellIndex = blockIdx.y;
911 if (vmeshes[cellIndex]==0) {
912 return; // Early return for invalid cells
913 }
915 vmesh::VelocityBlockContainer *blockContainer = blockContainers[cellIndex];
916 const split::SplitVector<vmesh::GlobalID>* __restrict__ list_with_replace_new = dev_list_with_replace_new[cellIndex];
917 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ list_delete = dev_list_delete[cellIndex];
918 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ list_to_replace = dev_list_to_replace[cellIndex];
919 const split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>* __restrict__ list_with_replace_old = dev_list_with_replace_old[cellIndex];
920
921 const vmesh::LocalID nBlocksBeforeAdjust = dev_nBefore[cellIndex];
922 const vmesh::LocalID nBlocksAfterAdjust = dev_nAfter[cellIndex];
923 const vmesh::LocalID nBlocksToChange = dev_nBlocksToChange[cellIndex];
924
925 if (blockIdx.x >= nBlocksToChange) {
926 return; // Early return if outside list of blocks to change
927 }
928 const uint ti = threadIdx.x; // [0,blockSize)
929
930 // This index into vectors can be adjusted along the way
931 uint index = (uint)blockIdx.x;
932
933 const int b_tid = ti % WID3; // [0,WID3)
934
935 const vmesh::LocalID n_with_replace_new = list_with_replace_new->size();
936 const vmesh::LocalID n_delete = list_delete->size();
937 const vmesh::LocalID n_to_replace = list_to_replace->size();
938 const vmesh::LocalID n_with_replace_old = list_with_replace_old->size();
939 // For tracking mass-loss
940 __shared__ Real massloss[WID3];
941
942 // Each block Processes one block from the lists.
943
944 /*********
945 Check if should delete item from end of vmesh.
946 For this, we get both GID and LID from the vector.
947 **/
948 if (index < n_delete) {
949 #ifdef DEBUG_SPATIAL_CELL
950 const vmesh::GlobalID rmGID = (list_delete->at(index)).first;
951 const vmesh::GlobalID rmLID = (list_delete->at(index)).second;
952 #else
953 const vmesh::GlobalID rmGID = ((*list_delete)[index]).first;
954 const vmesh::GlobalID rmLID = ((*list_delete)[index]).second;
955 #endif
956
957 #ifdef DEBUG_SPATIAL_CELL
958 if (rmGID == vmesh->invalidGlobalID()) {
959 if (rmLID != vmesh->invalidLocalID()) {
960 // Valid LID but invalid GID: only remove from vmesh localToGlobal?
961 if (b_tid==0) {
962 printf("Removing blocks: Valid LID %u but invalid GID!\n",rmLID);
963 }
964 } else {
965 if (b_tid==0) {
966 printf("Removing blocks: Invalid LID and GID!\n");
967 }
968 }
969 assert(0);
970 }
971 if (rmLID == vmesh->invalidLocalID()) {
972 if (rmGID != vmesh->invalidGlobalID()) {
973 // Valid GID but invalid LID: only remove from vmesh globalToLocal?
974 if (b_tid==0) {
975 printf("Removing blocks: Valid GID %ul but invalid LID!\n",rmGID);
976 }
977 }
978 assert(0);
979 }
980 if ((unsigned long)rmLID >= (unsigned long)nBlocksBeforeAdjust) {
981 if (b_tid==0) {
982 printf("Trying to outright remove block which has LID %ul >= nBlocksBeforeAdjust %ul!\n",rmLID,nBlocksBeforeAdjust);
983 }
984 assert(0);
985 }
986 if ((unsigned long)rmLID < (unsigned long)nBlocksAfterAdjust) {
987 if (b_tid==0) {
988 printf("Trying to outright remove block which has LID %u smaller than nBlocksAfterAdjust %u!\n",rmLID,nBlocksAfterAdjust);
989 }
990 assert(0);
991 }
992 #endif
993
994 // Track mass loss:
995 Realf* rm_avgs = blockContainer->getData(rmLID);
996 Real* rm_block_parameters = blockContainer->getParameters(rmLID);
997 const Real rm_DV3 = rm_block_parameters[BlockParams::DVX]
998 * rm_block_parameters[BlockParams::DVY]
999 * rm_block_parameters[BlockParams::DVZ];
1000 // thread-sum for rho
1001 massloss[ti] = rm_avgs[b_tid]*rm_DV3;
1002 __syncthreads();
1003 // Implemented just a simple non-optimized thread sum
1004 for (int s=WID3/2; s>0; s>>=1) {
1005 if (b_tid < s) {
1006 massloss[ti] += massloss[ti + s];
1007 }
1008 __syncthreads();
1009 }
1010 // Bookkeeping only by one thread per block
1011 if (b_tid==0) {
1012 Real old = atomicAdd(&dev_rhoLossAdjust[cellIndex], massloss[ti]);
1013 }
1014 __syncthreads();
1015
1016 // Delete from vmesh
1017 #ifdef USE_BATCH_WARPACCESSORS
1018 vmesh->warpDeleteBlock(rmGID,rmLID,b_tid);
1019 #else
1020 if (b_tid==0) {
1021 vmesh->deleteBlock(rmGID,rmLID);
1022 }
1023 #endif
1024 // GPUTODO debug checks
1025 return;
1026 }
1027 index -= n_delete;
1028
1029 /*********
1030 Check if should replace existing block with either
1031 existing block from end of vmesh or new block
1032 **/
1033 if (index < n_to_replace) {
1034 #ifdef DEBUG_SPATIAL_CELL
1035 const vmesh::GlobalID rmGID = (list_to_replace->at(index)).first;
1036 const vmesh::GlobalID rmLID = (list_to_replace->at(index)).second;
1037 #else
1038 const vmesh::GlobalID rmGID = ((*list_to_replace)[index]).first;
1039 const vmesh::GlobalID rmLID = ((*list_to_replace)[index]).second;
1040 #endif
1041 //const vmesh::LocalID rmLID = vmesh->warpGetLocalID(rmGID,b_tid);
1042
1043 #ifdef DEBUG_SPATIAL_CELL
1044 if (rmGID == vmesh->invalidGlobalID()) {
1045 if (rmLID != vmesh->invalidLocalID()) {
1046 // Valid LID but invalid GID: only remove from vmesh localToGlobal?
1047 if (b_tid==0) {
1048 printf("Replacing blocks: Valid LID %u but invalid GID!\n",rmLID);
1049 }
1050 } else {
1051 if (b_tid==0) {
1052 printf("Replacing blocks: Invalid LID and GID!\n");
1053 }
1054 }
1055 assert(0);
1056 }
1057 if (rmLID == vmesh->invalidLocalID()) {
1058 if (rmGID != vmesh->invalidGlobalID()) {
1059 // Valid GID but invalid LID: only remove from vmesh globalToLocal?
1060 if (b_tid==0) {
1061 printf("Replacing blocks: Valid GID %ul but invalid LID!\n",rmGID);
1062 }
1063 }
1064 assert(0);
1065 }
1066 if (rmLID >= nBlocksBeforeAdjust) {
1067 if (b_tid==0) {
1068 printf("Trying to replace block which has LID %ul >= nBlocksBeforeAdjust %ul!\n",rmLID,nBlocksBeforeAdjust);
1069 }
1070 assert(0);
1071 }
1072 #endif
1073
1074 // Track mass loss:
1075 Realf* rm_avgs = blockContainer->getData(rmLID);
1076 Real* rm_block_parameters = blockContainer->getParameters(rmLID);
1077 const Real rm_DV3 = rm_block_parameters[BlockParams::DVX]
1078 * rm_block_parameters[BlockParams::DVY]
1079 * rm_block_parameters[BlockParams::DVZ];
1080 // thread-sum for rho
1081 massloss[ti] = rm_avgs[b_tid]*rm_DV3;
1082 __syncthreads();
1083 // Implemented just a simple non-optimized thread sum
1084 for (int s=WID3/2; s>0; s>>=1) {
1085 if (b_tid < s) {
1086 massloss[ti] += massloss[ti + s];
1087 }
1088 __syncthreads();
1089 }
1090 // Bookkeeping only by one thread per block
1091 if (b_tid==0) {
1092 Real old = atomicAdd(&dev_rhoLossAdjust[cellIndex], massloss[ti]);
1093 }
1094 __syncthreads();
1095
1096 // Figure out what to use as replacement
1097 vmesh::GlobalID replaceGID;
1098 vmesh::LocalID replaceLID;
1099
1100 // First option: replace with existing block from end of vmesh
1101 if (index < n_with_replace_old) {
1102 #ifdef DEBUG_SPATIAL_CELL
1103 replaceGID = (list_with_replace_old->at(index)).first;
1104 replaceLID = (list_with_replace_old->at(index)).second;
1105 #else
1106 replaceGID = ((*list_with_replace_old)[index]).first;
1107 replaceLID = ((*list_with_replace_old)[index]).second;
1108 #endif
1109
1110 Realf* repl_avgs = blockContainer->getData(replaceLID);
1111 Real* repl_block_parameters = blockContainer->getParameters(replaceLID);
1112 rm_avgs[b_tid] = repl_avgs[b_tid];
1114 rm_block_parameters[b_tid] = repl_block_parameters[b_tid];
1115 }
1116 __syncthreads();
1117
1118 } else {
1119 // Second option: add new block instead
1120 #ifdef DEBUG_SPATIAL_CELL
1121 replaceGID = list_with_replace_new->at(index - n_with_replace_old);
1122 #else
1123 replaceGID = (*list_with_replace_new)[index - n_with_replace_old];
1124 #endif
1125 replaceLID = vmesh->invalidLocalID();
1126
1127 rm_avgs[b_tid] = 0;
1128 if (b_tid==0) {
1129 // Write in block parameters
1130 vmesh->getBlockInfo(replaceGID, rm_block_parameters+BlockParams::VXCRD);
1131 }
1132 __syncthreads();
1133 }
1134 // Remove hashmap entry for removed block, add instead created block
1135 #ifdef USE_BATCH_WARPACCESSORS
1136 vmesh->warpReplaceBlock(rmGID,rmLID,replaceGID,b_tid);
1137 #else
1138 if (b_tid==0) {
1139 vmesh->replaceBlock(rmGID,rmLID,replaceGID);
1140 }
1141 #endif
1142 #ifdef DEBUG_SPATIAL_CELL
1143 __syncthreads();
1144 if (vmesh->getGlobalID(rmLID) != replaceGID) {
1145 if (b_tid==0) {
1146 printf("Error! Replacing did not result in wanted GID at old LID in update_velocity_blocks_kernel! \n");
1147 }
1148 assert(0);
1149 }
1150 if (vmesh->getLocalID(replaceGID) != rmLID) {
1151 if (b_tid==0) {
1152 printf("Error! Replacing did not result in old LID at replaced GID in update_velocity_blocks_kernel! \n");
1153 }
1154 assert(0);
1155 }
1156 #endif
1157
1158 return;
1159 }
1160 index -= n_to_replace;
1161
1162 /*********
1163 Finally check if we should add new block after end of current vmesh
1164 We have reserved/used some entries from the beginning of the list_with_replace_new
1165 for the previous section, so now we access that with a different index.
1166 **/
1167 const uint add_index = index + (n_to_replace - n_with_replace_old);
1168 if (add_index < n_with_replace_new) {
1169 #ifdef DEBUG_SPATIAL_CELL
1170 const vmesh::GlobalID addGID = list_with_replace_new->at(add_index);
1171 if (vmesh->getLocalID(addGID) != vmesh->invalidLocalID()) {
1172 if (b_tid==0) {
1173 printf("Trying to add new GID %u to mesh which already contains it! index=%u addindex=%u\n",addGID,index,add_index);
1174 }
1175 assert(0);
1176 }
1177 __syncthreads();
1178 #else
1179 const vmesh::GlobalID addGID = (*list_with_replace_new)[add_index];
1180 #endif
1181
1182 // We need to add the data of addGID to a new LID. Here we still use the regular index.
1183 const vmesh::LocalID addLID = nBlocksBeforeAdjust + index;
1184 Realf* add_avgs = blockContainer->getData(addLID);
1185 #ifdef DEBUG_SPATIAL_CELL
1186 if (addGID == vmesh->invalidGlobalID()) {
1187 printf("Error! invalid addGID!\n");
1188 assert(0);
1189 }
1190 if (addLID == vmesh->invalidLocalID()) {
1191 printf("Error! invalid addLID!\n");
1192 assert(0);
1193 }
1194 #endif
1195 Real* add_block_parameters = blockContainer->getParameters(addLID);
1196 // Zero out blockdata
1197 add_avgs[b_tid] = 0;
1198 if (b_tid==0) {
1199 // Write in block parameters
1200 vmesh->getBlockInfo(addGID, add_block_parameters+BlockParams::VXCRD);
1201 }
1202 __syncthreads();
1203
1204 // Insert new hashmap entry into vmesh
1205 #ifdef USE_BATCH_WARPACCESSORS
1206 vmesh->warpPlaceBlock(addGID,addLID,b_tid);
1207 #else
1208 if (b_tid==0) {
1209 vmesh->placeBlock(addGID,addLID);
1210 }
1211 #endif
1212 #ifdef DEBUG_SPATIAL_CELL
1213 __syncthreads();
1214 if (vmesh->getGlobalID(addLID) == vmesh->invalidGlobalID()) {
1215 printf("Error! invalid GID after add from addLID!\n");
1216 assert(0);
1217 }
1218 if (vmesh->getLocalID(addGID) == vmesh->invalidLocalID()) {
1219 printf("Error! invalid LID after add from addGID!\n");
1220 assert(0);
1221 }
1222 #endif
1223 return;
1224 }
1225
1226 // Fall-through error!
1227 if (b_tid==0) {
1228 printf("Error! Fall through in batch_update_velocity_blocks_kernel! index %u nBlocksBeforeAdjust %u nBlocksAfterAdjust %u \n",
1229 index,nBlocksBeforeAdjust,nBlocksAfterAdjust);
1230 }
1231 __syncthreads();
1232}
1233
1236__global__ void __launch_bounds__(WID3, WID3S_PER_MP) batch_population_scale_kernel (
1237 vmesh::VelocityBlockContainer **blockContainers,
1238 Real* dev_mass_scale
1239 ) {
1240 // launch griddim3 grid(launchBlocks,nCells,1);
1241 const int cellIndex = blockIdx.y;
1242 const int blocki = blockIdx.x;
1243 const uint ti = threadIdx.x;
1244
1245 vmesh::VelocityBlockContainer* blockContainer = blockContainers[cellIndex];
1246 const Real cell_mass_scale = dev_mass_scale[cellIndex];
1247
1248 const uint b_tid = ti % WID3; // [0,WID3)
1249 const uint blockLID = blocki; // [0,nBlocksToChange)
1250
1251 const uint VBC_size = blockContainer->size();
1252 if (blockLID > VBC_size || cell_mass_scale <= 0) {
1253 return;
1254 }
1255 // Pointer to target block data
1256 Realf* data = blockContainer->getData(blockLID);
1257 // Scale value
1258 data[b_tid] = data[b_tid] * cell_mass_scale;
1259}
1260
1261#endif
for i
Definition Dispersion.m:24
#define gpuPeekAtLastError
#define WARPSPERBLOCK
#define gpuStream_t
#define CHK_ERR(err)
#define gpuKernelAny(mask, input)
#define GPUTHREADS
__global__ void batch_update_velocity_halo_kernel(const vmesh::VelocityMesh *__restrict__ const *vmeshes, const split::SplitVector< vmesh::GlobalID > *__restrict__ const *velocity_block_with_content_lists, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **allMaps, const uint warpsPerBlockBatchHalo)
#define warpsPerBlockBatchContent
__global__ void batch_resize_vbc_kernel_pre(vmesh::VelocityMesh **vmeshes, vmesh::VelocityBlockContainer **blockContainers, split::SplitVector< vmesh::GlobalID > **dev_list_with_replace_new, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_delete, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_to_replace, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **dev_list_with_replace_old, vmesh::LocalID *dev_nBefore, vmesh::LocalID *dev_nAfter, vmesh::LocalID *dev_nBlocksToChange, vmesh::LocalID *dev_resizeSuccess, Real *dev_rhoLossAdjust)
__global__ void batch_resize_vbc_kernel_post(vmesh::VelocityMesh **vmeshes, vmesh::VelocityBlockContainer **blockContainers, vmesh::LocalID *dev_nAfter)
__global__ void __launch_bounds__(WID3, WID3S_PER_MP) batch_update_velocity_block_content_lists_kernel(const vmesh
void extract_GIDs_kernel_launcher(Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **input_maps, split::SplitVector< ELEMENT > **output_vecs, vmesh::LocalID *output_sizes, Rule rule, vmesh::VelocityMesh **rule_meshes, Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > **rule_maps, split::SplitVector< vmesh::GlobalID > **rule_vectors, const uint nCells, gpuStream_t stream)
void clean_tombstones_launcher(vmesh::VelocityMesh **vmeshes, split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > **overflown_elements, vmesh::LocalID *output_sizes, Rule rule, const uint nCells, gpuStream_t stream)
ARCH_HOSTDEV vmesh::LocalID size() const
ARCH_HOSTDEV bool setNewSize(const vmesh::LocalID newSize)
const int WID3
Definition common.h:517
float Real
Definition definitions.h:41
float Realf
Definition definitions.h:33
const uint ti
__global__ void vmesh::VelocityMesh **__restrict__ vmeshes
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > ** allMaps
split::SplitVector< vmesh::GlobalID > * list_with_replace_new
__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
__syncthreads()
__global__ void const Realf const uint *__restrict__ const uint *__restrict__ const vmesh::GlobalID *__restrict__ const uint const uint const uint const Realf threshold
function res
Definition hamming.m:1
#define index(i, j, k)
@ N_VELOCITY_BLOCK_PARAMS
Definition common.h:115
uint32_t uint
uint32_t LocalID
Definition definitions.h:60
uint32_t GlobalID
Definition definitions.h:59