Skip to content

Commit 1a2bbfb

Browse files
committed
example19: Use interaction queues instead of SOAData
This revives an old concept from the toy examples 1, 2, and 4 (deleted in commit 9230c4a) to have one queue per discrete interaction. This appears to be equally fast as the quite complex scheduling kernel based on SOAData.
1 parent 9377092 commit 1a2bbfb

File tree

5 files changed

+293
-360
lines changed

5 files changed

+293
-360
lines changed

base/inc/AdePT/MParray.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,14 @@ class MParray : protected copcore::VariableSizeObjectInterface<MParray, int> {
101101
const_reference operator[](size_t index) const { return fData[index]; }
102102

103103
/** @brief Dispatch next free element, nullptr if none left */
104-
__host__ __device__
105-
__forceinline__
106-
bool push_back(value_type val)
104+
__host__ __device__ __forceinline__ int push_back(value_type val)
107105
{
108106
// Operation may fail if the max size is exceeded. Has to be checked by the user.
109107
int index = fNbooked.fetch_add(1);
110-
if (index >= fCapacity) return false;
108+
if (index >= fCapacity) return -1;
111109
fData[index] = val;
112110
fNused++;
113-
return true;
111+
return index;
114112
}
115113

116114
/** @brief Check if container is fully distributed */

examples/Example19/electrons.cu

+110-114
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
template <bool IsElectron>
3232
static __device__ __forceinline__ void TransportElectrons(Track *electrons, const adept::MParray *active,
3333
Secondaries &secondaries, adept::MParray *activeQueue,
34-
GlobalScoring *globalScoring,
35-
ScoringPerVolume *scoringPerVolume, SOAData soaData)
34+
Interactions interactions, GlobalScoring *globalScoring,
35+
ScoringPerVolume *scoringPerVolume)
3636
{
3737
#ifdef VECGEOM_FLOAT_PRECISION
3838
const Precision kPush = 10 * vecgeom::kTolerance;
@@ -57,9 +57,6 @@ static __device__ __forceinline__ void TransportElectrons(Track *electrons, cons
5757
if (push) activeQueue->push_back(globalSlot);
5858
};
5959

60-
// Signal that this globalSlot doesn't undergo an interaction (yet)
61-
soaData.nextInteraction[i] = -1;
62-
6360
// Init a track with the needed data to call into G4HepEm.
6461
G4HepEmElectronTrack elTrack;
6562
G4HepEmTrack *theTrack = elTrack.GetTrack();
@@ -285,151 +282,150 @@ static __device__ __forceinline__ void TransportElectrons(Track *electrons, cons
285282
continue;
286283
}
287284

288-
soaData.nextInteraction[i] = winnerProcessIndex;
285+
interactions.queues[winnerProcessIndex]->push_back(globalSlot);
289286

290287
survive(false);
291288
}
292289
}
293290

294291
// Instantiate kernels for electrons and positrons.
295292
__global__ void TransportElectrons(Track *electrons, const adept::MParray *active, Secondaries secondaries,
296-
adept::MParray *activeQueue, GlobalScoring *globalScoring,
297-
ScoringPerVolume *scoringPerVolume, SOAData soaData)
293+
adept::MParray *activeQueue, Interactions interactions, GlobalScoring *globalScoring,
294+
ScoringPerVolume *scoringPerVolume)
298295
{
299-
TransportElectrons</*IsElectron*/ true>(electrons, active, secondaries, activeQueue, globalScoring, scoringPerVolume,
300-
soaData);
296+
TransportElectrons</*IsElectron*/ true>(electrons, active, secondaries, activeQueue, interactions, globalScoring,
297+
scoringPerVolume);
301298
}
302299
__global__ void TransportPositrons(Track *positrons, const adept::MParray *active, Secondaries secondaries,
303-
adept::MParray *activeQueue, GlobalScoring *globalScoring,
304-
ScoringPerVolume *scoringPerVolume, SOAData soaData)
300+
adept::MParray *activeQueue, Interactions interactions, GlobalScoring *globalScoring,
301+
ScoringPerVolume *scoringPerVolume)
305302
{
306-
TransportElectrons</*IsElectron*/ false>(positrons, active, secondaries, activeQueue, globalScoring, scoringPerVolume,
307-
soaData);
303+
TransportElectrons</*IsElectron*/ false>(positrons, active, secondaries, activeQueue, interactions, globalScoring,
304+
scoringPerVolume);
308305
}
309306

