G4UserLimits causes corruption of output file

Please fill out the following information to help in answering your question, and also see tips for posting code snippets. If you don’t provide this information it will take more time to help with your problem!

Geant4 Version: 11.3.2
Operating System: Almalinux9
Compiler/Version: gcc 14.2.0
CMake Version: 3.30.6


Hi, I’m trying to implement special tracking tracks in my application. I found that using maxTrackLength cuts causes the .root output file to be corrupted. My application works without special cuts, and this issue doesn’t appear when defining stepLimit or maxTime cuts, only maxTrackLength causes this (unfortunately is the one I need).

This is what I put in DetectorConstruction, following the B2 example:

auto maxTrackLength = 20.0 * cm;
auto maxTime = 500.0 * ns;
fLimits = new G4UserLimits(DBL_MAX, maxTrackLength, maxTime);
calorLV->SetUserLimits(fLimits);

while in main.cc

auto physicsList = new FTFP_BERT(0);
//Optical Physics G4OpticalPhysics* opticalPhysics = new G4OpticalPhysics(1, "OpticalPhysics"); setOpticalParams(config);
physicsList->RegisterPhysics(opticalPhysics); //Set User Limits physicsList->RegisterPhysics(new G4StepLimiterPhysics()); runManager->SetUserInitialization(physicsList);

The application runs fine, but then when I try to open my .root output file I get:

root -l mu_BGO_bug01_10_90deg.root
root [0]
Attaching file mu_BGO_bug01_10_90deg.root as _file0…
Error in <TFile::ReadBuffer>: error reading all requested bytes from file mu_BGO_bug01_10_90deg.root, got 240 of 300
Error in <TFile::Init>: mu_BGO_bug01_10_90deg.root failed to read the file type data.
(TFile *) nullptr
root [1]

For the record, I also used the CLion IDE, and in that case the addition of maxTrackLength causes the IDE to crash

Hello,

Can you give more details about your ROOT output ?
How do you create your ROOT file and objects and in which Geant4 user actions or sensitive detectors you fill them ?

Best regards,

Hello,

My output consists in a single root file containing histograms and ntuples. I use both SteppingAction and SensitiveDetector to fill them. There a lot of Ntuples so I will omit some to keep the thread readable.

in the constructor of RunAction:

