9.4.  Accumulables

The classes for users accumulables management were added in 10.2 release for the purpose of simplification of users application code. The accumulables objects are named variables registered to the accumulable manager, which provides the acces to them by name and performs their merging in multi-threading mode according to their defined merge mode. Their usage is demonstrated in the basic examples B1 and B3a.

To better reflect the meaning of these objects, the classes base name "Parameter" used in 10.2 was changed in "Accumulable" in 10.3. Further integration in the Geant4 framework is foreseen in the next Geant4 versions.

9.4.1.  G4Accumulable<T>

G4Accumulable<T> templated class can be used instead of built-in types in order to facilitate merging of the values accumulated on workers to the master thread. The G4Accumulable<T> object has, besides its value of the templated type T, also a name, the initial value, which the value is set to in Reset() function and a merge mode, specifying the operation which is performed in Merge() function.

The accumulable object can be either instatiated using its constructor and registerd in G4AccumulablesManager explicitly, or it can be created using G4AccumulablesManager::CreateAccumulable() function, their registering is then automatic. The first way is used in the basic examples B1 and B3a:

// B1RunAction.hh
class B1RunAction : public G4UserRunAction
{
  // ...
  private:
    G4Accumulable<G4double> fEdep;
    G4Accumulable<G4double> fEdep2;
};

// B1RunAction.cc
B1RunAction::B1RunAction()
: G4UserRunAction(),
  fEdep("Edep", 0.), 
  fEdep2("Edep2", 0.)   // the accumulable is initialized with a name and a value = initValue
                        // (the name can be omitted)
{ 
  // ..
  // Register accumulable to the accumulable manager
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->RegisterAccumulable(fEdep);
  accumulableManager->RegisterAccumulable(fEdep2); 
}

An alternative way of creating an accumulable using G4AccumulablesManager is demonstrated below:

// B1RunAction.cc
B1RunAction::B1RunAction()
: G4UserRunAction()
{ 
  // ..
  // Accumulables can be also created via accumulable manager
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->CreateAccumulable<G4double>("EdepBis", 0.);
  accumulableManager->CreateAccumulable<G4double>("Edep2Bis", 0.);
}

The G4AccumulablesManager takes ownership of the accumulables created by its CreateAccumulable() function the accumulables allocated in the user code has to be deleted in the user code.

Since Geant4 10.3, the name of the accumulable can be omitted. A generic name "accumulable_N", where N is the current number of registered obects, will be then attributed.

In multi-threading mode all accumulables registered to G4AccumulablesManager accumulated on workers can be merged to the master thread by calling G4AccumulablesManager::Merge() function. This step may be not necessary in future after a planned closer integration of G4Accumulable classes in the Geant4 kernel.

// B1RunAction.cc
void B1RunAction::EndOfRunAction(const G4Run* run)
{
  // ...
  // Merge accumulables
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->Merge();
}

The merging mode can be specified using the third (or the second one, if the name is omitted) G4Accumulable<T> constructor argument. The merge modes are defined in G4MergeMode class enumeration:

enum class G4MergeMode {
  kAddition,        // "Or" if boolean type
  kMultiplication,  // "And" if boolean type
  kMaximum,         // "Or" if boolean type
  kMinimum          // "And" if boolean type
};  

The default accumulable merge operation is addition.

The registered accumulables can be accessed via G4AccumulablesManager by name or by the id, attributed in the order of registering:

  // ...
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  // Access accumulables by name
  G4double edepBis  = accumulableManager->GetAccumulable<G4double>("EdepBis")->GetValue();
  G4double edep2Bis = accumulableManager->GetAccumulable<G4double>("Edep2Bis")->GetValue();

  // Access accumulables by id
  G4VAccumulable* accumulable = accumulableManager->GetAccumulable(id);

9.4.2.  User defined accumulables

Users can define their own accumulable class derived from G4VAccumulable abstract base class. An example of a ProcessCounterAccumulable class, implementing an accumulable holding a map of the processes occurences by the procesesses names, is given below. Such processes occurences map is used in several electromagnetic extended examples, e.g. TestEm1.

ProcCounterAccumulable.hh

#include "G4VAccumulable.hh"
#include "globals.hh"
#include <map>
class ProcCounterAccumulable : public G4VAccumulable
{
  public:
    ProcCounterAccumulable(const G4String& name) 
      : G4VAccumulable(name, 0), fProcCounter() {}
    virtual ~ProcCounterAccumulable() {}
    
    void CountProcesses(G4String procName);
    
    virtual void Merge(const G4VAccumulable& other);
    virtual void Reset();

  private:
    std::map<G4String,G4int> fProcCounter;
};

ProcCounterAccumulable.cc

void ProcCounterAccumulable::Merge(const G4VAccumulable& other)
{
  const ProcCounterAccumulable& otherProcCounterAccumulable 
    = static_cast<const ProcCounterAccumulable&>(other);

  std::map<G4String,G4int>::const_iterator it;
  for (it = otherProcCounterAccumulable.fProcCounter.begin(); 
       it != otherProcCounterAccumulable.fProcCounter.end(); ++it) {
       
    G4String procName = it->first;
    G4int otherCount  = it->second;
    if ( fProcCounter.find(procName) == fProcCounter.end()) {
      fProcCounter[procName] = otherCount;
    }
    else {
      fProcCounter[procName] += otherCount;
    }         
  }
} 

void ProcCounterAccumulable::Reset()
{
  fProcCounter.clear();
}

The implementation of the CountProcesses() function is identical as in Run::CountProcesses() function in TestEm1.