310307
template <bool IsElectron, int ProcessIndex>
311-
__device__ void ElectronInteraction(int const globalSlot, SOAData const & /*soaData*/, int const /*soaSlot*/,
312-
Track *particles, Secondaries secondaries, adept::MParray *activeQueue,
313-
GlobalScoring *globalScoring, ScoringPerVolume *scoringPerVolume)
308+
__device__ void ElectronInteraction(Track *particles, const adept::MParray *active, Secondaries secondaries,
309+
adept::MParray *activeQueue, GlobalScoring *globalScoring,
310+
ScoringPerVolume *scoringPerVolume)
314311
{
315-
Track &currentTrack = particles[globalSlot];
316-
const auto volume = currentTrack.navState.Top();
317-
// the MCC vector is indexed by the logical volume id
318-
const int lvolID = volume->GetLogicalVolume()->id();
319-
const int theMCIndex = MCIndex[lvolID];
320-
321-
auto survive = [&] { activeQueue->push_back(globalSlot); };
322-
323-
const double energy = currentTrack.energy;
324-
const double theElCut = g4HepEmData.fTheMatCutData->fMatCutData[theMCIndex].fSecElProdCutE;
325-
326-
RanluxppDouble newRNG{currentTrack.rngState.Branch()};
327-
G4HepEmRandomEngine rnge{&currentTrack.rngState};
328-
329-
if constexpr (ProcessIndex == 0) {
330-
// Invoke ionization (for e-/e+):
331-
double deltaEkin = (IsElectron) ? G4HepEmElectronInteractionIoni::SampleETransferMoller(theElCut, energy, &rnge)
332-
: G4HepEmElectronInteractionIoni::SampleETransferBhabha(theElCut, energy, &rnge);
333-
334-
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
335-
double dirSecondary[3];
336-
G4HepEmElectronInteractionIoni::SampleDirections(energy, deltaEkin, dirSecondary, dirPrimary, &rnge);
337-
338-
Track &secondary = secondaries.electrons.NextTrack();
339-
atomicAdd(&globalScoring->numElectrons, 1);
340-
341-
secondary.InitAsSecondary(/*parent=*/currentTrack);
342-
secondary.rngState = newRNG;
343-
secondary.energy = deltaEkin;
344-
secondary.dir.Set(dirSecondary[0], dirSecondary[1], dirSecondary[2]);
345-
346-
currentTrack.energy = energy - deltaEkin;
347-
currentTrack.dir.Set(dirPrimary[0], dirPrimary[1], dirPrimary[2]);
348-
survive();
349-
} else if constexpr (ProcessIndex == 1) {
350-
// Invoke model for Bremsstrahlung: either SB- or Rel-Brem.
351-
double logEnergy = std::log(energy);
352-
double deltaEkin = energy < g4HepEmPars.fElectronBremModelLim
353-
? G4HepEmElectronInteractionBrem::SampleETransferSB(&g4HepEmData, energy, logEnergy,
354-
theMCIndex, &rnge, IsElectron)
355-
: G4HepEmElectronInteractionBrem::SampleETransferRB(&g4HepEmData, energy, logEnergy,
356-
theMCIndex, &rnge, IsElectron);
357-
358-
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
359-
double dirSecondary[3];
360-
G4HepEmElectronInteractionBrem::SampleDirections(energy, deltaEkin, dirSecondary, dirPrimary, &rnge);
361-
362-
Track &gamma = secondaries.gammas.NextTrack();
363-
atomicAdd(&globalScoring->numGammas, 1);
364-
365-
gamma.InitAsSecondary(/*parent=*/currentTrack);
366-
gamma.rngState = newRNG;
367-
gamma.energy = deltaEkin;
368-
gamma.dir.Set(dirSecondary[0], dirSecondary[1], dirSecondary[2]);
369-
370-
currentTrack.energy = energy - deltaEkin;
371-
currentTrack.dir.Set(dirPrimary[0], dirPrimary[1], dirPrimary[2]);
372-
survive();
373-
} else if constexpr (ProcessIndex == 2) {
374-
// Invoke annihilation (in-flight) for e+
375-
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
376-
double theGamma1Ekin, theGamma2Ekin;
377-
double theGamma1Dir[3], theGamma2Dir[3];
378-
G4HepEmPositronInteractionAnnihilation::SampleEnergyAndDirectionsInFlight(
379-
energy, dirPrimary, &theGamma1Ekin, theGamma1Dir, &theGamma2Ekin, theGamma2Dir, &rnge);
380-
381-
Track &gamma1 = secondaries.gammas.NextTrack();
382-
Track &gamma2 = secondaries.gammas.NextTrack();
383-
atomicAdd(&globalScoring->numGammas, 2);
384-
385-
gamma1.InitAsSecondary(/*parent=*/currentTrack);
386-
gamma1.rngState = newRNG;
387-
gamma1.energy = theGamma1Ekin;
388-
gamma1.dir.Set(theGamma1Dir[0], theGamma1Dir[1], theGamma1Dir[2]);
389-
390-
gamma2.InitAsSecondary(/*parent=*/currentTrack);
391-
// Reuse the RNG state of the dying track.
392-
gamma2.rngState = currentTrack.rngState;
393-
gamma2.energy = theGamma2Ekin;
394-
gamma2.dir.Set(theGamma2Dir[0], theGamma2Dir[1], theGamma2Dir[2]);
395-
396-
// The current track is killed by not enqueuing into the next activeQueue.
312+
int activeSize = active->size();
313+
for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < activeSize; i += blockDim.x * gridDim.x) {
314+
const int globalSlot = (*active)[i];
315+
Track &currentTrack = particles[globalSlot];
316+
const auto volume = currentTrack.navState.Top();
317+
// the MCC vector is indexed by the logical volume id
318+
const int lvolID = volume->GetLogicalVolume()->id();
319+
const int theMCIndex = MCIndex[lvolID];
320+
321+
auto survive = [&] { activeQueue->push_back(globalSlot); };
322+
323+
const double energy = currentTrack.energy;
324+
const double theElCut = g4HepEmData.fTheMatCutData->fMatCutData[theMCIndex].fSecElProdCutE;
325+
326+
RanluxppDouble newRNG{currentTrack.rngState.Branch()};
327+
G4HepEmRandomEngine rnge{&currentTrack.rngState};
328+
329+
if constexpr (ProcessIndex == 0) {
330+
// Invoke ionization (for e-/e+):
331+
double deltaEkin = (IsElectron) ? G4HepEmElectronInteractionIoni::SampleETransferMoller(theElCut, energy, &rnge)
332+
: G4HepEmElectronInteractionIoni::SampleETransferBhabha(theElCut, energy, &rnge);
333+
334+
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
335+
double dirSecondary[3];
336+
G4HepEmElectronInteractionIoni::SampleDirections(energy, deltaEkin, dirSecondary, dirPrimary, &rnge);
337+
338+
Track &secondary = secondaries.electrons.NextTrack();
339+
atomicAdd(&globalScoring->numElectrons, 1);
340+
341+
secondary.InitAsSecondary(/*parent=*/currentTrack);
342+
secondary.rngState = newRNG;
343+
secondary.energy = deltaEkin;
344+
secondary.dir.Set(dirSecondary[0], dirSecondary[1], dirSecondary[2]);
345+
346+
currentTrack.energy = energy - deltaEkin;
347+
currentTrack.dir.Set(dirPrimary[0], dirPrimary[1], dirPrimary[2]);
348+
survive();
349+
} else if constexpr (ProcessIndex == 1) {
350+
// Invoke model for Bremsstrahlung: either SB- or Rel-Brem.
351+
double logEnergy = std::log(energy);
352+
double deltaEkin = energy < g4HepEmPars.fElectronBremModelLim
353+
? G4HepEmElectronInteractionBrem::SampleETransferSB(&g4HepEmData, energy, logEnergy,
354+
theMCIndex, &rnge, IsElectron)
355+
: G4HepEmElectronInteractionBrem::SampleETransferRB(&g4HepEmData, energy, logEnergy,
356+
theMCIndex, &rnge, IsElectron);
357+
358+
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
359+
double dirSecondary[3];
360+
G4HepEmElectronInteractionBrem::SampleDirections(energy, deltaEkin, dirSecondary, dirPrimary, &rnge);
361+
362+
Track &gamma = secondaries.gammas.NextTrack();
363+
atomicAdd(&globalScoring->numGammas, 1);
364+
365+
gamma.InitAsSecondary(/*parent=*/currentTrack);
366+
gamma.rngState = newRNG;
367+
gamma.energy = deltaEkin;
368+
gamma.dir.Set(dirSecondary[0], dirSecondary[1], dirSecondary[2]);
369+
370+
currentTrack.energy = energy - deltaEkin;
371+
currentTrack.dir.Set(dirPrimary[0], dirPrimary[1], dirPrimary[2]);
372+
survive();
373+
} else if constexpr (ProcessIndex == 2) {
374+
// Invoke annihilation (in-flight) for e+
375+
double dirPrimary[] = {currentTrack.dir.x(), currentTrack.dir.y(), currentTrack.dir.z()};
376+
double theGamma1Ekin, theGamma2Ekin;
377+
double theGamma1Dir[3], theGamma2Dir[3];
378+
G4HepEmPositronInteractionAnnihilation::SampleEnergyAndDirectionsInFlight(
379+
energy, dirPrimary, &theGamma1Ekin, theGamma1Dir, &theGamma2Ekin, theGamma2Dir, &rnge);
380+
381+
Track &gamma1 = secondaries.gammas.NextTrack();
382+
Track &gamma2 = secondaries.gammas.NextTrack();
383+
atomicAdd(&globalScoring->numGammas, 2);
384+
385+
gamma1.InitAsSecondary(/*parent=*/currentTrack);
386+
gamma1.rngState = newRNG;
387+
gamma1.energy = theGamma1Ekin;
388+
gamma1.dir.Set(theGamma1Dir[0], theGamma1Dir[1], theGamma1Dir[2]);
389+
390+
gamma2.InitAsSecondary(/*parent=*/currentTrack);
391+
// Reuse the RNG state of the dying track.
392+
gamma2.rngState = currentTrack.rngState;
393+
gamma2.energy = theGamma2Ekin;
394+
gamma2.dir.Set(theGamma2Dir[0], theGamma2Dir[1], theGamma2Dir[2]);
395+
396+
// The current track is killed by not enqueuing into the next activeQueue.
397+
}
397398
}
398399
}
399400