RunAction::RunAction(const string& configFileName, EventAction\* eventAction)
: fEventAction(eventAction)
{

DefineCommands();

// Create analysis manager
// The choice of the output format is done via the specified
// file extension.

auto analysisManager = G4AnalysisManager::Instance();
analysisManager->SetVerboseLevel(1);
analysisManager->SetNtupleMerging(true);
analysisManager->SetActivation(true);

// Book histograms, ntuples

//

// Creating histograms

analysisManager->CreateH1(“Ecalo”,“Edep in crystal”, 100, Ecalo_min\*MeV, Ecalo_max\*MeV);  //0

analysisManager->CreateH1(“Lcalo”,“trackL in crystal”, 100, 0., 50\*m);  //1

analysisManager->CreateH1(“Elyso”,“Edep in lyso”, 100, 0.\*MeV, 50.\*MeV);  //2

analysisManager->CreateH1(“Eps”,“Edep  in preshower”, 100, 0.\*MeV, 1000\*MeV); //3

analysisManager->CreateH1(“Ecalo_SA”,“Edep  in crystal (SA)”, 100, Ecalo_min\*MeV, Ecalo_max\*MeV); //4

analysisManager->CreateH1(“N_scint_CH1”,“N photons scint in S”, 100, 0, 15000); //5

analysisManager->CreateH1(“N_cer_CH1”,“N photons cer in S”, 10, 0, 30); //6

analysisManager->CreateH1(“N_scint_CH2”,“N photons scint in C”, 100, 0, 100000);  //7

analysisManager->CreateH1(“N_cer_CH2”,“N photons cer in C”, 100, 0, 2000);  //8

// Create ntuples, first beam information and energy deposit information

analysisManager->CreateNtuple(“primaryEdep”, “Edep and TrackL”);

analysisManager->CreateNtupleDColumn(“primaryEnergy”);    //0

analysisManager->CreateNtupleDColumn(“primaryX”);         //1

analysisManager->CreateNtupleDColumn(“primaryY”);         //2

analysisManager->CreateNtupleDColumn(“primaryZ”);         //3

analysisManager->CreateNtupleDColumn(“primaryPx”);        //4

analysisManager->CreateNtupleDColumn(“primaryPy”);        //5

analysisManager->CreateNtupleDColumn(“primaryPz”);        //6

analysisManager->CreateNtupleDColumn(“Ecalo”);            //7

analysisManager->CreateNtupleDColumn(“Elyso”);            //8

analysisManager->CreateNtupleDColumn(“Epreshower”);       //9

analysisManager->CreateNtupleDColumn(“Lcalo”);            //10

analysisManager->CreateNtupleDColumn(“Ecalo_SA”);         //11

analysisManager->CreateNtupleIColumn(“eventID”);          //12

analysisManager->FinishNtuple();

//ntuple for information about arrival of photons

analysisManager->CreateNtuple(“photoDet”, “photons arrival”);

analysisManager->CreateNtupleIColumn(“N_scint_CH1”);      //0

analysisManager->CreateNtupleIColumn(“N_cer_CH1”);        //1

analysisManager->CreateNtupleIColumn(“N_scint_CH2”);      //2

analysisManager->CreateNtupleIColumn(“N_cer_CH2”);        //3

analysisManager->CreateNtupleIColumn(“N_scint_calo”);     //4

analysisManager->CreateNtupleIColumn(“N_cer_calo”);       //5

//vector branches don’t need to be filled in EventAction

//time vectors

analysisManager->CreateNtupleFColumn(“time_scint_CH1”, fEventAction->m_time_scint_CH1);

analysisManager->CreateNtupleFColumn(“time_cer_CH1”, fEventAction->m_time_cer_CH1);

analysisManager->CreateNtupleFColumn(“time_scint_CH2”, fEventAction->m_time_scint_CH2);

analysisManager->CreateNtupleFColumn(“time_cer_CH2”, fEventAction->m_time_cer_CH2);
//more branches following
analysisManager->FinishNtuple();

-BeginOfRunAction()

void RunAction::BeginOfRunAction(const G4Run\* /\*run\*/)

{

//inform the runManager to save random number seed

//G4RunManager::GetRunManager()->SetRandomNumberStore(true);

// Get analysis manager

auto analysisManager = G4AnalysisManager::Instance();

// Open an output file

//

if (!fname.empty()) fname = fname;

G4String fileName = fpath + fname + “.root”;

analysisManager->OpenFile(fileName);

G4cout << "Using " << analysisManager->GetType() << G4endl;

}

-EndOfRunAction()

void RunAction::EndOfRunAction(const G4Run\* /\*run\*/)

{

auto analysisManager = G4AnalysisManager::Instance();
analysisManager->Write();
analysisManager->CloseFile();

}

Some information is saved by SteppingAction()

void SteppingAction::UserSteppingAction(const G4Step\* step)

{

// get volume of the current step

auto volume = step->GetPreStepPoint()->GetTouchableHandle()->GetVolume();

auto theTrack = step->GetTrack();

auto theTrackInfo = dynamic_cast<TrackInformation\*>(theTrack->GetUserInformation());

//Beam information

if(G4StrUtil::contains(volume->GetName(), “World”)){

if ( theTrack->GetParentID() == 0 ) // select only the primary particle

{

  G4StepPoint\* thePrePoint  = step->GetPreStepPoint();
const G4ThreeVector& thePrePosition  = thePrePoint->GetPosition();

G4VPhysicalVolume\* thePrePV = thePrePoint->GetPhysicalVolume();

  G4String thePrePVName = "";

if (thePrePV) thePrePVName = thePrePV->GetName();

}

}

SensitiveDetectors are defined for SiPM volumes, which is not the volume where I defined the UserLimits:

G4bool SiPMSD::ProcessHits(G4Step\* aStep, G4TouchableHistory\*)

{

if (!aStep) return false;

G4Track\* theTrack = aStep->GetTrack();

//check if it's an optical photon, SiPM only measure optical photons

if (theTrack->GetDefinition() != G4OpticalPhoton::OpticalPhotonDefinition()) {
  return false;
}

//get step points of the step
G4StepPoint\* thePrePoint = aStep->GetPreStepPoint();
G4StepPoint\* thePostPoint = aStep->GetPostStepPoint();

G4String process = theTrack->GetCreatorProcess()->GetProcessName();
auto theTrackInfo = dynamic_cast<TrackInformation\*>(theTrack->GetUserInformation());
G4int parentID = theTrack->GetParentID();
G4double photonEnergy = theTrack->GetTotalEnergy();
G4double photonTime = theTrack->GetGlobalTime();
G4int trackID = theTrack->GetTrackID();
G4ThreeVector photonPosition = thePrePoint->GetPosition(); //a step belongs to the volume of its preStepPoint
G4ThreeVector photonMomentum = thePrePoint->GetMomentumDirection(); //a step belongs to the volume of its preStepPoint
G4String parentName = theTrackInfo->GetParentName();
G4double prodEnergy = theTrackInfo->GetParticleEnergy();
G4double prodTime = theTrackInfo->GetParticleTime();
G4ThreeVector prodPosition = theTrackInfo->GetParticlePosition();
G4ThreeVector prodMomentum = theTrackInfo->GetParticleMomentumDirection();

//define a hit and set its members

SiPMHit\* hit = new SiPMHit();
hit->SetCreatorProcess(process);
hit->SetEnergy(photonEnergy);
hit->SetTime(photonTime);
hit->SetTrackID(trackID);
hit->SetPos(photonPosition);
hit->SetMomentum(photonMomentum);
hit->SetParentName(parentName);
hit->SetProdEnergy(prodEnergy);
hit->SetProdTime(prodTime);
hit->SetProdPosition(prodPosition);
hit->SetProdMomentum(prodMomentum);
//add the hit to the hit collection
fSiPMHitCollection->insert(hit);
return true;

}

Thank you for your time

Thank you, but I do not see in your code posted where you call analysisManager->FillH1... or analysisManager->FillNtupleIColumn... etc.

Sorry I forgot to post, they are filled in EventAction:

void EventAction::BeginOfEventAction(const G4Event* event) {

  fEdep_SA = 0.;
  m_tot_phot_scint_CH1 = 0;
  m_tot_phot_cer_CH1 = 0;
  m_tot_phot_scint_CH2 = 0;
  m_tot_phot_cer_CH2 = 0;

  //time vectors
  m_time_scint_CH1.clear();
  m_time_cer_CH1.clear();
  m_time_scint_CH2.clear();
  m_time_cer_CH2.clear();
  //energy vectors
  m_energy_scint_CH1.clear();
  m_energy_cer_CH1.clear();
  m_energy_scint_CH2.clear();
  m_energy_cer_CH2.clear();

  m_nstep_scint_CH1.clear();
  m_nstep_cer_CH1.clear();
  m_nstep_scint_CH2.clear();
  m_nstep_cer_CH2.clear();

  m_scint_X_CH2.clear();
  m_scint_Y_CH2.clear();
  m_scint_Z_CH2.clear();
  m_cer_X_CH2.clear();
  m_cer_Y_CH2.clear();
  m_cer_Z_CH2.clear();
  m_scint_X_CH1.clear();
  m_scint_Y_CH1.clear();
  m_scint_Z_CH1.clear();
  m_cer_X_CH1.clear();
  m_cer_Y_CH1.clear();
  m_cer_Z_CH1.clear();

  m_scint_pX_CH2.clear();
  m_scint_pY_CH2.clear();
  m_scint_pZ_CH2.clear();
  m_cer_pX_CH2.clear();
  m_cer_pY_CH2.clear();
  m_cer_pZ_CH2.clear();
  m_scint_pX_CH1.clear();
  m_scint_pY_CH1.clear();
  m_scint_pZ_CH1.clear();
  m_cer_pX_CH1.clear();
  m_cer_pY_CH1.clear();
  m_cer_pZ_CH1.clear();

  //members for photon origin info
  m_parent_name.clear();
  m_ori_energy_scint_CH1.clear();
  m_ori_scint_X_CH1.clear();
  m_ori_scint_Y_CH1.clear();
  m_ori_scint_Z_CH1.clear();
  m_ori_scint_pX_CH1.clear();
  m_ori_scint_pY_CH1.clear();
  m_ori_scint_pZ_CH1.clear();

  m_ori_energy_cer_CH1.clear();
  m_ori_cer_X_CH1.clear();
  m_ori_cer_Y_CH1.clear();
  m_ori_cer_Z_CH1.clear();
  m_ori_cer_pX_CH1.clear();
  m_ori_cer_pY_CH1.clear();
  m_ori_cer_pZ_CH1.clear();

  m_ori_energy_scint_CH2.clear();
  m_ori_scint_X_CH2.clear();
  m_ori_scint_Y_CH2.clear();
  m_ori_scint_Z_CH2.clear();
  m_ori_scint_pX_CH2.clear();
  m_ori_scint_pY_CH2.clear();
  m_ori_scint_pZ_CH2.clear();

  m_ori_energy_cer_CH2.clear();
  m_ori_cer_X_CH2.clear();
  m_ori_cer_Y_CH2.clear();
  m_ori_cer_Z_CH2.clear();
  m_ori_cer_pX_CH2.clear();
  m_ori_cer_pY_CH2.clear();
  m_ori_cer_pZ_CH2.clear();

  G4int evtID = event->GetEventID();
  
  // Run status
  G4Run* run = static_cast<G4Run*>( G4RunManager::GetRunManager()->GetNonConstCurrentRun() );
  G4int nOfEvents = run->GetNumberOfEventToBeProcessed();
  G4double perCent = 10.; // status increment in percent
  
  if(fmod(evtID,double(nOfEvents*perCent*0.01))==0) {
    time_t my_time = time(NULL);
    tm *ltm = localtime(&my_time);
    G4double status=(100*(evtID/double(nOfEvents))); 
    std::cout << "=> Event " << evtID << " starts ("<< status << "%, "<< ltm->tm_hour << ":" <<  ltm->tm_min << ":" << ltm->tm_sec << ")" << std::endl;
  }
  
  G4PrimaryVertex* priVer = event->GetPrimaryVertex();
  G4PrimaryParticle* priPar = event->GetPrimaryVertex()->GetPrimary();
  G4double parE = priPar->GetKineticEnergy();
  G4ThreeVector parDir = priPar->GetMomentumDirection();

  // get analysis manager
  G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();

  analysisManager->FillNtupleDColumn(0, 0, parE);
  analysisManager->FillNtupleDColumn(0, 1, priVer->GetX0());
  analysisManager->FillNtupleDColumn(0, 2, priVer->GetY0());
  analysisManager->FillNtupleDColumn(0, 3, priVer->GetZ0());
  analysisManager->FillNtupleDColumn(0, 4, parDir.getX());
  analysisManager->FillNtupleDColumn(0, 5, parDir.getY());
  analysisManager->FillNtupleDColumn(0, 6, parDir.getZ());
  analysisManager->FillNtupleIColumn(0, 12, evtID);
}
void EventAction::EndOfEventAction(const G4Event* event){

  //instantiate sensitive detector manager
  auto* SDmanager = G4SDManager::GetSDMpointer();
   // Get hist collections IDs
  if ( fcaloEdepHCID == -1 ) {
    fcaloEdepHCID = SDmanager->GetCollectionID("caloDetector/Edep");
    flysoEdepHCID = SDmanager->GetCollectionID("lysoDetector/Edep");
    fpsEdepHCID = SDmanager->GetCollectionID("psDetector/Edep");
    fplasticNaEdepHCID = SDmanager->GetCollectionID("plasticNaDetector/Edep");
    fplasticMiBEdepHCID = SDmanager->GetCollectionID("plasticMiBDetector/Edep");
    fcaloTrackLengthHCID = SDmanager->GetCollectionID("caloDetector/TrackLength");
  }

  if (SDmanager->FindSensitiveDetector("SiPM_CH1_Detector")){
    fSiPMCH1HCID = SDmanager->GetCollectionID("SiPM_CH1_Detector/SiPMHitCollection");
    fSiPMCH2HCID = SDmanager->GetCollectionID("SiPM_CH2_Detector/SiPMHitCollection");
  }
  
  // Get sum values from hits collections
  //
  auto caloEdep = GetSum(GetHitsCollection(fcaloEdepHCID, event));
  auto caloTrackLength = GetSum(GetHitsCollection(fcaloTrackLengthHCID, event));
  auto lysoEdep = GetSum(GetHitsCollection(flysoEdepHCID, event));
  auto psEdep = GetSum(GetHitsCollection(fpsEdepHCID, event));
  auto plasticNaEdep = GetSum(GetHitsCollection(fplasticNaEdepHCID, event));
  auto plasticMiBEdep = GetSum(GetHitsCollection(fplasticMiBEdepHCID, event));
  
  //the number of hits in a collection corresponds to the number of detected photons, because only photons create hits
  SiPMHitCollection* SiPM_CH1_HitCollection = nullptr;
  SiPMHitCollection* SiPM_CH2_HitCollection = nullptr;

  G4HCofThisEvent* hce = event->GetHCofThisEvent();
  if (hce) {
    SiPM_CH1_HitCollection = dynamic_cast<SiPMHitCollection*>(hce->GetHC(fSiPMCH1HCID));
    SiPM_CH2_HitCollection = dynamic_cast<SiPMHitCollection*>(hce->GetHC(fSiPMCH2HCID));
  }

  int is_trig = 0;
  if (plasticNaEdep > 0 && plasticMiBEdep > 0 && lysoEdep > 0)    is_trig = 1;

  // get analysis manager
  auto analysisManager = G4AnalysisManager::Instance();

  // fill histograms
  //
  analysisManager->FillH1(0, caloEdep);
  analysisManager->FillH1(1, caloTrackLength);
  analysisManager->FillH1(2, lysoEdep);
  analysisManager->FillH1(3, psEdep);
  analysisManager->FillH1(4, fEdep_SA); //this is a sanity check to prove that we are collecting same values as in caloDep

  //save cerenkov & scintillation photons
  int totalHitsCH1 = SiPM_CH1_HitCollection->entries();
  int totalHitsCH2 = SiPM_CH2_HitCollection->entries();

  for (int j = 0; j < totalHitsCH1; ++j) {
    SiPMHit* Hit = (*SiPM_CH1_HitCollection)[j];
    if (Hit->GetCreatorProcess() == "Scintillation") {
      ++m_tot_phot_scint_CH1;
      m_energy_scint_CH1.push_back(Hit->GetEnergy());
      m_time_scint_CH1.push_back(Hit->GetTime());
      m_scint_X_CH1.push_back(Hit->GetPos().x());
      m_scint_Y_CH1.push_back(Hit->GetPos().y());
      m_scint_Z_CH1.push_back(Hit->GetPos().z());
      m_scint_pX_CH1.push_back(Hit->GetMomentum().x());
      m_scint_pY_CH1.push_back(Hit->GetMomentum().y());
      m_scint_pZ_CH1.push_back(Hit->GetMomentum().z());

      //info from TrackInformation
      m_ori_energy_scint_CH1.push_back(Hit->GetProdEnergy());
      m_ori_scint_X_CH1.push_back(Hit->GetProdPosition().x());
      m_ori_scint_Y_CH1.push_back(Hit->GetProdPosition().y());
      m_ori_scint_Z_CH1.push_back(Hit->GetProdPosition().z());
      m_ori_scint_pX_CH1.push_back(Hit->GetProdMomentum().x());
      m_ori_scint_pY_CH1.push_back(Hit->GetProdMomentum().y());
      m_ori_scint_pZ_CH1.push_back(Hit->GetProdMomentum().z());
    }
    else if (Hit->GetCreatorProcess() == "Cerenkov") {
      ++m_tot_phot_cer_CH1;
      m_energy_cer_CH1.push_back(Hit->GetEnergy());
      m_time_cer_CH1.push_back(Hit->GetTime());
      m_cer_X_CH1.push_back(Hit->GetPos().x());
      m_cer_Y_CH1.push_back(Hit->GetPos().y());
      m_cer_Z_CH1.push_back(Hit->GetPos().z());
      m_cer_pX_CH1.push_back(Hit->GetMomentum().x());
      m_cer_pY_CH1.push_back(Hit->GetMomentum().y());
      m_cer_pZ_CH1.push_back(Hit->GetMomentum().z());

      //info from Trackinformation
      m_ori_energy_cer_CH1.push_back(Hit->GetProdEnergy());
      m_ori_cer_X_CH1.push_back(Hit->GetProdPosition().x());
      m_ori_cer_Y_CH1.push_back(Hit->GetProdPosition().y());
      m_ori_cer_Z_CH1.push_back(Hit->GetProdPosition().z());
      m_ori_cer_pX_CH1.push_back(Hit->GetProdMomentum().x());
      m_ori_cer_pY_CH1.push_back(Hit->GetProdMomentum().y());
      m_ori_cer_pZ_CH1.push_back(Hit->GetProdMomentum().z());
    }
  }

  for (int j = 0; j < totalHitsCH2; ++j) {
    SiPMHit* Hit = (*SiPM_CH2_HitCollection)[j];
    if (Hit->GetCreatorProcess() == "Scintillation") {
      ++m_tot_phot_scint_CH2;
      m_energy_scint_CH2.push_back(Hit->GetEnergy());
      m_time_scint_CH2.push_back(Hit->GetTime());
      m_scint_X_CH2.push_back(Hit->GetPos().x());
      m_scint_Y_CH2.push_back(Hit->GetPos().y());
      m_scint_Z_CH2.push_back(Hit->GetPos().z());
      m_scint_pX_CH2.push_back(Hit->GetMomentum().x());
      m_scint_pY_CH2.push_back(Hit->GetMomentum().y());
      m_scint_pZ_CH2.push_back(Hit->GetMomentum().z());
      //info form trackinformation
      m_ori_energy_scint_CH2.push_back(Hit->GetProdEnergy());
      m_ori_scint_X_CH2.push_back(Hit->GetProdPosition().x());
      m_ori_scint_Y_CH2.push_back(Hit->GetProdPosition().y());
      m_ori_scint_Z_CH2.push_back(Hit->GetProdPosition().z());
      m_ori_scint_pX_CH2.push_back(Hit->GetProdMomentum().x());
      m_ori_scint_pY_CH2.push_back(Hit->GetProdMomentum().y());
      m_ori_scint_pZ_CH2.push_back(Hit->GetProdMomentum().z());
    }
    else if (Hit->GetCreatorProcess() == "Cerenkov") {
      ++m_tot_phot_cer_CH2;
      m_energy_cer_CH2.push_back(Hit->GetEnergy());
      m_time_cer_CH2.push_back(Hit->GetTime());
      m_cer_X_CH2.push_back(Hit->GetPos().x());
      m_cer_Y_CH2.push_back(Hit->GetPos().y());
      m_cer_Z_CH2.push_back(Hit->GetPos().z());
      m_cer_pX_CH2.push_back(Hit->GetMomentum().x());
      m_cer_pY_CH2.push_back(Hit->GetMomentum().y());
      m_cer_pZ_CH2.push_back(Hit->GetMomentum().z());
      //info from trackinformation
      m_energy_scint_CH2.push_back(Hit->GetProdEnergy());
      m_cer_X_CH2.push_back(Hit->GetProdPosition().x());
      m_cer_Y_CH2.push_back(Hit->GetProdPosition().y());
      m_cer_Z_CH2.push_back(Hit->GetProdPosition().z());
      m_cer_pX_CH2.push_back(Hit->GetProdMomentum().x());
      m_cer_pY_CH2.push_back(Hit->GetProdMomentum().y());
      m_cer_pZ_CH2.push_back(Hit->GetProdMomentum().z());
    }
  }

  analysisManager->FillNtupleIColumn(13, m_tot_phot_scint_CH1);
  analysisManager->FillNtupleIColumn(14, m_tot_phot_cer_CH1);
  analysisManager->FillNtupleIColumn(15, m_tot_phot_scint_CH2);
  analysisManager->FillNtupleIColumn(16, m_tot_phot_cer_CH2);

  analysisManager->FillH1(5, m_tot_phot_scint_CH1);
  analysisManager->FillH1(6, m_tot_phot_cer_CH1);
  analysisManager->FillH1(7, m_tot_phot_scint_CH2);
  analysisManager->FillH1(8, m_tot_phot_cer_CH2);

  analysisManager->FillNtupleDColumn(7, caloEdep);
  analysisManager->FillNtupleDColumn(8, caloTrackLength);
  analysisManager->FillNtupleDColumn(9, lysoEdep);
  analysisManager->FillNtupleDColumn(10, psEdep);
  analysisManager->FillNtupleDColumn(11, fEdep_SA);

  analysisManager->AddNtupleRow();
  auto eventID = event->GetEventID();

  if (lysoEdep > 10000) G4cout << G4BestUnit(lysoEdep, "Energy") << G4endl;

  //G4PrimaryVertex *priVer = event->GetPrimaryVertex();

  auto printModulo = G4RunManager::GetRunManager()->GetPrintProgress();
  if ( ( printModulo > 0 ) && ( eventID % printModulo == 0 ) ) {
    G4cout << "---> End of event: " << eventID << G4endl;
    PrintEventStatistics(caloEdep, caloTrackLength, lysoEdep, psEdep);//, gapTrackLength);
  }
}

Thank you; I do not see any evident reason, why your output file is corrupted.

Can you try if the problem persists, if you activate only one ntuple and inactivate all other ones ? You can also increase the analysis manager verbose level to 4, and run only one or few events and inspect the output if all analysis manager functions are reported in the log.

Best regards,

Hi, I did not managed to find the ntuple that causes the issue because I found another problem. The use of maxTrackLimit dramatically slows down the execution of the simulation. Without UserLimits the execution of 1 event takes 15 seconds, with UserLimits it takes more than 1 hour.

Hello,

I have tested B5 example with added track and time limit according to your code, and the limit works ok, with no slow down. So it seems that the problem is somewhere in your application code.

Can you try to run just 1 or few events with ‘/tracking/verbose 1’,and check in the output what happens when a track gets into the volume with the limits ?

Hello,

There’s indeed an issue when a primary particle enters the calorimeter, the program got stuck at this
step for an hour now. The volume “wrapping” is a small volume encountered before the calorimeter (where UserCuts are defined).


 G4WT0 > *********************************************************************************************************
G4WT0 > * G4Track Information:   Particle = mu+,   Track ID = 1,   Parent ID = 0
G4WT0 > *********************************************************************************************************
G4WT0 >
G4WT0 > Step#       X          Y          Z         KineE         dEStep    StepLeng   TrakLeng    Volume     Process
G4WT0 >    24  -2.344 mm     5.4 mm   1.419 m      120 GeV          0 eV       0 fm    3.18 m    wrapping   initStep
G4WT0 >    25  -2.344 mm     5.4 mm   1.419 m      120 GeV      19.13 meV    100 um   3.181 m    wrapping   Transportation

Interestingly, I noticed other particles entered the calorimeter and nothing bad happened


G4WT0 > *********************************************************************************************************
G4WT0 > * G4Track Information:   Particle = opticalphoton,   Track ID = 324,   Parent ID = 1
G4WT0 > *********************************************************************************************************
G4WT0 >
G4WT0 > Step#       X          Y          Z         KineE         dEStep    StepLeng   TrakLeng    Volume     Process
G4WT0 >     0  -2.331 mm   5.398 mm   1.201 m    2.694 eV           0 eV       0 fm       0 fm      World   initStep
G4WT0 >     1  -394.1 um   633.9 um   1.419 m    2.694 eV           0 eV   21.77 cm   21.77 cm      World   Transportation
G4WT0 >     2  -393.3 um   631.7 um   1.419 m    2.694 eV           0 eV     100 um   21.78 cm   wrapping   Transportation
G4WT0 >     3  -393.3 um   631.7 um   1.419 m        0 eV       2.694 eV       0 fm   21.78 cm Calorimeter   UserSpecialCut

I think I’m starting to see the issue. I thought the max track length referred to the the track length inside the volume where user cuts are defined, instead it refers to the “global“ track length. My primary particles are produced roughly 3 meters before the calorimeter, so when they enter it the total track length is already above the limit (20 cm) and it’s terminated, although it is still not clear to me why the program gets stuck.

But more importantly that’s not what I actually wanted. I wanted to cut the max track length for optical photons produced inside the calorimeter, so that they would not bounce around forever inside the calorimeter volume. But now I don’t know how to do that.

I have dealt with this same problem. The workarounds I have found are, in order of complexity:

  1. (In TrackingAction) Resetting the global time of all particles and their secondaries that enter the volume(s) of interest. Then checking if this time ever exceeds some threshold if still inside the volume. If it has, kill the particle and all of its secondaries.
  2. Doing that same as the above but having UserTrackInfo class that has flags such as “insideCrystal” or “insideSilicon” and acculumating a path length variable that can additionally be cut on.

With optical photons a max track length is analogous to a time cutoff. Option (1) is generally straightforward to implement and have only needed option (2) if I expect TIR to be overrepresented in a photodetector window. Option 1 has the advantage of only needing to modify the TrackingAction which means that the computational costs are very low. Option 2 will probably need some code in the SteppingAction.

Thank you for your answer.

But if I decide to pursue option 2, by implementing a “insideCrystal track length” and cutting on it, I suppose I can no longer use G4UserLimits because it uses the normal track length. Should I implement my own UserLimits and my own StepLimiter process?

I believe the original intent of the max track length is to give a distance variable to cut on similiar to time (i.e. global or local time). If you group all the volumes of interest in a single region then create flags to track distance in that combined region that should give you enough flexibility to do what you want.

The other way to possibly do this is to calculate the (remaining) range inside the material(s) or region(s) when you enter using EM calculators and to kill the track if above a “max track length”. This might work better but it’ll have a lot of annoying edge cases if you have more than one material to track.