G4AnalysisManager warning in multithread mode

Hi all,

So I have a simulation which appears to be working exactly like I want when running in single thread mode. However, when I switch to multithreaded mode an issue arises:

First, in my RunAction::RunAction I create the ntuple to save to my run specifc metadata in:

RunAction::RunAction()
{
  auto analysisManager = G4AnalysisManager::Instance();

  analysisManager->SetFileName("Shells");
  analysisManager->SetDefaultFileType("root");
  analysisManager->SetNtupleMerging(true);

  analysisManager->CreateNtuple("Run", "Run specific information");
  analysisManager->CreateNtupleIColumn("RandomSeed");
  analysisManager->FinishNtuple(0);
}

Then in the RunAction::EndOfRunAction I save the data:

void RunAction::EndOfRunAction(const G4Run* run)
{
  auto analysisManager = G4AnalysisManager::Instance();
  analysisManager->FillNtupleIColumn(0, 0, CLHEP::HepRandom::getTheSeed());
  analysisManager->AddNtupleRow(0);

  analysisManager->Write();
  analysisManager->CloseFile();
}

However, I get this warning from the master thread:

-------- WWWW ------- G4Exception-START -------- WWWW -------
*** G4Exception : Analysis_W001
      issued by : G4TNtupleManager<NT,FT>::FillNtupleTColumn
Ntuple 0 does not exist.
*** This is just a warning message. ***
-------- WWWW -------- G4Exception-END --------- WWWW -------

I was able to recreate this in the B4c example by adding analysisManager->FillNtupleDColumn(0, 10); to the RunAction::EndOfRunAction. Does anyone know why this happens and how to fix it?

Hello,

You should not call the Fill functions on master. The analysis manager is designed for collecting data during event processing, which in MT mode happens on workers. The analysis manager instance on master then take care of merging analysis objects which is triggered by the call to the Write function.

You can fix your code by adding a test:

  if (! isMaster) {
    analysisManager->FillNtupleIColumn(0, 0, CLHEP::HepRandom::getTheSeed());
    analysisManager->AddNtupleRow(0);
  }

Best regards,

Hi Ivana,

Thank you that has solved this issue! I have one more issue that is very similar to this, in my EventAction::EventAction I am trying to create an ntuple to save the event specific data which contains ~12 vectors:

EventAction::EventAction()
{
  auto analysisManager = G4AnalysisManager::Instance();

  analysisManager->CreateNtuple("Event", "Event specific information");

  analysisManager->CreateNtupleIColumn("EventNumber");               // columnId: 0

  // Primary particle information branches
  analysisManager->CreateNtupleSColumn("Name_primary");              // columnId: 1
  analysisManager->CreateNtupleIColumn("ID_primary");                // columnId: 2
  analysisManager->CreateNtupleDColumn("E_primary");                 // columnId: 3
  analysisManager->CreateNtupleDColumn("PosX_primary");              // columnId: 4
  analysisManager->CreateNtupleDColumn("PosY_primary");              // columnId: 5
  analysisManager->CreateNtupleDColumn("PosZ_primary");              // columnId: 6
  analysisManager->CreateNtupleDColumn("DirX_primary");              // columnId: 7
  analysisManager->CreateNtupleDColumn("DirY_primary");              // columnId: 8
  analysisManager->CreateNtupleDColumn("DirZ_primary");              // columnId: 9

  // Gas hit information branches
  analysisManager->CreateNtupleIColumn("nHits_gas");                 // columnId: 10
  analysisManager->CreateNtupleDColumn("Edep_gas", fEdep_gas);       // columnId: 11
  analysisManager->CreateNtupleDColumn("PosX_gas", fPosX_gas);       // columnId: 12
  analysisManager->CreateNtupleDColumn("PosY_gas", fPosY_gas);       // columnId: 13
  analysisManager->CreateNtupleDColumn("PosZ_gas", fPosZ_gas);       // columnId: 14

  // Active hit information branches
  analysisManager->CreateNtupleIColumn("nHits_active");              // columnId: 15
  analysisManager->CreateNtupleDColumn("Edep_active", fEdep_active); // columnId: 16
  analysisManager->CreateNtupleDColumn("PosX_active", fPosX_active); // columnId: 17
  analysisManager->CreateNtupleDColumn("PosY_active", fPosY_active); // columnId: 18
  analysisManager->CreateNtupleDColumn("PosZ_active", fPosZ_active); // columnId: 19

  // RFR hit information branches
  analysisManager->CreateNtupleIColumn("nHits_RFR");                 // columnId: 20
  analysisManager->CreateNtupleDColumn("Edep_RFR", fEdep_RFR);       // columnId: 21
  analysisManager->CreateNtupleDColumn("PosX_RFR", fPosX_RFR);       // columnId: 22
  analysisManager->CreateNtupleDColumn("PosY_RFR", fPosY_RFR);       // columnId: 23
  analysisManager->CreateNtupleDColumn("PosZ_RFR", fPosZ_RFR);       // columnId: 24

  analysisManager->FinishNtuple();
}