400401
__global__ void IonizationEl(Track *particles, const adept::MParray *active, Secondaries secondaries,
401402
adept::MParray *activeQueue, GlobalScoring *globalScoring,
402-
ScoringPerVolume *scoringPerVolume, SOAData const soaData)
403+
ScoringPerVolume *scoringPerVolume)
403404
{
404-
InteractionLoop<0>(&ElectronInteraction<true, 0>, active, soaData, particles, secondaries, activeQueue, globalScoring,
405-
scoringPerVolume);
405+
ElectronInteraction<true, 0>(particles, active, secondaries, activeQueue, globalScoring, scoringPerVolume);
406406
}
407407
__global__ void BremsstrahlungEl(Track *particles, const adept::MParray *active, Secondaries secondaries,
408408
adept::MParray *activeQueue, GlobalScoring *globalScoring,
409-
ScoringPerVolume *scoringPerVolume, SOAData const soaData)
409+
ScoringPerVolume *scoringPerVolume)
410410
{
411-
InteractionLoop<1>(&ElectronInteraction<true, 1>, active, soaData, particles, secondaries, activeQueue, globalScoring,
412-
scoringPerVolume);
411+
ElectronInteraction<true, 1>(particles, active, secondaries, activeQueue, globalScoring, scoringPerVolume);
413412
}
414413

