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.