Last step in a volume

In my implementation of G4VSensitiveDetector::ProcessHits I’d need to check if the current step is the last step inside the current volume. To my knowledge, this can happen due to quite some reasons:

  • the step ends on a geometry boundary
  • the particle is stopped
  • the particle energy goes below the user limit
  • the particle is destroyed by an interaction

and maybe others that I’m missing at the moment. Is there an “easy” way to check if the current step is the last one or do I need to check all the above conditions (and maybe others?) one by one? Thanks.



slipped under my radar, and is almost was looking for. “Almost” because from my tests it seems that when a particle is going to be killed inside the volume (e.g. because it is stopped) then G4Step::IsLastStepInVolume() returns false. So I guess that it is just a geometric check on the particle being on an exit boundary, and thus I see no difference with aStep->GetPostStepPoint()->GetStepStatus() == fGeomBoundary

It seems that also:


behaves in a way I cannot understand. I dumped the information about first step in volume and the step being or not on a boundary for a given track ID for two consecutive steps:

Track ID     firstStep     preBoundary     postBoundary
11744425         1                 1            0         
11744425         1                 0            0  

From this I understand that on first step the particle enters in the volume from a boundary, and then on second steps it enters again in the same volume but without crossing boundaries. Which does not make sense to me…


if a particle is killed to whatever reason its energy is set to zero.


I’m currently seeing a step in my sensitive detector which:

  1. is the first step for the given track ID (I put a printout inside an if condition checking for the track ID)
  2. is not the first step in volume, nor the last, according to G4Step::Is{First,Last}StepInVolume()
  3. deposits no energy
  4. has a null pre-step and post-step kinetic energy
  5. has a fUndefined pre-step status and a fAtRestDoItProc post-step status
  6. is for an antiproton particle
  7. has a track created by the hFritiofCaptureAtRest process
  8. has a fStopAndKill track status

To me, it looks quite a strange step. The particle is obviously created at rest inside the sensitive volume, but I don’t understand why the step it is not flagged as first step in volume.

“First step in volume” is flagged for a track that comes in to this volume through the surface. It is not set for a track that was born inside the volume.

@asaim thank you for the clarification. So going back to my original question, how can I check if a particle is doing its last step inside a given volume, either because it exits from a boundary or it is absorbed or killed by a process? Maybe G4Step::IsLastStepInVolume() is ok for exiting particles but what for absorbed/killed ones? Should I check for fStopAndKill track status? Is it sufficient?

Yes, you can identify a track that is leaving the volume with G4Step::IsLastStepInVolume(), and a track that is absorbed/decayed/killed within the volume with fStopAndKill.

@asaim I realized that there was something wrong with your reply about first step in volume, so I did more checks. In fact, it’s not true that the “is first step in volume” is false for tracks born inside the volume: simulating a 10 GeV photon hitting a BGO target the electron and positron produced by the pair production are flagged as making their first step inside the volume. So maybe this flag is set to false only when the newly created particle has null kinetic energy??

If fStopAndKill is set, that track won’t make a step. Thus, it won’t have the FirstStep flag.

@asaim ok, now it’s 100% clear. Thanks again.

There appears to be a couple other corner cases not fully covered by G4Step::IsLastStepInVolume() and fStopAndKill that could cause problems for people, depending on the application.

  1. We’re using the technique described above to sum the total Cherenkovs produced across each step taken by a charged particle in a single SD. As such, we define a global count variable that gets reset after one of these two conditions occurs. Doing this relies on the assumption that all steps from a single track are processed in a contiguous manner. This appears to be mostly correct in Geant, but apparently, for whatever reason, some processes break this pattern and steps from another track are sandwiched between the original, causing the Cherenkov count to be miscounted. And it’s even more serious if the other track was from a different SD, as then the count is misallocated.

  2. There are also cases where back-scattering happens and a track that gets registered as taking its LastStepInVolume later takes more steps in that same volume. So as @nmori said above, it’s important to realize that the LastStepInVolume flag is purely a geometric check at the boundary step. It does not guarantee that this is actually the last step this track takes in the volume.

The Cherenkov and Scintillation processes, can be tricky. The have a flag which may be set to put the main track into state fSuspend, track the generated secondaries first, and then come back and continue with the original track. If you’ve written your counter code assuming that a single track gets processes continuously, this can mess up your counting.