415414
__global__ void IonizationPos(Track *particles, const adept::MParray *active, Secondaries secondaries,
416415
adept::MParray *activeQueue, GlobalScoring *globalScoring,
417-
ScoringPerVolume *scoringPerVolume, SOAData const soaData)
416+
ScoringPerVolume *scoringPerVolume)
418417
{
419-
InteractionLoop<0>(&ElectronInteraction<false, 0>, active, soaData, particles, secondaries, activeQueue,
420-
globalScoring, scoringPerVolume);
418+
ElectronInteraction<false, 0>(particles, active, secondaries, activeQueue, globalScoring, scoringPerVolume);
421419
}
422420
__global__ void BremsstrahlungPos(Track *particles, const adept::MParray *active, Secondaries secondaries,
423421
adept::MParray *activeQueue, GlobalScoring *globalScoring,
424-
ScoringPerVolume *scoringPerVolume, SOAData const soaData)
422+
ScoringPerVolume *scoringPerVolume)
425423
{
426-
InteractionLoop<1>(&ElectronInteraction<false, 1>, active, soaData, particles, secondaries, activeQueue,
427-
globalScoring, scoringPerVolume);
424+
ElectronInteraction<false, 1>(particles, active, secondaries, activeQueue, globalScoring, scoringPerVolume);
428425
}
429426
__global__ void AnnihilationPos(Track *particles, const adept::MParray *active, Secondaries secondaries,
430427
adept::MParray *activeQueue, GlobalScoring *globalScoring,
431-
ScoringPerVolume *scoringPerVolume, SOAData const soaData)
428+
ScoringPerVolume *scoringPerVolume)
432429
{
433-
InteractionLoop<2>(&ElectronInteraction<false, 2>, active, soaData, particles, secondaries, activeQueue,
434-
globalScoring, scoringPerVolume);
430+
ElectronInteraction<false, 2>(particles, active, secondaries, activeQueue, globalScoring, scoringPerVolume);
435431
}

0 commit comments

Comments
 (0)