Then in my EventAction::EndOfEventAction I fill the columns. However, I’m getting a similar warning:

G4WT1 >
-------- WWWW ------- G4Exception-START -------- WWWW -------
*** G4Exception : Analysis_W001
issued by : G4RootPNtupleManager::FillNtupleTColumn
ntuple id= 1 does not exist.
*** This is just a warning message. ***
-------- WWWW -------- G4Exception-END --------- WWWW -------

and an eventual segfault. I assume the only way to fix this is to move the ntuple creation to the RunAction and use getters from the EventAction, like what is done in example B5? Or do you know of a better way to handle many vectors like this?

Thanks

Yes, when ntuple merging is active, ntuple must be created also on master, that’s why we create all objects in the RunAction constructor.
So you need to add the getters in a similar way as demonstrated in example B5.

Best regards,

1 Like

Thank you Ivana, I managed to get everything mostly working now. I just have 1 last issue (i hope), I’m trying to save run specific information (for example the seed i use) but because im only using Fill when not on the master thread:

  if (! isMaster) {
    analysisManager->FillNtupleIColumn(0, 0, CLHEP::HepRandom::getTheSeed());
    analysisManager->AddNtupleRow(0);
  }

I’m getting entries for each thread (instead of just 1) and storing the threads’ seeds rather the master’s one.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

reopening topic per request from @ivana

When ntuple merging is active, the ntuple on the master collects data from worker threads and it cannot be filled by the user calls. This design reflects the fact that when Geant4 runs in multithreaded mode, the event processing is performed on workers only, and so we do not expect filling data on master.

You can use the Geant4 UI command for saving random number status:

  /random/setSavingFlag true

This will activate saving the random generator status at each begin of run, but the status will be then saved on both master and workers.

Thanks for coming back to this Ivana.

So is it not possible to only save the master run specific information to an ntuple in a multithreaded simulation?

I basically want to run a simulation consisting of 1 run and many events over 128 workers. And have an ntuple with 1 entry for the run specific metadata (seed, macro info, git ID). So I ideally wouldn’t store this info on every worker.

So is it not possible to only save the master run specific information to an ntuple in a multithreaded simulation?

Unfortunately, as I wrote above, such use case was not considered in the design.

I basically want to run a simulation consisting of 1 run and many events over 128 workers. And have an ntuple with 1 entry for the run specific metadata (seed, macro info, git ID). So I ideally wouldn’t store this info on every worker.

As a work-around, if you are sure that your number of events guarantees that all workers participate in event processing, you can call Fill only on one selected worker, eg. the one with thread id = 0.
Something like this could work:

   if (G4Threading::G4GetThreadId() == 0 ) {
      const auto& metadata = myMasterRunAction->GetMetadata();
      analysisManager->FillNtupleDColumn(0, 0, metadata.Get...());
      /// ...
      analysisManager->AddNtupleRow(0);
}

We can consider your use case in the next version of Geant4.

1 Like

Hi, @ivana ! We have the same use case in SuperCDMS – we store geometry layout information, job-level (not event-thread level) random seeds, and collect timing data in the master thread. Because our simulation framework predated a lot of G4Analysis, our OutputManager talks to ROOT directly. It handles both master and worker thread accumulation via a global singleton with mutexes (ugh).

Thanks for clearing that up for me Ivana. I’ll use your work around for now and keep an eye out on the next version :slight_smile: