Problem 1397 - Multiple AtRest processes are called for a particle
Summary: Multiple AtRest processes are called for a particle
Status: RESOLVED FIXED
Alias: None
Product: Geant4
Classification: Unclassified
Component: tracking (show other problems)
Version: 9.5
Hardware: All All
: P5 major
Assignee: Takashi.Sasaki
URL:
Depends on:
Blocks:
 
Reported: 2012-11-28 17:42 CET by Daniel Barna
Modified: 2013-08-20 11:34 CEST (History)
1 user (show)

See Also:


Attachments
Modified code for debugging (9.46 KB, text/plain)
2012-12-08 23:09 CET, Daniel Barna
Details

Note You need to log in before you can comment on or make changes to this problem.
Description Daniel Barna 2012-11-28 17:42:29 CET
When a particle is at rest, the rest-process proposing the shortest interaction length should be called, the other rest processes associated with this particle not (unless they are forced).

However, if I associate these two processes below
with a particle, it is always only MyProcessA that should be called,
since this is the one with the shorter interaction length. Both are
called, as can easily be tested. If I invert the GPIL values (MyProcessA
returning 20, and MyProcessB returning 10), then only MyProcessB is
called. This depends on the ordering of the processes - see the explanation at the end of this report.


[code]

class MyProcessA : public G4VRestProcess
{
public:
     G4bool IsApplicable(const G4ParticleDefinition& ) { return true; }
     MyProcessA() : G4VRestProcess("MyProcessA",fHadronic) {}
     G4double AtRestGetPhysicalInteractionLength(const G4Track& , G4ForceCondition* condition)
         {
             *condition = NotForced;
             cerr<<"MyProcessA::AtRestGetPhysicalInteractionLength returning 10"<<endl;
             return 10;
         }
     G4VParticleChange *AtRestDoIt(const G4Track &track, const G4Step &)
         {
             aParticleChange.Initialize(track);
             aParticleChange.SetNumberOfSecondaries(0);
             aParticleChange.ProposeTrackStatus(fStopAndKill);
             cerr<<"MyProcessA::AtRestDoIt"<<endl;
             return &aParticleChange;
         }
     G4double GetMeanLifeTime(const G4Track&, G4ForceCondition*) { return 0; }
};

class MyProcessB : public G4VRestProcess
{
public:
     G4bool IsApplicable(const G4ParticleDefinition& ) { return true; }
     MyProcessB() : G4VRestProcess("MyProcessB",fHadronic) {}
     G4double AtRestGetPhysicalInteractionLength(const G4Track& , G4ForceCondition* condition)
         {
             *condition = NotForced;
             cerr<<"MyProcessB::AtRestGetPhysicalInteractionLength returning 20"<<endl;
             return 20;
         }
     G4VParticleChange *AtRestDoIt(const G4Track &track , const G4Step &)
         {
             aParticleChange.Initialize(track);
             aParticleChange.SetNumberOfSecondaries(0);
             aParticleChange.ProposeTrackStatus(fStopAndKill);
             cerr<<"MyProcessB::AtRestDoIt"<<endl;
             return &aParticleChange;
         }
     G4double GetMeanLifeTime(const G4Track&, G4ForceCondition*) { return 0; }
};
[/code]

The problem lies in G4SteppingManager::InvokeAtRestDoItProcs(), see my
comments in this code:

