I have been developing a complex geometry using G4AssemblyVolume. It is proving to be a really simple way to group solids together and then create multiple, identical, instances. I’m using it to create multiple gamma detectors. In each of the imprinted assembly volumes is a scintillator detector, which I need to act as a SensitiveDetector, ideally a G4MultiFunctionalDetector.
I am aware that a SensitiveDetector must be linked to a Logical Volume, but how do I link a SensitiveDetector to a component within each of the G4AssemblyVolumes?
I suspect that I will need to use Copy Numbers or Replica Numbers. An example of what I need to do would be very helpful.
Thanks in advance.
Hold on, am I being silly here - do I just add the G4MultiFunctionalDetector to LogicalVolume before I add it to the G4AssemblyVolume?
You’ve got it! Then, in your SD code, you’ll want to traverse the G4Touchable associated with the step (preStepPoint, to avoid the boundary-crossing issue) to get the copy numbers you’re interested in. By using the touchable, you can collect any nested placements all the way up the chain, if that’s important.
Ah ok, so if I want to log the energy deposited in the G4MultiFunctionalDetector on a per event basis, can I do that in EventAction.cc using the copy number?
Apologies for all the questions - there appear to be very few examples of G4AssemblyVolume out there
That sounds right. We don’t use MFD in our simulation, so I’m looking at the .hh file now. The ProcessHits()
command gets the G4Step and the correct G4TouchableHistory, from which the copy numbers can be extracted. If you write your own implementation of ProcessHits()
in your subclass, then you do what you need to do there. Maybe you make separate scores for each placement?
In our SD classes, we populate our own HitsCollection, and we include a data member for each hit which is the detector copy number.
Ok, so I swapped the MFD for a SensitiveDetector and Hit approach. Here’s the problem… the only place I can add a SensitiveDetector to a LogicalVolume is within ConstructSDandField. But isn’t this after I’ve added the LV to the AssemblyVolume?
What you have to do is store the LV pointer when you create it into a data member. Then in ConstructSDandField()
you can access that data member to do the attachment.
In our simulation, we have many volumes to which we attach SDs, so I have a std::vector<G4LogicalVolume*>
data member in our DetectorConstruction
subclass. The relevant LVs get pushed onto that, and in ConstructSDandField()
(in each worker thread), we loop over it and attach the SD instance for that thread. We avoid stale pointers by clearing the vector at the start of ::Construct()
.
Thanks for your explanation.
I sort of get what you are saying but would you be able to show a simple example to ensure I’m not misunderstanding something.
I can’t post our experiment’s code directly ((a) it’s not considered “open”, and (b) it’s freakin’ huge
). Here’s a sketch of what you’d do for the simple case of a single volume.
class MyGeometryBuilder : public G4VUserDetectorConstruction {
public:
MyGeometryBuilder() {;}
~MyGeometryBuilder() {;}
virtual void Construct();
virtual void ConstructSDandField();
private:
G4LogicalVolume* detectorLV;
};
void MyGeometryBuilder::Construct() {
// Make the world here
G4VSolid* detShape = G4Tubs("Detector", 0., 50*mm, 17*mm, 0., twopi);
G4Material* detMat = nistManager->FindOrBuildMaterial("G4_Si");
// Save the detector for later sensitization
detectorLV = new G4LogicalVolume(detShape, detMat, detShape->GetName());
// Place the detector (really, it'll be deep in some apparatus)
new G4PVPlacement(G4Transform3D(), detectorLV, detectorLV->GetName(),
worldLV, false, detNum);
}
void MyGeometryBuilder::ConstructSDandField() {
// Do all the stuff with G4SDManager!
G4VSensitiveDetector* SD = new MySD;
detectorLV->SetSensitiveDetector(SD);
}