KAHIBARO
Discord Login Register

15.2 Creating a Sensitive Detector

`G4VSensitiveDetector`

A sensitive detector in Geant4 is a user-defined C++ class that reacts to particle steps inside selected logical volumes and converts those interactions into detector response data. To create one, you derive your own class from the abstract base class G4VSensitiveDetector.

You typically implement one sensitive detector class per type of detector element. For example you might have one class for a scintillator tile and another one for a silicon strip. Many physical volumes in the geometry can share the same sensitive detector object, so you do not need one class per volume copy.

A minimal class declaration looks like this, usually placed in a header file such as MyDetectorSD.hh:

cpp
#include "G4VSensitiveDetector.hh"
#include "G4THitsCollection.hh"
class MyHit;  // forward declaration of your hit class
class MyDetectorSD : public G4VSensitiveDetector
{
  public:
    MyDetectorSD(const G4String& name,
                 const G4String& hitsCollectionName);
    ~MyDetectorSD() override = default;
    void Initialize(G4HCofThisEvent* hce) override;
    G4bool ProcessHits(G4Step* step, G4TouchableHistory* history) override;
  private:
    MyHit* fHit;  // or a hits collection pointer, depending on your design
};

The constructor of your sensitive detector must pass a name to the base class. This name is used later to attach the detector to logical volumes and to look it up in the event hit collections. Many users also register one or more hit collection names here.

A typical constructor implementation in the source file might be:

cpp
#include "MyDetectorSD.hh"
#include "MyHit.hh"
#include "G4SDManager.hh"
MyDetectorSD::MyDetectorSD(const G4String& name,
                           const G4String& hitsCollectionName)
  : G4VSensitiveDetector(name)
{
  collectionName.insert(hitsCollectionName);
}

The collectionName member belongs to G4VSensitiveDetector. Inserting a string registers one hits collection name for this detector. If you need multiple hit collections from the same sensitive detector, you can insert additional names.

Within the Initialize method you create the actual hits collection object for each event and register it with the Geant4 sensitive detector manager. A typical pattern is:

cpp
void MyDetectorSD::Initialize(G4HCofThisEvent* hce)
{
  static G4int hcID = -1;
  auto hitsCollection =
    new MyHitsCollection(SensitiveDetectorName, collectionName[0]);
  if (hcID < 0) {
    hcID = GetCollectionID(0);
  }
  hce->AddHitsCollection(hcID, hitsCollection);
  // Optionally store hitsCollection in a data member for later use
}

Here MyHitsCollection is commonly defined as a type alias based on G4THitsCollection<MyHit>. The G4HCofThisEvent object, provided for each event, holds all hit collections produced by all sensitive detectors.

The sensitive detector must then be attached to one or more logical volumes. This is usually done in your detector construction class, after you create the logical volume:

cpp
#include "G4SDManager.hh"
#include "MyDetectorSD.hh"
// inside DetectorConstruction::ConstructSDandField()
void DetectorConstruction::ConstructSDandField()
{
  auto sdManager = G4SDManager::GetSDMpointer();
  auto sd = new MyDetectorSD("MyDetectorSD", "MyHitsCollection");
  sdManager->AddNewDetector(sd);
  fDetectorLogicalVolume->SetSensitiveDetector(sd);
}

From that point on, every step inside fDetectorLogicalVolume will invoke the ProcessHits method of your sensitive detector.

A logical volume becomes sensitive only after you create a G4VSensitiveDetector subclass instance, register it with G4SDManager, and attach it to that logical volume with SetSensitiveDetector. Without this, no hits will be produced and your detector will appear inactive.

`ProcessHits()`

The ProcessHits method is the core of a sensitive detector. Geant4 calls it for every simulation step that occurs in any logical volume using this sensitive detector. Inside ProcessHits you inspect the step information and decide how to convert it into a hit or an update of an existing hit.

The method has the signature

cpp
G4bool MyDetectorSD::ProcessHits(G4Step* step,
                                 G4TouchableHistory* /*history*/)

The G4Step pointer gives access to all information about this step, such as energy deposit, step position, time, track, and the full geometry hierarchy. In many simple detectors you ignore the G4TouchableHistory argument, because you can access the same information through the step.

A very common pattern for energy deposition is:

cpp
#include "G4Step.hh"
#include "MyHit.hh"
G4bool MyDetectorSD::ProcessHits(G4Step* step,
                                 G4TouchableHistory*)
{
  G4double edep = step->GetTotalEnergyDeposit();
  if (edep <= 0.) {
    return false;
  }
  auto prePoint  = step->GetPreStepPoint();
  auto position  = prePoint->GetPosition();
  G4double time  = prePoint->GetGlobalTime();
  auto track     = step->GetTrack();
  G4int trackID  = track->GetTrackID();
  auto hit = new MyHit();
  hit->SetEdep(edep);
  hit->SetPosition(position);
  hit->SetTime(time);
  hit->SetTrackID(trackID);
  // Add hit to the current hits collection
  fHitsCollection->insert(hit);
  return true;
}

In this example, a new hit is created every time a step deposits positive energy. The hit class MyHit is a user-defined data container that stores the quantities you care about, such as deposited energy, position, time, or a detector element ID. The hits collection fHitsCollection is usually prepared in Initialize and then filled here.

You may also need to know which detector element or copy number recorded the hit. To obtain this, use the touchable object from the pre-step point:

cpp
auto touchable = prePoint->GetTouchable();
G4int copyNumber = touchable->GetCopyNumber();
// For deeper hierarchies you can use GetCopyNumber(depth)

With this copy number, you can map a hit to a specific detector cell in an array or a segmented detector.

ProcessHits returns a G4bool that indicates whether a hit was actually created or updated. Returning true is usual when you record something. If the step does not satisfy your conditions, for example no energy deposit or outside an active time window, you can return false.

In more advanced detectors you may want to accumulate energy over several steps into a single hit per event and per detector element. The algorithm inside ProcessHits then needs to search an existing hit with the same detector ID and add the new step energy to it instead of allocating a new hit. This avoids multiple hits for what physically corresponds to one detector signal.

In ProcessHits, always check whether the step is relevant to your detector response, for example by testing GetTotalEnergyDeposit(), the particle type, or the detector element ID. Recording every step without selection can create huge hit collections, slow down the simulation, and produce output that is difficult to analyze.

By implementing ProcessHits carefully, you control what your detector measures and how raw Geant4 step information is transformed into meaningful detector hits.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!