[code]
void G4SteppingManager::InvokeAtRestDoItProcs()
{
    // Select the rest process which has the shortest time before
    // it is invoked. In rest processes, GPIL()
    // returns the time before a process occurs.
    G4double lifeTime, shortestLifeTime;

    fAtRestDoItProcTriggered = 0;
    shortestLifeTime = DBL_MAX;

    unsigned int NofInactiveProc=0;
    for( size_t ri=0 ; ri < MAXofAtRestLoops ; ri++ ){
      fCurrentProcess = (*fAtRestGetPhysIntVector)[ri];
      if (fCurrentProcess== 0) {
        (*fSelectedAtRestDoItVector)[ri] = InActivated;
        NofInactiveProc++;
        continue;
      }   // NULL means the process is inactivated by a user on fly.

      lifeTime =
        fCurrentProcess->AtRestGPIL( *fTrack, &fCondition );

      if(fCondition==Forced && fCurrentProcess){
        (*fSelectedAtRestDoItVector)[ri] = Forced;
      }
      else{
        (*fSelectedAtRestDoItVector)[ri] = InActivated;
        if(lifeTime < shortestLifeTime ){
           shortestLifeTime = lifeTime;
           fAtRestDoItProcTriggered =  G4int(ri);

           // ----------  MY COMMENTS ---------------------------
           // this line below should be outside of the loop!!!
           // If the processes happen to return sequentially decreasing
           // lifetimes,
           // all processes will be set to NotForced in sequence, and
           // therefore be called in the later part of this function!
           (*fSelectedAtRestDoItVector)[fAtRestDoItProcTriggered] = NotForced;
        }
      }
    }
    ......;
}
[/code]
Comment 1 Takashi.Sasaki 2012-12-06 07:51:46 CET
I understood your problem. However, we need to confirm that any other processes already implemented do not depend on the currecnt implementation. 

We are sorry, but we could not include the fix in the next release.
Comment 2 Daniel Barna 2012-12-08 23:09:21 CET
Created attachment 196 [details]
Modified code for debugging

These are the 3 functions (G4SteppingManager::InvokeAtRestDoItProcs(), G4Decay::AtRestDoIt(...), G4PiMinusAbsorptionAtRest::AtRestDoIt()) which I modified to print debugging information.
Comment 3 Daniel Barna 2012-12-08 23:20:24 CET
I understand your concern. However please note that this is a *serious* bug, releasing a new major version without fixing this seems a bit strange to me.

In order to show that this bug has an effect in realistic and not only artificially created situations, I modified some code of geant4 (see previous attachments) to print debugging information (namely the secondary particles from G4PiMinusAbsorption and G4Decay). 

I started very low energy pi- particles so that they stop immediately and either decay or get absorbed via G4PiMinusAbsorptionAtRest. This is quite a common situation. Only one of these processes must be invoked, never both of them.
If I register the decay after the other processes, BOTH processes are invoked, generating protons and neutrons (from the absorption) and a mu+nu pair (from the decay) - an unphysical situation. (As I described earlier, the order of the processes is important to reproduce this bug. But the user might just register the processes in the wrong order...)

I used the SFPhysicsList (http://www.slac.stanford.edu/comp/physics/geant4/slac_physics_lists/simple/simplePhys/src/SFPhysicsList.cc) with the small modification that I changed the registration order of the processes:
[code]
PhysicsList::PhysicsList():  G4VModularPhysicsList()
{
  // default cut value  (1.0mm) 
  defaultCutValue = 1.0*mm;
  // SetVerboseLevel(1);


  // Bosons (gamma + geantinos)
  RegisterPhysics( new SFBosonPhysics("boson"));

  // Leptons
  RegisterPhysics( new SFLeptonPhysics("lepton"));

  // Neutron Physics
  RegisterPhysics( new SFNeutronPhysics("neutron"));

  // Hadron Physics
  RegisterPhysics( new SFHadronPhysics("hadron"));

  // Ion Physics
  RegisterPhysics( new SFIonPhysics("ion"));

  // Particle decays
  RegisterPhysics( new SFDecayPhysics("decay"));

}
[/code]

This is a typical output of my test program, showing that indeed both processes are invoked:
[code]
G4SteppingManager::InvokeAtRestDoItProcs() is examining the processes for pi-
  Checking process: Decay
  Lifetime: 14.2003
  This is less than shortest lifetime so far, this process is selected for execution
  Checking process: PiMinusAbsorptionAtRest
  Lifetime: 0
  This is less than shortest lifetime so far, this process is selected for execution
  Executing PiMinusAbsorptionAtRest::<<AtRestDoIt(...)
  * G4PiMinusAbsorptionAtRest generated the following particles:
      - neutron
      - proton
      - deuteron
  Executing Decay::<<AtRestDoIt(...)
  * G4Decay for pi- generated the following secondaries:
      - anti_nu_mu
      - mu-
[/code]
Comment 4 Gabriele Cosmo 2013-08-20 11:34:39 CEST
The fix is provided since release 9.6.p02.