Vlasiator ebf0dd394 on dev (v5.4.0 + 1054 commits)
Loading...
Searching...
No Matches
gpu_acc_semilag.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 <dccrg.hpp>
24#include <dccrg_cartesian_geometry.hpp>
25#include <phiprof.hpp>
26#include "../definitions.h"
27
28#include "gpu_acc_semilag.hpp"
30#include "gpu_acc_map.hpp"
32
33#ifdef _OPENMP
34#include <omp.h>
35#endif
36
51
52void gpu_accelerate_cells(dccrg::Dccrg<SpatialCell,dccrg::Cartesian_Geometry>& mpiGrid,
53 const std::vector<CellID>& acceleratedCells,
54 const uint popID,
55 const uint map_order
56 ) {
57
59 phiprof::Timer verificationTimer {"gpu ACC allocation verifications"};
60 const uint nCells = (uint)acceleratedCells.size();
61 gpu_batch_allocate(nCells,0);
62 verificationTimer.stop();
63
64 // Calculate intersections (should be constant cost per cell). Also reduces
65 // the largest found block count in order to ensure allocations.
66 int intersections_id {phiprof::initializeTimer("cell-compute-intersections")};
67 uint gpuMaxBlockCount = 0;
68 #pragma omp parallel
69 {
70 uint threadGpuMaxBlockCount = 0;
71 #pragma omp for schedule(static)
72 for (size_t cellIndex=0; cellIndex<acceleratedCells.size(); ++cellIndex) {
73 const CellID cid = acceleratedCells[cellIndex];
74 SpatialCell* SC = mpiGrid[cid];
75 Population& pop = SC->get_population(popID);
76 compute_cell_intersections(SC, popID, map_order, pop.subcycleDt, intersections_id);
77
79 const uint blockCount = vmesh->size();
80 threadGpuMaxBlockCount = std::max(threadGpuMaxBlockCount,blockCount);
81 }
82 #pragma omp critical
83 {
84 gpuMaxBlockCount = std::max(gpuMaxBlockCount,threadGpuMaxBlockCount);
85 }
86 }
87
88 // Do some overall preparation regarding dimensions and acceleration order
89 const uint D0 = (*vmesh::getMeshWrapper()->velocityMeshes)[popID].gridLength[0];
90 const uint D1 = (*vmesh::getMeshWrapper()->velocityMeshes)[popID].gridLength[1];
91 const uint D2 = (*vmesh::getMeshWrapper()->velocityMeshes)[popID].gridLength[2];
92
93 std::vector<int> dimOrder(3);
94 switch(map_order) {
95 case 0: { //Map order XYZ
96 dimOrder={0,1,2};
97 break;
98 }
99 case 1: { //Map order YZX
100 dimOrder={1,2,0};
101 break;
102 }
103 case 2: { //Map order ZXY
104 dimOrder={2,0,1};
105 break;
106 }
107 default:
108 std::cerr<<"ERROR! Incorrect map_order "<<map_order<<"!"<<std::endl;
109 abort();
110 }
111
116 for (int dimIndex = 0; dimIndex<3; ++dimIndex) {
117 int dimension = dimOrder[dimIndex];
118
119 // Gather up-to-date pointers for cell contents
120 uint gpuMaxBlockCount = 0;
121 #pragma omp parallel
122 {
123 uint threadGpuMaxBlockCount = 0;
124 #pragma omp for schedule(static)
125 for (size_t cellIndex=0; cellIndex<acceleratedCells.size(); ++cellIndex) {
126 const CellID cid = acceleratedCells[cellIndex];
127 SpatialCell* SC = mpiGrid[cid];
128 const uint blockCount = SC->get_velocity_mesh(popID)->size();
129 // Ensure per-cell allocations
130 SC->setReservation(popID,blockCount);
131 SC->applyReservation(popID);
132
133 threadGpuMaxBlockCount = std::max(threadGpuMaxBlockCount,blockCount);
134 // Store pointers in batch buffers
137 (GET_POINTER(gpuMemoryManager, Real, host_minValues))[cellIndex] = SC->getVelocityBlockMinValue(popID);
138 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec))[cellIndex] = SC->dev_velocity_block_with_content_list;
139 (GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new))[cellIndex] = SC->dev_list_with_replace_new;
140 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_delete))[cellIndex] = SC->dev_list_delete;
141 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_to_replace))[cellIndex] = SC->dev_list_to_replace;
142 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_with_replace_old))[cellIndex] = SC->dev_list_with_replace_old;
143 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*cellIndex] = SC->dev_velocity_block_with_content_map;
144 (GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps))[2*cellIndex+1] = SC->dev_velocity_block_with_no_content_map;
145 }
146 #pragma omp critical
147 {
148 gpuMaxBlockCount = std::max(gpuMaxBlockCount,threadGpuMaxBlockCount);
149 }
150 }
151 // Ensure accelerator has enough temporary memory allocated
152 verificationTimer.start();
153 gpu_vlasov_allocate(gpuMaxBlockCount);
154 gpu_acc_allocate(gpuMaxBlockCount);
155 verificationTimer.stop();
156
157 // Copy pointers and counters over to device
158 phiprof::Timer copyTimer {"copy pointer addresses to device"};
159 CHK_ERR( gpuMemset(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nBefore), 0, nCells*sizeof(vmesh::LocalID)) );
160 CHK_ERR( gpuMemset(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nAfter), 0, nCells*sizeof(vmesh::LocalID)) );
161 CHK_ERR( gpuMemset(GET_POINTER(gpuMemoryManager, vmesh::LocalID, dev_nBlocksToChange), 0, nCells*sizeof(vmesh::LocalID)) );
163
164 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), dev_allMaps), GET_POINTER(gpuMemoryManager, SINGLE_ARG(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), host_allMaps), 2*nCells*sizeof(Hashinator::Hashmap<vmesh::GlobalID,vmesh::LocalID>*), gpuMemcpyHostToDevice) );
166 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, Real, dev_minValues), GET_POINTER(gpuMemoryManager, Real, host_minValues), nCells*sizeof(Real), gpuMemcpyHostToDevice) );
167 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_vbwcl_vec), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_vbwcl_vec), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice) );
168 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, dev_lists_with_replace_new), GET_POINTER(gpuMemoryManager, split::SplitVector<vmesh::GlobalID>*, host_lists_with_replace_new), nCells*sizeof(split::SplitVector<vmesh::GlobalID>*), gpuMemcpyHostToDevice) );
169 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_delete), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_delete), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice) );
170 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_to_replace), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_to_replace), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice) );
171 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), dev_lists_with_replace_old), GET_POINTER(gpuMemoryManager, SINGLE_ARG(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), host_lists_with_replace_old), nCells*sizeof(split::SplitVector<Hashinator::hash_pair<vmesh::GlobalID,vmesh::LocalID>>*), gpuMemcpyHostToDevice) );
173 copyTimer.stop();
174
175 string profName = "accelerate "+getObjectWrapper().particleSpecies[popID].name;
176 phiprof::Timer accTimer {profName};
177
178 // used when computing id of target block.
179 uint block_indices_to_id[3] = {0, 0, 0};
180 uint block_indices_to_probe[3] = {0, 0, 0};
181 uint cell_indices_to_id[3] = {0, 0, 0};
182
183 // Find probe cube extents as well
184 int Dacc=0, Dother=0;
185
186 switch (dimension) {
187 case 0: /* i and k coordinates have been swapped */
188 /* set values in array that is used to convert block indices to id using a dot product */
189 block_indices_to_id[0] = D0*D1;
190 block_indices_to_id[1] = D0;
191 block_indices_to_id[2] = 1;
192
193 /* set values in array that is used to convert block indices to position in probe cube
194 propagate along X, flatten Y+Z */
195 block_indices_to_probe[0] = D1*D2;
196 block_indices_to_probe[1] = D2;
197 block_indices_to_probe[2] = 1;
198 Dacc = D0;
199 Dother = D1*D2;
200
201 /* set values in array that is used to convert block indices to id using a dot product */
202 cell_indices_to_id[0] = WID2;
203 cell_indices_to_id[1] = WID;
204 cell_indices_to_id[2] = 1;
205 break;
206 case 1: /* j and k coordinates have been swapped */
207 /* set values in array that is used to convert block indices to id using a dot product */
208 block_indices_to_id[0] = 1;
209 block_indices_to_id[1] = D0*D1;
210 block_indices_to_id[2] = D0;
211
212 /* set values in array that is used to convert block indices to position in probe cube
213 propagate along Y, flatten X+Z */
214 block_indices_to_probe[0] = D2;
215 block_indices_to_probe[1] = D0*D2;
216 block_indices_to_probe[2] = 1;
217 Dacc = D1;
218 Dother = D0*D2;
219
220 /* set values in array that is used to convert block indices to id using a dot product */
221 cell_indices_to_id[0] = 1;
222 cell_indices_to_id[1] = WID2;
223 cell_indices_to_id[2] = WID;
224 break;
225 case 2:
226 /* set values in array that is used to convert block indices to id using a dot product */
227 block_indices_to_id[0] = 1;
228 block_indices_to_id[1] = D0;
229 block_indices_to_id[2] = D0*D1;
230
231 /* set values in array that is used to convert block indices to position in probe cube
232 propagate along Z, flatten X+Y */
233 block_indices_to_probe[0] = D1;
234 block_indices_to_probe[1] = 1;
235 block_indices_to_probe[2] = D0*D1;
236 Dacc = D2;
237 Dother = D0*D1;
238
239 /* set values in array that is used to convert block indices to id using a dot product. */
240 cell_indices_to_id[0] = 1;
241 cell_indices_to_id[1] = WID;
242 cell_indices_to_id[2] = WID2;
243 break;
244 default:
245 std::cerr<<"Invalid dimension "<<dimension<<"!"<<std::endl;
246 abort();
247 }
248
249 // Copy indexing information to device. To be tested: might be faster to pass a single
250 // device-side struct or just 9 plain arguments?
251 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, uint, gpu_cell_indices_to_id), cell_indices_to_id, 3*sizeof(uint), gpuMemcpyHostToDevice) );
252 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, uint, gpu_block_indices_to_id), block_indices_to_id, 3*sizeof(uint), gpuMemcpyHostToDevice) );
253 CHK_ERR( gpuMemcpy(GET_POINTER(gpuMemoryManager, uint, gpu_block_indices_to_probe), block_indices_to_probe, 3*sizeof(uint), gpuMemcpyHostToDevice) );
254
255 // Select correct intersections for each mapping order
256 #pragma omp parallel for
257 for (size_t cellIndex=0; cellIndex<acceleratedCells.size(); ++cellIndex) {
258 const CellID cellID = acceleratedCells[cellIndex];
259 Population& pop = mpiGrid[cellID]->get_population(popID);
260 // Place intersections into array so that propagation direction is k-coordinate ("z")
261 switch (dimension) {
262 case 0:
263 // X: swap intersection i and k coordinates
264 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+0]=(Realf)pop.intersection_x;
265 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+1]=(Realf)pop.intersection_x_dk;
266 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+2]=(Realf)pop.intersection_x_dj;
267 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+3]=(Realf)pop.intersection_x_di;
268 break;
269 case 1:
270 // Y: swap intersection j and k coordinates
271 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+0]=(Realf)pop.intersection_y;
272 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+1]=(Realf)pop.intersection_y_di;
273 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+2]=(Realf)pop.intersection_y_dk;
274 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+3]=(Realf)pop.intersection_y_dj;
275 break;
276 case 2:
277 // Z: k remains propagation coordinate, no swaps
278 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+0]=(Realf)pop.intersection_z;
279 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+1]=(Realf)pop.intersection_z_di;
280 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+2]=(Realf)pop.intersection_z_dj;
281 (GET_POINTER(gpuMemoryManager, Realf, host_intersections))[cellIndex*4+3]=(Realf)pop.intersection_z_dk;
282 break;
283 default:
284 std::cerr<<"Invalid dimension "<<dimension<<"!"<<std::endl;
285 abort();
286 }
287 }
288 // Send intersection data to device
290
291 // Call acceleration solver in chunks, the size of which is determined by the GPU
292 // Vlasov allocation number.
293 const uint maxChunkSize = gpu_getAllocationCount();
294
295 uint queuedCells = 0;
296 uint checkedCells = 0;
297 size_t cumulativeOffset = 0;
298 uint chunk = 0;
299 std::vector<CellID> launchCells;
300
301 for (size_t cellIndex=0; cellIndex<nCells; ++cellIndex) {
302 CellID cid = acceleratedCells[cellIndex];
303 SpatialCell* SC = mpiGrid[cid];
304 const uint blockCount = SC->get_velocity_mesh(popID)->size();
305
306 if (blockCount > 0) {
307 // Only accelerate non-empty cells
308 launchCells.push_back(cid);
309 queuedCells++;
310 }
311 // Keep track of all checked cells for cumulative offset into pointer buffers
312 checkedCells++;
313
314 // Once enough cells have been gathered into the chunk, or we have evaluated
315 // the last of potential cells, Launch the acceleration solver for this chunk.
316 // Will not launch if no cells to be accelerated are left.
317 if (queuedCells == maxChunkSize || ( (cellIndex==nCells-1) && (queuedCells > 0) )) {
318 // Phiprof timer
319 string timerName = "semilag-acc-dim"+std::to_string(dimension)+"-chunk";
320 //timerName += "-"+std::to_string(chunk); // Optional: phiprof label for chunk id
321 phiprof::Timer accChunkTimer {timerName};
322
323 gpu_acc_map_1d(mpiGrid,
324 launchCells,
325 popID,
326 dimension,
327 Dacc,
328 Dother,
330 );
331 cumulativeOffset += checkedCells;
332 queuedCells = 0;
333 checkedCells = 0;
334 chunk++;
335 launchCells.clear();
336 }
337 }
338 }
339}
#define gpuMemcpyHostToDevice
#define CHK_ERR(err)
#define gpuMemcpy
#define gpuMemset
#define gpuDeviceSynchronize
split::SplitVector< vmesh::GlobalID > * dev_velocity_block_with_content_list
vmesh::VelocityMesh * get_velocity_mesh(const size_t &popID)
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_velocity_block_with_no_content_map
Real getVelocityBlockMinValue(const uint popID) const
Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > * dev_velocity_block_with_content_map
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_to_replace
split::SplitVector< vmesh::GlobalID > * dev_list_with_replace_new
void applyReservation(const uint popID)
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_delete
split::SplitVector< Hashinator::hash_pair< vmesh::GlobalID, vmesh::LocalID > > * dev_list_with_replace_old
Population & get_population(const uint popID)
void setReservation(const uint popID, const vmesh::LocalID reservationsize, bool force=false)
vmesh::VelocityBlockContainer * dev_get_velocity_blocks(const size_t &popID)
vmesh::VelocityMesh * dev_get_velocity_mesh(const size_t &popID)
size_t size(bool dummy=0) const
#define WID
Definition common.h:514
const int WID2
Definition common.h:516
void compute_cell_intersections(spatial_cell::SpatialCell *spatial_cell, const uint popID, const uint map_order, const Real &dt, int intersections_id)
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 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
__global__ void vmesh::VelocityMesh **__restrict__ ColumnOffsets split::SplitVector< vmesh::GlobalID > Hashinator::Hashmap< vmesh::GlobalID, vmesh::LocalID > const uint *__restrict__ const Realf * dev_intersections
__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 const int const Realf const Realf vmesh::LocalID * dev_resizeSuccess
void gpu_accelerate_cells(dccrg::Dccrg< SpatialCell, dccrg::Cartesian_Geometry > &mpiGrid, const std::vector< CellID > &acceleratedCells, const uint popID, const uint map_order)
Propagates the distribution function in velocity space of given list of real space cells using a semi...
__host__ void gpu_acc_allocate(uint maxBlockCount)
Definition gpu_base.cpp:537
GPUMemoryManager gpuMemoryManager
Definition gpu_base.cpp:64
__host__ void gpu_batch_allocate(uint nCells, uint maxNeighbours)
Definition gpu_base.cpp:462
__host__ uint gpu_getAllocationCount()
Definition gpu_base.cpp:259
__host__ void gpu_vlasov_allocate(const uint maxBlockCount)
Definition gpu_base.cpp:335
#define GET_POINTER(object, type, member)
Definition gpu_base.hpp:809
#define SINGLE_ARG(...)
Definition gpu_base.hpp:280
ObjectWrapper & getObjectWrapper()
Definition main.cpp:33
uint32_t LocalID
Definition definitions.h:60
ARCH_HOSTDEV MeshWrapper * getMeshWrapper()
std::vector< species::Species > particleSpecies
std::array< vmesh::MeshParameters, MAX_VMESH_PARAMETERS_COUNT > * velocityMeshes