Memory leak in MT mode white hit processing in collections?

Hello all, I need advice…

I try to make MT application with a sensitive detector that accumulate hits in collection and process them at the end of event action.

I created MyHit.hh (like in many G4 examples):

//---------------------------------------------------------------
#ifndef MyHit_h
#define MyHit_h 1

#include <G4VHit.hh>
#include <G4THitsCollection.hh>
#include <G4Allocator.hh>
#include <G4ThreeVector.hh>

class MyHit : public G4VHit
{
public:

MyHit() : G4VHit(), _EnDeposit(0), _Pos(0.,0.,0.), _PatEnergy(0), _CopyNo(0), _PatID(-1)
{
	_VolumeName  = "";
}

inline void* operator new(size_t);
inline void operator delete(void*);

private:

G4double _EnDeposit; // Energy deposit
G4double _PatEnergy; // Particle kinetic energy
G4int _PatID; // Particle PDG code
G4int _CopyNo; //Physical detector copy number
G4ThreeVector _Pos; // Position of the hit
G4String _VolumeName; //Physical volume name

public:

// Set functions to store information on hits

// Get functions to acess information on hits

};

typedef G4THitsCollection MyHitsCollection;
extern G4ThreadLocal G4Allocator* MyHitAllocator;

inline void* MyHit::operator new(size_t)
{
if (!MyHitAllocator) { MyHitAllocator = new G4Allocator; }
return (void*)MyHitAllocator->MallocSingle();

}

inline void MyHit::operator delete(void* aHit)
{
if (!MyHitAllocator) { MyHitAllocator = new G4Allocator; }
MyHitAllocator->FreeSingle((MyHit*)aHit);
}

#endif
//---------------------------------------------------------------

and simple MyHit.cc:
//---------------------------------------------------------------
#include “MyHit.hh”

#include <CLHEP/Units/SystemOfUnits.h>
#include <G4UnitsTable.hh>
#include <G4VVisManager.hh>
#include <G4VisAttributes.hh>

using namespace CLHEP;

G4ThreadLocal G4Allocator* MyHitAllocator = NULL;
//---------------------------------------------------------------

So, during the tests of my application on a cluster with 64 threads, the application 100% crashed due to end of available memory… It looks like the “ProcessHits” function of my sensitive detector just continuously allocate the memory but nobody freed it!!!

G4bool ScintillatorSD::ProcessHits(G4Step* theStep, G4TouchableHistory*)
{

//Add hit info to collection
MyHit* Hit = new MyHit;
Hit->SetEnergyDeposit(theStep->GetTotalEnergyDeposit());
Hit->SetPosition(thePrePoint->GetPosition());
Hit->SetVolumeName(thePrePoint->GetPhysicalVolume()->GetName());
EnHitsCollection->insert(Hit);

}

I have found the presentation “Improving memory handling in Geant4 MT”):

but it looks like nobody use something compatible to process hits and collections.

Does somebody have any ideas how to catch or fix this issue?

instead of strings, can you use the index of the volume (g4int)? maybe this does not exhaust memory limits so quickly

Many thanks for good advice, I forgot about this issue!

I add lookup table in DetectorConstruction class and pass the pointer to the instance of this class to ActionInitialisation and EventAction class to have possibility to resolve VolumeID to symbolic name during EndOfEventAction.

More interestingly that sometimes, while a particle pass the scintillator, GEANT4 generates few MILLIONS of hits and in MT application few threads could simultaneously require huge amount of memory! Therefore I add try/catch sections to check possible memory allocation problems.

Below some parts of my code. Maybe it will be useful for somebody.
Sincerely Yours,
Vyacheslav

class DetectorConstruction : public G4VUserDetectorConstruction, public DetectorSurfaces
{

//Service function to get the Name-ID pair
std::vector& GetVolumeMap() { return _mapVolumes; }

//lookup table of physical volume names
std::vector<G4String> _mapVolumes;


}

and fill it in the call of:
G4VPhysicalVolume* DetectorConstruction::Construct()
{

//=================================================
// Define look-up table of the volume names
//=================================================
G4PhysicalVolumeStore* pPVStore = G4PhysicalVolumeStore::GetInstance();
G4int nHighestIndex = 0;
for (auto it = pPVStore->begin(); it != pPVStore->end(); ++it)
{
int nID = (*it)->GetInstanceID();
G4cout << "ID: " << nID << " Name: " << (*it)->GetName() << G4endl;
//find the highest undex
if (nID > nHighestIndex) { nHighestIndex = nID; }

}
_mapVolumes.resize(size_t(nHighestIndex) + 1); //expand +1 to correctly process "0" elements

//fill map with names
for (auto it = pPVStore->begin(); it != pPVStore->end(); ++it)
{
    int nID = (*it)->GetInstanceID();
    _mapVolumes[nID] = (*it)->GetName();
}


}

after that I can fill my Hits with VolumeID.

G4bool ScintillatorSD::ProcessHits(G4Step* theStep, G4TouchableHistory*)
{
try
{

//Add hit info to collection
MyHit* Hit = new MyHit;

Hit->SetVolumeID(thePrePoint->GetPhysicalVolume()->GetInstanceID()); //->GetName()

}
catch (const std::bad_alloc&)
{
G4cout << “ProcessHit rised exception!” << G4endl;
}
catch (…)
{
G4cout << “ProcessHit rised unknown exception!” << G4endl;
}
}

Hello,

If you are using G4Allocator you indeed need to delete objects after use. G4Allocator provides a mechanism to do this efficiently. G4Allocator will clean memory after the job but without deletion of objects you will accumulate all hits for all events.

Vi

Hello,

It is strange but I didn’t find any example where the collection of hits is used and in which the elements are deleted at the end of event.

Sincerely Yours,
Vyacheslav

Additional comment… G4Allocator has a hardcoded magic:

size_type max_size() const throw()
{
// Returns the maximum number of elements that can be allocated
//
return 2147483647 / sizeof(Type);
}

what the strange limitation for 64-bit computers???