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 access to them by name or id 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.

All accumulable objects are derived from the G4VAccumulable abstract base class.

G4AccValue<T>

G4AccValue<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 G4AccValue<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 the Merge() function. The G4AccValue<T> class name has replaced the previous one, G4Accumulable<T>, with the addition of the accumulable collections in Geant4 11.3 .

The accumulable value can be either instantiated using its constructor and registered in G4AccumulableManager explicitly, or it can be created using G4AccumulableManager::CreateAccValue() function, their registering is then automatic. The first way is used in the basic examples B1 and B3a:

// B1/include/RunAction.hh
class RunAction : public G4UserRunAction
{
  // ...
  private:
    G4AccValue<G4double> fEdep  = 0.;
    G4AccValue<G4double> fEdep2 = 0.;
};

// B1/src/RunAction.cc
RunAction::RunAction()
{
  // ..
  // Register accumulable to the accumulable manager
  G4AccumulableManager* accumulableManager = G4AccumulableManager::Instance();
  accumulableManager->Register(fEdep);
  accumulableManager->Register(fEdep2);
}

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

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

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

Accumulable Collections

Since Geant4 11.3, classes defining collections of accumulables for the most frequent collections of the standard library: array, vector, map, unordered_map are available:

  • G4AccArray, G4AccVector, G4AccMap, G4AccUnorderedMap

They simplify the definition of collections of accumulables, which until now had to be implemented by users. All accumulable collections are implemented via a composition of the underlined standard library collection.

Accumulable collections must be instantiated using their constructor and expicitly registered in G4AccumulableManager. Collection classes provide contructors sets with the same parameter list as standard library collections + G4Acc-specific parameters: name and merge mode. Constructor implementation follows the same principles for all collections:

  • Constructors with brace initialization are supported for all collections.

  • The name, when provided, is always the first parameter.

  • The merge mode is always optional and it is always placed as the first optional parameter after parameters without a default value, the default is G4MergeMode::kAddition.

using MyArrayType = G4AccArray<G4double, 2>;

// Default conctruction
MyArrayType array1;

// Construction with brace initialization
MyArrayType array2{0., 0.};

// Construction with brace initialization
MyArrayType array3("array2");
MyArrayType array4{"EdepArray3", 0., 0.};

// Construction with optional merge mode parameter
MyArrayType array5(
"array2", 0., G4MergeMode::kMaximum);

An example of using G4AccArray is given below. Note that each code block is suppposed to be called in the appropriated phase of Geant4 run:

// Construct (with default constructor)
G4AccArray<G4double, 2> myArray;

// Register
auto accManager = G4AccumulableManager::Instance();
accManager->Register(myArray);

// Fill/update
myArray[0] += edep;
myArray[1] += edep*edep;

// Merge (all registered accumulables)
accManager->Merge();

// Print
myArray.Print();

G4AccumulableManager

The G4AccumulableManager only takes ownership of the accumulables created by its CreateAccValue() function. Accumulables allocated in user code must be deleted in user code.

The Register() overload function is provided for all accumulable values and collections type.

In multithreading mode, all accumulables registered in G4AccumulableManager accumulated on workers can be merged into the master thread by calling G4AccumulableManager::Merge() function.

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

The merge mode can be specified either when constructing the accumulable objects or via their SetMergeMode() function defined in the accumulables base class. Merge modes are defined in the 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 kAddition.

Registered accumulables are accessible via G4AccumulableManager by name or by the ID, assigned in the order of registration. The GetAcc*() functions are available for all types of accumulables (values and collections):

// ...
auto accumulableManager = G4AccumulableManager::Instance();
// Access accumulable values by name
auto edepBis  = accumulableManager->GetAccValue<G4double>("EdepBis")->GetValue();
auto edep2Bis = accumulableManager->GetAccValue<G4double>("Edep2Bis")->GetValue();

// Access accumulable value by id
auto accumulable = accumulableManager->GetAccValue(id);

// Access accumulable array by name
auto& edepArray  = accumulableManager->GetAccArray<G4double, 2>("Edep")->GetArray();

Since Geant4 11.3, the accumulable manager also offers functions to print a selected range or all accumulables:

void Print(G4PrintOptions options = G4PrintOptions()) const;
// Print a selected range
void Print(G4int startId, G4int count,
           G4PrintOptions options = G4PrintOptions()) const;
void Print(std::vector<G4VAccumulable*>::iterator startIt, std::size_t count,
           G4PrintOptions options = G4PrintOptions()) const;
void Print(std::vector<G4VAccumulable*>::iterator startIt,
           std::vector<G4VAccumulable*>::iterator endIt,
           G4PrintOptions options = G4PrintOptions()) const;

By default, the accumulable name and type are printed in addition to the object content (the value or the elements in the case of collections). The default values can be changed by providing the options parameter, for example to suppress the default printing of the type:

auto accumulableManager = G4AccumulableManager::Instance();
G4PrintOptions options;
options.Set(kType, false);
accumulableManager->Print(options);

User defined accumulables

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

With the addition of accumulable collections in Geant4 11.3, users can achieve the same functionality with G4AccMap<G4String,G4int> without needing to implement their own class; but the example is kept only to demonstrate the flexibility of the framework.

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.