Issue with Thread-Local Files in Multithreaded Mode

Hello everyone,

I’m encountering a strange issue in multithreaded (MT) mode. Although thread-local files are being created, the data from EndOfEventAction (specifically m_analysisManager->AddNtupleRow(ntupleId)) appears to be written to the master-thread file instead:

 rw-r--r-- 1 geant4 geant4  990787 Dec 12 19:04 mcGeant.root
-rw-r--r-- 1 geant4 geant4     172 Dec 12 19:04 mcGeant_t0.root
-rw-r--r-- 1 geant4 geant4     172 Dec 12 19:04 mcGeant_t1.root
-rw-r--r-- 1 geant4 geant4     172 Dec 12 19:04 mcGeant_t2.root
-rw-r--r-- 1 geant4 geant4     172 Dec 12 19:04 mcGeant_t3.root

After the simulation ends, the thread-local files are empty, and the main file contains data from different ntuples stored in various backup cycles of the TTree:

root -l mcGeant.root
root [0]
Attaching file mcGeant.root as _file0...
(TFile *) 0x55e68bf1cf90
root [1] .ls
TFile**         mcGeant.root
 TFile*         mcGeant.root
  KEY: TTree    T;5     My tree [current cycle]
  KEY: TTree    T;4     My tree [backup cycle]
  KEY: TTree    T;3     My tree [backup cycle]
  KEY: TTree    T;2     My tree [backup cycle]
  KEY: TTree    T;1     My tree [backup cycle]

I have in my app pretty standard of the MT-like function stack:

RunAction::RunAction():G4UserRunAction(){
  	m_AnalysisManager = G4AnalysisManager::Instance();
  	m_AnalysisManager->SetNtupleMerging(false);
  	m_AnalysisManager->SetVerboseLevel(2);
}
// _________________________________________________________
void RunAction::BeginOfRunAction(const G4Run* aRun){
	m_AnalysisManager->OpenFile("mcGeant.root");
	NTupleEventAnalisys::GetInstance()->CreateNTuple(aRun);
}
// _________________________________________________________
void RunAction::EndOfRunAction(const G4Run*){
	m_AnalysisManager->Write();
  	m_AnalysisManager->CloseFile();
}
// _________________________________________________________
void EventAction::EndOfEventAction(const G4Event* anEvent){
	NTupleEventAnalisys::GetInstance()->EndOfEventAction(anEvent)
}
// _________________________________________________________
void NTupleEventAnalisys::CreateNTuple(){
	auto ntupleId = m_analysisManager->CreateNtuple("T","My tree");
	m_ntupleId.Put(ntupleId); // G4Cache variable to keep it thread local
	m_analysisManager->CreateNtupleIColumn(ntupleId, "G4EvtId");
	m_analysisManager->CreateNtupleIColumn(ntupleId, "G4RunId");
	m_analysisManager->FinishNtuple(ntupleId);
}
// _________________________________________________________
void NTupleEventAnalisys::EndOfEventAction(const G4Event *evt){
	auto ntupleId = m_ntupleId.Get();
	G4int dummyVal = 1;
	m_analysisManager->FillNtupleIColumn(ntupleId,0, dummyVal); 
	m_analysisManager->FillNtupleIColumn(ntupleId,1, dummyVal); 
	m_analysisManager->AddNtupleRow(ntupleId); 
}

Has anyone experienced something similar or have any insights on how to resolve this issue?

Geant4 Version: geant4-11-01-patch-01 [MT] (10-February-2023)


Hello,

I will try to test your use case, but could you let me know first, the content of your ActionInitialization implementation ?

Your code is more complicated than needed. There is no need to create a new ntuple witch each run and cache its id. You can reuse the same ntuple object by creating in the RunAction constructor, see the basic examples (B4 and B5). On the other hand, your mode should work, too.

Best regards,